From d66a721448fe2f4cd99f26a372a8ef0bebd84382 Mon Sep 17 00:00:00 2001 From: zongor Date: Sat, 20 Dec 2025 21:32:01 -0800 Subject: [PATCH] add minimal linux tui --- src/arch/linux-tui/devices.c | 152 ++++++++++++++++++++ src/arch/linux-tui/devices.h | 61 ++++++++ src/arch/linux-tui/main.c | 269 +++++++++++++++++++++++++++++++++++ 3 files changed, 482 insertions(+) create mode 100644 src/arch/linux-tui/devices.c create mode 100644 src/arch/linux-tui/devices.h create mode 100644 src/arch/linux-tui/main.c diff --git a/src/arch/linux-tui/devices.c b/src/arch/linux-tui/devices.c new file mode 100644 index 0000000..8ba1ee0 --- /dev/null +++ b/src/arch/linux-tui/devices.c @@ -0,0 +1,152 @@ +#include "devices.h" +#include +#include +#include + +i32 console_open(void *data, u32 mode, u32 handle, u8 *buffer, u32 size) { + USED(mode); + USED(size); + ConsoleDeviceData *console = (ConsoleDeviceData *)data; + console->handle = handle; + + u8 *info = (u8 *)buffer; + memcpy(&info[0], &console->handle, sizeof(u32)); + return 0; /* Success */ +} + +i32 console_read(void *data, u8 *buffer, u32 size) { + USED(data); + for (u32 i = 0; i < size; i++) { + u8 ch = getchar(); + if (ch == '\0') + break; + if (ch == '\n') + break; + buffer[i] = ch; + } + return 0; +} + +i32 console_write(void *data, const u8 *buffer, u32 size) { + USED(data); + + if (size > MEMORY_SIZE) { + return 0; + } + + for (u32 i = 0; i < size; i++) { + putchar(buffer[i]); + } + return 0; +} + +i32 console_close(void *data) { + USED(data); + /* Nothing to close — stdin/stdout are process-owned */ + return 0; +} + +i32 console_ioctl(void *data, u32 cmd, const u8 *buffer) { + USED(data); + USED(cmd); + USED(buffer); + return -1; /* Unsupported */ +} + +i32 screen_open(void *data, u32 mode, u32 handle, u8 *buffer, u32 size) { + USED(data); + USED(mode); + USED(handle); + USED(buffer); + USED(size); + return -1; +} + +i32 screen_read(void *data, u8 *buffer, u32 size) { + USED(data); + USED(buffer); + USED(size); + return -1; +} + +i32 screen_write(void *data, const u8 *buffer, u32 size) { + USED(data); + USED(buffer); + USED(size); + return -1; +} + +i32 screen_close(void *data) { + USED(data); + return -1; +} + +i32 screen_ioctl(void *data, u32 cmd, const u8 *buffer) { + USED(data); + USED(cmd); + USED(buffer); + return -1; +} + +/* MOUSE */ +i32 mouse_open(void *data, u32 mode, u32 handle, u8 *buffer, u32 size) { + USED(data); + USED(mode); + USED(handle); + USED(buffer); + USED(size); + return -1; +} + +i32 mouse_read(void *data, u8 *buffer, u32 size) { + USED(data); + USED(buffer); + USED(size); + return -1; +} + +i32 mouse_refresh(void *data, u8 *buffer) { + USED(data); + USED(buffer); + return -1; +} + +i32 mouse_write(void *data, const u8 *buffer, u32 size) { + USED(data); + USED(buffer); + USED(size); + return -1; +} + +i32 mouse_close(void *data) { + USED(data); + return 0; +} + +i32 keyboard_open(void *data, u32 mode, u32 handle, u8 *buffer, u32 size) { + USED(data); + USED(mode); + USED(handle); + USED(buffer); + USED(size); + return -1; +} + +i32 keyboard_read(void *data, u8 *buffer, u32 size) { + USED(data); + USED(buffer); + USED(size); + return -1; +} + +i32 keyboard_write(void *data, const u8 *buffer, u32 size) { + USED(data); + USED(buffer); + USED(size); + return -1; /* not writable */ +} + +i32 keyboard_close(void *data) { + USED(data); + return 0; +} diff --git a/src/arch/linux-tui/devices.h b/src/arch/linux-tui/devices.h new file mode 100644 index 0000000..527b4e5 --- /dev/null +++ b/src/arch/linux-tui/devices.h @@ -0,0 +1,61 @@ +#include "../../vm/device.h" +#include "../../vm/vm.h" + +#define IOCTL_GET_INFO 0x01 + +/* Screen device data */ +typedef struct screen_device_data_s { + u32 handle; + u32 width; + u32 height; + u32 buffer_size; + u8 *screen_buffer; + u32 update; +} ScreenDeviceData; + +/* Mouse device data */ +typedef struct mouse_device_data_s { + u32 handle; + u32 x; + u32 y; + u8 btn1; + u8 btn2; + u8 btn3; + u8 btn4; +} MouseDeviceData; + +/* Keyboard device data */ +typedef struct keyboard_device_data_s { + u32 handle; + i32 key_count; + const u8 *keys; +} KeyboardDeviceData; + +/* Console device data */ +typedef struct console_device_data_s { + u32 handle; + u32 size; +} ConsoleDeviceData; + +i32 screen_open(void *data, u32 mode, u32 handle, u8 *buffer, u32 size); +i32 screen_read(void *data, u8 *buffer, u32 size); +i32 screen_write(void *data, const u8 *buffer, u32 size); +i32 screen_close(void *data); +i32 screen_ioctl(void *data, u32 cmd, const u8 *buffer); + +i32 mouse_open(void *data, u32 mode, u32 handle, u8 *buffer, u32 size); +i32 mouse_read(void *data, u8 *buffer, u32 size); +i32 mouse_refresh(void *data, u8 *buffer); +i32 mouse_write(void *data, const u8 *buffer, u32 size); +i32 mouse_close(void *data); + +i32 keyboard_open(void *data, u32 mode, u32 handle, u8 *buffer, u32 size); +i32 keyboard_read(void *data, u8 *buffer, u32 size); +i32 keyboard_write(void *data, const u8 *buffer, u32 size); +i32 keyboard_close(void *data); + +i32 console_open(void *data, u32 mode, u32 handle, u8 *buffer, u32 size); +i32 console_read(void *data, u8 *buffer, u32 size); +i32 console_write(void *data, const u8 *buffer, u32 size); +i32 console_close(void *data); +i32 console_ioctl(void *data, u32 cmd, const u8 *buffer); diff --git a/src/arch/linux-tui/main.c b/src/arch/linux-tui/main.c new file mode 100644 index 0000000..6d38298 --- /dev/null +++ b/src/arch/linux-tui/main.c @@ -0,0 +1,269 @@ +#include "../../tools/assembler/assembler.h" +#include "../../vm/vm.h" +#include "devices.h" + +#include +#include +#include +#include + +#define MAX_SRC_SIZE 16384 + +static DeviceOps console_device_ops = {.open = console_open, + .read = console_read, + .write = console_write, + .close = console_close, + .ioctl = console_ioctl, + .refresh = nil}; + +static ConsoleDeviceData console_data = {0}; + +// Function to save VM state to ROM file +bool saveVM(const char *filename, VM *vm) { + FILE *file = fopen(filename, "wb"); + if (!file) { + perror("Failed to open file for writing"); + return false; + } + + // Write VM state (locals and pointers) + if (fwrite(&vm->pc, sizeof(u32), 1, file) != 1 || + fwrite(&vm->cp, sizeof(u32), 1, file) != 1 || + fwrite(&vm->fp, sizeof(u32), 1, file) != 1 || + fwrite(&vm->sp, sizeof(u32), 1, file) != 1 || + fwrite(&vm->mp, sizeof(u32), 1, file) != 1 || + fwrite(&vm->dc, sizeof(u32), 1, file) != 1 || + fwrite(&vm->flag, sizeof(i32), 1, file) != 1) { + fprintf(stderr, "Failed to write VM state\n"); + fclose(file); + return false; + } + // Write code section + if (fwrite(vm->code, 1, vm->cp, file) != vm->cp) { + fprintf(stderr, "Failed to write code section\n"); + fclose(file); + return false; + } + + // Write memory section + if (fwrite(vm->memory, 1, vm->mp, file) != vm->mp) { + fprintf(stderr, "Failed to write memory section\n"); + fclose(file); + return false; + } + + fclose(file); + return true; +} + +// Function to load VM state from ROM file +bool loadVM(const char *filename, VM *vm) { + FILE *file = fopen(filename, "rb"); + if (!file) { + perror("Failed to open ROM file"); + return false; + } + + // Read VM state (locals and pointers) + if (fread(&vm->pc, sizeof(u32), 1, file) != 1 || + fread(&vm->cp, sizeof(u32), 1, file) != 1 || + fread(&vm->fp, sizeof(u32), 1, file) != 1 || + fread(&vm->sp, sizeof(u32), 1, file) != 1 || + fread(&vm->mp, sizeof(u32), 1, file) != 1 || + fread(&vm->dc, sizeof(u32), 1, file) != 1 || + fread(&vm->flag, sizeof(i32), 1, file) != 1) { + fprintf(stderr, "Failed to read VM state\n"); + fclose(file); + return false; + } + + // Read code section + if (fread(vm->code, 1, vm->cp, file) != vm->cp) { + fprintf(stderr, "Failed to read code section\n"); + fclose(file); + return false; + } + + // Read memory section + if (fread(vm->memory, 1, vm->mp, file) != vm->mp) { + fprintf(stderr, "Failed to read memory section\n"); + fclose(file); + return false; + } + + fclose(file); + return true; +} + +// Function to compile and optionally save +bool compileAndSave(const char *source_file, const char *output_file, VM *vm) { + USED(vm); + USED(output_file); + USED(source_file); + + return true; +} + +#ifdef STATIC +#define SCOPES_COUNT 2048 +SymbolTable scopes[SCOPES_COUNT]; +#endif + +void symbol_table_init(ScopeTable *t) { +#ifdef STATIC + memset(scopes, 0, SCOPES_COUNT * sizeof(SymbolTable)); + t->scopes = scopes; + t->count = 0; + t->capacity = SCOPES_COUNT; +#else + t->scopes = calloc(16, sizeof(SymbolTable)); + t->count = 0; + t->capacity = 16; +#endif + + // Make sure that all the parents are the 'global' namespace. + for (u32 i = 0; i < t->capacity; i++) { + t->scopes[i].parent = -1; + } +} + +bool table_realloc(ScopeTable *table) { +#ifdef STATIC + if (table->count >= table->capacity) { + return false; + } +#else + if (table->count >= table->capacity) { + table->capacity *= 2; + table->scopes = + realloc(table->scopes, table->capacity * sizeof(SymbolTable)); + + // Make sure that all the parents are the 'global' namespace. + for (u32 i = table->count; i < table->capacity; i++) { + table->scopes[i].parent = -1; + } + } +#endif + return true; +} + +// Function to assemble and optionally save +bool assembleAndSave(const char *source_file, const char *output_file, VM *vm) { + FILE *f = fopen(source_file, "rb"); + if (!f) { + perror("fopen"); + return false; + } + + static char source[MAX_SRC_SIZE + 1]; + + fseek(f, 0, SEEK_END); + long len = ftell(f); + fseek(f, 0, SEEK_SET); + if (len >= MAX_SRC_SIZE) { + fprintf(stderr, "Source is larger than buffer\n"); + fclose(f); + return false; + } + size_t read = fread(source, 1, len, f); + source[read] = '\0'; + fclose(f); + + ScopeTable table = {0}; + symbol_table_init(&table); + assemble(vm, &table, source); +#ifndef STATIC + free(table.scopes); +#endif + + if (output_file) { + if (!saveVM(output_file, vm)) { + printf("Failed to save VM to %s\n", output_file); + return false; + } + printf("VM saved to %s\n", output_file); + } + return true; +} + +i32 main(i32 argc, char *argv[]) { + bool dump_rom = false; + char *input_file = nil; + char *output_file = nil; + bool is_rom = false; + bool is_ir = false; + + // Parse command line arguments + for (i32 i = 1; i < argc; i++) { + if (strcmp(argv[i], "-o") == 0 || strcmp(argv[i], "--dump-rom") == 0) { + dump_rom = true; + } else if (input_file == nil) { + // This is the input file + input_file = argv[i]; + + // Check if it's a ROM file + const char *ext = strrchr(argv[i], '.'); + if (ext && (strcmp(ext, ".rom") == 0)) { + is_rom = true; + } + if (ext && (strcmp(ext, ".ir") == 0)) { + is_ir = true; + } + } else if (output_file == nil && dump_rom) { + // This is the output file for -o flag + output_file = argv[i]; + } + } + + VM vm = {0}; + bool compilation_success = true; + if (input_file) { + if (is_rom) { + // Load ROM file directly + compilation_success = loadVM(input_file, &vm); + } else if (is_ir) { + // Compile Lisp file + if (dump_rom && output_file) { + compilation_success = assembleAndSave(input_file, output_file, &vm); + } else { + compilation_success = assembleAndSave(input_file, nil, &vm); + } + } else { + if (dump_rom && output_file) { + compilation_success = compileAndSave(input_file, output_file, &vm); + } else { + compilation_success = compileAndSave(input_file, nil, &vm); + } + } + } else { + printf("usage: undar ..."); + return 1; + } + + if (dump_rom) { + return (compilation_success) ? EXIT_SUCCESS : EXIT_FAILURE; + } + + // If dump_rom flag was set without specifying output file, use default + if (dump_rom && !is_rom && !output_file) { + if (!saveVM("memory_dump.bin", &vm)) { + printf("Failed to save VM to memory_dump.bin\n"); + return EXIT_FAILURE; + } + printf("VM saved to memory_dump.bin\n"); + return EXIT_SUCCESS; + } + + vm_register_device(&vm, "/dev/term/0", "terminal", &console_data, + &console_device_ops, 4); + + bool running = true; + while (running) { + if (!step_vm(&vm)) { + running = false; + break; + } + } + + return vm.flag; +}