+++ /dev/null
-/*
- * COPYRIGHT: GPL - See COPYING in the top level directory
- * PROJECT: ReactOS Virtual DOS Machine
- * FILE: ps2.c
- * PURPOSE: PS/2 controller emulation
- * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
- * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
- */
-
-/* INCLUDES *******************************************************************/
-
-#define NDEBUG
-
-#include "emulator.h"
-#include "io.h"
-#include "ps2.h"
-#include "pic.h"
-
-/* PRIVATE VARIABLES **********************************************************/
-
-#define BUFFER_SIZE 32
-
-typedef struct _PS2_PORT
-{
- BOOLEAN IsEnabled;
-
- BOOLEAN QueueEmpty;
- BYTE Queue[BUFFER_SIZE];
- UINT QueueStart;
- UINT QueueEnd;
- HANDLE QueueMutex;
-
- LPVOID Param;
- PS2_DEVICE_CMDPROC DeviceCommand;
-} PS2_PORT, *PPS2_PORT;
-
-/*
- * Port 1: Keyboard
- * Port 2: Mouse
- */
-#define PS2_PORTS 2
-static PS2_PORT Ports[PS2_PORTS];
-
-#define PS2_DEFAULT_CONFIG 0x47
-static BYTE ControllerConfig = PS2_DEFAULT_CONFIG;
-static BYTE ControllerCommand = 0x00;
-
-static BYTE StatusRegister = 0x00;
-// static BYTE InputBuffer = 0x00; // PS/2 Input Buffer
-static BYTE OutputBuffer = 0x00; // PS/2 Output Buffer
-
-/* PRIVATE FUNCTIONS **********************************************************/
-
-static VOID PS2SendCommand(PPS2_PORT Port, BYTE Command)
-{
- if (!Port->IsEnabled) return;
-
- /* Call the device command */
- if (Port->DeviceCommand) Port->DeviceCommand(Port->Param, Command);
-}
-
-static BYTE WINAPI PS2ReadPort(USHORT Port)
-{
- if (Port == PS2_CONTROL_PORT)
- {
- /* Be sure bit 2 is always set */
- StatusRegister |= 1 << 2;
-
- // FIXME: Should clear bits 6 and 7 because there are
- // no timeouts and no parity errors.
-
- return StatusRegister;
- }
- else if (Port == PS2_DATA_PORT)
- {
- /*
- * If there is something to read (response byte from the
- * controller or data from a PS/2 device), read it.
- */
- if (StatusRegister & (1 << 0)) // || StatusRegister & (1 << 5) for second PS/2 port
- StatusRegister &= ~(1 << 0); // StatusRegister &= ~(1 << 5);
-
- // FIXME: We may check there whether there is data latched in
- // PS2 ports 1 or 2 (keyboard or mouse) and retrieve it there...
-
- /* Always return the available byte stored in the output buffer */
- return OutputBuffer;
- }
-
- return 0x00;
-}
-
-static VOID WINAPI PS2WritePort(USHORT Port, BYTE Data)
-{
- if (Port == PS2_CONTROL_PORT)
- {
- switch (Data)
- {
- /* Read configuration byte */
- case 0x20:
- {
- OutputBuffer = ControllerConfig;
- StatusRegister |= (1 << 0); // There is something to read
- break;
- }
-
- /* Write configuration byte */
- case 0x60:
- /* Write controller output port */
- case 0xD1:
- /* Write to the first PS/2 port output buffer */
- case 0xD2:
- /* Write to the second PS/2 port output buffer */
- case 0xD3:
- /* Write to the second PS/2 port input buffer */
- case 0xD4:
- {
- /* These commands require a response */
- ControllerCommand = Data;
- StatusRegister |= (1 << 3); // This is a controller command
- break;
- }
-
- /* Disable second PS/2 port */
- case 0xA7:
- {
- Ports[1].IsEnabled = FALSE;
- break;
- }
-
- /* Enable second PS/2 port */
- case 0xA8:
- {
- Ports[1].IsEnabled = TRUE;
- break;
- }
-
- /* Test second PS/2 port */
- case 0xA9:
- {
- OutputBuffer = 0x00; // Success code
- StatusRegister |= (1 << 0); // There is something to read
- break;
- }
-
- /* Test PS/2 controller */
- case 0xAA:
- {
- OutputBuffer = 0x55; // Success code
- StatusRegister |= (1 << 0); // There is something to read
- break;
- }
-
- /* Test first PS/2 port */
- case 0xAB:
- {
- OutputBuffer = 0x00; // Success code
- StatusRegister |= (1 << 0); // There is something to read
- break;
- }
-
- /* Disable first PS/2 port */
- case 0xAD:
- {
- Ports[0].IsEnabled = FALSE;
- break;
- }
-
- /* Enable first PS/2 port */
- case 0xAE:
- {
- Ports[0].IsEnabled = TRUE;
- break;
- }
-
- /* Read controller output port */
- case 0xD0:
- {
- // TODO: Not implemented
- break;
- }
-
- /* CPU Reset */
- case 0xF0:
- case 0xF2:
- case 0xF4:
- case 0xF6:
- case 0xF8:
- case 0xFA:
- case 0xFC:
- case 0xFE:
- {
- /* Stop the VDM */
- EmulatorTerminate();
- break;
- }
- }
- }
- else if (Port == PS2_DATA_PORT)
- {
- /* Check if the controller is waiting for a response */
- if (StatusRegister & (1 << 3)) // If we have data for the controller
- {
- StatusRegister &= ~(1 << 3);
-
- /* Check which command it was */
- switch (ControllerCommand)
- {
- /* Write configuration byte */
- case 0x60:
- {
- ControllerConfig = Data;
- break;
- }
-
- /* Write controller output */
- case 0xD1:
- {
- /* Check if bit 0 is unset */
- if (!(Data & (1 << 0)))
- {
- /* CPU disabled - Stop the VDM */
- EmulatorTerminate();
- }
-
- /* Update the A20 line setting */
- EmulatorSetA20(Data & (1 << 1));
-
- break;
- }
-
- /* Push the data byte into the first PS/2 port queue */
- case 0xD2:
- {
- PS2QueuePush(0, Data);
- break;
- }
-
- /* Push the data byte into the second PS/2 port queue */
- case 0xD3:
- {
- PS2QueuePush(1, Data);
- break;
- }
-
- /*
- * Send a command to the second PS/2 port (by default
- * it is a command for the first PS/2 port)
- */
- case 0xD4:
- {
- PS2SendCommand(&Ports[1], Data);
- break;
- }
- }
-
- return;
- }
-
- /* By default, send a command to the first PS/2 port */
- PS2SendCommand(&Ports[0], Data);
- }
-}
-
-static BOOLEAN PS2PortQueueRead(BYTE PS2Port)
-{
- BOOLEAN Result = TRUE;
- PPS2_PORT Port;
-
- if (PS2Port >= PS2_PORTS) return FALSE;
- Port = &Ports[PS2Port];
-
- if (!Port->IsEnabled) return FALSE;
-
- /* Make sure the queue is not empty (fast check) */
- if (Port->QueueEmpty)
- {
- /* Only the keyboard should have its last data latched */
- // FIXME: Alternatively this can be done in PS2ReadPort when
- // we read PS2_DATA_PORT. What is the best solution??
- if (PS2Port == 0)
- {
- OutputBuffer = Port->Queue[(Port->QueueStart - 1) % BUFFER_SIZE];
- }
-
- return FALSE;
- }
-
- WaitForSingleObject(Port->QueueMutex, INFINITE);
-
- /*
- * Recheck whether the queue is not empty (it may
- * have changed after having grabbed the mutex).
- */
- if (Port->QueueEmpty)
- {
- Result = FALSE;
- goto Done;
- }
-
- /* Get the data */
- OutputBuffer = Port->Queue[Port->QueueStart];
- StatusRegister |= (1 << 0); // There is something to read
- // Sometimes StatusRegister |= (1 << 5); for the second PS/2 port
-
- /* Remove the value from the queue */
- Port->QueueStart++;
- Port->QueueStart %= BUFFER_SIZE;
-
- /* Check if the queue is now empty */
- if (Port->QueueStart == Port->QueueEnd)
- Port->QueueEmpty = TRUE;
-
-Done:
- ReleaseMutex(Port->QueueMutex);
- return Result;
-}
-
-/* PUBLIC FUNCTIONS ***********************************************************/
-
-VOID PS2SetDeviceCmdProc(BYTE PS2Port, LPVOID Param, PS2_DEVICE_CMDPROC DeviceCommand)
-{
- if (PS2Port >= PS2_PORTS) return;
-
- Ports[PS2Port].Param = Param;
- Ports[PS2Port].DeviceCommand = DeviceCommand;
-}
-
-// PS2SendToPort
-BOOLEAN PS2QueuePush(BYTE PS2Port, BYTE Data)
-{
- BOOLEAN Result = TRUE;
- PPS2_PORT Port;
-
- if (PS2Port >= PS2_PORTS) return FALSE;
- Port = &Ports[PS2Port];
-
- if (!Port->IsEnabled) return FALSE;
-
- WaitForSingleObject(Port->QueueMutex, INFINITE);
-
- /* Check if the queue is full */
- if (!Port->QueueEmpty && (Port->QueueStart == Port->QueueEnd))
- {
- Result = FALSE;
- goto Done;
- }
-
- /* Insert the value in the queue */
- Port->Queue[Port->QueueEnd] = Data;
- Port->QueueEnd++;
- Port->QueueEnd %= BUFFER_SIZE;
-
- /* The queue is not empty anymore */
- Port->QueueEmpty = FALSE;
-
-/*
- // Get the data
- OutputBuffer = Port->Queue[Port->QueueStart];
- StatusRegister |= (1 << 0); // There is something to read
- // FIXME: Sometimes StatusRegister |= (1 << 5); for the second PS/2 port
-
- if (PS2Port == 0)
- PicInterruptRequest(1);
- else if (PS2Port == 1)
- PicInterruptRequest(12);
-*/
-
-Done:
- ReleaseMutex(Port->QueueMutex);
- return Result;
-}
-
-VOID GenerateIrq1(VOID)
-{
- /* Generate an interrupt if interrupts for the first PS/2 port are enabled */
- if (ControllerConfig & 0x01)
- {
- /* Generate an IRQ 1 if there is data ready in the output queue */
- if (PS2PortQueueRead(0)) PicInterruptRequest(1);
- }
-}
-
-VOID GenerateIrq12(VOID)
-{
- /* Generate an interrupt if interrupts for the second PS/2 port are enabled */
- if (ControllerConfig & 0x02)
- {
- /* Generate an IRQ 12 if there is data ready in the output queue */
- if (PS2PortQueueRead(1)) PicInterruptRequest(12);
- }
-}
-
-BOOLEAN PS2Initialize(VOID)
-{
- /* Initialize the PS/2 ports */
- Ports[0].IsEnabled = TRUE;
- Ports[0].QueueEmpty = TRUE;
- Ports[0].QueueStart = 0;
- Ports[0].QueueEnd = 0;
- Ports[0].QueueMutex = CreateMutex(NULL, FALSE, NULL);
-
- Ports[1].IsEnabled = TRUE;
- Ports[1].QueueEmpty = TRUE;
- Ports[1].QueueStart = 0;
- Ports[1].QueueEnd = 0;
- Ports[1].QueueMutex = CreateMutex(NULL, FALSE, NULL);
-
- /* Register the I/O Ports */
- RegisterIoPort(PS2_CONTROL_PORT, PS2ReadPort, PS2WritePort);
- RegisterIoPort(PS2_DATA_PORT , PS2ReadPort, PS2WritePort);
-
- return TRUE;
-}
-
-VOID PS2Cleanup(VOID)
-{
- CloseHandle(Ports[1].QueueMutex);
- CloseHandle(Ports[0].QueueMutex);
-}
-
-/* EOF */