aboutsummaryrefslogtreecommitdiffstats
path: root/c
diff options
context:
space:
mode:
authoruser <user@clank>2026-07-17 01:39:29 +0200
committeruser <user@clank>2026-07-17 01:39:29 +0200
commitc4b9a1b1c4f8c211f765a089b34b66a2d046f3be (patch)
tree2f9245dc8c0741a8e547cc8ae77073cf2d5c56d9 /c
parentnet: wget - real HTTP over the link port via the gateway (diff)
downloadgbos-c4b9a1b1c4f8c211f765a089b34b66a2d046f3be.tar.gz
gbos-c4b9a1b1c4f8c211f765a089b34b66a2d046f3be.tar.xz
gbos-c4b9a1b1c4f8c211f765a089b34b66a2d046f3be.zip
net: chat client - async receive + OSK send over the link port
The application layer of the link-port demo, and it ties the whole system together: the LCD terminal displays, the on-screen keyboard types, and the link port carries a live chat. Kernel: sys_srecv_nb (non-blocking link receive; A=byte, CF=none) and sys_pollin (poll the OSK for a typed char without blocking) - syscalls 31/32. Both are what a poll loop needs to receive and type at once. Userland: c/chat.c runs a poll loop - it feeds non-blocking bytes through a SLIP receive state machine and prints whole incoming frames as messages, while pollin() drives the on-screen keyboard; SELECT shows the keys, type a line, START sends it as a frame. libc srecv_nb()/pollin(). Host: tools/gateway.py --mode chat is a simple bot peer (echoes each GB message and injects a few async ones); --keys can drive the OSK for tests. Verified: the gateway pushes 'welcome', '<alice> hey gameboy!', '<bob> nice link cable' unprompted and the GB displays all three (async receive); typing 'hi' on the OSK echoes it and emits the SLIP frame \xC0hi\xC0 (send). A Game Boy in the chat, keyboard on screen, over the link cable.
Diffstat (limited to 'c')
-rw-r--r--c/chat.c57
-rw-r--r--c/gbos.h2
-rw-r--r--c/libc.s19
3 files changed, 77 insertions, 1 deletions
diff --git a/c/chat.c b/c/chat.c
new file mode 100644
index 0000000..987b336
--- /dev/null
+++ b/c/chat.c
@@ -0,0 +1,57 @@
+#include "netlib.h"
+/* chat: a link-port chat client. Incoming SLIP frames from the host gateway
+ print as messages; SELECT brings up the on-screen keyboard, type a line and
+ press START to send it. A poll loop interleaves receiving and typing so
+ messages arrive while you're not mid-line. */
+
+static char rxbuf[96];
+static unsigned char rxlen, rxin, rxesc;
+
+/* feed one non-blocking byte to the SLIP receiver; returns a frame length when
+ a whole frame has arrived (copied into out), else 0. */
+static unsigned char slip_poll(char *out) {
+ int b = srecv_nb();
+ unsigned char c, i, n;
+ if (b < 0) return 0;
+ c = (unsigned char)b;
+ if (c == SLIP_END) {
+ if (rxin && rxlen) {
+ n = rxlen;
+ for (i = 0; i < n; i++) out[i] = rxbuf[i];
+ rxlen = 0; rxin = 0; rxesc = 0;
+ return n;
+ }
+ rxin = 1; rxlen = 0; rxesc = 0;
+ return 0;
+ }
+ if (!rxin) return 0;
+ if (rxesc) {
+ if (c == SLIP_ESC_END) c = SLIP_END;
+ else if (c == SLIP_ESC_ESC) c = SLIP_ESC;
+ rxesc = 0;
+ } else if (c == SLIP_ESC) { rxesc = 1; return 0; }
+ if (rxlen < 96) rxbuf[rxlen++] = (char)c;
+ return 0;
+}
+
+void main(void) {
+ char line[64], frame[96];
+ unsigned char linelen = 0, n, i, c;
+ puts("chat: SELECT=keyboard START=send"); nl();
+ for (;;) {
+ n = slip_poll(frame); /* incoming message? */
+ if (n) {
+ for (i = 0; i < n; i++) putc(frame[i]);
+ nl();
+ }
+ c = pollin(); /* typed a key? */
+ if (c == '\n') {
+ if (linelen) { slip_send(line, linelen); linelen = 0; }
+ } else if (c == 8) {
+ if (linelen) { linelen--; putc(8); }
+ } else if (c) {
+ if (linelen < 63) { line[linelen++] = (char)c; putc((char)c); }
+ }
+ yield();
+ }
+}
diff --git a/c/gbos.h b/c/gbos.h
index d207a75..df52fbe 100644
--- a/c/gbos.h
+++ b/c/gbos.h
@@ -15,6 +15,8 @@ unsigned char getpid(void);
unsigned char pipe(void); /* -> read fd; write end is read+1 */
void ssend(unsigned char b); /* link-port: transmit one byte */
unsigned char srecv(void); /* link-port: receive one byte (blocks) */
+int srecv_nb(void); /* link-port: receive, or -1 if none */
+unsigned char pollin(void); /* poll OSK for a typed char, or 0 */
/* helpers (c/libc.c) */
void putu(unsigned int n); /* print decimal */
diff --git a/c/libc.s b/c/libc.s
index 50d7860..7897a79 100644
--- a/c/libc.s
+++ b/c/libc.s
@@ -1,6 +1,6 @@
.module libc
.globl _writes, _putc, _puts, _nl, _strlen, _readc, _sexit, _getpid, _getargs
- .globl _open, _close, _fgetc, _fputc, _flist, _fremove, _pipe, _ssend, _srecv
+ .globl _open, _close, _fgetc, _fputc, _flist, _fremove, _pipe, _ssend, _srecv, _srecv_nb, _pollin
;; 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.
@@ -105,6 +105,23 @@ _srecv:
rst #0x30
ret
+ ;; int srecv_nb(void) -> byte, or -1 if none (non-blocking)
+_srecv_nb:
+ ld c, #31 ; SYS_SRECVNB
+ rst #0x30
+ jr c, 1$
+ ld c, a
+ ld b, #0
+ ret
+1$: ld bc, #0xffff
+ ret
+
+ ;; unsigned char pollin(void) -> A (typed OSK char, or 0)
+_pollin:
+ ld c, #32 ; SYS_POLLIN
+ rst #0x30
+ ret
+
;; unsigned char open(const char *name /*DE*/, unsigned char mode /*A*/) -> A(fd)
_open:
ld b, a ; B = mode