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

541 lines
14 KiB
C

/*
* 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 ***********************************************************/
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);
}
BOOLEAN MouseBiosInitialize(VOID)
{
return TRUE;
}
VOID MouseBios32Cleanup(VOID)
{
}