start forth shell
This commit is contained in:
parent
7ec40a1d7e
commit
0330ab168e
30 changed files with 1446 additions and 87 deletions
BIN
disk/calcite/qwerty.bin
Normal file
BIN
disk/calcite/qwerty.bin
Normal file
Binary file not shown.
|
|
@ -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);
|
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);
|
void wait_for_mouse_packet(struct mouse_packet *packet_out);
|
||||||
|
int wait_for_keyboard_packet();
|
||||||
|
|
||||||
void *map_pages(uint64_t count);
|
void *map_pages(uint64_t count);
|
||||||
|
|
||||||
|
|
@ -82,3 +83,6 @@ void wait_event(event_handle_t handle);
|
||||||
|
|
||||||
uint64_t get_ms_since_boot();
|
uint64_t get_ms_since_boot();
|
||||||
void sleep_until_ms_since_boot(uint64_t ms);
|
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);
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,30 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
void init_terminal();
|
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();
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/* Calcite, include/kernel-public/input.h
|
/* 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
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
@ -21,3 +21,133 @@ struct mouse_packet {
|
||||||
int x_change;
|
int x_change;
|
||||||
int y_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
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -44,5 +44,7 @@ enum {
|
||||||
SYSCALL_IPC_CREATE_PRIVATE_DGRAM_RECEIVER,
|
SYSCALL_IPC_CREATE_PRIVATE_DGRAM_RECEIVER,
|
||||||
SYSCALL_IPC_CREATE_PRIVATE_DGRAM_SENDER,
|
SYSCALL_IPC_CREATE_PRIVATE_DGRAM_SENDER,
|
||||||
SYSCALL_GET_MS_SINCE_BOOT,
|
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
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -259,7 +259,7 @@ static const char *cmdline_look_up(const char *key) {
|
||||||
set_irq_handler(0x01, &on_keyboard_irq);
|
set_irq_handler(0x01, &on_keyboard_irq);
|
||||||
set_irq_handler(0x0c, &on_mouse_irq);
|
set_irq_handler(0x0c, &on_mouse_irq);
|
||||||
|
|
||||||
enable_interrupts();
|
init_interrupts();
|
||||||
|
|
||||||
//set up syscalls
|
//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_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_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_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
|
//probe pci devices
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/* Calcite, src/kernel/input.c
|
/* 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
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
@ -19,10 +19,11 @@
|
||||||
#include "process.h"
|
#include "process.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "input.h"
|
#include "input.h"
|
||||||
|
#include "heap.h"
|
||||||
|
|
||||||
static int is_somebody_waiting_for_mouse_packet = 0;
|
static int is_somebody_waiting_for_mouse_packet = 0;
|
||||||
static struct continuation_info waiting_continuation;
|
static struct continuation_info waiting_continuation_mouse;
|
||||||
static struct mouse_packet *resume_by_reporting_to;
|
static struct mouse_packet *resume_by_reporting_to_mouse;
|
||||||
|
|
||||||
static int total_unreported_x_movement = 0;
|
static int total_unreported_x_movement = 0;
|
||||||
static int total_unreported_y_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;
|
total_unreported_y_movement += y;
|
||||||
|
|
||||||
if (is_somebody_waiting_for_mouse_packet) {
|
if (is_somebody_waiting_for_mouse_packet) {
|
||||||
resume_by_reporting_to->x_change = total_unreported_x_movement;
|
resume_by_reporting_to_mouse->x_change = total_unreported_x_movement;
|
||||||
resume_by_reporting_to->y_change = total_unreported_y_movement;
|
resume_by_reporting_to_mouse->y_change = total_unreported_y_movement;
|
||||||
add_to_queue(&ready_continuations, &waiting_continuation);
|
add_to_queue(&ready_continuations, &waiting_continuation_mouse);
|
||||||
total_unreported_x_movement = 0;
|
total_unreported_x_movement = 0;
|
||||||
total_unreported_y_movement = 0;
|
total_unreported_y_movement = 0;
|
||||||
is_somebody_waiting_for_mouse_packet = 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);
|
assert(running_thread != 0);
|
||||||
|
|
||||||
__asm__ ("cli");
|
|
||||||
|
|
||||||
//TODO: handle these
|
//TODO: handle these
|
||||||
if (is_somebody_waiting_for_mouse_packet ||
|
if (is_somebody_waiting_for_mouse_packet ||
|
||||||
!is_mapped_writable(
|
!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;
|
packet_out->y_change = total_unreported_y_movement;
|
||||||
total_unreported_x_movement = 0;
|
total_unreported_x_movement = 0;
|
||||||
total_unreported_y_movement = 0;
|
total_unreported_y_movement = 0;
|
||||||
__asm__ ("sti");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
is_somebody_waiting_for_mouse_packet = 1;
|
is_somebody_waiting_for_mouse_packet = 1;
|
||||||
resume_by_reporting_to = packet_out;
|
resume_by_reporting_to_mouse = packet_out;
|
||||||
yield_sti(&waiting_continuation);
|
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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/* Calcite, src/kernel/input.h
|
/* 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
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
@ -19,7 +19,10 @@
|
||||||
|
|
||||||
#include <kernel-public/input.h>
|
#include <kernel-public/input.h>
|
||||||
|
|
||||||
//should only be called with interrupts disabled
|
|
||||||
void add_mouse_movement(int x, int y);
|
void add_mouse_movement(int x, int y);
|
||||||
|
|
||||||
void syscall_wait_for_mouse_packet(struct mouse_packet *packet_out);
|
void syscall_wait_for_mouse_packet(struct mouse_packet *packet_out);
|
||||||
|
|
||||||
|
void add_keyboard_packet(int packet);
|
||||||
|
|
||||||
|
int syscall_wait_for_keyboard_packet();
|
||||||
|
|
|
||||||
|
|
@ -135,8 +135,8 @@ isr_irq_common:
|
||||||
;rdi is pointer to gdt descriptor
|
;rdi is pointer to gdt descriptor
|
||||||
;rsi is pointer to idt descriptor
|
;rsi is pointer to idt descriptor
|
||||||
;dx is offset of tss into gdt
|
;dx is offset of tss into gdt
|
||||||
global enable_interrupts_asm
|
global init_interrupts_asm
|
||||||
enable_interrupts_asm:
|
init_interrupts_asm:
|
||||||
|
|
||||||
;load tables
|
;load tables
|
||||||
|
|
||||||
|
|
@ -168,7 +168,4 @@ enable_interrupts_asm:
|
||||||
mov al, 0x00
|
mov al, 0x00
|
||||||
out 0xa1, al
|
out 0xa1, al
|
||||||
|
|
||||||
;enable interrupts and return
|
|
||||||
|
|
||||||
sti
|
|
||||||
ret
|
ret
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,6 @@ const char *exception_names[32] = {
|
||||||
log(" r15 = 0x%h", parameter->r15, 16)
|
log(" r15 = 0x%h", parameter->r15, 16)
|
||||||
log("halting")
|
log("halting")
|
||||||
|
|
||||||
__asm__ ("cli");
|
|
||||||
while (1)
|
while (1)
|
||||||
__asm__ ("hlt");
|
__asm__ ("hlt");
|
||||||
|
|
||||||
|
|
@ -254,12 +253,12 @@ static struct idt_descriptor the_idt_descriptor = {
|
||||||
extern void *isrs[48];
|
extern void *isrs[48];
|
||||||
|
|
||||||
//defined in interrupts.asm
|
//defined in interrupts.asm
|
||||||
void enable_interrupts_asm(
|
void init_interrupts_asm(
|
||||||
struct gdt_descriptor *gdtr,
|
struct gdt_descriptor *gdtr,
|
||||||
struct idt_descriptor *idtr,
|
struct idt_descriptor *idtr,
|
||||||
uint16_t tssr);
|
uint16_t tssr);
|
||||||
|
|
||||||
void enable_interrupts() {
|
void init_interrupts() {
|
||||||
|
|
||||||
the_tss.ist1 = (uint64_t)interrupt_stack_one + INTERRUPT_STACK_SIZE;
|
the_tss.ist1 = (uint64_t)interrupt_stack_one + INTERRUPT_STACK_SIZE;
|
||||||
the_tss.ist2 = (uint64_t)interrupt_stack_two + 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;
|
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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/* Calcite, src/kernel/interrupts.h
|
/* 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
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
@ -19,8 +19,8 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
//switches to kernel gdt and idt, maps and unmasks irqs, enables interrupts
|
//switches to kernel gdt and idt, maps and unmasks irqs
|
||||||
void enable_interrupts();
|
void init_interrupts();
|
||||||
|
|
||||||
//the handler should have normal C linkage
|
//the handler should have normal C linkage
|
||||||
void set_irq_handler(uint8_t irq_line, void (*handler)());
|
void set_irq_handler(uint8_t irq_line, void (*handler)());
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
void create_guard_page_for_process(struct process *process, void *virtual_base) {
|
||||||
|
|
||||||
int p1i, p2i, p3i;
|
int p1i, p2i, p3i;
|
||||||
|
|
|
||||||
|
|
@ -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_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(
|
enum ipc_dgram_result syscall_ipc_create_dgram_receiver(
|
||||||
const char *address, ipc_dgram_receiver_handle_t *handle_out);
|
const char *address, ipc_dgram_receiver_handle_t *handle_out);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/* Calcite, src/kernel/ps2.c
|
/* 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
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
@ -18,19 +18,74 @@
|
||||||
#include "input.h"
|
#include "input.h"
|
||||||
#include "ps2.h"
|
#include "ps2.h"
|
||||||
|
|
||||||
|
#include <kernel-public/input.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
//defined in ps2.asm
|
//defined in ps2.asm
|
||||||
//returns -1 if no byte available
|
//returns -1 if no byte available
|
||||||
int read_ps2_byte();
|
int read_ps2_byte();
|
||||||
|
|
||||||
|
static uint8_t keyboard_packet[7];
|
||||||
|
static int keyboard_packet_length = 0;
|
||||||
|
|
||||||
void on_keyboard_irq() {
|
void on_keyboard_irq() {
|
||||||
|
|
||||||
int byte = read_ps2_byte();
|
int byte = read_ps2_byte();
|
||||||
if (byte == -1)
|
if (byte == -1)
|
||||||
return;
|
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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -60,26 +60,6 @@ yield:
|
||||||
.ret:
|
.ret:
|
||||||
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
|
global yield_to_queue
|
||||||
yield_to_queue:
|
yield_to_queue:
|
||||||
mov qword [rsp - 72], .ret
|
mov qword [rsp - 72], .ret
|
||||||
|
|
|
||||||
|
|
@ -35,8 +35,11 @@ void init_scheduler() {
|
||||||
running_thread = 0;
|
running_thread = 0;
|
||||||
|
|
||||||
struct continuation_info ci;
|
struct continuation_info ci;
|
||||||
while (!take_from_queue(&ready_continuations, &ci))
|
while (!take_from_queue(&ready_continuations, &ci)) {
|
||||||
|
__asm__ ("sti");
|
||||||
__asm__ ("hlt");
|
__asm__ ("hlt");
|
||||||
|
__asm__ ("cli");
|
||||||
|
}
|
||||||
|
|
||||||
assert(running_thread == 0)
|
assert(running_thread == 0)
|
||||||
assert(ci.running_thread != 0)
|
assert(ci.running_thread != 0)
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ extern struct continuation_queue ready_continuations;
|
||||||
void create_queue(struct continuation_queue *queue, int buffer_size);
|
void create_queue(struct continuation_queue *queue, int buffer_size);
|
||||||
void destroy_queue(struct continuation_queue *queue);
|
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);
|
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.
|
//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).
|
//the next ready continuation (thus doesn't return until save_current_continuation_to is resumed).
|
||||||
void yield(struct continuation_info *save_current_continuation_to);
|
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.
|
//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);
|
void yield_to_queue(struct continuation_queue *queue);
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,9 @@ syscall_entry:
|
||||||
push r11
|
push r11
|
||||||
|
|
||||||
mov rcx, rax
|
mov rcx, rax
|
||||||
|
cli
|
||||||
call syscall_entry_c
|
call syscall_entry_c
|
||||||
|
sti
|
||||||
|
|
||||||
xor rdi, rdi
|
xor rdi, rdi
|
||||||
xor rsi, rsi
|
xor rsi, rsi
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,8 @@ static struct sleeping_list_entry *last_entry = 0;
|
||||||
//pit irq happens ~ once per millisecond
|
//pit irq happens ~ once per millisecond
|
||||||
static uint64_t pit_irqs_since_boot = 0;
|
static uint64_t pit_irqs_since_boot = 0;
|
||||||
|
|
||||||
|
static struct sleeping_list_entry *local_sleeping_list_entry_pool = 0;
|
||||||
|
|
||||||
void on_pit_irq() {
|
void on_pit_irq() {
|
||||||
|
|
||||||
++pit_irqs_since_boot;
|
++pit_irqs_since_boot;
|
||||||
|
|
@ -42,7 +44,8 @@ void on_pit_irq() {
|
||||||
|
|
||||||
struct sleeping_list_entry *old_first = first_entry;
|
struct sleeping_list_entry *old_first = first_entry;
|
||||||
first_entry = old_first->next;
|
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)
|
if (first_entry == 0)
|
||||||
last_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) {
|
static void sleep_until(uint64_t until) {
|
||||||
|
|
||||||
struct sleeping_list_entry *entry = heap_alloc(sizeof(struct sleeping_list_entry));
|
struct sleeping_list_entry *entry;
|
||||||
entry->sleeping_until = until;
|
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) {
|
if (first_entry == 0) {
|
||||||
entry->next = 0;
|
entry->next = 0;
|
||||||
|
|
@ -84,7 +94,7 @@ static void sleep_until(uint64_t until) {
|
||||||
just_before->next = entry;
|
just_before->next = entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
yield_sti(&entry->continuation);
|
yield(&entry->continuation);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
218
src/user-apps/shell/assembler.c
Normal file
218
src/user-apps/shell/assembler.c
Normal 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);
|
||||||
|
}
|
||||||
38
src/user-apps/shell/assembler.h
Normal file
38
src/user-apps/shell/assembler.h
Normal 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);
|
||||||
226
src/user-apps/shell/builtin-words.c
Normal file
226
src/user-apps/shell/builtin-words.c
Normal 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( "*", ×_interpret);
|
||||||
|
add_emit_call( "/", ÷_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);
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
/* Calcite, src/user-apps/shell/shell.c
|
/* Calcite, src/user-apps/shell/builtin-words.h
|
||||||
* Copyright 2026 Benji Dial
|
* Copyright 2026 Benji Dial
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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/>.
|
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <calcite/syscalls.h>
|
#pragma once
|
||||||
#include <calcite/terminal.h>
|
|
||||||
|
|
||||||
void main() {
|
void add_builtin_words();
|
||||||
init_terminal();
|
|
||||||
terminal_write("hello\n", 6);
|
|
||||||
while (1)
|
|
||||||
sleep_ms(1000000000);
|
|
||||||
}
|
|
||||||
36
src/user-apps/shell/main.c
Normal file
36
src/user-apps/shell/main.c
Normal 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();
|
||||||
|
|
||||||
|
}
|
||||||
40
src/user-apps/shell/runtime.asm
Normal file
40
src/user-apps/shell/runtime.asm
Normal 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
|
||||||
218
src/user-apps/shell/runtime.c
Normal file
218
src/user-apps/shell/runtime.c
Normal 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;
|
||||||
63
src/user-apps/shell/runtime.h
Normal file
63
src/user-apps/shell/runtime.h
Normal 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();
|
||||||
|
|
@ -18,9 +18,12 @@
|
||||||
#include <kernel-public/framebuffer.h>
|
#include <kernel-public/framebuffer.h>
|
||||||
#include <kernel-public/process.h>
|
#include <kernel-public/process.h>
|
||||||
#include <flanterm_backends/fb.h>
|
#include <flanterm_backends/fb.h>
|
||||||
|
#include <kernel-public/files.h>
|
||||||
|
#include <kernel-public/input.h>
|
||||||
#include <kernel-public/ipc.h>
|
#include <kernel-public/ipc.h>
|
||||||
#include <calcite/dispatch.h>
|
#include <calcite/dispatch.h>
|
||||||
#include <calcite/syscalls.h>
|
#include <calcite/syscalls.h>
|
||||||
|
#include <calcite/terminal.h>
|
||||||
#include <calcite/format.h>
|
#include <calcite/format.h>
|
||||||
#include <calcite/memory.h>
|
#include <calcite/memory.h>
|
||||||
#include <flanterm.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 input_handle;
|
||||||
static ipc_dgram_receiver_handle_t output_handle;
|
static ipc_dgram_receiver_handle_t output_handle;
|
||||||
|
|
||||||
|
static char keytable[2048];
|
||||||
|
|
||||||
static struct dispatch_queue *queue;
|
static struct dispatch_queue *queue;
|
||||||
|
|
||||||
struct got_output_param {
|
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) {
|
[[noreturn]] static void input_thread(uint64_t) {
|
||||||
|
while (1) {
|
||||||
|
|
||||||
//TODO
|
int key = wait_for_keyboard_packet();
|
||||||
while (1)
|
int shift = flags & (TKF_LEFT_SHIFT | TKF_RIGHT_SHIFT);
|
||||||
sleep_ms(1000000000);
|
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() {
|
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(¶m);
|
||||||
|
close_file(keytable_handle);
|
||||||
|
}
|
||||||
|
|
||||||
uint64_t output_pipe = ipc_create_private_dgram_pipe();
|
uint64_t output_pipe = ipc_create_private_dgram_pipe();
|
||||||
uint64_t input_pipe = ipc_create_private_dgram_pipe();
|
uint64_t input_pipe = ipc_create_private_dgram_pipe();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -145,3 +145,11 @@ uint64_t get_ms_since_boot() {
|
||||||
void sleep_until_ms_since_boot(uint64_t ms) {
|
void sleep_until_ms_since_boot(uint64_t ms) {
|
||||||
do_syscall(ms, 0, 0, SYSCALL_SLEEP_UNTIL_MS_SINCE_BOOT);
|
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);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
* 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 <kernel-public/ipc.h>
|
||||||
#include <calcite/syscalls.h>
|
#include <calcite/syscalls.h>
|
||||||
#include <calcite/terminal.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_input = 0;
|
||||||
static int set_up_terminal_output = 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_size;
|
||||||
static int output_buffer_used;
|
static int output_buffer_used;
|
||||||
|
|
||||||
|
|
@ -40,7 +42,7 @@ static void output_buffer_size_at_least(int bytes) {
|
||||||
while (new_size < bytes)
|
while (new_size < bytes)
|
||||||
new_size *= 2;
|
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);
|
memcpy(new_buffer, output_buffer, output_buffer_used);
|
||||||
heap_dealloc(output_buffer, output_buffer_size);
|
heap_dealloc(output_buffer, output_buffer_size);
|
||||||
output_buffer = new_buffer;
|
output_buffer = new_buffer;
|
||||||
|
|
@ -50,7 +52,7 @@ static void output_buffer_size_at_least(int bytes) {
|
||||||
|
|
||||||
#define MAX_PACKET_LENGTH 500
|
#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)
|
if (set_up_terminal_output == 0)
|
||||||
return;
|
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)
|
if (bytes == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
const uint8_t *data8 = (const uint8_t *)data;
|
||||||
|
|
||||||
int last_newline = bytes - 1;
|
int last_newline = bytes - 1;
|
||||||
while (last_newline >= 0 && data[last_newline] != '\n')
|
while (last_newline >= 0 && data8[last_newline] != '\n')
|
||||||
--last_newline;
|
--last_newline;
|
||||||
|
|
||||||
if (last_newline == -1) {
|
if (last_newline == -1) {
|
||||||
|
|
@ -116,17 +120,152 @@ void terminal_write(const char *data, int bytes) {
|
||||||
send_output(data, last_newline + 1);
|
send_output(data, last_newline + 1);
|
||||||
if (last_newline != bytes - 1) {
|
if (last_newline != bytes - 1) {
|
||||||
output_buffer_size_at_least(bytes - last_newline - 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;
|
output_buffer_used = bytes - last_newline - 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void terminal_read(char *buffer, int bytes) {
|
void terminal_flush() {
|
||||||
//TODO
|
if (output_buffer_used != 0) {
|
||||||
(void)buffer;
|
send_output(output_buffer, output_buffer_used);
|
||||||
(void)bytes;
|
output_buffer_used = 0;
|
||||||
while (1)
|
}
|
||||||
sleep_ms(1000000000);
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue