parent
fecf3b2e42
commit
c8edeab750
@ -0,0 +1,78 @@ |
|||||||
|
#include "src/app/cat.h" |
||||||
|
#include "src/fs.h" |
||||||
|
#include "src/memory.h" |
||||||
|
#include "src/string.h" |
||||||
|
#include "src/util.h" |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
|
||||||
|
static stream_t *stream_in; |
||||||
|
static stream_t *stream_out; |
||||||
|
|
||||||
|
#define WRITE_S(s) stream_out->write(stream_out, s, sizeof(s) - 1); |
||||||
|
#define WRITE_D(s) stream_out->write(stream_out, s, string_length(s)); |
||||||
|
|
||||||
|
void cat(stream_t *stdin, stream_t *stdout, uint8_t argc, char **argv) { |
||||||
|
stream_in = stdin; |
||||||
|
stream_out = stdout; |
||||||
|
|
||||||
|
if (argc != 2) { |
||||||
|
WRITE_S("cat: requires a single path\n"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
char *path_components[16]; |
||||||
|
uint64_t path_length = string_split(argv[1], '/', 16, path_components); |
||||||
|
if (!string_empty(path_components[0])) { |
||||||
|
WRITE_S("cat: relative paths not supported\n"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
fs_node_t *root = fs_mount(); |
||||||
|
|
||||||
|
fs_node_t *prev = NUL; |
||||||
|
fs_node_t *curr = root; |
||||||
|
uint8_t i = 1; |
||||||
|
while (i < path_length) { |
||||||
|
if (string_empty(path_components[i])) { |
||||||
|
i++; |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
if (!curr->is_dir) { |
||||||
|
WRITE_S("cat: can not navigate into a file\n"); |
||||||
|
fs_close(curr); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
prev = curr; |
||||||
|
curr = fs_open_by(curr, path_components[i]); |
||||||
|
if (prev != root) { |
||||||
|
fs_close(prev); |
||||||
|
} |
||||||
|
|
||||||
|
if (!curr) { |
||||||
|
WRITE_S("cat: invalid path\n"); |
||||||
|
return; |
||||||
|
} |
||||||
|
i++; |
||||||
|
} |
||||||
|
|
||||||
|
if (curr->is_dir) { |
||||||
|
WRITE_S("cat: can not print a directory\n"); |
||||||
|
if (curr != root) { |
||||||
|
fs_close(curr); |
||||||
|
} |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
char *content = memory_allocate(curr->size + 1); |
||||||
|
fs_read(curr, 0, curr->size, content); |
||||||
|
stream_out->write(stream_out, content, curr->size); |
||||||
|
memory_free(content); |
||||||
|
fs_close(curr); |
||||||
|
|
||||||
|
fs_unmount(root); |
||||||
|
|
||||||
|
stream_in = stream_out = NUL; |
||||||
|
} |
||||||
@ -0,0 +1,5 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
#include "src/stream.h" |
||||||
|
|
||||||
|
void cat(stream_t *stdin, stream_t *stdout, uint8_t argc, char **argv); |
||||||
@ -0,0 +1,81 @@ |
|||||||
|
#include "src/app/ls.h" |
||||||
|
#include "src/fs.h" |
||||||
|
#include "src/string.h" |
||||||
|
#include "src/util.h" |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
|
||||||
|
static stream_t *stream_in; |
||||||
|
static stream_t *stream_out; |
||||||
|
|
||||||
|
#define WRITE_S(s) stream_out->write(stream_out, s, sizeof(s) - 1); |
||||||
|
#define WRITE_D(s) stream_out->write(stream_out, s, string_length(s)); |
||||||
|
|
||||||
|
void ls(stream_t *stdin, stream_t *stdout, uint8_t argc, char **argv) { |
||||||
|
stream_in = stdin; |
||||||
|
stream_out = stdout; |
||||||
|
|
||||||
|
if (argc != 2) { |
||||||
|
WRITE_S("ls: requires a single path\n"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
char *path_components[16]; |
||||||
|
uint64_t path_length = string_split(argv[1], '/', 16, path_components); |
||||||
|
if (!string_empty(path_components[0])) { |
||||||
|
WRITE_S("ls: relative paths not supported\n"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
fs_node_t *root = fs_mount(); |
||||||
|
|
||||||
|
fs_node_t *prev = NUL; |
||||||
|
fs_node_t *curr = root; |
||||||
|
uint8_t i = 1; |
||||||
|
while (i < path_length) { |
||||||
|
if (string_empty(path_components[i])) { |
||||||
|
i++; |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
if (!curr->is_dir) { |
||||||
|
WRITE_S("ls: can not navigate into a file\n"); |
||||||
|
fs_close(curr); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
prev = curr; |
||||||
|
curr = fs_open_by(curr, path_components[i]); |
||||||
|
if (prev != root) { |
||||||
|
fs_close(prev); |
||||||
|
} |
||||||
|
|
||||||
|
if (!curr) { |
||||||
|
WRITE_S("ls: invalid path\n"); |
||||||
|
return; |
||||||
|
} |
||||||
|
i++; |
||||||
|
} |
||||||
|
|
||||||
|
if (!curr->is_dir) { |
||||||
|
WRITE_S("ls: can not list file\n"); |
||||||
|
fs_close(curr); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
uint8_t j = 0; |
||||||
|
fs_node_t *item = fs_open_at(curr, j++); |
||||||
|
while (item) { |
||||||
|
WRITE_D(item->name); |
||||||
|
WRITE_S("\n"); |
||||||
|
fs_close(item); |
||||||
|
item = fs_open_at(curr, j++); |
||||||
|
} |
||||||
|
if (curr != root) { |
||||||
|
fs_close(curr); |
||||||
|
} |
||||||
|
|
||||||
|
fs_unmount(root); |
||||||
|
|
||||||
|
stream_in = stream_out = NUL; |
||||||
|
} |
||||||
@ -0,0 +1,5 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
#include "src/stream.h" |
||||||
|
|
||||||
|
void ls(stream_t *stdin, stream_t *stdout, uint8_t argc, char **argv); |
||||||
@ -0,0 +1,255 @@ |
|||||||
|
#include "src/app/terminal.h" |
||||||
|
#include "src/app/cat.h" |
||||||
|
#include "src/app/ls.h" |
||||||
|
#include "src/fs.h" |
||||||
|
#include "src/memory.h" |
||||||
|
#include "src/string.h" |
||||||
|
#include "src/util.h" |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
|
||||||
|
#define PROMPT_LENGTH 255 |
||||||
|
|
||||||
|
fs_node_t *root; |
||||||
|
|
||||||
|
static const char bs[PROMPT_LENGTH + 1]; |
||||||
|
static const char ws[PROMPT_LENGTH + 1]; |
||||||
|
|
||||||
|
static char prompt[PROMPT_LENGTH + 1]; |
||||||
|
static uint8_t prompt_length = 0; |
||||||
|
static uint8_t prompt_offset = 0; |
||||||
|
|
||||||
|
static stream_t *stream_in; |
||||||
|
static stream_t *stream_out; |
||||||
|
|
||||||
|
static uint8_t active = 1; |
||||||
|
|
||||||
|
#define WRITE_S(s) stream_out->write(stream_out, s, sizeof(s) - 1); |
||||||
|
#define WRITE_D(s) stream_out->write(stream_out, s, string_length(s)); |
||||||
|
|
||||||
|
static void on_home_pressed() { |
||||||
|
if (!prompt_offset) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
stream_out->write(stream_out, bs, prompt_offset); |
||||||
|
prompt_offset = 0; |
||||||
|
} |
||||||
|
|
||||||
|
static void on_left_pressed() { |
||||||
|
if (!prompt_offset) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
stream_out->write(stream_out, bs, 1); |
||||||
|
prompt_offset--; |
||||||
|
} |
||||||
|
|
||||||
|
static void on_right_pressed() { |
||||||
|
if (prompt_offset == prompt_length) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
stream_out->write(stream_out, prompt + prompt_offset, 1); |
||||||
|
prompt_offset++; |
||||||
|
} |
||||||
|
|
||||||
|
static void on_end_pressed() { |
||||||
|
if (prompt_offset == prompt_length) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
stream_out->write(stream_out, prompt + prompt_offset, prompt_length - prompt_offset); |
||||||
|
prompt_offset = prompt_length; |
||||||
|
} |
||||||
|
|
||||||
|
static void help(uint8_t argc, __attribute__((unused)) char **argv) { |
||||||
|
if (argc > 1) { |
||||||
|
WRITE_S("help: accepts no arguments\n"); |
||||||
|
} |
||||||
|
|
||||||
|
WRITE_S("Available commands:\n" |
||||||
|
"help\n" |
||||||
|
"ls DIR\n" |
||||||
|
"cat FILE\n") |
||||||
|
} |
||||||
|
|
||||||
|
static void on_enter_pressed() { |
||||||
|
prompt[prompt_length] = '\0'; |
||||||
|
|
||||||
|
char *argv[16]; |
||||||
|
uint8_t argc = (uint8_t)string_split(prompt, ' ', 16, argv); |
||||||
|
|
||||||
|
WRITE_S("\n"); |
||||||
|
|
||||||
|
if (string_equal(argv[0], "help")) { |
||||||
|
help(argc, argv); |
||||||
|
} else if (string_equal(argv[0], "ls")) { |
||||||
|
ls(stream_in, stream_out, argc, argv); |
||||||
|
} else if (string_equal(argv[0], "cat")) { |
||||||
|
cat(stream_in, stream_out, argc, argv); |
||||||
|
} else { |
||||||
|
WRITE_S("Unknown command\n"); |
||||||
|
} |
||||||
|
WRITE_S("$ "); |
||||||
|
prompt_length = prompt_offset = 0; |
||||||
|
} |
||||||
|
|
||||||
|
static void on_cancel_pressed() { |
||||||
|
WRITE_S("^C\n"); |
||||||
|
WRITE_S("$ "); |
||||||
|
prompt_length = prompt_offset = 0; |
||||||
|
} |
||||||
|
|
||||||
|
static void on_disconnect_pressed() { |
||||||
|
WRITE_S("^D\n"); |
||||||
|
active = 0; |
||||||
|
} |
||||||
|
|
||||||
|
static void on_ctrl_character_pressed(char c) { |
||||||
|
if (c == 'c') { |
||||||
|
on_cancel_pressed(); |
||||||
|
} else if (c == 'd') { |
||||||
|
on_disconnect_pressed(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static void redraw_from_cursor() { |
||||||
|
uint64_t tail = prompt_length - prompt_offset; |
||||||
|
stream_out->write(stream_out, prompt + prompt_offset, tail); |
||||||
|
stream_out->write(stream_out, ws, 1); // erase the character past the end
|
||||||
|
stream_out->write(stream_out, bs, tail + 1); // move back to cursor position
|
||||||
|
} |
||||||
|
|
||||||
|
static void on_character_pressed(char c) { |
||||||
|
if (prompt_offset >= PROMPT_LENGTH) |
||||||
|
return; |
||||||
|
|
||||||
|
if (prompt_offset != prompt_length) { |
||||||
|
memory_move(prompt + prompt_offset, prompt_length - prompt_offset, prompt + prompt_offset + 1); |
||||||
|
} |
||||||
|
prompt[prompt_offset] = c; |
||||||
|
prompt_length++; |
||||||
|
prompt_offset++; |
||||||
|
|
||||||
|
stream_out->write(stream_out, &c, 1); // emit the character itself
|
||||||
|
redraw_from_cursor(); |
||||||
|
} |
||||||
|
|
||||||
|
static void on_backspace_pressed() { |
||||||
|
if (!prompt_offset) |
||||||
|
return; |
||||||
|
|
||||||
|
if (prompt_offset != prompt_length) { |
||||||
|
memory_move(prompt + prompt_offset, prompt_length - prompt_offset, prompt + prompt_offset - 1); |
||||||
|
} |
||||||
|
prompt_offset--; |
||||||
|
prompt_length--; |
||||||
|
|
||||||
|
stream_out->write(stream_out, bs, 1); |
||||||
|
redraw_from_cursor(); |
||||||
|
} |
||||||
|
|
||||||
|
static void on_delete_pressed() { |
||||||
|
if (prompt_offset == prompt_length) |
||||||
|
return; |
||||||
|
|
||||||
|
memory_move(prompt + prompt_offset + 1, prompt_length - prompt_offset - 1, prompt + prompt_offset); |
||||||
|
prompt_length--; |
||||||
|
|
||||||
|
redraw_from_cursor(); |
||||||
|
} |
||||||
|
|
||||||
|
typedef enum { |
||||||
|
PARSE_NORMAL, |
||||||
|
PARSE_ESC, |
||||||
|
PARSE_CSI, |
||||||
|
} parse_state_t; |
||||||
|
|
||||||
|
static parse_state_t parser_state = PARSE_NORMAL; |
||||||
|
static char parser_csi_param[8]; |
||||||
|
static uint8_t parser_csi_len; |
||||||
|
|
||||||
|
static void on_char_received(char c) { |
||||||
|
switch (parser_state) { |
||||||
|
case PARSE_NORMAL: |
||||||
|
if (c == '\x1B') { |
||||||
|
parser_state = PARSE_ESC; |
||||||
|
} else if (c == '\b' || c == '\x7F') { |
||||||
|
on_backspace_pressed(); |
||||||
|
} else if (c == '\n' || c == '\r') { |
||||||
|
on_enter_pressed(); |
||||||
|
} else if (c >= '\x01' && c <= '\x1A') { |
||||||
|
on_ctrl_character_pressed(c + 'a' - 1); |
||||||
|
} else if (c >= ' ') { |
||||||
|
on_character_pressed(c); |
||||||
|
} |
||||||
|
break; |
||||||
|
|
||||||
|
case PARSE_ESC: |
||||||
|
if (c == '[') { |
||||||
|
parser_state = PARSE_CSI; |
||||||
|
parser_csi_len = 0; |
||||||
|
} else { |
||||||
|
parser_state = PARSE_NORMAL; |
||||||
|
} |
||||||
|
break; |
||||||
|
|
||||||
|
case PARSE_CSI: |
||||||
|
if ((c >= '0' && c <= '9') || c == ';') { |
||||||
|
if (parser_csi_len < sizeof(parser_csi_param) - 1) { |
||||||
|
parser_csi_param[parser_csi_len++] = c; |
||||||
|
} |
||||||
|
} else { |
||||||
|
parser_state = PARSE_NORMAL; |
||||||
|
parser_csi_param[parser_csi_len] = '\0'; |
||||||
|
|
||||||
|
switch (c) { |
||||||
|
case 'C': |
||||||
|
on_right_pressed(); |
||||||
|
break; |
||||||
|
case 'D': |
||||||
|
on_left_pressed(); |
||||||
|
break; |
||||||
|
case 'H': |
||||||
|
on_home_pressed(); |
||||||
|
break; |
||||||
|
case 'F': |
||||||
|
on_end_pressed(); |
||||||
|
break; |
||||||
|
case '~': |
||||||
|
if (parser_csi_param[0] == '3') { |
||||||
|
on_delete_pressed(); |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void terminal(stream_t *stdin, stream_t *stdout, __attribute__((unused)) uint8_t argc, __attribute__((unused)) char **argv) { |
||||||
|
memory_set('\b', PROMPT_LENGTH, (char *)bs); |
||||||
|
memory_set(' ', PROMPT_LENGTH, (char *)ws); |
||||||
|
|
||||||
|
stream_in = stdin; |
||||||
|
stream_out = stdout; |
||||||
|
|
||||||
|
root = fs_mount(); |
||||||
|
|
||||||
|
char *greeting = string_format("Welcome to FreywarOS v%s!\n\n", VERSION); |
||||||
|
WRITE_D(greeting); |
||||||
|
memory_free(greeting); |
||||||
|
|
||||||
|
WRITE_S("$ "); |
||||||
|
|
||||||
|
while (active) { |
||||||
|
char c; |
||||||
|
if (stream_in->read(stream_in, 1, &c)) { |
||||||
|
on_char_received(c); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// fs_unmount(root);
|
||||||
|
stream_in = stream_out = NUL; |
||||||
|
} |
||||||
@ -0,0 +1,5 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
#include "src/stream.h" |
||||||
|
|
||||||
|
void terminal(stream_t *stdin, stream_t *stdout, uint8_t argc, char **argv); |
||||||
@ -1,22 +1,161 @@ |
|||||||
#include "src/keyboard.h" |
#include "src/keyboard.h" |
||||||
#include "src/idt.h" |
#include "src/idt.h" |
||||||
|
#include "src/memory.h" |
||||||
|
#include "src/stream.h" |
||||||
#include "src/util.h" |
#include "src/util.h" |
||||||
|
#include <stdint.h> |
||||||
|
|
||||||
static keyboard_handler_t current_handler = 0; |
#define KEYBOARD_STATE_LSHIFT 0b00000001 |
||||||
|
#define KEYBOARD_STATE_RSHIFT 0b00000010 |
||||||
|
#define KEYBOARD_STATE_LCTRL 0b00000100 |
||||||
|
#define KEYBOARD_STATE_RCTRL 0b00001000 |
||||||
|
#define KEYBOARD_STATE_LALT 0b00010000 |
||||||
|
#define KEYBOARD_STATE_RALT 0b00100000 |
||||||
|
#define KEYBOARD_STATE_SEQ 0b01000000 |
||||||
|
|
||||||
__attribute__((interrupt)) static void isr_keyboard([[maybe_unused]] struct interrupt_frame *frame) { |
static uint8_t keyboard_state = 0; |
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
static const char scancode_normal[128] = { |
||||||
|
0, 0, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', |
||||||
|
'i', 'o', 'p', '[', ']', '\n', 0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0, '\\', 'z', 'x', |
||||||
|
'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, '*', 0, ' ', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0, 0, '+', 0, 0, 0, 0, 0, 0, 0, 0, 0 |
||||||
|
}; |
||||||
|
|
||||||
|
static const char scancode_shifted[128] = { |
||||||
|
0, 0, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\b', '\t', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', |
||||||
|
'I', 'O', 'P', '{', '}', '\n', 0, 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', 0, '|', 'Z', 'X', |
||||||
|
'C', 'V', 'B', 'N', 'M', '<', '>', '?', 0, '*', 0, ' ', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0, 0, '+', 0, 0, 0, 0, 0, 0, 0, 0, 0 |
||||||
|
}; |
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
#define BUFFER_SIZE 256 |
||||||
|
|
||||||
|
static char buffer[BUFFER_SIZE]; |
||||||
|
static uint16_t buffer_offset = 0; |
||||||
|
static uint16_t buffer_length = 0; |
||||||
|
|
||||||
|
static void append(char c) { |
||||||
|
buffer[(buffer_offset + buffer_length) % BUFFER_SIZE] = c; |
||||||
|
if (buffer_length < BUFFER_SIZE) { |
||||||
|
buffer_length++; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static void append_sequence(const char *s) { |
||||||
|
while (*s) { |
||||||
|
append(*s); |
||||||
|
s++; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static void stream_write(__attribute__((unused)) stream_t *self, __attribute__((unused)) const char *from, __attribute__((unused)) uint64_t size) { |
||||||
|
} |
||||||
|
|
||||||
|
static uint64_t stream_read(__attribute__((unused)) stream_t *self, uint64_t max, char *to) { |
||||||
|
if (max == 0 || buffer_length == 0) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
uint64_t size = buffer_length > max ? max : buffer_length; |
||||||
|
|
||||||
|
if (buffer_offset + size < BUFFER_SIZE) { |
||||||
|
memory_copy(buffer + buffer_offset, size, to); |
||||||
|
} else { |
||||||
|
uint64_t chunk_0 = BUFFER_SIZE - buffer_offset; |
||||||
|
memory_copy(buffer + buffer_offset, chunk_0, to); |
||||||
|
memory_copy(buffer, size - chunk_0, to + chunk_0); |
||||||
|
} |
||||||
|
|
||||||
|
buffer_length -= size; |
||||||
|
buffer_offset = (buffer_offset + size) % BUFFER_SIZE; |
||||||
|
|
||||||
|
return size; |
||||||
|
} |
||||||
|
|
||||||
|
static stream_t stream = {stream_write, stream_read}; |
||||||
|
|
||||||
|
static void on_key(uint8_t scancode) { |
||||||
|
const uint8_t pressed = !(scancode & 0x80); |
||||||
|
const uint8_t code = scancode & ~0x80; |
||||||
|
|
||||||
|
if (!(keyboard_state & KEYBOARD_STATE_SEQ)) { |
||||||
|
switch (code) { |
||||||
|
case 0x60: |
||||||
|
keyboard_state = keyboard_state | KEYBOARD_STATE_SEQ; |
||||||
|
break; |
||||||
|
case 0x2A: |
||||||
|
keyboard_state = pressed ? keyboard_state | KEYBOARD_STATE_LSHIFT : keyboard_state & ~KEYBOARD_STATE_LSHIFT; |
||||||
|
break; |
||||||
|
case 0x36: |
||||||
|
keyboard_state = pressed ? keyboard_state | KEYBOARD_STATE_RSHIFT : keyboard_state & ~KEYBOARD_STATE_RSHIFT; |
||||||
|
break; |
||||||
|
case 0x38: |
||||||
|
keyboard_state = pressed ? keyboard_state | KEYBOARD_STATE_LALT : keyboard_state & ~KEYBOARD_STATE_LALT; |
||||||
|
break; |
||||||
|
case 0x1D: |
||||||
|
keyboard_state = pressed ? keyboard_state | KEYBOARD_STATE_LCTRL : keyboard_state & ~KEYBOARD_STATE_LCTRL; |
||||||
|
break; |
||||||
|
case 0x0E: |
||||||
|
pressed ? append('\b') : 0; |
||||||
|
break; |
||||||
|
case 0x1C: |
||||||
|
pressed ? append('\n') : 0; |
||||||
|
break; |
||||||
|
default: |
||||||
|
if (pressed && scancode_normal[code]) { |
||||||
|
if (keyboard_state && (keyboard_state & (KEYBOARD_STATE_LCTRL | KEYBOARD_STATE_RCTRL)) == keyboard_state) { |
||||||
|
if ((scancode_normal[code] >= '0' && scancode_normal[code] <= '9') || |
||||||
|
(scancode_normal[code] >= 'a' && scancode_normal[code] <= 'z')) { |
||||||
|
append(scancode_normal[code] - 'a' + 1); |
||||||
|
} |
||||||
|
} else if (keyboard_state && (keyboard_state & (KEYBOARD_STATE_LALT | KEYBOARD_STATE_RALT)) == keyboard_state) { |
||||||
|
// TODO Alt combinations.
|
||||||
|
} else { |
||||||
|
append((keyboard_state & (KEYBOARD_STATE_LSHIFT | KEYBOARD_STATE_RSHIFT) ? scancode_shifted : scancode_normal)[code]); |
||||||
|
} |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
} else { |
||||||
|
keyboard_state = keyboard_state & ~KEYBOARD_STATE_SEQ; |
||||||
|
switch (code) { |
||||||
|
case 0x38: |
||||||
|
keyboard_state = pressed ? keyboard_state | KEYBOARD_STATE_RALT : keyboard_state & ~KEYBOARD_STATE_RALT; |
||||||
|
break; |
||||||
|
case 0x1D: |
||||||
|
keyboard_state = pressed ? keyboard_state | KEYBOARD_STATE_RCTRL : keyboard_state & ~KEYBOARD_STATE_RCTRL; |
||||||
|
break; |
||||||
|
case 0x47: |
||||||
|
pressed ? append_sequence(STREAM_SEQ_HOME) : 0; |
||||||
|
break; |
||||||
|
case 0x4B: |
||||||
|
pressed ? append_sequence(STREAM_SEQ_LEFT) : 0; |
||||||
|
break; |
||||||
|
case 0x4D: |
||||||
|
pressed ? append_sequence(STREAM_SEQ_RIGHT) : 0; |
||||||
|
break; |
||||||
|
case 0x4F: |
||||||
|
pressed ? append_sequence(STREAM_SEQ_END) : 0; |
||||||
|
break; |
||||||
|
case 0x53: |
||||||
|
pressed ? append_sequence(STREAM_SEQ_DELETE) : 0; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
__attribute__((interrupt)) static void isr_keyboard(__attribute__((unused)) struct interrupt_frame *frame) { |
||||||
uint8_t scancode = inb(0x60); |
uint8_t scancode = inb(0x60); |
||||||
outb(0x20, 0x20); |
outb(0x20, 0x20); |
||||||
if (current_handler) { |
on_key(scancode); |
||||||
current_handler(scancode); |
|
||||||
} |
|
||||||
} |
} |
||||||
|
|
||||||
void keyboard_init() { |
stream_t *keyboard_init() { |
||||||
outb(0x21, inb(0x21) & ~0x02); |
outb(0x21, inb(0x21) & ~0x02); |
||||||
idt_set_entry(33, isr_keyboard, 0x8E); |
idt_set_entry(33, isr_keyboard, 0x8E); |
||||||
} |
|
||||||
|
|
||||||
void keyboard_set_handler(keyboard_handler_t handler) { |
return &stream; |
||||||
current_handler = handler; |
|
||||||
} |
} |
||||||
|
|||||||
@ -1,9 +1,6 @@ |
|||||||
#pragma once |
#pragma once |
||||||
|
|
||||||
|
#include "src/stream.h" |
||||||
#include <stdint.h> |
#include <stdint.h> |
||||||
|
|
||||||
void keyboard_init(); |
stream_t *keyboard_init(); |
||||||
|
|
||||||
typedef void (*keyboard_handler_t)(uint8_t scancode); |
|
||||||
|
|
||||||
void keyboard_set_handler(keyboard_handler_t handler); |
|
||||||
|
|||||||
@ -0,0 +1,36 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
|
||||||
|
#define STREAM_SEQ_UP "\x1B[A" |
||||||
|
#define STREAM_SEQ_DOWN "\x1B[B" |
||||||
|
#define STREAM_SEQ_RIGHT "\x1B[C" |
||||||
|
#define STREAM_SEQ_LEFT "\x1B[D" |
||||||
|
#define STREAM_SEQ_HOME "\x1B[H" |
||||||
|
#define STREAM_SEQ_END "\x1B[F" |
||||||
|
#define STREAM_SEQ_INSERT "\x1B[2~" |
||||||
|
#define STREAM_SEQ_DELETE "\x1B[3~" |
||||||
|
#define STREAM_SEQ_PAGE_UP "\x1B[5~" |
||||||
|
#define STREAM_SEQ_PAGE_DOWN "\x1B[6~" |
||||||
|
#define STREAM_SEQ_F1 "\x1B[11~" |
||||||
|
#define STREAM_SEQ_F2 "\x1B[12~" |
||||||
|
#define STREAM_SEQ_F3 "\x1B[13~" |
||||||
|
#define STREAM_SEQ_F4 "\x1B[14~" |
||||||
|
#define STREAM_SEQ_F5 "\x1B[15~" |
||||||
|
#define STREAM_SEQ_F6 "\x1B[16~" |
||||||
|
#define STREAM_SEQ_F7 "\x1B[17~" |
||||||
|
#define STREAM_SEQ_F8 "\x1B[18~" |
||||||
|
#define STREAM_SEQ_F9 "\x1B[19~" |
||||||
|
#define STREAM_SEQ_F10 "\x1B[1A~" |
||||||
|
#define STREAM_SEQ_F11 "\x1B[1B~" |
||||||
|
#define STREAM_SEQ_F12 "\x1B[1C~" |
||||||
|
|
||||||
|
typedef struct stream stream_t; |
||||||
|
|
||||||
|
typedef void (*stream_write_t)(stream_t *self, const char *from, uint64_t size); |
||||||
|
typedef uint64_t (*stream_read_t)(stream_t *self, uint64_t max, char *to); |
||||||
|
|
||||||
|
typedef struct stream { |
||||||
|
stream_write_t write; |
||||||
|
stream_read_t read; |
||||||
|
} stream_t; |
||||||
@ -1,397 +0,0 @@ |
|||||||
#include "src/terminal.h" |
|
||||||
#include "src/fs.h" |
|
||||||
#include "src/keyboard.h" |
|
||||||
#include "src/memory.h" |
|
||||||
#include "src/string.h" |
|
||||||
#include "src/util.h" |
|
||||||
#include "src/vga.h" |
|
||||||
#include <stdint.h> |
|
||||||
|
|
||||||
#define KEYBOARD_STATE_LSHIFT 0b00000001 |
|
||||||
#define KEYBOARD_STATE_RSHIFT 0b00000010 |
|
||||||
#define KEYBOARD_STATE_LCTRL 0b00000100 |
|
||||||
#define KEYBOARD_STATE_RCTRL 0b00001000 |
|
||||||
#define KEYBOARD_STATE_LALT 0b00010000 |
|
||||||
#define KEYBOARD_STATE_RALT 0b00100000 |
|
||||||
#define KEYBOARD_STATE_SEQ 0b01000000 |
|
||||||
|
|
||||||
static uint8_t keyboard_state = 0; |
|
||||||
|
|
||||||
// clang-format off
|
|
||||||
static const char scancode_normal[128] = { |
|
||||||
0, 0, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', |
|
||||||
'i', 'o', 'p', '[', ']', '\n', 0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0, '\\', 'z', 'x', |
|
||||||
'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, '*', 0, ' ', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0, 0, '+', 0, 0, 0, 0, 0, 0, 0, 0, 0 |
|
||||||
}; |
|
||||||
|
|
||||||
static const char scancode_shifted[128] = { |
|
||||||
0, 0, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\b', '\t', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', |
|
||||||
'I', 'O', 'P', '{', '}', '\n', 0, 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', 0, '|', 'Z', 'X', |
|
||||||
'C', 'V', 'B', 'N', 'M', '<', '>', '?', 0, '*', 0, ' ', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0, 0, '+', 0, 0, 0, 0, 0, 0, 0, 0, 0 |
|
||||||
}; |
|
||||||
// clang-format on
|
|
||||||
|
|
||||||
#define PROMPT_PREFIX 2 |
|
||||||
#define PROMPT_LENGTH 255 |
|
||||||
|
|
||||||
fs_node_t *root; |
|
||||||
|
|
||||||
static char prompt[PROMPT_LENGTH + 1]; |
|
||||||
static char prompt_swap[PROMPT_LENGTH + 1]; |
|
||||||
static uint8_t prompt_row = 0; |
|
||||||
static uint8_t prompt_length = 0; |
|
||||||
static uint8_t prompt_offset = 0; |
|
||||||
static char prompt_buf[VGA_WIDTH + 1] = "$ "; |
|
||||||
|
|
||||||
static void render_prompt() { |
|
||||||
uint8_t offset = PROMPT_PREFIX + prompt_offset < VGA_WIDTH ? 0 : PROMPT_PREFIX + prompt_offset - VGA_WIDTH + 1; |
|
||||||
uint8_t length = PROMPT_PREFIX + prompt_length < VGA_WIDTH ? prompt_length : VGA_WIDTH - PROMPT_PREFIX; |
|
||||||
prompt_buf[1] = PROMPT_PREFIX + prompt_offset < VGA_WIDTH ? ' ' : '<'; |
|
||||||
memory_copy(prompt + offset, length, prompt_buf + PROMPT_PREFIX); |
|
||||||
if (PROMPT_PREFIX + prompt_length > VGA_WIDTH && prompt_offset < prompt_length - 1) { |
|
||||||
prompt_buf[VGA_WIDTH - 1] = '>'; |
|
||||||
} |
|
||||||
prompt_buf[PROMPT_PREFIX + length] = '\0'; |
|
||||||
|
|
||||||
vga_clear(prompt_row, 1); |
|
||||||
vga_set_string(prompt_row, 0, prompt_buf, 0x0F); |
|
||||||
vga_set_cursor(prompt_row, PROMPT_PREFIX + prompt_offset - offset); |
|
||||||
} |
|
||||||
|
|
||||||
static void on_home_pressed() { |
|
||||||
if (keyboard_state || !prompt_offset) { |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
prompt_offset = 0; |
|
||||||
render_prompt(); |
|
||||||
} |
|
||||||
|
|
||||||
static void on_left_pressed() { |
|
||||||
if (keyboard_state || !prompt_offset) { |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
prompt_offset--; |
|
||||||
render_prompt(); |
|
||||||
} |
|
||||||
|
|
||||||
static void on_right_pressed() { |
|
||||||
if (keyboard_state || prompt_offset == prompt_length) { |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
prompt_offset++; |
|
||||||
render_prompt(); |
|
||||||
} |
|
||||||
|
|
||||||
static void on_end_pressed() { |
|
||||||
if (keyboard_state || prompt_offset == prompt_length) { |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
prompt_offset = prompt_length; |
|
||||||
render_prompt(); |
|
||||||
} |
|
||||||
|
|
||||||
static void advance_row(uint8_t rows) { |
|
||||||
while (rows--) { |
|
||||||
if (prompt_row == VGA_HEIGHT - 1) { |
|
||||||
vga_copy(1, VGA_HEIGHT - 1, 0); |
|
||||||
} else { |
|
||||||
prompt_row++; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
static void print_line(const char *string) { |
|
||||||
advance_row(1); |
|
||||||
vga_clear(prompt_row - 1, 1); |
|
||||||
vga_set_string(prompt_row - 1, 0, string, 0x0F); |
|
||||||
} |
|
||||||
|
|
||||||
static void help(uint8_t argc, [[maybe_unused]] char **argv) { |
|
||||||
if (argc > 1) { |
|
||||||
print_line("help: accepts no arguments"); |
|
||||||
} |
|
||||||
|
|
||||||
advance_row(4); |
|
||||||
vga_set_string(prompt_row - 4, 0, "Available commands:", 0x0F); |
|
||||||
vga_set_string(prompt_row - 3, 0, "help", 0x0F); |
|
||||||
vga_set_string(prompt_row - 2, 0, "ls DIR", 0x0F); |
|
||||||
vga_set_string(prompt_row - 1, 0, "cat FILE", 0x0F); |
|
||||||
} |
|
||||||
|
|
||||||
static void ls(uint8_t argc, char **argv) { |
|
||||||
if (argc != 2) { |
|
||||||
print_line("ls: requires a single path"); |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
char *path_components[16]; |
|
||||||
uint64_t path_length = string_split(argv[1], '/', 16, path_components); |
|
||||||
if (!string_empty(path_components[0])) { |
|
||||||
print_line("ls: relative paths not supported"); |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
fs_node_t *prev = NUL; |
|
||||||
fs_node_t *curr = root; |
|
||||||
uint8_t i = 1; |
|
||||||
while (i < path_length) { |
|
||||||
if (string_empty(path_components[i])) { |
|
||||||
i++; |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
if (!curr->is_dir) { |
|
||||||
print_line("ls: can not navigate into a file"); |
|
||||||
fs_close(curr); |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
prev = curr; |
|
||||||
curr = fs_open_by(curr, path_components[i]); |
|
||||||
if (prev != root) { |
|
||||||
fs_close(prev); |
|
||||||
} |
|
||||||
|
|
||||||
if (!curr) { |
|
||||||
print_line("ls: invalid path"); |
|
||||||
return; |
|
||||||
} |
|
||||||
i++; |
|
||||||
} |
|
||||||
|
|
||||||
if (!curr->is_dir) { |
|
||||||
print_line("ls: can not list file"); |
|
||||||
fs_close(curr); |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
uint8_t j = 0; |
|
||||||
fs_node_t *item = fs_open_at(curr, j++); |
|
||||||
while (item) { |
|
||||||
print_line(item->name); |
|
||||||
fs_close(item); |
|
||||||
item = fs_open_at(curr, j++); |
|
||||||
} |
|
||||||
if (curr != root) { |
|
||||||
fs_close(curr); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
static void cat(uint8_t argc, char **argv) { |
|
||||||
if (argc != 2) { |
|
||||||
print_line("cat: requires a single path"); |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
char *path_components[16]; |
|
||||||
uint64_t path_length = string_split(argv[1], '/', 16, path_components); |
|
||||||
if (!string_empty(path_components[0])) { |
|
||||||
print_line("cat: relative paths not supported"); |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
fs_node_t *prev = NUL; |
|
||||||
fs_node_t *curr = root; |
|
||||||
uint8_t i = 1; |
|
||||||
while (i < path_length) { |
|
||||||
if (string_empty(path_components[i])) { |
|
||||||
i++; |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
if (!curr->is_dir) { |
|
||||||
print_line("cat: can not navigate into a file"); |
|
||||||
fs_close(curr); |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
prev = curr; |
|
||||||
curr = fs_open_by(curr, path_components[i]); |
|
||||||
if (prev != root) { |
|
||||||
fs_close(prev); |
|
||||||
} |
|
||||||
|
|
||||||
if (!curr) { |
|
||||||
print_line("cat: invalid path"); |
|
||||||
return; |
|
||||||
} |
|
||||||
i++; |
|
||||||
} |
|
||||||
|
|
||||||
if (curr->is_dir) { |
|
||||||
print_line("cat: can not print a directory"); |
|
||||||
if (curr != root) { |
|
||||||
fs_close(curr); |
|
||||||
} |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
char *content = memory_allocate(curr->size + 1); |
|
||||||
fs_read(curr, 0, curr->size, content); |
|
||||||
content[curr->size] = '\0'; |
|
||||||
|
|
||||||
char **lines = memory_allocate(curr->size * sizeof(char *)); |
|
||||||
uint64_t lines_count = string_split(content, '\n', curr->size, lines); |
|
||||||
|
|
||||||
for (uint64_t i = 0; i < lines_count; i++) { |
|
||||||
print_line(lines[i]); |
|
||||||
} |
|
||||||
|
|
||||||
memory_free(lines); |
|
||||||
memory_free(content); |
|
||||||
fs_close(curr); |
|
||||||
} |
|
||||||
|
|
||||||
static void on_enter_pressed() { |
|
||||||
prompt[prompt_length] = '\0'; |
|
||||||
|
|
||||||
char *argv[16]; |
|
||||||
uint8_t argc = (uint8_t)string_split(prompt, ' ', 16, argv); |
|
||||||
|
|
||||||
advance_row(1); |
|
||||||
|
|
||||||
if (string_equal(argv[0], "help")) { |
|
||||||
help(argc, argv); |
|
||||||
} else if (string_equal(argv[0], "ls")) { |
|
||||||
ls(argc, argv); |
|
||||||
} else if (string_equal(argv[0], "cat")) { |
|
||||||
cat(argc, argv); |
|
||||||
} else { |
|
||||||
print_line("Unknown command"); |
|
||||||
} |
|
||||||
prompt_length = prompt_offset = 0; |
|
||||||
render_prompt(); |
|
||||||
} |
|
||||||
|
|
||||||
static void on_cancel_pressed() { |
|
||||||
advance_row(1); |
|
||||||
prompt_length = prompt_offset = 0; |
|
||||||
render_prompt(); |
|
||||||
} |
|
||||||
|
|
||||||
static void on_character_pressed(uint8_t code) { |
|
||||||
if (keyboard_state && (keyboard_state & (KEYBOARD_STATE_LCTRL | KEYBOARD_STATE_RCTRL)) == keyboard_state) { |
|
||||||
switch (code) { |
|
||||||
case 0x2E: // C
|
|
||||||
on_cancel_pressed(); |
|
||||||
} |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
if (keyboard_state & ~(KEYBOARD_STATE_LSHIFT | KEYBOARD_STATE_RSHIFT) || prompt_length >= PROMPT_LENGTH) { |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
if (prompt_offset == prompt_length) { |
|
||||||
prompt[prompt_offset + 1] = '\0'; |
|
||||||
} else { |
|
||||||
memory_copy(prompt, prompt_length + 1, prompt_swap); |
|
||||||
memory_copy(prompt_swap + prompt_offset, prompt_length - prompt_offset, prompt + prompt_offset + 1); |
|
||||||
} |
|
||||||
prompt[prompt_offset] = (keyboard_state & (KEYBOARD_STATE_LSHIFT | KEYBOARD_STATE_RSHIFT) ? scancode_shifted : scancode_normal)[code]; |
|
||||||
prompt_offset++; |
|
||||||
prompt_length++; |
|
||||||
render_prompt(); |
|
||||||
} |
|
||||||
|
|
||||||
static void on_backspace_pressed() { |
|
||||||
if (keyboard_state || !prompt_offset) { |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
if (prompt_offset == prompt_length) { |
|
||||||
prompt[prompt_offset - 1] = '\0'; |
|
||||||
} else { |
|
||||||
memory_copy(prompt, prompt_length + 1, prompt_swap); |
|
||||||
memory_copy(prompt_swap + prompt_offset, prompt_length - prompt_offset, prompt + prompt_offset - 1); |
|
||||||
} |
|
||||||
prompt_offset--; |
|
||||||
prompt_length--; |
|
||||||
render_prompt(); |
|
||||||
} |
|
||||||
|
|
||||||
static void on_delete_pressed() { |
|
||||||
if (keyboard_state || prompt_offset == prompt_length) { |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
prompt_offset++; |
|
||||||
on_backspace_pressed(); |
|
||||||
} |
|
||||||
|
|
||||||
static void terminal_on_key(uint8_t scancode) { |
|
||||||
const uint8_t pressed = !(scancode & 0x80); |
|
||||||
const uint8_t code = scancode & ~0x80; |
|
||||||
|
|
||||||
if (!(keyboard_state & KEYBOARD_STATE_SEQ)) { |
|
||||||
switch (code) { |
|
||||||
case 0x60: |
|
||||||
keyboard_state = keyboard_state | KEYBOARD_STATE_SEQ; |
|
||||||
break; |
|
||||||
case 0x2A: |
|
||||||
keyboard_state = pressed ? keyboard_state | KEYBOARD_STATE_LSHIFT : keyboard_state & ~KEYBOARD_STATE_LSHIFT; |
|
||||||
break; |
|
||||||
case 0x36: |
|
||||||
keyboard_state = pressed ? keyboard_state | KEYBOARD_STATE_RSHIFT : keyboard_state & ~KEYBOARD_STATE_RSHIFT; |
|
||||||
break; |
|
||||||
case 0x38: |
|
||||||
keyboard_state = pressed ? keyboard_state | KEYBOARD_STATE_LALT : keyboard_state & ~KEYBOARD_STATE_LALT; |
|
||||||
break; |
|
||||||
case 0x1D: |
|
||||||
keyboard_state = pressed ? keyboard_state | KEYBOARD_STATE_LCTRL : keyboard_state & ~KEYBOARD_STATE_LCTRL; |
|
||||||
break; |
|
||||||
case 0x0E: |
|
||||||
pressed ? on_backspace_pressed() : 0; |
|
||||||
break; |
|
||||||
case 0x1C: |
|
||||||
pressed ? on_enter_pressed() : 0; |
|
||||||
break; |
|
||||||
default: |
|
||||||
pressed &&scancode_normal[code] ? on_character_pressed(code) : 0; |
|
||||||
break; |
|
||||||
} |
|
||||||
} else { |
|
||||||
keyboard_state = keyboard_state & ~KEYBOARD_STATE_SEQ; |
|
||||||
switch (code) { |
|
||||||
case 0x38: |
|
||||||
keyboard_state = pressed ? keyboard_state | KEYBOARD_STATE_RALT : keyboard_state & ~KEYBOARD_STATE_RALT; |
|
||||||
break; |
|
||||||
case 0x1D: |
|
||||||
keyboard_state = pressed ? keyboard_state | KEYBOARD_STATE_RCTRL : keyboard_state & ~KEYBOARD_STATE_RCTRL; |
|
||||||
break; |
|
||||||
case 0x47: |
|
||||||
pressed ? on_home_pressed() : 0; |
|
||||||
break; |
|
||||||
case 0x4B: |
|
||||||
pressed ? on_left_pressed() : 0; |
|
||||||
break; |
|
||||||
case 0x4D: |
|
||||||
pressed ? on_right_pressed() : 0; |
|
||||||
break; |
|
||||||
case 0x4F: |
|
||||||
pressed ? on_end_pressed() : 0; |
|
||||||
break; |
|
||||||
case 0x53: |
|
||||||
pressed ? on_delete_pressed() : 0; |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void terminal_init() { |
|
||||||
root = fs_mount(); |
|
||||||
|
|
||||||
keyboard_set_handler(terminal_on_key); |
|
||||||
vga_clear(0, VGA_HEIGHT); |
|
||||||
char *greeting = string_format("Welcome to FreywarOS v%s!", VERSION); |
|
||||||
vga_set_string(0, 0, greeting, 0x0F); |
|
||||||
memory_free(greeting); |
|
||||||
prompt_row += 2; |
|
||||||
render_prompt(); |
|
||||||
} |
|
||||||
@ -1,3 +0,0 @@ |
|||||||
#pragma once |
|
||||||
|
|
||||||
void terminal_init(); |
|
||||||
@ -1,48 +1,170 @@ |
|||||||
#include "src/vga.h" |
#include "src/vga.h" |
||||||
#include "src/memory.h" |
#include "src/memory.h" |
||||||
#include "src/panic.h" |
|
||||||
#include "src/util.h" |
#include "src/util.h" |
||||||
|
#include <stdint.h> |
||||||
|
|
||||||
static uint16_t *vga = (uint16_t *)0x000B8000; |
static uint16_t *vga = (uint16_t *)0x000B8000; |
||||||
|
static uint16_t color = (uint16_t)0x0F << 8; |
||||||
|
static uint16_t offset = 0; |
||||||
|
|
||||||
void vga_set_cursor(uint8_t row, uint8_t col) { |
static void set_char(char c) { |
||||||
ASSERT(row < VGA_HEIGHT && col < VGA_WIDTH, "vga_set_cursor: invalid coordinates") |
vga[offset] = color | (uint16_t)c; |
||||||
uint16_t pos = row * VGA_WIDTH + col; |
} |
||||||
|
|
||||||
|
static void set_cursor() { |
||||||
outb(0x3D4, 0x0F); |
outb(0x3D4, 0x0F); |
||||||
outb(0x3D5, pos & 0xFF); |
outb(0x3D5, offset & 0xFF); |
||||||
outb(0x3D4, 0x0E); |
outb(0x3D4, 0x0E); |
||||||
outb(0x3D5, (pos >> 8) & 0xFF); |
outb(0x3D5, (offset >> 8) & 0xFF); |
||||||
} |
} |
||||||
|
|
||||||
void vga_set_char(uint8_t row, uint8_t col, char c, uint8_t color) { |
static void scroll() { |
||||||
ASSERT(row < VGA_HEIGHT && col < VGA_WIDTH, "vga_set_char: invalid coordinates"); |
memory_move(vga + VGA_WIDTH, 2 * VGA_WIDTH * (VGA_HEIGHT - 1), vga); |
||||||
|
for (offset = VGA_WIDTH * (VGA_HEIGHT - 1); offset < VGA_WIDTH * VGA_HEIGHT; offset++) { |
||||||
|
set_char(' '); |
||||||
|
} |
||||||
|
offset = VGA_WIDTH * (VGA_HEIGHT - 1); |
||||||
|
} |
||||||
|
|
||||||
vga[row * VGA_WIDTH + col] = (uint16_t)(((uint16_t)color << 8) | (uint16_t)c); |
static void advance() { |
||||||
|
offset++; |
||||||
|
if (offset >= VGA_WIDTH * VGA_HEIGHT) { |
||||||
|
scroll(); |
||||||
|
} |
||||||
} |
} |
||||||
|
|
||||||
void vga_set_string(uint8_t row, uint8_t col, const char *str, uint8_t color) { |
static void clear() { |
||||||
ASSERT(row < VGA_HEIGHT && col < VGA_WIDTH, "vga_set_string: invalid coordinates") |
uint16_t *ptr = vga; |
||||||
while (*str) { |
uint16_t fill = 0x0F20; |
||||||
vga_set_char(row, col++, *str++, color); |
uint32_t count = VGA_WIDTH * VGA_HEIGHT; |
||||||
if (col >= VGA_WIDTH) { |
__asm__ volatile("rep stosw" : "=D"(ptr), "=c"(count) : "D"(ptr), "a"(fill), "c"(count) : "memory"); |
||||||
col = 0; |
|
||||||
row++; |
offset = 0; |
||||||
ASSERT(row < VGA_HEIGHT && col < VGA_WIDTH, "vga_set_string: invalid coordinates") |
set_cursor(); |
||||||
|
} |
||||||
|
|
||||||
|
typedef enum { |
||||||
|
PARSE_NORMAL, |
||||||
|
PARSE_ESC, |
||||||
|
PARSE_CSI, |
||||||
|
} parse_state_t; |
||||||
|
|
||||||
|
static parse_state_t parser_state = PARSE_NORMAL; |
||||||
|
static char parser_csi_param[8]; |
||||||
|
static uint8_t parser_csi_len; |
||||||
|
|
||||||
|
static void on_char_received(char c) { |
||||||
|
switch (parser_state) { |
||||||
|
case PARSE_NORMAL: |
||||||
|
switch (c) { |
||||||
|
case '\x1B': |
||||||
|
parser_state = PARSE_ESC; |
||||||
|
break; |
||||||
|
case '\b': |
||||||
|
case '\x7F': |
||||||
|
if (offset > 0) { |
||||||
|
offset--; |
||||||
|
set_cursor(); |
||||||
|
} |
||||||
|
break; |
||||||
|
case '\r': |
||||||
|
offset -= offset % VGA_WIDTH; |
||||||
|
set_cursor(); |
||||||
|
break; |
||||||
|
case '\n': |
||||||
|
if (offset / VGA_WIDTH == VGA_HEIGHT - 1) { |
||||||
|
scroll(); |
||||||
|
} else { |
||||||
|
offset += VGA_WIDTH; |
||||||
|
} |
||||||
|
offset -= offset % VGA_WIDTH; |
||||||
|
set_cursor(); |
||||||
|
break; |
||||||
|
default: |
||||||
|
if (c >= '\x01' && c <= '\x1A') { |
||||||
|
set_char('^'); |
||||||
|
advance(); |
||||||
|
set_char(c + 'A' - 1); |
||||||
|
advance(); |
||||||
|
set_cursor(); |
||||||
|
} else if (c >= ' ') { |
||||||
|
set_char(c); |
||||||
|
advance(); |
||||||
|
set_cursor(); |
||||||
|
} |
||||||
|
break; |
||||||
} |
} |
||||||
|
break; |
||||||
|
|
||||||
|
case PARSE_ESC: |
||||||
|
if (c == '[') { |
||||||
|
parser_state = PARSE_CSI; |
||||||
|
parser_csi_len = 0; |
||||||
|
} else { |
||||||
|
parser_state = PARSE_NORMAL; |
||||||
|
} |
||||||
|
break; |
||||||
|
|
||||||
|
case PARSE_CSI: |
||||||
|
if ((c >= '0' && c <= '9') || c == ';') { |
||||||
|
if (parser_csi_len < sizeof(parser_csi_param) - 1) { |
||||||
|
parser_csi_param[parser_csi_len++] = c; |
||||||
|
} |
||||||
|
} else { |
||||||
|
parser_state = PARSE_NORMAL; |
||||||
|
parser_csi_param[parser_csi_len] = '\0'; |
||||||
|
|
||||||
|
switch (c) { |
||||||
|
case 'C': |
||||||
|
if (offset < VGA_WIDTH * VGA_HEIGHT - 1) { |
||||||
|
offset++; |
||||||
|
} |
||||||
|
set_cursor(); |
||||||
|
break; |
||||||
|
case 'D': |
||||||
|
if (offset > 0) { |
||||||
|
offset--; |
||||||
|
} |
||||||
|
set_cursor(); |
||||||
|
break; |
||||||
|
case 'H': |
||||||
|
offset -= offset % VGA_WIDTH; |
||||||
|
set_cursor(); |
||||||
|
break; |
||||||
|
case 'F': |
||||||
|
offset -= offset % VGA_WIDTH; |
||||||
|
offset += VGA_WIDTH - 1; |
||||||
|
set_cursor(); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
break; |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
void vga_copy(uint8_t src_row, uint8_t rows, uint8_t dst_row) { |
static void stream_write(__attribute__((unused)) stream_t *self, const char *from, uint64_t size) { |
||||||
ASSERT(src_row < VGA_HEIGHT && dst_row < VGA_HEIGHT && src_row + rows <= VGA_HEIGHT && dst_row + rows <= VGA_HEIGHT, |
while (size--) { |
||||||
"vga_copy: invalid copy dimensions"); |
on_char_received(*from++); |
||||||
memory_move(vga + src_row * VGA_WIDTH, rows * VGA_WIDTH * 2, vga + dst_row * VGA_WIDTH); |
} |
||||||
} |
} |
||||||
|
|
||||||
void vga_clear(uint8_t row, uint8_t rows) { |
static uint64_t stream_read(__attribute__((unused)) stream_t *self, __attribute__((unused)) uint64_t max, |
||||||
ASSERT(row < VGA_HEIGHT && rows <= VGA_HEIGHT && row + rows <= VGA_HEIGHT, "vga_clear: invalid clear dimensions"); |
__attribute__((unused)) char *to) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
uint16_t *dst = vga + row * VGA_WIDTH; |
static stream_t stream = {stream_write, stream_read}; |
||||||
uint16_t fill = 0x0F20; |
|
||||||
uint32_t count = VGA_WIDTH * rows; |
stream_t *vga_init() { |
||||||
__asm__ volatile("rep stosw" : "=D"(dst), "=c"(count) : "D"(dst), "a"(fill), "c"(count) : "memory"); |
clear(); |
||||||
|
return &stream; |
||||||
|
} |
||||||
|
|
||||||
|
void vga_set_string(uint8_t row, uint8_t col, const char *str, uint8_t c) { |
||||||
|
ASSERT(row < VGA_HEIGHT && col < VGA_WIDTH, "vga_set_string: invalid coordinates") |
||||||
|
color = (uint16_t)((uint16_t)c << 8); |
||||||
|
offset = row * VGA_WIDTH + col; |
||||||
|
while (*str) { |
||||||
|
on_char_received(*str++); |
||||||
|
} |
||||||
} |
} |
||||||
|
|||||||
Loading…
Reference in new issue