reactos/subsystems/mvdm/ntvdm/bios/bios32/kbdbios32.c

386 lines
12 KiB
C

/*
* COPYRIGHT: GPL - See COPYING in the top level directory
* PROJECT: ReactOS Virtual DOS Machine
* FILE: subsystems/mvdm/ntvdm/bios/bios32/kbdbios32.c
* PURPOSE: VDM 32-bit PS/2 Keyboard BIOS
* PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
*/
/* INCLUDES *******************************************************************/
#include "ntvdm.h"
#define NDEBUG
#include <debug.h>
#include "kbdbios32.h"
#include <bios/kbdbios.h>
#include "bios32p.h"
#include "int32.h"
#include "cpu/cpu.h" // for EMULATOR_FLAG_ZF
#include "io.h"
#include "hardware/ps2.h"
/* PRIVATE VARIABLES **********************************************************/
static BYTE BiosKeyboardMap[256];
/* PRIVATE FUNCTIONS **********************************************************/
static BOOLEAN BiosKbdBufferPush(WORD Data)
{
/* Get the location of the element after the tail */
WORD NextElement = Bda->KeybdBufferTail + sizeof(WORD);
/* 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->KeybdBufferHead)
{
DPRINT1("BIOS keyboard buffer full.\n");
return FALSE;
}
/* Put the value in the queue */
*((LPWORD)((ULONG_PTR)Bda + Bda->KeybdBufferTail)) = Data;
Bda->KeybdBufferTail = NextElement;
/* Return success */
return TRUE;
}
static BOOLEAN BiosKbdBufferTop(LPWORD Data)
{
/* If it's empty, fail */
if (Bda->KeybdBufferHead == Bda->KeybdBufferTail) return FALSE;
/* Otherwise, get the value and return success */
*Data = *((LPWORD)((ULONG_PTR)Bda + Bda->KeybdBufferHead));
return TRUE;
}
static BOOLEAN BiosKbdBufferPop(VOID)
{
/* If it's empty, fail */
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 */
VOID WINAPI BiosKeyboardService(LPWORD Stack)
{
switch (getAH())
{
/* Wait for keystroke and read */
case 0x00:
/* Wait for extended keystroke and read */
case 0x10:
{
WORD Character;
/* Read the character (and wait if necessary) */
if (!BiosKbdBufferTop(&Character))
{
/* No key available. Set the handler CF to repeat the BOP */
setCF(1);
break;
}
if (getAH() == 0x00 && LOBYTE(Character) == 0xE0)
{
/* Clear the extended code */
Character &= 0xFF00;
}
BiosKbdBufferPop();
setAX(Character);
setCF(0);
break;
}
/* Get keystroke status */
case 0x01:
/* Get extended keystroke status */
case 0x11:
{
WORD Character;
if (BiosKbdBufferTop(&Character))
{
/* There is a character, clear ZF and return it */
Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_ZF;
if (getAH() == 0x01 && LOBYTE(Character) == 0xE0)
{
/* Clear the extended code */
Character &= 0xFF00;
}
setAX(Character);
}
else
{
/* No character, set ZF */
Stack[STACK_FLAGS] |= EMULATOR_FLAG_ZF;
}
break;
}
/* Get shift status */
case 0x02:
{
/* Return the lower byte of the keyboard shift status word */
setAL(LOBYTE(Bda->KeybdShiftFlags));
break;
}
/* Reserved */
case 0x04:
{
DPRINT1("BIOS Function INT 16h, AH = 0x04 is RESERVED\n");
break;
}
/* Push keystroke */
case 0x05:
{
/* Return 0 if success, 1 if failure */
setAL(BiosKbdBufferPush(getCX()) == FALSE);
break;
}
/* Get extended shift status */
case 0x12:
{
/*
* Be careful! The returned word is similar to 'Bda->KeybdShiftFlags'
* but the high byte is organized differently:
* the bits 2 and 3 of the high byte are not the same:
* instead they correspond to the right CTRL and ALT keys as specified
* in bits 2 and 3 of LOBYTE(Bda->KeybdStatusFlags).
*/
// Bda->KeybdShiftFlags & 0xF3FF;
WORD KeybdShiftFlags = MAKEWORD(LOBYTE(Bda->KeybdShiftFlags),
(HIBYTE(Bda->KeybdShiftFlags ) & 0xF3) |
(LOBYTE(Bda->KeybdStatusFlags) & 0x0C));
/* Return the extended keyboard shift status word */
setAX(KeybdShiftFlags);
break;
}
default:
{
DPRINT1("BIOS Function INT 16h, AH = 0x%02X NOT IMPLEMENTED\n",
getAH());
}
}
}
// Keyboard IRQ 1
/* static */
VOID WINAPI BiosKeyboardIrq(LPWORD Stack)
{
static BOOLEAN Extended = FALSE;
BOOLEAN SkipScanCode;
BYTE ScanCode, VirtualKey;
WORD Character;
/*
* Get the scan code from the PS/2 port, then call the
* INT 15h, AH=4Fh Keyboard Intercept function to try to
* translate the scan code. CF must be set before the call.
* In return, if CF is set we continue processing the scan code
* stored in AL, and if not, we skip it.
*/
WORD AX = getAX();
WORD CX = getCX();
WORD DX = getDX();
WORD BX = getBX();
WORD BP = getBP();
WORD SI = getSI();
WORD DI = getDI();
WORD DS = getDS();
WORD ES = getES();
setCF(1);
setAL(IOReadB(PS2_DATA_PORT));
setAH(0x4F);
Int32Call(&BiosContext, BIOS_MISC_INTERRUPT);
/* Retrieve the modified scan code in AL */
SkipScanCode = (getCF() == 0);
ScanCode = getAL();
setAX(AX);
setCX(CX);
setDX(DX);
setBX(BX);
setBP(BP);
setSI(SI);
setDI(DI);
setDS(DS);
setES(ES);
setCF(0);
if (ScanCode == 0xE0)
{
Extended = TRUE;
Bda->KeybdStatusFlags |= 0x02;
goto Quit;
}
// FIXME: For diagnostic purposes. We should decide what to do then!!
if (ScanCode == 0xE1)
DPRINT1("BiosKeyboardIrq, ScanCode == 0xE1\n");
/* Check whether CF is clear. If so, skip the scan code. */
if (SkipScanCode) goto Quit;
/* Get the corresponding virtual key code */
VirtualKey = MapVirtualKey(ScanCode & 0x7F, MAPVK_VSC_TO_VK);
/* Check if this is a key press or release */
if (!(ScanCode & (1 << 7)))
{
/* Key press, set the highest bit */
BiosKeyboardMap[VirtualKey] |= (1 << 7);
switch (VirtualKey)
{
case VK_NUMLOCK:
case VK_CAPITAL:
case VK_SCROLL:
case VK_INSERT:
{
/* For toggle keys, toggle the lowest bit in the keyboard map */
BiosKeyboardMap[VirtualKey] ^= ~(1 << 0);
break;
}
case VK_SHIFT:
case VK_LSHIFT:
case VK_RSHIFT:
case VK_CONTROL:
case VK_RCONTROL:
case VK_LCONTROL:
case VK_MENU:
case VK_LMENU:
case VK_RMENU:
{
/* Modifier keys don't go in the buffer */
break;
}
default:
{
Character = Extended ? 0xE0 : 0x00;
/* If this is not an extended scancode, and ALT isn't held down, find out which character this is */
if (!Extended && !(Bda->KeybdShiftFlags & (BDA_KBDFLAG_ALT | BDA_KBDFLAG_LALT | BDA_KBDFLAG_RALT)))
{
if (ToAscii(VirtualKey, ScanCode, BiosKeyboardMap, &Character, 0) == 0)
{
/* Not ASCII */
Character = 0;
}
}
/* Push it onto the BIOS keyboard queue */
BiosKbdBufferPush(MAKEWORD(Character, ScanCode));
}
}
}
else
{
/* Key release, unset the highest bit */
BiosKeyboardMap[VirtualKey] &= ~(1 << 7);
}
/* Clear the keyboard flags */
Bda->KeybdShiftFlags = 0;
// Release right CTRL and ALT keys
Bda->KeybdStatusFlags &= ~(BDA_KBDFLAG_RCTRL | BDA_KBDFLAG_RALT);
/* Set the appropriate flags based on the state */
// SHIFT
if (BiosKeyboardMap[VK_RSHIFT] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_RSHIFT;
if (BiosKeyboardMap[VK_LSHIFT] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_LSHIFT;
if (BiosKeyboardMap[VK_SHIFT] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_LSHIFT;
// CTRL
if (BiosKeyboardMap[VK_RCONTROL] & (1 << 7)) Bda->KeybdStatusFlags |= BDA_KBDFLAG_RCTRL;
if (BiosKeyboardMap[VK_LCONTROL] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_LCTRL;
if (BiosKeyboardMap[VK_CONTROL] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_CTRL;
// ALT
if (BiosKeyboardMap[VK_RMENU] & (1 << 7)) Bda->KeybdStatusFlags |= BDA_KBDFLAG_RALT;
if (BiosKeyboardMap[VK_LMENU] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_LALT;
if (BiosKeyboardMap[VK_MENU] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_ALT;
// Others
if (BiosKeyboardMap[VK_SCROLL] & (1 << 0)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_SCROLL_ON;
if (BiosKeyboardMap[VK_NUMLOCK] & (1 << 0)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_NUMLOCK_ON;
if (BiosKeyboardMap[VK_CAPITAL] & (1 << 0)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_CAPSLOCK_ON;
if (BiosKeyboardMap[VK_INSERT] & (1 << 0)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_INSERT_ON;
if (BiosKeyboardMap[VK_SNAPSHOT] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_SYSRQ;
if (BiosKeyboardMap[VK_PAUSE] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_PAUSE;
if (BiosKeyboardMap[VK_SCROLL] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_SCROLL;
if (BiosKeyboardMap[VK_NUMLOCK] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_NUMLOCK;
if (BiosKeyboardMap[VK_CAPITAL] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_CAPSLOCK;
if (BiosKeyboardMap[VK_INSERT] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_INSERT;
/* Clear the extended key flag */
Extended = FALSE;
Bda->KeybdStatusFlags &= ~0x02; // Remove the 0xE0 code flag
// Bda->KeybdStatusFlags &= ~0x01; // Remove the 0xE1 code flag
DPRINT("BiosKeyboardIrq - Character = 0x%X, ScanCode = 0x%X, KeybdShiftFlags = 0x%X\n",
Character, ScanCode, Bda->KeybdShiftFlags);
Quit:
PicIRQComplete(LOBYTE(Stack[STACK_INT_NUM]));
}
/* PUBLIC FUNCTIONS ***********************************************************/
VOID KbdBios32Post(VOID)
{
/* Initialize the BDA */
Bda->KeybdBufferStart = FIELD_OFFSET(BIOS_DATA_AREA, KeybdBuffer);
Bda->KeybdBufferEnd = Bda->KeybdBufferStart + BIOS_KBD_BUFFER_SIZE * sizeof(WORD);
Bda->KeybdBufferHead = Bda->KeybdBufferTail = Bda->KeybdBufferStart;
// FIXME: Fill the keyboard buffer with invalid values for diagnostic purposes...
RtlFillMemory(((LPVOID)((ULONG_PTR)Bda + Bda->KeybdBufferStart)),
BIOS_KBD_BUFFER_SIZE * sizeof(WORD), 'A');
Bda->KeybdShiftFlags = 0;
Bda->KeybdStatusFlags = (1 << 4); // 101/102 enhanced keyboard installed
Bda->KeybdLedFlags = 0;
/*
* Register the BIOS 32-bit Interrupts:
* - Software vector handler
* - HW vector interrupt
*/
RegisterBiosInt32(BIOS_KBD_INTERRUPT, BiosKeyboardService);
EnableHwIRQ(1, BiosKeyboardIrq);
}
/* EOF */