diff options
| author | user <user@clank> | 2026-07-17 01:11:11 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-07-17 01:11:11 +0200 |
| commit | 41dd342751541ab843262c13420e08db1a0035b0 (patch) | |
| tree | b06d974c75ced81babf07f552e8a3a3aa4658872 | |
| parent | kernel+sh: real anonymous pipes (concurrent, streaming, SIGPIPE) (diff) | |
| download | gbos-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-- | include/gbos.inc | 2 | ||||
| -rw-r--r-- | src/pipe.asm | 31 |
2 files changed, 23 insertions, 10 deletions
diff --git a/include/gbos.inc b/include/gbos.inc index aca92e3..bd7fbc2 100644 --- a/include/gbos.inc +++ b/include/gbos.inc @@ -181,7 +181,7 @@ DEF OF_POS EQU 3 DEF OF_SIZE EQU 5 ; anonymous pipes -DEF PIPE_MAX EQU 2 ; concurrent pipes +DEF PIPE_MAX EQU 4 ; concurrent pipes (up to 5-stage pipelines) DEF PIPE_BUFSZ EQU 64 ; ring buffer bytes per pipe DEF PIPE_FD_BASE EQU $F0 ; pipe fds: $F0+idx*2 (read), +1 (write); $FF=console 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 |
