Added PIC initialization and keyboard.
This commit is contained in:
parent
f03963a485
commit
04d25d498d
36
targets/_any/include/os/port.hpp
Normal file
36
targets/_any/include/os/port.hpp
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if !defined(BAD_APPLE_OS_PORT_HPP_INCLUDED)
|
||||||
|
#define BAD_APPLE_OS_PORT_HPP_INCLUDED
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#define writePortByte(port, value) \
|
||||||
|
{ \
|
||||||
|
__asm__ __volatile__( \
|
||||||
|
"outb %0, %1" \
|
||||||
|
: \
|
||||||
|
: "a"(static_cast<std::uint8_t>(value)), \
|
||||||
|
"Nd"(static_cast<std::uint16_t>(port)) \
|
||||||
|
); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define readPortByte(port) \
|
||||||
|
[&]() \
|
||||||
|
{ \
|
||||||
|
std::uint8_t value = 0; \
|
||||||
|
__asm__ __volatile__( \
|
||||||
|
"inb %1, %0" \
|
||||||
|
: "=a"(value) \
|
||||||
|
: "Nd"(static_cast<std::uint16_t>(port)) \
|
||||||
|
); \
|
||||||
|
return value; \
|
||||||
|
}()
|
||||||
|
|
||||||
|
inline void ioWait() noexcept
|
||||||
|
{
|
||||||
|
writePortByte(0x80, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // !defined(BAD_APPLE_OS_PORT_HPP_INCLUDED)
|
@ -6,6 +6,8 @@
|
|||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "./port.hpp"
|
||||||
|
|
||||||
inline constexpr std::uint16_t PORT_COM1 = 0x3F8;
|
inline constexpr std::uint16_t PORT_COM1 = 0x3F8;
|
||||||
inline constexpr std::uint16_t PORT_COM2 = 0x2F8;
|
inline constexpr std::uint16_t PORT_COM2 = 0x2F8;
|
||||||
inline constexpr std::uint16_t PORT_COM3 = 0x3E8;
|
inline constexpr std::uint16_t PORT_COM3 = 0x3E8;
|
||||||
@ -15,26 +17,6 @@ inline constexpr std::uint16_t PORT_COM6 = 0x4F8;
|
|||||||
inline constexpr std::uint16_t PORT_COM7 = 0x5E8;
|
inline constexpr std::uint16_t PORT_COM7 = 0x5E8;
|
||||||
inline constexpr std::uint16_t PORT_COM8 = 0x4E8;
|
inline constexpr std::uint16_t PORT_COM8 = 0x4E8;
|
||||||
|
|
||||||
inline void writePortByte(std::uint16_t port, std::uint8_t value) noexcept
|
|
||||||
{
|
|
||||||
__asm__ __volatile__(
|
|
||||||
"outb %0, %1"
|
|
||||||
:
|
|
||||||
: "a"(value), "Nd"(port)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::uint8_t readPortByte(std::uint16_t port) noexcept
|
|
||||||
{
|
|
||||||
std::uint8_t value = 0;
|
|
||||||
__asm__ __volatile__(
|
|
||||||
"inb %1, %0"
|
|
||||||
: "=a"(value)
|
|
||||||
: "Nd"(port)
|
|
||||||
);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] inline bool initSerialPort(std::uint16_t port) noexcept
|
[[nodiscard]] inline bool initSerialPort(std::uint16_t port) noexcept
|
||||||
{
|
{
|
||||||
writePortByte(port + 1, 0x00); // Disable all interrupts
|
writePortByte(port + 1, 0x00); // Disable all interrupts
|
||||||
|
@ -27,12 +27,16 @@ crti_o = kernel_env.Object('src/crt/crti.s')
|
|||||||
crtn_o = kernel_env.Object('src/crt/crtn.s')
|
crtn_o = kernel_env.Object('src/crt/crtn.s')
|
||||||
kernel_env['LINKCOM'] = env['LINKCOM'].replace('$_LIBFLAGS', f'{crti_o[0].abspath} {crtbegin_o} $_LIBFLAGS -lgcc {crtend_o} {crtn_o[0].abspath}')
|
kernel_env['LINKCOM'] = env['LINKCOM'].replace('$_LIBFLAGS', f'{crti_o[0].abspath} {crtbegin_o} $_LIBFLAGS -lgcc {crtend_o} {crtn_o[0].abspath}')
|
||||||
|
|
||||||
|
kernel_irs_sources = Split('''
|
||||||
|
src/kernel/keyboard.cpp
|
||||||
|
src/kernel/pic.cpp
|
||||||
|
''')
|
||||||
kernel_sources = env['KERNEL_SOURCES'] + Split('''
|
kernel_sources = env['KERNEL_SOURCES'] + Split('''
|
||||||
src/cstdlib/memory.s
|
src/cstdlib/memory.s
|
||||||
|
|
||||||
src/kernel/boot.s
|
src/kernel/boot.s
|
||||||
src/kernel/startup.cpp
|
src/kernel/startup.cpp
|
||||||
''')
|
''') + [kernel_env.Object(f, CCFLAGS = kernel_env['CCFLAGS'] + ['-mgeneral-regs-only']) for f in kernel_irs_sources]
|
||||||
|
|
||||||
kernel_target = kernel_env.File('#kernel.x86_64.bin')
|
kernel_target = kernel_env.File('#kernel.x86_64.bin')
|
||||||
prog_kernel = kernel_env.Program(
|
prog_kernel = kernel_env.Program(
|
||||||
|
@ -6,6 +6,10 @@
|
|||||||
|
|
||||||
#include <efi.h>
|
#include <efi.h>
|
||||||
|
|
||||||
|
// srsly, FUCK YOU
|
||||||
|
#undef ARROW_UP
|
||||||
|
#undef ARROW_DOWN
|
||||||
|
|
||||||
class EfiMemoryMapIterator
|
class EfiMemoryMapIterator
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
@ -63,6 +63,23 @@ __setIDT:
|
|||||||
lidt __idt_storage
|
lidt __idt_storage
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
.global __reloadSegments
|
||||||
|
.type __reloadSegments @function
|
||||||
|
// void __reloadSegments(uint64_t code [%rdi], uint64_t data [%rsi])
|
||||||
|
__reloadSegments:
|
||||||
|
push %rdi
|
||||||
|
lea (.reloadCS), %rax
|
||||||
|
push %rax
|
||||||
|
lretq
|
||||||
|
.reloadCS:
|
||||||
|
movw %si, %ax
|
||||||
|
movw %ax, %ds
|
||||||
|
movw %ax, %es
|
||||||
|
movw %ax, %fs
|
||||||
|
movw %ax, %gs
|
||||||
|
movw %ax, %ss
|
||||||
|
ret
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Set the size of the _start symbol to the current location '.' minus its start.
|
Set the size of the _start symbol to the current location '.' minus its start.
|
||||||
This is useful when debugging or when you implement call tracing.
|
This is useful when debugging or when you implement call tracing.
|
||||||
|
560
targets/x86_64/src/kernel/keyboard.cpp
Normal file
560
targets/x86_64/src/kernel/keyboard.cpp
Normal file
@ -0,0 +1,560 @@
|
|||||||
|
|
||||||
|
#include "./keyboard.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 kbd
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
|
[[nodiscard]] bool read(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] 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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
161
targets/x86_64/src/kernel/keyboard.hpp
Normal file
161
targets/x86_64/src/kernel/keyboard.hpp
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if !defined(BAD_APPLE_OS_KERNEL_KEYBOARD_HPP_INCLUDED)
|
||||||
|
#define BAD_APPLE_OS_KERNEL_KEYBOARD_HPP_INCLUDED
|
||||||
|
|
||||||
|
#include "./pic.hpp"
|
||||||
|
|
||||||
|
namespace kbd
|
||||||
|
{
|
||||||
|
[[nodiscard]] bool initialize() noexcept;
|
||||||
|
|
||||||
|
__attribute__((interrupt))
|
||||||
|
void isrKeyboard(InterruptFrame* interruptFrame) noexcept;
|
||||||
|
|
||||||
|
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 readRaw(std::uint8_t& outData) noexcept;
|
||||||
|
[[nodiscard]] bool read(KeyEvent& outEvent) noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]] const char* keyName(Scancode scancode) noexcept;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // !defined(BAD_APPLE_OS_KERNEL_KEYBOARD_HPP_INCLUDED)
|
121
targets/x86_64/src/kernel/pic.cpp
Normal file
121
targets/x86_64/src/kernel/pic.cpp
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
|
||||||
|
#include "./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);
|
||||||
|
}
|
39
targets/x86_64/src/kernel/pic.hpp
Normal file
39
targets/x86_64/src/kernel/pic.hpp
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct InterruptFrame
|
||||||
|
{
|
||||||
|
std::uint64_t ip;
|
||||||
|
std::uint64_t cs;
|
||||||
|
std::uint64_t flags;
|
||||||
|
std::uint64_t sp;
|
||||||
|
std::uint64_t ss;
|
||||||
|
};
|
||||||
|
|
||||||
|
using interrupt_handler_t = __attribute__((interrupt)) void(*)(InterruptFrame*);
|
||||||
|
|
||||||
|
#endif // !defined(BAD_APPLE_OS_KERNEL_PIC_HPP_INCLUDED)
|
@ -7,12 +7,39 @@
|
|||||||
#include "os/draw.hpp"
|
#include "os/draw.hpp"
|
||||||
#include "os/serial.hpp"
|
#include "os/serial.hpp"
|
||||||
#include "os/tty.hpp"
|
#include "os/tty.hpp"
|
||||||
|
#include "./keyboard.hpp"
|
||||||
|
#include "./pic.hpp"
|
||||||
#include "./x86_64.hpp"
|
#include "./x86_64.hpp"
|
||||||
|
|
||||||
extern "C" int gKernelEnd;
|
extern "C" int gKernelEnd;
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
inline constexpr std::uint8_t MASTER_PIC_OFFSET = 0x20;
|
||||||
|
inline constexpr std::uint8_t SLAVE_PIC_OFFSET = 0x28;
|
||||||
|
static_assert(isValidPICOffset(MASTER_PIC_OFFSET));
|
||||||
|
static_assert(isValidPICOffset(SLAVE_PIC_OFFSET));
|
||||||
|
|
||||||
|
constexpr std::uint8_t irqToInterrupt(std::uint8_t irq) noexcept
|
||||||
|
{
|
||||||
|
if (irq < 8) {
|
||||||
|
return MASTER_PIC_OFFSET + irq;
|
||||||
|
}
|
||||||
|
return SLAVE_PIC_OFFSET + irq;
|
||||||
|
}
|
||||||
|
inline constexpr std::uint8_t INTERRUPT_KEYBOARD = irqToInterrupt(1);
|
||||||
|
|
||||||
|
constinit std::array GDT alignas(16) = {
|
||||||
|
SEGMENT_NULL,
|
||||||
|
SEGMENT_KERNEL_CODE,
|
||||||
|
SEGMENT_KERNEL_DATA,
|
||||||
|
SEGMENT_USER_CODE,
|
||||||
|
SEGMENT_USER_DATA,
|
||||||
|
SEGMENT_NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
constinit std::array<InterruptDescriptor, 256> IDT alignas(16);
|
||||||
|
|
||||||
void initHeapFromEfiMemoryMap(const EfiMemoryMap& memoryMap) noexcept
|
void initHeapFromEfiMemoryMap(const EfiMemoryMap& memoryMap) noexcept
|
||||||
{
|
{
|
||||||
const uint64_t minAddr = reinterpret_cast<uint64_t>(&gKernelEnd);
|
const uint64_t minAddr = reinterpret_cast<uint64_t>(&gKernelEnd);
|
||||||
@ -86,24 +113,17 @@ void initGlobals()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
constinit std::array GDT alignas(16) = {
|
|
||||||
SEGMENT_NULL,
|
|
||||||
SEGMENT_KERNEL_CODE,
|
|
||||||
SEGMENT_KERNEL_DATA,
|
|
||||||
SEGMENT_USER_CODE,
|
|
||||||
SEGMENT_USER_DATA
|
|
||||||
};
|
|
||||||
|
|
||||||
constinit std::array<InterruptDescriptor, 256> IDT alignas(16);
|
|
||||||
extern "C" void __setGDT(uint16_t limit, void* base);
|
extern "C" void __setGDT(uint16_t limit, void* base);
|
||||||
extern "C" void __setIDT(uint16_t limit, void* base);
|
extern "C" void __setIDT(uint16_t limit, void* base);
|
||||||
|
extern "C" void __reloadSegments(uint64_t code, uint64_t data);
|
||||||
|
|
||||||
void setupInterrupt(std::uint8_t index, void* handler, std::uint8_t flags) noexcept
|
void setupInterrupt(std::uint8_t index, interrupt_handler_t handler, std::uint8_t flags) noexcept
|
||||||
{
|
{
|
||||||
const std::uint64_t offset = std::bit_cast<std::uint64_t>(handler);
|
const std::uint64_t offset = std::bit_cast<std::uint64_t>(handler);
|
||||||
IDT[index] = InterruptDescriptor{
|
IDT[index] = InterruptDescriptor{
|
||||||
.offsetLow = static_cast<unsigned>(offset & 0xFFFF),
|
.offsetLow = static_cast<unsigned>(offset & 0xFFFF),
|
||||||
.selector = 1, // kernel code
|
.selectorPrivilegeLevel = 0,
|
||||||
|
.selectorIndex = 1, // kernel code
|
||||||
.interruptStackTable = 0,
|
.interruptStackTable = 0,
|
||||||
.gateType = InterruptGateType::INTERRUPT_GATE,
|
.gateType = InterruptGateType::INTERRUPT_GATE,
|
||||||
.privilegeLevel = 0,
|
.privilegeLevel = 0,
|
||||||
@ -119,10 +139,21 @@ EfiBootInfo* gBootInfo;
|
|||||||
|
|
||||||
void main();
|
void main();
|
||||||
|
|
||||||
|
extern void __isr21(InterruptFrame*);
|
||||||
|
|
||||||
void kernel_main()
|
void kernel_main()
|
||||||
{
|
{
|
||||||
|
setupInterrupt(INTERRUPT_KEYBOARD, &kbd::isrKeyboard, 0);
|
||||||
|
|
||||||
__setGDT(sizeof(GDT), &GDT);
|
__setGDT(sizeof(GDT), &GDT);
|
||||||
__setIDT(sizeof(IDT), &IDT);
|
__setIDT(sizeof(IDT), &IDT);
|
||||||
|
__reloadSegments(0x08, 0x10);
|
||||||
|
|
||||||
|
const bool picsInited = initPICs(MASTER_PIC_OFFSET, SLAVE_PIC_OFFSET);
|
||||||
|
unmaskIRQ(1);
|
||||||
|
|
||||||
|
// initialize the hardware
|
||||||
|
const bool kbdInited = kbd::initialize();
|
||||||
|
|
||||||
// done initializing OS stuff, enable interrupts
|
// done initializing OS stuff, enable interrupts
|
||||||
__asm__ __volatile__("sti");
|
__asm__ __volatile__("sti");
|
||||||
@ -150,7 +181,31 @@ void kernel_main()
|
|||||||
}
|
}
|
||||||
serialWriteString(PORT_COM1, "\r\n\r\n"); // write newlines to separate from UEFI output
|
serialWriteString(PORT_COM1, "\r\n\r\n"); // write newlines to separate from UEFI output
|
||||||
|
|
||||||
|
if (!picsInited)
|
||||||
|
{
|
||||||
|
std::puts("Error initializing the PICs!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!kbdInited)
|
||||||
|
{
|
||||||
|
std::puts("Error initializing the keyboard!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::puts("This is BadAppleOS and everything is fine!\n");
|
std::puts("This is BadAppleOS and everything is fine!\n");
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
kbd::KeyEvent event;
|
||||||
|
while (!kbd::read(event));
|
||||||
|
std::printf(
|
||||||
|
"Key event: scancode=%s(0x%X), down=%s, repeat=%s\n",
|
||||||
|
kbd::keyName(event.scancode),
|
||||||
|
static_cast<unsigned>(event.scancode),
|
||||||
|
event.down ? "true" : "false",
|
||||||
|
event.repeat ? "true" : "false"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
main();
|
main();
|
||||||
}
|
}
|
||||||
|
@ -148,17 +148,26 @@ inline constexpr const SegmentDescriptor SEGMENT_USER_DATA = {
|
|||||||
|
|
||||||
enum class InterruptGateType
|
enum class InterruptGateType
|
||||||
{
|
{
|
||||||
|
NONE = 0,
|
||||||
INTERRUPT_GATE = 0xE,
|
INTERRUPT_GATE = 0xE,
|
||||||
TRAP_GATE = 0xF
|
TRAP_GATE = 0xF
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class InterruptSelectorTable
|
||||||
|
{
|
||||||
|
GDT = 0,
|
||||||
|
LDT = 1
|
||||||
|
};
|
||||||
|
|
||||||
struct InterruptDescriptor
|
struct InterruptDescriptor
|
||||||
{
|
{
|
||||||
/* 0 */ unsigned offsetLow : 16 = 0;
|
/* 0 */ unsigned offsetLow : 16 = 0;
|
||||||
/* 16 */ unsigned selector : 16 = 0;
|
/* 16 */ unsigned selectorPrivilegeLevel : 2 = 0;
|
||||||
|
/* 18 */ InterruptSelectorTable selectorTable : 1 = InterruptSelectorTable::GDT;
|
||||||
|
/* 19 */ unsigned selectorIndex : 13 = 0;
|
||||||
/* 32 */ unsigned interruptStackTable : 3 = 0;
|
/* 32 */ unsigned interruptStackTable : 3 = 0;
|
||||||
/* 35 */ unsigned reserved0_ : 5 = 0;
|
/* 35 */ unsigned reserved0_ : 5 = 0;
|
||||||
/* 40 */ InterruptGateType gateType : 4 = InterruptGateType::INTERRUPT_GATE;
|
/* 40 */ InterruptGateType gateType : 4 = InterruptGateType::NONE;
|
||||||
/* 44 */ unsigned reserved1_ : 1 = 0;
|
/* 44 */ unsigned reserved1_ : 1 = 0;
|
||||||
/* 45 */ unsigned privilegeLevel : 2 = 0;
|
/* 45 */ unsigned privilegeLevel : 2 = 0;
|
||||||
/* 47 */ bool present : 1 = 0;
|
/* 47 */ bool present : 1 = 0;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user