start forth shell

This commit is contained in:
Benji Dial 2026-01-06 13:04:05 -05:00
parent 7ec40a1d7e
commit 0330ab168e
30 changed files with 1446 additions and 87 deletions

BIN
disk/calcite/qwerty.bin Normal file

Binary file not shown.

View file

@ -37,6 +37,7 @@ enum fs_access_result read_file(struct read_file_parameter *parameter);
enum fs_access_result read_file_splat(file_handle_t handle, void *buffer, uint64_t start, uint64_t bytes);
void wait_for_mouse_packet(struct mouse_packet *packet_out);
int wait_for_keyboard_packet();
void *map_pages(uint64_t count);
@ -82,3 +83,6 @@ void wait_event(event_handle_t handle);
uint64_t get_ms_since_boot();
void sleep_until_ms_since_boot(uint64_t ms);
//vma should be page-aligned. writable and executable should be 1 or 0.
void change_page_permissions(void *vma, int writable, int executable);

View file

@ -17,6 +17,30 @@
#pragma once
#include <stdint.h>
void init_terminal();
void terminal_write(const char *data, int bytes);
void terminal_read(char *buffer, int bytes);
void terminal_write(const void *data, int bytes);
void terminal_flush();
enum terminal_key_flags {
TKF_LEFT_SHIFT = 0x20000,
TKF_RIGHT_SHIFT = 0x40000,
TKF_LEFT_CTRL = 0x80000,
TKF_RIGHT_CTRL = 0x100000,
TKF_LEFT_ALT = 0x200000,
TKF_RIGHT_ALT = 0x400000,
TKF_CAPS_LOCK = 0x800000,
TKF_NUM_LOCK = 0x1000000
};
//0x000000ff is printable character, if any, otherwise 0.
// this includes space, newline, and tab, but not delete or backspace.
// control, alt, and make are ignored, but shift, caps lock, and num lock are considered.
//0x0001ff00 is result of syscall_wait_for_keyboard_packet, shifted left.
//0x01fe0000 is flags as above.
int terminal_readkey();
//has null terminator, no newline. returned pointer is invalidated by next call.
const char *terminal_readline();

View file

@ -1,5 +1,5 @@
/* Calcite, include/kernel-public/input.h
* Copyright 2025 Benji Dial
* Copyright 2025-2026 Benji Dial
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -21,3 +21,133 @@ struct mouse_packet {
int x_change;
int y_change;
};
//named after qwerty value of key
enum key_value {
KEY_F9 = 0x01,
KEY_F5 = 0x03,
KEY_F3 = 0x04,
KEY_F1 = 0x05,
KEY_F2 = 0x06,
KEY_F12 = 0x07,
KEY_F10 = 0x09,
KEY_F8 = 0x0a,
KEY_F6 = 0x0b,
KEY_F4 = 0x0c,
KEY_TAB = 0x0d,
KEY_GRAVE = 0x0e,
KEY_LEFT_ALT = 0x11,
KEY_LEFT_SHIFT = 0x12,
KEY_LEFT_CTRL = 0x14,
KEY_Q = 0x15,
KEY_1 = 0x16,
KEY_Z = 0x1a,
KEY_S = 0x1b,
KEY_A = 0x1c,
KEY_W = 0x1d,
KEY_2 = 0x1e,
KEY_C = 0x21,
KEY_X = 0x22,
KEY_D = 0x23,
KEY_E = 0x24,
KEY_4 = 0x25,
KEY_3 = 0x26,
KEY_SPACE = 0x29,
KEY_V = 0x2a,
KEY_F = 0x2b,
KEY_T = 0x2c,
KEY_R = 0x2d,
KEY_5 = 0x2e,
KEY_N = 0x31,
KEY_B = 0x32,
KEY_H = 0x33,
KEY_G = 0x34,
KEY_Y = 0x35,
KEY_6 = 0x36,
KEY_M = 0x3a,
KEY_J = 0x3b,
KEY_U = 0x3c,
KEY_7 = 0x3d,
KEY_8 = 0x3e,
KEY_COMMA = 0x41,
KEY_K = 0x42,
KEY_I = 0x43,
KEY_O = 0x44,
KEY_0 = 0x45,
KEY_9 = 0x46,
KEY_PERIOD = 0x49,
KEY_SLASH = 0x4a,
KEY_L = 0x4b,
KEY_SEMICOLON = 0x4c,
KEY_P = 0x4d,
KEY_MINUS = 0x4e,
KEY_APOSTRAPHE = 0x52,
KEY_OPEN_BRACKET = 0x54,
KEY_EQUALS = 0x55,
KEY_CAPS_LOCK = 0x58,
KEY_RIGHT_SHIFT = 0x59,
KEY_ENTER = 0x5a,
KEY_CLOSE_BRACKET = 0x5b,
KEY_BACKSLASH = 0x5d,
KEY_BACKSPACE = 0x66,
KEY_NUM_1 = 0x69,
KEY_NUM_4 = 0x6b,
KEY_NUM_7 = 0x6c,
KEY_NUM_0 = 0x70,
KEY_NUM_PERIOD = 0x71,
KEY_NUM_2 = 0x72,
KEY_NUM_5 = 0x73,
KEY_NUM_6 = 0x74,
KEY_NUM_8 = 0x75,
KEY_ESCAPE = 0x76,
KEY_NUM_LOCK = 0x77,
KEY_F11 = 0x78,
KEY_NUM_PLUS = 0x79,
KEY_NUM_3 = 0x7a,
KEY_NUM_MINUS = 0x7b,
KEY_NUM_TIMES = 0x7c,
KEY_NUM_9 = 0x7d,
KEY_SCROLL_LOCK = 0x7e,
KEY_F7 = 0x83,
KEY_WEB_SEARCH = 0x90,
KEY_RIGHT_ALT = 0x91,
KEY_RIGHT_CTRL = 0x94,
KEY_MEDIA_PREVIOUS = 0x95,
KEY_WEB_FAVORITES = 0x98,
KEY_LEFT_GUI = 0x9f,
KEY_WEB_REFRESH = 0xa0,
KEY_MEDIA_VOLUME_DOWN = 0xa1,
KEY_MEDIA_MUTE = 0xa3,
KEY_RIGHT_GUI = 0xa7,
KEY_WEB_STOP = 0xa8,
KEY_CALCULATOR = 0xab,
KEY_MENU = 0xaf,
KEY_WEB_FORWARD = 0xb0,
KEY_MEDIA_VOLUME_UP = 0xb2,
KEY_MEDIA_PAUSE = 0xb4,
KEY_ACPI_POWER = 0xb7,
KEY_WEB_BACK = 0xb8,
KEY_WEB_HOME = 0xba,
KEY_MEDIA_STOP = 0xbb,
KEY_ACPI_SLEEP = 0xbf,
KEY_COMPUTER = 0xc0,
KEY_EMAIL = 0xc8,
KEY_NUM_DIVIDE = 0xca,
KEY_MEDIA_NEXT = 0xcd,
KEY_MEDIA_SELECT = 0xd0,
KEY_NUM_ENTER = 0xda,
KEY_ACPI_WAKE = 0xde,
KEY_END = 0xe9,
KEY_LEFT = 0xeb,
KEY_HOME = 0xec,
KEY_INSERT = 0xf0,
KEY_DELETE = 0xf1,
KEY_DOWN = 0xf2,
KEY_RIGHT = 0xf4,
KEY_UP = 0xf5,
KEY_PAGE_DOWN = 0xfa,
KEY_PAGE_UP = 0xfd,
KEY_PRINT_SCREEN = 0xfe,
KEY_PAUSE_BREAK = 0xff,
KEY_FLAG_MAKE = 0x100
};

View file

@ -44,5 +44,7 @@ enum {
SYSCALL_IPC_CREATE_PRIVATE_DGRAM_RECEIVER,
SYSCALL_IPC_CREATE_PRIVATE_DGRAM_SENDER,
SYSCALL_GET_MS_SINCE_BOOT,
SYSCALL_SLEEP_UNTIL_MS_SINCE_BOOT
SYSCALL_SLEEP_UNTIL_MS_SINCE_BOOT,
SYSCALL_CHANGE_PAGE_PERMISSIONS,
SYSCALL_WAIT_FOR_KEYBOARD_PACKET
};

View file

@ -259,7 +259,7 @@ static const char *cmdline_look_up(const char *key) {
set_irq_handler(0x01, &on_keyboard_irq);
set_irq_handler(0x0c, &on_mouse_irq);
enable_interrupts();
init_interrupts();
//set up syscalls
@ -291,6 +291,8 @@ static const char *cmdline_look_up(const char *key) {
register_syscall(SYSCALL_IPC_CREATE_PRIVATE_DGRAM_SENDER, (void *)&syscall_ipc_create_private_dgram_sender);
register_syscall(SYSCALL_GET_MS_SINCE_BOOT, (void *)&syscall_get_ms_since_boot);
register_syscall(SYSCALL_SLEEP_UNTIL_MS_SINCE_BOOT, (void *)&syscall_sleep_until_ms_since_boot);
register_syscall(SYSCALL_CHANGE_PAGE_PERMISSIONS, (void *)&syscall_change_page_permissions);
register_syscall(SYSCALL_WAIT_FOR_KEYBOARD_PACKET, (void *)&syscall_wait_for_keyboard_packet);
//probe pci devices

View file

@ -1,5 +1,5 @@
/* Calcite, src/kernel/input.c
* Copyright 2025 Benji Dial
* Copyright 2025-2026 Benji Dial
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -19,10 +19,11 @@
#include "process.h"
#include "debug.h"
#include "input.h"
#include "heap.h"
static int is_somebody_waiting_for_mouse_packet = 0;
static struct continuation_info waiting_continuation;
static struct mouse_packet *resume_by_reporting_to;
static struct continuation_info waiting_continuation_mouse;
static struct mouse_packet *resume_by_reporting_to_mouse;
static int total_unreported_x_movement = 0;
static int total_unreported_y_movement = 0;
@ -33,9 +34,9 @@ void add_mouse_movement(int x, int y) {
total_unreported_y_movement += y;
if (is_somebody_waiting_for_mouse_packet) {
resume_by_reporting_to->x_change = total_unreported_x_movement;
resume_by_reporting_to->y_change = total_unreported_y_movement;
add_to_queue(&ready_continuations, &waiting_continuation);
resume_by_reporting_to_mouse->x_change = total_unreported_x_movement;
resume_by_reporting_to_mouse->y_change = total_unreported_y_movement;
add_to_queue(&ready_continuations, &waiting_continuation_mouse);
total_unreported_x_movement = 0;
total_unreported_y_movement = 0;
is_somebody_waiting_for_mouse_packet = 0;
@ -47,8 +48,6 @@ void syscall_wait_for_mouse_packet(struct mouse_packet *packet_out) {
assert(running_thread != 0);
__asm__ ("cli");
//TODO: handle these
if (is_somebody_waiting_for_mouse_packet ||
!is_mapped_writable(
@ -60,12 +59,82 @@ void syscall_wait_for_mouse_packet(struct mouse_packet *packet_out) {
packet_out->y_change = total_unreported_y_movement;
total_unreported_x_movement = 0;
total_unreported_y_movement = 0;
__asm__ ("sti");
return;
}
is_somebody_waiting_for_mouse_packet = 1;
resume_by_reporting_to = packet_out;
yield_sti(&waiting_continuation);
resume_by_reporting_to_mouse = packet_out;
yield(&waiting_continuation_mouse);
}
static int is_somebody_waiting_for_keyboard_packet = 0;
static struct continuation_info waiting_continuation_keyboard;
static int *resume_by_reporting_to_keyboard;
//in packets
#define INITIAL_KEYBOARD_BUFFER_LENGTH 1024
static int *keyboard_buffer = 0;
static int keyboard_buffer_length = 0;
static int keyboard_buffer_used = 0;
static int keyboard_buffer_read_pointer = 0;
void add_keyboard_packet(int packet) {
if (is_somebody_waiting_for_keyboard_packet) {
*resume_by_reporting_to_keyboard = packet;
add_to_queue(&ready_continuations, &waiting_continuation_keyboard);
is_somebody_waiting_for_keyboard_packet = 0;
}
else if (keyboard_buffer_used < keyboard_buffer_length) {
int write_pointer = (keyboard_buffer_read_pointer + keyboard_buffer_used) % keyboard_buffer_length;
keyboard_buffer[write_pointer] = packet;
++keyboard_buffer_used;
}
else if (keyboard_buffer == 0) {
keyboard_buffer = heap_alloc(INITIAL_KEYBOARD_BUFFER_LENGTH * sizeof(int));
keyboard_buffer_length = INITIAL_KEYBOARD_BUFFER_LENGTH;
keyboard_buffer[0] = packet;
keyboard_buffer_used = 1;
}
else {
int *new_buffer = heap_alloc(2 * keyboard_buffer_length * sizeof(int));
memcpy(
new_buffer, &keyboard_buffer[keyboard_buffer_read_pointer],
keyboard_buffer_length - keyboard_buffer_read_pointer);
memcpy(
&new_buffer[keyboard_buffer_length - keyboard_buffer_read_pointer],
keyboard_buffer, keyboard_buffer_read_pointer);
heap_dealloc(keyboard_buffer, keyboard_buffer_length * sizeof(int));
keyboard_buffer = new_buffer;
keyboard_buffer_length *= 2;
keyboard_buffer_read_pointer = 0;
keyboard_buffer[keyboard_buffer_length / 2] = packet;
++keyboard_buffer_used;
}
}
int syscall_wait_for_keyboard_packet() {
if (keyboard_buffer_used > 0) {
int value = keyboard_buffer[keyboard_buffer_read_pointer];
keyboard_buffer_read_pointer = (keyboard_buffer_read_pointer + 1) % keyboard_buffer_length;
--keyboard_buffer_used;
return value;
}
if (is_somebody_waiting_for_keyboard_packet)
panic("TODO")
is_somebody_waiting_for_keyboard_packet = 1;
int result;
resume_by_reporting_to_keyboard = &result;
yield(&waiting_continuation_keyboard);
return result;
}

View file

@ -1,5 +1,5 @@
/* Calcite, src/kernel/input.h
* Copyright 2025 Benji Dial
* Copyright 2025-2026 Benji Dial
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -19,7 +19,10 @@
#include <kernel-public/input.h>
//should only be called with interrupts disabled
void add_mouse_movement(int x, int y);
void syscall_wait_for_mouse_packet(struct mouse_packet *packet_out);
void add_keyboard_packet(int packet);
int syscall_wait_for_keyboard_packet();

View file

@ -135,8 +135,8 @@ isr_irq_common:
;rdi is pointer to gdt descriptor
;rsi is pointer to idt descriptor
;dx is offset of tss into gdt
global enable_interrupts_asm
enable_interrupts_asm:
global init_interrupts_asm
init_interrupts_asm:
;load tables
@ -168,7 +168,4 @@ enable_interrupts_asm:
mov al, 0x00
out 0xa1, al
;enable interrupts and return
sti
ret

View file

@ -96,7 +96,6 @@ const char *exception_names[32] = {
log(" r15 = 0x%h", parameter->r15, 16)
log("halting")
__asm__ ("cli");
while (1)
__asm__ ("hlt");
@ -254,12 +253,12 @@ static struct idt_descriptor the_idt_descriptor = {
extern void *isrs[48];
//defined in interrupts.asm
void enable_interrupts_asm(
void init_interrupts_asm(
struct gdt_descriptor *gdtr,
struct idt_descriptor *idtr,
uint16_t tssr);
void enable_interrupts() {
void init_interrupts() {
the_tss.ist1 = (uint64_t)interrupt_stack_one + INTERRUPT_STACK_SIZE;
the_tss.ist2 = (uint64_t)interrupt_stack_two + INTERRUPT_STACK_SIZE;
@ -316,6 +315,6 @@ void enable_interrupts() {
the_idt[i].offset_32 = offset >> 32;
}
enable_interrupts_asm(&the_gdt_descriptor, &the_idt_descriptor, 0x08);
init_interrupts_asm(&the_gdt_descriptor, &the_idt_descriptor, 0x08);
}

View file

@ -1,5 +1,5 @@
/* Calcite, src/kernel/interrupts.h
* Copyright 2025 Benji Dial
* Copyright 2025-2026 Benji Dial
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -19,8 +19,8 @@
#include <stdint.h>
//switches to kernel gdt and idt, maps and unmasks irqs, enables interrupts
void enable_interrupts();
//switches to kernel gdt and idt, maps and unmasks irqs
void init_interrupts();
//the handler should have normal C linkage
void set_irq_handler(uint8_t irq_line, void (*handler)());

View file

@ -135,6 +135,30 @@ void map_page_for_process(
}
void syscall_change_page_permissions(void *vma, int writable, int executable) {
assert(running_thread != 0)
if (((uint64_t)vma & 0xfff) != 0 || (uint64_t)vma >= 0x0000008000000000 || vma == 0 ||
writable < 0 || writable > 1 || executable < 0 || executable > 1)
syscall_illegal_args();
int p1i, p2i, p3i;
get_page_table_entry(
running_thread->process, vma, &p1i, &p2i, &p3i);
uint64_t *entry = &running_thread->process->p1_virtual_bases[p3i][p2i][p1i];
if (!(*entry & 0x1))
syscall_illegal_args();
*entry =
(*entry & 0x7ffffffffffffffd) |
(executable ? 0 : 0x8000000000000000) |
(writable ? 0x2 : 0x0);
}
void create_guard_page_for_process(struct process *process, void *virtual_base) {
int p1i, p2i, p3i;

View file

@ -167,6 +167,8 @@ enum fs_access_result syscall_read_file(struct read_file_parameter *parameter);
void *syscall_map_pages(uint64_t count);
void syscall_change_page_permissions(void *vma, int writable, int executable);
enum ipc_dgram_result syscall_ipc_create_dgram_receiver(
const char *address, ipc_dgram_receiver_handle_t *handle_out);

View file

@ -1,5 +1,5 @@
/* Calcite, src/kernel/ps2.c
* Copyright 2025 Benji Dial
* Copyright 2025-2026 Benji Dial
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -18,19 +18,74 @@
#include "input.h"
#include "ps2.h"
#include <kernel-public/input.h>
#include <stdint.h>
//defined in ps2.asm
//returns -1 if no byte available
int read_ps2_byte();
static uint8_t keyboard_packet[7];
static int keyboard_packet_length = 0;
void on_keyboard_irq() {
int byte = read_ps2_byte();
if (byte == -1)
return;
//TODO
if (keyboard_packet_length == 0 && byte != 0xe0 && byte != 0xe1 && byte != 0xf0) {
add_keyboard_packet(KEY_FLAG_MAKE | byte);
return;
}
if (keyboard_packet_length == 1 && keyboard_packet[0] == 0xe0 && byte != 0xf0 && byte != 0x12) {
add_keyboard_packet(KEY_FLAG_MAKE | 0x80 | byte);
keyboard_packet_length = 0;
return;
}
if (keyboard_packet_length == 1 && keyboard_packet[0] == 0xf0) {
add_keyboard_packet(byte);
keyboard_packet_length = 0;
return;
}
if (keyboard_packet_length == 2 && keyboard_packet[0] == 0xe0 && keyboard_packet[1] == 0xf0 && byte != 0x7c) {
add_keyboard_packet(0x80 | byte);
keyboard_packet_length = 0;
return;
}
if (keyboard_packet_length == 3 && keyboard_packet[0] == 0xe0 && keyboard_packet[1] == 0x12 &&
keyboard_packet[2] == 0xe0 && byte == 0x7c) {
add_keyboard_packet(KEY_FLAG_MAKE | KEY_PRINT_SCREEN);
keyboard_packet_length = 0;
return;
}
if (keyboard_packet_length == 5 && keyboard_packet[0] == 0xe0 && keyboard_packet[1] == 0xf0 &&
keyboard_packet[2] == 0x7c && keyboard_packet[3] == 0xe0 && keyboard_packet[4] == 0xf0 && byte == 0x12) {
add_keyboard_packet(KEY_PRINT_SCREEN);
keyboard_packet_length = 0;
return;
}
if (keyboard_packet_length == 7 && keyboard_packet[0] == 0xe1 && keyboard_packet[1] == 0x14 &&
keyboard_packet[2] == 0x77 && keyboard_packet[3] == 0xe1 && keyboard_packet[4] == 0xf0 &&
keyboard_packet[5] == 0x14 && keyboard_packet[6] == 0xf0 && byte == 0x77) {
add_keyboard_packet(KEY_FLAG_MAKE | KEY_PAUSE_BREAK);
add_keyboard_packet(KEY_PAUSE_BREAK);
keyboard_packet_length = 0;
return;
}
if (keyboard_packet_length == 7) {
keyboard_packet_length = 0;
return;
}
keyboard_packet[keyboard_packet_length++] = byte;
}

View file

@ -60,26 +60,6 @@ yield:
.ret:
ret
global yield_sti
yield_sti:
mov qword [rdi], .ret
mov qword [rdi + 8], rbx
mov qword [rdi + 16], rbp
mov qword [rdi + 24], rsp
mov qword [rdi + 32], r12
mov qword [rdi + 40], r13
mov qword [rdi + 48], r14
mov qword [rdi + 56], r15
mov qword rsi, qword [running_thread]
mov qword [rdi + 64], rsi
sti
jmp resume_next_continuation
.ret:
ret
global yield_to_queue
yield_to_queue:
mov qword [rsp - 72], .ret

View file

@ -35,8 +35,11 @@ void init_scheduler() {
running_thread = 0;
struct continuation_info ci;
while (!take_from_queue(&ready_continuations, &ci))
while (!take_from_queue(&ready_continuations, &ci)) {
__asm__ ("sti");
__asm__ ("hlt");
__asm__ ("cli");
}
assert(running_thread == 0)
assert(ci.running_thread != 0)

View file

@ -50,7 +50,7 @@ extern struct continuation_queue ready_continuations;
void create_queue(struct continuation_queue *queue, int buffer_size);
void destroy_queue(struct continuation_queue *queue);
//ci is copied
//ci is copied.
void add_to_queue(struct continuation_queue *queue, struct continuation_info *ci);
//if queue is empty, returns 0. otherwise, copies next read to ci, advances read, and returns 1.
@ -60,8 +60,5 @@ int take_from_queue(struct continuation_queue *queue, struct continuation_info *
//the next ready continuation (thus doesn't return until save_current_continuation_to is resumed).
void yield(struct continuation_info *save_current_continuation_to);
//save as yield but enables interrupts between saving this continuation and resuming next ready one.
void yield_sti(struct continuation_info *save_current_continuation_to);
//same as yield, but instead of saving continuation to a pointer, saves it to the end of the queue.
void yield_to_queue(struct continuation_queue *queue);

View file

@ -42,7 +42,9 @@ syscall_entry:
push r11
mov rcx, rax
cli
call syscall_entry_c
sti
xor rdi, rdi
xor rsi, rsi

View file

@ -32,6 +32,8 @@ static struct sleeping_list_entry *last_entry = 0;
//pit irq happens ~ once per millisecond
static uint64_t pit_irqs_since_boot = 0;
static struct sleeping_list_entry *local_sleeping_list_entry_pool = 0;
void on_pit_irq() {
++pit_irqs_since_boot;
@ -42,7 +44,8 @@ void on_pit_irq() {
struct sleeping_list_entry *old_first = first_entry;
first_entry = old_first->next;
heap_dealloc(old_first, sizeof(struct sleeping_list_entry));
*(struct sleeping_list_entry **)old_first = local_sleeping_list_entry_pool;
local_sleeping_list_entry_pool = old_first;
if (first_entry == 0)
last_entry = 0;
@ -51,13 +54,20 @@ void on_pit_irq() {
}
//until in pit irqs since boot
//until in pit irqs since boot.
//must be called with interrupts disabled.
//on return, interrupts are enabled.
static void sleep_until(uint64_t until) {
struct sleeping_list_entry *entry = heap_alloc(sizeof(struct sleeping_list_entry));
entry->sleeping_until = until;
struct sleeping_list_entry *entry;
if (local_sleeping_list_entry_pool != 0) {
entry = local_sleeping_list_entry_pool;
local_sleeping_list_entry_pool = *(struct sleeping_list_entry **)entry;
}
else
entry = heap_alloc(sizeof(struct sleeping_list_entry));
__asm__ ("cli");
entry->sleeping_until = until;
if (first_entry == 0) {
entry->next = 0;
@ -84,7 +94,7 @@ static void sleep_until(uint64_t until) {
just_before->next = entry;
}
yield_sti(&entry->continuation);
yield(&entry->continuation);
}

View file

@ -0,0 +1,218 @@
/* Calcite, src/user-apps/shell/assembler.c
* Copyright 2026 Benji Dial
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "assembler.h"
#include <calcite/syscalls.h>
#include <calcite/memory.h>
#include <stdint.h>
static uint8_t *function_buffer = 0;
static int function_buffer_length;
static int function_buffer_used;
struct relocation_info {
int offset_into_function;
int from_offset_into_function;
void *to;
struct relocation_info *previous;
};
static struct relocation_info *relocations = 0;
static char *function_name = 0;
static int function_name_length;
struct function_alloc {
void *start;
int bytes;
struct function_alloc *previous;
};
static struct function_alloc *allocs = 0;
#define FUNCTION_BUFFER_INITIAL_SIZE 4096
void start_function(const char *name, int name_length) {
if (function_buffer == 0) {
function_buffer_length = FUNCTION_BUFFER_INITIAL_SIZE;
function_buffer = heap_alloc(function_buffer_length);
}
function_buffer_used = 0;
function_name_length = name_length;
char *name_copy = heap_alloc(name_length);
memcpy(name_copy, name, name_length);
function_name = name_copy;
}
static void destroy_relocations() {
struct relocation_info *r = relocations;
while (r != 0) {
struct relocation_info *p = r->previous;
heap_dealloc(r, sizeof(struct relocation_info));
r = p;
}
relocations = 0;
}
static void destroy_function_allocs(int and_free) {
struct function_alloc *a = allocs;
while (a != 0) {
struct function_alloc *p = a->previous;
if (and_free)
heap_dealloc(a->start, a->bytes);
heap_dealloc(a, sizeof(struct function_alloc));
a = p;
}
allocs = 0;
}
const char *get_function_name() {
return function_name;
}
int get_function_name_length() {
return function_name_length;
}
//must be multiple of 4096.
#define MIN_RX_SPACE (1 << 20)
static void *rx_pointer;
static int rx_space = 0;
void (*end_function())() {
void *function_start = 0;
if (function_buffer_used <= rx_space) {
function_start = rx_pointer;
rx_pointer += function_buffer_used;
rx_space -= function_buffer_used;
}
else {
int to_allocate = ((function_buffer_length - 1) / 4096 + 1) * 4096;
if (to_allocate < MIN_RX_SPACE)
to_allocate = MIN_RX_SPACE;
void *pages = map_pages(to_allocate / 4096);
for (int i = 0; i < to_allocate; i += 4096)
change_page_permissions(pages + i, 1, 1);
function_start = pages;
rx_pointer = pages + function_buffer_used;
rx_space = to_allocate - function_buffer_used;
}
memcpy(function_start, function_buffer, function_buffer_used);
for (struct relocation_info *r = relocations; r != 0; r = r->previous) {
int32_t *at = function_start + r->offset_into_function;
void *from = function_start + r->from_offset_into_function;
*at = r->to - from;
}
destroy_relocations();
destroy_function_allocs(0);
function_name = 0;
return function_start;
}
void abort_function() {
destroy_relocations();
destroy_function_allocs(1);
if (function_name != 0) {
heap_dealloc(function_name, function_name_length);
function_name = 0;
}
}
void *function_data_alloc(int bytes) {
void *start = heap_alloc(bytes);
struct function_alloc *a = heap_alloc(sizeof(struct function_alloc));
a->start = start;
a->bytes = bytes;
a->previous = allocs;
allocs = a;
return start;
}
static void emit_bytes(const uint8_t *bytes, int count) {
if (function_buffer_used + count > function_buffer_length) {
int new_length = function_name_length * 2;
while (function_buffer_used + count > function_buffer_length)
new_length *= 2;
void *new_buffer = heap_alloc(new_length);
memcpy(new_buffer, function_buffer, function_buffer_used);
heap_dealloc(function_buffer, function_buffer_length);
function_buffer = new_buffer;
function_buffer_length = new_length;
}
memcpy(function_buffer + function_buffer_used, bytes, count);
function_buffer_used += count;
}
static struct relocation_info *new_relocation() {
struct relocation_info *r = heap_alloc(sizeof(struct relocation_info));
r->previous = relocations;
relocations = r;
return r;
}
void emit_ret() {
uint8_t byte = 0xc3;
emit_bytes(&byte, 1);
}
void emit_call(void (*to)()) {
struct relocation_info *r = new_relocation();
r->offset_into_function = function_buffer_used + 1;
r->from_offset_into_function = function_buffer_used + 5;
r->to = to;
uint8_t bytes[5];
bytes[0] = 0xe8;
emit_bytes(bytes, 5);
}
void emit_mov_edi(int value) {
uint8_t bytes[5];
bytes[0] = 0xbf;
*(int32_t *)&bytes[1] = value;
emit_bytes(bytes, 5);
}
void emit_mov_esi(int value) {
uint8_t bytes[5];
bytes[0] = 0xbe;
*(int32_t *)&bytes[1] = value;
emit_bytes(bytes, 5);
}
void emit_mov_rdi(uint64_t value) {
uint8_t bytes[10];
bytes[0] = 0x48;
bytes[1] = 0xbf;
*(uint64_t *)&bytes[2] = value;
emit_bytes(bytes, 10);
}

View file

@ -0,0 +1,38 @@
/* Calcite, src/user-apps/shell/assembler.h
* Copyright 2026 Benji Dial
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
//name is copied
void start_function(const char *name, int name_length);
const char *get_function_name();
int get_function_name_length();
//returns interpret
void (*end_function())();
void abort_function();
void *function_data_alloc(int bytes);
void emit_ret();
void emit_call(void (*to)());
void emit_mov_edi(int value);
void emit_mov_esi(int value);
void emit_mov_rdi(uint64_t value);

View file

@ -0,0 +1,226 @@
/* Calcite, src/user-apps/shell/builtin-words.c
* Copyright 2026 Benji Dial
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <calcite/terminal.h>
#include <calcite/memory.h>
#include "builtin-words.h"
#include "assembler.h"
#include "runtime.h"
static void words_interpret() {
print_all_words();
}
static void dup_interpret() {
int x = pop();
push(x);
push(x);
}
static void drop_interpret() {
pop();
}
static void swap_interpret() {
int y = pop();
int x = pop();
push(y);
push(x);
}
static void rot_interpret() {
int z = pop();
int y = pop();
int x = pop();
push(y);
push(z);
push(x);
}
static void plus_interpret() {
int y = pop();
int x = pop();
push(x + y);
}
static void minus_interpret() {
int y = pop();
int x = pop();
push(x - y);
}
static void times_interpret() {
int y = pop();
int x = pop();
push(x * y);
}
static void divide_interpret() {
int y = pop();
int x = pop();
if (y == 0)
error("division by zero");
push(x / y);
}
static void mod_interpret() {
int y = pop();
int x = pop();
if (y == 0)
error("division by zero");
push(x % y);
}
static void print_integer(int x) {
if (x == 0) {
terminal_write("0 ", 2);
return;
}
int is_negative;
if (x < 0) {
x = -x;
is_negative = 1;
}
else
is_negative = 0;
char text[12];
text[11] = ' ';
int text_ptr = 11;
while (x > 0) {
--text_ptr;
text[text_ptr] = '0' + x % 10;
x /= 10;
}
if (is_negative) {
--text_ptr;
text[text_ptr] = '-';
}
terminal_write(&text[text_ptr], 12 - text_ptr);
}
static void dot_s_interpret() {
int *stack_pointer = get_data_stack_pointer();
int *stack_top = get_data_stack_top();
for (int *i = stack_top - 1; i >= stack_pointer; --i)
print_integer(*i);
}
static void dot_interpret() {
print_integer(pop());
}
static void dot_quote_interpret() {
const char *start;
int length;
read_until('"', &start, &length);
terminal_write(start, length);
}
static void dot_quote_compile() {
const char *start;
int length;
read_until('"', &start, &length);
void *address = function_data_alloc(length);
memcpy(address, start, length);
emit_mov_rdi((uint64_t)address);
emit_mov_esi(length);
emit_call((void *)&terminal_write);
}
static void cr_interpret() {
terminal_write("\n", 1);
}
static void colon_interpret() {
const char *name_start;
int name_length;
read_input_word(&name_start, &name_length);
if (name_length == 0)
error("colon at end of input");
start_function(name_start, name_length);
compiling = 1;
}
static void semicolon_compile() {
emit_ret();
const char *function_name = get_function_name();
int function_name_length = get_function_name_length();
void (*interpret)() = end_function();
struct word *word = new_word();
word->name_length = function_name_length;
word->name = function_name;
word->interpret = interpret;
word->compile = COMPILE_EMIT_CALL;
compiling = 0;
}
static void add(const char *name, void (*interpret)(), void (*compile)()) {
int name_length = 0;
while (name[name_length] != 0)
++name_length;
struct word *w = new_word();
w->name_length = name_length;
w->name = name;
w->interpret = interpret;
w->compile = compile;
}
static void add_compile_only(const char *name, void (*compile)()) {
add(name, INTERPRET_ERROR, compile);
}
static void add_interpret_only(const char *name, void (*interpret)()) {
add(name, interpret, COMPILE_ERROR);
}
static void add_emit_call(const char *name, void (*interpret)()) {
add(name, interpret, COMPILE_EMIT_CALL);
}
void add_builtin_words() {
add_emit_call( "words", &words_interpret);
add_emit_call( "dup", &dup_interpret);
add_emit_call( "drop", &drop_interpret);
add_emit_call( "swap", &swap_interpret);
add_emit_call( "rot", &rot_interpret);
add_emit_call( "+", &plus_interpret);
add_emit_call( "-", &minus_interpret);
add_emit_call( "*", &times_interpret);
add_emit_call( "/", &divide_interpret);
add_emit_call( "mod", &mod_interpret);
add_emit_call( ".s", &dot_s_interpret);
add_emit_call( ".", &dot_interpret);
add( ".\"", &dot_quote_interpret, &dot_quote_compile);
add_emit_call( "cr", &cr_interpret);
add_interpret_only(":", &colon_interpret);
add_compile_only( ";", &semicolon_compile);
}

View file

@ -1,4 +1,4 @@
/* Calcite, src/user-apps/shell/shell.c
/* Calcite, src/user-apps/shell/builtin-words.h
* Copyright 2026 Benji Dial
*
* This program is free software: you can redistribute it and/or modify
@ -15,12 +15,6 @@
* with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <calcite/syscalls.h>
#include <calcite/terminal.h>
#pragma once
void main() {
init_terminal();
terminal_write("hello\n", 6);
while (1)
sleep_ms(1000000000);
}
void add_builtin_words();

View file

@ -0,0 +1,36 @@
/* Calcite, src/user-apps/shell/main.c
* Copyright 2026 Benji Dial
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "builtin-words.h"
#include "runtime.h"
#include <calcite/terminal.h>
void main() {
init_terminal();
save_return_stack();
create_data_stack();
add_builtin_words();
terminal_write("words: ", 7);
print_all_words();
terminal_write("\n", 1);
main_loop();
}

View file

@ -0,0 +1,40 @@
; Calcite, src/user-apps/shell/runtime.asm
; Copyright 2026 Benji Dial
;
; This program is free software: you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation, either version 3 of the License, or
; (at your option) any later version.
;
; This program is distributed in the hope that it will be useful, but
; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
; for more details.
;
; You should have received a copy of the GNU General Public License along
; with this program. If not, see <https://www.gnu.org/licenses/>.
bits 64
default rel
section .bss
saved_return_stack:
resq 1
section .text
global save_return_stack
save_return_stack:
mov rax, rsp
and ax, 0xf000
add rax, 0x1000
mov qword [saved_return_stack], rax
ret
global restore_return_stack
restore_return_stack:
mov rsp, qword [saved_return_stack]
push qword 0
jmp rdi

View file

@ -0,0 +1,218 @@
/* Calcite, src/user-apps/shell/runtime.c
* Copyright 2026 Benji Dial
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "assembler.h"
#include "runtime.h"
#include <calcite/terminal.h>
#include <calcite/memory.h>
static int *data_stack_bottom;
static int *data_stack_top;
static int *data_stack_pointer;
#define INITIAL_DATA_STACK_INTS 1024
void create_data_stack() {
data_stack_bottom = heap_alloc(INITIAL_DATA_STACK_INTS * sizeof(int));
data_stack_top = data_stack_bottom + INITIAL_DATA_STACK_INTS;
data_stack_pointer = data_stack_top;
}
void push(int value) {
if (data_stack_pointer == data_stack_bottom) {
int old_size = data_stack_top - data_stack_bottom;
int new_size = old_size * 2;
int *new_stack = heap_alloc(new_size * sizeof(int));
memcpy(new_stack + old_size, data_stack_bottom, old_size * sizeof(int));
heap_dealloc(data_stack_bottom, old_size * sizeof(int));
data_stack_bottom = new_stack;
data_stack_top = new_stack + new_size;
data_stack_pointer = new_stack + old_size;
}
*--data_stack_pointer = value;
}
int pop() {
if (data_stack_pointer == data_stack_top)
error("stack underflow");
return *(data_stack_pointer++);
}
int *get_data_stack_pointer() {
return data_stack_pointer;
}
int *get_data_stack_top() {
return data_stack_top;
}
//null-terminated
static const char *input_buffer;
static const char *input_pointer;
void set_input(const char *buffer) {
input_buffer = buffer;
input_pointer = buffer;
}
static int is_ws(char ch) {
return ch == ' ';
}
void read_input_word(const char **start_out, int *length_out) {
const char *start = input_pointer;
while (start[0] != 0 && is_ws(start[0]))
++start;
int length = 0;
while (start[length] != 0 && !is_ws(start[length]))
++length;
*start_out = start;
*length_out = length;
if (start[length] == 0)
input_pointer = &start[length];
else
input_pointer = &start[length + 1];
}
void read_until(char end, const char **start_out, int *length_out) {
const char *start = input_pointer;
int length = 0;
while (start[length] != 0 && start[length] != end)
++length;
if (start[length] == 0)
error("unexpected eof");
*start_out = start;
*length_out = length;
input_pointer = &start[length + 1];
}
static const struct word *dictionary = 0;
const struct word *look_up_word(int name_length, const char *name) {
for (const struct word *word = dictionary; word != 0; word = word->previous) {
if (word->name_length == name_length) {
for (int i = 0; i < name_length; ++i)
if (word->name[i] != name[i])
goto next_word;
return word;
}
next_word:
;
}
return 0;
}
struct word *new_word() {
struct word *word = heap_alloc(sizeof(struct word));
word->previous = dictionary;
dictionary = word;
return word;
}
void print_all_words() {
for (const struct word *w = dictionary; w != 0; w = w->previous) {
terminal_write(w->name, w->name_length);
terminal_write(" ", 1);
}
}
[[noreturn]] void error(const char *message) {
terminal_write("*** error: ", 11);
int length = 0;
while (message[length] != 0)
++length;
terminal_write(message, length);
terminal_write("\n", 1);
abort_function();
restore_return_stack(&main_loop);
}
static void interpret_all() {
while (1) {
const char *start;
int length;
read_input_word(&start, &length);
if (length == 0)
return;
const struct word *word = look_up_word(length, start);
if (word != 0) {
if (compiling) {
if (word->compile == COMPILE_ERROR)
error("interpret-only word in compile context");
else if (word->compile == COMPILE_EMIT_CALL)
emit_call(word->interpret);
else
(*word->compile)();
}
else {
if (word->interpret == INTERPRET_ERROR)
error("compile-only word in interpret context");
else
(*word->interpret)();
}
continue;
}
int is_negative;
if (start[0] == '-') {
is_negative = 1;
++start;
--length;
}
else
is_negative = 0;
int abs = 0;
for (int i = 0; i < length; ++i) {
if (start[i] < '0' || start[i] > '9')
error("unknown word");
abs = abs * 10 + start[i] - '0';
}
int x = is_negative ? -abs : abs;
if (compiling) {
emit_mov_edi(x);
emit_call((void *)&push);
}
else
push(x);
}
}
[[noreturn]] void main_loop() {
compiling = 0;
while (1) {
terminal_write("> ", 2);
set_input(terminal_readline());
interpret_all();
}
}
int compiling;

View file

@ -0,0 +1,63 @@
/* Calcite, src/user-apps/shell/runtime.h
* Copyright 2026 Benji Dial
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
void save_return_stack();
//and_call should not return.
[[noreturn]] void restore_return_stack(void (*and_call)());
void create_data_stack();
void push(int value);
int pop();
int *get_data_stack_pointer();
int *get_data_stack_top();
//null-terminated. not copied.
void set_input(const char *buffer);
//length_out is set to 0 on eof. skips all whitespace before and up to one character after.
void read_input_word(const char **start_out, int *length_out);
//errors on eof before end. skips end. end not included in length.
void read_until(char end, const char **start_out, int *length_out);
struct word {
int name_length;
const char *name;
void (*interpret)();
void (*compile)();
const struct word *previous;
};
#define INTERPRET_ERROR ((void (*)())-1)
#define COMPILE_ERROR ((void (*)())-1)
#define COMPILE_EMIT_CALL ((void (*)())-2)
//returns 0 if not found. name does not need to be null-terminated.
const struct word *look_up_word(int name_length, const char *name);
struct word *new_word();
void print_all_words();
[[noreturn]] void error(const char *message);
extern int compiling;
[[noreturn]] void main_loop();

View file

@ -18,9 +18,12 @@
#include <kernel-public/framebuffer.h>
#include <kernel-public/process.h>
#include <flanterm_backends/fb.h>
#include <kernel-public/files.h>
#include <kernel-public/input.h>
#include <kernel-public/ipc.h>
#include <calcite/dispatch.h>
#include <calcite/syscalls.h>
#include <calcite/terminal.h>
#include <calcite/format.h>
#include <calcite/memory.h>
#include <flanterm.h>
@ -30,6 +33,8 @@ static struct flanterm_context *flanterm_context;
static ipc_dgram_receiver_handle_t input_handle;
static ipc_dgram_receiver_handle_t output_handle;
static char keytable[2048];
static struct dispatch_queue *queue;
struct got_output_param {
@ -64,16 +69,87 @@ static void got_output(void *x) {
}
static int flags = 0;
static int entering_caps_lock = 0;
static int entering_num_lock = 0;
[[noreturn]] static void input_thread(uint64_t) {
while (1) {
//TODO
while (1)
sleep_ms(1000000000);
int key = wait_for_keyboard_packet();
int shift = flags & (TKF_LEFT_SHIFT | TKF_RIGHT_SHIFT);
int caps = flags & TKF_CAPS_LOCK;
int num = flags & TKF_NUM_LOCK;
char printable = keytable[(key & 0xff) | (shift ? 0x100 : 0x0) | (caps ? 0x200 : 0x0) | (num ? 0x400 : 0x0)];
if (key == KEY_LEFT_SHIFT)
flags &= ~TKF_LEFT_SHIFT;
else if (key == KEY_RIGHT_SHIFT)
flags &= ~TKF_RIGHT_SHIFT;
else if (key == KEY_LEFT_CTRL)
flags &= ~TKF_LEFT_CTRL;
else if (key == KEY_RIGHT_CTRL)
flags &= ~TKF_RIGHT_CTRL;
else if (key == KEY_LEFT_ALT)
flags &= ~TKF_LEFT_ALT;
else if (key == KEY_RIGHT_ALT)
flags &= ~TKF_RIGHT_ALT;
else if (key == KEY_CAPS_LOCK) {
if (!entering_caps_lock)
flags &= ~TKF_CAPS_LOCK;
else
entering_caps_lock = 0;
}
else if (key == KEY_NUM_LOCK) {
if (!entering_num_lock)
flags &= ~TKF_NUM_LOCK;
else
entering_num_lock = 0;
}
else if (key == (KEY_FLAG_MAKE | KEY_LEFT_SHIFT))
flags |= TKF_LEFT_SHIFT;
else if (key == (KEY_FLAG_MAKE | KEY_RIGHT_SHIFT))
flags |= TKF_RIGHT_SHIFT;
else if (key == (KEY_FLAG_MAKE | KEY_LEFT_CTRL))
flags |= TKF_LEFT_CTRL;
else if (key == (KEY_FLAG_MAKE | KEY_RIGHT_CTRL))
flags |= TKF_RIGHT_CTRL;
else if (key == (KEY_FLAG_MAKE | KEY_LEFT_ALT))
flags |= TKF_LEFT_ALT;
else if (key == (KEY_FLAG_MAKE | KEY_RIGHT_ALT))
flags |= TKF_RIGHT_ALT;
else if (key == (KEY_FLAG_MAKE | KEY_CAPS_LOCK)) {
if (!(flags & TKF_CAPS_LOCK)) {
flags |= TKF_CAPS_LOCK;
entering_caps_lock = 1;
}
}
else if (key == (KEY_FLAG_MAKE | KEY_NUM_LOCK)) {
if (!(flags & TKF_NUM_LOCK)) {
flags |= TKF_NUM_LOCK;
entering_num_lock = 1;
}
}
int packet = flags | (key << 8) | printable;
ipc_send_dgram(input_handle, &packet, sizeof(int));
}
}
void main() {
file_handle_t keytable_handle;
if (open_file("root://calcite/qwerty.bin", &keytable_handle) == FAR_SUCCESS) {
struct read_file_parameter param;
param.handle = keytable_handle;
param.buffer = keytable;
param.start = 0;
param.bytes = 2048;
read_file(&param);
close_file(keytable_handle);
}
uint64_t output_pipe = ipc_create_private_dgram_pipe();
uint64_t input_pipe = ipc_create_private_dgram_pipe();

View file

@ -145,3 +145,11 @@ uint64_t get_ms_since_boot() {
void sleep_until_ms_since_boot(uint64_t ms) {
do_syscall(ms, 0, 0, SYSCALL_SLEEP_UNTIL_MS_SINCE_BOOT);
}
void change_page_permissions(void *vma, int writable, int executable) {
do_syscall((uint64_t)vma, writable, executable, SYSCALL_CHANGE_PAGE_PERMISSIONS);
}
int wait_for_keyboard_packet() {
return do_syscall(0, 0, 0, SYSCALL_WAIT_FOR_KEYBOARD_PACKET);
}

View file

@ -15,6 +15,8 @@
* with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <kernel-public/files.h>
#include <kernel-public/input.h>
#include <kernel-public/ipc.h>
#include <calcite/syscalls.h>
#include <calcite/terminal.h>
@ -27,7 +29,7 @@ static ipc_dgram_sender_handle_t output_handle;
static int set_up_terminal_input = 0;
static int set_up_terminal_output = 0;
static char *output_buffer;
static uint8_t *output_buffer;
static int output_buffer_size;
static int output_buffer_used;
@ -40,7 +42,7 @@ static void output_buffer_size_at_least(int bytes) {
while (new_size < bytes)
new_size *= 2;
char *new_buffer = heap_alloc(new_size);
uint8_t *new_buffer = heap_alloc(new_size);
memcpy(new_buffer, output_buffer, output_buffer_used);
heap_dealloc(output_buffer, output_buffer_size);
output_buffer = new_buffer;
@ -50,7 +52,7 @@ static void output_buffer_size_at_least(int bytes) {
#define MAX_PACKET_LENGTH 500
static void send_output(const char *buffer, int bytes) {
static void send_output(const void *buffer, int bytes) {
if (set_up_terminal_output == 0)
return;
@ -93,13 +95,15 @@ void init_terminal() {
}
void terminal_write(const char *data, int bytes) {
void terminal_write(const void *data, int bytes) {
if (bytes == 0)
return;
const uint8_t *data8 = (const uint8_t *)data;
int last_newline = bytes - 1;
while (last_newline >= 0 && data[last_newline] != '\n')
while (last_newline >= 0 && data8[last_newline] != '\n')
--last_newline;
if (last_newline == -1) {
@ -116,17 +120,152 @@ void terminal_write(const char *data, int bytes) {
send_output(data, last_newline + 1);
if (last_newline != bytes - 1) {
output_buffer_size_at_least(bytes - last_newline - 1);
memcpy(output_buffer, &data[last_newline + 1], bytes - last_newline - 1);
memcpy(output_buffer, data + last_newline + 1, bytes - last_newline - 1);
output_buffer_used = bytes - last_newline - 1;
}
}
}
void terminal_read(char *buffer, int bytes) {
//TODO
(void)buffer;
(void)bytes;
while (1)
sleep_ms(1000000000);
void terminal_flush() {
if (output_buffer_used != 0) {
send_output(output_buffer, output_buffer_used);
output_buffer_used = 0;
}
}
int terminal_readkey() {
if (!set_up_terminal_input)
return -1;
terminal_flush();
int result;
int bytes = sizeof(int);
ipc_receive_dgram(input_handle, &result, &bytes);
//TODO: handle case where terminal misbehaves and sends more than sizeof(int)
return result;
}
static char *readline_buffer = 0;
static int readline_buffer_length = 0;
static int readline_buffer_used = 0;
static int readline_cursor = 0;
static void room_for_one_more() {
if (readline_buffer == 0) {
readline_buffer_length = 1024;
readline_buffer = heap_alloc(readline_buffer_length);
}
else if (readline_buffer_used == readline_buffer_length) {
char *new_buffer = heap_alloc(readline_buffer_length * 2);
memcpy(new_buffer, readline_buffer, readline_buffer_length);
heap_dealloc(readline_buffer, readline_buffer_length);
readline_buffer = new_buffer;
readline_buffer_length *= 2;
}
}
const char *terminal_readline() {
readline_buffer_used = 0;
readline_cursor = 0;
while (1) {
int key_packet = terminal_readkey();
if (!(key_packet & (KEY_FLAG_MAKE << 8)) ||
(key_packet & (TKF_LEFT_CTRL | TKF_RIGHT_CTRL | TKF_LEFT_ALT | TKF_RIGHT_ALT)))
continue;
int key = (key_packet >> 8) & 0xff;
int num_lock = key_packet & TKF_NUM_LOCK;
if (key == KEY_LEFT || (!num_lock && key == KEY_NUM_4)) {
if (readline_cursor != 0) {
--readline_cursor;
terminal_write("\x1b[D", 3);
}
}
else if (key == KEY_RIGHT || (!num_lock && key == KEY_NUM_6)) {
if (readline_cursor != readline_buffer_used) {
++readline_cursor;
terminal_write("\x1b[C", 3);
}
}
else if (key == KEY_BACKSPACE) {
if (readline_cursor != 0) {
for (int i = readline_cursor; i < readline_buffer_used; ++i)
readline_buffer[i - 1] = readline_buffer[i];
--readline_cursor;
--readline_buffer_used;
terminal_write("\x1b[D", 3);
terminal_write(&readline_buffer[readline_cursor], readline_buffer_used - readline_cursor);
terminal_write(" ", 1);
for (int i = readline_cursor; i < readline_buffer_used + 1; ++i)
terminal_write("\x1b[D", 3);
}
}
else if (key == KEY_DELETE || (!num_lock && key == KEY_NUM_PERIOD)) {
if (readline_cursor != readline_buffer_used) {
++readline_cursor;
for (int i = readline_cursor; i < readline_buffer_used; ++i)
readline_buffer[i - 1] = readline_buffer[i];
--readline_cursor;
--readline_buffer_used;
terminal_write(&readline_buffer[readline_cursor], readline_buffer_used - readline_cursor);
terminal_write(" ", 1);
for (int i = readline_cursor; i < readline_buffer_used + 1; ++i)
terminal_write("\x1b[D", 3);
}
}
else if (key == KEY_ENTER || key == KEY_NUM_ENTER) {
room_for_one_more();
readline_buffer[readline_buffer_used] = 0;
for (int i = readline_cursor; i < readline_buffer_used; ++i)
terminal_write("\x1b[C", 3);
terminal_write("\n", 1);
return readline_buffer;
}
else if (key == KEY_TAB)
continue;
else if (key == KEY_HOME || (!num_lock && key == KEY_NUM_7)) {
for (int i = 0; i < readline_cursor; ++i)
terminal_write("\x1b[D", 3);
readline_cursor = 0;
}
else if (key == KEY_END || (!num_lock && key == KEY_NUM_1)) {
for (int i = readline_cursor; i < readline_buffer_used; ++i)
terminal_write("\x1b[C", 3);
readline_cursor = readline_buffer_used;
}
else {
char value = key_packet & 0xff;
if (value != 0) {
room_for_one_more();
for (int i = readline_buffer_used; i > readline_cursor; --i)
readline_buffer[i] = readline_buffer[i - 1];
readline_buffer[readline_cursor] = value;
++readline_buffer_used;
terminal_write(&readline_buffer[readline_cursor], readline_buffer_used - readline_cursor);
++readline_cursor;
for (int i = readline_buffer_used; i > readline_cursor; --i)
terminal_write("\x1b[D", 3);
}
}
}
}