Implementing support for loading images and returning to previous image.
h3rald h3rald@h3rald.com
Wed, 22 Jul 2026 11:44:03 +0200
4 files changed,
240 insertions(+),
10 deletions(-)
M
devices/system.c
→
devices/system.c
@@ -7,6 +7,11 @@ #define SYSTEM_STATE 0x00
#define SYSTEM_ERROR 0x01 #define SYSTEM_PAGE 0x02 #define SYSTEM_RANDOM 0x03 +#define SYSTEM_IMAGE_EXEC 0x06 +#define SYSTEM_IMAGE_EXEC_TRIGGER 0x08 + +#define SYSTEM_IMAGE_EXEC_DIRECTION 0x01 // 0 = exec child, 1 = return to parent +#define SYSTEM_IMAGE_EXEC_PRESERVE 0x02 // EXEC only: snapshot self to be able to return to previous image void system_setup(xyw_byte *data) {@@ -24,3 +29,24 @@ }
(void)error; return data[addr]; } + +void system_output(xyw_byte *data, xyw_byte addr, xyw_byte *error) +{ + (void)error; + if (addr == SYSTEM_IMAGE_EXEC_TRIGGER && data[SYSTEM_IMAGE_EXEC_TRIGGER] != 0) + { + xyw_byte trig = data[SYSTEM_IMAGE_EXEC_TRIGGER]; + data[SYSTEM_IMAGE_EXEC_TRIGGER] = 0; + if (trig & SYSTEM_IMAGE_EXEC_DIRECTION) + { + xyw_request_return(); + } + else + { + xyw_word target = XYW_PEEKW(&data[SYSTEM_IMAGE_EXEC]); + if (target != 0) { + xyw_request_exec(target, trig & SYSTEM_IMAGE_EXEC_PRESERVE); + } + } + } +}
M
devices/system.h
→
devices/system.h
@@ -2,3 +2,4 @@ #include "../xyw.h"
void system_setup(xyw_byte *data); xyw_byte system_input(xyw_byte *data, xyw_byte addr, xyw_byte *error); +void system_output(xyw_byte *data, xyw_byte addr, xyw_byte *error);
M
xywrun.c
→
xywrun.c
@@ -13,11 +13,14 @@
#define USER_STACK_SIZE 128 #define SYSTEM_FRAME_SIZE 8 #define SYSTEM_MAX_FRAMES 48 -#define XYW_MAX_VECTORS 4 +#define MAX_VECTORS 4 #define USER_MEMORY_START 0x000 #define DEVICE_AREA_START 0xff00 +#define MAX_IMAGE_EXEC_DEPTH 8 +#define MAX_IMAGE_EXEC_PATH_LENGTH 256 + /* system device addresses */ #define SYSTEM_STATE 0xff00 #define SYSTEM_ERROR 0xff01@@ -54,6 +57,7 @@ XYW_ERROR_NONE = 0,
XYW_ERROR_DIVISION_BY_ZERO, XYW_ERROR_STACK_UNDERFLOW, XYW_ERROR_STACK_OVERFLOW, + XYW_ERROR_EXEC_DEPTH_EXCEEDED, } xyw_error; #define SYSTEM_STATE_RUNNING 0x01@@ -88,7 +92,7 @@ xyw_word *xw = &xw_storage;
xyw_word *yw = &yw_storage; //// Vector slots -static xyw_vector_slot vector_slots[XYW_MAX_VECTORS]; +static xyw_vector_slot vector_slots[MAX_VECTORS]; //// Helpers to retrieve device number and address within the device static inline int get_device(xyw_word addr)@@ -203,7 +207,7 @@
// Request a vector void xyw_request_vector(xyw_word handler) { - for (int i = 0; i < XYW_MAX_VECTORS; i++) + for (int i = 0; i < MAX_VECTORS; i++) { if (!vector_slots[i].pending) {@@ -218,7 +222,7 @@
// Dispatch requested vectors (called once per xyw_eval iteration) static void dispatch_vectors(void) { - for (int i = 0; i < XYW_MAX_VECTORS; i++) + for (int i = 0; i < MAX_VECTORS; i++) { if (vector_slots[i].pending) {@@ -365,8 +369,8 @@
//// Initialize devices xyw_device xyw_devices[XYW_TOTAL_DEVICES] = { - // System device (not implemented yet) - {&xyw_memory[0xff00], system_setup, system_input, 0, 0}, + // System device + {&xyw_memory[0xff00], system_setup, system_input, system_output, 0}, // Terminal device {&xyw_memory[0xff10], 0, terminal_input, terminal_output, terminal_teardown}, // Clock device@@ -430,14 +434,207 @@ xyw_request_vector(on_timer_handler);
} } - // TODO: Beeper playback-finished handling (need BEEPER_ON_FINISHED vector) - // xyw_word on_beeper_finished = XYW_PEEKW(&xyw_memory[BEEPER_ON_FINISHED]); - // if (on_beeper_finished != 0 && beeper_check_finished()) - // xyw_request_vector(on_beeper_finished); + return 0; +} + +//// Snapshot management + +typedef struct { + xyw_byte memory[MEMORY_SIZE]; + xyw_byte user_stack[USER_STACK_SIZE]; + xyw_byte system_stack[SYSTEM_MAX_FRAMES * SYSTEM_FRAME_SIZE]; + xyw_byte s, u, x_val, y_val; + xyw_word pc_val, xw_val, yw_val; + xyw_vector_slot vector_slots[MAX_VECTORS]; + xyw_byte error_handler_entry_s; + int argv_processed; +} xyw_image_snapshot; + +static xyw_image_snapshot exec_stack[MAX_IMAGE_EXEC_DEPTH]; +static int exec_stack_depth = 0; +static int exec_pending = 0; +static int exec_preserve = 0; +static char exec_path_buf[MAX_IMAGE_EXEC_PATH_LENGTH]; +static int return_pending = 0; + +// Read a null-terminated string out of VM memory into a local buffer +static void get_string_from_memory(xyw_word addr, char *buf, size_t bufsize) +{ + size_t i; + for (i = 0; i < bufsize - 1 && xyw_memory[addr + i] != 0; i++) + buf[i] = xyw_memory[addr + i]; + buf[i] = '\0'; +} + +void xyw_request_exec(xyw_word path_addr, int preserve) +{ + get_string_from_memory(path_addr, exec_path_buf, sizeof(exec_path_buf)); + exec_preserve = preserve; + exec_pending = 1; +} + +void xyw_request_return(void) +{ + return_pending = 1; +} + +static void split_exec_string(char *full, char **path, char **argstart) +{ + char *sp = strchr(full, ' '); + if (sp) + { + *sp = '\0'; + *argstart = sp + 1; + } + else + { + *argstart = NULL; + } + *path = full; +} + +static void reset_vm_state(void) +{ + memset(xyw_memory, 0, sizeof(xyw_memory)); + memset(user_stack, 0, sizeof(user_stack)); + memset(system_stack, 0, sizeof(system_stack)); + s = 0; + u = 0; + x_val = 0; + y_val = 0; + xw_storage = 0; + yw_storage = 0; + pc_storage = 0; + memset(vector_slots, 0, sizeof(vector_slots)); + error_handler_entry_s = 0; + xyw_argv_processed = 0; +} + +static int load_and_launch(const char *path, char *argstart) +{ + FILE *fp = fopen(path, "rb"); + if (!fp) + { + fprintf(stderr, "Error - Could not open file [%s]\n", path); + xyw_memory[SYSTEM_STATE] &= ~SYSTEM_STATE_RUNNING; + return -1; + } + size_t bytes_read = fread(&xyw_memory[USER_MEMORY_START], 1, MEMORY_SIZE - USER_MEMORY_START, fp); + fclose(fp); + if (bytes_read == 0) + { + fprintf(stderr, "Error - Could not read file contents [%s]\n", path); + xyw_memory[SYSTEM_STATE] &= ~SYSTEM_STATE_RUNNING; + return -1; + } + xyw_memory[SYSTEM_STATE] |= SYSTEM_STATE_RUNNING; + + for (int i = 0; i < XYW_TOTAL_DEVICES; i++) + if (xyw_devices[i].setup) + xyw_devices[i].setup(xyw_devices[i].data); + xyw_memory[SYSTEM_PAGE] = 0xff; + *pc = 0x0000; + + if (argstart && XYW_PEEKW(&xyw_memory[TERMINAL_ON_ARGUMENT])) + { + char *tok = strtok(argstart, " "); + int first = 1; + while (tok) + { + if (!first) + writeb(TERMINAL_INPUT, XYW_ARGUMENT_SEPARATOR); + for (size_t j = 0; tok[j] != '\0'; j++) + writeb(TERMINAL_INPUT, (xyw_byte)tok[j]); + first = 0; + tok = strtok(NULL, " "); + } + writeb(TERMINAL_INPUT, XYW_END_OF_ARGUMENTS); + xyw_argv_processed = 1; + } return 0; } +static void save_snapshot(xyw_image_snapshot *snap) +{ + memcpy(snap->memory, xyw_memory, sizeof(xyw_memory)); + memcpy(snap->user_stack, user_stack, sizeof(user_stack)); + memcpy(snap->system_stack, system_stack, sizeof(system_stack)); + snap->s = s; + snap->u = u; + snap->x_val = x_val; + snap->y_val = y_val; + snap->pc_val = *pc; + snap->xw_val = *xw; + snap->yw_val = *yw; + memcpy(snap->vector_slots, vector_slots, sizeof(vector_slots)); + snap->error_handler_entry_s = error_handler_entry_s; + snap->argv_processed = xyw_argv_processed; +} + +static void restore_snapshot(xyw_image_snapshot *snap) +{ + memcpy(xyw_memory, snap->memory, sizeof(xyw_memory)); + memcpy(user_stack, snap->user_stack, sizeof(user_stack)); + memcpy(system_stack, snap->system_stack, sizeof(system_stack)); + s = snap->s; + u = snap->u; + x_val = snap->x_val; + y_val = snap->y_val; + *pc = snap->pc_val; + *xw = snap->xw_val; + *yw = snap->yw_val; + memcpy(vector_slots, snap->vector_slots, sizeof(vector_slots)); + error_handler_entry_s = snap->error_handler_entry_s; + xyw_argv_processed = snap->argv_processed; + + // Devices with state outside xyw_memory (file cursor, clock timer target, + // beeper device) don't roll back automatically — re-sync to a clean slate + for (int i = 0; i < XYW_TOTAL_DEVICES; i++) + if (xyw_devices[i].setup) + xyw_devices[i].setup(xyw_devices[i].data); +} + +static void handle_exec_return_requests(void) +{ + if (exec_pending) + { + exec_pending = 0; + if (exec_preserve) + { + if (exec_stack_depth >= MAX_IMAGE_EXEC_DEPTH) + { + xyw_memory[SYSTEM_ERROR] = XYW_ERROR_EXEC_DEPTH_EXCEEDED; + return; + } + save_snapshot(&exec_stack[exec_stack_depth++]); + } + // preserve == 0: one-way handoff — no push, no way back to this image + + char path_copy[MAX_IMAGE_EXEC_PATH_LENGTH]; + strncpy(path_copy, exec_path_buf, sizeof(path_copy) - 1); + path_copy[sizeof(path_copy) - 1] = '\0'; + char *path, *argstart; + split_exec_string(path_copy, &path, &argstart); + + reset_vm_state(); + load_and_launch(path, argstart); + } + else if (return_pending) + { + return_pending = 0; + if (exec_stack_depth > 0) + { + restore_snapshot(&exec_stack[--exec_stack_depth]); + } + else + { + // No parent to return to — behaves like HLT + xyw_memory[SYSTEM_STATE] &= ~SYSTEM_STATE_RUNNING; + } + } +} + //// Main evaluation function - runs from current pc until BRK int xyw_eval(xyw_word start_pc) {@@ -457,6 +654,10 @@ while ((system_running_state()) && *pc < DEVICE_AREA_START)
{ process_events(); dispatch_vectors(); + handle_exec_return_requests(); + if (!system_running_state()) { + break; // exec/return may have halted or swapped mid-iteration + } XYW_DBG(" -- PC: %04X -> %02X (%s) [U:%02X|S:%02X|X:%02X|Y:%02X|XW:%04X|YW:%04X]\n", *pc, xyw_memory[*pc], xyw_instructions[xyw_memory[*pc] & 0x1F], u, s, *x, *y, *xw, *yw); switch (xyw_memory[*pc]) {