aboutsummaryrefslogtreecommitdiff
path: root/pu1
diff options
context:
space:
mode:
authorGravatar Moon Flower <moon@coffin.ir> 2025-08-11 11:34:18 +0330
committerGravatar Moon Flower <moon@coffin.ir> 2025-08-11 11:34:18 +0330
commit2fc20ad0b11e60ecfacc653024f2b582acad6e86 (patch)
treed7e3f793f32928e40a18c56d00875a5b8e74dc62 /pu1
parentvscode theme extension (diff)
ir2 initial release!
Diffstat (limited to 'pu1')
-rw-r--r--pu1/code.pasm23
-rw-r--r--pu1/pasm-syntax/package.json26
-rw-r--r--pu1/pasm-syntax/pasm-syntax-0.0.1.vsixbin0 -> 2163 bytes
-rw-r--r--pu1/pasm-syntax/syntaxes/pasm.tmLanguage.json145
-rw-r--r--pu1/pu.c419
-rw-r--r--pu1/pu.out.cbin0 -> 1405 bytes
-rw-r--r--pu1/putest.c380
-rw-r--r--pu1/repl.pu1
-rw-r--r--pu1/tbin0 -> 115 bytes
9 files changed, 994 insertions, 0 deletions
diff --git a/pu1/code.pasm b/pu1/code.pasm
new file mode 100644
index 0000000..94f37c5
--- /dev/null
+++ b/pu1/code.pasm
@@ -0,0 +1,23 @@
+(1 ar4 con 60 ar2 r4a mem con 1 ar4 con 60 ar3 con 0 ar2 con 2 hlt)
+(2 con 255 ar7 con 2 ar2 con 1 ar3 con 2 hlt)
+
+# init sdl (1000, 600)
+con 0 ar3 con 2 ar2 con 1000 ar4 con 600 ar5 con 2 hlt
+
+# set color to 200,200,0,255
+con 200 ar4 con 200 ar5 con 0 ar6 @2
+
+# save spot
+r8a ar6
+
+# clear screen
+con 2 ar2 con 2 ar3 con 2 hlt
+
+# present screen
+con 2 ar2 con 3 ar3 con 2 hlt
+
+# jump-back
+r6a ar8
+
+# quit
+con 0 hlt
diff --git a/pu1/pasm-syntax/package.json b/pu1/pasm-syntax/package.json
new file mode 100644
index 0000000..d287cc2
--- /dev/null
+++ b/pu1/pasm-syntax/package.json
@@ -0,0 +1,26 @@
+{
+ "name": "pasm-syntax",
+ "displayName": "PASM Syntax",
+ "description": "Syntax highlighting for PASM custom assembly",
+ "version": "0.0.1",
+ "engines": {
+ "vscode": "^1.50.0"
+ },
+ "categories": ["Programming Languages"],
+ "contributes": {
+ "languages": [
+ {
+ "id": "pasm",
+ "aliases": ["PASM", "pasm"],
+ "extensions": [".pasm"]
+ }
+ ],
+ "grammars": [
+ {
+ "language": "pasm",
+ "scopeName": "source.pasm",
+ "path": "./syntaxes/pasm.tmLanguage.json"
+ }
+ ]
+ }
+}
diff --git a/pu1/pasm-syntax/pasm-syntax-0.0.1.vsix b/pu1/pasm-syntax/pasm-syntax-0.0.1.vsix
new file mode 100644
index 0000000..e94bb8b
--- /dev/null
+++ b/pu1/pasm-syntax/pasm-syntax-0.0.1.vsix
Binary files differ
diff --git a/pu1/pasm-syntax/syntaxes/pasm.tmLanguage.json b/pu1/pasm-syntax/syntaxes/pasm.tmLanguage.json
new file mode 100644
index 0000000..e0df85e
--- /dev/null
+++ b/pu1/pasm-syntax/syntaxes/pasm.tmLanguage.json
@@ -0,0 +1,145 @@
+{
+ "name": "PASM",
+ "scopeName": "source.pasm",
+ "fileTypes": [
+ "pasm"
+ ],
+ "patterns": [
+ {
+ "include": "#comment"
+ },
+ {
+ "include": "#macro"
+ },
+ {
+ "include": "#instruction"
+ },
+ {
+ "include": "#register"
+ },
+ {
+ "include": "#number"
+ }
+ ],
+ "repository": {
+ "comment": {
+ "patterns": [
+ {
+ "name": "comment.line.number-sign.pasm",
+ "match": "#.*$"
+ }
+ ]
+ },
+ "macro": {
+ "patterns": [
+ {
+ "name": "meta.macro.pasm",
+ "begin": "(\\()",
+ "beginCaptures": {
+ "1": {
+ "name": "punctuation.definition.macro.begin.pasm"
+ }
+ },
+ "end": "(\\))",
+ "endCaptures": {
+ "1": {
+ "name": "punctuation.definition.macro.end.pasm"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#instruction"
+ },
+ {
+ "include": "#register"
+ },
+ {
+ "include": "#number"
+ },
+ {
+ "include": "#comment"
+ }
+ ]
+ },
+ {
+ "name": "keyword.other.macro.pasm",
+ "match": "@\\d+"
+ }
+ ]
+ },
+ "instruction": {
+ "patterns": [
+ {
+ "name": "meta.instruction.ar.pasm",
+ "captures": {
+ "1": {
+ "name": "keyword.instruction.prefix.ar.pasm"
+ },
+ "2": {
+ "name": "string.quoted.double.pasm"
+ }
+ },
+ "match": "\\b(ar)([1-8])\\b"
+ },
+ {
+ "name": "meta.instruction.r.pasm",
+ "captures": {
+ "1": {
+ "name": "keyword.instruction.prefix.r.pasm"
+ },
+ "2": {
+ "name": "constant.numeric.instruction-number.r.pasm"
+ },
+ "3": {
+ "name": "keyword.instruction.suffix.a.pasm"
+ }
+ },
+ "match": "\\b(r)([1-8])(a)\\b"
+ },
+ {
+ "name": "keyword.instruction.simple.pasm",
+ "match": "\\b(hlt|con|mem|aca|add|sub|not|clr)\\b"
+ }
+ ]
+ },
+ "register": {
+ "patterns": [
+ {
+ "name": "meta.register.ar.pasm",
+ "captures": {
+ "1": {
+ "name": "variable.other.register.prefix.ar.pasm"
+ },
+ "2": {
+ "name": "constant.numeric.register-number.ar.pasm"
+ }
+ },
+ "match": "\\b(ar)([1-8])\\b"
+ },
+ {
+ "name": "meta.register.r.pasm",
+ "captures": {
+ "1": {
+ "name": "variable.other.register.prefix.r.pasm"
+ },
+ "2": {
+ "name": "constant.numeric.register-number.r.pasm"
+ },
+ "3": {
+ "name": "variable.other.register.suffix.a.pasm"
+ }
+ },
+ "match": "\\b(r)([1-8])(a)\\b"
+ }
+ ]
+ },
+ "number": {
+ "patterns": [
+ {
+ "name": "constant.numeric.decimal.pasm",
+ "match": "\\b\\d+\\b"
+ }
+ ]
+ }
+ }
+} \ No newline at end of file
diff --git a/pu1/pu.c b/pu1/pu.c
new file mode 100644
index 0000000..f182e65
--- /dev/null
+++ b/pu1/pu.c
@@ -0,0 +1,419 @@
+#define _XOPEN_SOURCE 700
+#include <SDL2/SDL.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#pragma clang diagnostic ignored "-Wreturn-type"
+#pragma clang diagnostic ignored "-Wc23-extensions"
+#pragma clang diagnostic ignored "-Wnull-character"
+#pragma clang diagnostic ignored "-Wimplicit-int"
+#pragma clang diagnostic ignored "-Wparentheses"
+#pragma clang diagnostic ignored "-Wempty-body"
+
+// NOTE: If you want the CPU to crash when there is an illegal instruction, replace the useless "4"
+// in the code with "__builtin_trap()".
+// clang-format off
+#define S 1<<20
+#define H !--c?
+#define J while
+m[S],c,a;pu(){J(c=m[m[7]++])H*m=m[m[7]++]:c--<9?!a?m[c]=*m:0:8>(c-=8)?*m=m[c]:!(c-=8)?m[m[1]]=*m:H*m=a:H a+=*m:H a-=*m:H a=!a:H a=0:4;}
+// clang-format on
+
+// a is acc
+// first 8 memory bytes are registers actually
+// c is pc, read from m[7]
+
+// --- opcodes ---
+// 0 = halt DONE
+// 1 = const in r0 DONE
+// 2...9 = r0 in rX (only if acc is zero) DONE
+// 10...17 = rX in r0 DONE
+// 18 = r0 in m[r1] DONE
+// 19 = acc in r0 DONE
+// 20 = acc += r0 DONE
+// 21 = acc -= r0 DONE
+// 22 = acc = !acc DONE
+// 23 = acc = 0 DONE
+// for ops 2..9 and 10..17 it basically works like this: 2 handles r0, 3 r1, 4 r2, etc. 10 handles
+// r0, 11 r1, 12 r2 and so on.
+
+// compiler!
+// the switch skips whitespace. then, the beautiful loop thingymajigy emits bytecode.
+
+// clang-format off
+#define W case
+#define Q goto M;
+char R[]={
+#embed "t"
+};K;char*Y[1<<9];void*as(char*I,int*O){char*V;J(K=*I){V=R;switch(K){W 32:W 10:W 9:I++;Q;W 35:J(*++I!=10);Q W 40:I++;K=strtol(I,&I,10);Y[K]=I;J(*I++!=41);Q W 41:return O;W 64:O=as(Y[strtol(++I,&I,10)],O);if((long)O<0)return O;Q W 39:*O++=1;I++;*O++=*I++;Q}strncmp("con",I,3)?:({*O++=1,*O++=strtol(I+=3,&I,10);Q});J(*V)V+=3,(strncmp(V-3,I,3)?:({*O++=*V;I+=3;Q})),V+=2;return(char*)-(long)I;M:}return 0;}
+// clang-format on
+
+#undef Q
+#undef H
+#undef W
+#undef S
+#undef J
+
+#ifndef __WRAPPER
+
+SDL_Window *window = NULL;
+SDL_Renderer *renderer = NULL;
+
+// runs pu until exit code != 2,
+// that exit code is an interrupt for talking to the emulated hardware
+emu() {
+ while (1) {
+ pu();
+ int e_code = m[ 0 ];
+
+ // Poll SDL events to handle window close, etc.
+ SDL_Event event;
+ while (SDL_PollEvent(&event)) {
+ if (event.type == SDL_QUIT) {
+ if (renderer) SDL_DestroyRenderer(renderer);
+ if (window) SDL_DestroyWindow(window);
+ SDL_Quit();
+ return 0;
+ }
+ // Additional event handling can be added here if needed.
+ }
+
+ if (e_code != 2) {
+ if (renderer) SDL_DestroyRenderer(renderer);
+ if (window) SDL_DestroyWindow(window);
+ SDL_Quit();
+ return e_code;
+ }
+
+ // handle hardware interrupts
+ int int_code = m[ 1 ];
+
+ // 0: output string
+ if (int_code == 0) {
+ int ptr = m[ 2 ];
+ int size = m[ 3 ], i = 0;
+
+ for (; i < size; i++) putchar(m[ ptr++ ]);
+ while (size--) putchar(m[ ptr++ ]);
+ }
+
+ // 1: input character
+ else if (int_code == 1) {
+ m[ 2 ] = getchar();
+ }
+
+ // 2: graphics operations via SDL2
+ else if (int_code == 2) {
+ int gfx_op = m[ 2 ];
+ switch (gfx_op) {
+ case 0: // Initialize window (parameters: width in m[3], height in m[4])
+ {
+ int width = m[ 3 ];
+ int height = m[ 4 ];
+ if (SDL_Init(SDL_INIT_VIDEO) < 0) {
+ fprintf(stderr, "SDL_Init failed: %s\n", SDL_GetError());
+ m[ 0 ] = 1; // Set e_code to indicate error
+ break;
+ }
+ window = SDL_CreateWindow(
+ "PU VM Graphics",
+ SDL_WINDOWPOS_CENTERED,
+ SDL_WINDOWPOS_CENTERED,
+ width,
+ height,
+ 0);
+ if (!window) {
+ fprintf(stderr, "SDL_CreateWindow failed: %s\n", SDL_GetError());
+ m[ 0 ] = 1; // Set e_code to indicate error
+ break;
+ }
+ renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
+ if (!renderer) {
+ fprintf(stderr, "SDL_CreateRenderer failed: %s\n", SDL_GetError());
+ m[ 0 ] = 1; // Set e_code to indicate error
+ break;
+ }
+
+ break;
+ }
+ case 1: // Set draw color (parameters: r in m[3], g in m[4], b in m[5], a in m[6])
+ {
+ Uint8 r = (Uint8) m[ 3 ];
+ Uint8 g = (Uint8) m[ 4 ];
+ Uint8 b = (Uint8) m[ 5 ];
+ Uint8 a = (Uint8) m[ 6 ];
+ SDL_SetRenderDrawColor(renderer, r, g, b, a);
+ break;
+ }
+ case 2: // Clear screen
+ SDL_RenderClear(renderer);
+ break;
+ case 3: // Present (flip buffers)
+ SDL_RenderPresent(renderer);
+ break;
+ case 4: // Draw point (parameters: x in m[3], y in m[4])
+ {
+ int x = m[ 3 ];
+ int y = m[ 4 ];
+ SDL_RenderDrawPoint(renderer, x, y);
+ break;
+ }
+ case 5: // Draw line (parameters: x1 in m[3], y1 in m[4], x2 in m[5], y2 in m[6])
+ {
+ int x1 = m[ 3 ];
+ int y1 = m[ 4 ];
+ int x2 = m[ 5 ];
+ int y2 = m[ 6 ];
+ SDL_RenderDrawLine(renderer, x1, y1, x2, y2);
+ break;
+ }
+ case 6: // Fill rectangle (parameters: x in m[3], y in m[4], w in m[5], h in m[6])
+ {
+ int x = m[ 3 ];
+ int y = m[ 4 ];
+ int w = m[ 5 ];
+ int h = m[ 6 ];
+ SDL_Rect rect = { x, y, w, h };
+ SDL_RenderFillRect(renderer, &rect);
+ break;
+ }
+ case 7: // Draw rectangle outline (parameters: x in m[3], y in m[4], w in m[5], h in
+ // m[6])
+ {
+ int x = m[ 3 ];
+ int y = m[ 4 ];
+ int w = m[ 5 ];
+ int h = m[ 6 ];
+ SDL_Rect rect = { x, y, w, h };
+ SDL_RenderDrawRect(renderer, &rect);
+ break;
+ }
+ case 8: // Quit graphics
+ if (renderer) SDL_DestroyRenderer(renderer);
+ if (window) SDL_DestroyWindow(window);
+ SDL_Quit();
+ renderer = NULL;
+ window = NULL;
+ break;
+ default:
+ // Unknown graphics operation; ignored for simplicity.
+ break;
+ }
+ }
+
+ else {
+ // unknown interrupt. what to do?
+ }
+
+ m[ 0 ] = 0;
+ }
+}
+
+char R_backup[] = "hlt\0 ar1\2 ar2\3 ar3\4 ar4\5 ar5\6 ar6\7 ar7\x8 ar8\x9 r1a\xA r2a\xB r3a\xC "
+ "r4a\xD r5a\xE r6a\xF r7a\20 r8a\21 mem\22 aca\23 add\24 sub\25 not\26 clr\27";
+
+error_debug(char *fail_point, char *code, int sz) {
+ if ((long) fail_point >= 0) return;
+
+ fail_point = (char *) -(long) fail_point;
+ int error_pos = (int) (fail_point - code);
+ int i;
+
+ int line = 1, col = 1;
+ for (i = 0; i < error_pos && i < sz; i++) {
+ if (code[ i ] == '\n') {
+ line++;
+ col = 1;
+ } else {
+ col++;
+ }
+ }
+
+ fprintf(
+ stderr,
+ "\x1b[1m\x1b[31merror:\x1b[0m "
+ "\x1b[1massembly failed\x1b[0m "
+ "at \x1b[36mline %d\x1b[0m, \x1b[36mcolumn %d\x1b[0m "
+ "(\x1b[33mposition %d\x1b[0m):\n",
+ line,
+ col,
+ error_pos);
+
+ int line_start = error_pos;
+ while (line_start > 0 && code[ line_start - 1 ] != '\n') { line_start--; }
+
+ int line_end = error_pos;
+ while (line_end < sz && code[ line_end ] != '\n' && code[ line_end ] != '\0') { line_end++; }
+
+ fprintf(stderr, " \x1b[1m");
+ for (i = line_start; i < line_end; i++) { fputc(code[ i ], stderr); }
+ fprintf(stderr, "\x1b[0m\n");
+
+ fprintf(stderr, " ");
+ for (i = line_start; i < error_pos; i++) { fputc((code[ i ] == '\t') ? '\t' : ' ', stderr); }
+ fprintf(stderr, "\x1b[1m\x1b[31m^\x1b[0m \x1b[90mparsing stopped here\x1b[0m\n");
+}
+
+main(int argc, char **argv) {
+ if (access("t", F_OK)) {
+ // patch the instruction table incase you removed it.
+ printf("you've deleted `t`! i'll write it again for you.\n\n");
+ FILE *fp = fopen("t", "w+");
+ fwrite(R_backup, 1, sizeof(R_backup), fp);
+ fclose(fp);
+ memcpy(R, R_backup, sizeof(R_backup));
+ }
+
+ if (argc == 1) {
+ printf(
+ "Usage:\n"
+ " %s run <filename>: Execute a file\n"
+ " %s repl: Open in REPL mode",
+ argv[ 0 ],
+ argv[ 0 ]);
+ exit(1);
+ }
+
+ if (argc == 2 && !strcmp(argv[ 1 ], "repl")) {
+ printf("\npu repl\n"
+ "| code is inserted at address 1024\n"
+ "| submit RUN to execute the code.\n"
+ "| submit CLEAR to erase history.\n"
+ "| submit SAVE to write contents to repl.pu\n"
+ "| submit LOAD to fetch contents from repl.pu\n"
+ "\n");
+
+ char *code = malloc(1);
+ *code = '\n';
+ size_t size = 0;
+
+ while (1) {
+ printf("%li > ", size);
+ char *line;
+ size_t _len;
+ size_t len = getline(&line, &_len, stdin);
+
+ if (len == 1) continue;
+
+ if (len == 4 && strncmp(line, "run", 3) == 0) {
+ memset(m, 0, 8);
+ m[ 7 ] = 1024;
+ emu();
+ int i;
+ for (i = 0; i < 16; i++) printf("%i ", m[ 1024 + i ]);
+ printf("\n\n");
+ continue;
+ }
+
+ if (len == 5 && strncmp(line, "save", 4) == 0) {
+ FILE *fd = fopen("repl.pu", "w+");
+ fwrite(code, 1, size, fd);
+ fclose(fd);
+ printf("saved %li bytes.\n", size);
+
+ continue;
+ }
+
+ if (len == 5 && strncmp(line, "load", 4) == 0) {
+ FILE *fp = fopen("repl.pu", "r");
+ fseek(fp, 0, SEEK_END);
+ int sz = ftell(fp);
+ rewind(fp);
+ char *saved = malloc(sz + 1);
+ fread(saved, 1, sz, fp);
+ saved[ sz ] = 0;
+ fclose(fp);
+
+ free(code);
+ code = saved;
+ size = sz;
+ printf("loaded %i bytes.\n", sz);
+
+ goto SKIP_STRAIGHT;
+ }
+
+ if (len == 6 && strncmp(line, "clear", 5) == 0) {
+ printf("-- code cleared\n");
+ free(code);
+ code = malloc(1);
+ size = 0;
+ continue;
+ }
+
+ if (len == 7 && strncmp(line, "status", 6) == 0) {
+ printf(
+ "\nregisters\n"
+ "r1: %i r2: %i\n"
+ "r3: %i r4: %i\n"
+ "r5: %i r6: %i\n"
+ "r7: %i pc: %i\n\n",
+ m[ 0 ],
+ m[ 1 ],
+ m[ 2 ],
+ m[ 3 ],
+ m[ 4 ],
+ m[ 5 ],
+ m[ 6 ],
+ m[ 7 ]);
+
+ continue;
+ }
+
+ char *old_code = malloc(size);
+ memcpy(old_code, code, size);
+
+ code = realloc(code, size += len);
+ memcpy(&code[ size - len ], line, len);
+
+ SKIP_STRAIGHT:
+
+ char *exit_code = as(code, &m[ 1024 ]);
+
+ if (exit_code != 0) {
+ printf("\n");
+ error_debug(exit_code, code, size);
+ printf("\n");
+
+ free(code);
+ code = old_code;
+ size -= len;
+ continue;
+ }
+
+ free(old_code);
+ }
+
+ exit(0);
+ }
+
+ if (argc == 3 && strcmp(argv[ 1 ], "run") != 0) {
+ printf("Invalid mode - Only supported modes are 'run' and 'repl'\n");
+ return;
+ }
+
+ // read code
+ FILE *fp = fopen(argv[ 2 ], "r");
+ fseek(fp, 0, SEEK_END);
+ int sz = ftell(fp);
+ rewind(fp);
+ char code[ sz + 1 ];
+ fread(code, 1, sz, fp);
+ code[ sz ] = 0;
+ fclose(fp);
+
+ // set pc
+ m[ 7 ] = 1024;
+ // assemble
+ char *fail_point = as(code, &m[ 1024 ]);
+
+ if (fail_point != 0) {
+ error_debug(fail_point, code, sz);
+ return;
+ }
+
+ // run emulation
+ emu();
+}
+#endif \ No newline at end of file
diff --git a/pu1/pu.out.c b/pu1/pu.out.c
new file mode 100644
index 0000000..c62b50a
--- /dev/null
+++ b/pu1/pu.out.c
Binary files differ
diff --git a/pu1/putest.c b/pu1/putest.c
new file mode 100644
index 0000000..013a48b
--- /dev/null
+++ b/pu1/putest.c
@@ -0,0 +1,380 @@
+#include <stdio.h>
+#include <string.h>
+
+#define __WRAPPER
+#include "pu.out.c"
+
+/* ─── color & statistics ──────────────────────────────────────────────────── */
+#define COLOR_RED "\x1b[1;31m"
+#define COLOR_BLUE "\033[1;34m"
+#define COLOR_SUM "\033[1;33m"
+#define COLOR_RESET "\x1b[0m"
+
+static int tests_run = 0;
+static int tests_passed = 0;
+
+/* PASS if cond true, else FAIL; color and bump counters */
+#define PRINT_RESULT(cond) \
+ do { \
+ tests_run++; \
+ if (cond) { \
+ tests_passed++; \
+ printf(COLOR_BLUE "PASS" COLOR_RESET "\n"); \
+ } else { \
+ printf(COLOR_RED "FAIL" COLOR_RESET "\n"); \
+ } \
+ } while (0)
+/* ───────────────────────────────────────────────────────────────────────── */
+
+void reset() {
+ memset(m, 0, sizeof(m));
+ a = 0;
+ c = 0;
+}
+
+void test_opcode_0() {
+ printf("Testing opcode 0 (hlt)... ");
+ reset();
+ m[ 0 ] = 0;
+ m[ 7 ] = 0;
+ pu();
+ PRINT_RESULT(1);
+}
+
+void test_opcode_1() {
+ printf("Testing opcode 1 (const in r0)... ");
+ reset();
+ m[ 0 ] = 1;
+ m[ 1 ] = 42;
+ m[ 2 ] = 0;
+ m[ 7 ] = 0;
+ pu();
+ PRINT_RESULT(m[ 0 ] == 42);
+}
+
+void test_opcode_2() {
+ printf("Testing opcode 4 (r0 in r2 if acc zero)... ");
+ reset();
+ m[ 0 ] = 123;
+ a = 0;
+ m[ 10 ] = 4;
+ m[ 11 ] = 0;
+ m[ 7 ] = 10;
+ pu();
+ PRINT_RESULT(m[ 2 ] == 123);
+}
+
+void test_opcode_2_zero_acc() {
+ printf("Testing opcode 4 with zero acc (should not store)... ");
+ reset();
+ m[ 0 ] = 123;
+ m[ 2 ] = 999;
+ a = 1;
+ m[ 10 ] = 4;
+ m[ 11 ] = 0;
+ m[ 7 ] = 10;
+ pu();
+ PRINT_RESULT(m[ 2 ] == 999);
+}
+
+void test_opcode_10() {
+ printf("Testing opcode 12 (r2 in r0)... ");
+ reset();
+ m[ 2 ] = 456;
+ m[ 10 ] = 12;
+ m[ 11 ] = 0;
+ m[ 7 ] = 10;
+ pu();
+ PRINT_RESULT(m[ 0 ] == 456);
+}
+
+void test_opcode_18() {
+ printf("Testing opcode 18 (r0 in m[r1])... ");
+ reset();
+ m[ 0 ] = 789;
+ m[ 1 ] = 100;
+ m[ 10 ] = 18;
+ m[ 11 ] = 0;
+ m[ 7 ] = 10;
+ pu();
+ PRINT_RESULT(m[ 100 ] == 789);
+}
+
+void test_opcode_19() {
+ printf("Testing opcode 19 (acc in r0)... ");
+ reset();
+ a = 555;
+ m[ 10 ] = 19;
+ m[ 11 ] = 0;
+ m[ 7 ] = 10;
+ pu();
+ PRINT_RESULT(m[ 0 ] == 555);
+}
+
+void test_opcode_20() {
+ printf("Testing opcode 20 (acc += r0)... ");
+ reset();
+ a = 10;
+ m[ 0 ] = 5;
+ m[ 10 ] = 20;
+ m[ 11 ] = 0;
+ m[ 7 ] = 10;
+ pu();
+ PRINT_RESULT(a == 15);
+}
+
+void test_opcode_21() {
+ printf("Testing opcode 21 (acc -= r0)... ");
+ reset();
+ a = 20;
+ m[ 0 ] = 7;
+ m[ 10 ] = 21;
+ m[ 11 ] = 0;
+ m[ 7 ] = 10;
+ pu();
+ PRINT_RESULT(a == 13);
+}
+
+void test_opcode_22() {
+ printf("Testing opcode 22 (acc = !acc)... ");
+ reset();
+ a = 5;
+ m[ 10 ] = 22;
+ m[ 11 ] = 0;
+ m[ 7 ] = 10;
+ pu();
+ PRINT_RESULT(a == 0);
+}
+
+void test_opcode_23() {
+ printf("Testing opcode 23 (acc = 0)... ");
+ reset();
+ a = 999;
+ m[ 10 ] = 23;
+ m[ 11 ] = 0;
+ m[ 7 ] = 10;
+ pu();
+ PRINT_RESULT(a == 0);
+}
+
+/* ─── assembler support ───────────────────────────────────────────────────── */
+
+i;
+int check_output(int* O, int* expected, int len, const char* test_name) {
+ for (i = 0; i < len; i++) {
+ if (O[ i ] != expected[ i ]) {
+ printf(
+ "%s: output[%d]=%d (expected %d): " COLOR_RED "FAIL" COLOR_RESET "\n",
+ test_name,
+ i,
+ O[ i ],
+ expected[ i ]);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+void reset_output(int* O, int size) { memset(O, 0, size * sizeof(int)); }
+
+void test_hlt() {
+ printf("Testing hlt (opcode 0)... ");
+ int O[ 10 ], ok;
+ reset_output(O, 10);
+ int exp[] = { 0 };
+ char* ret = as("hlt", O);
+ ok = (ret == 0 && check_output(O, exp, 1, "hlt"));
+ PRINT_RESULT(ok);
+}
+
+void test_con() {
+ printf("Testing con 123 (opcode 1, 123)... ");
+ int O[ 10 ], ok;
+ reset_output(O, 10);
+ int exp[] = { 1, 123 };
+ char* ret = as("con 123", O);
+ ok = (ret == 0 && check_output(O, exp, 2, "con"));
+ PRINT_RESULT(ok);
+}
+
+void test_ar1() {
+ printf("Testing ar1 (opcode 2)... ");
+ int O[ 10 ], ok;
+ reset_output(O, 10);
+ int exp[] = { 2 };
+ char* ret = as("ar1", O);
+ ok = (ret == 0 && check_output(O, exp, 1, "ar1"));
+ PRINT_RESULT(ok);
+}
+
+void test_ar8() {
+ printf("Testing ar8 (opcode 9)... ");
+ int O[ 10 ], ok;
+ reset_output(O, 10);
+ int exp[] = { 9 };
+ char* ret = as("ar8", O);
+ ok = (ret == 0 && check_output(O, exp, 1, "ar8"));
+ PRINT_RESULT(ok);
+}
+
+void test_r1a() {
+ printf("Testing r1a (opcode 10)... ");
+ int O[ 10 ], ok;
+ reset_output(O, 10);
+ int exp[] = { 10 };
+ char* ret = as("r1a", O);
+ ok = (ret == 0 && check_output(O, exp, 1, "r1a"));
+ PRINT_RESULT(ok);
+}
+
+void test_r8a() {
+ printf("Testing r8a (opcode 17)... ");
+ int O[ 10 ], ok;
+ reset_output(O, 10);
+ int exp[] = { 17 };
+ char* ret = as("r8a", O);
+ ok = (ret == 0 && check_output(O, exp, 1, "r8a"));
+ PRINT_RESULT(ok);
+}
+
+void test_mem() {
+ printf("Testing mem (opcode 18)... ");
+ int O[ 10 ], ok;
+ reset_output(O, 10);
+ int exp[] = { 18 };
+ char* ret = as("mem", O);
+ ok = (ret == 0 && check_output(O, exp, 1, "mem"));
+ PRINT_RESULT(ok);
+}
+
+void test_aca() {
+ printf("Testing aca (opcode 19)... ");
+ int O[ 10 ], ok;
+ reset_output(O, 10);
+ int exp[] = { 19 };
+ char* ret = as("aca", O);
+ ok = (ret == 0 && check_output(O, exp, 1, "aca"));
+ PRINT_RESULT(ok);
+}
+
+void test_add() {
+ printf("Testing add (opcode 20)... ");
+ int O[ 10 ], ok;
+ reset_output(O, 10);
+ int exp[] = { 20 };
+ char* ret = as("add", O);
+ ok = (ret == 0 && check_output(O, exp, 1, "add"));
+ PRINT_RESULT(ok);
+}
+
+void test_sub() {
+ printf("Testing sub (opcode 21)... ");
+ int O[ 10 ], ok;
+ reset_output(O, 10);
+ int exp[] = { 21 };
+ char* ret = as("sub", O);
+ ok = (ret == 0 && check_output(O, exp, 1, "sub"));
+ PRINT_RESULT(ok);
+}
+
+void test_not() {
+ printf("Testing not (opcode 22)... ");
+ int O[ 10 ], ok;
+ reset_output(O, 10);
+ int exp[] = { 22 };
+ char* ret = as("not", O);
+ ok = (ret == 0 && check_output(O, exp, 1, "not"));
+ PRINT_RESULT(ok);
+}
+
+void test_clr() {
+ printf("Testing clr (opcode 23)... ");
+ int O[ 10 ], ok;
+ reset_output(O, 10);
+ int exp[] = { 23 };
+ char* ret = as("clr", O);
+ ok = (ret == 0 && check_output(O, exp, 1, "clr"));
+ PRINT_RESULT(ok);
+}
+
+void test_multi_instruction() {
+ printf("Testing multi-instruction (hlt con 123 ar1 clr)... ");
+ int O[ 10 ], ok;
+ reset_output(O, 10);
+ int exp[] = { 0, 1, 123, 2, 23 };
+ char* ret = as("hlt con 123 ar1 clr", O);
+ ok = (ret == 0 && check_output(O, exp, 5, "multi-instruction"));
+ PRINT_RESULT(ok);
+}
+
+void test_whitespace() {
+ printf("Testing whitespace handling (hlt \\t\\n con 456 \\n clr)... ");
+ int O[ 10 ], ok;
+ reset_output(O, 10);
+ int exp[] = { 0, 1, 456, 23 };
+ char* ret = as("hlt \t\n con 456 \n clr", O);
+ ok = (ret == 0 && check_output(O, exp, 4, "whitespace"));
+ PRINT_RESULT(ok);
+}
+
+void test_comment() {
+ printf("Testing comment handling (should ignore line)... ");
+ int O[ 10 ], ok;
+ reset_output(O, 10);
+ int exp[] = { 1, 90 };
+ char* ret = as("# hello\ncon 90", O);
+ ok = (ret == 0 && check_output(O, exp, 2, "comment"));
+ PRINT_RESULT(ok);
+}
+
+void test_error() {
+ printf("Testing error case (invalid instruction)... ");
+ int O[ 10 ], ok;
+ reset_output(O, 10);
+ char* ret = as("xyz", O);
+ ok = (ret != 0);
+ PRINT_RESULT(ok);
+}
+
+int main() {
+ printf(COLOR_SUM "Running CPU unit tests...\n\n" COLOR_RESET);
+
+ test_opcode_0();
+ test_opcode_1();
+ test_opcode_2();
+ test_opcode_2_zero_acc();
+ test_opcode_10();
+ test_opcode_18();
+ test_opcode_19();
+ test_opcode_20();
+ test_opcode_21();
+ test_opcode_22();
+ test_opcode_23();
+
+ printf(COLOR_SUM "\nRunning assembler unit tests...\n\n" COLOR_RESET);
+
+ test_hlt();
+ test_con();
+ test_ar1();
+ test_ar8();
+ test_r1a();
+ test_r8a();
+ test_mem();
+ test_aca();
+ test_add();
+ test_sub();
+ test_not();
+ test_clr();
+ test_multi_instruction();
+ test_whitespace();
+ test_comment();
+ test_error();
+
+ printf(
+ "\nTotal tests run: %d, passed: %d, failed: %d\n",
+ tests_run,
+ tests_passed,
+ tests_run - tests_passed);
+
+ return 0;
+}
diff --git a/pu1/repl.pu b/pu1/repl.pu
new file mode 100644
index 0000000..2cb3020
--- /dev/null
+++ b/pu1/repl.pu
@@ -0,0 +1 @@
+con 5
diff --git a/pu1/t b/pu1/t
new file mode 100644
index 0000000..7b992ff
--- /dev/null
+++ b/pu1/t
Binary files differ