static VOID MouseDispatchPacket(PMOUSE_PACKET Packet) { PS2QueuePush(MousePS2Port, Packet->Flags); PS2QueuePush(MousePS2Port, Packet->HorzCounter); PS2QueuePush(MousePS2Port, Packet->VertCounter); if (MouseId >= 3) PS2QueuePush(MousePS2Port, Packet->Extra); }
VOID KeyboardEventHandler(PKEY_EVENT_RECORD KeyEvent) { WORD i; BYTE ScanCode = (BYTE)KeyEvent->wVirtualScanCode; /* Check if we're not reporting */ if (!Reporting) return; /* If this is a key release, set the highest bit in the scan code */ if (!KeyEvent->bKeyDown) ScanCode |= 0x80; /* Push the scan code into the PS/2 queue */ for (i = 0; i < KeyEvent->wRepeatCount; i++) { if (KeyEvent->dwControlKeyState & ENHANCED_KEY) PS2QueuePush(PS2Port, 0xE0); PS2QueuePush(PS2Port, ScanCode); } DPRINT("Press 0x%X\n", ScanCode); }
static VOID WINAPI PS2WritePort(ULONG 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 VOID WINAPI KeyboardCommand(LPVOID Param, BYTE Command) { /* Check if we were waiting for a data byte */ if (DataByteWait) { PS2QueuePush(PS2Port, KEYBOARD_ACK); switch (DataByteWait) { /* Set/Reset Mode Indicators */ case 0xED: { // Ignore setting the keyboard LEDs break; } /* PS/2 Select/Read Alternate Scan Code Sets */ case 0xF0: /* Set Typematic Rate/Delay */ case 0xF3: { // FIXME: UNIMPLEMENTED; just return ACKnowledge. // This unblocks some programs that want to initialize // the keyboard by sending keyboard commands and then // performing polling on the port until "valid" data // comes out. DPRINT1("KeyboardCommand(0x%02X) NOT IMPLEMENTED\n", DataByteWait); break; } default: { /* Shouldn't happen */ ASSERT(FALSE); } } DataByteWait = 0; return; } switch (Command) { /* Set/Reset Mode Indicators */ case 0xED: /* PS/2 Select/Read Alternate Scan Code Sets */ case 0xF0: /* Set Typematic Rate/Delay */ case 0xF3: { DataByteWait = Command; PS2QueuePush(PS2Port, KEYBOARD_ACK); break; } /* Echo test command */ case 0xEE: { PS2QueuePush(PS2Port, 0xEE); break; } /* Get Keyboard ID */ case 0xF2: { PS2QueuePush(PS2Port, KEYBOARD_ACK); PS2QueuePush(PS2Port, KeyboardId); break; } /* Enable Reporting */ case 0xF4: { Reporting = TRUE; PS2QueuePush(PS2Port, KEYBOARD_ACK); break; } /* Disable Reporting */ case 0xF5: { Reporting = FALSE; PS2QueuePush(PS2Port, KEYBOARD_ACK); break; } /* Set Defaults */ case 0xF6: { // So far, nothing to reset PS2QueuePush(PS2Port, KEYBOARD_ACK); break; } /* PS/2 Typematic & Make/Break key modes */ case 0xF7: case 0xF8: case 0xF9: case 0xFA: case 0xFB: case 0xFC: case 0xFD: { /* * Unsupported on PC-AT, they are just ignored * and acknowledged as discussed in: * http://stanislavs.org/helppc/keyboard_commands.html */ PS2QueuePush(PS2Port, KEYBOARD_ACK); } /* Resend */ case 0xFE: { PS2QueuePush(PS2Port, KEYBOARD_ACK); UNIMPLEMENTED; break; } /* Reset */ case 0xFF: { /* Send ACKnowledge */ PS2QueuePush(PS2Port, KEYBOARD_ACK); // So far, nothing to reset /* Send the Basic Assurance Test success code and the device ID */ PS2QueuePush(PS2Port, KEYBOARD_BAT_SUCCESS); PS2QueuePush(PS2Port, KeyboardId); break; } /* Unknown command */ default: { PS2QueuePush(PS2Port, KEYBOARD_ERROR); } } }
static VOID WINAPI MouseCommand(LPVOID Param, BYTE Command) { /* Check if we were waiting for a data byte */ if (MouseDataByteWait) { PS2QueuePush(MousePS2Port, MOUSE_ACK); switch (MouseDataByteWait) { /* Set Resolution */ case 0xE8: { Resolution = Command; break; } /* Set Sample Rate */ case 0xF3: { /* Check for the scroll wheel enabling sequence */ if (MouseId == 0) { if (Command == ScrollMagic[ScrollMagicCounter]) { ScrollMagicCounter++; if (ScrollMagicCounter == 3) MouseId = 3; } else { ScrollMagicCounter = 0; } } /* Check for the 5-button enabling sequence */ if (MouseId == 3) { if (Command == ExtraButtonMagic[ExtraButtonMagicCounter]) { ExtraButtonMagicCounter++; if (ExtraButtonMagicCounter == 3) MouseId = 4; } else { ExtraButtonMagicCounter = 0; } } MouseCycles = 1000 / (UINT)Command; break; } default: { /* Shouldn't happen */ ASSERT(FALSE); } } MouseDataByteWait = 0; return; } /* Check if we're in wrap mode */ if (Mode == MOUSE_WRAP_MODE) { /* * In this mode, we just echo whatever byte we get, * except for the 0xEC and 0xFF commands. */ if (Command != 0xEC && Command != 0xFF) { PS2QueuePush(MousePS2Port, Command); return; } } switch (Command) { /* Set 1:1 Scaling */ case 0xE6: { Scaling = FALSE; PS2QueuePush(MousePS2Port, MOUSE_ACK); break; } /* Set 2:1 Scaling */ case 0xE7: { Scaling = TRUE; PS2QueuePush(MousePS2Port, MOUSE_ACK); break; } /* Set Resolution */ case 0xE8: /* Set Sample Rate */ case 0xF3: { MouseDataByteWait = Command; PS2QueuePush(MousePS2Port, MOUSE_ACK); break; } /* Read Status */ case 0xE9: { BYTE Status = ButtonState & 7; if (Scaling) Status |= 1 << 4; if (MouseReporting) Status |= 1 << 5; if (Mode == MOUSE_REMOTE_MODE) Status |= 1 << 6; PS2QueuePush(MousePS2Port, MOUSE_ACK); PS2QueuePush(MousePS2Port, Status); PS2QueuePush(MousePS2Port, Resolution); PS2QueuePush(MousePS2Port, (BYTE)(1000 / MouseCycles)); break; } /* Enter Streaming Mode */ case 0xEA: { MouseResetCounters(); Mode = MOUSE_STREAMING_MODE; PS2QueuePush(MousePS2Port, MOUSE_ACK); break; } /* Read Packet */ case 0xEB: { PS2QueuePush(MousePS2Port, MOUSE_ACK); MouseGetPacket(&LastPacket); MouseDispatchPacket(&LastPacket); break; } /* Return from Wrap Mode */ case 0xEC: { if (Mode == MOUSE_WRAP_MODE) { /* Restore the previous mode */ MouseResetCounters(); Mode = PreviousMode; PS2QueuePush(MousePS2Port, MOUSE_ACK); } else { PS2QueuePush(MousePS2Port, MOUSE_ERROR); } break; } /* Enter Wrap Mode */ case 0xEE: { if (Mode != MOUSE_WRAP_MODE) { /* Save the previous mode */ PreviousMode = Mode; } MouseResetCounters(); Mode = MOUSE_WRAP_MODE; PS2QueuePush(MousePS2Port, MOUSE_ACK); break; } /* Enter Remote Mode */ case 0xF0: { MouseResetCounters(); Mode = MOUSE_REMOTE_MODE; PS2QueuePush(MousePS2Port, MOUSE_ACK); break; } /* Get Mouse ID */ case 0xF2: { PS2QueuePush(MousePS2Port, MOUSE_ACK); PS2QueuePush(MousePS2Port, MouseId); break; } /* Enable Reporting */ case 0xF4: { MouseReporting = TRUE; MouseResetCounters(); PS2QueuePush(MousePS2Port, MOUSE_ACK); break; } /* Disable Reporting */ case 0xF5: { MouseReporting = FALSE; MouseResetCounters(); PS2QueuePush(MousePS2Port, MOUSE_ACK); break; } /* Set Defaults */ case 0xF6: { /* Reset the configuration and counters */ MouseResetConfig(); MouseResetCounters(); PS2QueuePush(MousePS2Port, MOUSE_ACK); break; } /* Resend */ case 0xFE: { PS2QueuePush(MousePS2Port, MOUSE_ACK); MouseDispatchPacket(&LastPacket); break; } /* Reset */ case 0xFF: { /* Send ACKnowledge */ PS2QueuePush(MousePS2Port, MOUSE_ACK); MouseReset(); /* Send the Basic Assurance Test success code and the device ID */ PS2QueuePush(MousePS2Port, MOUSE_BAT_SUCCESS); PS2QueuePush(MousePS2Port, MouseId); break; } /* Unknown command */ default: { PS2QueuePush(MousePS2Port, MOUSE_ERROR); } } }