diff --git a/Makefile b/Makefile index ccb5783..4ea1b59 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ BASE_CFLAGS := -I$(SRC_DIR) -Wall -Wextra -Werror -pedantic CORE_CFLAGS := $(BASE_CFLAGS) -std=c89 -ffreestanding -nostdlib -fno-builtin # --- PLATFORM-SPECIFIC FLAGS --- -PLATFORM_CFLAGS := $(BASE_CFLAGS) -std=c99 +PLATFORM_CFLAGS := $(BASE_CFLAGS) # --- MODE & PLATFORM SPECIFIC FLAGS --- ifeq ($(BUILD_MODE), deploy) diff --git a/src/arch/linux-tui/devices.c b/src/arch/linux-tui/devices.c new file mode 100644 index 0000000..dcca021 --- /dev/null +++ b/src/arch/linux-tui/devices.c @@ -0,0 +1,98 @@ +#include "devices.h" +#include +#include + +i32 console_open(void *data, u32 mode) { + USED(mode); + USED(data); + /* Nothing to open — stdin/stdout are always available */ + return 0; /* Success */ +} + +i32 console_read(void *data, u8 *buffer, u32 size) { + USED(data); + ssize_t result = read(STDIN_FILENO, buffer, size); + if (result < 0) return -1; /* Error */ + return (i32)result; /* Bytes read */ +} + +i32 console_write(void *data, const u8 *buffer, u32 size) { + USED(data); + ssize_t result = write(STDOUT_FILENO, buffer, size); + if (result < 0) return -1; /* Error */ + return (i32)result; /* Bytes written */ +} + +i32 console_close(void *data) { + USED(data); + /* Nothing to close — stdin/stdout are process-owned */ + return 0; +} + +i32 console_ioctl(void *data, u32 cmd, void *args) { + USED(data); + USED(cmd); + USED(args); + return -1; /* Unsupported */ +} + +i32 screen_open(void *data, u32 mode) { + USED(mode); + ScreenDeviceData *screen = (ScreenDeviceData *)data; + USED(screen); + + return 0; +} + +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) { + ScreenDeviceData *screen = (ScreenDeviceData *)data; + + if (size > screen->framebuffer_size * sizeof(u8)) { + return -1; + } + + memcpy(&screen->vm->memory[screen->framebuffer_pos], buffer, size); + return 0; +} + +i32 screen_close(void *data) { + ScreenDeviceData *screen = (ScreenDeviceData *)data; + USED(screen); + + return 0; +} + +i32 keyboard_open(void *data, u32 mode) { + USED(data); + USED(mode); + return 0; +} + +i32 keyboard_read(void *data, u8 *buffer, u32 size) { + KeyboardDeviceData *kbd = (KeyboardDeviceData *)data; + + if (size < (u32)kbd->key_count) + return -1; + + memcpy(buffer, kbd->keys, kbd->key_count); + return 0; +} + +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..7481a6c --- /dev/null +++ b/src/arch/linux-tui/devices.h @@ -0,0 +1,33 @@ +#include "../../vm/device.h" +#include "../../vm/vm.h" + +/* Screen device data */ +typedef struct screen_device_data_s { + u32 width; + u32 height; + u32 framebuffer_pos; + u32 framebuffer_size; + VM* vm; +} ScreenDeviceData; + +/* Keyboard device data */ +typedef struct keyboard_device_data_s { + const u8 *keys; + i32 key_count; +} KeyboardDeviceData; + +i32 screen_open(void *data, u32 mode); +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 keyboard_open(void *data, u32 mode); +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); +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, void *args); diff --git a/src/arch/linux-tui/main.c b/src/arch/linux-tui/main.c new file mode 100644 index 0000000..c8a47bb --- /dev/null +++ b/src/arch/linux-tui/main.c @@ -0,0 +1,264 @@ +#include "../../tools/test.h" +#include "../../tools/parser.h" +#include "../../vm/vm.h" +#include "devices.h" + +#include +#include +#include +#include +#include +#include +#include + +#define MAX_SRC_SIZE 16384 + +static DeviceOps screen_ops = {.open = screen_open, + .read = screen_read, + .write = screen_write, + .close = screen_close, + .ioctl = nil}; + +static DeviceOps console_device_ops = { + .open = console_open, + .read = console_read, + .write = console_write, + .close = console_close, + .ioctl = console_ioctl, +}; + +static ScreenDeviceData screen_data = {0}; + +void compileFile(const char *path, VM *vm) { + USED(vm); + + FILE *f = fopen(path, "rb"); + if (!f) { + perror("fopen"); + exit(1); + } + + 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) { + perror("source is larget than buffer"); + exit(1); + } + size_t read = fread(source, 1, len, f); + source[read] = '\0'; + fclose(f); +} + +void repl(VM *vm) { + USED(vm); + + char buffer[1024 * 10] = {0}; // Larger buffer for multi-line input + char line[1024]; + + for (;;) { + // Count current parentheses balance + int paren_balance = 0; + for (int i = 0; buffer[i]; i++) { + if (buffer[i] == '(') paren_balance++; + else if (buffer[i] == ')') paren_balance--; + } + + // Show appropriate prompt + if (paren_balance > 0) { + printf(".. "); // Continuation prompt when unbalanced + } else { + printf("> "); // Normal prompt when balanced + } + fflush(stdout); + + if (!fgets(line, sizeof(line), stdin)) { + printf("\n"); + break; + } + + // Append the new line to buffer + strncat(buffer, line, sizeof(buffer) - strlen(buffer) - 1); + + // Recalculate balance after adding new line + paren_balance = 0; + for (int i = 0; buffer[i]; i++) { + if (buffer[i] == '(') paren_balance++; + else if (buffer[i] == ')') paren_balance--; + } + + // Only parse when parentheses are balanced + if (paren_balance == 0) { + // Check if buffer has actual content (not just whitespace) + int has_content = 0; + for (int i = 0; buffer[i]; i++) { + if (!isspace(buffer[i])) { + has_content = 1; + break; + } + } + + if (has_content) { + ExprNode *ast = expr_parse(buffer, strlen(buffer)); + if (!ast) { + printf("Parse failed.\n"); + } else { + printf("AST:\n"); + expr_print(ast, 0); + expr_free(ast); + } + } + + // Reset buffer for next input + buffer[0] = '\0'; + } + // If unbalanced, continue reading more lines + } + exit(0); +} + +enum FlagType { + FLAG_NONE = 0, + FLAG_TEST_MODE = 1, + FLAG_DUMP_ROM = 2, + FLAG_GUI_MODE = 4, +}; + +#define MAX_INPUT_FILES 16 /* Adjust based on your system's constraints */ + +struct CompilerConfig { + u32 flags; + char *input_files[MAX_INPUT_FILES]; + i32 input_file_count; +}; + +i32 parse_arguments(i32 argc, char *argv[], struct CompilerConfig *config) { + i32 i; + + /* Initialize config */ + config->flags = 0; + config->input_file_count = 0; + + /* Zero out input files array for safety */ + for (i = 0; i < MAX_INPUT_FILES; i++) { + config->input_files[i] = NULL; + } + + for (i = 1; i < argc; i++) { + if (argv[i][0] == '-') { + /* Long and short flag handling */ + if (strcmp(argv[i], "-g") == 0 || strcmp(argv[i], "--gui") == 0) { + config->flags |= FLAG_GUI_MODE; + } else if (strcmp(argv[i], "-t") == 0 || strcmp(argv[i], "--test") == 0) { + config->flags |= FLAG_TEST_MODE; + } else if (strcmp(argv[i], "-o") == 0 || + strcmp(argv[i], "--dump-rom") == 0) { + config->flags |= FLAG_DUMP_ROM; + } else { + fprintf(stderr, "Unknown flag: %s\n", argv[i]); + return -1; + } + } else if (strstr(argv[i], ".ul") != NULL) { + /* Collect input files */ + if (config->input_file_count >= MAX_INPUT_FILES) { + fprintf(stderr, "Too many input files. Maximum is %lld\n", + MAX_INPUT_FILES); + return -1; + } + config->input_files[config->input_file_count++] = argv[i]; + } + } + + return 0; +} + +/* + * This needs to be done dynamically eventually + */ +void register_sdl_devices(VM *vm) { + screen_data.vm = vm; + + struct winsize w; + ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); + + screen_data.width = w.ws_row * 2; + screen_data.height = w.ws_col; + screen_data.framebuffer_size = w.ws_row * w.ws_col * 2; + screen_data.framebuffer_pos = vm->mp; + vm->mp += screen_data.framebuffer_size; /* advance memory pointer */ + + vm_register_device(vm, "/dev/screen/0", "screen", &screen_data, &screen_ops); +} + +i32 main(i32 argc, char *argv[]) { + struct CompilerConfig config = {0}; + + if (parse_arguments(argc, argv, &config) != 0) { + fprintf(stderr, "Usage: %s [-d] [-g] [-o] [file2.ul] ...\n", + argv[0]); + return 64; + } + + VM vm = {0}; + if (config.input_file_count == 0) { + repl(&vm); + } else { + for (i32 j = 0; j < config.input_file_count; j++) { + compileFile(config.input_files[j], &vm); + } + + if (config.flags & FLAG_DUMP_ROM) { + FILE *file = fopen("memory_dump.bin", "wb"); + if (!file) { + perror("Failed to open file"); + return EXIT_FAILURE; + } + + size_t code_written = fwrite(vm.code, 1, CODE_SIZE, file); + if (code_written != CODE_SIZE) { + fprintf(stderr, "Incomplete code write: %zu bytes written out of %llu\n", + code_written, CODE_SIZE); + fclose(file); + return EXIT_FAILURE; + } + + size_t memory_written = fwrite(vm.memory, 1, MEMORY_SIZE, file); + if (memory_written != MEMORY_SIZE) { + fprintf(stderr, + "Incomplete memory write: %zu bytes written out of %llu\n", + memory_written, MEMORY_SIZE); + fclose(file); + return EXIT_FAILURE; + } + + fclose(file); + } + } + bool running = true; + vm_register_device(&vm, "/dev/term/0", "terminal", nil, &console_device_ops); + + if (config.flags & FLAG_GUI_MODE) { + u32 i; + register_sdl_devices(&vm); + while (running) { + step_vm(&vm); + + for (i = 0; i < vm.dc; i++) { + Device *dev = &vm.devices[i]; + if (strcmp(dev->type, "screen") == 0) { + //ScreenDeviceData *screen = (ScreenDeviceData *)dev->data; + //vm.memory[screen->framebuffer_pos], + break; + } + } + } + } else { + while (running) { + running = step_vm(&vm); + } + } + + return 0; +}