aboutsummaryrefslogtreecommitdiffstats
path: root/src/pipe.asm
diff options
context:
space:
mode:
authoruser <user@clank>2026-07-17 01:04:16 +0200
committeruser <user@clank>2026-07-17 01:04:16 +0200
commit73295e7acc1e5ec3ee8814fd4ccc45f7cee63392 (patch)
tree88f66fb3db17e8ee755b2d2a408ce232292f9248 /src/pipe.asm
parentterm: compute the OSK view offset from the cursor row (keep input visible) (diff)
downloadgbos-73295e7acc1e5ec3ee8814fd4ccc45f7cee63392.tar.gz
gbos-73295e7acc1e5ec3ee8814fd4ccc45f7cee63392.tar.xz
gbos-73295e7acc1e5ec3ee8814fd4ccc45f7cee63392.zip
kernel+sh: real anonymous pipes (concurrent, streaming, SIGPIPE)
Replace the shell's temp-file pipe hack with proper in-kernel FIFOs. Kernel (src/pipe.asm, new): - A small pool of bounded ring buffers (PIPE_MAX=2, 64 B each) with writers/readers refcounts. Pipe fds are $F0+idx*2 (read) / +1 (write); $FF stays "console". - SYS_PIPE allocates one (writers=readers=1) and returns the read fd (write = read+1). getb/putb/close dispatch pipe fds here; sys_exit drops the refcounts held as PROC_STDIN/PROC_STDOUT. - Blocking with SchedYield, which is the flow control: read blocks while empty with a writer (EOF once writers hit 0), write blocks while full with a reader, and if the last reader is gone the writer is killed (SIGPIPE -> exit 141). Cooperative-scheduler friendly. Shell (c/sh.c): - run_pipeline(): split on '|', make a pipe between adjacent stages, and fork ALL stages concurrently (no wait between), wiring stdin/stdout; then wait for all. Per-stage >/< still honored; orphaned pipe ends are closed on a lookup miss so EOF/EPIPE propagate. - Drop the __pipe temp file and its 2 KB / serialized limits. libc: pipe(). New c/ptest.c exercises the FIFO (write, read back, EOF). Now works (old version couldn't): multi-stage a|b|c; streaming beyond 2 KB (count 120 | wc = 3372 bytes through a 64 B buffer); early-exit SIGPIPE (count 200 | true kills count instead of hanging/overflowing a file).
Diffstat (limited to 'src/pipe.asm')
-rw-r--r--src/pipe.asm215
1 files changed, 215 insertions, 0 deletions
diff --git a/src/pipe.asm b/src/pipe.asm
new file mode 100644
index 0000000..f9473a4
--- /dev/null
+++ b/src/pipe.asm
@@ -0,0 +1,215 @@
+; =============================================================================
+; pipe.asm - anonymous pipes: bounded in-kernel FIFOs with blocking ends.
+;
+; A pipe fd is $F0 + idx*2 (+1 for the write end); $FF stays "console". getb/
+; putb/close dispatch pipe fds here. read blocks (yields) while empty and there
+; is still a writer, then returns EOF once all writers are gone; write blocks
+; while full and there is a reader, and if the last reader is gone the writer is
+; killed (SIGPIPE). refcounts (writers/readers) are set to 1 by pipe() - the one
+; producer and one consumer of a shell pipeline - and dropped on close/exit.
+; =============================================================================
+INCLUDE "include/gbos.inc"
+
+SECTION "pipe_bss", WRAM0
+wPipeW:: DS PIPE_MAX ; writers refcount (0 = end/free)
+wPipeR:: DS PIPE_MAX ; readers refcount
+wPipeHead:: DS PIPE_MAX ; read index
+wPipeTail:: DS PIPE_MAX ; write index
+wPipeCnt:: DS PIPE_MAX ; bytes buffered
+wPipeBuf:: DS PIPE_MAX * PIPE_BUFSZ ; ring buffers
+wPipeByte:: DS 1 ; scratch
+
+SECTION "pipe", ROM0
+
+; pipe_init - mark every pipe free (writers=readers=0). Called at boot.
+pipe_init::
+ ld hl, wPipeW
+ ld b, PIPE_MAX * 2 ; wPipeW followed by wPipeR
+ xor a
+.z
+ ld [hl+], a
+ dec b
+ jr nz, .z
+ ret
+
+; sys_pipe - allocate a pipe -> A = read fd (write = read+1), or $FF.
+sys_pipe::
+ jp pipe_alloc
+
+; is_pipe_fd - A = fd. CF set (carry) if it is NOT a pipe fd. Preserves A.
+is_pipe_fd::
+ cp PIPE_FD_BASE
+ ret c ; < base -> not a pipe (CF set)
+ cp PIPE_FD_BASE + PIPE_MAX*2
+ ccf ; >= top -> CF set (not pipe); else CF clear
+ ret
+
+; pipe_alloc - -> A = read fd, or $FF if none free. Empties + writers=readers=1.
+pipe_alloc::
+ ld c, 0
+.scan
+ ld hl, wPipeW
+ ld b, 0
+ add hl, bc
+ ld a, [hl]
+ or a
+ jr nz, .next
+ ld hl, wPipeR
+ add hl, bc
+ ld a, [hl]
+ or a
+ jr z, .found
+.next
+ inc c
+ ld a, c
+ cp PIPE_MAX
+ jr c, .scan
+ ld a, $FF
+ ret
+.found
+ ld b, 0
+ ld hl, wPipeW
+ add hl, bc
+ ld [hl], 1
+ ld hl, wPipeR
+ add hl, bc
+ ld [hl], 1
+ ld hl, wPipeHead
+ add hl, bc
+ ld [hl], 0
+ ld hl, wPipeTail
+ add hl, bc
+ ld [hl], 0
+ ld hl, wPipeCnt
+ add hl, bc
+ ld [hl], 0
+ ld a, c
+ add a ; idx*2
+ add PIPE_FD_BASE
+ ret
+
+; pipe_close - A = fd. If a pipe fd, drop the appropriate refcount.
+pipe_close::
+ call is_pipe_fd
+ ret c
+ sub PIPE_FD_BASE
+ ld c, a
+ srl c ; C = idx
+ and 1 ; A = 1 if write end
+ ld hl, wPipeR
+ jr z, .have
+ ld hl, wPipeW
+.have
+ ld b, 0
+ add hl, bc
+ ld a, [hl]
+ or a
+ ret z
+ dec [hl]
+ ret
+
+; pipe_bufptr - C = idx, A = pos -> HL = &wPipeBuf[idx*PIPE_BUFSZ + pos]. keeps BC.
+; (idx*64 + pos <= 255, so the whole offset fits in one byte.)
+pipe_bufptr:
+ ld e, a ; E = pos
+ ld a, c
+ swap a
+ add a
+ add a ; A = idx*64
+ add e ; + pos
+ ld e, a
+ ld d, 0
+ ld hl, wPipeBuf
+ add hl, de
+ ret
+
+; pipe_read - A = read fd -> A = byte, CF set on EOF. Blocks while empty w/ writer.
+pipe_read::
+ sub PIPE_FD_BASE
+ srl a
+ ld c, a ; C = idx
+.retry
+ ld hl, wPipeCnt
+ ld b, 0
+ add hl, bc
+ ld a, [hl]
+ or a
+ jr nz, .data
+ ld hl, wPipeW ; empty: any writers left?
+ add hl, bc
+ ld a, [hl]
+ or a
+ jr z, .eof ; none -> EOF
+ call SchedYield ; block: let the writer run
+ jr .retry
+.data
+ ld hl, wPipeHead
+ add hl, bc
+ ld a, [hl] ; head
+ push af
+ call pipe_bufptr ; A=head, C=idx -> HL = &buf byte
+ ld a, [hl]
+ ld [wPipeByte], a
+ pop af ; head
+ inc a
+ cp PIPE_BUFSZ
+ jr c, .hok
+ xor a
+.hok
+ ld hl, wPipeHead
+ add hl, bc
+ ld [hl], a
+ ld hl, wPipeCnt
+ add hl, bc
+ dec [hl]
+ ld a, [wPipeByte]
+ and a ; CF = 0
+ ret
+.eof
+ scf
+ ret
+
+; pipe_write - A = write fd, byte in wPipeByte. Blocks while full w/ reader;
+; SIGPIPE (exit) if no reader.
+pipe_write::
+ sub PIPE_FD_BASE
+ srl a
+ ld c, a ; C = idx
+.retry
+ ld hl, wPipeCnt
+ ld b, 0
+ add hl, bc
+ ld a, [hl]
+ cp PIPE_BUFSZ
+ jr c, .space
+ ld hl, wPipeR ; full: any readers left?
+ add hl, bc
+ ld a, [hl]
+ or a
+ jr z, .epipe ; none -> SIGPIPE
+ call SchedYield ; block: let the reader drain
+ jr .retry
+.space
+ ld hl, wPipeTail
+ add hl, bc
+ ld a, [hl] ; tail
+ push af
+ call pipe_bufptr ; A=tail, C=idx -> HL = &buf byte
+ ld a, [wPipeByte]
+ ld [hl], a
+ pop af ; tail
+ inc a
+ cp PIPE_BUFSZ
+ jr c, .tok
+ xor a
+.tok
+ ld hl, wPipeTail
+ add hl, bc
+ ld [hl], a
+ ld hl, wPipeCnt
+ add hl, bc
+ inc [hl]
+ ret
+.epipe
+ ld b, 141 ; 128 + SIGPIPE(13)
+ jp sys_exit