all repos — xyw @ 55e3cd8b27768abe33e6f3c488bbf8fca9b521dc

A minimal virtual machine and assembler for terminals.

Implemented terminal device including on_keypress event; removed metadata support as it's not necessary if focusing only on terminal.
h3rald h3rald@h3rald.com
Fri, 30 Jan 2026 14:41:13 +0100
commit

55e3cd8b27768abe33e6f3c488bbf8fca9b521dc

parent

da2d0985fb6f8ebe7f072144482105b1c04a1e2e

5 files changed, 101 insertions(+), 113 deletions(-)

jump to
M devices/terminal.cdevices/terminal.c

@@ -11,6 +11,13 @@ {

(void)error; switch (addr) { + case TERMINAL_INPUT: + xyw_word vector = XYW_PEEKW(&data[TERMINAL_ON_KEYPRESS]); + if (vector) + { + xyw_eval(vector); + } + break; case TERMINAL_OUTPUT: fputc(data[addr], stdout); fflush(stdout);

@@ -18,19 +25,8 @@ break;

} } -void terminal_input(xyw_byte *data, xyw_byte addr, xyw_byte *error) +xyw_byte terminal_input(xyw_byte *data, xyw_byte addr, xyw_byte *error) { (void)error; - switch (addr) - { - case TERMINAL_INPUT: - data[addr] = (xyw_byte)getchar(); - // If on keypress word is set, trigger jump to address at that location - if (data[TERMINAL_ON_KEYPRESS] != 0) - { - xyw_word jump_addr = XYW_PEEKW(&data[TERMINAL_ON_KEYPRESS]); - // Todo: execute code at address - } - break; - } + return data[addr]; }
M devices/terminal.hdevices/terminal.h

@@ -1,3 +1,4 @@

#include "../xyw.h" void terminal_output(xyw_byte *data, xyw_byte addr, xyw_byte *error); +xyw_byte terminal_input(xyw_byte *data, xyw_byte addr, xyw_byte *error);
A examples/print-stdin.xyw

@@ -0,0 +1,11 @@

+ +.include "devices.xyw" +print-key terminal.on_keypress STW +end JMPw + +.label print-key + terminal.input LDB terminal.output STB + $0A terminal.output STB + BRK + +.label end
M xyw.hxyw.h

@@ -37,5 +37,6 @@ extern xyw_device xyw_devices[XYW_TOTAL_DEVICES];

int xyw_assemble(const char *input, const char *output); int xyw_run(const char *input); +int xyw_eval(xyw_word pc); #endif // XYW_H
M xywrun.cxywrun.c

@@ -22,6 +22,18 @@ #define SYSTEM_U 0xff03

#define SYSTEM_PAGE 0xff04 #define SYSTEM_ON_ERROR 0xff05 +#ifdef _WIN32 +#include <conio.h> +int getch() +{ + return _getch(); +} +#else +int getch() +{ + return getchar(); +} +#endif typedef enum { XYW_ERROR_NONE = 0,

@@ -248,7 +260,7 @@ xyw_device xyw_devices[XYW_TOTAL_DEVICES] = {

// System device (not implemented yet) {&xyw_memory[0xff00], 0, 0, 0}, // Terminal device - {&xyw_memory[0xff10], 0, 0, terminal_output}, + {&xyw_memory[0xff10], 0, terminal_input, terminal_output}, // Clock device {&xyw_memory[0xff20], clock_init, clock_input, 0}, // File device

@@ -257,64 +269,59 @@ // ...

}; // clang-format on -char *title = "xyw", *description = 0, *author = 0, *date = 0; - -static void process_metadata() +//// Main evaluation function - runs from current pc until BRK +//// Returns 1 on success, 0 on error +int xyw_eval(xyw_word start_pc) { - if ((xyw_memory[0x0003] == (0x1C | XYW_MODE_W)) && xyw_memory[0x0004] == '-' && xyw_memory[0x0005] == '-' && xyw_memory[0x0006] == '-' && xyw_memory[0x0007] == 0) + *pc = start_pc; + while (xyw_memory[SYSTEM_STATE] && *pc < SYSTEM_MEMORY_START) { - xyw_dbg("Metadata found\n"); - // JMPw found followed by ---, read metadata - xyw_word p = 0x0008; - - title = (char *)&xyw_memory[p]; - while (p < MEMORY_SIZE && xyw_memory[p] != 0) - p++; - if (p >= MEMORY_SIZE) - { - xyw_memory[MEMORY_SIZE - 1] = 0; - p = MEMORY_SIZE - 1; - } - p++; - xyw_dbg("- title: %s\n", title); - - description = (char *)&xyw_memory[p]; - while (p < MEMORY_SIZE && xyw_memory[p] != 0) - p++; - if (p >= MEMORY_SIZE) - { - xyw_memory[MEMORY_SIZE - 1] = 0; - p = MEMORY_SIZE - 1; - } - p++; - xyw_dbg("- description: %s\n", description); - - author = (char *)&xyw_memory[p]; - while (p < MEMORY_SIZE && xyw_memory[p] != 0) - p++; - if (p >= MEMORY_SIZE) + 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]) { - xyw_memory[MEMORY_SIZE - 1] = 0; - p = MEMORY_SIZE - 1; + // clang-format off + /* BRK */ case 0x00: return 1; // Simply return from evaluation + /* PSH */ OPC_SR1(0x01, , if (rx||ry) { PUSH(ARG); } else { (*pc)++; PUSH(READ(*pc)); if (w) { (*pc)++; } }) + /* POP */ OPC_SR1(0x02, , if (rx||ry) { SET(POP()); } else { POP(); } ); + /* CLR */ OPC_SR1(0x03, , SET(0)); + /* LDB */ OPC_SR1(0x04, , us_push(read(ADDR(ARG)))); + /* LDW */ OPC_SR1(0x05, , us_pushw(readw(ADDR(ARG)) )); + /* STB */ OPC_SR1(0x06, xyw_word addr = ADDR(ARG), write( addr, us_pop() )); + /* STW */ OPC_SR1(0x07, xyw_word addr = ADDR(ARG), writew( addr, us_popw() )); + /* INC */ OPC_SR1(0x08, , SET(ARG + 1)); + /* DEC */ OPC_SR1(0x09, , SET(ARG - 1)); + /* SHL */ OPC_SR1(0x0A, xyw_byte b = us_pop(), PUSH(ARG << b)); + /* SHR */ OPC_SR1(0x0B, xyw_byte b = us_pop(), PUSH(ARG >> b)); + /* ADD */ OPC_SR2(0x0C, PUSH(a + b)); + /* SUB */ OPC_SR2(0x0D, PUSH(a - b)); + /* MUL */ OPC_SR2(0x0E, PUSH(a * b)); + /* DIV */ OPC_SR2(0x0F, PUSH(a / b)); + /* EQU */ OPC_SR2(0x10, PUSH(a == b)); + /* NEQ */ OPC_SR2(0x11, PUSH(a != b)); + /* GTH */ OPC_SR2(0x12, PUSH(a > b)); + /* LTH */ OPC_SR2(0x13, PUSH(a < b)); + /* AND */ OPC_SR2(0x14, PUSH(a & b)); + /* IOR */ OPC_SR2(0x15, PUSH(a | b)); + /* XOR */ OPC_SR2(0x16, PUSH(a ^ b)); + /* NOT */ OPC_SR1(0x17, , SET(~ARG)); + /* SWP */ OPC_SR2(0x18, if ((rx||ry)) { SET(b); } else { PUSH(a); PUSH(b); } ); + /* DUP */ OPC_SR1(0x19, , PUSH(ARG)); + /* OVR */ OPC_SR2(0x1A, PUSH(b); PUSH(a); PUSH(b)); + /* ROT */ OPC_SR2(0x1B, PUSH(b); PUSH(POP()); PUSH(a)); + /* JMP */ OPC_SR1(0x1C, , *pc = ADDR(ARG)-1;); + /* JCN */ OPC_SR1(0x1D, xyw_word addr = ADDR(ARG), if (us_pop()) { *pc = addr-1; }); + /* JSR */ OPC_SR1(0x1E, xyw_word addr = ADDR(ARG), ss_pushw(*pc+1); *pc = addr-1;); + /* RTS */ case 0x1F: *pc = ss_popw()-1; break; + // clang-format on } - p++; - xyw_dbg("- author: %s\n", author); - - date = (char *)&xyw_memory[p]; - while (p < MEMORY_SIZE && xyw_memory[p] != 0) - p++; - if (p >= MEMORY_SIZE) - { - xyw_memory[MEMORY_SIZE - 1] = 0; - p = MEMORY_SIZE - 1; - } - xyw_dbg("- date: %s\n", date); - - if (p + 1 >= MEMORY_SIZE) - return; - p++; + (*pc)++; } + return 0; } + +//// Terminal device addresses +#define TERMINAL_INPUT 0xff10 +#define TERMINAL_ON_KEYPRESS 0xff12 //// Main run function int xyw_run(const char *input)

@@ -344,58 +351,30 @@ {

xyw_devices[i].init(xyw_devices[i].data); } } - // Process metadata - process_metadata(); // Set direct page to device page by default xyw_memory[SYSTEM_PAGE] = 0xff; - xyw_byte paused = 0; - while (xyw_memory[SYSTEM_STATE] && *pc < SYSTEM_MEMORY_START) + + int eval_result = xyw_eval(0x0000); + if (eval_result) { - if (!paused) + xyw_word terminal_vector = XYW_PEEKW(&xyw_memory[TERMINAL_ON_KEYPRESS]); + + // If terminal vector is set, enter stdin reading loop + if (terminal_vector) { - 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); + xyw_dbg("terminal.on_keypress vector set to %04X, entering input loop\n", terminal_vector); + while (xyw_memory[SYSTEM_STATE]) + { + int c = getch(); + // if CTRL+C or CTRL+D is pressed, exit + if (c == 3 || c == 4) + { + return 0; + } + write(TERMINAL_INPUT, (xyw_byte)c); + } } - switch (xyw_memory[*pc]) - { - // clang-format off - /* BRK */ case 0x00: (*pc)--; paused = 1; break; // Stop at current instruction, keep loop running - /* PSH */ OPC_SR1(0x01, , if (rx||ry) { PUSH(ARG); } else { (*pc)++; PUSH(READ(*pc)); if (w) { (*pc)++; } }) - /* POP */ OPC_SR1(0x02, , if (rx||ry) { SET(POP()); } else { POP(); } ); - /* CLR */ OPC_SR1(0x03, , SET(0)); - /* LDB */ OPC_SR1(0x04, , us_push(read(ADDR(ARG)))); - /* LDW */ OPC_SR1(0x05, , us_pushw(readw(ADDR(ARG)) )); - /* STB */ OPC_SR1(0x06, xyw_word addr = ADDR(ARG), write( addr, us_pop() )); - /* STW */ OPC_SR1(0x07, xyw_word addr = ADDR(ARG), writew( addr, us_popw() )); - /* INC */ OPC_SR1(0x08, , SET(ARG + 1)); - /* DEC */ OPC_SR1(0x09, , SET(ARG - 1)); - /* SHL */ OPC_SR1(0x0A, xyw_byte b = us_pop(), PUSH(ARG << b)); - /* SHR */ OPC_SR1(0x0B, xyw_byte b = us_pop(), PUSH(ARG >> b)); - /* ADD */ OPC_SR2(0x0C, PUSH(a + b)); - /* SUB */ OPC_SR2(0x0D, PUSH(a - b)); - /* MUL */ OPC_SR2(0x0E, PUSH(a * b)); - /* DIV */ OPC_SR2(0x0F, PUSH(a / b)); - /* EQU */ OPC_SR2(0x10, PUSH(a == b)); - /* NEQ */ OPC_SR2(0x11, PUSH(a != b)); - /* GTH */ OPC_SR2(0x12, PUSH(a > b)); - /* LTH */ OPC_SR2(0x13, PUSH(a < b)); - /* AND */ OPC_SR2(0x14, PUSH(a & b)); - /* IOR */ OPC_SR2(0x15, PUSH(a | b)); - /* XOR */ OPC_SR2(0x16, PUSH(a ^ b)); - /* NOT */ OPC_SR1(0x17, , SET(~ARG)); - /* SWP */ OPC_SR2(0x18, if ((rx||ry)) { SET(b); } else { PUSH(a); PUSH(b); } ); - /* DUP */ OPC_SR1(0x19, , PUSH(ARG)); - /* OVR */ OPC_SR2(0x1A, PUSH(b); PUSH(a); PUSH(b)); - /* ROT */ OPC_SR2(0x1B, PUSH(b); PUSH(POP()); PUSH(a)); - /* JMP */ OPC_SR1(0x1C, , *pc = ADDR(ARG)-1;); - /* JCN */ OPC_SR1(0x1D, xyw_word addr = ADDR(ARG), if (us_pop()) { *pc = addr-1; }); - /* JSR */ OPC_SR1(0x1E, xyw_word addr = ADDR(ARG), ss_pushw(*pc+1); *pc = addr-1;); - /* RTS */ case 0x1F: *pc = ss_popw()-1; break; - // clang-format on - } - // TODO: error handling etc. - // At present, JCN, JMP, JSR and RTS set the pc to 1 value less than the actual address - // because we are relying on the fact that pc will be incremented at the end of the loop - (*pc)++; } + return 0; }