repos / gbc

GBC - Go B Compiler
git clone https://github.com/xplshn/gbc.git

commit
5a47f8a
parent
0805fab
author
xplshn
date
2025-08-24 02:23:14 +0000 UTC
push ./lib/ dir

Signed-off-by: xplshn <[email protected]>
22 files changed,  +371, -65
A lib/b/6502-posix.b
+206, -0
  1@@ -0,0 +1,206 @@
  2+exit(code) {
  3+    0(code);
  4+}
  5+
  6+abort() {
  7+    exit(69);
  8+}
  9+
 10+putchar(c) {
 11+    0xFFEF(c);
 12+}
 13+
 14+char __asm__(
 15+    "TSX",
 16+    "CLC",
 17+    "ADC $0103,X", // i&0xFF
 18+    "STA $00", // we can safely use zero-page, as our assembler
 19+               // doesn't expect it to be preserved across op-boundaries
 20+    "TYA",
 21+    "ADC $0104,X", // i&0xFF00 >> 8
 22+    "STA $01",
 23+    "LDY #0",
 24+    "LDA ($00),Y",
 25+    "RTS"
 26+);
 27+
 28+lchar __asm__(
 29+    "TSX",
 30+    "CLC",
 31+    "ADC $0103,X", // i&0xFF
 32+    "STA $00", // we can safely use zero-page, as our assembler
 33+               // doesn't expect it to be preserved across op-boundaries
 34+    "TYA",
 35+    "ADC $0104,X", // i&0xFF00 >> 8
 36+    "STA $01",
 37+    "LDA $0105,X",
 38+    "LDY #0",
 39+    "STA ($00),Y",
 40+    "RTS"
 41+);
 42+
 43+/* TODO: fd not supported */
 44+fputc(c, fd) {
 45+    putchar(c);
 46+}
 47+
 48+/* TODO: actually allocate something */
 49+__heap_ptr 0x0200;
 50+malloc(size) {
 51+    extrn printf;
 52+    auto ptr;
 53+    ptr = __heap_ptr;
 54+    __heap_ptr += size;
 55+    if (__heap_ptr >= 0x1000) {
 56+        printf("Allocation reached end: %p\nTODO: allow allocating more, implement free\n", __heap_ptr);
 57+        abort();
 58+    }
 59+    return (ptr);
 60+}
 61+/* TODO: free someting? */
 62+realloc(ptr, size) {
 63+    return (malloc(size));
 64+}
 65+
 66+/* TODO: Try to implement this function with assembly
 67+   Problem with this implementation is that it is not
 68+   mapped to the operator
 69+   We cannot call this function `div` as it conflicts
 70+   with the `divmod` test
 71+*/
 72+_div(a, b) {
 73+    auto d, sign;
 74+    sign = 0;
 75+    if (a < 0) {
 76+        sign = !sign;
 77+        a = -a;
 78+    }
 79+    if (b < 0) {
 80+        sign = !sign;
 81+        b = -a;
 82+    }
 83+
 84+    d = 0; while(a >= b) {
 85+        a = a - b;
 86+        d++;
 87+    }
 88+    if (sign) d = -d;
 89+    return (d);
 90+}
 91+_udiv(a, b) {
 92+    auto d;
 93+    d = 0; while(a >= b | a < 0) {
 94+        a = a - b;
 95+        d++;
 96+    }
 97+    return (d);
 98+}
 99+
100+/* TODO: Try to implement this function with assembly
101+   Problem with this implementation is that it is not
102+   mapped to the operator */
103+_rem (a, b) {
104+    auto d;
105+    while(a >= b) {
106+        a = a - b;
107+    }
108+    return (a);
109+}
110+_urem(a, b) {
111+    auto d;
112+    while(a >= b | a < 0) {
113+        a = a - b;
114+    }
115+    return (a);
116+}
117+
118+printn(n, b, sign) {
119+    auto a, c, d, __div, __rem;
120+
121+    /* use correct div/rem based on sign */
122+    __div = sign ? &_div : &_udiv;
123+    __rem = sign ? &_rem : &_urem;
124+
125+    if (sign & n < 0) {
126+        putchar('-');
127+        n = -n;
128+    }
129+
130+    if(a=__div(n, b)) /* assignment, not test for equality */
131+        printn(a, b, 0); /* recursive */
132+    c = __rem(n,b) + '0';
133+    if (c > '9') c += 7;
134+    putchar(c);
135+}
136+
137+printf(str, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15) {
138+    auto i, j, arg, c;
139+    i = 0;
140+    j = 0;
141+
142+    arg = &x1;
143+
144+    c = char(str, i);
145+    while (c != 0) {
146+        if (c == '\n') {
147+            putchar(0xD); // \r
148+        }
149+
150+        if(c == '%') {
151+            i += 1;
152+            c = char(str, i);
153+            if (c == 0) {
154+                return;
155+            } else if (c == 'd') {
156+                printn(*arg, 10, 1);
157+            } else if (c == 'u') {
158+                printn(*arg, 10, 0);
159+            } else if (c == 'p') {
160+                putchar('$');
161+                printn(*arg, 16, 0);
162+            } else if (c == 'c') {
163+                putchar(*arg);
164+            } else if (c == 's') { /* clobbers `c`, the last one */
165+                while (c = char(*arg, j++)) {
166+                    putchar(c);
167+                }
168+            } else if (c == 'z' | c == 'l') { /* hack for %zu %lu, % */
169+                c = '%';
170+                goto while_end;
171+            } else {
172+                putchar('%');
173+                arg += 2; /* word size */
174+            }
175+            arg -= 2; /* word size */
176+        } else {
177+            putchar(c); /* ECHO */
178+        }
179+        i++;
180+        c = char(str, i);
181+        while_end:;
182+    }
183+}
184+
185+strlen(s) {
186+    auto n;
187+    n = 0;
188+    while (char(s, n)) n++;
189+    return (n);
190+}
191+
192+toupper(c) {
193+    if ('a' <= c & c <= 'z') return (c - 'a' + 'A');
194+    return (c);
195+}
196+
197+
198+/* memory related functions */
199+memset(addr, val, size) {
200+    extrn lchar;
201+    auto i;
202+    i = 0;
203+    while (i < size) {
204+        lchar(addr, i, val);
205+        i += 1;
206+    }
207+}
M lib/b/aarch64-Darwin.b
+1, -1
1@@ -1 +1 @@
2-arm64_darwin.b
3+gas-aarch64-darwin.b
M lib/b/aarch64-Linux.b
+1, -1
1@@ -1 +1 @@
2-arm64_linux.b
3+gas-aarch64-linux.b
D lib/b/amd64_darwin.b
+0, -7
1@@ -1,7 +0,0 @@
2-sx64 __asm__("movslq %edi, %rax", "ret");
3-char  __asm__("xorq %rax, %rax", "movb (%rdi, %rsi), %al", "ret");
4-lchar __asm__("movb %dl, (%rdi, %rsi)", "ret");
5-extrn printf;
6-extrn putchar;
7-extrn getchar;
8-extrn exit;
A lib/b/amd64_darwin.b
+1, -0
1@@ -0,0 +1 @@
2+x86_64-Darwin.b
D lib/b/amd64_linux.b
+0, -7
1@@ -1,7 +0,0 @@
2-sx64 __asm__("movslq %edi, %rax", "ret");
3-char  __asm__("xorq %rax, %rax", "movb (%rdi, %rsi), %al", "ret");
4-lchar __asm__("movb %dl, (%rdi, %rsi)", "ret");
5-extrn printf;
6-extrn putchar;
7-extrn getchar;
8-extrn exit;
A lib/b/amd64_linux.b
+1, -0
1@@ -0,0 +1 @@
2+x86_64-Linux.b
D lib/b/amd64_windows.b
+0, -7
1@@ -1,7 +0,0 @@
2-sx64 __asm__("xorq %rax, %rax", "movslq %ecx, %rax", "ret");
3-char  __asm__("xorq %rax, %rax", "movb (%rcx, %rdx), %al", "ret");
4-lchar __asm__("movb %r8b, (%rcx, %rdx)", "ret");
5-extrn printf;
6-extrn putchar;
7-extrn getchar;
8-extrn exit;
A lib/b/amd64_windows.b
+1, -0
1@@ -0,0 +1 @@
2+x86_64-Windows.b
D lib/b/arm64_darwin.b
+0, -8
1@@ -1,8 +0,0 @@
2-sx64 __asm__("sxtw x0, w0", "ret");
3-char __asm__("ldrb w0, [x0, x1]", "ret");
4-lchar __asm__("strb w2, [x0, x1]", "ret");
5-extrn printf;
6-__variadic__(printf, 1);
7-extrn putchar;
8-extrn getchar;
9-extrn exit;
A lib/b/arm64_darwin.b
+1, -0
1@@ -0,0 +1 @@
2+aarch64-Darwin.b
D lib/b/arm64_linux.b
+0, -7
1@@ -1,7 +0,0 @@
2-sx64 __asm__("sxtw x0, w0", "ret");
3-char __asm__("ldrb w0, [x0, x1]", "ret");
4-lchar __asm__("strb w2, [x0, x1]", "ret");
5-extrn printf;
6-extrn putchar;
7-extrn getchar;
8-extrn exit;
A lib/b/arm64_linux.b
+1, -0
1@@ -0,0 +1 @@
2+aarch64-Linux.b
A lib/b/gas-aarch64-darwin.b
+8, -0
1@@ -0,0 +1,8 @@
2+sx64 __asm__("sxtw x0, w0", "ret");
3+char __asm__("ldrb w0, [x0, x1]", "ret");
4+lchar __asm__("strb w2, [x0, x1]", "ret");
5+extrn printf;
6+__variadic__(printf, 1);
7+extrn putchar;
8+extrn getchar;
9+extrn exit;
A lib/b/gas-aarch64-linux.b
+7, -0
1@@ -0,0 +1,7 @@
2+sx64 __asm__("sxtw x0, w0", "ret");
3+char __asm__("ldrb w0, [x0, x1]", "ret");
4+lchar __asm__("strb w2, [x0, x1]", "ret");
5+extrn printf;
6+extrn putchar;
7+extrn getchar;
8+extrn exit;
A lib/b/gas-x86_64-darwin.b
+7, -0
1@@ -0,0 +1,7 @@
2+sx64 __asm__("movslq %edi, %rax", "ret");
3+char  __asm__("xorq %rax, %rax", "movb (%rdi, %rsi), %al", "ret");
4+lchar __asm__("movb %dl, (%rdi, %rsi)", "ret");
5+extrn printf;
6+extrn putchar;
7+extrn getchar;
8+extrn exit;
A lib/b/gas-x86_64-linux.b
+7, -0
1@@ -0,0 +1,7 @@
2+sx64 __asm__("movslq %edi, %rax", "ret");
3+char  __asm__("xorq %rax, %rax", "movb (%rdi, %rsi), %al", "ret");
4+lchar __asm__("movb %dl, (%rdi, %rsi)", "ret");
5+extrn printf;
6+extrn putchar;
7+extrn getchar;
8+extrn exit;
A lib/b/gas-x86_64-windows.b
+7, -0
1@@ -0,0 +1,7 @@
2+sx64 __asm__("xorq %rax, %rax", "movslq %ecx, %rax", "ret");
3+char  __asm__("xorq %rax, %rax", "movb (%rcx, %rdx), %al", "ret");
4+lchar __asm__("movb %r8b, (%rcx, %rdx)", "ret");
5+extrn printf;
6+extrn putchar;
7+extrn getchar;
8+extrn exit;
M lib/b/uxn.b
+119, -24
  1@@ -1,7 +1,106 @@
  2 /* Standard Library for the Uxn target */
  3 
  4+/*
  5+ch = char(string, i);
  6+returns the ith character in a string pointed to by string, 0 based
  7+*/
  8+
  9+char __asm__(
 10+    "lit 4", "ldz2", /* first arg, string */
 11+    "lit 6", "ldz2", /* second arg, i */
 12+    "add2",
 13+    "lda",
 14+    "lit 0",
 15+    "swp",
 16+    "lit 4", "stz2", /* return value (same spot as the first arg) */
 17+    "jmp2r"
 18+);
 19+
 20+/*
 21+ch = lchar(string, i, char);
 22+replaces the ith character in the string pointed to by string with the character char.
 23+The value LCHAR returns is the character char that was placed in the string.
 24+*/
 25+
 26+lchar __asm__(
 27+    "lit 9", "ldz", /* low byte of the arg 2, char */
 28+    "lit 4", "ldz2",
 29+    "lit 6", "ldz2",
 30+    "add2",
 31+    "stak",
 32+    "pop2",
 33+    "lit 0",
 34+    "swp",
 35+    "lit 4", "stz2",
 36+    "jmp2r"
 37+);
 38+
 39+/*
 40+value = uxn_dei(device);
 41+reads 8 bit value off a device
 42+*/
 43+
 44+uxn_dei __asm__(
 45+    "lit 0", "lit 4", "stz", /* zero the high byte of arg0/return */
 46+    "lit 5", "ldzk", /* low byte of arg0 */
 47+    "dei",
 48+    "swp",
 49+    "stz",
 50+    "jmp2r"
 51+);
 52+
 53+/*
 54+value = uxn_dei2(device);
 55+reads 16 bit value off a device
 56+*/
 57+
 58+uxn_dei2 __asm__(
 59+    "lit 5", "ldz", /* low byte of arg0 */
 60+    "dei2",
 61+    "lit 4", "stz2",
 62+    "jmp2r"
 63+);
 64+
 65+/*
 66+uxn_deo(device, value);
 67+outputs 8 bit value to a device
 68+*/
 69+
 70+uxn_deo __asm__(
 71+    "lit 7", "ldz", /* low byte of arg1 */
 72+    "lit 5", "ldz", /* low byte of arg0 */
 73+    "deo",
 74+    "lit2 0", "lit 4", "stz2", /* return 0 */
 75+    "jmp2r"
 76+);
 77+
 78+/*
 79+uxn_deo2(device, value);
 80+outputs 16 bit value to a device
 81+*/
 82+
 83+uxn_deo2 __asm__(
 84+    "lit 6", "ldz2", /* arg1 */
 85+    "lit 5", "ldz", /* low byte of arg0 */
 86+    "deo2",
 87+    "lit2 0", "lit 4", "stz2", /* return 0 */
 88+    "jmp2r"
 89+);
 90+
 91+/*
 92+uxn_udiv(a, b)
 93+outputs 16 bit unsigned division of a / b.
 94+*/
 95+
 96+uxn_div2 __asm__(
 97+    "lit 4", "ldz2", /* arg0 */
 98+    "lit 6", "ldz2", /* arg1 */
 99+    "div2",
100+    "lit 4", "stz2",
101+    "jmp2r"
102+);
103+
104 fputc(c, fd) {
105-    extrn uxn_deo;
106     uxn_deo(fd + 0x18, c); /* 0x18 - Console/write,
107                               0x19 - Console/error */
108 }
109@@ -11,12 +110,22 @@ putchar(c) {
110 }
111 
112 exit(code) {
113-    extrn uxn_deo;
114     uxn_deo(0x0f, code | 0x80); /* System/state */
115 }
116 
117+_exit_after_main 1;
118+
119+uxn_disable_exit_after_main() {
120+    _exit_after_main = 0;
121+}
122+
123+_exit_main(code) {
124+    if (_exit_after_main) {
125+        exit(code);
126+    }
127+}
128+
129 abort() {
130-    extrn printf;
131     printf("Aborted\n");
132     exit(1);
133 }
134@@ -50,7 +159,6 @@ printn(n, b) _fprintn(n, b, 0);
135 /* TODO: Consider adding support for negative numbers to Uxn's printf. */
136 /* TODO: Consider adding support for %ul to Uxn's printf. */
137 fprintf(fd, string, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12) {
138-    extrn char;
139     auto i, j, c, arg;
140     i = 0;
141     j = 0;
142@@ -103,7 +211,6 @@ printf(string, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12) {
143 
144 // TODO: doesn't skip whitespace, doesn't handle negative numbers
145 atoi(s) {
146-    extrn char;
147     auto i, result, c;
148     i = 0;
149     while (1) {
150@@ -119,20 +226,16 @@ out:
151 
152 /* simple bump allocator */
153 
154-__alloc_ptr;
155+__alloc_ptr 0x8000; /* provide __heap_base by the compiler? */
156 
157 malloc(size) {
158     auto ret;
159-    if (__alloc_ptr == 0) {
160-        __alloc_ptr = 0x8000; /* provide __heap_base by the compiler? */
161-    }
162     ret = __alloc_ptr;
163     __alloc_ptr += size;
164     return (ret);
165 }
166 
167 memset(addr, val, size) {
168-    extrn lchar;
169     auto i;
170     i = 0;
171     while (i < size) {
172@@ -141,14 +244,13 @@ memset(addr, val, size) {
173     }
174 }
175 
176-stdout; stderr;
177+stdout 0; stderr 1;
178 
179-_args_count;
180-_args_items;
181-_prog_name;
182+_args_count 1;
183+_args_items 0x7f00; /* 128 arguments ought to be enough for everyone */
184+_prog_name "-";
185 
186 _start_with_arguments() {
187-    extrn uxn_dei, uxn_deo2, lchar, main;
188     auto type, c;
189     type = uxn_dei(0x17); /* Console/type */
190     c = uxn_dei(0x12);
191@@ -160,24 +262,17 @@ _start_with_arguments() {
192     } else if (type == 4) { /* arguments end */
193         lchar(__alloc_ptr++, 0, 0);
194         uxn_deo2(0x10, 0);
195-        exit(main(_args_count, _args_items));
196+        _exit_main(main(_args_count, _args_items));
197     }
198 }
199 
200 _start() {
201-    extrn main, uxn_dei, uxn_deo2;
202-    __alloc_ptr = 0x8000;
203-    _args_items = 0x7f00; /* 128 arguments ought to be enough for everyone */
204-    stdout = 0;
205-    stderr = 1;
206-    _prog_name = "-"; /* we don't have access to it */
207     *_args_items = _prog_name;
208-    _args_count = 1;
209     if (uxn_dei(0x17) != 0) {
210         *(_args_items + (_args_count++)*2) = __alloc_ptr;
211         uxn_deo2(0x10, &_start_with_arguments);
212     } else {
213-        exit(main(_args_count, _args_items));
214+        _exit_main(main(_args_count, _args_items));
215     }
216 }
217 
M lib/b/x86_64-Darwin.b
+1, -1
1@@ -1 +1 @@
2-amd64_darwin.b
3+gas-x86_64-darwin.b
M lib/b/x86_64-Linux.b
+1, -1
1@@ -1 +1 @@
2-amd64_linux.b
3+gas-x86_64-linux.b
M lib/b/x86_64-Windows.b
+1, -1
1@@ -1 +1 @@
2-amd64_windows.b
3+gas-x86_64-windows.b