all repos — xyw @ 3007

A minimal virtual machine and assembler for terminals.

devices/system.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "../xyw.h"

#define SYSTEM_STATE 0x00
#define SYSTEM_ERROR 0x01
#define SYSTEM_PAGE 0x02
#define SYSTEM_RANDOM 0x03
#define SYSTEM_ON_ERROR 0x04
#define SYSTEM_IMAGE_EXEC 0x06

#define MAX_IMAGE_EXEC_DEPTH 8
#define MAX_IMAGE_EXEC_PATH_LENGTH 256

/// Error handler tracking
static xyw_byte error_handler_entry_s = 0;
static int error_handler_armed = 0;

/// Exec stack management
static xyw_vm_snapshot exec_stack[MAX_IMAGE_EXEC_DEPTH];
static int exec_stack_depth = 0;
static int exec_pending = 0;
static char exec_path_buf[MAX_IMAGE_EXEC_PATH_LENGTH];

void system_setup(xyw_byte *data, xyw_byte *error)
{
    (void)data;
    (void)error;
    srand(time(NULL));
    error_handler_entry_s = 0;
    error_handler_armed = 0;
}

xyw_byte system_input(xyw_byte *data, xyw_byte addr, xyw_byte *error)
{
    if (addr == SYSTEM_RANDOM)
        return (xyw_byte)(rand() & 0xFF);
    (void)error;
    return data[addr];
}

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';
}

static void request_exec(xyw_word path_addr)
{
    get_string_from_memory(path_addr, exec_path_buf, sizeof(exec_path_buf));
    exec_pending = 1;
}

void system_output(xyw_byte *data, xyw_byte addr, xyw_byte *error)
{
    (void)error;
    if (addr == SYSTEM_IMAGE_EXEC + 1)
    {
        xyw_word target = XYW_PEEKW(&data[SYSTEM_IMAGE_EXEC]);
        if (target != 0)
            request_exec(target);
    }
}

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 handle_exec_requests(xyw_byte *data)
{
    if (!exec_pending)
        return;
    exec_pending = 0;

    if (exec_stack_depth >= MAX_IMAGE_EXEC_DEPTH)
    {
        data[SYSTEM_ERROR] = XYW_SYSTEM_ERROR_EXEC_DEPTH_EXCEEDED;
        return;
    }
    xyw_vm_save_snapshot(&exec_stack[exec_stack_depth++]);

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

    xyw_vm_reset();
    if (xyw_vm_load_image(path, argstart) != 0)
    {
        // Restore parent state so error handler can run in the caller's context
        xyw_vm_restore_snapshot(&exec_stack[--exec_stack_depth]);
    }
}

void system_tick(xyw_byte *data)
{
    // Check if returning from error handler (after RTS popped the frame)
    if ((data[SYSTEM_STATE] & XYW_STATE_ERROR) &&
        error_handler_armed &&
        xyw_vm_get_system_depth() == error_handler_entry_s)
    {
        data[SYSTEM_STATE] &= ~XYW_STATE_ERROR;
        error_handler_entry_s = 0;
        error_handler_armed = 0;
    }

    // Check for new error condition
    if (data[SYSTEM_ERROR] != XYW_SYSTEM_ERROR_NONE && !(data[SYSTEM_STATE] & XYW_STATE_ERROR))
    {
        xyw_word on_error_handler = XYW_PEEKW(&data[SYSTEM_ON_ERROR]);
        if (on_error_handler != 0)
        {
            XYW_DBG("Error occurred: %02X, jumping to error handler at %04X\n", data[SYSTEM_ERROR], on_error_handler);
            data[SYSTEM_STATE] |= XYW_STATE_ERROR;
            error_handler_armed = 1;
            error_handler_entry_s = xyw_vm_get_system_depth();
            xyw_vm_push_frame(*pc);
            *pc = on_error_handler;
        }
        else
        {
            // No error handler: halt execution
            data[SYSTEM_STATE] &= ~XYW_STATE_RUNNING;
        }
    }

    // Handle exec requests
    handle_exec_requests(data);
}

void system_save(xyw_byte *data, xyw_byte *state)
{
    (void)data;
    state[0] = error_handler_entry_s;
    state[1] = (xyw_byte)error_handler_armed;
}

void system_restore(xyw_byte *data, const xyw_byte *state)
{
    (void)data;
    error_handler_entry_s = state[0];
    error_handler_armed = state[1];
}

int xyw_system_try_restore_parent(void)
{
    if (exec_stack_depth > 0)
    {
        xyw_vm_restore_snapshot(&exec_stack[--exec_stack_depth]);
        return 1;
    }
    return 0;
}