diff options
| author | user <user@clank> | 2026-07-17 01:04:16 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-07-17 01:04:16 +0200 |
| commit | 73295e7acc1e5ec3ee8814fd4ccc45f7cee63392 (patch) | |
| tree | 88f66fb3db17e8ee755b2d2a408ce232292f9248 /src/pipe.asm | |
| parent | term: compute the OSK view offset from the cursor row (keep input visible) (diff) | |
| download | gbos-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 '')
| -rw-r--r-- | src/pipe.asm | 215 |
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 |
