/* INCLUDES *******************************************************************/
+#define NDEBUG
+
#include "bios.h"
#include "emulator.h"
#include "pic.h"
/* PRIVATE VARIABLES **********************************************************/
+static PBIOS_DATA_AREA Bda;
static BYTE BiosKeyboardMap[256];
-static WORD BiosKbdBuffer[BIOS_KBD_BUFFER_SIZE];
-static UINT BiosKbdBufferStart = 0, BiosKbdBufferEnd = 0;
-static BOOLEAN BiosKbdBufferEmpty = TRUE;
-static DWORD BiosTickCount = 0;
-static BOOLEAN BiosPassedMidnight = FALSE;
-static HANDLE BiosConsoleInput, BiosConsoleOutput;
-static BYTE CurrentVideoMode = BIOS_DEFAULT_VIDEO_MODE;
-static BYTE CurrentVideoPage = 0;
-static HANDLE ConsoleBuffers[BIOS_MAX_PAGES] = { NULL };
-static LPVOID ConsoleFramebuffers[BIOS_MAX_PAGES] = { NULL };
+static HANDLE BiosConsoleInput = INVALID_HANDLE_VALUE;
+static HANDLE BiosConsoleOutput = INVALID_HANDLE_VALUE;
+static HANDLE BiosGraphicsOutput = NULL;
+static LPVOID ConsoleFramebuffer = NULL;
+static HANDLE ConsoleMutex = NULL;
+static BYTE CurrentVideoMode, CurrentVideoPage;
+static BOOLEAN VideoNeedsUpdate = TRUE;
+static SMALL_RECT UpdateRectangle = { 0, 0, 0, 0 };
+static CONSOLE_SCREEN_BUFFER_INFO BiosSavedBufferInfo;
+
static VIDEO_MODE VideoModes[] =
{
- /* Width | Height | Text | Colors | Gray | Pages | Segment */
+ /* Width | Height | Text | Bpp | Gray | Pages | Segment */
{ 40, 25, TRUE, 16, TRUE, 8, 0xB800}, /* Mode 00h */
{ 40, 25, TRUE, 16, FALSE, 8, 0xB800}, /* Mode 01h */
{ 80, 25, TRUE, 16, TRUE, 8, 0xB800}, /* Mode 02h */
{ 80, 25, TRUE, 16, FALSE, 8, 0xB800}, /* Mode 03h */
- { 320, 200, FALSE, 4, FALSE, 4, 0xB800}, /* Mode 04h */
- { 320, 200, FALSE, 4, TRUE, 4, 0xB800}, /* Mode 05h */
- { 640, 200, FALSE, 2, FALSE, 2, 0xB800}, /* Mode 06h */
- { 80, 25, TRUE, 3, FALSE, 1, 0xB000}, /* Mode 07h */
+ { 320, 200, FALSE, 2, FALSE, 1, 0xB800}, /* Mode 04h */
+ { 320, 200, FALSE, 2, TRUE, 1, 0xB800}, /* Mode 05h */
+ { 640, 200, FALSE, 1, FALSE, 1, 0xB800}, /* Mode 06h */
+ { 80, 25, TRUE, 8, FALSE, 1, 0xB000}, /* Mode 07h */
{ 0, 0, FALSE, 0, FALSE, 0, 0x0000}, /* Mode 08h - not used */
{ 0, 0, FALSE, 0, FALSE, 0, 0x0000}, /* Mode 09h - not used */
{ 0, 0, FALSE, 0, FALSE, 0, 0x0000}, /* Mode 0Ah - not used */
{ 0, 0, FALSE, 0, FALSE, 0, 0x0000}, /* Mode 0Bh - not used */
{ 0, 0, FALSE, 0, FALSE, 0, 0x0000}, /* Mode 0Ch - not used */
- { 320, 200, FALSE, 16, FALSE, 8, 0xA000}, /* Mode 0Dh */
- { 640, 200, FALSE, 16, FALSE, 4, 0xA000}, /* Mode 0Eh */
- { 640, 350, FALSE, 3, FALSE, 2, 0xA000}, /* Mode 0Fh */
- { 640, 350, FALSE, 4, FALSE, 2, 0xA000}, /* Mode 10h */
- { 640, 480, FALSE, 2, FALSE, 1, 0xA000}, /* Mode 11h */
- { 640, 480, FALSE, 16, FALSE, 1, 0xA000}, /* Mode 12h */
- { 640, 480, FALSE, 256, FALSE, 1, 0xA000} /* Mode 13h */
+ { 320, 200, FALSE, 4, FALSE, 1, 0xA000}, /* Mode 0Dh */
+ { 640, 200, FALSE, 4, FALSE, 1, 0xA000}, /* Mode 0Eh */
+ { 640, 350, FALSE, 1, FALSE, 1, 0xA000}, /* Mode 0Fh */
+ { 640, 350, FALSE, 4, FALSE, 1, 0xA000}, /* Mode 10h */
+ { 640, 480, FALSE, 1, FALSE, 1, 0xA000}, /* Mode 11h */
+ { 640, 480, FALSE, 4, FALSE, 1, 0xA000}, /* Mode 12h */
+ { 320, 200, FALSE, 8, FALSE, 1, 0xA000} /* Mode 13h */
};
/* PRIVATE FUNCTIONS **********************************************************/
+static DWORD BiosGetVideoPageSize()
+{
+ INT i;
+ DWORD BufferSize = VideoModes[CurrentVideoMode].Width
+ * VideoModes[CurrentVideoMode].Height
+ * VideoModes[CurrentVideoMode].Bpp
+ / 8;
+
+ for (i = 0; i < 32; i++) if ((1 << i) >= BufferSize) break;
+
+ return 1 << i;
+}
+
+static BYTE BiosVideoAddressToPage(ULONG Address)
+{
+ return (Address - BiosGetVideoMemoryStart())
+ / BiosGetVideoPageSize();
+}
+
static COORD BiosVideoAddressToCoord(ULONG Address)
{
COORD Result = {0, 0};
+ DWORD PageStart = BiosVideoAddressToPage(Address) * BiosGetVideoPageSize();
+ DWORD Offset = Address - BiosGetVideoMemoryStart() - PageStart;
- Result.X = ((Address - (VideoModes[CurrentVideoMode].Segment << 4)) >> 1)
- % VideoModes[CurrentVideoMode].Width;
- Result.Y = ((Address - (VideoModes[CurrentVideoMode].Segment << 4)) >> 1)
- / VideoModes[CurrentVideoMode].Width;
+ if (VideoModes[CurrentVideoMode].Text)
+ {
+ Result.X = (Offset / sizeof(WORD)) % VideoModes[CurrentVideoMode].Width;
+ Result.Y = (Offset / sizeof(WORD)) / VideoModes[CurrentVideoMode].Width;
+ }
+ else
+ {
+ Result.X = ((Offset * 8) / VideoModes[CurrentVideoMode].Bpp)
+ % VideoModes[CurrentVideoMode].Width;
+ Result.Y = ((Offset * 8) / VideoModes[CurrentVideoMode].Bpp)
+ / VideoModes[CurrentVideoMode].Width;
+ }
return Result;
}
static BOOLEAN BiosKbdBufferPush(WORD Data)
{
+ /* Get the location of the element after the head */
+ WORD NextElement = Bda->KeybdBufferHead + 2;
+
+ /* Wrap it around if it's at or beyond the end */
+ if (NextElement >= Bda->KeybdBufferEnd) NextElement = Bda->KeybdBufferStart;
+
/* If it's full, fail */
- if (!BiosKbdBufferEmpty && (BiosKbdBufferStart == BiosKbdBufferEnd))
+ if (NextElement == Bda->KeybdBufferTail) return FALSE;
+
+ /* Put the value in the queue */
+ *((LPWORD)((ULONG_PTR)Bda + Bda->KeybdBufferTail)) = Data;
+ Bda->KeybdBufferTail += sizeof(WORD);
+
+ /* Check if we are at, or have passed, the end of the buffer */
+ if (Bda->KeybdBufferTail >= Bda->KeybdBufferEnd)
{
- return FALSE;
+ /* Return it to the beginning */
+ Bda->KeybdBufferTail = Bda->KeybdBufferStart;
}
- /* Otherwise, add the value to the queue */
- BiosKbdBuffer[BiosKbdBufferEnd] = Data;
- BiosKbdBufferEnd++;
- BiosKbdBufferEnd %= BIOS_KBD_BUFFER_SIZE;
- BiosKbdBufferEmpty = FALSE;
-
/* Return success */
return TRUE;
}
static BOOLEAN BiosKbdBufferTop(LPWORD Data)
{
/* If it's empty, fail */
- if (BiosKbdBufferEmpty) return FALSE;
+ if (Bda->KeybdBufferHead == Bda->KeybdBufferTail) return FALSE;
/* Otherwise, get the value and return success */
- *Data = BiosKbdBuffer[BiosKbdBufferStart];
+ *Data = *((LPWORD)((ULONG_PTR)Bda + Bda->KeybdBufferHead));
+
return TRUE;
}
static BOOLEAN BiosKbdBufferPop()
{
/* If it's empty, fail */
- if (BiosKbdBufferEmpty) return FALSE;
+ if (Bda->KeybdBufferHead == Bda->KeybdBufferTail) return FALSE;
+
+ /* Remove the value from the queue */
+ Bda->KeybdBufferHead += sizeof(WORD);
+
+ /* Check if we are at, or have passed, the end of the buffer */
+ if (Bda->KeybdBufferHead >= Bda->KeybdBufferEnd)
+ {
+ /* Return it to the beginning */
+ Bda->KeybdBufferHead = Bda->KeybdBufferStart;
+ }
+
+ /* Return success */
+ return TRUE;
+}
+
+static BOOLEAN BiosCreateGraphicsBuffer(BYTE ModeNumber)
+{
+ INT i;
+ CONSOLE_GRAPHICS_BUFFER_INFO GraphicsBufferInfo;
+ LPBITMAPINFO BitmapInfo;
+ LPWORD PaletteIndex;
- /* Otherwise, remove the value and return success */
- BiosKbdBufferStart++;
- BiosKbdBufferStart %= BIOS_KBD_BUFFER_SIZE;
- if (BiosKbdBufferStart == BiosKbdBufferEnd) BiosKbdBufferEmpty = TRUE;
+ /* Allocate a bitmap info structure */
+ BitmapInfo = (LPBITMAPINFO)HeapAlloc(GetProcessHeap(),
+ HEAP_ZERO_MEMORY,
+ sizeof(BITMAPINFOHEADER)
+ + (1 << VideoModes[ModeNumber].Bpp)
+ * sizeof(WORD));
+ if (BitmapInfo == NULL) return FALSE;
+
+ /* Fill the bitmap info header */
+ ZeroMemory(&BitmapInfo->bmiHeader, sizeof(BITMAPINFOHEADER));
+ BitmapInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ BitmapInfo->bmiHeader.biWidth = VideoModes[ModeNumber].Width;
+ BitmapInfo->bmiHeader.biHeight = VideoModes[ModeNumber].Height;
+ BitmapInfo->bmiHeader.biPlanes = 1;
+ BitmapInfo->bmiHeader.biCompression = BI_RGB;
+ BitmapInfo->bmiHeader.biBitCount = VideoModes[ModeNumber].Bpp;
+
+ /* Calculate the image size */
+ BitmapInfo->bmiHeader.biSizeImage = BitmapInfo->bmiHeader.biWidth
+ * BitmapInfo->bmiHeader.biHeight
+ * (BitmapInfo->bmiHeader.biBitCount >> 3);
+
+ /* Fill the palette data */
+ PaletteIndex = (LPWORD)((ULONG_PTR)BitmapInfo + sizeof(BITMAPINFOHEADER));
+ for (i = 0; i < (1 << VideoModes[ModeNumber].Bpp); i++)
+ {
+ PaletteIndex[i] = i;
+ }
+
+ /* Fill the console graphics buffer info */
+ GraphicsBufferInfo.dwBitMapInfoLength = sizeof(BITMAPINFOHEADER)
+ + (1 << VideoModes[ModeNumber].Bpp)
+ * sizeof(WORD);
+ GraphicsBufferInfo.lpBitMapInfo = BitmapInfo;
+ GraphicsBufferInfo.dwUsage = DIB_PAL_COLORS;
+
+ /* Create the buffer */
+ BiosGraphicsOutput = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ CONSOLE_GRAPHICS_BUFFER,
+ &GraphicsBufferInfo);
+
+ /* Save the framebuffer address and mutex */
+ ConsoleFramebuffer = GraphicsBufferInfo.lpBitMap;
+ ConsoleMutex = GraphicsBufferInfo.hMutex;
+
+ /* Free the bitmap information */
+ HeapFree(GetProcessHeap(), 0, BitmapInfo);
return TRUE;
}
+static VOID BiosDestroyGraphicsBuffer()
+{
+ CloseHandle(ConsoleMutex);
+ CloseHandle(BiosGraphicsOutput);
+}
+
/* PUBLIC FUNCTIONS ***********************************************************/
BYTE BiosGetVideoMode()
BOOLEAN BiosSetVideoMode(BYTE ModeNumber)
{
- INT i;
COORD Coord;
- CONSOLE_GRAPHICS_BUFFER_INFO GraphicsBufferInfo;
- LPBITMAPINFO BitmapInfo;
- LPWORD PaletteIndex;
/* Make sure this is a valid video mode */
if (ModeNumber > BIOS_MAX_VIDEO_MODE) return FALSE;
if (VideoModes[ModeNumber].Pages == 0) return FALSE;
- /* Free the current buffers */
- for (i = 0; i < VideoModes[CurrentVideoMode].Pages; i++)
+ /* Set the new video mode size */
+ Coord.X = VideoModes[ModeNumber].Width;
+ Coord.Y = VideoModes[ModeNumber].Height;
+
+ if (VideoModes[ModeNumber].Text && VideoModes[CurrentVideoMode].Text)
{
- if (ConsoleBuffers[i] != NULL) CloseHandle(ConsoleBuffers[i]);
- }
+ /* Switching from text mode to another text mode */
- if (VideoModes[ModeNumber].Text)
+ /* Resize the text buffer */
+ SetConsoleScreenBufferSize(BiosConsoleOutput, Coord);
+ }
+ else if (VideoModes[ModeNumber].Text && !VideoModes[CurrentVideoMode].Text)
{
- /* Page 0 is CONOUT$ */
- ConsoleBuffers[0] = CreateFile(TEXT("CONOUT$"),
- GENERIC_READ | GENERIC_WRITE,
- FILE_SHARE_READ | FILE_SHARE_WRITE,
- NULL,
- OPEN_EXISTING,
- 0,
- NULL);
-
- /* Set the current page to page 0 */
- CurrentVideoPage = 0;
-
- /* Create console buffers for other pages */
- for (i = 1; i < VideoModes[ModeNumber].Pages; i++)
- {
- ConsoleBuffers[i] = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
- FILE_SHARE_READ | FILE_SHARE_WRITE,
- NULL,
- CONSOLE_TEXTMODE_BUFFER,
- NULL);
- }
+ /* Switching from graphics mode to text mode */
- /* Set the size for the buffers */
- for (i = 0; i < VideoModes[ModeNumber].Pages; i++)
- {
- Coord.X = VideoModes[ModeNumber].Width;
- Coord.Y = VideoModes[ModeNumber].Height;
+ /* Resize the text buffer */
+ SetConsoleScreenBufferSize(BiosConsoleOutput, Coord);
- SetConsoleScreenBufferSize(ConsoleBuffers[i], Coord);
- }
+ /* Change the active screen buffer to the text buffer */
+ SetConsoleActiveScreenBuffer(BiosConsoleOutput);
+
+ /* Cleanup the graphics buffer */
+ BiosDestroyGraphicsBuffer();
}
- else
+ else if (!VideoModes[ModeNumber].Text && VideoModes[CurrentVideoMode].Text)
{
- /* Allocate a bitmap info structure */
- BitmapInfo = (LPBITMAPINFO)HeapAlloc(GetProcessHeap(),
- HEAP_ZERO_MEMORY,
- sizeof(BITMAPINFOHEADER)
- + VideoModes[ModeNumber].Colors
- * sizeof(WORD));
- if (BitmapInfo == NULL) return FALSE;
-
- /* Fill the bitmap info header */
- ZeroMemory(&BitmapInfo->bmiHeader, sizeof(BITMAPINFOHEADER));
- BitmapInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
- BitmapInfo->bmiHeader.biWidth = VideoModes[ModeNumber].Width;
- BitmapInfo->bmiHeader.biHeight = VideoModes[ModeNumber].Height;
- BitmapInfo->bmiHeader.biPlanes = 1;
- BitmapInfo->bmiHeader.biCompression = BI_RGB;
-
- /* Calculate the number of color bits */
- for (i = 31; i >= 0; i--)
- {
- if (VideoModes[ModeNumber].Colors & (1 << i)) break;
- }
- if (i == 0) i = 32;
- BitmapInfo->bmiHeader.biBitCount = i;
+ /* Switching from text mode to graphics mode */
+ if (!BiosCreateGraphicsBuffer(ModeNumber)) return FALSE;
- /* Calculate the image size */
- BitmapInfo->bmiHeader.biSizeImage = BitmapInfo->bmiHeader.biWidth
- * BitmapInfo->bmiHeader.biHeight
- * (BitmapInfo->bmiHeader.biBitCount >> 3);
+ SetConsoleActiveScreenBuffer(BiosGraphicsOutput);
+ }
+ else if (!VideoModes[ModeNumber].Text && !VideoModes[CurrentVideoMode].Text)
+ {
+ /* Switching from graphics mode to another graphics mode */
+
+ /* Temporarily switch to the text mode buffer */
+ SetConsoleActiveScreenBuffer(BiosConsoleOutput);
- /* Fill the palette data */
- PaletteIndex = (LPWORD)((ULONG_PTR)BitmapInfo + sizeof(BITMAPINFOHEADER));
- for (i = 0; i < VideoModes[ModeNumber].Colors; i++)
- {
- PaletteIndex[i] = i;
- }
+ /* Cleanup the current graphics mode buffer */
+ BiosDestroyGraphicsBuffer();
- /* Create a console buffer for each page */
- for (i = 0; i < VideoModes[ModeNumber].Pages; i++)
- {
- /* Fill the console graphics buffer info */
- GraphicsBufferInfo.dwBitMapInfoLength = sizeof(BITMAPINFOHEADER)
- + VideoModes[ModeNumber].Colors
- * sizeof(WORD);
- GraphicsBufferInfo.lpBitMapInfo = BitmapInfo;
- GraphicsBufferInfo.dwUsage = DIB_PAL_COLORS;
-
- /* Create the buffer */
- ConsoleBuffers[i] = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
- FILE_SHARE_READ | FILE_SHARE_WRITE,
- NULL,
- CONSOLE_GRAPHICS_BUFFER,
- &GraphicsBufferInfo);
-
- /* Save the framebuffer address */
- ConsoleFramebuffers[i] = GraphicsBufferInfo.lpBitMap;
- }
+ /* Create a new graphics mode buffer */
+ if (!BiosCreateGraphicsBuffer(ModeNumber)) return FALSE;
- /* Free the bitmap information */
- HeapFree(GetProcessHeap(), 0, BitmapInfo);
+ /* Switch to it */
+ SetConsoleActiveScreenBuffer(BiosGraphicsOutput);
}
- /* Set the active page console buffer */
- SetConsoleActiveScreenBuffer(ConsoleBuffers[CurrentVideoPage]);
-
- /* Update the mode number */
+ /* Change the mode number */
CurrentVideoMode = ModeNumber;
+ CurrentVideoPage = 0;
+
+ /* Update the BDA */
+ Bda->VideoMode = CurrentVideoMode;
+ Bda->VideoPage = CurrentVideoPage;
+ Bda->VideoPageSize = BiosGetVideoPageSize();
+ Bda->VideoPageOffset = 0;
+ Bda->ScreenColumns = VideoModes[ModeNumber].Width;
+
+ return TRUE;
+}
+
+BOOLEAN BiosSetVideoPage(BYTE PageNumber)
+{
+ ULONG PageStart;
+ COORD Coordinates;
+ CONSOLE_SCREEN_BUFFER_INFO BufferInfo;
+
+ /* Make sure this is a valid page number */
+ if (PageNumber >= VideoModes[CurrentVideoMode].Pages) return FALSE;
+
+ /* Save the current console buffer in the video memory */
+ PageStart = BiosGetVideoMemoryStart() + CurrentVideoPage * BiosGetVideoPageSize();
+ BiosUpdateVideoMemory(PageStart, PageStart + BiosGetVideoPageSize());
+
+ /* Save the cursor */
+ if (!GetConsoleScreenBufferInfo(BiosConsoleOutput, &BufferInfo)) return FALSE;
+ Bda->CursorPosition[CurrentVideoPage] = MAKEWORD(BufferInfo.dwCursorPosition.X,
+ BufferInfo.dwCursorPosition.Y);
+
+ /* Set the page */
+ CurrentVideoPage = PageNumber;
+
+ /* Update the BDA */
+ Bda->VideoPage = CurrentVideoPage;
+ Bda->VideoPageSize = BiosGetVideoPageSize();
+ Bda->VideoPageOffset = CurrentVideoPage * Bda->VideoPageSize;
+
+ /* Update the console */
+ PageStart = BiosGetVideoMemoryStart() + Bda->VideoPage * BiosGetVideoPageSize();
+ BiosUpdateConsole(PageStart, PageStart + BiosGetVideoPageSize());
+
+ /* Set the cursor */
+ Coordinates.X = LOBYTE(Bda->CursorPosition[Bda->VideoPage]);
+ Coordinates.Y = HIBYTE(Bda->CursorPosition[Bda->VideoPage]);
+ SetConsoleCursorPosition(BiosConsoleOutput, Coordinates);
return TRUE;
}
inline VOID BiosVerticalRefresh()
{
- SMALL_RECT Region;
-
/* Ignore if we're in text mode */
if (VideoModes[CurrentVideoMode].Text) return;
- /* Fill the rectangle structure */
- Region.Left = Region.Top = 0;
- Region.Right = VideoModes[CurrentVideoMode].Width;
- Region.Bottom = VideoModes[CurrentVideoMode].Height;
+ /* Ignore if there's nothing to update */
+ if (!VideoNeedsUpdate) return;
/* Redraw the screen */
- InvalidateConsoleDIBits(ConsoleBuffers[CurrentVideoPage],
- &Region);
+ InvalidateConsoleDIBits(BiosGraphicsOutput, &UpdateRectangle);
+
+ /* Clear the update flag */
+ VideoNeedsUpdate = FALSE;
}
-BOOLEAN BiosInitialize(HANDLE ConsoleInput, HANDLE ConsoleOutput)
+BOOLEAN BiosInitialize()
{
INT i;
WORD Offset = 0;
LPWORD IntVecTable = (LPWORD)((ULONG_PTR)BaseAddress);
LPBYTE BiosCode = (LPBYTE)((ULONG_PTR)BaseAddress + TO_LINEAR(BIOS_SEGMENT, 0));
+ /* Initialize the BDA */
+ Bda = (PBIOS_DATA_AREA)((ULONG_PTR)BaseAddress + TO_LINEAR(BDA_SEGMENT, 0));
+ Bda->EquipmentList = BIOS_EQUIPMENT_LIST;
+ 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;
- if (i != SPECIAL_INT_NUM)
- {
- BiosCode[Offset++] = 0xFA; // cli
+ BiosCode[Offset++] = 0xFA; // cli
- BiosCode[Offset++] = 0x6A; // push i
- BiosCode[Offset++] = (BYTE)i;
+ BiosCode[Offset++] = 0x6A; // push i
+ BiosCode[Offset++] = (BYTE)i;
- BiosCode[Offset++] = 0xCD; // int SPECIAL_INT_NUM
- BiosCode[Offset++] = SPECIAL_INT_NUM;
+ 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++] = 0x83; // add sp, 2
- BiosCode[Offset++] = 0xC4;
- BiosCode[Offset++] = 0x02;
- }
+ BiosCode[Offset++] = 0x83; // add sp, 2
+ BiosCode[Offset++] = 0xC4;
+ BiosCode[Offset++] = 0x02;
BiosCode[Offset++] = 0xCF; // iret
}
- /* Set global console I/O handles */
- BiosConsoleInput = ConsoleInput;
- BiosConsoleOutput = ConsoleOutput;
+ /* Get the input and output handles to the real console */
+ BiosConsoleInput = CreateFile(TEXT("CONIN$"),
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ 0,
+ NULL);
+
+ BiosConsoleOutput = CreateFile(TEXT("CONOUT$"),
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ 0,
+ NULL);
+
+ /* Make sure it was successful */
+ if ((BiosConsoleInput == INVALID_HANDLE_VALUE)
+ || (BiosConsoleOutput == INVALID_HANDLE_VALUE))
+ {
+ return FALSE;
+ }
+
+ /* Save the console screen buffer information */
+ if (!GetConsoleScreenBufferInfo(BiosConsoleOutput, &BiosSavedBufferInfo))
+ {
+ return FALSE;
+ }
+ /* Store the cursor position */
+ Bda->CursorPosition[0] = MAKEWORD(BiosSavedBufferInfo.dwCursorPosition.X,
+ BiosSavedBufferInfo.dwCursorPosition.Y);
+
/* Set the default video mode */
BiosSetVideoMode(BIOS_DEFAULT_VIDEO_MODE);
/* Set the console input mode */
- SetConsoleMode(ConsoleInput, ENABLE_MOUSE_INPUT | ENABLE_PROCESSED_INPUT);
+ SetConsoleMode(BiosConsoleInput, ENABLE_MOUSE_INPUT | ENABLE_PROCESSED_INPUT);
/* Initialize the PIC */
PicWriteCommand(PIC_MASTER_CMD, PIC_ICW1 | PIC_ICW1_ICW4);
return TRUE;
}
+VOID BiosCleanup()
+{
+ /* Restore the old screen buffer */
+ SetConsoleActiveScreenBuffer(BiosConsoleOutput);
+
+ /* Restore the screen buffer size */
+ SetConsoleScreenBufferSize(BiosConsoleOutput, BiosSavedBufferInfo.dwSize);
+
+ /* Free the graphics buffer */
+ if (!VideoModes[CurrentVideoMode].Text) BiosDestroyGraphicsBuffer();
+
+ /* Close the console handles */
+ if (BiosConsoleInput != INVALID_HANDLE_VALUE) CloseHandle(BiosConsoleInput);
+ if (BiosConsoleOutput != INVALID_HANDLE_VALUE) CloseHandle(BiosConsoleOutput);
+}
+
VOID BiosUpdateConsole(ULONG StartAddress, ULONG EndAddress)
{
ULONG i;
/* Get the coordinates */
Coordinates = BiosVideoAddressToCoord(i);
+ /* Make sure this is the current page */
+ if (BiosVideoAddressToPage(i) != CurrentVideoPage) continue;
+
/* Fill the rectangle structure */
Rect.Left = Coordinates.X;
Rect.Top = Coordinates.Y;
}
else
{
+ /* Wait for the mutex object */
+ WaitForSingleObject(ConsoleMutex, INFINITE);
+
/* Copy the data to the framebuffer */
- RtlCopyMemory((LPVOID)((ULONG_PTR)ConsoleFramebuffers[CurrentVideoPage]
+ RtlCopyMemory((LPVOID)((ULONG_PTR)ConsoleFramebuffer
+ StartAddress - BiosGetVideoMemoryStart()),
(LPVOID)((ULONG_PTR)BaseAddress + StartAddress),
EndAddress - StartAddress);
+
+ /* Release the mutex */
+ ReleaseMutex(ConsoleMutex);
+
+ /* Check if this is the first time the rectangle is updated */
+ if (!VideoNeedsUpdate)
+ {
+ UpdateRectangle.Left = UpdateRectangle.Top = (SHORT)0x7FFF;
+ UpdateRectangle.Right = UpdateRectangle.Bottom = (SHORT)0x8000;
+ }
+
+ /* Expand the update rectangle */
+ for (i = StartAddress; i < EndAddress; i++)
+ {
+ /* Get the coordinates */
+ Coordinates = BiosVideoAddressToCoord(i);
+
+ /* Expand the rectangle to include the point */
+ UpdateRectangle.Left = min(UpdateRectangle.Left, Coordinates.X);
+ UpdateRectangle.Right = max(UpdateRectangle.Right, Coordinates.X);
+ UpdateRectangle.Top = min(UpdateRectangle.Top, Coordinates.Y);
+ UpdateRectangle.Bottom = max(UpdateRectangle.Bottom, Coordinates.Y);
+ }
+
+ /* Set the update flag */
+ VideoNeedsUpdate = TRUE;
}
}
/* Get the coordinates */
Coordinates = BiosVideoAddressToCoord(i);
+ /* Make sure this is the current page */
+ if (BiosVideoAddressToPage(i) != CurrentVideoPage) continue;
+
/* Check if this is a character byte or an attribute byte */
- if ((i - (VideoModes[CurrentVideoMode].Segment << 4)) % 2 == 0)
+ if ((i - BiosGetVideoMemoryStart()) % 2 == 0)
{
/* This is a regular character */
ReadConsoleOutputCharacterA(BiosConsoleOutput,
}
else
{
+ /* Wait for the mutex object */
+ WaitForSingleObject(ConsoleMutex, INFINITE);
+
/* Copy the data to the emulator memory */
RtlCopyMemory((LPVOID)((ULONG_PTR)BaseAddress + StartAddress),
- (LPVOID)((ULONG_PTR)ConsoleFramebuffers[CurrentVideoPage]
+ (LPVOID)((ULONG_PTR)ConsoleFramebuffer
+ StartAddress - BiosGetVideoMemoryStart()),
EndAddress - StartAddress);
+
+ /* Release the mutex */
+ ReleaseMutex(ConsoleMutex);
}
}
WORD CharacterData;
/* Check if there is a key available */
- if (BiosKbdBufferEmpty) return 0xFFFF;
+ if (Bda->KeybdBufferHead == Bda->KeybdBufferTail) return 0xFFFF;
/* Get the key from the queue, but don't remove it */
BiosKbdBufferTop(&CharacterData);
DWORD Count;
/* Check if there is a key available */
- if (!BiosKbdBufferEmpty)
+ if (Bda->KeybdBufferHead != Bda->KeybdBufferTail)
{
/* Get the key from the queue, and remove it */
BiosKbdBufferTop(&CharacterData);
VOID BiosVideoService()
{
- INT CursorHeight;
+ INT i, CursorHeight;
BOOLEAN Invisible = FALSE;
COORD Position;
CONSOLE_CURSOR_INFO CursorInfo;
- CONSOLE_SCREEN_BUFFER_INFO ScreenBufferInfo;
CHAR_INFO Character;
SMALL_RECT Rect;
DWORD Eax = EmulatorGetRegister(EMULATOR_REG_AX);
if (CursorHeight < 1) CursorHeight = 1;
if (CursorHeight > 100) CursorHeight = 100;
+ /* Update the BDA */
+ Bda->CursorStartLine = HIBYTE(Ecx);
+ Bda->CursorEndLine = LOBYTE(Ecx) & 0x1F;
+
/* Set the cursor */
CursorInfo.dwSize = (CursorHeight * 100) / CONSOLE_FONT_HEIGHT;
CursorInfo.bVisible = !Invisible;
/* Set Cursor Position */
case 0x02:
{
- Position.X = LOBYTE(Edx);
- Position.Y = HIBYTE(Edx);
+ /* Make sure the selected video page exists */
+ if (HIBYTE(Ebx) >= VideoModes[CurrentVideoMode].Pages) break;
+
+ Bda->CursorPosition[HIBYTE(Ebx)] = LOWORD(Edx);
+
+ /* Check if this is the current video page */
+ if (HIBYTE(Ebx) == CurrentVideoPage)
+ {
+ /* Yes, change the actual cursor */
+ Position.X = LOBYTE(Edx);
+ Position.Y = HIBYTE(Edx);
+ SetConsoleCursorPosition(BiosConsoleOutput, Position);
+ }
- SetConsoleCursorPosition(BiosConsoleOutput, Position);
break;
}
/* Get Cursor Position */
case 0x03:
{
- INT StartLine;
-
- /* Retrieve the data */
- GetConsoleCursorInfo(BiosConsoleOutput, &CursorInfo);
- GetConsoleScreenBufferInfo(BiosConsoleOutput, &ScreenBufferInfo);
-
- /* Find the first line */
- StartLine = 32 - ((CursorInfo.dwSize * 32) / 100);
+ /* Make sure the selected video page exists */
+ if (HIBYTE(Ebx) >= VideoModes[CurrentVideoMode].Pages) break;
/* Return the result */
EmulatorSetRegister(EMULATOR_REG_AX, 0);
- EmulatorSetRegister(EMULATOR_REG_CX, (StartLine << 8) | 0x1F);
- EmulatorSetRegister(EMULATOR_REG_DX,
- LOWORD(ScreenBufferInfo.dwCursorPosition.Y) << 8
- || LOWORD(ScreenBufferInfo.dwCursorPosition.X));
+ EmulatorSetRegister(EMULATOR_REG_CX,
+ (Bda->CursorStartLine << 8) | Bda->CursorEndLine);
+ EmulatorSetRegister(EMULATOR_REG_DX, Bda->CursorPosition[HIBYTE(Ebx)]);
+
+ break;
+ }
+
+ /* Select Active Display Page */
+ case 0x05:
+ {
+ /* Check if the page exists */
+ if (LOBYTE(Eax) >= VideoModes[CurrentVideoMode].Pages) break;
+
+ /* Check if this is the same page */
+ if (LOBYTE(Eax) == CurrentVideoPage) break;
+
+ /* Change the video page */
+ BiosSetVideoPage(LOBYTE(Eax));
+
break;
}
/* Scroll Up/Down Window */
+ // TODO: Implement for different pages
case 0x06:
case 0x07:
{
+ BYTE Lines = LOBYTE(Eax);
+
Rect.Top = HIBYTE(Ecx);
Rect.Left = LOBYTE(Ecx);
Rect.Bottom = HIBYTE(Edx);
Character.Char.UnicodeChar = L' ';
Character.Attributes = HIBYTE(Ebx);
Position.X = Rect.Left;
- if (HIBYTE(Eax) == 0x06) Position.Y = Rect.Top - LOBYTE(Eax);
- else Position.Y = Rect.Top + LOBYTE(Eax);
+
+ /* 0 means clear entire window */
+ if (Lines == 0) Lines = Rect.Bottom - Rect.Top;
+
+ if (HIBYTE(Eax) == 0x06) Position.Y = Rect.Top - Lines;
+ else Position.Y = Rect.Top + Lines;
ScrollConsoleScreenBuffer(BiosConsoleOutput,
&Rect,
&Rect,
Position,
&Character);
+
break;
}
/* Read Character And Attribute At Cursor Position */
case 0x08:
{
- COORD BufferSize = { 1, 1 }, Origin = { 0, 0 };
-
- /* Get the cursor position */
- GetConsoleScreenBufferInfo(BiosConsoleOutput, &ScreenBufferInfo);
-
- /* Read at cursor position */
- Rect.Left = ScreenBufferInfo.dwCursorPosition.X;
- Rect.Top = ScreenBufferInfo.dwCursorPosition.Y;
+ DWORD Address;
- /* Read the console output */
- ReadConsoleOutput(BiosConsoleOutput, &Character, BufferSize, Origin, &Rect);
+ /* Make sure this is text mode */
+ if (!VideoModes[CurrentVideoMode].Text) break;
- /* Return the result */
+ /* Make sure the selected video page exists */
+ if (HIBYTE(Ebx) >= VideoModes[CurrentVideoMode].Pages) break;
+
+ /* Find the address */
+ Address = BiosGetVideoMemoryStart()
+ + HIBYTE(Ebx) * BiosGetVideoPageSize()
+ + (HIBYTE(Bda->CursorPosition[HIBYTE(Ebx)])
+ * VideoModes[CurrentVideoMode].Height
+ + LOBYTE(Bda->CursorPosition[HIBYTE(Ebx)]))
+ * VideoModes[CurrentVideoMode].Bpp / 8;
+
+ /* Update the video memory at that address */
+ BiosUpdateVideoMemory(Address,
+ Address + VideoModes[CurrentVideoMode].Bpp / 8);
+
+ /* Return the result in AX */
EmulatorSetRegister(EMULATOR_REG_AX,
- (LOBYTE(Character.Attributes) << 8)
- | Character.Char.AsciiChar);
+ *((LPWORD)((ULONG_PTR)BaseAddress + Address)));
break;
}
/* Write Character And Attribute At Cursor Position */
case 0x09:
+ case 0x0A:
{
- DWORD CharsWritten;
-
- /* Get the cursor position */
- GetConsoleScreenBufferInfo(BiosConsoleOutput, &ScreenBufferInfo);
-
- /* Write the attribute to the output */
- FillConsoleOutputAttribute(BiosConsoleOutput,
- LOBYTE(Ebx),
- LOWORD(Ecx),
- ScreenBufferInfo.dwCursorPosition,
- &CharsWritten);
+ BYTE PixelSize = VideoModes[CurrentVideoMode].Bpp / 8;
+ WORD Data = (LOBYTE(Ebx) << 8) | LOBYTE(Eax);
+ WORD Repeat = LOWORD(Ecx);
+ DWORD Address = BiosGetVideoMemoryStart()
+ + CurrentVideoPage * BiosGetVideoPageSize()
+ + (HIBYTE(Bda->CursorPosition[CurrentVideoPage])
+ * VideoModes[CurrentVideoMode].Height
+ + LOBYTE(Bda->CursorPosition[CurrentVideoPage]))
+ * PixelSize;
+
+ /* Make sure this is text mode */
+ if (!VideoModes[CurrentVideoMode].Text) break;
+
+ /* Make sure the selected video page exists */
+ if (HIBYTE(Ebx) >= VideoModes[CurrentVideoMode].Pages) break;
+
+ /* Make sure we don't write over the end of video memory */
+ Repeat = min(Repeat,
+ (CONSOLE_VIDEO_MEM_END - Address)
+ / PixelSize);
+
+ /* Copy the values to the memory */
+ for (i = 0; i < Repeat; i++)
+ {
+ if (PixelSize == sizeof(BYTE) || HIBYTE(Eax) == 0x0A)
+ {
+ /* Just characters, no attributes */
+ *((LPBYTE)((ULONG_PTR)BaseAddress + Address) + i * PixelSize) = LOBYTE(Data);
+ }
+ else if (PixelSize == sizeof(WORD))
+ {
+ /* First byte for characters, second for attributes */
+ *((LPWORD)((ULONG_PTR)BaseAddress + Address) + i) = Data;
+ }
+ }
- /* Write the character to the output */
- FillConsoleOutputCharacterA(BiosConsoleOutput,
- LOBYTE(Eax),
- LOWORD(Ecx),
- ScreenBufferInfo.dwCursorPosition,
- &CharsWritten);
+ /* Update the range */
+ BiosUpdateConsole(Address,
+ Address + Repeat * (VideoModes[CurrentVideoMode].Bpp / 8));
break;
}
- /* Write Character Only At Cursor Position */
- case 0x0A:
+ /* Teletype Output */
+ case 0x0E:
{
- DWORD CharsWritten;
+ CHAR Character = LOBYTE(Eax);
+ DWORD NumWritten;
- /* Get the cursor position */
- GetConsoleScreenBufferInfo(BiosConsoleOutput, &ScreenBufferInfo);
+ /* Make sure the page exists */
+ if (HIBYTE(Ebx) >= VideoModes[CurrentVideoMode].Pages) break;
- /* Write the character to the output */
- FillConsoleOutputCharacterA(BiosConsoleOutput,
- LOBYTE(Eax),
- LOWORD(Ecx),
- ScreenBufferInfo.dwCursorPosition,
- &CharsWritten);
+ /* Set the attribute */
+ SetConsoleTextAttribute(BiosConsoleOutput, LOBYTE(Ebx));
+
+ /* Write the character */
+ WriteConsoleA(BiosConsoleOutput,
+ &Character,
+ sizeof(CHAR),
+ &NumWritten,
+ NULL);
break;
}
case 0x0F:
{
EmulatorSetRegister(EMULATOR_REG_AX,
- (VideoModes[CurrentVideoMode].Width << 8)
- | CurrentVideoMode);
+ MAKEWORD(Bda->VideoMode, Bda->ScreenColumns));
EmulatorSetRegister(EMULATOR_REG_BX,
- (CurrentVideoPage << 8) | LOBYTE(Ebx));
+ MAKEWORD(LOBYTE(Ebx), Bda->VideoPage));
+
+ break;
+ }
+
+ /* Scroll Window */
+ case 0x12:
+ {
+ Rect.Top = HIBYTE(Ecx);
+ Rect.Left = LOBYTE(Ecx);
+ Rect.Bottom = HIBYTE(Edx);
+ Rect.Right = LOBYTE(Edx);
+ Character.Char.UnicodeChar = L' ';
+ Character.Attributes = 0x07;
+ Position.X = Rect.Left;
+ Position.Y = Rect.Top;
+
+ if (LOBYTE(Ebx) == 0) Position.Y -= LOBYTE(Eax);
+ else if (LOBYTE(Ebx) == 1) Position.Y += LOBYTE(Eax);
+ else if (LOBYTE(Ebx) == 2) Position.X -= LOBYTE(Eax);
+ else if (LOBYTE(Ebx) == 3) Position.X += LOBYTE(Eax);
+
+ ScrollConsoleScreenBuffer(BiosConsoleOutput,
+ &Rect,
+ &Rect,
+ Position,
+ &Character);
break;
}
{
/* Set AL to 1 if midnight had passed, 0 otherwise */
Eax &= 0xFFFFFF00;
- if (BiosPassedMidnight) Eax |= 1;
+ if (Bda->MidnightPassed) Eax |= 1;
/* Return the tick count in CX:DX */
EmulatorSetRegister(EMULATOR_REG_AX, Eax);
- EmulatorSetRegister(EMULATOR_REG_CX, HIWORD(BiosTickCount));
- EmulatorSetRegister(EMULATOR_REG_DX, LOWORD(BiosTickCount));
+ EmulatorSetRegister(EMULATOR_REG_CX, HIWORD(Bda->TickCounter));
+ EmulatorSetRegister(EMULATOR_REG_DX, LOWORD(Bda->TickCounter));
/* Reset the midnight flag */
- BiosPassedMidnight = FALSE;
+ Bda->MidnightPassed = FALSE;
break;
}
case 0x01:
{
/* Set the tick count to CX:DX */
- BiosTickCount = MAKELONG(LOWORD(Edx), LOWORD(Ecx));
+ Bda->TickCounter = MAKELONG(LOWORD(Edx), LOWORD(Ecx));
/* Reset the midnight flag */
- BiosPassedMidnight = FALSE;
+ Bda->MidnightPassed = FALSE;
break;
}
}
}
+VOID BiosSystemTimerInterrupt()
+{
+ /* Increase the system tick count */
+ Bda->TickCounter++;
+}
+
VOID BiosEquipmentService()
{
/* Return the equipment list */
- EmulatorSetRegister(EMULATOR_REG_AX, BIOS_EQUIPMENT_LIST);
+ EmulatorSetRegister(EMULATOR_REG_AX, Bda->EquipmentList);
}
VOID BiosHandleIrq(BYTE IrqNumber)
/* PIT IRQ */
case 0:
{
- /* Increase the system tick count */
- BiosTickCount++;
-
/* Perform the system timer interrupt */
- EmulatorInterrupt(0x1C);
+ EmulatorInterrupt(BIOS_SYS_TIMER_INTERRUPT);
break;
}
/* Get the scan code and virtual key code */
ScanCode = KeyboardReadData();
- VirtualKey = MapVirtualKey(ScanCode, MAPVK_VSC_TO_VK);
+ VirtualKey = MapVirtualKey(ScanCode & 0x7F, MAPVK_VSC_TO_VK);
/* Check if this is a key press or release */
if (!(ScanCode & (1 << 7)))
BiosKeyboardMap[VirtualKey] |= (1 << 7);
/* Find out which character this is */
- ToAscii(ScanCode, VirtualKey, BiosKeyboardMap, &Character, 0);
-
- /* Push it onto the BIOS keyboard queue */
- BiosKbdBufferPush((ScanCode << 8) | (Character & 0xFF));
+ if (ToAscii(VirtualKey, ScanCode, BiosKeyboardMap, &Character, 0) > 0)
+ {
+ /* Push it onto the BIOS keyboard queue */
+ BiosKbdBufferPush((ScanCode << 8) | (Character & 0xFF));
+ }
}
else
{