Use streams for inter-"process" communications

master
Freywar Ulvnaudgari 2 months ago
parent fecf3b2e42
commit c8edeab750
  1. 4
      meson.build
  2. 78
      src/app/cat.c
  3. 5
      src/app/cat.h
  4. 81
      src/app/ls.c
  5. 5
      src/app/ls.h
  6. 255
      src/app/terminal.c
  7. 5
      src/app/terminal.h
  8. 3
      src/fat16.c
  9. 22
      src/kernel.c
  10. 157
      src/keyboard.c
  11. 7
      src/keyboard.h
  12. 2
      src/memory.c
  13. 2
      src/panic.c
  14. 36
      src/stream.h
  15. 397
      src/terminal.c
  16. 3
      src/terminal.h
  17. 176
      src/vga.c
  18. 10
      src/vga.h

@ -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);

@ -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;
} }

@ -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");
} }

@ -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);

@ -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,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;

@ -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++);
}
} }

@ -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);

Loading…
Cancel
Save