261 lines
5.2 KiB
C
261 lines
5.2 KiB
C
#include <stdio.h>
|
||
#include <port_io.h>
|
||
#include <tty.h>
|
||
#include <stdlib.h>
|
||
|
||
#include <drivers/ps2_keyboard.h>
|
||
|
||
|
||
/*
|
||
PS/2 Controller IO Ports
|
||
|
||
The PS/2 Controller itself uses 2 IO ports (IO ports 0x60 and 0x64). Like many IO ports, reads and writes may access different internal registers.
|
||
|
||
Historical note: The PC-XT PPI had used port 0x61 to reset the keyboard interrupt request signal (among other unrelated functions). Port 0x61 has no keyboard related functions on AT and PS/2 compatibles.
|
||
IO Port Access Type Purpose
|
||
0x60 Read/Write Data Port
|
||
0x64 Read Status Register
|
||
0x64 Write Command Register
|
||
*/
|
||
|
||
/*
|
||
Note:
|
||
The interrupt number for keyboard input in 1.
|
||
*/
|
||
|
||
#define PS2_DATA_PORT 0x60
|
||
#define PS2_STATUS_PORT 0x64
|
||
|
||
#define KEYBOARD_IRQ 1
|
||
|
||
bool ps2keyboard_initialized = false;
|
||
|
||
/* State for shift key */
|
||
static bool shift_pressed = false;
|
||
|
||
/* State for caps-lock key */
|
||
static bool capslock_pressed = false;
|
||
|
||
/* Used for arrow keys among others */
|
||
static bool extended = false;
|
||
|
||
static bool is_new_char = false;
|
||
static bool is_new_key = false;
|
||
|
||
volatile unsigned char current_char;
|
||
volatile char* current_string = NULL;
|
||
volatile int32_t current_length = 0;
|
||
volatile int32_t capacity = 0;
|
||
volatile uint16_t current_key;
|
||
|
||
|
||
static const char scancode_map[128] = {
|
||
0, 27, '1','2','3','4','5','6','7','8','9','0','-','=','\b', /* Backspace */
|
||
'\t', /* Tab */
|
||
'q','w','e','r','t','y','u','i','o','p','[',']','\n', /* Enter */
|
||
0, /* Control */
|
||
'a','s','d','f','g','h','j','k','l',';','\'','`',
|
||
0, /* Left shift */
|
||
'\\','z','x','c','v','b','n','m',',','.','/',
|
||
0, /* Right shift */
|
||
'*',
|
||
0, /* Alt */
|
||
' ', /* Spacebar */
|
||
0, /* Caps lock */
|
||
/* The rest are function and control keys */
|
||
};
|
||
|
||
|
||
void keyboard_init(void)
|
||
{
|
||
outb(0x64, 0xAE); /* Enable keyboard interface (often optional, but here for safety) */
|
||
ps2keyboard_initialized = true;
|
||
}
|
||
|
||
char get_char(void)
|
||
{
|
||
/*
|
||
ASCII code 5 (Enquiry) is/was used in legacy systems meant for requesting a response from a remote terminal or device.
|
||
For example, one system might send ENQ (ASCII 5), and the receiver could reply with ACK (ASCII 6) to indicate it’s ready or still online.
|
||
|
||
In modern computing, ENQ is largely obsolete:
|
||
|
||
- Terminal emulators, shells, operating systems, and network protocols generally do not use ENQ.
|
||
|
||
- It's not used in text files, programming, or standard communications.
|
||
|
||
- It may still appear in legacy systems, embedded devices, or proprietary serial protocols, but that's niche.
|
||
*/
|
||
char temp = 5;
|
||
|
||
if (is_new_char)
|
||
{
|
||
temp = current_char;
|
||
}
|
||
|
||
is_new_char = false;
|
||
|
||
return temp;
|
||
}
|
||
|
||
uint16_t get_key(void)
|
||
{
|
||
uint16_t temp = 0xFFFA;
|
||
|
||
if (is_new_key)
|
||
{
|
||
temp = current_key;
|
||
is_new_key = false;
|
||
}
|
||
else if (is_new_char)
|
||
{
|
||
temp = (uint16_t)current_char;
|
||
is_new_char = false;
|
||
}
|
||
|
||
return temp;
|
||
}
|
||
|
||
char* get_string(void)
|
||
{
|
||
return current_string;
|
||
}
|
||
|
||
static void append_char(char c)
|
||
{
|
||
if (current_length + 1 >= capacity)
|
||
{
|
||
/* Need more space (+1 for the null zero) */
|
||
int new_capacity = (capacity == 0) ? 16 : capacity * 2;
|
||
|
||
char* new_str = (char*)malloc(new_capacity);
|
||
if (!new_str) {
|
||
return;
|
||
}
|
||
|
||
if (current_string)
|
||
{
|
||
memcpy(new_str, current_string, current_length);
|
||
free(current_string);
|
||
}
|
||
|
||
if (!current_string)
|
||
{
|
||
return;
|
||
}
|
||
|
||
current_string = new_str;
|
||
capacity = new_capacity;
|
||
}
|
||
|
||
current_string[current_length] = c;
|
||
current_length++;
|
||
current_string[current_length] = '\0'; /* Maintain null terminator */
|
||
}
|
||
|
||
static void free_current_string(void)
|
||
{
|
||
if (current_string)
|
||
{
|
||
free(current_string);
|
||
current_string = NULL;
|
||
current_length = 0;
|
||
capacity = 0;
|
||
}
|
||
}
|
||
|
||
void keyboard_handler(void)
|
||
{
|
||
uint8_t scancode = inb(PS2_DATA_PORT);
|
||
|
||
/* Handle shift press/release */
|
||
if (scancode == 0x2A || scancode == 0x36)
|
||
{
|
||
shift_pressed = true;
|
||
return;
|
||
}
|
||
else if (scancode == 0xAA || scancode == 0xB6)
|
||
{
|
||
shift_pressed = false;
|
||
return;
|
||
}
|
||
else if (scancode == 0x3A)
|
||
{
|
||
capslock_pressed = !capslock_pressed;
|
||
return;
|
||
}
|
||
else if (scancode == 0xE0)
|
||
{
|
||
extended = true;
|
||
return;
|
||
}
|
||
|
||
|
||
if (scancode & 0x80)
|
||
{
|
||
extended = false;
|
||
}
|
||
else
|
||
{
|
||
uint16_t c = (uint16_t)scancode_map[scancode];
|
||
if ((shift_pressed ^ capslock_pressed) && ((char)c >= 'a') && ((char)c <= 'z'))
|
||
{
|
||
c -= 32; /* Convert to uppercase */
|
||
}
|
||
else if (shift_pressed)
|
||
{
|
||
c = (uint16_t) terminal_get_shifted((unsigned char) c);
|
||
}
|
||
|
||
/*if (scancode == 0x1C) {
|
||
printf("\n");
|
||
return;
|
||
}*/
|
||
|
||
|
||
if (extended)
|
||
{
|
||
switch (scancode)
|
||
{
|
||
case 0x48:
|
||
c = KEY_ARROW_UP;
|
||
break;
|
||
case 0x50:
|
||
c = KEY_ARROW_DOWN;
|
||
break;
|
||
case 0x4B:
|
||
c = KEY_ARROW_LEFT;
|
||
break;
|
||
case 0x4D:
|
||
c = KEY_ARROW_RIGHT;
|
||
break;
|
||
default:
|
||
c = KEY_NONE;
|
||
break;
|
||
}
|
||
|
||
current_key = c;
|
||
|
||
is_new_key = true;
|
||
extended = false;
|
||
|
||
return;
|
||
}
|
||
|
||
if (c)
|
||
{
|
||
current_char = c;
|
||
|
||
is_new_char = true;
|
||
|
||
if (c == '\n')
|
||
{
|
||
free_current_string();
|
||
}
|
||
|
||
append_char(c);
|
||
}
|
||
}
|
||
}
|
||
|