From 2fc20ad0b11e60ecfacc653024f2b582acad6e86 Mon Sep 17 00:00:00 2001 From: Moon Flower Date: Mon, 11 Aug 2025 11:34:18 +0330 Subject: ir2 initial release! --- README.md | 46 ++- code.pasm | 23 -- pasm-syntax/package.json | 26 -- pasm-syntax/pasm-syntax-0.0.1.vsix | Bin 2163 -> 0 bytes pasm-syntax/syntaxes/pasm.tmLanguage.json | 145 ------- penus | Bin 16424 -> 0 bytes penus.c | 30 -- pu.c | 419 -------------------- pu.out.c | Bin 1405 -> 0 bytes pu1/code.pasm | 23 ++ pu1/pasm-syntax/package.json | 26 ++ pu1/pasm-syntax/pasm-syntax-0.0.1.vsix | Bin 0 -> 2163 bytes pu1/pasm-syntax/syntaxes/pasm.tmLanguage.json | 145 +++++++ pu1/pu.c | 419 ++++++++++++++++++++ pu1/pu.out.c | Bin 0 -> 1405 bytes pu1/putest.c | 380 ++++++++++++++++++ pu1/repl.pu | 1 + pu1/t | Bin 0 -> 115 bytes pu2/code.i2 | 15 + pu2/i2-syntax/language-configuration.json | 30 ++ pu2/i2-syntax/package.json | 35 ++ pu2/i2-syntax/syntaxes/i2.tmLanguage.json | 106 +++++ pu2/p2m-syntax/README.md | 9 + pu2/p2m-syntax/language-configuration.json | 30 ++ pu2/p2m-syntax/p2m-asm-syntax-0.0.1.vsix | Bin 0 -> 3130 bytes pu2/p2m-syntax/package.json | 30 ++ pu2/p2m-syntax/syntaxes/p2m.tmLanguage | 105 +++++ pu2/pu2.c | 547 ++++++++++++++++++++++++++ pu2/pu2test.c | 227 +++++++++++ putest.c | 380 ------------------ repl.pu | 1 - t | Bin 115 -> 0 bytes 32 files changed, 2157 insertions(+), 1041 deletions(-) delete mode 100644 code.pasm delete mode 100644 pasm-syntax/package.json delete mode 100644 pasm-syntax/pasm-syntax-0.0.1.vsix delete mode 100644 pasm-syntax/syntaxes/pasm.tmLanguage.json delete mode 100755 penus delete mode 100644 penus.c delete mode 100644 pu.c delete mode 100644 pu.out.c create mode 100644 pu1/code.pasm create mode 100644 pu1/pasm-syntax/package.json create mode 100644 pu1/pasm-syntax/pasm-syntax-0.0.1.vsix create mode 100644 pu1/pasm-syntax/syntaxes/pasm.tmLanguage.json create mode 100644 pu1/pu.c create mode 100644 pu1/pu.out.c create mode 100644 pu1/putest.c create mode 100644 pu1/repl.pu create mode 100644 pu1/t create mode 100644 pu2/code.i2 create mode 100644 pu2/i2-syntax/language-configuration.json create mode 100644 pu2/i2-syntax/package.json create mode 100644 pu2/i2-syntax/syntaxes/i2.tmLanguage.json create mode 100644 pu2/p2m-syntax/README.md create mode 100644 pu2/p2m-syntax/language-configuration.json create mode 100644 pu2/p2m-syntax/p2m-asm-syntax-0.0.1.vsix create mode 100644 pu2/p2m-syntax/package.json create mode 100644 pu2/p2m-syntax/syntaxes/p2m.tmLanguage create mode 100644 pu2/pu2.c create mode 100644 pu2/pu2test.c delete mode 100644 putest.c delete mode 100644 repl.pu delete mode 100644 t diff --git a/README.md b/README.md index dcc0fbf..e2bfa42 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,35 @@ -# moonvm -i have no idea what to name this. gimme names if you got any. +# pu2! +smaller, better, faster, compact-er! let's gooooo -## parts? how's it work? -i don't remember. i have ptsd from writing it. +## how does this workkk +to be humble, pretty clever stuff. (i forgor) -## what is it -i asked it and it said it's a vm+assembler. the unit tests are written by ai cus i was really not feeling like writing unit tests of all things. the skeleton code is all mine tho :P +## WHAT IS PU2 +- CPU Emulator +- Assembler +- Test Suite -## wowie -ikr,,, +## instruction set +pu2 has less instructions than pu (aka pu1), -## instruction set? -check the comments in pu.c +# ~~moonvm~~ +~~i have no idea what to name this. gimme names if you got any.~~ -i still haven't added docs for the assembler, but ehh it's pretty self explanatory. ask ai to explain it to you if you can't be assed to read. +## ~~parts? how's it work?~~ +~~i don't remember. i have ptsd from writing it.~~ -## t? -that file is the instruction encoding table for the assembler. you can add shit to it if you wanna. this is the original: -```c -char *R="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"; -``` -removing it means you can't assemble stuff anymore. though the pu.c wrapper does write it for you if it doesn't exist. \ No newline at end of file +## ~~what is it~~ +~~i asked it and it said it's a vm+assembler. the unit tests are written by ai cus i was really not feeling like writing unit tests of all things. the skeleton code is all mine tho :P~~ + +## ~~wowie~~ +~~ikr,,,~~ + +## ~~instruction set?~~ +~~check the comments in pu.c~~ + +~~i still haven't added docs for the assembler, but ehh it's pretty self explanatory. ask ai to explain it to you if you can't be assed to read.~~ + +## ~~t?~~ +~~that file is the instruction encoding table for the assembler. you can add shit to it if you wanna. this is the original:~~ +~~```char *R="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";```~~ +~~removing it means you can't assemble stuff anymore. though the pu.c wrapper does write it for you if it doesn't exist.~~ \ No newline at end of file diff --git a/code.pasm b/code.pasm deleted file mode 100644 index 94f37c5..0000000 --- a/code.pasm +++ /dev/null @@ -1,23 +0,0 @@ -(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/pasm-syntax/package.json b/pasm-syntax/package.json deleted file mode 100644 index d287cc2..0000000 --- a/pasm-syntax/package.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "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/pasm-syntax/pasm-syntax-0.0.1.vsix b/pasm-syntax/pasm-syntax-0.0.1.vsix deleted file mode 100644 index e94bb8b..0000000 Binary files a/pasm-syntax/pasm-syntax-0.0.1.vsix and /dev/null differ diff --git a/pasm-syntax/syntaxes/pasm.tmLanguage.json b/pasm-syntax/syntaxes/pasm.tmLanguage.json deleted file mode 100644 index e0df85e..0000000 --- a/pasm-syntax/syntaxes/pasm.tmLanguage.json +++ /dev/null @@ -1,145 +0,0 @@ -{ - "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/penus b/penus deleted file mode 100755 index a986fe2..0000000 Binary files a/penus and /dev/null differ diff --git a/penus.c b/penus.c deleted file mode 100644 index ae9f866..0000000 --- a/penus.c +++ /dev/null @@ -1,30 +0,0 @@ -#include -#include - -int main() { - if (SDL_Init(SDL_INIT_VIDEO) < 0) { - printf("SDL_Init failed: %s\n", SDL_GetError()); - return 1; - } - SDL_Window *window = SDL_CreateWindow("Test", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 256, 256, 0); - if (!window) { - printf("SDL_CreateWindow failed: %s\n", SDL_GetError()); - SDL_Quit(); - return 1; - } - SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); - if (!renderer) { - printf("SDL_CreateRenderer failed: %s\n", SDL_GetError()); - SDL_DestroyWindow(window); - SDL_Quit(); - return 1; - } - SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255); - SDL_RenderClear(renderer); - SDL_RenderPresent(renderer); - SDL_Delay(3000); // Wait 3 seconds - SDL_DestroyRenderer(renderer); - SDL_DestroyWindow(window); - SDL_Quit(); - return 0; -} \ No newline at end of file diff --git a/pu.c b/pu.c deleted file mode 100644 index 3de2576..0000000 --- a/pu.c +++ /dev/null @@ -1,419 +0,0 @@ -#define _XOPEN_SOURCE 700 -#include -#include -#include -#include -#include - -#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 -int 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"; - -void 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"); -} - -int 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 : 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/pu.out.c b/pu.out.c deleted file mode 100644 index c62b50a..0000000 Binary files a/pu.out.c and /dev/null differ 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 Binary files /dev/null and b/pu1/pasm-syntax/pasm-syntax-0.0.1.vsix 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 +#include +#include +#include +#include + +#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 : 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 Binary files /dev/null and b/pu1/pu.out.c 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 +#include + +#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 Binary files /dev/null and b/pu1/t differ diff --git a/pu2/code.i2 b/pu2/code.i2 new file mode 100644 index 0000000..772fd85 --- /dev/null +++ b/pu2/code.i2 @@ -0,0 +1,15 @@ +macro putchar() { + @1 = 0; // write + @2 = 10; // ptr + @3 = 1; // size + @10 = /1/; // value + system(); // interrupt (@0 = 2; halt;) +} + +putchar(['h']); +putchar(['e']); +putchar(['l']); +putchar(['l']); +putchar(['o']); + +exit(0); \ No newline at end of file diff --git a/pu2/i2-syntax/language-configuration.json b/pu2/i2-syntax/language-configuration.json new file mode 100644 index 0000000..2cbf13c --- /dev/null +++ b/pu2/i2-syntax/language-configuration.json @@ -0,0 +1,30 @@ +{ + "comments": { + "lineComment": "//" + }, + "brackets": [ + ["{", "}"], + ["[", "]"], + ["(", ")"] + ], + "autoClosingPairs": [ + { "open": "{", "close": "}" }, + { "open": "[", "close": "]" }, + { "open": "(", "close": ")" }, + { "open": "\"", "close": "\"", "notIn": ["string"] }, + { "open": "'", "close": "'", "notIn": ["string"] } + ], + "surroundingPairs": [ + { "open": "{", "close": "}" }, + { "open": "[", "close": "]" }, + { "open": "(", "close": ")" }, + { "open": "\"", "close": "\"" }, + { "open": "'", "close": "'" } + ], + "folding": { + "markers": { + "start": "^\\s*\\{", + "end": "^\\s*\\}" + } + } +} diff --git a/pu2/i2-syntax/package.json b/pu2/i2-syntax/package.json new file mode 100644 index 0000000..1810074 --- /dev/null +++ b/pu2/i2-syntax/package.json @@ -0,0 +1,35 @@ +{ + "name": "i2-asm-syntax", + "displayName": "I2 Assembly (i2)", + "version": "0.0.1", + "publisher": "moonflower", + "engines": { + "vscode": "^1.50.0" + }, + "categories": [ + "Programming Languages" + ], + "contributes": { + "languages": [ + { + "id": "i2", + "aliases": ["i2", "I2", "i2-asm"], + "extensions": [".i2"], + "configuration": "./language-configuration.json" + } + ], + "grammars": [ + { + "language": "i2", + "scopeName": "source.i2", + "path": "./syntaxes/i2.tmLanguage.json" + } + ], + "tokenColors": [ + { + "scope": "constant.other.macro-reference.i2", + "settings": { "foreground": "#FF8C00" } + } + ] + } +} diff --git a/pu2/i2-syntax/syntaxes/i2.tmLanguage.json b/pu2/i2-syntax/syntaxes/i2.tmLanguage.json new file mode 100644 index 0000000..d740a7d --- /dev/null +++ b/pu2/i2-syntax/syntaxes/i2.tmLanguage.json @@ -0,0 +1,106 @@ +{ + "scopeName": "source.i2", + "name": "I2 IR / Macro language", + "fileTypes": ["i2"], + "patterns": [ + { "include": "#comments" }, + { "include": "#macro-reference" }, + { "include": "#braces" }, + { "include": "#brackets" }, + { "include": "#semicolon" }, + { "include": "#keywords" }, + { "include": "#function-call" }, + { "include": "#memory-index" }, + { "include": "#variables" }, + { "include": "#hex" }, + { "include": "#decimal" }, + { "include": "#char" }, + { "include": "#blob" }, + { + "begin": "\\b(macro)\\b\\s+([A-Za-z_][A-Za-z0-9_]*)\\s*(?=\\()", + "beginCaptures": { + "1": { "name": "keyword.control.macro.i2" }, + "2": { "name": "entity.name.function.macro.i2" } + }, + "end": "\\}", + "name": "meta.macro.definition.i2", + "patterns": [ + { "include": "#comments" }, + { "include": "#macro-reference" }, + { "include": "#braces" }, + { "include": "#semicolon" }, + { "include": "#keywords" }, + { "include": "#function-call" }, + { "include": "#memory-index" }, + { "include": "#variables" }, + { "include": "#hex" }, + { "include": "#decimal" }, + { "include": "#char" }, + { "include": "#blob" } + ] + } + ], + "repository": { + "comments": { + "name": "comment.line.double-slash.i2", + "match": "//.*$" + }, + "braces": { + "name": "punctuation.section.block.i2", + "match": "[\\{\\}]" + }, + "brackets": { + "name": "punctuation.brackets.square.i2", + "match": "[\\[\\]]" + }, + "semicolon": { + "name": "punctuation.separator.semicolon.i2", + "match": ";" + }, + "keywords": { + "name": "keyword.control.i2", + "match": "\\b(?:macro|system|exit)\\b" + }, + "function-call": { + "name": "entity.name.function.i2", + "match": "\\b[a-zA-Z_][a-zA-Z0-9_]*\\s*(?=\\()" + }, + "variables": { + "name": "variable.other.i2", + "match": "\\b[a-zA-Z_][a-zA-Z0-9_]*\\b" + }, + "memory-index": { + "name": "variable.parameter.memory-index.i2", + "match": "@\\s*\\d+" + }, + "macro-reference": { + "name": "constant.other.macro-reference.i2", + "match": "/[^/\\r\\n]+/" + }, + "hex": { + "name": "constant.numeric.hex.i2", + "match": "\\b0x[0-9A-Fa-f]+\\b" + }, + "decimal": { + "name": "constant.numeric.decimal.i2", + "match": "\\b\\d+\\b" + }, + "char": { + "name": "string.quoted.single.char.i2", + "match": "'(\\\\.|[^'\\\\])'" + }, + "blob": { + "begin": "\\[", + "end": "\\]", + "name": "meta.bracket.blob.i2", + "patterns": [ + { "include": "#char" }, + { "include": "#decimal" }, + { + "name": "punctuation.separator.comma.i2", + "match": "," + } + ] + } + } +} \ No newline at end of file diff --git a/pu2/p2m-syntax/README.md b/pu2/p2m-syntax/README.md new file mode 100644 index 0000000..4dab972 --- /dev/null +++ b/pu2/p2m-syntax/README.md @@ -0,0 +1,9 @@ +# P2M Assembly - VSCode Syntax Extension + +This extension provides basic TextMate-based highlighting for PU2 / `.p2m` assembly files. + +## Install for development + +1. Run this script to create the files. +2. Open the `p2m-syntax` folder in VS Code and press `F5` to run an Extension Development Host with the grammar. +3. Optionally pack with `vsce package` to create a .vsix. diff --git a/pu2/p2m-syntax/language-configuration.json b/pu2/p2m-syntax/language-configuration.json new file mode 100644 index 0000000..92ebca0 --- /dev/null +++ b/pu2/p2m-syntax/language-configuration.json @@ -0,0 +1,30 @@ +{ + "comments": { + "lineComment": "#" + }, + "brackets": [ + ["{", "}"], + ["[", "]"], + ["(", ")"] + ], + "autoClosingPairs": [ + { "open": "{", "close": "}" }, + { "open": "[", "close": "]" }, + { "open": "(", "close": ")" }, + { "open": "'", "close": "'", "notIn": ["string"] }, + { "open": "\"", "close": "\"", "notIn": ["string"] } + ], + "surroundingPairs": [ + { "open": "{", "close": "}" }, + { "open": "[", "close": "]" }, + { "open": "(", "close": ")" }, + { "open": "'", "close": "'" }, + { "open": "\"", "close": "\"" } + ], + "folding": { + "markers": { + "start": "^\\s*\\{", + "end": "^\\s*\\}" + } + } +} diff --git a/pu2/p2m-syntax/p2m-asm-syntax-0.0.1.vsix b/pu2/p2m-syntax/p2m-asm-syntax-0.0.1.vsix new file mode 100644 index 0000000..65e5608 Binary files /dev/null and b/pu2/p2m-syntax/p2m-asm-syntax-0.0.1.vsix differ diff --git a/pu2/p2m-syntax/package.json b/pu2/p2m-syntax/package.json new file mode 100644 index 0000000..71b10a7 --- /dev/null +++ b/pu2/p2m-syntax/package.json @@ -0,0 +1,30 @@ +{ + "name": "p2m-asm-syntax", + "displayName": "P2M Assembly (p2m)", + "description": "TextMate grammar for the PU2 / .p2m assembly + small language config for VS Code.", + "version": "0.0.1", + "publisher": "moonflower", + "engines": { + "vscode": "^1.50.0" + }, + "categories": [ + "Programming Languages" + ], + "contributes": { + "languages": [ + { + "id": "p2m", + "aliases": ["p2m", "P2M", "p2m-asm"], + "extensions": [".p2m"], + "configuration": "./language-configuration.json" + } + ], + "grammars": [ + { + "language": "p2m", + "scopeName": "source.p2m", + "path": "./syntaxes/p2m.tmLanguage" + } + ] + } +} diff --git a/pu2/p2m-syntax/syntaxes/p2m.tmLanguage b/pu2/p2m-syntax/syntaxes/p2m.tmLanguage new file mode 100644 index 0000000..3aa35e0 --- /dev/null +++ b/pu2/p2m-syntax/syntaxes/p2m.tmLanguage @@ -0,0 +1,105 @@ + + + + + name + P2M Assembly + + scopeName + source.p2m + + fileTypes + + p2m + + + patterns + + + + + name + comment.line.number-sign.p2m + match + #.*$ + + + + + name + punctuation.section.block.p2m + match + [\{\}] + + + + + name + keyword.control.mnemonic.p2m + match + \b(?:hlt|imm|lod|str|swp|add|sub|and|nnd)\b + + + + + name + variable.other.register.p2m + match + \b(?:a|b|c|v)\b + + + + + name + variable.other.memory.p2m + match + \bm\[\s*\d+\s*\] + + + + + name + variable.parameter.memory-index.p2m + match + @\d+ + + + + + name + constant.numeric.hex.p2m + match + \b0x[0-9A-Fa-f]+\b + + + name + constant.numeric.decimal.p2m + match + \b\d+\b + + + + + name + string.quoted.single.p2m + match + '(.) + + + + + name + comment.block.annotation.p2m + match + \([^\)]*\) + + + + + repository + + scope + source.p2m + + diff --git a/pu2/pu2.c b/pu2/pu2.c new file mode 100644 index 0000000..66794bc --- /dev/null +++ b/pu2/pu2.c @@ -0,0 +1,547 @@ +#include +#include +#include +#include +#pragma clang diagnostic ignored "-Wparentheses" +#pragma clang diagnostic ignored "-Wreturn-type" +#pragma clang diagnostic ignored "-Wempty-body" + +// pu2! +// maybe compacter? idk +// last cpu: 167 chars + +// NOTE: if you want the cpu to +// crash or do something when an invalid opcode is met, +// replace the `8` in the code with whatever behavior you desire. +// you can use ({...}) to insert normal code, or use __builtin_trap() for a SIGTRAP + +// clang-format off +#define S 1<<20 +#define H !--c +#define J while +m[S],c,v,a,b,u;pu2(){J(c=m[m[7]++],v=m[m[7]++],c){H?a=v:H?a=m[b]:H?u?m[a]=b:0:H?v=a,a=b,b=v:H?a+=b:H?a-=b:H?a=a&&b:H?a=!a:H?u=1:8;u=v;}} +// clang-format on + +// there's just memory. no, really +// ok actually there's two registers called a and b +// 0 HLT: halt +// 1 IMM: a = imm +// 2 LOD: a = m[b] +// 3 STR: m[a] = b (only happens if acc != 0) +// 4 SWP: swap a and b +// 5 ADD: a += b +// 6 SUB: a -= b +// 7 AND: a &&= b +// 8 NOT: a = !a +// 9 ACC: u = imm + +// NOTE: Memory indexes less than 1024 should be considered as temporary/compute memory, and +// programs should be written at index 1024 and upwards. +// +// NOTE: Memory index 7 is the program +// counter, in which the current instruction is read from m[m[7]] at each cycle + +// ------------------------- + +// yeehaw baby it's time for as2 +// E is the error code loc. zero it before calling as2 +// Y is macro storage. extend as needed +// set O for output before running. keep in mind, it gets modified whilst as2 is running. +// therefore, i recommend you store a copy somewhere! + +// clang-format off +char*C="hltimmlodstrswpaddsubandnotacc",*Y[1<<10],*E,*L,i;*O;as2(char*I){J(c=*I++){if(c==35){J(*++I-10);c=*I++;}L=C;(!isspace(c)?c-39?c-125?c-123?c-64?({i=0,I--;J(i++,*L)strncmp(L,I,3)?L+=3:({I+=3;*O++=i-1;*O++=i-2?0:strtol(I,&I,10);goto Q;});E=I;return 1;Q:;}):as2(Y[strtol(I,&I,10)]):({Y[strtol(I,&I,10)]=I+1;J(*I++-125);}):({break;}):({*O++=1;*O++=*I++;}):0);}} +// clang-format on + +#undef S +#undef H +#undef J + +#ifndef __WRAPPER + +typedef char *string; + +string read_file(string path, size_t *out_size) { + FILE *f = fopen(path, "rb"); + string buf; + size_t size; + if (!f) return NULL; + fseek(f, 0, SEEK_END); + size = ftell(f); + fseek(f, 0, SEEK_SET); + buf = malloc(size + 1); + if (!buf) { + fclose(f); + return NULL; + } + if (fread(buf, 1, size, f) != size) { + free(buf); + fclose(f); + return NULL; + } + buf[ size ] = '\0'; + fclose(f); + if (out_size) *out_size = size; + return buf; +} + +error_debug(string fail_point, string code, int sz) { + 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"); +} + +emu() { + while (1) { + pu2(); + int e_code = m[ 0 ]; + + if (e_code != 2) 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(); + } + + else { + // unknown interrupt. what to do? + } + + m[ 0 ] = 0; + } +} + +skip_space(string *I) { + while ((**I == '\n' || isspace(**I)) && (**I != 0)) (*I)++; +} + +// IR parser for pu2's as2 +// clang-format off +struct { + int hlt; int imm; int lod; int str; int swp; int add; int sub; int and; int not; int acc; +} opcode = { + 0, 1, 2, 3, 4, 5, 6, 7, 8,9 +}; + +#define emit(OP, VAL) ({ *(*O)++ = (OP); *(*O)++ = (VAL); }) +#define store() ({ emit(opcode.acc, 1); emit(opcode.str, 0); }) +#define run_err(result) \ + ({ \ + printf("\n"); \ + error_debug((result), ERR_code, strlen(ERR_code)); \ + printf("\nir2 error message: %s\n", ERR); \ + printf("\n"); \ + exit(1); \ + }) + +#define checkskip(char, message) \ + ({ \ + if (!*I) { \ + ERR = (message); run_err(I); \ + } \ + if (*I++ != (char) ) { \ + ERR = (message); ERR_code=init_I; run_err(I-1); \ + } \ + }) +#define scanword() ({ slice VAR = { 0, I}; while (isalnum(*I)) VAR.len++, I++; VAR; }) +#define memslice slice __attribute__((cleanup(clean_slice_ref))) +#define readd() ({ if(used == new.len) new.ref = realloc(new.ref, new.len *= 2); }) +// clang-format on + +typedef unsigned char byte; +typedef byte bool; +const bool true = 1; +const bool false = 0; + +// use this instead of strdup'ing and allocating extra storage! +typedef struct { + size_t len; + string ref; +} slice; + +typedef struct { + slice key; + slice value; +} entry; + +clean_slice_ref(slice *val) { free(val->ref); } + +T; +string ERR; +string ERR_code; +_ir2(string I, int **O, entry macros[ 1 << 9 ]) { + string init_I = I; + ERR = "all good"; + + while (T = *I) { + skip_space(&I); + + if (T == '/' && I[ 1 ] == '/') { // comment + I += 2; + while (*I != '\n') I++; + } + + // handle assignments! + if (T == '@') { + I++; + int mem_index = strtol(I, &I, 10); + skip_space(&I); + checkskip('=', "expected assigment after memory blob"); + skip_space(&I); + + if (*I == '@') { + I++; + if (!isdigit(*I)) { + ERR = "expected a digit after @"; + run_err(I); + } + + int mem2_index = strtol(I, &I, 10); + + emit(opcode.imm, mem2_index); + emit(opcode.swp, 0); + emit(opcode.lod, 0); + emit(opcode.swp, 0); + emit(opcode.imm, mem_index); + store(); + } else if (isdigit(*I) || *I == '\'') { + int imm; + if (*I == '\'') { + I++; + imm = *I++; + checkskip('\'', "couldn't find closing quote on character"); + } else { + imm = strtol(I, &I, 10); + } + + emit(opcode.imm, imm); + emit(opcode.swp, 0); + emit(opcode.imm, mem_index); + store(); + } + + skip_space(&I); + checkskip(';', "expected semicolon"); + continue; + } + + // register macros + if (strncmp("macro", I, 5) == 0 && isspace(I[ 5 ])) { + I += 6; + skip_space(&I); + // parse macro stuff + + if (!isalpha(*I)) { + ERR = "expected letter at macro name"; + run_err(I - 1); + } + slice name = scanword(); + + skip_space(&I); + + checkskip('(', "expected open paren in macro defintion"); + + skip_space(&I); + + checkskip( + ')', + "expected close paren in macro definition (hint: macros do not take named " + "arguments)"); + + skip_space(&I); + + checkskip('{', "expected open curly bracket in macro body"); + + // find open space in macros + int x; + for (x = 0; x < (1 << 9); x++) { + entry g = macros[ x ]; + if (g.key.ref == NULL) { goto DONE; } + } + + ERR = "macro count exceeded 1<<9"; + run_err(I - 1); + + DONE:; + + skip_space(&I); // might aswell skip whitespace for funsies. + entry *g = ¯os[ x ]; + g->key = name; + g->value.ref = I; + g->value.len = 0; + + int dep = 0; + while (dep != 0 || *I != '}') { + if (*I == '{') dep++; + if (*I == '}') dep--; + g->value.len++; + I++; + + if (*I == 0) { + checkskip('}', "expected close curly bracket in macro body (met EOF instead)"); + } + } + + I++; + continue; + } + + // emit exit calls + if (strncmp("exit", I, 4) == 0) { + I += 4; + checkskip('(', "expected open paren on exit blob"); + + string old = I; + int value = strtol(I, &I, 10); + if (old == I && *I != ')') { + ERR = "invalid input to exit blob, expected number"; + run_err(I - 1); + } + + skip_space(&I); + checkskip(')', "expected open paren on exit blob"); + skip_space(&I); + checkskip(';', "expected semicolon after call"); + + // actually emit stuff + if (old != I) emit(opcode.imm, value); + emit(opcode.hlt, 0); + + continue; + } + + // emit system + if (strncmp("system", I, 6) == 0 && !isalnum(I[ 7 ])) { + I += 6; + skip_space(&I); + checkskip('(', "expected open paren on system blob"); + skip_space(&I); + checkskip( + ')', + "expected closing paren on system blob (hint: system does not take any arguments)"); + skip_space(&I); + checkskip(';', "expected semicolon after system blob"); + + _ir2("@0 = 2; exit();", O, macros); + + continue; + } + + // macro call, maybe? + if (isalpha(*I)) { + slice name = scanword(); + skip_space(&I); + + checkskip('(', "expected open paren after alphanumeric word (assuming this is a blob)"); + skip_space(&I); + + slice arguments[ 1 << 7 ]; + size_t arg_count = 0; + + while (*I != ')') { + if (!*I) { + ERR = "reached EOF whilst looking for closing paren on blob"; + run_err(I); + } + + checkskip('[', "expected bracket in blob argument (macro-type)"); + arguments[ arg_count ].len = 0; + arguments[ arg_count ].ref = I; + + int dep = 0; + while (dep != 0 || *I != ']') { + if (*I == '[') dep++; + if (*I == ']') dep--; + arguments[ arg_count ].len++; + I++; + + if (*I == 0) { + checkskip( + ']', "expected closing bracket in macro argument (met EOF instead)"); + } + } + + skip_space(&I); + checkskip(']', "expected closing bracket in macro arguments"); + + skip_space(&I); + if (*I != ',' && *I != ')') { + ERR = "expected comma or closing paren in blob"; + run_err(I); + } + + if (*I == ',') { + I++; + skip_space(&I); + } + + arg_count++; + } + + checkskip(')', "expected closing paren after blob arguments"); + skip_space(&I); + checkskip(';', "expected semicolon after blob"); + + // done reading blob! check it and parse macro :) + // find index + size_t i; + for (i = 0; i < (1 << 9); i++) { + entry cur = macros[ i ]; + if (cur.key.len != name.len) continue; + if (strncmp(name.ref, cur.key.ref, name.len) == 0) goto FOUND; + } + + ERR = "could not find a macro by that name"; + run_err(I); + + FOUND:; + slice target = macros[ i ].value; + string N = target.ref; + string oldN = N; + memslice new = { target.len, malloc(target.len) }; + size_t used = 0; + bool needs_scan = true; + + while (needs_scan) { + needs_scan = false; + + while (*N) { + if (N - oldN >= target.len) break; + + if (*N == '/' && isdigit(N[ 1 ])) { + N++; + string old = N - 1; + size_t index = strtol(N, &N, 10); + + if (index > arg_count) { + N--; + goto END; + } + + if (*N++ != '/') { + N = old + 1; + while (*N != '/') { + new.ref[ used++ ] = *N++; + readd(); + } + goto END; + } + + needs_scan = true; + slice rep = arguments[ index - 1 ]; + string val = rep.ref; + for (i = 0; i < rep.len; i++) { + new.ref[ used++ ] = *val++; + readd(); + } + + continue; + } + + END: + new.ref[ used++ ] = *N++; + readd(); + } + } + + new.ref[ used ] = 0; + + _ir2(new.ref, O, macros); + + continue; + } + } +} + +ir2(string I, int **O) { + entry macros[ 1 << 9 ]; + _ir2(I, O, macros); +} + +main() { + int *out = &m[ 1024 ]; + size_t o_size; + ir2(read_file("code.i2", &o_size), &out); + m[ 7 ] = 1024; + + // size_t res_size = out - &m[ 1024 ]; + // for(i = 0; i < res_size; i++) { + // printf("%i ", m[1024+i]); + // } + // fflush(stdout); + // printf("\n\n"); + + emu(); +} + +main2(int argc, char **argv) { + if (argc == 1) { + return printf( + "\nusage:\n" + " %s \n\n", + argv[ 0 ]); + } + + char *filename = argv[ 1 ]; + size_t file_size; + char *contents = read_file(filename, &file_size); + + E = 0; + O = &m[ 1024 ]; + as2(contents); + + if (E != 0) { + error_debug(E, contents, file_size); + exit(1); + } + + m[ 7 ] = 1024; + emu(); +} + +#endif \ No newline at end of file diff --git a/pu2/pu2test.c b/pu2/pu2test.c new file mode 100644 index 0000000..93d76aa --- /dev/null +++ b/pu2/pu2test.c @@ -0,0 +1,227 @@ +#define __WRAPPER +#include "pu2.c" +#include +#include + +/* ANSI colors */ +#define CLR_GREEN "\x1b[32m" +#define CLR_RED "\x1b[31m" +#define CLR_YEL "\x1b[33m" +#define CLR_RST "\x1b[0m" + +/* test counters */ +static int total_tests = 0; +static int passed_tests = 0; + +/* helper: load program at pc and set PC (m[7]) */ +void load_prog(int pc, int *prog, int len) { + int i; + /* clear memory (keeps tests isolated) */ + for (i = 0; i < S; ++i) m[ i ] = 0; + for (i = 0; i < len; ++i) m[ pc + i ] = prog[ i ]; + m[ 7 ] = pc; +} + +/* unified expect function (increments counters and prints colored output) */ +void expect_int(const char *name, int got, int want) { + ++total_tests; + if (got == want) { + ++passed_tests; + printf("%sPASS%s: %s == %d\n", CLR_GREEN, CLR_RST, name, want); + } else { + printf("%sFAIL%s: %s == %d (want %d)\n", CLR_RED, CLR_RST, name, got, want); + } +} + +unsigned char outbuf[ 512 ]; + +void zero_Y(void) { + int ii; + for (ii = 0; ii < (1 << 10); ++ii) Y[ ii ] = 0; +} + +void set_outbuf(void) { + memset(outbuf, 0, sizeof outbuf); + O = (char *) outbuf; + zero_Y(); +} + +/* small helper to run a CPU test with a program loaded at 1024 */ +void run_cpu_test(int *prog, int len, void (*setup)(void), const char *name, int (*check)(void)) { + load_prog(1024, prog, len); + if (setup) setup(); + pu2(); + if (check) { + /* check should call expect_int internally or return 1 on success */ + check(); + } + (void) name; /* kept for future extension */ +} + +/* example setups and checks for clarity */ +int check_a_is_42(void) { + expect_int("a (load imm 42)", a, 42); + return 0; +} + +int main(void) { + /* CPU tests */ + { + int prog[] = { 1, 42, 0, 0 }; /* [opcode, imm, halt, dummy] */ + load_prog(1024, prog, sizeof prog / sizeof prog[ 0 ]); + a = b = v = c = 0; + pu2(); + expect_int("a (load imm 42)", a, 42); + } + + { + int prog[] = { 2, 0, 0, 0 }; + load_prog(1024, prog, 4); + /* place value at memory index 500 and set b=500 */ + m[ 500 ] = 99; + b = 500; + a = 0; + pu2(); + expect_int("a (a = m[b])", a, 99); + } + + { + int prog[] = { 3, 0, 0, 0 }; + load_prog(1024, prog, 4); + a = 600; + b = 1234; + pu2(); + expect_int("m[600] (m[a]=b)", m[ 600 ], 1234); + } + + { + int prog[] = { 4, 0, 0, 0 }; + load_prog(1024, prog, 4); + a = 7; + b = 11; + v = 0; + pu2(); + expect_int("a (after swap)", a, 11); + expect_int("b (after swap)", b, 7); + } + + { + int prog[] = { 5, 0, 0, 0 }; + load_prog(1024, prog, 4); + a = 5; + b = 8; + pu2(); + expect_int("a (a += b)", a, 13); + } + + { + int prog[] = { 6, 0, 0, 0 }; + load_prog(1024, prog, 4); + a = 20; + b = 3; + pu2(); + expect_int("a (a -= b)", a, 17); + } + + { + int prog[] = { 7, 0, 0, 0 }; + load_prog(1024, prog, 4); + a = 5; + b = 2; + pu2(); + expect_int("a (5 && 2)", a, 1); + load_prog(1024, prog, 4); + a = 0; + b = 3; + pu2(); + expect_int("a (0 && 3)", a, 0); + } + + { + int prog[] = { 8, 0, 0, 0 }; + load_prog(1024, prog, 4); + a = 1; + b = 1; + pu2(); + expect_int("a (!a)", a, 0); + load_prog(1024, prog, 4); + a = 0; + b = 1; + pu2(); + expect_int("a (!a)", a, 1); + } + + // .. TODO: implement test case for ACC instruction .. + + { + int prog[] = { 1, 9, 4, 0, 3, 0, 0, 0 }; + /* sequence: load imm 9 into a, swap, store b into m[a], halt */ + load_prog(1024, prog, sizeof prog / sizeof prog[ 0 ]); + a = 2; + b = 87; /* after load & swap: a=87, b=9 ; then m[87]=9 */ + pu2(); + expect_int("m[87] (from multi-op)", m[ 87 ], 9); + } + + /* Assembler (as2) tests: use outbuf */ + { + unsigned char *o; + + set_outbuf(); + as2("hlt"); + o = outbuf; + expect_int("hlt:opcode", o[ 0 ], 0); + expect_int("hlt:imm", o[ 1 ], 0); + + set_outbuf(); + as2("imm 42"); + o = outbuf; + expect_int("imm:opcode", o[ 0 ], 1); + expect_int("imm:imm", o[ 1 ], 42); + + set_outbuf(); + as2("add sub"); + o = outbuf; + expect_int("add:opcode", o[ 0 ], 5); + expect_int("add:imm", o[ 1 ], 0); + expect_int("sub:opcode", o[ 2 ], 6); + expect_int("sub:imm", o[ 3 ], 0); + + set_outbuf(); + as2("(0 hlt) @0"); + o = outbuf; + expect_int("macro-hlt:opcode", o[ 0 ], 0); + expect_int("macro-hlt:imm", o[ 1 ], 0); + + set_outbuf(); + as2("(0 imm 7) @0"); + o = outbuf; + expect_int("macro-imm:opcode", o[ 0 ], 1); + expect_int("macro-imm:imm", o[ 1 ], 7); + + set_outbuf(); + as2("(0 imm 3 add) @0"); + o = outbuf; + expect_int("macro-mix:imm-op", o[ 0 ], 1); + expect_int("macro-mix:imm-val", o[ 1 ], 3); + expect_int("macro-mix:add-op", o[ 2 ], 5); + expect_int("macro-mix:add-imm", o[ 3 ], 0); + } + + /* Summary */ + printf( + "\n%sTest summary%s: %d total, %d passed, %d failed\n", + CLR_YEL, + CLR_RST, + total_tests, + passed_tests, + total_tests - passed_tests); + + if (passed_tests == total_tests) { + printf("%sALL TESTS PASSED%s\n", CLR_GREEN, CLR_RST); + return 0; + } else { + printf("%sSOME TESTS FAILED%s\n", CLR_RED, CLR_RST); + return 1; + } +} diff --git a/putest.c b/putest.c deleted file mode 100644 index 013a48b..0000000 --- a/putest.c +++ /dev/null @@ -1,380 +0,0 @@ -#include -#include - -#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/repl.pu b/repl.pu deleted file mode 100644 index 2cb3020..0000000 --- a/repl.pu +++ /dev/null @@ -1 +0,0 @@ -con 5 diff --git a/t b/t deleted file mode 100644 index 7b992ff..0000000 Binary files a/t and /dev/null differ -- cgit v1.2.3