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

541 lines
14 KiB
C
Raw Normal View History

/*
* COPYRIGHT: GPL - See COPYING in the top level directory
* PROJECT: ReactOS Virtual DOS Machine
* FILE: subsystems/mvdm/ntvdm/bios/bios32/moubios32.c
* PURPOSE: VDM 32-bit PS/2 Mouse BIOS
* PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
* Hermes Belusca-Maito (hermes.belusca@sfr.fr)
*
* NOTE: Based from VirtualBox OSE ROM BIOS, and SeaBIOS.
*/
/* INCLUDES *******************************************************************/
#include "ntvdm.h"
#define NDEBUG
#include <debug.h>
#include "emulator.h"
#include "cpu/cpu.h" // for EMULATOR_FLAG_CF
#include "moubios32.h"
#include "bios32p.h"
#include "io.h"
#include "hardware/mouse.h"
#include "hardware/ps2.h"
/* PRIVATE VARIABLES **********************************************************/
#define MOUSE_IRQ_INT 0x74
static BOOLEAN MouseEnabled = FALSE;
static DWORD OldIrqHandler;
/*
* Far pointer to a device handler. In compatible PS/2, it is stored in the EBDA.
*
* See Ralf Brown: http://www.ctyme.com/intr/rb-1603.htm
* for more information. In particular:
* when the subroutine is called, it is given 4 WORD values on the stack;
* the handler should return with a FAR return without popping the stack.
*/
static ULONG DeviceHandler = 0;
/* PRIVATE FUNCTIONS **********************************************************/
static VOID DisableMouseInt(VOID)
{
BYTE ControllerConfig;
/* Clear the mouse queue */
while (PS2PortQueueRead(1)) ; // NOTE: Should be a IOReadB! But see r67231
/* Disable mouse interrupt and events */
IOWriteB(PS2_CONTROL_PORT, 0x20);
ControllerConfig = IOReadB(PS2_DATA_PORT);
ControllerConfig &= ~0x02; // Turn off IRQ12
ControllerConfig |= 0x20; // Disable mouse clock line
IOWriteB(PS2_CONTROL_PORT, 0x60);
IOWriteB(PS2_DATA_PORT, ControllerConfig);
}
static VOID EnableMouseInt(VOID)
{
BYTE ControllerConfig;
/* Clear the mouse queue */
while (PS2PortQueueRead(1)) ; // NOTE: Should be a IOReadB! But see r67231
/* Enable mouse interrupt and events */
IOWriteB(PS2_CONTROL_PORT, 0x20);
ControllerConfig = IOReadB(PS2_DATA_PORT);
ControllerConfig |= 0x02; // Turn on IRQ12
ControllerConfig &= ~0x20; // Enable mouse clock line
IOWriteB(PS2_CONTROL_PORT, 0x60);
IOWriteB(PS2_DATA_PORT, ControllerConfig);
}
static inline
VOID SendMouseCommand(UCHAR Command)
{
/* Clear the mouse queue */
while (PS2PortQueueRead(1)) ; // NOTE: Should be a IOReadB! But see r67231
/* Send the command */
IOWriteB(PS2_CONTROL_PORT, 0xD4);
IOWriteB(PS2_DATA_PORT, Command);
}
static inline
UCHAR ReadMouseData(VOID)
{
PS2PortQueueRead(1); // NOTE: Should be a IOReadB! But see r67231
return IOReadB(PS2_DATA_PORT);
}
static
VOID BiosMouseEnable(VOID)
{
if (MouseEnabled) return;
MouseEnabled = TRUE;
/* Get the old IRQ handler */
OldIrqHandler = ((PDWORD)BaseAddress)[MOUSE_IRQ_INT];
/* Set the IRQ handler */
//RegisterInt32(MAKELONG(FIELD_OFFSET(MOUSE_DRIVER, MouseIrqInt16Stub), MouseDataSegment),
// MOUSE_IRQ_INT, DosMouseIrq, NULL);
}
static
VOID BiosMouseDisable(VOID)
{
if (!MouseEnabled) return;
/* Restore the old IRQ handler */
// ((PDWORD)BaseAddress)[MOUSE_IRQ_INT] = OldIrqHandler;
MouseEnabled = FALSE;
}
// Mouse IRQ 12
static VOID WINAPI BiosMouseIrq(LPWORD Stack)
{
DPRINT1("PS/2 Mouse IRQ! DeviceHandler = 0x%04X:0x%04X\n",
HIWORD(DeviceHandler), LOWORD(DeviceHandler));
if (DeviceHandler != 0)
{
/*
* Prepare the stack for the mouse device handler:
* push Status, X and Y data, and a zero word.
*/
setSP(getSP() - sizeof(WORD));
*((LPWORD)SEG_OFF_TO_PTR(getSS(), getSP())) = 0; // Status
setSP(getSP() - sizeof(WORD));
*((LPWORD)SEG_OFF_TO_PTR(getSS(), getSP())) = 0; // X data (high byte = 0)
setSP(getSP() - sizeof(WORD));
*((LPWORD)SEG_OFF_TO_PTR(getSS(), getSP())) = 0; // Y data (high byte = 0)
setSP(getSP() - sizeof(WORD));
*((LPWORD)SEG_OFF_TO_PTR(getSS(), getSP())) = 0; // Zero
/* Call the device handler */
RunCallback16(&BiosContext, DeviceHandler);
/* Pop the stack */
setSP(getSP() + 4*sizeof(WORD));
}
PicIRQComplete(LOBYTE(Stack[STACK_INT_NUM]));
}
VOID BiosMousePs2Interface(LPWORD Stack)
{
/* Disable mouse interrupt and events */
DisableMouseInt();
switch (getAL())
{
/* Enable / Disable */
case 0x00:
{
UCHAR State = getBH();
if (State > 2)
{
/* Invalid function */
setAH(0x01);
Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
break;
}
if (State == 0x00)
{
BiosMouseDisable();
/* Disable packet reporting */
SendMouseCommand(0xF5);
}
else // if (State == 0x01)
{
/* Check for the presence of the device handler */
if (DeviceHandler == 0)
{
/* No device handler installed */
setAH(0x05);
Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
break;
}
BiosMouseEnable();
/* Enable packet reporting */
SendMouseCommand(0xF4);
}
if (ReadMouseData() != MOUSE_ACK)
{
/* Failure */
setAH(0x03);
Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
break;
}
/* Success */
setAH(0x00);
Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
break;
}
/* Initialize */
case 0x05:
{
// Fall through
}
/* Reset */
case 0x01:
{
UCHAR Answer;
SendMouseCommand(0xFF);
Answer = ReadMouseData();
/* A "Resend" signal (0xFE) is sent if no mouse is attached */
if (Answer == 0xFE)
{
/* Resend */
setAH(0x04);
Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
break;
}
else if (Answer != MOUSE_ACK)
{
/* Failure */
setAH(0x03);
Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
break;
}
setBL(ReadMouseData()); // Should be MOUSE_BAT_SUCCESS
setBH(ReadMouseData()); // Mouse ID
/* Success */
setAH(0x00);
Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
break;
}
/* Set Sampling Rate */
case 0x02:
{
UCHAR SampleRate = 0;
switch (getBH())
{
case 0x00: SampleRate = 10; break; // 10 reports/sec
case 0x01: SampleRate = 20; break; // 20 " "
case 0x02: SampleRate = 40; break; // 40 " "
case 0x03: SampleRate = 60; break; // 60 " "
case 0x04: SampleRate = 80; break; // 80 " "
case 0x05: SampleRate = 100; break; // 100 " "
case 0x06: SampleRate = 200; break; // 200 " "
default: SampleRate = 0;
}
if (SampleRate == 0)
{
/* Invalid input */
setAH(0x02);
Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
break;
}
SendMouseCommand(0xF3);
if (ReadMouseData() != MOUSE_ACK)
{
/* Failure */
setAH(0x03);
Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
break;
}
SendMouseCommand(SampleRate);
if (ReadMouseData() != MOUSE_ACK)
{
/* Failure */
setAH(0x03);
Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
break;
}
/* Success */
setAH(0x00);
Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
break;
}
/* Set Resolution */
case 0x03:
{
UCHAR Resolution = getBH();
/*
* 0: 25 dpi, 1 count per millimeter
* 1: 50 dpi, 2 counts per millimeter
* 2: 100 dpi, 4 counts per millimeter
* 3: 200 dpi, 8 counts per millimeter
*/
if (Resolution > 3)
{
/* Invalid input */
setAH(0x02);
Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
break;
}
SendMouseCommand(0xE8);
if (ReadMouseData() != MOUSE_ACK)
{
/* Failure */
setAH(0x03);
Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
break;
}
SendMouseCommand(Resolution);
if (ReadMouseData() != MOUSE_ACK)
{
/* Failure */
setAH(0x03);
Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
break;
}
/* Success */
setAH(0x00);
Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
break;
}
/* Get Type */
case 0x04:
{
SendMouseCommand(0xF2);
if (ReadMouseData() != MOUSE_ACK)
{
/* Failure */
setAH(0x03);
Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
break;
}
setBH(ReadMouseData());
/* Success */
setAH(0x00);
Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
break;
}
/* Extended Commands (Return Status and Set Scaling Factor) */
case 0x06:
{
UCHAR Command = getBH();
switch (Command)
{
/* Return Status */
case 0x00:
{
SendMouseCommand(0xE9);
if (ReadMouseData() != MOUSE_ACK)
{
/* Failure */
setAH(0x03);
Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
break;
}
setBL(ReadMouseData()); // Status
setCL(ReadMouseData()); // Resolution
setDL(ReadMouseData()); // Sample rate
/* Success */
setAH(0x00);
Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
break;
}
/* Set Scaling Factor to 1:1 */
case 0x01:
/* Set Scaling Factor to 2:1 */
case 0x02:
{
SendMouseCommand(Command == 0x01 ? 0xE6 : 0xE7);
if (ReadMouseData() != MOUSE_ACK)
{
/* Failure */
setAH(0x03);
Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
break;
}
/* Success */
setAH(0x00);
Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
break;
}
default:
{
/* Invalid function */
setAH(0x01);
Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
break;
}
}
break;
}
/* Set Device Handler Address */
case 0x07:
{
/* ES:BX == 0000h:0000h removes the device handler */
DeviceHandler = MAKELONG(getBX(), getES());
/* Success */
setAH(0x00);
Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
break;
}
/* Write to Pointer Port */
case 0x08:
{
SendMouseCommand(getBL());
if (ReadMouseData() != MOUSE_ACK)
{
/* Failure */
setAH(0x03);
Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
break;
}
/* Success */
setAH(0x00);
Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
break;
}
/* Read from Pointer Port */
case 0x09:
{
setBL(ReadMouseData());
setCL(ReadMouseData());
setDL(ReadMouseData());
/* Success */
setAH(0x00);
Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
break;
}
default:
{
DPRINT1("INT 15h, AH = C2h, AL = %02Xh NOT IMPLEMENTED\n",
getAL());
/* Unknown function */
setAH(0x01);
Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
}
}
/* Reenable mouse interrupt and events */
EnableMouseInt();
}
/* PUBLIC FUNCTIONS ***********************************************************/
[NTVDM] - Add some level of "Advanced debugging" (see ntvdm.h) which one can adjust to enable/disable debugging features inside NTVDM (this can be useful as long as NTVDM is under heavy bugfixing. When it will be more perfect, this stuff will be removed). - Add the possibility to load option ROMs at a given segment. Currently their list should be specified from inside ntvdm.c (in the BiosInitialize call), but I plan to make it available from a registry option (or via command-line for NTVDM-standalone mode). - Start to separate the initialization of "static" BIOS data (stuff that lives in ROM space) and initialization of "dynamic" BIOS data (eg. initializing the interrupt vector table, the BIOS data area at segment 40h, ...) so that we may be able to reuse part of our code to be able to more-or-less run external (16-bit) BIOSes, or the Windows NTVDM BIOS that uses BOPs to run some of its stuff in ntvdm in 32-bit (as we do for our 32-bit BIOS, except that *all* of our bios is 32-bit, not just some parts). Also, some file reorganization will be in order there soon... - Add video BIOS version information in memory so that tools such as Microsoft Diagnostics can correctly recognize our video BIOS (btw, we try to emulate the functionality of Cirrus' CL-GD5434). - Correctly put video BIOS ROM header (+ checksum) in memory so that it is recognized as such by diagnostics tools. - During BIOS POST, scan for ROMs starting segment 0xC000 (where video ROMs reside). - Store statically the BIOS configuration table. - Fix INT 16h, AH=12h "Get extended shift states" so that it correctly returns the state of right Ctrl and Alt keys. - Fix bit-setting state; report that our keyboard is 101/102 enhanced keyboard. - Correctly set the error return values (AH=86h and CF set) when a function of INT 15h is unsupported. - Implement INT 15h, AH=C9h "Get CPU Type and Mask Revision"; INT 1Ah, AH=02h "Get Real-Time Clock Time" and Ah=04h "Get Real-Time Clock Date" by reading the CMOS. - Implement CMOS century register support. svn path=/trunk/; revision=68598
2015-08-04 20:17:05 +00:00
VOID MouseBios32Post(VOID)
{
UCHAR Answer;
/* Initialize PS/2 mouse port */
// Enable the port
IOWriteB(PS2_CONTROL_PORT, 0xA8);
/* Detect mouse presence by attempting a reset */
SendMouseCommand(0xFF);
Answer = ReadMouseData();
/* A "Resend" signal (0xFE) is sent if no mouse is attached */
if (Answer == 0xFE)
{
DPRINT1("No mouse present!\n");
}
else if (Answer != MOUSE_ACK)
{
DPRINT1("Mouse reset failure!\n");
}
else
{
/* Mouse present, try to completely enable it */
// FIXME: The following is temporary until
// this is moved into the mouse driver!!
/* Enable packet reporting */
SendMouseCommand(0xF4);
if (ReadMouseData() != MOUSE_ACK)
{
DPRINT1("Failed to enable mouse!\n");
}
else
{
/* Enable mouse interrupt and events */
EnableMouseInt();
}
}
/* No mouse driver available so far */
RegisterBiosInt32(0x33, NULL);
/* Set up the HW vector interrupts */
EnableHwIRQ(12, BiosMouseIrq);
[NTVDM] - Add some level of "Advanced debugging" (see ntvdm.h) which one can adjust to enable/disable debugging features inside NTVDM (this can be useful as long as NTVDM is under heavy bugfixing. When it will be more perfect, this stuff will be removed). - Add the possibility to load option ROMs at a given segment. Currently their list should be specified from inside ntvdm.c (in the BiosInitialize call), but I plan to make it available from a registry option (or via command-line for NTVDM-standalone mode). - Start to separate the initialization of "static" BIOS data (stuff that lives in ROM space) and initialization of "dynamic" BIOS data (eg. initializing the interrupt vector table, the BIOS data area at segment 40h, ...) so that we may be able to reuse part of our code to be able to more-or-less run external (16-bit) BIOSes, or the Windows NTVDM BIOS that uses BOPs to run some of its stuff in ntvdm in 32-bit (as we do for our 32-bit BIOS, except that *all* of our bios is 32-bit, not just some parts). Also, some file reorganization will be in order there soon... - Add video BIOS version information in memory so that tools such as Microsoft Diagnostics can correctly recognize our video BIOS (btw, we try to emulate the functionality of Cirrus' CL-GD5434). - Correctly put video BIOS ROM header (+ checksum) in memory so that it is recognized as such by diagnostics tools. - During BIOS POST, scan for ROMs starting segment 0xC000 (where video ROMs reside). - Store statically the BIOS configuration table. - Fix INT 16h, AH=12h "Get extended shift states" so that it correctly returns the state of right Ctrl and Alt keys. - Fix bit-setting state; report that our keyboard is 101/102 enhanced keyboard. - Correctly set the error return values (AH=86h and CF set) when a function of INT 15h is unsupported. - Implement INT 15h, AH=C9h "Get CPU Type and Mask Revision"; INT 1Ah, AH=02h "Get Real-Time Clock Time" and Ah=04h "Get Real-Time Clock Date" by reading the CMOS. - Implement CMOS century register support. svn path=/trunk/; revision=68598
2015-08-04 20:17:05 +00:00
}
[NTVDM] - Add some level of "Advanced debugging" (see ntvdm.h) which one can adjust to enable/disable debugging features inside NTVDM (this can be useful as long as NTVDM is under heavy bugfixing. When it will be more perfect, this stuff will be removed). - Add the possibility to load option ROMs at a given segment. Currently their list should be specified from inside ntvdm.c (in the BiosInitialize call), but I plan to make it available from a registry option (or via command-line for NTVDM-standalone mode). - Start to separate the initialization of "static" BIOS data (stuff that lives in ROM space) and initialization of "dynamic" BIOS data (eg. initializing the interrupt vector table, the BIOS data area at segment 40h, ...) so that we may be able to reuse part of our code to be able to more-or-less run external (16-bit) BIOSes, or the Windows NTVDM BIOS that uses BOPs to run some of its stuff in ntvdm in 32-bit (as we do for our 32-bit BIOS, except that *all* of our bios is 32-bit, not just some parts). Also, some file reorganization will be in order there soon... - Add video BIOS version information in memory so that tools such as Microsoft Diagnostics can correctly recognize our video BIOS (btw, we try to emulate the functionality of Cirrus' CL-GD5434). - Correctly put video BIOS ROM header (+ checksum) in memory so that it is recognized as such by diagnostics tools. - During BIOS POST, scan for ROMs starting segment 0xC000 (where video ROMs reside). - Store statically the BIOS configuration table. - Fix INT 16h, AH=12h "Get extended shift states" so that it correctly returns the state of right Ctrl and Alt keys. - Fix bit-setting state; report that our keyboard is 101/102 enhanced keyboard. - Correctly set the error return values (AH=86h and CF set) when a function of INT 15h is unsupported. - Implement INT 15h, AH=C9h "Get CPU Type and Mask Revision"; INT 1Ah, AH=02h "Get Real-Time Clock Time" and Ah=04h "Get Real-Time Clock Date" by reading the CMOS. - Implement CMOS century register support. svn path=/trunk/; revision=68598
2015-08-04 20:17:05 +00:00
BOOLEAN MouseBiosInitialize(VOID)
{
return TRUE;
}
VOID MouseBios32Cleanup(VOID)
{
}