/** * We rely on our bootloader setting the A20, but we have code for it just in case. */ void SetupA20() { uint8_t response_byte; /* Do a couple of dummy writes to clear the output buffers. */ PS2ReadData(); PS2ReadData(); /* Disable interrupts. */ PICDisableInterrupts(); /* Disable both PS/2 ports. */ PS2WaitInputBuffer(); PS2SendCommand(PS2_COMMAND_DISABLE_PORT1); PS2WaitInputBuffer(); PS2SendCommand(PS2_COMMAND_DISABLE_PORT2); A20_check: /* Tell the PS/2 controller to send us the config. */ PS2WaitInputBuffer(); PS2SendCommand(PS2_COMMAND_READ_CONFIG); /* Get the status byte. */ response_byte = PS2ReadData(); /* Check if the A20 bit is set. */ if((response_byte & 0x02) != 0x02) { /* A20 is NOT set. Set it and write it. */ PS2WaitOutputBuffer(); PS2SendCommand(PS2_COMMAND_WRITE_CONFIG); PS2WaitOutputBuffer(); PS2SendData(response_byte | 0x02); goto A20_check; } else { /* A20 was set. */ printf("PS/2 A20 line set.\n"); } /* Re-enable both ports. */ PS2WaitInputBuffer(); PS2SendCommand(PS2_COMMAND_ENABLE_PORT1); PS2WaitInputBuffer(); PS2SendCommand(PS2_COMMAND_ENABLE_PORT2); /* And finally re-enable interrupts. */ PICResumeInterrupts(); return; }
void SetupPS2() { char response_byte; /* Do a couple of dummy writes to clear the output buffers. */ PS2ReadData(); PS2ReadData(); /* Initialize USB controllers. */ /* Determine if PS2 exists using ACPI. */ /* Disable devices so they don't send data. */ PS2WaitInputBuffer(); PS2SendCommand(PS2_COMMAND_DISABLE_PORT1); PS2WaitInputBuffer(); PS2SendCommand(PS2_COMMAND_DISABLE_PORT2); /* Flush output buffer. */ PS2ReadStatus(); /* Set the controller configuration byte. */ response_byte = PS2ReadStatus(); /* Perform controller self test. */ PS2WaitInputBuffer(); PS2SendCommand(PS2_COMMAND_TEST_CONTROLLER); PS2WaitOutputBuffer(); if(PS2ReadData() == PS2_RESPONSE_TEST_PASS) { printf("PS/2 controller passed self test.\n"); } else { printf("PS/2 controller did not pass self test.\n"); } /* Determine if there is 2 channels. */ PS2WaitInputBuffer(); PS2SendCommand(PS2_COMMAND_ENABLE_PORT2); PS2WaitInputBuffer(); PS2SendCommand(PS2_COMMAND_READ_CONFIG); response_byte = PS2ReadData(); /* Check if bit 5 of the controller config is set. */ if(BIT_CHECK(response_byte, 5)) { printf("PS/2 controller has no second channel.\n"); ps2_has_second_channel = false; } else { printf("PS/2 controller has a second channel.\n"); ps2_has_second_channel = true; } /* Disable port 2 again to finish startup. */ PS2WaitInputBuffer(); PS2SendCommand(PS2_COMMAND_DISABLE_PORT2); /* Test port 1. */ PS2WaitInputBuffer(); PS2SendCommand(PS2_COMMAND_TEST_PORT1); PS2WaitOutputBuffer(); response_byte = PS2ReadData(); switch(response_byte) { case PS2_RESPONSE_PORT_TEST_PASS: printf("PS/2 port 1 has passed self-test.\n"); break; case PS2_RESPONSE_CLOCK_STUCK_LOW: printf("PS/2 port 1 has stuck low clock.\n"); break; case PS2_RESPONSE_CLOCK_STUCK_HIGH: printf("PS/2 port 1 has stuck high clock.\n"); break; case PS2_RESPONSE_DATA_STUCK_LOW: printf("PS/2 port 1 has stuck low data line.\n"); break; case PS2_RESPONSE_DATA_STUCK_HIGH: printf("PS/2 port 1 has stuck high data line.\n"); break; default: printf("PS/2 port 1 has failed self-test.\n"); break; } /* Test port 2. */ PS2WaitInputBuffer(); PS2SendCommand(PS2_COMMAND_TEST_PORT2); PS2WaitOutputBuffer(); response_byte = PS2ReadData(); switch(response_byte) { case PS2_RESPONSE_PORT_TEST_PASS: printf("PS/2 port 2 has passed self-test.\n"); break; case PS2_RESPONSE_CLOCK_STUCK_LOW: printf("PS/2 port 2 has stuck low clock.\n"); break; case PS2_RESPONSE_CLOCK_STUCK_HIGH: printf("PS/2 port 2 has stuck high clock.\n"); break; case PS2_RESPONSE_DATA_STUCK_LOW: printf("PS/2 port 2 has stuck low data line.\n"); break; case PS2_RESPONSE_DATA_STUCK_HIGH: printf("PS/2 port 2 has stuck high data line.\n"); break; default: printf("PS/2 port 2 has failed self-test.\n"); break; } /* Re-enable device 1. */ PS2WaitInputBuffer(); PS2SendCommand(PS2_COMMAND_ENABLE_PORT1); /* Re-enable device 2. */ PS2WaitInputBuffer(); PS2SendCommand(PS2_COMMAND_ENABLE_PORT2); /* Identify device 1. */ /* Identify device 2. */ }
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); } }