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 @@
+
\ 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;