feat: shell (sh) with keyboard driver, SYS_READ, SYS_EXEC
- PS/2 keyboard driver: IRQ1 handler, scancode set 1 translation, ring buffer, shift key support - SYS_READ (fd=0): non-blocking keyboard read for stdin - SYS_YIELD: calls schedule_tick directly (no nested INT 0x80) - SYS_EXEC: loads binary from CPIO initrd, replaces process image - User-space libc: crt0.S (C runtime startup), syscalls.h (inline syscall wrappers, basic string functions) - Shell app (sh): readline with echo/backspace, builtins (cd, env, help, exit), fork+exec for external commands - Updated build_apps.sh: C app support with crt0 linking, libc include path, .bss section in objcopy Tested: shell boots, keyboard input works, hello-world runs via fork+exec, env shows CWD, exit cleanly terminates.
This commit is contained in:
188
src/keyboard.c
Normal file
188
src/keyboard.c
Normal file
@@ -0,0 +1,188 @@
|
||||
/**
|
||||
* @file keyboard.c
|
||||
* @brief PS/2 keyboard driver implementation.
|
||||
*
|
||||
* Reads scancodes from I/O port 0x60 on IRQ1, translates scancode set 1
|
||||
* to ASCII using a simple lookup table, stores characters in a ring buffer,
|
||||
* and wakes any process blocked waiting for keyboard input.
|
||||
*/
|
||||
|
||||
#include "keyboard.h"
|
||||
#include "port_io.h"
|
||||
#include "pic.h"
|
||||
#include "process.h"
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Debug print helpers defined in kernel.c */
|
||||
extern void offset_print(const char *str);
|
||||
extern void print_hex(uint32_t val);
|
||||
|
||||
/** PS/2 keyboard data port. */
|
||||
#define KB_DATA_PORT 0x60
|
||||
|
||||
/** Ring buffer for keyboard input. */
|
||||
static char kb_buffer[KB_BUFFER_SIZE];
|
||||
static volatile uint32_t kb_head = 0;
|
||||
static volatile uint32_t kb_tail = 0;
|
||||
|
||||
/** Process waiting for keyboard input (PID, or 0 if none). */
|
||||
static volatile uint32_t kb_waiting_pid = 0;
|
||||
|
||||
/** Shift key state. */
|
||||
static int shift_pressed = 0;
|
||||
|
||||
/**
|
||||
* Scancode set 1 to ASCII lookup table (unshifted).
|
||||
* Index = scancode, value = ASCII character (0 = unmapped).
|
||||
*/
|
||||
static const char scancode_ascii[128] = {
|
||||
0, 27, '1', '2', '3', '4', '5', '6', /* 0x00 - 0x07 */
|
||||
'7', '8', '9', '0', '-', '=', '\b', '\t', /* 0x08 - 0x0F */
|
||||
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', /* 0x10 - 0x17 */
|
||||
'o', 'p', '[', ']', '\n', 0, 'a', 's', /* 0x18 - 0x1F */
|
||||
'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', /* 0x20 - 0x27 */
|
||||
'\'', '`', 0, '\\', 'z', 'x', 'c', 'v', /* 0x28 - 0x2F */
|
||||
'b', 'n', 'm', ',', '.', '/', 0, '*', /* 0x30 - 0x37 */
|
||||
0, ' ', 0, 0, 0, 0, 0, 0, /* 0x38 - 0x3F */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 - 0x47 */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, /* 0x48 - 0x4F */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 - 0x57 */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, /* 0x58 - 0x5F */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, /* 0x60 - 0x67 */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, /* 0x68 - 0x6F */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, /* 0x70 - 0x77 */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, /* 0x78 - 0x7F */
|
||||
};
|
||||
|
||||
/**
|
||||
* Scancode set 1 to ASCII lookup table (shifted).
|
||||
*/
|
||||
static const char scancode_ascii_shift[128] = {
|
||||
0, 27, '!', '@', '#', '$', '%', '^', /* 0x00 - 0x07 */
|
||||
'&', '*', '(', ')', '_', '+', '\b', '\t', /* 0x08 - 0x0F */
|
||||
'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', /* 0x10 - 0x17 */
|
||||
'O', 'P', '{', '}', '\n', 0, 'A', 'S', /* 0x18 - 0x1F */
|
||||
'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', /* 0x20 - 0x27 */
|
||||
'"', '~', 0, '|', 'Z', 'X', 'C', 'V', /* 0x28 - 0x2F */
|
||||
'B', 'N', 'M', '<', '>', '?', 0, '*', /* 0x30 - 0x37 */
|
||||
0, ' ', 0, 0, 0, 0, 0, 0, /* 0x38 - 0x3F */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 - 0x47 */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, /* 0x48 - 0x4F */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 - 0x57 */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, /* 0x58 - 0x5F */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, /* 0x60 - 0x67 */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, /* 0x68 - 0x6F */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, /* 0x70 - 0x77 */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, /* 0x78 - 0x7F */
|
||||
};
|
||||
|
||||
/** Left/right shift scancodes. */
|
||||
#define SC_LSHIFT_PRESS 0x2A
|
||||
#define SC_LSHIFT_RELEASE 0xAA
|
||||
#define SC_RSHIFT_PRESS 0x36
|
||||
#define SC_RSHIFT_RELEASE 0xB6
|
||||
|
||||
/**
|
||||
* Put a character into the ring buffer.
|
||||
*/
|
||||
static void kb_buffer_put(char c) {
|
||||
uint32_t next = (kb_head + 1) % KB_BUFFER_SIZE;
|
||||
if (next == kb_tail) {
|
||||
return; /* Buffer full, drop character */
|
||||
}
|
||||
kb_buffer[kb_head] = c;
|
||||
kb_head = next;
|
||||
}
|
||||
|
||||
void keyboard_init(void) {
|
||||
kb_head = 0;
|
||||
kb_tail = 0;
|
||||
kb_waiting_pid = 0;
|
||||
shift_pressed = 0;
|
||||
|
||||
/* Flush any pending data from the keyboard controller */
|
||||
while (inb(0x64) & 0x01) {
|
||||
inb(KB_DATA_PORT);
|
||||
}
|
||||
|
||||
/* Unmask IRQ1 (keyboard) in the PIC */
|
||||
pic_clear_mask(1);
|
||||
|
||||
offset_print(" KEYBOARD: initialized\n");
|
||||
}
|
||||
|
||||
void keyboard_irq(registers_t *regs) {
|
||||
(void)regs;
|
||||
uint8_t scancode = inb(KB_DATA_PORT);
|
||||
|
||||
/* Handle shift keys */
|
||||
if (scancode == SC_LSHIFT_PRESS || scancode == SC_RSHIFT_PRESS) {
|
||||
shift_pressed = 1;
|
||||
return;
|
||||
}
|
||||
if (scancode == SC_LSHIFT_RELEASE || scancode == SC_RSHIFT_RELEASE) {
|
||||
shift_pressed = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Ignore key releases (bit 7 set) */
|
||||
if (scancode & 0x80) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Translate scancode to ASCII */
|
||||
char c;
|
||||
if (shift_pressed) {
|
||||
c = scancode_ascii_shift[scancode];
|
||||
} else {
|
||||
c = scancode_ascii[scancode];
|
||||
}
|
||||
|
||||
if (c == 0) {
|
||||
return; /* Unmapped key */
|
||||
}
|
||||
|
||||
/* Put character in buffer */
|
||||
kb_buffer_put(c);
|
||||
|
||||
/* Wake any process waiting for keyboard input */
|
||||
if (kb_waiting_pid != 0) {
|
||||
process_t *waiter = process_get(kb_waiting_pid);
|
||||
if (waiter && waiter->state == PROCESS_BLOCKED) {
|
||||
waiter->state = PROCESS_READY;
|
||||
}
|
||||
kb_waiting_pid = 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t keyboard_read(char *buf, uint32_t count) {
|
||||
uint32_t n = 0;
|
||||
while (n < count && kb_tail != kb_head) {
|
||||
buf[n++] = kb_buffer[kb_tail];
|
||||
kb_tail = (kb_tail + 1) % KB_BUFFER_SIZE;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
int keyboard_has_data(void) {
|
||||
return kb_head != kb_tail;
|
||||
}
|
||||
|
||||
void keyboard_block_for_input(registers_t *regs) {
|
||||
process_t *cur = process_current();
|
||||
if (!cur) return;
|
||||
|
||||
cur->state = PROCESS_BLOCKED;
|
||||
cur->saved_regs = *regs;
|
||||
|
||||
/* Rewind EIP by 2 bytes so that when the process is unblocked and
|
||||
* scheduled, the CPU re-executes the INT 0x80 instruction. At that
|
||||
* point keyboard_has_data() will return true and the read succeeds. */
|
||||
cur->saved_regs.eip -= 2;
|
||||
|
||||
kb_waiting_pid = cur->pid;
|
||||
|
||||
/* Schedule next process */
|
||||
schedule_tick(regs);
|
||||
}
|
||||
Reference in New Issue
Block a user