windows-nt/Source/XPSP1/NT/base/mvdm/v86/scaffold/i386/fakekbd.c
2020-09-26 16:20:57 +08:00

282 lines
6.3 KiB
C

//
// Fake Keyboard rom support
//
// This file provides interrim support for keyboard rom bios services.
// It is only intended for use until Insignia produces proper rom support
// for NTVDM
//
// Note: portions of this code were lifted from the following source.
/* x86 v1.0
*
* XBIOSKBD.C
* Guest ROM BIOS keyboard emulation
*
* History
* Created 20-Oct-90 by Jeff Parsons
*
* COPYRIGHT NOTICE
* This source file may not be distributed, modified or incorporated into
* another product without prior approval from the author, Jeff Parsons.
* This file may be copied to designated servers and machines authorized to
* access those servers, but that does not imply any form of approval.
*/
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <string.h>
#include "softpc.h"
#include "bop.h"
#include "xbios.h"
#include "xbioskbd.h"
#include "xwincon.h"
#include "fun.h"
#include <conapi.h>
extern HANDLE InputHandle;
#define MAX_KBD_BUFFER 256
CHAR KbdBuffer[MAX_KBD_BUFFER];
ULONG Head=0,Tail=0;
HANDLE KbdSyncEvent;
CRITICAL_SECTION csKbd;
CRITICAL_SECTION csConsole;
CRITICAL_SECTION csCtrlc;
BOOL fEventThreadBlock = FALSE;
HANDLE hConsoleWait;
ULONG nCtrlc=0;
HANDLE StdIn;
static BYTE ServiceRoutine[] = { 0xC4, 0xC4, BOP_KBD, 0x50, 0x55, 0x8B,
0xEC, 0x9C, 0x58, 0x89, 0x46, 0x08, 0x5d, 0x58, 0xCF };
#define SERVICE_LENGTH sizeof(ServiceRoutine)
/* BiosKbdInit - Initialize ROM BIOS keyboard support
*
* ENTRY
* argc - # of command-line options
* argv - pointer to first option pointer
* ServiceAddress - linear address to put service routine at
*
* EXIT
* TRUE if successful, FALSE if not
*/
BOOL BiosKbdInit(int argc, char *argv[], PVOID *ServiceAddress)
{
PVOID Address;
argc, argv;
memcpy(*ServiceAddress, ServiceRoutine, SERVICE_LENGTH);
Address = (PVOID)(BIOSINT_KBD * 4);
*((PWORD)Address) = RMOFF(*ServiceAddress);
*(((PWORD)Address) + 1) = RMSEG(*ServiceAddress);
(PCHAR)*ServiceAddress += SERVICE_LENGTH;
StdIn = GetStdHandle(STD_INPUT_HANDLE);
KbdSyncEvent = CreateEvent( NULL, TRUE, FALSE,NULL );
InitializeCriticalSection (&csKbd);
InitializeCriticalSection(&csConsole);
InitializeCriticalSection(&csCtrlc);
hConsoleWait = CreateEvent (NULL,TRUE,FALSE,NULL);
return TRUE;
}
/* BiosKbd - Emulate ROM BIOS keyboard functions
*
* ENTRY
* None (x86 registers contain parameters)
*
* EXIT
* None (x86 registers/memory updated appropriately)
*
* This function receives control on INT 16h, routes control to the
* appropriate subfunction based on the function # in AH, and
* then simulates an IRET and returns back to the instruction emulator.
*/
VOID BiosKbdReadLoop (VOID)
{
ULONG Temp;
while (1) {
Temp = Head + 1;
if(Temp >= MAX_KBD_BUFFER)
Temp =0;
if(Temp == Tail){
Sleep (20);
continue;
}
KbdBuffer[Head] = getche();
EnterCriticalSection(&csConsole);
if(fEventThreadBlock == TRUE){
LeaveCriticalSection(&csConsole);
WaitForSingleObject(hConsoleWait,-1);
ResetEvent(hConsoleWait);
continue;
}
else{
LeaveCriticalSection(&csConsole);
}
EnterCriticalSection(&csKbd);
Head = Temp;
LeaveCriticalSection(&csKbd);
SetEvent(KbdSyncEvent);
}
}
BOOL tkbhit(VOID)
{
if (Tail != Head || nCtrlc)
return TRUE;
return FALSE;
}
CHAR tgetch(VOID)
{
CHAR ch;
while(TRUE) {
EnterCriticalSection(&csCtrlc);
if (nCtrlc){
nCtrlc--;
LeaveCriticalSection(&csCtrlc);
return (CHAR)0x3; // return ctrlc
}
LeaveCriticalSection(&csCtrlc);
if (Tail != Head) {
EnterCriticalSection(&csKbd);
ch = KbdBuffer[Tail++];
if (Tail >= MAX_KBD_BUFFER)
Tail = 0;
LeaveCriticalSection(&csKbd);
return ch;
}
WaitForSingleObject(KbdSyncEvent, -1);
ResetEvent(KbdSyncEvent);
}
}
VOID BiosKbd()
{
static ULONG ulch;
DWORD nRead=0;
switch(getAH()) {
case KBDFUNC_READCHAR:
if (ulch) {
setAL(ulch & 0xff);
setAH(ulch & 0xFF00);
ulch = 0;
}
else {
setAH(0); // zero scan code field for now
setAL((BYTE)tgetch());
if (getAL() == 0 || getAL() == 0xE0) {
setAL(0);
setAH((BYTE)tgetch());
}
}
break;
case KBDFUNC_PEEKCHAR:
setZF(1);
if (ulch) {
setAL(ulch & 0xFF);
setAH(ulch & 0xFF00);
setZF(0);
}
else if(tkbhit()) {
setAH(0); // zero scan code field for now
setAL((BYTE)tgetch());
if (getAL() == 0 || getAL() == 0xE0) {
setAL(0);
setAH((BYTE)tgetch());
}
ulch = getAL() | getAH()<<8 | 0x10000;
setZF(0);
}
break;
}
}
void nt_block_event_thread(void)
{
INPUT_RECORD InputRecord;
DWORD nRecordsWritten;
InputRecord.EventType = 1;
InputRecord.Event.KeyEvent.bKeyDown = 1;
InputRecord.Event.KeyEvent.wRepeatCount = 1;
InputRecord.Event.KeyEvent.wVirtualKeyCode = 32;
InputRecord.Event.KeyEvent.wVirtualScanCode = 41;
InputRecord.Event.KeyEvent.uChar.AsciiChar = ' ';
InputRecord.Event.KeyEvent.dwControlKeyState = 32;
EnterCriticalSection(&csConsole);
WriteConsoleInput(InputHandle,&InputRecord,1,&nRecordsWritten);
InputRecord.EventType = 1;
InputRecord.Event.KeyEvent.bKeyDown = 0;
InputRecord.Event.KeyEvent.wRepeatCount = 1;
InputRecord.Event.KeyEvent.wVirtualKeyCode = 32;
InputRecord.Event.KeyEvent.wVirtualScanCode = 41;
InputRecord.Event.KeyEvent.uChar.AsciiChar = ' ';
InputRecord.Event.KeyEvent.dwControlKeyState = 32;
WriteConsoleInput(InputHandle,&InputRecord,1,&nRecordsWritten);
fEventThreadBlock = TRUE;
LeaveCriticalSection(&csConsole);
return;
}
void nt_resume_event_thread(void)
{
fEventThreadBlock = FALSE;
SetEvent (hConsoleWait);
return;
}
// TEMP Till we have proper multitasking in WOW
extern BOOL VDMForWOW;
extern ULONG iWOWTaskId;
VOID VDMCtrlCHandler(ULONG ulCtrlType)
{
// DebugBreak();
if(ulCtrlType == SYSTEM_ROOT_CONSOLE_EVENT) {
if(VDMForWOW)
// Kill everything for WOW VDM
ExitVDM(VDMForWOW,(ULONG)-1);
else
ExitVDM(FALSE,0);
ExitProcess(0);
return;
}
EnterCriticalSection(&csCtrlc);
nCtrlc++;
LeaveCriticalSection(&csCtrlc);
SetEvent(KbdSyncEvent);
}