aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authoruser <user@clank>2026-07-17 15:12:11 +0200
committeruser <user@clank>2026-07-17 15:12:11 +0200
commitf68fc872755189bc287a5f7ca838dbd9f45c8353 (patch)
tree4911c8836326d2c8c8431f4bd528a61a5c839f28 /src
parenttools: gbhub --daemon + gbjoin - interactive multi-Game-Boy networking (diff)
downloadgbos-f68fc872755189bc287a5f7ca838dbd9f45c8353.tar.gz
gbos-f68fc872755189bc287a5f7ca838dbd9f45c8353.tar.xz
gbos-f68fc872755189bc287a5f7ca838dbd9f45c8353.zip
net: pump the network from the console wait (answer pings at the prompt)
A Game Boy sitting at the shell prompt now services the network instead of being deaf until you run netd: KGetc (the console input wait) pumps net_pump each poll, so inbound pings are auto-answered while idle. net_pump gained an in-frame model and routes any non-framed link bytes to a small console-input ring (con_push/con_pop), so a headless-injected command still reaches the shell while SLIP frames go to the stack. Removes the old SLIP-skip-in-KGetc hack. Verified: two Game Boys on the hub, GB0 idle at the prompt (no netd), GB1 `ping 10.0.0.2` -> 4/4 replies, routed GB1->hub->GB0->hub->GB1. (Separate, pre-existing: gbhub *spawn* mode and windowed gbjoin can drop an idle link socket - under investigation; daemon mode + headless is solid.)
Diffstat (limited to '')
-rw-r--r--src/kdata.asm1
-rw-r--r--src/socket.asm88
-rw-r--r--src/syscall.asm38
3 files changed, 85 insertions, 42 deletions
diff --git a/src/kdata.asm b/src/kdata.asm
index eece708..83ed162 100644
--- a/src/kdata.asm
+++ b/src/kdata.asm
@@ -56,7 +56,6 @@ wFsPath:: DS 40 ; kernel-side copy of a path argument
wFsSlashPtr::DS 2 ; last '/' seen while splitting a path
wListDir:: DS 1 ; directory sys_list enumerates
wKChar:: DS 1 ; scratch for KPutc
-wConInSkip:: DS 1 ; console input: mid-SLIP-frame? (skip net packets)
wSleepTarget::DS 1 ; sys_sleep: target in 1/64-second units
wSleepAcc:: DS 2 ; sys_sleep: accumulated DIV ticks
wSleepLastDiv::DS 1 ; sys_sleep: previous DIV reading
diff --git a/src/socket.asm b/src/socket.asm
index 3b2b0c9..fa6b245 100644
--- a/src/socket.asm
+++ b/src/socket.asm
@@ -52,6 +52,10 @@ wNetTx:: DS 320 ; tx build buffer
wNetRxBuf:: DS 320 ; rx frame assembly / parse
wNetRxLen:: DS 2 ; 16-bit: frames can exceed 255 bytes (DHCP/BOOTP)
wNetRxEsc:: DS 1
+wNetInFrame:: DS 1 ; net_pump: currently inside a SLIP frame?
+wConRing:: DS 16 ; console-input ring: non-framed bytes from the link
+wConHead:: DS 1
+wConTail:: DS 1
wNetUdpPort:: DS 2 ; scratch: UDP dst port being demuxed
wNetTO:: DS 3 ; recv timeout counter (net_pump clobbers registers)
wNetTcpFlags:: DS 1 ; TCP flags for the segment being built
@@ -82,9 +86,56 @@ net_init::
ld [wNetRxLen], a
ld [wNetRxLen+1], a
ld [wNetRxEsc], a
+ ld [wNetInFrame], a
+ ld [wConHead], a
+ ld [wConTail], a
ld [wNetSeq], a
ret
+; con_push(A) - queue a console-input byte (dropped if the ring is full)
+con_push::
+ ld b, a
+ ld a, [wConTail]
+ inc a
+ and 15
+ ld c, a
+ ld a, [wConHead]
+ cp c
+ ret z ; full -> drop
+ ld a, [wConTail]
+ ld e, a
+ ld d, 0
+ ld hl, wConRing
+ add hl, de
+ ld [hl], b
+ ld a, c
+ ld [wConTail], a
+ ret
+
+; con_pop -> A = byte, CF set if the ring is empty
+con_pop::
+ ld a, [wConHead]
+ ld b, a
+ ld a, [wConTail]
+ cp b
+ jr nz, .have
+ scf
+ ret
+.have
+ ld a, b
+ ld e, a
+ ld d, 0
+ ld hl, wConRing
+ add hl, de
+ ld c, [hl]
+ ld a, b
+ inc a
+ and 15
+ ld [wConHead], a
+ ld a, c
+ and a ; CF = 0
+ ret
+
; net_setip - store our IPv4 address (from wNetReq.NR_IP). Used by the DHCP
; client once it has a lease.
net_op_setip:
@@ -757,14 +808,20 @@ udp_send:
; net_pump - read available serial bytes, assemble SLIP frames, process each.
; Non-blocking: returns when the link has no more bytes ready.
; =============================================================================
-net_pump:
+net_pump::
.next
call net_getbyte_nb
ret c ; no byte ready
- ; SLIP decode using wNetRxLen / wNetRxEsc
cp SLIP_END
- jr z, .frame
+ jr z, .delim
ld c, a ; save byte
+ ld a, [wNetInFrame]
+ or a
+ jr nz, .inframe
+ ld a, c ; outside a frame -> console input
+ call con_push
+ jr .next
+.inframe
ld a, [wNetRxEsc]
or a
jr nz, .escaped
@@ -816,25 +873,36 @@ net_pump:
ld a, [wNetRxLen] ; index++
inc a
ld [wNetRxLen], a
- jr nz, .next
+ jp nz, .next
ld a, [wNetRxLen+1]
inc a
ld [wNetRxLen+1], a
- jr .next
-.frame
- ; END: if we have a frame, process it and return (one frame per pump, so a
- ; recv drains each segment before the next is delivered - no rx overwrite).
+ jp .next
+.delim
+ ; 0xC0 delimiter: end the current frame (process it) or start a new one.
xor a
ld [wNetRxEsc], a
+ ld a, [wNetInFrame]
+ or a
+ jr z, .startframe
ld a, [wNetRxLen]
ld hl, wNetRxLen+1
or [hl]
- jr z, .next ; empty frame, keep reading
+ jr z, .resetframe ; empty frame
call process_frame
+.resetframe
xor a
+ ld [wNetInFrame], a
ld [wNetRxLen], a
ld [wNetRxLen+1], a
- ret
+ ret ; one frame per pump
+.startframe
+ ld a, 1
+ ld [wNetInFrame], a
+ xor a
+ ld [wNetRxLen], a
+ ld [wNetRxLen+1], a
+ jp .next
; -----------------------------------------------------------------------------
; process_frame - IPv4 demux of the frame in wNetRxBuf (wNetRxLen bytes).
diff --git a/src/syscall.asm b/src/syscall.asm
index 1051a4d..2adf5bf 100644
--- a/src/syscall.asm
+++ b/src/syscall.asm
@@ -132,41 +132,17 @@ KGetc::
call pad_poll ; poll joypad; drive the on-screen keyboard
call osk_handle ; CF set + A = byte if a key was typed
jr c, .done
- ld a, $80
- ld [rSC], a
- ld a, [rSC]
- bit 7, a
- jr nz, .yield
- ld a, [rSB] ; A = serial byte
- ; The link port is also the network. Skip SLIP frames (0xC0-delimited) so
- ; inbound packets never surface as console input; plain bytes (an injected
- ; command in headless use) still pass through.
- ld b, a
- ld a, [wConInSkip]
- or a
- jr nz, .inframe
- ld a, b
- cp SLIP_END
- jr z, .frameopen
- ld a, b ; ordinary byte -> console input
- jr .done
-.frameopen
- ld a, 1
- ld [wConInSkip], a
- jr .poll
-.inframe
- ld a, b
- cp SLIP_END
- jr nz, .poll ; still inside a frame -> discard
- xor a
- ld [wConInSkip], a ; frame end -> resume
+ ; The link port is the network. Pump it here so the Game Boy services the
+ ; net (answers pings, etc.) while sitting at the prompt; net_pump routes any
+ ; non-framed link bytes (a headless-injected command) to the console ring.
+ call net_pump
+ call con_pop ; queued console byte? CF clear -> return it
+ jr nc, .done
+ call SchedYield
jr .poll
.done
and a ; CF = 0 (not EOF)
ret
-.yield
- call SchedYield
- jr .poll
; -----------------------------------------------------------------------------
; KPutc(E = byte) - write to this process's stdout (console/file).