Add Linux TUI example

This commit is contained in:
zongor 2025-09-21 16:23:26 +00:00
parent 299e36172b
commit 37ce661648
4 changed files with 396 additions and 1 deletions

View File

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

View File

@ -0,0 +1,98 @@
#include "devices.h"
#include <unistd.h>
#include <string.h>
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;
}

View File

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

264
src/arch/linux-tui/main.c Normal file
View File

@ -0,0 +1,264 @@
#include "../../tools/test.h"
#include "../../tools/parser.h"
#include "../../vm/vm.h"
#include "devices.h"
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <unistd.h>
#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] <file1.ul> [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;
}