Use streams for inter-"process" communications
This commit is contained in:
+3
-1
@@ -41,6 +41,9 @@ kernel_entry_o = custom_target(
|
|||||||
cc = meson.get_compiler('c')
|
cc = meson.get_compiler('c')
|
||||||
|
|
||||||
kernel_sources = files(
|
kernel_sources = files(
|
||||||
|
'src/app/cat.c',
|
||||||
|
'src/app/ls.c',
|
||||||
|
'src/app/terminal.c',
|
||||||
'src/ata.c',
|
'src/ata.c',
|
||||||
'src/fat16.c',
|
'src/fat16.c',
|
||||||
'src/fs.c',
|
'src/fs.c',
|
||||||
@@ -51,7 +54,6 @@ kernel_sources = files(
|
|||||||
'src/panic.c',
|
'src/panic.c',
|
||||||
'src/pic.c',
|
'src/pic.c',
|
||||||
'src/string.c',
|
'src/string.c',
|
||||||
'src/terminal.c',
|
|
||||||
'src/vga.c',
|
'src/vga.c',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
+2
-1
@@ -255,5 +255,6 @@ void fat16_close(fs_node_t *node) {
|
|||||||
void fat16_unmount(fs_node_t *node) {
|
void fat16_unmount(fs_node_t *node) {
|
||||||
ASSERT(node->type == FAT16, "fat16_unmount: node is not FAT16");
|
ASSERT(node->type == FAT16, "fat16_unmount: node is not FAT16");
|
||||||
ASSERT(node == fs, "fat16_unmount: node is not filesystem");
|
ASSERT(node == fs, "fat16_unmount: node is not filesystem");
|
||||||
ASSERT(0, "fat16_unmount: not implemented");
|
memory_free(node);
|
||||||
|
fs = NUL;
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-9
@@ -1,30 +1,31 @@
|
|||||||
|
#include "src/app/terminal.h"
|
||||||
#include "src/idt.h"
|
#include "src/idt.h"
|
||||||
#include "src/keyboard.h"
|
#include "src/keyboard.h"
|
||||||
#include "src/memory.h"
|
#include "src/memory.h"
|
||||||
#include "src/pic.h"
|
#include "src/pic.h"
|
||||||
#include "src/terminal.h"
|
#include "src/stream.h"
|
||||||
#include "src/util.h"
|
#include "src/util.h"
|
||||||
#include "src/vga.h"
|
#include "src/vga.h"
|
||||||
|
|
||||||
__attribute__((interrupt)) void isr_divide_by_zero([[maybe_unused]] struct interrupt_frame *frame) {
|
__attribute__((interrupt)) void isr_divide_by_zero(__attribute__((unused)) struct interrupt_frame *frame) {
|
||||||
vga_set_string(VGA_HEIGHT - 1, 0, "EXCEPTION: divide by zero", 0x4F);
|
vga_set_string(VGA_HEIGHT - 1, 0, "EXCEPTION: divide by zero", 0x4F);
|
||||||
while (1)
|
while (1)
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((interrupt)) void isr_page_fault([[maybe_unused]] struct interrupt_frame *frame) {
|
__attribute__((interrupt)) void isr_page_fault(__attribute__((unused)) struct interrupt_frame *frame) {
|
||||||
vga_set_string(VGA_HEIGHT - 1, 0, "EXCEPTION: page fault", 0x4F);
|
vga_set_string(VGA_HEIGHT - 1, 0, "EXCEPTION: page fault", 0x4F);
|
||||||
while (1)
|
while (1)
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((interrupt)) void isr_general_violation([[maybe_unused]] struct interrupt_frame *frame) {
|
__attribute__((interrupt)) void isr_general_violation(__attribute__((unused)) struct interrupt_frame *frame) {
|
||||||
vga_set_string(VGA_HEIGHT - 1, 0, "EXCEPTION: general violation", 0x4F);
|
vga_set_string(VGA_HEIGHT - 1, 0, "EXCEPTION: general violation", 0x4F);
|
||||||
while (1)
|
while (1)
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((interrupt)) void isr_ata_primary([[maybe_unused]] struct interrupt_frame *frame) {
|
__attribute__((interrupt)) void isr_ata_primary(__attribute__((unused)) struct interrupt_frame *frame) {
|
||||||
outb(0x20, 0x20);
|
outb(0x20, 0x20);
|
||||||
outb(0xA0, 0x20);
|
outb(0xA0, 0x20);
|
||||||
}
|
}
|
||||||
@@ -40,9 +41,12 @@ void kernel_main() {
|
|||||||
__asm__ volatile("sti");
|
__asm__ volatile("sti");
|
||||||
|
|
||||||
memory_init();
|
memory_init();
|
||||||
keyboard_init();
|
|
||||||
terminal_init();
|
|
||||||
|
|
||||||
while (1)
|
stream_t *stdin = keyboard_init();
|
||||||
;
|
stream_t *stdout = vga_init();
|
||||||
|
|
||||||
|
terminal(stdin, stdout, 0, NUL);
|
||||||
|
|
||||||
|
outw(0x604, 0x2000); // TODO: parse ACPI tables for real hardware
|
||||||
|
__asm__ volatile("cli; hlt");
|
||||||
}
|
}
|
||||||
|
|||||||
+148
-9
@@ -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;
|
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-5
@@ -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);
|
|
||||||
|
|||||||
+1
-1
@@ -40,7 +40,7 @@ static void *memory_page_allocate() {
|
|||||||
return (void *)((uint64_t)page * PAGE_SIZE);
|
return (void *)((uint64_t)page * PAGE_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[maybe_unused]] static void memory_page_free(void *address) {
|
__attribute__((unused)) static void memory_page_free(void *address) {
|
||||||
uint16_t page = (uint16_t)((uint64_t)address / PAGE_SIZE);
|
uint16_t page = (uint16_t)((uint64_t)address / PAGE_SIZE);
|
||||||
ASSERT(get_allocated(page), "memory_page_free: page not allocated")
|
ASSERT(get_allocated(page), "memory_page_free: page not allocated")
|
||||||
set_allocated(page, 0);
|
set_allocated(page, 0);
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
#include "src/panic.h"
|
#include "src/panic.h"
|
||||||
#include "src/vga.h"
|
#include "src/vga.h"
|
||||||
|
|
||||||
void kernel_panic(const char *msg, [[maybe_unused]] const char *file, [[maybe_unused]] int line) {
|
void kernel_panic(const char *msg, __attribute__((unused)) const char *file, __attribute__((unused)) int line) {
|
||||||
vga_set_string(0, VGA_HEIGHT - 1, msg, 0x28);
|
vga_set_string(0, VGA_HEIGHT - 1, msg, 0x28);
|
||||||
while (1)
|
while (1)
|
||||||
;
|
;
|
||||||
|
|||||||
@@ -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;
|
||||||
-397
@@ -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++) {
|
||||||
vga[row * VGA_WIDTH + col] = (uint16_t)(((uint16_t)color << 8) | (uint16_t)c);
|
set_char(' ');
|
||||||
|
}
|
||||||
|
offset = VGA_WIDTH * (VGA_HEIGHT - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void vga_set_string(uint8_t row, uint8_t col, const char *str, uint8_t color) {
|
static void advance() {
|
||||||
ASSERT(row < VGA_HEIGHT && col < VGA_WIDTH, "vga_set_string: invalid coordinates")
|
offset++;
|
||||||
while (*str) {
|
if (offset >= VGA_WIDTH * VGA_HEIGHT) {
|
||||||
vga_set_char(row, col++, *str++, color);
|
scroll();
|
||||||
if (col >= VGA_WIDTH) {
|
|
||||||
col = 0;
|
|
||||||
row++;
|
|
||||||
ASSERT(row < VGA_HEIGHT && col < VGA_WIDTH, "vga_set_string: invalid coordinates")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void vga_copy(uint8_t src_row, uint8_t rows, uint8_t dst_row) {
|
static void clear() {
|
||||||
ASSERT(src_row < VGA_HEIGHT && dst_row < VGA_HEIGHT && src_row + rows <= VGA_HEIGHT && dst_row + rows <= VGA_HEIGHT,
|
uint16_t *ptr = vga;
|
||||||
"vga_copy: invalid copy dimensions");
|
|
||||||
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) {
|
|
||||||
ASSERT(row < VGA_HEIGHT && rows <= VGA_HEIGHT && row + rows <= VGA_HEIGHT, "vga_clear: invalid clear dimensions");
|
|
||||||
|
|
||||||
uint16_t *dst = vga + row * VGA_WIDTH;
|
|
||||||
uint16_t fill = 0x0F20;
|
uint16_t fill = 0x0F20;
|
||||||
uint32_t count = VGA_WIDTH * rows;
|
uint32_t count = VGA_WIDTH * VGA_HEIGHT;
|
||||||
__asm__ volatile("rep stosw" : "=D"(dst), "=c"(count) : "D"(dst), "a"(fill), "c"(count) : "memory");
|
__asm__ volatile("rep stosw" : "=D"(ptr), "=c"(count) : "D"(ptr), "a"(fill), "c"(count) : "memory");
|
||||||
|
|
||||||
|
offset = 0;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stream_write(__attribute__((unused)) stream_t *self, const char *from, uint64_t size) {
|
||||||
|
while (size--) {
|
||||||
|
on_char_received(*from++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t stream_read(__attribute__((unused)) stream_t *self, __attribute__((unused)) uint64_t max,
|
||||||
|
__attribute__((unused)) char *to) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static stream_t stream = {stream_write, stream_read};
|
||||||
|
|
||||||
|
stream_t *vga_init() {
|
||||||
|
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++);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stdint.h>
|
#include "src/stream.h"
|
||||||
|
|
||||||
#define VGA_WIDTH 80
|
#define VGA_WIDTH 80
|
||||||
#define VGA_HEIGHT 25
|
#define VGA_HEIGHT 25
|
||||||
|
|
||||||
void vga_set_cursor(uint8_t row, uint8_t col);
|
stream_t *vga_init();
|
||||||
|
|
||||||
void vga_set_char(uint8_t row, uint8_t col, char c, uint8_t color);
|
|
||||||
|
|
||||||
void vga_set_string(uint8_t row, uint8_t col, const char *str, uint8_t color);
|
void vga_set_string(uint8_t row, uint8_t col, const char *str, uint8_t color);
|
||||||
|
|
||||||
void vga_copy(uint8_t src_row, uint8_t rows, uint8_t dst_row);
|
|
||||||
|
|
||||||
void vga_clear(uint8_t row, uint8_t rows);
|
|
||||||
|
|||||||
Reference in New Issue
Block a user