From 4335d248d18987a050d8ac26965c90718eef7562 Mon Sep 17 00:00:00 2001 From: user Date: Thu, 16 Jul 2026 07:21:40 +0200 Subject: libc + CLI tools: a small C userland - libc: add putc, puts, strlen, getargs (args string), EOF (=4) for readc. - args: shell splits the command line at the first space ("cmd\0args\0" at $A000, inherited by the child via fork); getargs() returns the arg string. - shell: exit on Ctrl-D/EOF; $ prompt over a clean read loop. - tools (c/): echo (argv), cat (stdin->stdout to EOF), uname, pid (decimal print), true, false. Each built to a ROM-bank blob and registered. - Makefile: CBLOBS list builds all C programs. - README: document libc + the tool set. --- c/cat.c | 10 ++++++++ c/crt0.s | 6 ++--- c/echo.c | 5 ++++ c/false.c | 2 ++ c/gbos.h | 16 ++++++++---- c/libc.s | 84 +++++++++++++++++++++++++++++++++++++++++++-------------------- c/pid.c | 9 +++++++ c/true.c | 2 ++ c/uname.c | 2 ++ 9 files changed, 102 insertions(+), 34 deletions(-) create mode 100644 c/cat.c create mode 100644 c/echo.c create mode 100644 c/false.c create mode 100644 c/pid.c create mode 100644 c/true.c create mode 100644 c/uname.c (limited to 'c') diff --git a/c/cat.c b/c/cat.c new file mode 100644 index 0000000..4092778 --- /dev/null +++ b/c/cat.c @@ -0,0 +1,10 @@ +#include "gbos.h" +/* cat: copy stdin to stdout until EOF (Ctrl-D / pipe end). */ +void main(void) { + char c; + for (;;) { + c = readc(); + if (c == EOF) break; + putc(c); + } +} diff --git a/c/crt0.s b/c/crt0.s index cde4b67..0d8371f 100644 --- a/c/crt0.s +++ b/c/crt0.s @@ -1,11 +1,11 @@ .module crt0 .globl _main - ;; gbos C entry. exec() maps our ROM bank and jumps to the start of _CODE - ;; (linked at 0x4000). Call main(), then exit(0). exec() already set SP. + ;; gbos C entry. exec() maps our ROM bank and jumps here ($4000). + ;; Call main(), then exit(0). (Args are fetched on demand via getargs().) .area _CODE _start:: call _main ld b, #0 - ld c, #0 ; SYS_EXIT + ld c, #0 ; SYS_EXIT rst #0x30 1$: jr 1$ diff --git a/c/echo.c b/c/echo.c new file mode 100644 index 0000000..f8d5125 --- /dev/null +++ b/c/echo.c @@ -0,0 +1,5 @@ +#include "gbos.h" +void main(void) { + puts(getargs()); + nl(); +} diff --git a/c/false.c b/c/false.c new file mode 100644 index 0000000..a124951 --- /dev/null +++ b/c/false.c @@ -0,0 +1,2 @@ +#include "gbos.h" +void main(void) { sexit(1); } diff --git a/c/gbos.h b/c/gbos.h index 9a09a5d..3a10e06 100644 --- a/c/gbos.h +++ b/c/gbos.h @@ -1,9 +1,15 @@ -/* gbos userland C API (thin wrappers over rst $30 syscalls; see c/libc.asm) */ +/* gbos userland C API (thin wrappers over rst $30 syscalls; see c/libc.s) */ #ifndef GBOS_H #define GBOS_H -void writes(const char *buf, unsigned char len); /* write to console */ -void nl(void); /* newline (CRLF) */ -unsigned char readc(void); /* read one byte */ -void sexit(unsigned char code); /* exit(code) */ +#define EOF 4 /* readc() returns this at end of input */ + +void writes(const char *buf, unsigned char len); +void putc(char c); +void puts(const char *s); /* write a NUL-terminated string */ +void nl(void); /* newline (CRLF) */ +unsigned char strlen(const char *s); +char *getargs(void); /* this program's argument string */ +char readc(void); /* one input byte, EOF at end */ +void sexit(unsigned char code); unsigned char getpid(void); #endif diff --git a/c/libc.s b/c/libc.s index 4fca5e3..29031e9 100644 --- a/c/libc.s +++ b/c/libc.s @@ -1,19 +1,48 @@ .module libc - .globl _writes, _nl, _readc, _sexit, _getpid + .globl _writes, _putc, _puts, _nl, _strlen, _readc, _sexit, _getpid, _getargs + ;; SDCC sm83 sdcccall(1): 1st arg -> A (byte) or DE (pointer); ret A / BC. + ;; rst $30 (the trap) clobbers BC/DE/HL; SDCC treats them caller-saved, so + ;; wrappers need not preserve them - but we compute cleanly regardless. .area _CODE + ;; void writes(const char *buf /*DE*/, unsigned char len /*A*/) _writes: - push hl - push de - push bc ld b, a - ld c, #3 ; SYS_WRITE + ld c, #3 ; SYS_WRITE rst #0x30 - pop bc - pop de - pop hl ret - ;; void nl(void) - CRLF straight to serial + + ;; char *getargs(void) -> BC : the arg string (command line past word 1). + ;; The shell leaves "cmd\0args\0" at 0xA000, inherited by the child via fork. +_getargs: + ld hl, #0xa000 +1$: ld a, (hl) + inc hl + or a + jr nz, 1$ + ld b, h + ld c, l + ret + + ;; void putc(char c /*A*/) +_putc: + ld (0xff01), a + ld a, #0x81 + ld (0xff02), a + ret + + ;; void puts(const char *s /*DE*/) - write until NUL (no newline) +_puts: +1$: ld a, (de) + or a + ret z + ld (0xff01), a + ld a, #0x81 + ld (0xff02), a + inc de + jr 1$ + + ;; void nl(void) - CRLF _nl: ld a, #0x0d ld (0xff01), a @@ -24,31 +53,34 @@ _nl: ld a, #0x81 ld (0xff02), a ret - ;; unsigned char readc(void) -> A + + ;; unsigned char strlen(const char *s /*DE*/) -> A +_strlen: + ld c, #0 +1$: ld a, (de) + or a + jr z, 2$ + inc c + inc de + jr 1$ +2$: ld a, c + ret + + ;; char readc(void) -> A (4 = EOF) _readc: - push hl - push de - push bc - ld c, #2 ; SYS_READ + ld c, #2 ; SYS_READ rst #0x30 - pop bc - pop de - pop hl ret - ;; void sexit(unsigned char code /*A*/) + + ;; void sexit(unsigned char code /*A*/) - never returns _sexit: ld b, a - ld c, #0 ; SYS_EXIT + ld c, #0 ; SYS_EXIT rst #0x30 ret + ;; unsigned char getpid(void) -> A _getpid: - push hl - push de - push bc - ld c, #8 ; SYS_GETPID + ld c, #8 ; SYS_GETPID rst #0x30 - pop bc - pop de - pop hl ret diff --git a/c/pid.c b/c/pid.c new file mode 100644 index 0000000..a282a89 --- /dev/null +++ b/c/pid.c @@ -0,0 +1,9 @@ +#include "gbos.h" +/* print an unsigned byte as decimal (no runtime division: subtract-based) */ +static void putu(unsigned char n) { + char buf[3]; unsigned char i = 0; + if (n == 0) { putc('0'); return; } + while (n) { buf[i++] = '0' + (n % 10); n = n / 10; } + while (i) putc(buf[--i]); +} +void main(void) { putu(getpid()); nl(); } diff --git a/c/true.c b/c/true.c new file mode 100644 index 0000000..4cf437e --- /dev/null +++ b/c/true.c @@ -0,0 +1,2 @@ +#include "gbos.h" +void main(void) { sexit(0); } diff --git a/c/uname.c b/c/uname.c new file mode 100644 index 0000000..511501d --- /dev/null +++ b/c/uname.c @@ -0,0 +1,2 @@ +#include "gbos.h" +void main(void) { puts("gbos sm83 (Game Boy Color)"); nl(); } -- cgit v1.3.1-sl0p