aboutsummaryrefslogtreecommitdiffstats
path: root/src/pipe.asm
diff options
context:
space:
mode:
authoruser <user@clank>2026-07-17 01:11:11 +0200
committeruser <user@clank>2026-07-17 01:11:11 +0200
commit41dd342751541ab843262c13420e08db1a0035b0 (patch)
treeb06d974c75ced81babf07f552e8a3a3aa4658872 /src/pipe.asm
parentkernel+sh: real anonymous pipes (concurrent, streaming, SIGPIPE) (diff)
downloadgbos-41dd342751541ab843262c13420e08db1a0035b0.tar.gz
gbos-41dd342751541ab843262c13420e08db1a0035b0.tar.xz
gbos-41dd342751541ab843262c13420e08db1a0035b0.zip
pipe: PIPE_MAX 2->4 (5-stage pipelines) + fix cross-yield byte corruption
Bump PIPE_MAX to 4 so a|b|c|d|e (4 pipes) works. This stays within the single-byte buffer-offset math (idx*64+pos <= 3*64+63 = 255) and the fd space ($F0..$F7, clear of $FF console). The bump exposed a data-corruption bug that also affected the 2-pipe case (just invisibly - a wc-only test can't see mangled bytes): pipe_write kept the byte-to-write in the SHARED wPipeByte global across its SchedYield (buffer full), so a concurrent pipe op clobbered it and the writer then stored the wrong byte. Now the byte is held in D across the yield, and pipe_bufptr no longer clobbers D; pipe_read/pipe_write also push their idx across SchedYield rather than assume the yield preserves registers. Verified: count N | cat now streams EXACT content (no 'linn'/'llne' corruption); count 60 | cat | cat | wc = 60 180 1671; 5-stage count 4 | cat | cat | cat | cat prints line 1..4; SIGPIPE (count 200|true) and count 100|wc still fine, no hangs.
Diffstat (limited to '')
-rw-r--r--src/pipe.asm31
1 files changed, 22 insertions, 9 deletions
diff --git a/src/pipe.asm b/src/pipe.asm
index f9473a4..1a77c5e 100644
--- a/src/pipe.asm
+++ b/src/pipe.asm
@@ -108,19 +108,23 @@ pipe_close::
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 - C = idx, A = pos -> HL = &wPipeBuf[idx*PIPE_BUFSZ + pos].
+; (idx*64 + pos <= 255, so the whole offset fits in one byte.) Preserves B,C,D.
pipe_bufptr:
ld e, a ; E = pos
ld a, c
swap a
add a
add a ; A = idx*64
- add e ; + pos
+ add e ; + pos (0..255)
ld e, a
- ld d, 0
ld hl, wPipeBuf
- add hl, de
+ ld a, l
+ add e
+ ld l, a
+ ld a, h
+ adc 0
+ ld h, a
ret
; pipe_read - A = read fd -> A = byte, CF set on EOF. Blocks while empty w/ writer.
@@ -140,7 +144,9 @@ pipe_read::
ld a, [hl]
or a
jr z, .eof ; none -> EOF
- call SchedYield ; block: let the writer run
+ push bc ; block: let the writer run (keep idx)
+ call SchedYield
+ pop bc
jr .retry
.data
ld hl, wPipeHead
@@ -175,6 +181,9 @@ pipe_write::
sub PIPE_FD_BASE
srl a
ld c, a ; C = idx
+ ld a, [wPipeByte]
+ ld d, a ; D = byte (must survive yields; wPipeByte
+ ; is shared and gets clobbered by others)
.retry
ld hl, wPipeCnt
ld b, 0
@@ -187,15 +196,19 @@ pipe_write::
ld a, [hl]
or a
jr z, .epipe ; none -> SIGPIPE
- call SchedYield ; block: let the reader drain
+ push bc ; block: let the reader drain (keep idx+byte)
+ push de
+ call SchedYield
+ pop de
+ pop bc
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]
+ call pipe_bufptr ; A=tail, C=idx -> HL (preserves D)
+ ld a, d ; the byte
ld [hl], a
pop af ; tail
inc a