aboutsummaryrefslogtreecommitdiff
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
parentvscode theme extension (diff)
ir2 initial release!
-rw-r--r--README.md46
-rwxr-xr-xpenusbin16424 -> 0 bytes
-rw-r--r--penus.c30
-rw-r--r--pu1/code.pasm (renamed from code.pasm)0
-rw-r--r--pu1/pasm-syntax/package.json (renamed from pasm-syntax/package.json)0
-rw-r--r--pu1/pasm-syntax/pasm-syntax-0.0.1.vsix (renamed from pasm-syntax/pasm-syntax-0.0.1.vsix)bin2163 -> 2163 bytes
-rw-r--r--pu1/pasm-syntax/syntaxes/pasm.tmLanguage.json (renamed from pasm-syntax/syntaxes/pasm.tmLanguage.json)0
-rw-r--r--pu1/pu.c (renamed from pu.c)6
-rw-r--r--pu1/pu.out.c (renamed from pu.out.c)bin1405 -> 1405 bytes
-rw-r--r--pu1/putest.c (renamed from putest.c)0
-rw-r--r--pu1/repl.pu (renamed from repl.pu)0
-rw-r--r--pu1/t (renamed from t)bin115 -> 115 bytes
-rw-r--r--pu2/code.i215
-rw-r--r--pu2/i2-syntax/language-configuration.json30
-rw-r--r--pu2/i2-syntax/package.json35
-rw-r--r--pu2/i2-syntax/syntaxes/i2.tmLanguage.json106
-rw-r--r--pu2/p2m-syntax/README.md9
-rw-r--r--pu2/p2m-syntax/language-configuration.json30
-rw-r--r--pu2/p2m-syntax/p2m-asm-syntax-0.0.1.vsixbin0 -> 3130 bytes
-rw-r--r--pu2/p2m-syntax/package.json30
-rw-r--r--pu2/p2m-syntax/syntaxes/p2m.tmLanguage105
-rw-r--r--pu2/pu2.c547
-rw-r--r--pu2/pu2test.c227
23 files changed, 1166 insertions, 50 deletions
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/penus b/penus
deleted file mode 100755
index a986fe2..0000000
--- a/penus
+++ /dev/null
Binary files 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 <SDL2/SDL.h>
-#include <stdio.h>
-
-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/code.pasm b/pu1/code.pasm
index 94f37c5..94f37c5 100644
--- a/code.pasm
+++ b/pu1/code.pasm
diff --git a/pasm-syntax/package.json b/pu1/pasm-syntax/package.json
index d287cc2..d287cc2 100644
--- a/pasm-syntax/package.json
+++ b/pu1/pasm-syntax/package.json
diff --git a/pasm-syntax/pasm-syntax-0.0.1.vsix b/pu1/pasm-syntax/pasm-syntax-0.0.1.vsix
index e94bb8b..e94bb8b 100644
--- a/pasm-syntax/pasm-syntax-0.0.1.vsix
+++ b/pu1/pasm-syntax/pasm-syntax-0.0.1.vsix
Binary files differ
diff --git a/pasm-syntax/syntaxes/pasm.tmLanguage.json b/pu1/pasm-syntax/syntaxes/pasm.tmLanguage.json
index e0df85e..e0df85e 100644
--- a/pasm-syntax/syntaxes/pasm.tmLanguage.json
+++ b/pu1/pasm-syntax/syntaxes/pasm.tmLanguage.json
diff --git a/pu.c b/pu1/pu.c
index 3de2576..f182e65 100644
--- a/pu.c
+++ b/pu1/pu.c
@@ -63,7 +63,7 @@ SDL_Renderer *renderer = NULL;
// runs pu until exit code != 2,
// that exit code is an interrupt for talking to the emulated hardware
-int emu() {
+emu() {
while (1) {
pu();
int e_code = m[ 0 ];
@@ -214,7 +214,7 @@ int emu() {
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) {
+error_debug(char *fail_point, char *code, int sz) {
if ((long) fail_point >= 0) return;
fail_point = (char *) -(long) fail_point;
@@ -256,7 +256,7 @@ void error_debug(char *fail_point, char *code, int sz) {
fprintf(stderr, "\x1b[1m\x1b[31m^\x1b[0m \x1b[90mparsing stopped here\x1b[0m\n");
}
-int main(int argc, char **argv) {
+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");
diff --git a/pu.out.c b/pu1/pu.out.c
index c62b50a..c62b50a 100644
--- a/pu.out.c
+++ b/pu1/pu.out.c
Binary files differ
diff --git a/putest.c b/pu1/putest.c
index 013a48b..013a48b 100644
--- a/putest.c
+++ b/pu1/putest.c
diff --git a/repl.pu b/pu1/repl.pu
index 2cb3020..2cb3020 100644
--- a/repl.pu
+++ b/pu1/repl.pu
diff --git a/t b/pu1/t
index 7b992ff..7b992ff 100644
--- a/t
+++ b/pu1/t
Binary files 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
--- /dev/null
+++ b/pu2/p2m-syntax/p2m-asm-syntax-0.0.1.vsix
Binary files 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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
+ "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+ <dict>
+ <key>name</key>
+ <string>P2M Assembly</string>
+
+ <key>scopeName</key>
+ <string>source.p2m</string>
+
+ <key>fileTypes</key>
+ <array>
+ <string>p2m</string>
+ </array>
+
+ <key>patterns</key>
+ <array>
+
+ <!-- comments -->
+ <dict>
+ <key>name</key>
+ <string>comment.line.number-sign.p2m</string>
+ <key>match</key>
+ <string>#.*$</string>
+ </dict>
+
+ <!-- block braces -->
+ <dict>
+ <key>name</key>
+ <string>punctuation.section.block.p2m</string>
+ <key>match</key>
+ <string>[\{\}]</string>
+ </dict>
+
+ <!-- mnemonics / instructions -->
+ <dict>
+ <key>name</key>
+ <string>keyword.control.mnemonic.p2m</string>
+ <key>match</key>
+ <string>\b(?:hlt|imm|lod|str|swp|add|sub|and|nnd)\b</string>
+ </dict>
+
+ <!-- registers (single-letter a b c v) -->
+ <dict>
+ <key>name</key>
+ <string>variable.other.register.p2m</string>
+ <key>match</key>
+ <string>\b(?:a|b|c|v)\b</string>
+ </dict>
+
+ <!-- memory references like m[10] -->
+ <dict>
+ <key>name</key>
+ <string>variable.other.memory.p2m</string>
+ <key>match</key>
+ <string>\bm\[\s*\d+\s*\]</string>
+ </dict>
+
+ <!-- at-index references like @0 -->
+ <dict>
+ <key>name</key>
+ <string>variable.parameter.memory-index.p2m</string>
+ <key>match</key>
+ <string>@\d+</string>
+ </dict>
+
+ <!-- numeric literals -->
+ <dict>
+ <key>name</key>
+ <string>constant.numeric.hex.p2m</string>
+ <key>match</key>
+ <string>\b0x[0-9A-Fa-f]+\b</string>
+ </dict>
+ <dict>
+ <key>name</key>
+ <string>constant.numeric.decimal.p2m</string>
+ <key>match</key>
+ <string>\b\d+\b</string>
+ </dict>
+
+ <!-- quoted chars -->
+ <dict>
+ <key>name</key>
+ <string>string.quoted.single.p2m</string>
+ <key>match</key>
+ <string>'(.)</string>
+ </dict>
+
+ <!-- inline annotations -->
+ <dict>
+ <key>name</key>
+ <string>comment.block.annotation.p2m</string>
+ <key>match</key>
+ <string>\([^\)]*\)</string>
+ </dict>
+
+ </array>
+
+ <key>repository</key>
+ <dict/>
+ <key>scope</key>
+ <string>source.p2m</string>
+ </dict>
+</plist>
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 <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#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 = &macros[ 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 <file_name>\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 <stdio.h>
+#include <string.h>
+
+/* 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;
+ }
+}