Implement the Bios Data Area (BDA).
Fix keyboard character translation bug.


svn path=/branches/ntvdm/; revision=59489
This commit is contained in:
Aleksandar Andrejevic 2013-07-15 01:37:38 +00:00
parent 7ba3013923
commit 40d639e7ff
2 changed files with 140 additions and 58 deletions

View file

@ -16,23 +16,18 @@
/* 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 = INVALID_HANDLE_VALUE;
static HANDLE BiosConsoleOutput = INVALID_HANDLE_VALUE;
static BYTE CurrentVideoMode = BIOS_DEFAULT_VIDEO_MODE;
static BYTE CurrentVideoPage = 0;
static COORD BiosCursorPositions[BIOS_MAX_PAGES];
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 | Bpp | Gray | Pages | Segment */
@ -103,17 +98,25 @@ static COORD BiosVideoAddressToCoord(ULONG Address)
static BOOLEAN BiosKbdBufferPush(WORD Data)
{
/* If it's full, fail */
if (!BiosKbdBufferEmpty && (BiosKbdBufferStart == BiosKbdBufferEnd))
{
return FALSE;
}
/* Get the location of the element after the head */
WORD NextElement = Bda->KeybdBufferHead + 2;
/* Otherwise, add the value to the queue */
BiosKbdBuffer[BiosKbdBufferEnd] = Data;
BiosKbdBufferEnd++;
BiosKbdBufferEnd %= BIOS_KBD_BUFFER_SIZE;
BiosKbdBufferEmpty = FALSE;
/* Wrap it around if it's at or beyond the end */
if (NextElement >= Bda->KeybdBufferEnd) NextElement = Bda->KeybdBufferStart;
/* If it's full, fail */
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 it to the beginning */
Bda->KeybdBufferTail = Bda->KeybdBufferStart;
}
/* Return success */
return TRUE;
@ -122,23 +125,30 @@ static BOOLEAN BiosKbdBufferPush(WORD Data)
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;
/* Otherwise, remove the value and return success */
BiosKbdBufferStart++;
BiosKbdBufferStart %= BIOS_KBD_BUFFER_SIZE;
if (BiosKbdBufferStart == BiosKbdBufferEnd) BiosKbdBufferEmpty = TRUE;
/* 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;
}
@ -271,8 +281,16 @@ BOOLEAN BiosSetVideoMode(BYTE ModeNumber)
SetConsoleActiveScreenBuffer(BiosGraphicsOutput);
}
/* Set the video mode */
/* 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;
}
@ -280,6 +298,7 @@ BOOLEAN BiosSetVideoMode(BYTE ModeNumber)
BOOLEAN BiosSetVideoPage(BYTE PageNumber)
{
ULONG PageStart;
COORD Coordinates;
CONSOLE_SCREEN_BUFFER_INFO BufferInfo;
/* Make sure this is a valid page number */
@ -291,17 +310,25 @@ BOOLEAN BiosSetVideoPage(BYTE PageNumber)
/* Save the cursor */
if (!GetConsoleScreenBufferInfo(BiosConsoleOutput, &BufferInfo)) return FALSE;
BiosCursorPositions[CurrentVideoPage] = BufferInfo.dwCursorPosition;
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() + CurrentVideoPage * BiosGetVideoPageSize();
PageStart = BiosGetVideoMemoryStart() + Bda->VideoPage * BiosGetVideoPageSize();
BiosUpdateConsole(PageStart, PageStart + BiosGetVideoPageSize());
/* Set the cursor */
SetConsoleCursorPosition(BiosConsoleOutput, BiosCursorPositions[PageNumber]);
Coordinates.X = LOBYTE(Bda->CursorPosition[Bda->VideoPage]);
Coordinates.Y = HIBYTE(Bda->CursorPosition[Bda->VideoPage]);
SetConsoleCursorPosition(BiosConsoleOutput, Coordinates);
return TRUE;
}
@ -333,6 +360,12 @@ BOOLEAN BiosInitialize()
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++)
{
@ -388,8 +421,8 @@ BOOLEAN BiosInitialize()
}
/* Store the cursor position */
ZeroMemory(&BiosCursorPositions, sizeof(BiosCursorPositions));
BiosCursorPositions[0] = BiosSavedBufferInfo.dwCursorPosition;
Bda->CursorPosition[0] = MAKEWORD(BiosSavedBufferInfo.dwCursorPosition.X,
BiosSavedBufferInfo.dwCursorPosition.Y);
/* Set the default video mode */
BiosSetVideoMode(BIOS_DEFAULT_VIDEO_MODE);
@ -582,7 +615,7 @@ WORD BiosPeekCharacter()
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);
@ -597,7 +630,7 @@ WORD BiosGetCharacter()
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);
@ -673,15 +706,14 @@ VOID BiosVideoService()
/* Make sure the selected video page exists */
if (HIBYTE(Ebx) >= VideoModes[CurrentVideoMode].Pages) break;
Position.X = LOBYTE(Edx);
Position.Y = HIBYTE(Edx);
BiosCursorPositions[HIBYTE(Ebx)] = Position;
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);
}
@ -705,9 +737,8 @@ VOID BiosVideoService()
/* Return the result */
EmulatorSetRegister(EMULATOR_REG_AX, 0);
EmulatorSetRegister(EMULATOR_REG_CX, (StartLine << 8) | 0x1F);
EmulatorSetRegister(EMULATOR_REG_DX,
(LOBYTE(BiosCursorPositions[HIBYTE(Ebx)].Y) << 8)
| LOBYTE(BiosCursorPositions[HIBYTE(Ebx)].X));
EmulatorSetRegister(EMULATOR_REG_DX, Bda->CursorPosition[HIBYTE(Ebx)]);
break;
}
@ -877,10 +908,9 @@ VOID BiosVideoService()
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;
}
@ -946,15 +976,15 @@ VOID BiosTimeService()
{
/* 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;
}
@ -962,10 +992,10 @@ VOID BiosTimeService()
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;
}
@ -981,13 +1011,13 @@ VOID BiosTimeService()
VOID BiosSystemTimerInterrupt()
{
/* Increase the system tick count */
BiosTickCount++;
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)
@ -1014,7 +1044,7 @@ VOID BiosHandleIrq(BYTE IrqNumber)
/* 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)))
@ -1032,10 +1062,11 @@ VOID BiosHandleIrq(BYTE IrqNumber)
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
{

View file

@ -18,6 +18,7 @@
#define CONSOLE_VIDEO_MEM_END 0xBFFFF
#define ROM_AREA_START 0xC0000
#define ROM_AREA_END 0xFFFFF
#define BDA_SEGMENT 0x40
#define BIOS_PIC_MASTER_INT 0x08
#define BIOS_PIC_SLAVE_INT 0x70
#define BIOS_SEGMENT 0xF000
@ -27,8 +28,8 @@
#define BIOS_TIME_INTERRUPT 0x1A
#define BIOS_SYS_TIMER_INTERRUPT 0x1C
#define CONSOLE_FONT_HEIGHT 8
#define BIOS_KBD_BUFFER_SIZE 256
#define BIOS_EQUIPMENT_LIST 0x3C // HACK: Disable FPU for now
#define BIOS_KBD_BUFFER_SIZE 16
#define BIOS_EQUIPMENT_LIST 0x2C // HACK: Disable FPU for now
#define BIOS_DEFAULT_VIDEO_MODE 0x03
#define BIOS_MAX_PAGES 8
#define BIOS_MAX_VIDEO_MODE 0x13
@ -44,6 +45,56 @@ typedef struct
WORD Segment;
} VIDEO_MODE;
#pragma pack(push, 1)
typedef struct
{
WORD SerialPorts[4];
WORD ParallelPorts[3];
WORD EbdaSegment;
WORD EquipmentList;
BYTE Reserved0;
WORD MemorySize;
WORD Reserved1;
WORD KeyboardFlags;
BYTE AlternateKeypad;
WORD KeybdBufferHead;
WORD KeybdBufferTail;
WORD KeybdBuffer[BIOS_KBD_BUFFER_SIZE];
BYTE DriveRecalibrate;
BYTE DriveMotorStatus;
BYTE MotorShutdownCounter;
BYTE LastDisketteOperation;
BYTE Reserved2[7];
BYTE VideoMode;
WORD ScreenColumns;
WORD VideoPageSize;
WORD VideoPageOffset;
WORD CursorPosition[BIOS_MAX_PAGES];
BYTE CursorEndLine;
BYTE CursorStartLine;
BYTE VideoPage;
WORD CrtBasePort;
BYTE CrtModeControl;
BYTE CrtColorPaletteMask;
DWORD Uptime;
BYTE Reserved3;
DWORD TickCounter;
BYTE MidnightPassed;
BYTE BreakFlag;
WORD SoftReset;
BYTE LastDiskOperation;
BYTE NumDisks;
BYTE DriveControlByte;
BYTE DiskPortOffset;
BYTE LptTimeOut[4];
BYTE ComTimeOut[4];
WORD KeybdBufferStart;
WORD KeybdBufferEnd;
} BIOS_DATA_AREA, *PBIOS_DATA_AREA;
#pragma pack(pop)
/* FUNCTIONS ******************************************************************/
BOOLEAN BiosInitialize();