Some cleanup, moved PS2 code to drivers folder in the shared sources.
This commit is contained in:
@@ -33,6 +33,10 @@ extern const unsigned {symbol}_SIZE;
|
||||
env.Command([f'{res}.s', f'{res}.hpp'.replace('src/', 'include/')], res, action = _generate_s)
|
||||
env.AddMethod(_resource, 'Resource')
|
||||
|
||||
irs_sources = Split('''
|
||||
src/drivers/pic.cpp
|
||||
src/drivers/ps2.cpp
|
||||
''')
|
||||
any_target_sources = Split('''
|
||||
src/app/main.cpp
|
||||
|
||||
@@ -51,5 +55,6 @@ any_target_sources = Split('''
|
||||
env.Resource(env.File('src/os/resources/lat9-08.psf').abspath, 'LAT9_08')
|
||||
|
||||
env.Append(KERNEL_SOURCES = [env.File(f) for f in any_target_sources])
|
||||
env.Append(KERNEL_ISR_SOURCES = [env.File(f) for f in irs_sources])
|
||||
|
||||
Return('env')
|
||||
41
targets/_any/include/drivers/pic.hpp
Normal file
41
targets/_any/include/drivers/pic.hpp
Normal file
@@ -0,0 +1,41 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(BAD_APPLE_OS_KERNEL_PIC_HPP_INCLUDED)
|
||||
#define BAD_APPLE_OS_KERNEL_PIC_HPP_INCLUDED
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "os/port.hpp"
|
||||
|
||||
inline constexpr std::uint16_t PORT_PIC1 = 0x20;
|
||||
inline constexpr std::uint16_t PORT_PIC2 = 0xA0;
|
||||
inline constexpr std::uint8_t PIC_CMD_EOI = 0x20;
|
||||
|
||||
[[nodiscard]] bool initPICs(std::uint8_t masterOffset, std::uint8_t slaveOffset) noexcept;
|
||||
__attribute__((no_caller_saved_registers))
|
||||
void sendEndOfInterrupt(std::uint8_t irq) noexcept;
|
||||
void maskIRQ(std::uint8_t irq) noexcept;
|
||||
void unmaskIRQ(std::uint8_t irq) noexcept;
|
||||
[[nodiscard]] std::uint16_t readPICIRR() noexcept;
|
||||
[[nodiscard]] std::uint16_t readPICISR() noexcept;
|
||||
|
||||
[[nodiscard]] constexpr bool isValidPICOffset(std::uint8_t offset) noexcept
|
||||
{
|
||||
return (offset & 7) == 0;
|
||||
}
|
||||
|
||||
#if defined(__x86_64__)
|
||||
struct InterruptFrame
|
||||
{
|
||||
std::uint64_t ip;
|
||||
std::uint64_t cs;
|
||||
std::uint64_t flags;
|
||||
std::uint64_t sp;
|
||||
std::uint64_t ss;
|
||||
};
|
||||
#endif
|
||||
|
||||
using interrupt_handler_t = __attribute__((interrupt)) void(*)(InterruptFrame*);
|
||||
|
||||
#endif // !defined(BAD_APPLE_OS_KERNEL_PIC_HPP_INCLUDED)
|
||||
159
targets/_any/include/drivers/ps2.hpp
Normal file
159
targets/_any/include/drivers/ps2.hpp
Normal file
@@ -0,0 +1,159 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(BAD_APPLE_OS_DRIVERS_PS2_HPP_INCLUDED)
|
||||
#define BAD_APPLE_OS_DRIVERS_PS2_HPP_INCLUDED
|
||||
|
||||
#include "drivers/pic.hpp"
|
||||
|
||||
namespace baos::ps2
|
||||
{
|
||||
enum class Scancode
|
||||
{
|
||||
INVALID = 0,
|
||||
|
||||
F9 = 0x01,
|
||||
F5 = 0x03,
|
||||
F3 = 0x04,
|
||||
F1 = 0x05,
|
||||
F2 = 0x06,
|
||||
F12 = 0x07,
|
||||
F10 = 0x09,
|
||||
F8 = 0x0A,
|
||||
F6 = 0x0B,
|
||||
F4 = 0x0C,
|
||||
TAB = 0x0D,
|
||||
BACKTICK = 0x0E,
|
||||
LEFT_ALT = 0x11,
|
||||
LEFT_SHIFT = 0x12,
|
||||
LEFT_CONTROL = 0x14,
|
||||
Q = 0x15,
|
||||
_1 = 0x16,
|
||||
Z = 0x1A,
|
||||
S = 0x1B,
|
||||
A = 0x1C,
|
||||
W = 0x1D,
|
||||
_2 = 0x1E,
|
||||
C = 0x21,
|
||||
X = 0x22,
|
||||
D = 0x23,
|
||||
E = 0x24,
|
||||
_4 = 0x25,
|
||||
_3 = 0x26,
|
||||
SPACE = 0x29,
|
||||
V = 0x2A,
|
||||
F = 0x2B,
|
||||
T = 0x2C,
|
||||
R = 0x2D,
|
||||
_5 = 0x2E,
|
||||
N = 0x31,
|
||||
B = 0x32,
|
||||
H = 0x33,
|
||||
G = 0x34,
|
||||
Y = 0x35,
|
||||
_6 = 0x36,
|
||||
M = 0x3A,
|
||||
J = 0x3B,
|
||||
U = 0x3C,
|
||||
_7 = 0x3D,
|
||||
_8 = 0x3E,
|
||||
COMMA = 0x41,
|
||||
K = 0x42,
|
||||
I = 0x43,
|
||||
O = 0x44,
|
||||
_0 = 0x45,
|
||||
_9 = 0x46,
|
||||
DOT = 0x49,
|
||||
SLASH = 0x4A,
|
||||
L = 0x4B,
|
||||
SEMICOLON = 0x4C,
|
||||
P = 0x4D,
|
||||
MINUS = 0x4E,
|
||||
APOSTROPHE = 0x52,
|
||||
BRACKET_OPEN = 0x54,
|
||||
EQUALS = 0x55,
|
||||
CAPSLOCK = 0x58,
|
||||
RIGHT_SHIFT = 0x59,
|
||||
ENTER = 0x5A,
|
||||
BRACKET_CLOSE = 0x5B,
|
||||
BACKSLASH = 0x5D,
|
||||
BACKSPACE = 0x66,
|
||||
KP_1 = 0x69,
|
||||
KP_4 = 0x6B,
|
||||
KP_7 = 0x6C,
|
||||
KP_0 = 0x70,
|
||||
KP_DOT = 0x71,
|
||||
KP_2 = 0x72,
|
||||
KP_5 = 0x73,
|
||||
KP_6 = 0x74,
|
||||
KP_8 = 0x75,
|
||||
ESCAPE = 0x76,
|
||||
NUMLOCK = 0x77,
|
||||
F11 = 0x78,
|
||||
KP_PLUS = 0x79,
|
||||
KP_3 = 0x7A,
|
||||
KP_MINUS = 0x7B,
|
||||
KP_ASTERISK = 0x7C,
|
||||
KP_9 = 0x7D,
|
||||
SCROLLOCK = 0x7E,
|
||||
F7 = 0x83,
|
||||
|
||||
MM_WWW_SEARCH = 0xE010,
|
||||
RIGHT_ALT = 0xE011,
|
||||
RIGHT_CONTROL = 0xE014,
|
||||
MM_PREV_TRACK = 0xE015,
|
||||
LEFT_GUI = 0xE01F,
|
||||
MM_WWW_FAVORITES = 0xE020,
|
||||
MM_VOLUME_DOWN = 0xE021,
|
||||
MM_MUTE = 0xE023,
|
||||
RIGHT_GUI = 0xE027,
|
||||
MM_WWW_STOP = 0xE028,
|
||||
MM_CALCULATOR = 0xE02B,
|
||||
APPS = 0xE02F,
|
||||
MM_WWW_FORWARD = 0xE030,
|
||||
MM_VOLUME_UP = 0xE032,
|
||||
MM_PLAY_PAUSE = 0xE034,
|
||||
ACPI_POWER = 0xE037,
|
||||
MM_WWW_BACK = 0xE038,
|
||||
MM_WWW_HOME = 0xE03A,
|
||||
MM_STOP = 0xE03B,
|
||||
ACPI_SLEEP = 0xE03F,
|
||||
MM_MY_COMPUTER = 0xE040,
|
||||
MM_EMAIL = 0xE048,
|
||||
KP_SLASH = 0xE04A,
|
||||
MM_NEXT_TRACK = 0xE04D,
|
||||
MM_MEDIA_SELECT = 0xE050,
|
||||
KP_ENTER = 0xE05A,
|
||||
ACPI_WAKE = 0xE05E,
|
||||
END = 0xE069,
|
||||
ARROW_LEFT = 0xE06B,
|
||||
HOME = 0xE06C,
|
||||
INSERT = 0xE070,
|
||||
DELETE = 0xE071,
|
||||
ARROW_DOWN = 0xE072,
|
||||
ARROW_RIGHT = 0xE074,
|
||||
ARROW_UP = 0xE075,
|
||||
PAGE_DOWN = 0xE07A,
|
||||
PAGE_UP = 0xE07D,
|
||||
|
||||
PRINT_SCREEN = 0xF000,
|
||||
PAUSE = 0xF001
|
||||
};
|
||||
|
||||
struct KeyEvent
|
||||
{
|
||||
Scancode scancode = Scancode::INVALID;
|
||||
bool down : 1 = false;
|
||||
bool repeat : 1 = false;
|
||||
};
|
||||
|
||||
[[nodiscard]] bool initialize() noexcept;
|
||||
|
||||
__attribute__((interrupt))
|
||||
void isrKeyboard(InterruptFrame* interruptFrame) noexcept;
|
||||
|
||||
[[nodiscard]] bool readKey(KeyEvent& outEvent) noexcept;
|
||||
[[nodiscard]] const char* keyName(Scancode scancode) noexcept;
|
||||
}
|
||||
|
||||
#endif // !defined(BAD_APPLE_OS_DRIVERS_PS2_HPP_INCLUDED)
|
||||
121
targets/_any/src/drivers/pic.cpp
Normal file
121
targets/_any/src/drivers/pic.cpp
Normal file
@@ -0,0 +1,121 @@
|
||||
|
||||
#include "drivers/pic.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
inline constexpr std::uint8_t PIC_CMD_INIT = 0x10;
|
||||
inline constexpr std::uint8_t PIC_CMD_READ_IRR = 0x0a;
|
||||
inline constexpr std::uint8_t PIC_CMD_READ_ISR = 0x0b;
|
||||
inline constexpr std::uint8_t PIC_INIT_WITH_ICW4 = 0x01;
|
||||
inline constexpr std::uint8_t PIC_CFG_8086 = 0x01;
|
||||
|
||||
void picWriteCmd(std::uint16_t port, std::uint8_t command) noexcept
|
||||
{
|
||||
writePortByte(port, command);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::uint8_t picReadResponse(std::uint8_t port) noexcept
|
||||
{
|
||||
return readPortByte(port);
|
||||
}
|
||||
|
||||
void picWriteData(std::uint16_t port, std::uint8_t data) noexcept
|
||||
{
|
||||
writePortByte(port + 1, data);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::uint8_t picReadData(std::uint16_t port) noexcept
|
||||
{
|
||||
return readPortByte(port + 1);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::uint16_t readPICIRQRegisters(std::uint8_t readCommand) noexcept
|
||||
{
|
||||
picWriteCmd(PORT_PIC1, readCommand);
|
||||
picWriteCmd(PORT_PIC2, readCommand);
|
||||
return (picReadResponse(PORT_PIC1) << 8) | picReadResponse(PORT_PIC2);
|
||||
}
|
||||
}
|
||||
|
||||
bool initPICs(std::uint8_t masterOffset, std::uint8_t slaveOffset) noexcept
|
||||
{
|
||||
if (!isValidPICOffset(masterOffset) || !isValidPICOffset(slaveOffset))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint8_t mask1 = picReadData(PORT_PIC1);
|
||||
const uint8_t mask2 = picReadData(PORT_PIC2);
|
||||
|
||||
// ICW1 - init command
|
||||
picWriteCmd(PORT_PIC1, PIC_CMD_INIT | PIC_INIT_WITH_ICW4);
|
||||
ioWait();
|
||||
picWriteCmd(PORT_PIC2, PIC_CMD_INIT | PIC_INIT_WITH_ICW4);
|
||||
ioWait();
|
||||
|
||||
// ICW2 - offset
|
||||
picWriteData(PORT_PIC1, masterOffset);
|
||||
ioWait();
|
||||
picWriteData(PORT_PIC2, slaveOffset);
|
||||
ioWait();
|
||||
|
||||
// ICW3 - master/slave config
|
||||
picWriteData(PORT_PIC1, (1 << 2)); // there is a slave at IRQ2
|
||||
ioWait();
|
||||
picWriteData(PORT_PIC2, 2); // you are slave no. 2
|
||||
ioWait();
|
||||
|
||||
// ICW3 - additional config
|
||||
picWriteData(PORT_PIC1, PIC_CFG_8086);
|
||||
ioWait();
|
||||
picWriteData(PORT_PIC2, PIC_CFG_8086);
|
||||
ioWait();
|
||||
|
||||
// restore masks
|
||||
picWriteData(PORT_PIC1, mask1);
|
||||
picWriteData(PORT_PIC2, mask2);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void sendEndOfInterrupt(std::uint8_t irq) noexcept
|
||||
{
|
||||
if (irq >= 8) {
|
||||
writePortByte(PORT_PIC2, PIC_CMD_EOI);
|
||||
}
|
||||
writePortByte(PORT_PIC1, PIC_CMD_EOI);
|
||||
}
|
||||
|
||||
void maskIRQ(std::uint8_t irq) noexcept
|
||||
{
|
||||
std::uint16_t port = PORT_PIC1;
|
||||
if (irq >= 8)
|
||||
{
|
||||
port = PORT_PIC2;
|
||||
irq -= 8;
|
||||
}
|
||||
const std::uint8_t currentMask = picReadData(port);
|
||||
picWriteData(port, currentMask | (1 << irq));
|
||||
}
|
||||
|
||||
void unmaskIRQ(std::uint8_t irq) noexcept
|
||||
{
|
||||
std::uint16_t port = PORT_PIC1;
|
||||
if (irq >= 8)
|
||||
{
|
||||
port = PORT_PIC2;
|
||||
irq -= 8;
|
||||
}
|
||||
const std::uint8_t currentMask = picReadData(port);
|
||||
picWriteData(port, currentMask & ~(1 << irq));
|
||||
}
|
||||
|
||||
std::uint16_t readPICIRR() noexcept
|
||||
{
|
||||
return readPICIRQRegisters(PIC_CMD_READ_IRR);
|
||||
}
|
||||
|
||||
std::uint16_t readPICISR() noexcept
|
||||
{
|
||||
return readPICIRQRegisters(PIC_CMD_READ_ISR);
|
||||
}
|
||||
564
targets/_any/src/drivers/ps2.cpp
Normal file
564
targets/_any/src/drivers/ps2.cpp
Normal file
@@ -0,0 +1,564 @@
|
||||
|
||||
#include "drivers/ps2.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include "drivers/pic.hpp"
|
||||
#include "os/port.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
inline constexpr std::uint16_t PORT_PS2_DATA = 0x60;
|
||||
inline constexpr std::uint16_t PORT_PS2_STATUS = 0x64;
|
||||
inline constexpr std::uint8_t FLAG_PS2_OUTPUT_FULL = (1 << 0);
|
||||
inline constexpr std::uint8_t FLAG_PS2_INPUT_FULL = (1 << 1);
|
||||
inline constexpr std::uint8_t PS2_CMD_DISABLE_FIRST = 0xAD;
|
||||
inline constexpr std::uint8_t PS2_CMD_ENABLE_FIRST = 0xAE;
|
||||
inline constexpr std::uint8_t PS2_CMD_DISABLE_SECOND = 0xA7;
|
||||
inline constexpr std::uint8_t PS2_CMD_ENABLE_SECOND = 0xA8;
|
||||
inline constexpr std::uint8_t PS2_CMD_READ_CONF_BYTE = 0x20;
|
||||
inline constexpr std::uint8_t PS2_CMD_WRITE_CONF_BYTE = 0x60;
|
||||
inline constexpr std::uint8_t PS2_CMD_SELFTEST = 0xAA;
|
||||
inline constexpr std::uint8_t PS2_CMD_PORTTEST_FIRST = 0xAB;
|
||||
inline constexpr std::uint8_t PS2_CMD_PORTTEST_SECOND = 0xA9;
|
||||
inline constexpr std::uint8_t PS2_CMD_WRITE_TO_SECOND = 0xD4;
|
||||
inline constexpr std::uint8_t PS2_DEVICE_CMD_RESET = 0xFF;
|
||||
inline constexpr std::uint8_t PS2_RESPONSE_ACK = 0xFA;
|
||||
inline constexpr std::uint8_t PS2_RESPONSE_RESEND = 0xFE;
|
||||
inline constexpr std::uint8_t PS2_RESPONSE_SELFTEST_PASSED = 0x55;
|
||||
inline constexpr std::uint8_t PS2_RESPONSE_SELFTEST_FAILED = 0xFC;
|
||||
inline constexpr std::uint8_t PS2_RESPONSE_PORTTEST_PASSED = 0x00;
|
||||
inline constexpr std::uint8_t PS2_CONFIGFLAG_IRQ_ENABLE_FIRST = (1 << 0);
|
||||
inline constexpr std::uint8_t PS2_CONFIGFLAG_IRQ_ENABLE_SECOND = (1 << 1);
|
||||
inline constexpr std::uint8_t PS2_CONFIGFLAG_CLOCK_FIRST = (1 << 4);
|
||||
inline constexpr std::uint8_t PS2_CONFIGFLAG_CLOCK_SECOND = (1 << 5);
|
||||
inline constexpr std::uint8_t PS2_CONFIGFLAG_TRANSLATION = (1 << 6);
|
||||
|
||||
inline constexpr unsigned KEY_BUFFER_LENGTH = 16;
|
||||
std::uint8_t gKeyboardBuffer[KEY_BUFFER_LENGTH];
|
||||
unsigned gBufferedKeys = 0;
|
||||
unsigned gKeyPos = 0;
|
||||
|
||||
__attribute__((no_caller_saved_registers))
|
||||
void pushKey(std::uint8_t data) noexcept
|
||||
{
|
||||
if (gBufferedKeys == KEY_BUFFER_LENGTH) {
|
||||
return;
|
||||
}
|
||||
gKeyboardBuffer[gKeyPos] = data;
|
||||
++gBufferedKeys;
|
||||
gKeyPos = (gKeyPos + 1) % KEY_BUFFER_LENGTH;
|
||||
}
|
||||
}
|
||||
|
||||
namespace baos::ps2
|
||||
{
|
||||
inline bool waitForResponse(unsigned tries = -1) noexcept
|
||||
{
|
||||
for (unsigned t = 0; t < tries; ++t)
|
||||
{
|
||||
if (readPortByte(PORT_PS2_STATUS) & FLAG_PS2_OUTPUT_FULL) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void waitForInputFree() noexcept
|
||||
{
|
||||
while (readPortByte(PORT_PS2_STATUS) & FLAG_PS2_INPUT_FULL);
|
||||
}
|
||||
|
||||
inline std::uint8_t cmdWithResponse(std::uint8_t cmd) noexcept
|
||||
{
|
||||
writePortByte(PORT_PS2_STATUS, cmd);
|
||||
waitForResponse();
|
||||
return readPortByte(PORT_PS2_DATA);
|
||||
}
|
||||
|
||||
inline void cmdWithData(std::uint8_t cmd, std::uint8_t data) noexcept
|
||||
{
|
||||
writePortByte(PORT_PS2_STATUS, cmd);
|
||||
waitForInputFree();
|
||||
writePortByte(PORT_PS2_DATA, data);
|
||||
}
|
||||
|
||||
inline void sendToFirstDevice(std::uint8_t cmd) noexcept
|
||||
{
|
||||
waitForInputFree();
|
||||
writePortByte(PORT_PS2_DATA, cmd);
|
||||
}
|
||||
|
||||
inline void sendToSecondDevice(std::uint8_t cmd) noexcept
|
||||
{
|
||||
cmdWithData(PS2_CMD_WRITE_TO_SECOND, cmd);
|
||||
waitForResponse();
|
||||
}
|
||||
|
||||
bool initialize() noexcept
|
||||
{
|
||||
// disable PS2
|
||||
writePortByte(PORT_PS2_STATUS, PS2_CMD_DISABLE_FIRST);
|
||||
writePortByte(PORT_PS2_STATUS, PS2_CMD_DISABLE_SECOND);
|
||||
|
||||
// flush output register
|
||||
readPortByte(PORT_PS2_DATA);
|
||||
|
||||
// read configuration
|
||||
std::uint8_t config = cmdWithResponse(PS2_CMD_READ_CONF_BYTE);
|
||||
|
||||
// adjust configuration
|
||||
const bool secondDevice = (config & PS2_CONFIGFLAG_CLOCK_SECOND) != 0;
|
||||
config &= ~PS2_CONFIGFLAG_IRQ_ENABLE_FIRST;
|
||||
config &= ~PS2_CONFIGFLAG_IRQ_ENABLE_SECOND;
|
||||
config &= ~PS2_CONFIGFLAG_TRANSLATION;
|
||||
|
||||
// write configuration
|
||||
cmdWithData(PS2_CMD_WRITE_CONF_BYTE, config);
|
||||
|
||||
// run self-test
|
||||
const std::uint8_t testResult = cmdWithResponse(PS2_CMD_SELFTEST);
|
||||
if (testResult != PS2_RESPONSE_SELFTEST_PASSED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// write configuration again, in case it was reset
|
||||
cmdWithData(PS2_CMD_WRITE_CONF_BYTE, config);
|
||||
|
||||
// check if dual-channel
|
||||
bool dualChannel = false;
|
||||
if ((config & PS2_CONFIGFLAG_CLOCK_SECOND) != 0) // it may be
|
||||
{
|
||||
writePortByte(PORT_PS2_STATUS, PS2_CMD_ENABLE_SECOND);
|
||||
dualChannel = (cmdWithResponse(PS2_CMD_READ_CONF_BYTE) & PS2_CONFIGFLAG_CLOCK_SECOND) == 0;
|
||||
if (dualChannel)
|
||||
{
|
||||
writePortByte(PORT_PS2_STATUS, PS2_CMD_DISABLE_SECOND);
|
||||
}
|
||||
}
|
||||
|
||||
// test interfaces
|
||||
if (cmdWithResponse(PS2_CMD_PORTTEST_FIRST) != PS2_RESPONSE_PORTTEST_PASSED)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (dualChannel && cmdWithResponse(PS2_CMD_PORTTEST_SECOND) != PS2_RESPONSE_PORTTEST_PASSED)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// enable devices
|
||||
writePortByte(PORT_PS2_STATUS, PS2_CMD_ENABLE_FIRST);
|
||||
if (dualChannel) {
|
||||
writePortByte(PORT_PS2_STATUS, PS2_CMD_ENABLE_SECOND);
|
||||
}
|
||||
|
||||
// enable interrupts
|
||||
config |= PS2_CONFIGFLAG_IRQ_ENABLE_FIRST;
|
||||
if (dualChannel) {
|
||||
config |= PS2_CONFIGFLAG_IRQ_ENABLE_SECOND;
|
||||
}
|
||||
cmdWithData(PS2_CMD_WRITE_CONF_BYTE, config);
|
||||
|
||||
// reset
|
||||
sendToFirstDevice(PS2_DEVICE_CMD_RESET);
|
||||
bool firstOk = false;
|
||||
while (waitForResponse(10))
|
||||
{
|
||||
const std::uint8_t response = readPortByte(PORT_PS2_DATA);
|
||||
if (response == PS2_RESPONSE_SELFTEST_FAILED) {
|
||||
return false;
|
||||
}
|
||||
firstOk = true;
|
||||
}
|
||||
if (!firstOk) {
|
||||
return false;
|
||||
}
|
||||
if (dualChannel)
|
||||
{
|
||||
sendToSecondDevice(PS2_DEVICE_CMD_RESET);
|
||||
bool secondOk = false;
|
||||
while (waitForResponse(10))
|
||||
{
|
||||
const std::uint8_t response = readPortByte(PORT_PS2_DATA);
|
||||
if (response == PS2_RESPONSE_SELFTEST_FAILED) {
|
||||
return false;
|
||||
}
|
||||
secondOk = true;
|
||||
}
|
||||
if (!secondOk) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
__attribute__((interrupt))
|
||||
void isrKeyboard(InterruptFrame* interruptFrame) noexcept
|
||||
{
|
||||
(void) interruptFrame;
|
||||
const std::uint8_t data = readPortByte(PORT_PS2_DATA);
|
||||
if (data != 0)
|
||||
{
|
||||
pushKey(data);
|
||||
}
|
||||
sendEndOfInterrupt(1);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool readRaw(std::uint8_t& outData) noexcept
|
||||
{
|
||||
maskIRQ(1);
|
||||
bool result = false;
|
||||
if (gBufferedKeys > 0)
|
||||
{
|
||||
outData = gKeyboardBuffer[(gKeyPos + KEY_BUFFER_LENGTH - gBufferedKeys) % KEY_BUFFER_LENGTH];
|
||||
--gBufferedKeys;
|
||||
result = true;
|
||||
}
|
||||
unmaskIRQ(1);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool gNextIsRelease = false;
|
||||
bool gNextIsExtended = false;
|
||||
bool gNextIsMoreExtended = false;
|
||||
bool gNextIsExtendedest = false;
|
||||
bool gPrevWasExtendedest = false;
|
||||
|
||||
bool readKey(KeyEvent& outEvent) noexcept
|
||||
{
|
||||
if (gPrevWasExtendedest)
|
||||
{
|
||||
gPrevWasExtendedest = gNextIsExtended = gNextIsMoreExtended = gNextIsExtendedest = false;
|
||||
outEvent.scancode = Scancode::PAUSE;
|
||||
outEvent.down = false;
|
||||
outEvent.repeat = false;
|
||||
return true;
|
||||
}
|
||||
std::uint8_t data = 0;
|
||||
if (!readRaw(data)) {
|
||||
return false;
|
||||
}
|
||||
if (gNextIsExtendedest)
|
||||
{
|
||||
if (data == 0x77)
|
||||
{
|
||||
// second 0x77, our most special key is done
|
||||
if (gNextIsExtended)
|
||||
{
|
||||
gPrevWasExtendedest = true; // the next scan will return the key up event
|
||||
outEvent.scancode = Scancode::PAUSE;
|
||||
outEvent.down = true;
|
||||
outEvent.repeat = false;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// first 0x77 in sequence, wait for the second one
|
||||
gNextIsExtended = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false; // ignore everything else until we finished the most special key
|
||||
}
|
||||
|
||||
switch (data)
|
||||
{
|
||||
case 0xF0:
|
||||
gNextIsRelease = true;
|
||||
return false;
|
||||
case 0xE0:
|
||||
gNextIsExtended = true;
|
||||
return false;
|
||||
case 0x12:
|
||||
if (gNextIsExtended)
|
||||
{
|
||||
gNextIsMoreExtended = true;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 0xE1:
|
||||
gNextIsExtendedest = true;
|
||||
return false;
|
||||
default: break;
|
||||
}
|
||||
|
||||
std::uint16_t scancode = data;
|
||||
if (gNextIsExtended)
|
||||
{
|
||||
if (gNextIsMoreExtended)
|
||||
{
|
||||
scancode = static_cast<std::uint16_t>(Scancode::PRINT_SCREEN);
|
||||
}
|
||||
else
|
||||
{
|
||||
scancode |= 0xE000;
|
||||
}
|
||||
}
|
||||
outEvent.scancode = static_cast<Scancode>(scancode);
|
||||
outEvent.down = !gNextIsRelease;
|
||||
outEvent.repeat = false; // TODO
|
||||
|
||||
// reset state
|
||||
gNextIsExtendedest = gNextIsMoreExtended = gNextIsRelease = gNextIsExtended = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* keyName(Scancode scancode) noexcept
|
||||
{
|
||||
switch (scancode)
|
||||
{
|
||||
case Scancode::INVALID:
|
||||
default:
|
||||
return "INVALID";
|
||||
case Scancode::F9:
|
||||
return "F9";
|
||||
case Scancode::F5:
|
||||
return "F5";
|
||||
case Scancode::F3:
|
||||
return "F3";
|
||||
case Scancode::F1:
|
||||
return "F1";
|
||||
case Scancode::F2:
|
||||
return "F2";
|
||||
case Scancode::F12:
|
||||
return "F12";
|
||||
case Scancode::F10:
|
||||
return "F10";
|
||||
case Scancode::F8:
|
||||
return "F8";
|
||||
case Scancode::F6:
|
||||
return "F6";
|
||||
case Scancode::F4:
|
||||
return "F4";
|
||||
case Scancode::TAB:
|
||||
return "TAB";
|
||||
case Scancode::BACKTICK:
|
||||
return "BACKTICK";
|
||||
case Scancode::LEFT_ALT:
|
||||
return "LEFT_ALT";
|
||||
case Scancode::LEFT_SHIFT:
|
||||
return "LEFT_SHIFT";
|
||||
case Scancode::LEFT_CONTROL:
|
||||
return "LEFT_CONTROL";
|
||||
case Scancode::Q:
|
||||
return "Q";
|
||||
case Scancode::_1:
|
||||
return "_1";
|
||||
case Scancode::Z:
|
||||
return "Z";
|
||||
case Scancode::S:
|
||||
return "S";
|
||||
case Scancode::A:
|
||||
return "A";
|
||||
case Scancode::W:
|
||||
return "W";
|
||||
case Scancode::_2:
|
||||
return "_2";
|
||||
case Scancode::C:
|
||||
return "C";
|
||||
case Scancode::X:
|
||||
return "X";
|
||||
case Scancode::D:
|
||||
return "D";
|
||||
case Scancode::E:
|
||||
return "E";
|
||||
case Scancode::_4:
|
||||
return "_4";
|
||||
case Scancode::_3:
|
||||
return "_3";
|
||||
case Scancode::SPACE:
|
||||
return "SPACE";
|
||||
case Scancode::V:
|
||||
return "V";
|
||||
case Scancode::F:
|
||||
return "F";
|
||||
case Scancode::T:
|
||||
return "T";
|
||||
case Scancode::R:
|
||||
return "R";
|
||||
case Scancode::_5:
|
||||
return "_5";
|
||||
case Scancode::N:
|
||||
return "N";
|
||||
case Scancode::B:
|
||||
return "B";
|
||||
case Scancode::H:
|
||||
return "H";
|
||||
case Scancode::G:
|
||||
return "G";
|
||||
case Scancode::Y:
|
||||
return "Y";
|
||||
case Scancode::_6:
|
||||
return "_6";
|
||||
case Scancode::M:
|
||||
return "M";
|
||||
case Scancode::J:
|
||||
return "J";
|
||||
case Scancode::U:
|
||||
return "U";
|
||||
case Scancode::_7:
|
||||
return "_7";
|
||||
case Scancode::_8:
|
||||
return "_8";
|
||||
case Scancode::COMMA:
|
||||
return "COMMA";
|
||||
case Scancode::K:
|
||||
return "K";
|
||||
case Scancode::I:
|
||||
return "I";
|
||||
case Scancode::O:
|
||||
return "O";
|
||||
case Scancode::_0:
|
||||
return "_0";
|
||||
case Scancode::_9:
|
||||
return "_9";
|
||||
case Scancode::DOT:
|
||||
return "DOT";
|
||||
case Scancode::SLASH:
|
||||
return "SLASH";
|
||||
case Scancode::L:
|
||||
return "L";
|
||||
case Scancode::SEMICOLON:
|
||||
return "SEMICOLON";
|
||||
case Scancode::P:
|
||||
return "P";
|
||||
case Scancode::MINUS:
|
||||
return "MINUS";
|
||||
case Scancode::APOSTROPHE:
|
||||
return "APOSTROPHE";
|
||||
case Scancode::BRACKET_OPEN:
|
||||
return "BRACKET_OPEN";
|
||||
case Scancode::EQUALS:
|
||||
return "EQUALS";
|
||||
case Scancode::CAPSLOCK:
|
||||
return "CAPSLOCK";
|
||||
case Scancode::RIGHT_SHIFT:
|
||||
return "RIGHT_SHIFT";
|
||||
case Scancode::ENTER:
|
||||
return "ENTER";
|
||||
case Scancode::BRACKET_CLOSE:
|
||||
return "BRACKET_CLOSE";
|
||||
case Scancode::BACKSLASH:
|
||||
return "BACKSLASH";
|
||||
case Scancode::BACKSPACE:
|
||||
return "BACKSPACE";
|
||||
case Scancode::KP_1:
|
||||
return "KP_1";
|
||||
case Scancode::KP_4:
|
||||
return "KP_4";
|
||||
case Scancode::KP_7:
|
||||
return "KP_7";
|
||||
case Scancode::KP_0:
|
||||
return "KP_0";
|
||||
case Scancode::KP_DOT:
|
||||
return "KP_DOT";
|
||||
case Scancode::KP_2:
|
||||
return "KP_2";
|
||||
case Scancode::KP_5:
|
||||
return "KP_5";
|
||||
case Scancode::KP_6:
|
||||
return "KP_6";
|
||||
case Scancode::KP_8:
|
||||
return "KP_8";
|
||||
case Scancode::ESCAPE:
|
||||
return "ESCAPE";
|
||||
case Scancode::NUMLOCK:
|
||||
return "NUMLOCK";
|
||||
case Scancode::F11:
|
||||
return "F11";
|
||||
case Scancode::KP_PLUS:
|
||||
return "KP_PLUS";
|
||||
case Scancode::KP_3:
|
||||
return "KP_3";
|
||||
case Scancode::KP_MINUS:
|
||||
return "KP_MINUS";
|
||||
case Scancode::KP_ASTERISK:
|
||||
return "KP_ASTERISK";
|
||||
case Scancode::KP_9:
|
||||
return "KP_9";
|
||||
case Scancode::SCROLLOCK:
|
||||
return "SCROLLOCK";
|
||||
case Scancode::F7:
|
||||
return "F7";
|
||||
case Scancode::MM_WWW_SEARCH:
|
||||
return "MM_WWW_SEARCH";
|
||||
case Scancode::RIGHT_ALT:
|
||||
return "RIGHT_ALT";
|
||||
case Scancode::RIGHT_CONTROL:
|
||||
return "RIGHT_CONTROL";
|
||||
case Scancode::MM_PREV_TRACK:
|
||||
return "MM_PREV_TRACK";
|
||||
case Scancode::LEFT_GUI:
|
||||
return "LEFT_GUI";
|
||||
case Scancode::MM_WWW_FAVORITES:
|
||||
return "MM_WWW_FAVORITES";
|
||||
case Scancode::MM_VOLUME_DOWN:
|
||||
return "MM_VOLUME_DOWN";
|
||||
case Scancode::MM_MUTE:
|
||||
return "MM_MUTE";
|
||||
case Scancode::RIGHT_GUI:
|
||||
return "RIGHT_GUI";
|
||||
case Scancode::MM_WWW_STOP:
|
||||
return "MM_WWW_STOP";
|
||||
case Scancode::MM_CALCULATOR:
|
||||
return "MM_CALCULATOR";
|
||||
case Scancode::APPS:
|
||||
return "APPS";
|
||||
case Scancode::MM_WWW_FORWARD:
|
||||
return "MM_WWW_FORWARD";
|
||||
case Scancode::MM_VOLUME_UP:
|
||||
return "MM_VOLUME_UP";
|
||||
case Scancode::MM_PLAY_PAUSE:
|
||||
return "MM_PLAY_PAUSE";
|
||||
case Scancode::ACPI_POWER:
|
||||
return "ACPI_POWER";
|
||||
case Scancode::MM_WWW_BACK:
|
||||
return "MM_WWW_BACK";
|
||||
case Scancode::MM_WWW_HOME:
|
||||
return "MM_WWW_HOME";
|
||||
case Scancode::MM_STOP:
|
||||
return "MM_STOP";
|
||||
case Scancode::ACPI_SLEEP:
|
||||
return "ACPI_SLEEP";
|
||||
case Scancode::MM_MY_COMPUTER:
|
||||
return "MM_MY_COMPUTER";
|
||||
case Scancode::MM_EMAIL:
|
||||
return "MM_EMAIL";
|
||||
case Scancode::KP_SLASH:
|
||||
return "KP_SLASH";
|
||||
case Scancode::MM_NEXT_TRACK:
|
||||
return "MM_NEXT_TRACK";
|
||||
case Scancode::MM_MEDIA_SELECT:
|
||||
return "MM_MEDIA_SELECT";
|
||||
case Scancode::KP_ENTER:
|
||||
return "KP_ENTER";
|
||||
case Scancode::ACPI_WAKE:
|
||||
return "ACPI_WAKE";
|
||||
case Scancode::END:
|
||||
return "END";
|
||||
case Scancode::ARROW_LEFT:
|
||||
return "ARROW_LEFT";
|
||||
case Scancode::HOME:
|
||||
return "HOME";
|
||||
case Scancode::INSERT:
|
||||
return "INSERT";
|
||||
case Scancode::DELETE:
|
||||
return "DELETE";
|
||||
case Scancode::ARROW_DOWN:
|
||||
return "ARROW_DOWN";
|
||||
case Scancode::ARROW_RIGHT:
|
||||
return "ARROW_RIGHT";
|
||||
case Scancode::ARROW_UP:
|
||||
return "ARROW_UP";
|
||||
case Scancode::PAGE_DOWN:
|
||||
return "PAGE_DOWN";
|
||||
case Scancode::PAGE_UP:
|
||||
return "PAGE_UP";
|
||||
case Scancode::PRINT_SCREEN:
|
||||
return "PRINT_SCREEN";
|
||||
case Scancode::PAUSE:
|
||||
return "PAUSE";
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user