diff --git a/README.md b/README.md index aced14a..e9cd94f 100644 --- a/README.md +++ b/README.md @@ -86,4 +86,3 @@ To install all the required tools, you can use the following commands: ```bash make run -``` diff --git a/docs/assets/tty/tty.drawio.svg b/docs/assets/tty/tty.drawio.svg new file mode 100644 index 0000000..14dbc6c --- /dev/null +++ b/docs/assets/tty/tty.drawio.svg @@ -0,0 +1,273 @@ + + + + + + + + + + + + + + + +
+
+
+ user +
+
+
+
+ + user + +
+
+
+ + + + + + + + +
+
+
+ output +
+
+
+
+ + output + +
+
+
+ + + + + + + + + + + + +
+
+
+ read +
+
+
+
+ + read + +
+
+
+ + + + + + + +
+
+
+
+ + TTY + +
+
+
+
+
+ + TTY + +
+
+
+ + + + + + + + +
+
+
+ input +
+
+
+
+ + input + +
+
+
+ + + + + + + +
+
+
+ Terminal +
+
+
+
+ + Terminal + +
+
+
+ + + + + + + + + + + +
+
+
+ Console +
+
+
+
+ + Console + +
+
+
+ + + + + + + +
+
+
+ secreen +
+
+
+
+ + secreen + +
+
+
+ + + + + + + +
+
+
+ network +
+
+
+
+ + network + +
+
+
+ + + + + + + + +
+
+
+ write +
+
+
+
+ + write + +
+
+
+ + + + + + + +
+
+
+
+
+
+
+
+
+
+ Shell/App +
+
+
+
+
+ + Shell/App... + +
+
+
+
+ + + + + Text is not SVG - cannot display + + + +
\ No newline at end of file diff --git a/docs/assets/tty/tty.png b/docs/assets/tty/tty.png new file mode 100644 index 0000000..3a2021c Binary files /dev/null and b/docs/assets/tty/tty.png differ diff --git a/includes/driver/console.h b/includes/driver/console.h new file mode 100644 index 0000000..3190686 --- /dev/null +++ b/includes/driver/console.h @@ -0,0 +1,22 @@ +#ifndef CONSOLE_H +#define CONSOLE_H + +#include "driver/keyboard/event_queue.h" +#include "keyboard_keys.h" +#include "keyboard_layout.h" +#include "types.h" + +struct console_t; + +typedef struct { + void (*write)(struct console_t *self, char *str); + void (*clear)(struct console_t *self); +} console_interface_t; + +typedef struct console_t { + console_interface_t method; +} console_t; + +void console_init(console_t *console); + +#endif \ No newline at end of file diff --git a/includes/driver/keyboard/event_queue.h b/includes/driver/keyboard/event_queue.h index bc5ae83..91226c5 100644 --- a/includes/driver/keyboard/event_queue.h +++ b/includes/driver/keyboard/event_queue.h @@ -6,38 +6,38 @@ #include "ps2.h" #include "types.h" -#define KEYBOARD_EVENT_QUEUE_SIZE 256 - -typedef struct { - key_event_t events[KEYBOARD_EVENT_QUEUE_SIZE]; - uint32_t head; - uint32_t tail; -} keyboard_event_queue_t; - -extern keyboard_event_queue_t keyboard_queue; +#define INPUT_EVENT_QUEUE_SIZE 256 typedef enum { - INPUT_CHAR, - INPUT_ENTER, - INPUT_BACKSPACE, - INPUT_TAB, - INPUT_UP, - INPUT_DOWN, - INPUT_LEFT, - INPUT_RIGHT, -} input_type_t; + INPUT_EVENT_KEY, + INPUT_EVENT_CHAR, +} input_event_type_t; typedef struct { - input_type_t type; + input_event_type_t type; + keycode_t key; + key_action_t action; + bool shift; + bool ctrl; + bool alt; char character; } input_event_t; +typedef struct { + input_event_t events[INPUT_EVENT_QUEUE_SIZE]; + uint32_t head; + uint32_t tail; +} input_event_queue_t; + +extern input_event_queue_t input_event_queue; + +void input_queue_init(void); void input_queue_push(input_event_t event); -void input_queue_pop(input_event_t *event); +bool input_queue_pop(input_event_t *event); +bool input_queue_is_empty(void); +bool input_queue_is_full(void); void input_queue_print(); void input_queue_print_last(); -void input_queue_clear(); - -extern input_event_t input_queue[KEYBOARD_EVENT_QUEUE_SIZE]; +void input_queue_clear(void); #endif \ No newline at end of file diff --git a/includes/driver/keyboard/keyboard.h b/includes/driver/keyboard/keyboard.h index 51842ef..48e6be3 100644 --- a/includes/driver/keyboard/keyboard.h +++ b/includes/driver/keyboard/keyboard.h @@ -16,6 +16,7 @@ typedef enum { void init_keyboard(void); void reset_keyboard(void); +void set_scanning(bool state); void set_scaning(bool state); void select_scancode_set(uint8_t set); void keyboard_set_layout(const keyboard_layout_t *layout); diff --git a/includes/driver/keyboard/keyboard_keys.h b/includes/driver/keyboard/keyboard_keys.h index 40e3dfc..e4fedda 100644 --- a/includes/driver/keyboard/keyboard_keys.h +++ b/includes/driver/keyboard/keyboard_keys.h @@ -184,11 +184,6 @@ typedef enum { typedef enum { KEY_PRESSED, KEY_RELEASED } key_action_t; -typedef struct { - keycode_t key; - key_action_t action; -} key_event_t; - typedef struct { keycode_t normal[256]; keycode_t extended[256]; diff --git a/includes/driver/tty.h b/includes/driver/tty.h new file mode 100644 index 0000000..f402f72 --- /dev/null +++ b/includes/driver/tty.h @@ -0,0 +1,58 @@ +#ifndef TTY_H +#define TTY_H + +#include "driver/keyboard/event_queue.h" +#include "keyboard_keys.h" +#include "keyboard_layout.h" +#include "types.h" + +#include "console.h" + +#define TTY_BUFFER_SIZE 4096 + +// modifiers +typedef enum { + MOD_NONE = 0, + MOD_SHIFT = 1 << 0, + MOD_CTRL = 1 << 1, + MOD_ALT = 1 << 2, + MOD_ALTGR = 1 << 3, + MOD_CAPS = 1 << 4, + MOD_NUM = 1 << 5, + MOD_SCROLL = 1 << 6, + MOD_SUPER = 1 << 7 +} keyboard_modifiers_t; + +struct tty_t; + +typedef struct { + void (*putc)(struct tty_t *tty, char c); + void (*write)(struct tty_t *tty, const char *str); + void (*read)(struct tty_t *tty, char *buf, size_t len); + void (*clear)(struct tty_t *tty); +} tty_interface_t; + +typedef struct tty_t { + bool echo; + bool line_ready; + + int input_length; + int buffer_index; + int cursor_index; + char buffer[TTY_BUFFER_SIZE]; + + keyboard_modifiers_t modifiers; + + tty_interface_t method; + + console_t *console; +} tty_t; + +void tty_init(tty_t *tty); + +void tty_handle_event(tty_t *tty, const input_event_t *event); +void tty_update_modifiers(tty_t *tty, const input_event_t *event); + +void tty_process_events(tty_t *tty); + +#endif \ No newline at end of file diff --git a/includes/driver/vga.h b/includes/driver/vga.h index 7b90c93..5a62b0a 100644 --- a/includes/driver/vga.h +++ b/includes/driver/vga.h @@ -28,7 +28,7 @@ #define BROWN 0x06 // YELLOW #define LIGHT_GRAY 0x07 // WHITE -#define DARK_GRAY BLACk | LIGHT_FLAG +#define DARK_GRAY BLACK | LIGHT_FLAG #define LIGHT_BLUE BLUE | LIGHT_FLAG #define LIGHT_GREEN GREEN | LIGHT_FLAG #define LIGHT_CYAN CYAN | LIGHT_FLAG @@ -77,10 +77,21 @@ void vga_set_cursor(uint8_t x, uint8_t y); void vga_set_cursor_position(uint16_t p); +void vga_cursor_up(void); +void vga_cursor_down(void); +void vga_cursor_left(void); +void vga_cursor_right(void); + uint16_t vga_get_cursor_position(void); uint8_t vga_get_cursor_x(void); uint8_t vga_get_cursor_y(void); +void vga_put_at(uint8_t x, uint8_t y, char c, uint8_t color); +void vga_clear_region(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t color); + +void vga_print_at(uint8_t x, uint8_t y, const char *str, uint8_t color); +void vga_print_at_center(uint8_t y, const char *str, uint8_t color); + #endif \ No newline at end of file diff --git a/includes/lib/stdio.h b/includes/lib/stdio.h index d3097bf..2962f4c 100644 --- a/includes/lib/stdio.h +++ b/includes/lib/stdio.h @@ -12,6 +12,8 @@ void print_at_end(const char *str, uint8_t color); void printf(const char *format, ...); void snprintf(char *buffer, size_t buffer_size, const char *format, ...); +char scan_char(void); +int scanf(const char *format, ...); char *vformatter(const char *format, va_list args); char *formatter(const char *format, ...); diff --git a/includes/lib/string.h b/includes/lib/string.h index 004e7dd..a75f1fa 100644 --- a/includes/lib/string.h +++ b/includes/lib/string.h @@ -3,7 +3,7 @@ #include "types.h" -int strlen(char *str); +int strlen(const char *str); void strcpy(char *dest, char *src); void strcat(char *dest, const char *src); diff --git a/src/bootloader/boot.asm b/src/bootloader/boot.asm index 5d2c8b2..cf2fbad 100644 --- a/src/bootloader/boot.asm +++ b/src/bootloader/boot.asm @@ -34,31 +34,31 @@ int 0x13 jc disk_read_error -jmp start +; jmp start -msg db 13, 10,"Welcom to my bootLoader", 13, 10 - db "press any key to continue...", 0 +; msg db 13, 10,"Welcom to my bootLoader", 13, 10 +; db "press any key to continue...", 0 -s_msg db 13,10,10,"starting...",0 +; s_msg db 13,10,10,"starting...",0 -%include "src/bootloader/utils.asm" +; %include "src/bootloader/utils.asm" -start: +; start: -call clear_screen +; call clear_screen -MOV si, msg -call print +; MOV si, msg +; call print -call get_key +; call get_key -MOV si, s_msg -call print -jmp PM_start +; MOV si, s_msg +; call print +; jmp PM_start PM_start: diff --git a/src/driver/console/vga/vga_console.c b/src/driver/console/vga/vga_console.c new file mode 100644 index 0000000..265366c --- /dev/null +++ b/src/driver/console/vga/vga_console.c @@ -0,0 +1,39 @@ +#include "driver/vga.h" + +#include "types.h" + +#include "console.h" + +#include "driver/tty.h" + +#include "stdio.h" + +void console_write(console_t *console, char *str); +void console_clear(console_t *console); + +static const console_interface_t console_default_interface = { + .write = console_write, + .clear = console_clear, +}; + +void console_init(console_t *console) { + if (console == NULL) + return; + + console->method = console_default_interface; +} + +void console_write(console_t *console, char *str) { + + if (console == NULL || str == NULL) + return; + + print(str); +} + +void console_clear(console_t *console) { + if (console == NULL) + return; + + vga_clear(); +} \ No newline at end of file diff --git a/src/driver/keyboard/event_queue.c b/src/driver/keyboard/event_queue.c index 5d87d42..2c186e4 100644 --- a/src/driver/keyboard/event_queue.c +++ b/src/driver/keyboard/event_queue.c @@ -1,54 +1,69 @@ #include "event_queue.h" #include "stdio.h" -// Input event queue +input_event_queue_t input_event_queue = {0}; -input_event_t input_queue[KEYBOARD_EVENT_QUEUE_SIZE]; -uint32_t input_queue_head = 0; -uint32_t input_queue_tail = 0; +void input_queue_init(void) { + input_event_queue.head = 0; + input_event_queue.tail = 0; +} void input_queue_push(input_event_t event) { - uint32_t next = (input_queue_head + 1) % KEYBOARD_EVENT_QUEUE_SIZE; + uint32_t next = (input_event_queue.head + 1) % INPUT_EVENT_QUEUE_SIZE; - if (next == input_queue_tail) { + if (next == input_event_queue.tail) { return; /* queue full */ } - input_queue[input_queue_head] = event; - input_queue_head = next; + input_event_queue.events[input_event_queue.head] = event; + input_event_queue.head = next; } -void input_queue_pop(input_event_t *event) { - if (input_queue_head == input_queue_tail) { - return; /* queue empty */ +bool input_queue_pop(input_event_t *event) { + if (input_event_queue.head == input_event_queue.tail) { + if (event != NULL) { + *event = (input_event_t){0}; + } + return false; /* queue empty */ + } + + if (event != NULL) { + *event = input_event_queue.events[input_event_queue.tail]; } - *event = input_queue[input_queue_tail]; + input_event_queue.tail = (input_event_queue.tail + 1) % INPUT_EVENT_QUEUE_SIZE; + return true; +} + +bool input_queue_is_empty(void) { return input_event_queue.head == input_event_queue.tail; } - input_queue_tail = (input_queue_tail + 1) % KEYBOARD_EVENT_QUEUE_SIZE; +bool input_queue_is_full(void) { + return ((input_event_queue.head + 1) % INPUT_EVENT_QUEUE_SIZE) == input_event_queue.tail; } void input_queue_print() { - printf("\033[32mInput Queue: \033[0m head=%d, tail=%d\n", input_queue_head, input_queue_tail); - for (uint32_t i = 0; i < input_queue_head - input_queue_tail; i++) { - input_event_t event = input_queue[i]; - printf("Event[%d]: type=%d, character=%c\n", i, event.type, event.character); + printf("\033[32mInput Queue: \033[0m head=%d, tail=%d\n", input_event_queue.head, + input_event_queue.tail); + uint32_t count = (input_event_queue.head - input_event_queue.tail + INPUT_EVENT_QUEUE_SIZE) % + INPUT_EVENT_QUEUE_SIZE; + for (uint32_t i = 0; i < count; i++) { + uint32_t index = (input_event_queue.tail + i) % INPUT_EVENT_QUEUE_SIZE; + input_event_t event = input_event_queue.events[index]; + printf("Event[%d]: key=%d action=%d char=%c\n", i, event.key, event.action, + event.character); } } void input_queue_print_last() { - if (input_queue_head == input_queue_tail) { + if (input_queue_is_empty()) { printf("\033[31mInput Queue: \033[0m Empty\n"); return; } - uint32_t last = (input_queue_head - 1 + KEYBOARD_EVENT_QUEUE_SIZE) % KEYBOARD_EVENT_QUEUE_SIZE; - input_event_t event = input_queue[last]; - printf("\033[32mInput Queue:\033[0m Last event[%d] : type=%d, character=%c\n", last, event.type, - event.character); + uint32_t last = (input_event_queue.head - 1 + INPUT_EVENT_QUEUE_SIZE) % INPUT_EVENT_QUEUE_SIZE; + input_event_t event = input_event_queue.events[last]; + printf("\033[32mInput Queue:\033[0m Last event key=%d action=%d char=%c\n", event.key, + event.action, event.character); } -void input_queue_clear() { - input_queue_head = 0; - input_queue_tail = 0; -} +void input_queue_clear(void) { input_queue_init(); } diff --git a/src/driver/keyboard/handler.c b/src/driver/keyboard/handler.c index baaf715..5f2bf92 100644 --- a/src/driver/keyboard/handler.c +++ b/src/driver/keyboard/handler.c @@ -18,7 +18,6 @@ void keyboard_irq_handler(void) { } uint8_t data = ps2_read(); - keyboard_process_scancode(data); } @@ -105,70 +104,50 @@ void keyboard_process_scancode(uint8_t data) { void keyboard_handle_key(keycode_t key, key_action_t action) { keyboard_update_state(key, action); - /* - * Key release events are used only to update the keyboard state. - */ - if (action != KEY_PRESSED) { - return; - } - input_event_t input = {0}; - - /* ---------------------------------------------------------------------- */ - /* Character input */ - /* ---------------------------------------------------------------------- */ + input.type = INPUT_EVENT_KEY; + input.key = key; + input.action = action; + input.shift = keyboard_state.left_shift || keyboard_state.right_shift; + input.ctrl = keyboard_state.left_ctrl || keyboard_state.right_ctrl; + input.alt = keyboard_state.left_alt || keyboard_state.right_alt; char character = keyboard_translate(key, &keyboard_state); - if (character != '\0') { - - input.type = INPUT_CHAR; + input.type = INPUT_EVENT_CHAR; input.character = character; - input_queue_push(input); - return; } - /* ---------------------------------------------------------------------- */ - /* Special keys */ - /* ---------------------------------------------------------------------- */ - switch (key) { - case KEY_ENTER: - input.type = INPUT_ENTER; - break; - case KEY_BACKSPACE: - input.type = INPUT_BACKSPACE; - break; - case KEY_TAB: - input.type = INPUT_TAB; - break; - case KEY_UP: - input.type = INPUT_UP; - break; - case KEY_DOWN: - input.type = INPUT_DOWN; - break; - case KEY_LEFT: - input.type = INPUT_LEFT; - break; - case KEY_RIGHT: - input.type = INPUT_RIGHT; - break; + case KEY_HOME: + case KEY_END: + case KEY_DELETE: + case KEY_ESC: + case KEY_LEFT_SHIFT: + case KEY_RIGHT_SHIFT: + case KEY_LEFT_CTRL: + case KEY_RIGHT_CTRL: + case KEY_LEFT_ALT: + case KEY_RIGHT_ALT: + case KEY_CAPS_LOCK: + case KEY_NUM_LOCK: + case KEY_SCROLL_LOCK: + input.character = '\0'; + input_queue_push(input); + return; default: return; } - - input_queue_push(input); } /* -------------------------------------------------------------------------- */ diff --git a/src/driver/keyboard/keyboard.c b/src/driver/keyboard/keyboard.c index f325aa6..2d20fc3 100644 --- a/src/driver/keyboard/keyboard.c +++ b/src/driver/keyboard/keyboard.c @@ -10,9 +10,9 @@ static const keyboard_layout_t *active_layout; void init_keyboard(void) { reset_keyboard(); - set_scaning(false); + set_scanning(false); select_scancode_set(0x02); - set_scaning(true); + set_scanning(true); ps2_flush_output(); @@ -44,9 +44,12 @@ void reset_keyboard(void) { } } -void set_scaning(bool state) { +void set_scanning(bool state) { ps2_flush_output(); - ps2_wait_input_clear(); + if (!ps2_wait_input_clear()) { + printf("\033[31mERROR:\033[0m Keyboard input timeout while toggling scanning.\n"); + return; + } if (state) { ps2_write(PS2_DC_ENABLE_SCANNING); @@ -54,23 +57,36 @@ void set_scaning(bool state) { ps2_write(PS2_DC_DISABLE_SCANNING); } - ps2_wait_output_full(); + if (!ps2_wait_output_full()) { + printf("\033[31mERROR:\033[0m Keyboard scan ack timeout.\n"); + return; + } if (ps2_read() != K_ACK) { printf("\033[31mERROR:\033[0m Keyboard did not acknowledge scanning command.\n"); - return; } } +void set_scaning(bool state) { set_scanning(state); } + void select_scancode_set(uint8_t set) { - ps2_flush_output(); - ps2_wait_input_clear(); + if (set > 3) { + printf("\033[31mERROR:\033[0m Invalid scancode set %u.\n", set); + return; + } - if (set <= 3) { - ps2_write(PS2_DC_SELECT_SET); + ps2_flush_output(); + if (!ps2_wait_input_clear()) { + printf("\033[31mERROR:\033[0m Keyboard input timeout before scancode set select.\n"); + return; } - ps2_wait_output_full(); + ps2_write(PS2_DC_SELECT_SET); + + if (!ps2_wait_output_full()) { + printf("\033[31mERROR:\033[0m Keyboard ack timeout while selecting scancode set.\n"); + return; + } if (ps2_read() != K_ACK) { printf("\033[31mERROR:\033[0m keyboard.h:53 select_scancode_set .\n"); @@ -78,14 +94,19 @@ void select_scancode_set(uint8_t set) { } ps2_flush_output(); - ps2_wait_input_clear(); + if (!ps2_wait_input_clear()) { + printf("\033[31mERROR:\033[0m Keyboard input timeout before scancode value.\n"); + return; + } ps2_write(set); - ps2_wait_output_full(); + if (!ps2_wait_output_full()) { + printf("\033[31mERROR:\033[0m Keyboard ack timeout after scancode value.\n"); + return; + } if (ps2_read() != K_ACK) { printf("\033[31mERROR:\033[0m keyboard.h:65 select_scancode_set .\n"); - return; } } diff --git a/src/driver/keyboard/layout/us.c b/src/driver/keyboard/layout/us.c index b334008..813dfce 100644 --- a/src/driver/keyboard/layout/us.c +++ b/src/driver/keyboard/layout/us.c @@ -103,7 +103,7 @@ const keyboard_layout_t keyboard_layout_us = { char keyboard_layout_translate(const keyboard_layout_t *layout, keycode_t key, const keyboard_state_t *state) { - if (key >= KEY_COUNT) { + if (layout == NULL || key >= KEY_COUNT || state == NULL) { return '\0'; } @@ -114,8 +114,10 @@ char keyboard_layout_translate(const keyboard_layout_t *layout, keycode_t key, } bool shifted = state->left_shift || state->right_shift; + bool caps_active = + state->caps_lock && ((key >= KEY_A && key <= KEY_Z) || (key >= KEY_0 && key <= KEY_9)); - if (shifted) { + if (shifted ^ caps_active) { return entry.shift; } diff --git a/src/driver/ps2.c b/src/driver/ps2.c index 561de21..2f94517 100644 --- a/src/driver/ps2.c +++ b/src/driver/ps2.c @@ -22,16 +22,24 @@ bool ps2_parity_error(void) { return ps2_status().parity_error; } bool ps2_zero(void) { return ps2_status().zero; } +#define PS2_WAIT_MAX 100000 + bool ps2_wait_input_clear(void) { - while (ps2_input_buffer_full()) - ; - return true; + for (uint32_t i = 0; i < PS2_WAIT_MAX; i++) { + if (!ps2_input_buffer_full()) { + return true; + } + } + return false; } bool ps2_wait_output_full(void) { - while (!ps2_output_buffer_full()) - ; - return true; + for (uint32_t i = 0; i < PS2_WAIT_MAX; i++) { + if (ps2_output_buffer_full()) { + return true; + } + } + return false; } void ps2_flush_output(void) { diff --git a/src/driver/tty.c b/src/driver/tty.c new file mode 100644 index 0000000..55b0891 --- /dev/null +++ b/src/driver/tty.c @@ -0,0 +1,324 @@ +#include "tty.h" +#include "driver/vga.h" +#include "keyboard/event_queue.h" +#include "keyboard_keys.h" + +#include "console.h" + +void tty_putc(tty_t *tty, char c); +void tty_write(tty_t *tty, const char *str); +void tty_read(tty_t *tty, char *buf, size_t len); +void tty_clear(tty_t *tty); + +static const tty_interface_t tty_default_interface = { + .putc = tty_putc, + .write = tty_write, + .read = tty_read, + .clear = tty_clear, +}; + +console_t vga_console; + +void tty_init(tty_t *tty) { + + if (tty == NULL) + return; + + tty->echo = true; + tty->line_ready = false; + + tty->input_length = 0; + tty->buffer_index = 0; + tty->cursor_index = 0; + + tty->modifiers = MOD_NONE; + + for (size_t i = 0; i < TTY_BUFFER_SIZE; i++) + tty->buffer[i] = '\0'; + + tty->method = tty_default_interface; + + tty->console = &vga_console; + + console_init(tty->console); +} + +void tty_putc(tty_t *tty, char c) { + if (tty == NULL) + return; + + if (tty->buffer_index >= TTY_BUFFER_SIZE - 1) + return; + + tty->buffer[tty->buffer_index++] = c; + tty->buffer[tty->buffer_index] = '\0'; + + tty->input_length++; +} + +void tty_write(tty_t *tty, const char *str) { + if (tty == NULL || str == NULL) + return; + + if (tty->console == NULL || tty->console->method.write == NULL) + return; + + for (size_t i = 0; str[i] != '\0'; i++) { + char ch[2] = {str[i], '\0'}; + tty->console->method.write(tty->console, ch); + } +} + +void tty_read(tty_t *tty, char *buf, size_t len) { + if (tty == NULL || buf == NULL || len == 0) + return; + + size_t i = 0; + + /* + * Reserve one byte for '\0'. + */ + while (i < len - 1 && tty->buffer[i] != '\0') { + buf[i] = tty->buffer[i]; + i++; + } + + buf[i] = '\0'; +} + +void tty_clear(tty_t *tty) { + if (tty == NULL) + return; + + for (size_t i = 0; i < TTY_BUFFER_SIZE; i++) + tty->buffer[i] = '\0'; + + tty->buffer_index = 0; + tty->cursor_index = 0; + tty->input_length = 0; + tty->line_ready = false; + + if (tty->console != NULL && tty->console->method.clear != NULL) + tty->console->method.clear(tty->console); +} + +void tty_update_modifiers(tty_t *tty, const input_event_t *event) { + if (tty == NULL || event == NULL) { + return; + } + + bool pressed = event->action == KEY_PRESSED; + + switch (event->key) { + + case KEY_LEFT_SHIFT: + case KEY_RIGHT_SHIFT: + if (pressed) + tty->modifiers |= MOD_SHIFT; + else + tty->modifiers &= ~MOD_SHIFT; + break; + + case KEY_LEFT_CTRL: + case KEY_RIGHT_CTRL: + if (pressed) + tty->modifiers |= MOD_CTRL; + else + tty->modifiers &= ~MOD_CTRL; + break; + + case KEY_LEFT_ALT: + if (pressed) + tty->modifiers |= MOD_ALT; + else + tty->modifiers &= ~MOD_ALT; + break; + + case KEY_RIGHT_ALT: + if (pressed) + tty->modifiers |= MOD_ALTGR; + else + tty->modifiers &= ~MOD_ALTGR; + break; + + case KEY_CAPS_LOCK: + if (pressed) + tty->modifiers ^= MOD_CAPS; + break; + + case KEY_NUM_LOCK: + if (pressed) + tty->modifiers ^= MOD_NUM; + break; + + case KEY_SCROLL_LOCK: + if (pressed) + tty->modifiers ^= MOD_SCROLL; + break; + + default: + break; + } +} + +void tty_handle_event(tty_t *tty, const input_event_t *event) { + if (tty == NULL || event == NULL) + return; + + tty_update_modifiers(tty, event); + + if (event->action != KEY_PRESSED) + return; + + switch (event->key) { + + case KEY_LEFT_SHIFT: + case KEY_RIGHT_SHIFT: + + case KEY_LEFT_CTRL: + case KEY_RIGHT_CTRL: + + case KEY_LEFT_ALT: + case KEY_RIGHT_ALT: + + case KEY_CAPS_LOCK: + case KEY_NUM_LOCK: + case KEY_SCROLL_LOCK: + + return; + + default: + break; + } + + if (tty->modifiers & MOD_CTRL) { + + switch (event->key) { + + case KEY_C: + /* + * Ctrl+C + * + * Process/signal handling can be added later. + */ + return; + + default: + break; + } + } + + switch (event->key) { + + case KEY_LEFT: + if (tty->cursor_index > 0) { + tty->cursor_index--; + vga_cursor_left(); + } + return; + + case KEY_RIGHT: + if (tty->cursor_index < tty->buffer_index) { + tty->cursor_index++; + vga_cursor_right(); + } + return; + + case KEY_BACKSPACE: + + if (tty->cursor_index > 0) { + uint16_t delete_position = vga_get_cursor_position(); + + for (int i = tty->cursor_index - 1; i < tty->buffer_index; i++) + tty->buffer[i] = tty->buffer[i + 1]; + + tty->cursor_index--; + tty->buffer_index--; + + if (tty->input_length > 0) + tty->input_length--; + + if (tty->echo) { + vga_cursor_left(); + delete_position = vga_get_cursor_position(); + + for (int i = tty->cursor_index; i < tty->buffer_index; i++) { + char output[2] = {tty->buffer[i], '\0'}; + tty->console->method.write(tty->console, output); + } + + vga_print_char(' '); + vga_set_cursor_position(delete_position); + } + + tty->buffer[tty->buffer_index] = '\0'; + } + + return; + + case KEY_ENTER: + + if (tty->buffer_index < TTY_BUFFER_SIZE - 1) { + + if (tty->echo) { + vga_set_cursor_position(vga_get_cursor_position() + + (tty->buffer_index - tty->cursor_index)); + } + + tty->buffer[tty->buffer_index++] = '\n'; + tty->buffer[tty->buffer_index] = '\0'; + + tty->input_length++; + tty->cursor_index = tty->buffer_index; + tty->line_ready = true; + + if (tty->echo) + tty->console->method.write(tty->console, "\n"); + } + + return; + + default: + break; + } + + char c = event->character; + + if (c == '\0') + return; + + if (tty->buffer_index < TTY_BUFFER_SIZE - 1) { + uint16_t cursor_position = vga_get_cursor_position(); + + for (int i = tty->buffer_index; i > tty->cursor_index; i--) + tty->buffer[i] = tty->buffer[i - 1]; + + tty->buffer[tty->cursor_index++] = c; + tty->buffer_index++; + tty->buffer[tty->buffer_index] = '\0'; + + tty->input_length++; + + if (tty->echo) { + char output[2] = {c, '\0'}; + tty->console->method.write(tty->console, output); + + for (int i = tty->cursor_index; i < tty->buffer_index; i++) { + output[0] = tty->buffer[i]; + tty->console->method.write(tty->console, output); + } + + vga_set_cursor_position(cursor_position + 1); + } + } +} + +// entry point + +void tty_process_events(tty_t *tty) { + input_event_t event; + + while (input_queue_pop(&event)) { + tty_handle_event(tty, &event); + } +} \ No newline at end of file diff --git a/src/driver/vga.c b/src/driver/vga.c index 0fe1104..ff1ca61 100644 --- a/src/driver/vga.c +++ b/src/driver/vga.c @@ -1,4 +1,5 @@ #include "vga.h" +#include "string.h" #include "utils.h" static uint8_t cursor_x = 0; @@ -100,9 +101,21 @@ void vga_print_char(char c) { break; default: + if (cursor_position >= VGA_RES) { + vga_scroll_up(); + cursor_position = (VGA_HEIGHT - 1) * VGA_WIDTH; + } + vga_buf[cursor_position] = ((uint16_t)current_color << 8) | (uint8_t)c; - vga_set_cursor_position(cursor_position + 1); + if (cursor_position + 1 >= VGA_RES) { + vga_scroll_up(); + cursor_position = (VGA_HEIGHT - 1) * VGA_WIDTH; + } else { + cursor_position++; + } + + vga_set_cursor_position(cursor_position); break; } } @@ -144,10 +157,10 @@ void vga_set_color(uint8_t foreground, uint8_t background) { // Cursor Enabling void vga_cursor_enable(void) { outb(VGA_INDEX_PORT, VGA_CURSOR_START); - outb(VGA_DATA_PORT, 0x06); + outb(VGA_DATA_PORT, 0x00); outb(VGA_INDEX_PORT, VGA_CURSOR_END); - outb(VGA_DATA_PORT, 0x07); + outb(VGA_DATA_PORT, 0x0F); } void vga_cursor_disable(void) { @@ -172,6 +185,9 @@ void vga_set_cursor(uint8_t x, uint8_t y) { } void vga_set_cursor_position(uint16_t p) { + if (p >= VGA_RES) { + p = VGA_RES - 1; + } outb(VGA_INDEX_PORT, VGA_CURSOR_HIGH); outb(VGA_DATA_PORT, (p >> 8)); @@ -184,6 +200,34 @@ void vga_set_cursor_position(uint16_t p) { cursor_y = p / VGA_WIDTH; } +void vga_cursor_up(void) { + if (cursor_position < VGA_WIDTH) + return; + + vga_set_cursor_position(cursor_position - VGA_WIDTH); +} + +void vga_cursor_down(void) { + if (cursor_position + VGA_WIDTH >= VGA_RES) + return; + + vga_set_cursor_position(cursor_position + VGA_WIDTH); +} + +void vga_cursor_left(void) { + if (cursor_position == 0) + return; + + vga_set_cursor_position(cursor_position - 1); +} + +void vga_cursor_right(void) { + if (cursor_position + 1 >= VGA_RES) + return; + + vga_set_cursor_position(cursor_position + 1); +} + uint16_t vga_get_cursor_position(void) { outb(VGA_INDEX_PORT, VGA_CURSOR_HIGH); @@ -243,3 +287,38 @@ void vga_scroll_up(void) { vga_set_cursor(0, VGA_HEIGHT - 1); } + +// update specific position + +void vga_put_at(uint8_t x, uint8_t y, char c, uint8_t color) { + uint16_t index = y * VGA_WIDTH + x; + + vga_buf[index] = ((uint16_t)color << 8) | (uint8_t)c; +} + +void vga_clear_region(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t color) { + for (uint8_t row = 0; row < height; row++) { + for (uint8_t col = 0; col < width; col++) { + vga_put_at(x + col, y + row, ' ', color); + } + } +} + +void vga_print_at(uint8_t x, uint8_t y, const char *str, uint8_t color) { + for (uint32_t i = 0; str[i] != '\0'; i++) { + vga_put_at(x + i, y, str[i], color); + } +} + +void vga_print_at_center(uint8_t y, const char *str, uint8_t color) { + size_t len = strlen(str); + if (len > VGA_WIDTH) { + len = VGA_WIDTH; + } + + uint8_t x = (VGA_WIDTH - len) / 2; + + for (size_t i = 0; i < len; i++) { + vga_put_at(x + i, y, str[i], color); + } +} diff --git a/src/kernel/entry.asm b/src/kernel/entry.asm index d8787f9..356b7b2 100644 --- a/src/kernel/entry.asm +++ b/src/kernel/entry.asm @@ -10,7 +10,7 @@ extern kernel_main _start: cli - call print_kernel_loaded + ; call print_kernel_loaded call kernel_main cli @@ -21,23 +21,23 @@ _start: ; ---------------------------------------- ; Print "Kernel is loaded" ; ---------------------------------------- -print_kernel_loaded: - pushad +; print_kernel_loaded: +; pushad - mov edi, 0xB8000 ; VGA text buffer - mov esi, kernel_msg +; mov edi, 0xB8000 ; VGA text buffer +; mov esi, kernel_msg -.print: - lodsb ; AL = *ESI++ - test al, al - jz .done +; .print: +; lodsb ; AL = *ESI++ +; test al, al +; jz .done - mov ah, 0x0F ; White text on black background - stosw ; Write AX (character + attribute) - jmp .print +; mov ah, 0x0F ; White text on black background +; stosw ; Write AX (character + attribute) +; jmp .print -.done: - popad - ret +; .done: +; popad +; ret -kernel_msg db "Hello from kernel!", 0 \ No newline at end of file +; kernel_msg db "Hello from kernel!", 0 \ No newline at end of file diff --git a/src/kernel/kernel.c b/src/kernel/kernel.c index dfda71f..f74c2d7 100644 --- a/src/kernel/kernel.c +++ b/src/kernel/kernel.c @@ -19,6 +19,8 @@ #include "string.h" +#include "driver/tty.h" + #include "keyboard/event_queue.h" #include "keyboard/handler.h" @@ -57,25 +59,17 @@ void kernel_main() { KERNEL_INIT("PS/2 controller", ps2_init); KERNEL_INIT("Keyboard Driver", init_keyboard); - pic_clear_mask(1); + tty_t tty0; + tty_init(&tty0); + + int i = 0; while (1) { - input_event_t event; - input_queue_pop(&event); - - if (event.type == INPUT_CHAR) { - print("Character: "); - print_char(event.character); - print("\n"); - event = (input_event_t){0}; - event.type = 11; - } - } - for (;;) { - asm volatile("hlt"); + tty_process_events(&tty0); } + print("[\033[32mready\033[0m] Kernel initialized successfully!\n"); return; } \ No newline at end of file diff --git a/src/libs/stdio.c b/src/libs/stdio.c index fa49dad..c120319 100644 --- a/src/libs/stdio.c +++ b/src/libs/stdio.c @@ -5,11 +5,203 @@ #include "vga_lib.h" #include "string.h" +#include "keyboard/event_queue.h" #include "stdio.h" #define FORMATTER_BUFFER_SIZE 1024 static char buffer[FORMATTER_BUFFER_SIZE]; +static bool scan_has_pending; +static char scan_pending; + +static char scan_read_char(void) { + input_event_t event; + + if (scan_has_pending) { + scan_has_pending = false; + return scan_pending; + } + + while (input_queue_pop(&event)) { + if (event.action != KEY_PRESSED) + continue; + + if (event.type == INPUT_EVENT_CHAR) + return event.character; + + if (event.key == KEY_ENTER) + return '\n'; + } + + return '\0'; +} + +static void scan_unread_char(char character) { + scan_pending = character; + scan_has_pending = true; +} + +static bool scan_is_space(char character) { + return character == ' ' || character == '\t' || character == '\n' || character == '\r'; +} + +char scan_char(void) { + char character; + + do { + character = scan_read_char(); + } while (character == '\0'); + + return character; +} + +int scanf(const char *format, ...) { + va_list args; + int assigned = 0; + + va_start(args, format); + + while (*format) { + if (scan_is_space(*format)) { + while (scan_is_space(*format)) + format++; + + char character; + do { + character = scan_char(); + } while (scan_is_space(character)); + scan_unread_char(character); + continue; + } + + if (*format != '%') { + char character = scan_char(); + if (character != *format) { + scan_unread_char(character); + break; + } + format++; + continue; + } + + format++; + if (*format == '\0') + break; + + switch (*format) { + case 'c': { + char *value = va_arg(args, char *); + *value = scan_char(); + assigned++; + break; + } + + case 's': { + char *value = va_arg(args, char *); + char character; + size_t length = 0; + + do { + character = scan_char(); + } while (scan_is_space(character)); + + while (!scan_is_space(character)) { + value[length++] = character; + character = scan_char(); + } + value[length] = '\0'; + assigned++; + break; + } + + case 'd': { + int *value = va_arg(args, int *); + char character; + int sign = 1; + int number = 0; + + do { + character = scan_char(); + } while (scan_is_space(character)); + + if (character == '-' || character == '+') { + sign = character == '-' ? -1 : 1; + character = scan_char(); + } + + if (character < '0' || character > '9') { + scan_unread_char(character); + break; + } + + do { + number = number * 10 + (character - '0'); + character = scan_char(); + } while (character >= '0' && character <= '9'); + + scan_unread_char(character); + *value = number * sign; + assigned++; + break; + } + + case 'x': { + uint32_t *value = va_arg(args, uint32_t *); + char character; + uint32_t number = 0; + bool has_digit = false; + + do { + character = scan_char(); + } while (scan_is_space(character)); + + if (character == '0') { + char prefix = scan_char(); + if (prefix == 'x' || prefix == 'X') + character = scan_char(); + else + scan_unread_char(prefix); + } + + while ((character >= '0' && character <= '9') || + (character >= 'a' && character <= 'f') || + (character >= 'A' && character <= 'F')) { + uint32_t digit; + if (character >= '0' && character <= '9') + digit = (uint32_t)(character - '0'); + else if (character >= 'a' && character <= 'f') + digit = (uint32_t)(character - 'a' + 10); + else + digit = (uint32_t)(character - 'A' + 10); + + number = number * 16 + digit; + has_digit = true; + character = scan_char(); + } + + if (!has_digit) { + scan_unread_char(character); + break; + } + + scan_unread_char(character); + *value = number; + assigned++; + break; + } + + default: + scan_unread_char('%'); + va_end(args); + return assigned; + } + + format++; + } + + va_end(args); + return assigned; +} void print(const char *str) { while (*str) { @@ -133,68 +325,110 @@ char *formatter(const char *format, ...) { char *vformatter(const char *format, va_list args) { buffer[0] = '\0'; + size_t used = 0; while (*format) { - if (*format != '%') { char temp[2] = {*format, '\0'}; - strcat(buffer, temp); - + size_t len = strlen(temp); + if (used + len + 1 < sizeof(buffer)) { + strcat(buffer, temp); + used += len; + } format++; continue; } format++; + if (*format == '\0') { + if (used + 1 < sizeof(buffer)) { + buffer[used++] = '%'; + buffer[used] = '\0'; + } + break; + } switch (*format) { case 's': { const char *str = va_arg(args, const char *); - strcat(buffer, str); + size_t len = strlen(str); + if (used + len + 1 < sizeof(buffer)) { + strcat(buffer, str); + used += len; + } break; } case 'd': { int number = va_arg(args, int); - strcat(buffer, int2str(number)); + char *str = int2str(number); + size_t len = strlen(str); + if (used + len + 1 < sizeof(buffer)) { + strcat(buffer, str); + used += len; + } break; } case 'x': { uint32_t number = va_arg(args, uint32_t); - strcat(buffer, uint2str(number, 16, false)); + char *str = uint2str(number, 16, false); + size_t len = strlen(str); + if (used + len + 1 < sizeof(buffer)) { + strcat(buffer, str); + used += len; + } break; } case 'h': { uint32_t number = va_arg(args, uint32_t); - strcat(buffer, uint2str(number, 16, true)); + char *str = uint2str(number, 16, true); + size_t len = strlen(str); + if (used + len + 1 < sizeof(buffer)) { + strcat(buffer, str); + used += len; + } break; } case 'b': { uint32_t number = va_arg(args, uint32_t); - strcat(buffer, uint2str(number, 2, false)); + char *str = uint2str(number, 2, false); + size_t len = strlen(str); + if (used + len + 1 < sizeof(buffer)) { + strcat(buffer, str); + used += len; + } break; } case 'c': { char temp[2]; - temp[0] = (char)va_arg(args, int); temp[1] = '\0'; - - strcat(buffer, temp); + size_t len = strlen(temp); + if (used + len + 1 < sizeof(buffer)) { + strcat(buffer, temp); + used += len; + } break; } case '%': { - strcat(buffer, "%"); + if (used + 2 < sizeof(buffer)) { + strcat(buffer, "%"); + used++; + } break; } default: { - strcat(buffer, "%"); + if (used + 2 < sizeof(buffer)) { + strcat(buffer, "%"); + used++; + } break; } } diff --git a/src/libs/string.c b/src/libs/string.c index 4911539..ed8d14a 100644 --- a/src/libs/string.c +++ b/src/libs/string.c @@ -2,7 +2,7 @@ #include "types.h" #include -int strlen(char *str) { +int strlen(const char *str) { int len = 0; while (*str++) { len++; @@ -14,24 +14,23 @@ char *int2str(int num) { static char str[33]; char *p = str + 32; - *p = '\0'; if (num == 0) { *(--p) = '0'; - } else { - bool negative = num < 0; + return p; + } - if (negative) - num = -num; + bool negative = num < 0; + uint32_t value = (uint32_t)(negative ? -(num + 1) + 1 : num); - while (num > 0) { - *(--p) = (num % 10) + '0'; - num /= 10; - } + while (value > 0) { + *(--p) = (char)('0' + (value % 10)); + value /= 10; + } - if (negative) - *(--p) = '-'; + if (negative) { + *(--p) = '-'; } return p;