repos / gbc

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

gbc / lib / b
xplshn  ·  2025-08-24

uxn.b

B
  1/* Standard Library for the Uxn target */
  2
  3/*
  4ch = char(string, i);
  5returns the ith character in a string pointed to by string, 0 based
  6*/
  7
  8char __asm__(
  9    "lit 4", "ldz2", /* first arg, string */
 10    "lit 6", "ldz2", /* second arg, i */
 11    "add2",
 12    "lda",
 13    "lit 0",
 14    "swp",
 15    "lit 4", "stz2", /* return value (same spot as the first arg) */
 16    "jmp2r"
 17);
 18
 19/*
 20ch = lchar(string, i, char);
 21replaces the ith character in the string pointed to by string with the character char.
 22The value LCHAR returns is the character char that was placed in the string.
 23*/
 24
 25lchar __asm__(
 26    "lit 9", "ldz", /* low byte of the arg 2, char */
 27    "lit 4", "ldz2",
 28    "lit 6", "ldz2",
 29    "add2",
 30    "stak",
 31    "pop2",
 32    "lit 0",
 33    "swp",
 34    "lit 4", "stz2",
 35    "jmp2r"
 36);
 37
 38/*
 39value = uxn_dei(device);
 40reads 8 bit value off a device
 41*/
 42
 43uxn_dei __asm__(
 44    "lit 0", "lit 4", "stz", /* zero the high byte of arg0/return */
 45    "lit 5", "ldzk", /* low byte of arg0 */
 46    "dei",
 47    "swp",
 48    "stz",
 49    "jmp2r"
 50);
 51
 52/*
 53value = uxn_dei2(device);
 54reads 16 bit value off a device
 55*/
 56
 57uxn_dei2 __asm__(
 58    "lit 5", "ldz", /* low byte of arg0 */
 59    "dei2",
 60    "lit 4", "stz2",
 61    "jmp2r"
 62);
 63
 64/*
 65uxn_deo(device, value);
 66outputs 8 bit value to a device
 67*/
 68
 69uxn_deo __asm__(
 70    "lit 7", "ldz", /* low byte of arg1 */
 71    "lit 5", "ldz", /* low byte of arg0 */
 72    "deo",
 73    "lit2 0", "lit 4", "stz2", /* return 0 */
 74    "jmp2r"
 75);
 76
 77/*
 78uxn_deo2(device, value);
 79outputs 16 bit value to a device
 80*/
 81
 82uxn_deo2 __asm__(
 83    "lit 6", "ldz2", /* arg1 */
 84    "lit 5", "ldz", /* low byte of arg0 */
 85    "deo2",
 86    "lit2 0", "lit 4", "stz2", /* return 0 */
 87    "jmp2r"
 88);
 89
 90/*
 91uxn_udiv(a, b)
 92outputs 16 bit unsigned division of a / b.
 93*/
 94
 95uxn_div2 __asm__(
 96    "lit 4", "ldz2", /* arg0 */
 97    "lit 6", "ldz2", /* arg1 */
 98    "div2",
 99    "lit 4", "stz2",
100    "jmp2r"
101);
102
103fputc(c, fd) {
104    uxn_deo(fd + 0x18, c); /* 0x18 - Console/write,
105                              0x19 - Console/error */
106}
107
108putchar(c) {
109    fputc(c, 0);
110}
111
112exit(code) {
113    uxn_deo(0x0f, code | 0x80); /* System/state */
114}
115
116_exit_after_main 1;
117
118uxn_disable_exit_after_main() {
119    _exit_after_main = 0;
120}
121
122_exit_main(code) {
123    if (_exit_after_main) {
124        exit(code);
125    }
126}
127
128abort() {
129    printf("Aborted\n");
130    exit(1);
131}
132
133_udiv(a, b) {
134    extrn uxn_div2;
135    return (uxn_div2(a, b));
136}
137
138// TODO: `b` has to be <32768, because of `*`
139_urem(a, b) {
140    return (a - _udiv(a, b) * b);
141}
142
143/* loosely based on the original code by Ken Thompson */
144
145_fprintn(n, b, fd) {
146    auto a, c;
147
148    if(a=_udiv(n,b)) /* assignment, not test for equality */
149        _fprintn(a, b, fd); /* recursive */
150    c = _urem(n,b) + '0';
151    if (c > '9') c += 7;
152    fputc(c, fd);
153}
154
155printn(n, b) _fprintn(n, b, 0);
156
157/* doesn't support fancy features like padding, but neither did the original in B */
158
159/* TODO: Consider adding support for negative numbers to Uxn's printf. */
160/* TODO: Consider adding support for %ul to Uxn's printf. */
161fprintf(fd, string, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12) {
162    auto i, j, c, arg;
163    i = 0;
164    j = 0;
165    c = char(string, i);
166    arg = &x1;
167    while (c != 0) {
168        if (c == '%') {
169            i += 1;
170            c = char(string, i);
171            if (c == 0) {
172                return;
173            } else if (c == 'x') {
174                _fprintn(*arg, 16, fd);
175            } else if (c == 'd') {
176                if (*arg < 0) {
177                    fputc('-', fd);
178                    *arg = -*arg;
179                }
180                _fprintn(*arg, 10, fd);
181            } else if (c == 'u') {
182                _fprintn(*arg, 10, fd);
183            } else if (c == 'o') {
184                _fprintn(*arg, 8, fd);
185            } else if (c == 'c') {
186                fputc(*arg, fd);
187            } else if (c == 's') { /* clobbers `c`, the last one */
188                while (c = char(*arg, j++)) {
189                    fputc(c, fd);
190                }
191            } else if (c == 'l' | c == 'z') {
192                c = '%';
193                goto continue;
194            } else {
195                fputc('%', fd);
196                arg += 2; /* word size */
197            }
198            arg -= 2; /* word size */
199        } else {
200            fputc(c, fd);
201        }
202        i += 1;
203        c = char(string, i);
204        continue:;
205    }
206}
207
208printf(string, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12) {
209    fprintf(0, string, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12);
210}
211
212// TODO: doesn't skip whitespace, doesn't handle negative numbers
213atoi(s) {
214    auto i, result, c;
215    i = 0;
216    while (1) {
217        c = char(s, i++);
218        if (c < '0' | c > '9') {
219            goto out;
220        }
221        result = result * 10 + (c - '0');
222    }
223out:
224    return (result);
225}
226
227/* simple bump allocator */
228
229__alloc_ptr 0x8000; /* provide __heap_base by the compiler? */
230
231malloc(size) {
232    auto ret;
233    ret = __alloc_ptr;
234    __alloc_ptr += size;
235    return (ret);
236}
237
238memset(addr, val, size) {
239    auto i;
240    i = 0;
241    while (i < size) {
242        lchar(addr, i, val);
243        i += 1;
244    }
245}
246
247stdout 0; stderr 1;
248
249_args_count 1;
250_args_items 0x7f00; /* 128 arguments ought to be enough for everyone */
251_prog_name "-";
252
253_start_with_arguments() {
254    auto type, c;
255    type = uxn_dei(0x17); /* Console/type */
256    c = uxn_dei(0x12);
257    if (type == 2) { /* argument */
258        lchar(__alloc_ptr++, 0, c);
259    } else if (type == 3) { /* argument spacer */
260        lchar(__alloc_ptr++, 0, 0);
261        *(_args_items + (_args_count++)*2) = __alloc_ptr;
262    } else if (type == 4) { /* arguments end */
263        lchar(__alloc_ptr++, 0, 0);
264        uxn_deo2(0x10, 0);
265        _exit_main(main(_args_count, _args_items));
266    }
267}
268
269_start() {
270    *_args_items = _prog_name;
271    if (uxn_dei(0x17) != 0) {
272        *(_args_items + (_args_count++)*2) = __alloc_ptr;
273        uxn_deo2(0x10, &_start_with_arguments);
274    } else {
275        _exit_main(main(_args_count, _args_items));
276    }
277}
278
279strlen(s) {
280    auto n;
281    n = 0;
282    while (*s++) n++;
283    return (n);
284}
285
286toupper(c) {
287    if ('a' <= c & c <= 'z') return (c - 'a' + 'A');
288    return (c);
289}