diff options
| author | user <user@clank> | 2026-07-17 13:43:10 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-07-17 13:43:10 +0200 |
| commit | b52f37602968768901d2dd83ccf966d953a532f0 (patch) | |
| tree | d497c52bf672291d1cd9cf0579b6191155cb56d4 | |
| parent | net: keep network packets out of the console (fixes shell garbage on netboot) (diff) | |
| download | gbos-b52f37602968768901d2dd83ccf966d953a532f0.tar.gz gbos-b52f37602968768901d2dd83ccf966d953a532f0.tar.xz gbos-b52f37602968768901d2dd83ccf966d953a532f0.zip | |
kernel: sys_sleep - a cooperative delay off the DIV timer (+ ping pacing)
There was no time source at all (IRQ vectors just reti; scheduler is purely
cooperative). The DIV register (FF04) free-runs at 16384 Hz regardless of
interrupts, so sys_sleep accumulates DIV deltas across SchedYields (other procs
keep running) until the requested number of 1/64-second units elapse.
- SYS_SLEEP(34): B = 1/64s units; libc gsleep(units) / msleep(ms) wrappers.
- ping now msleep(800) between echoes, so it paces like real ping instead of
blasting all four at once.
Verified real-time (capped emulator): replies land ~0.85s apart. In --uncapped
runs the delay is GB-time (fast wall-clock), as expected.
| -rw-r--r-- | c/gbos.h | 2 | ||||
| -rw-r--r-- | c/libc.c | 9 | ||||
| -rw-r--r-- | c/libc.s | 9 | ||||
| -rw-r--r-- | c/ping.c | 1 | ||||
| -rw-r--r-- | include/gbos.inc | 4 | ||||
| -rw-r--r-- | src/kdata.asm | 3 | ||||
| -rw-r--r-- | src/syscall.asm | 41 |
7 files changed, 67 insertions, 2 deletions
@@ -18,6 +18,8 @@ unsigned char srecv(void); /* link-port: receive one byte (blocks) int srecv_nb(void); /* link-port: receive, or -1 if none */ unsigned char pollin(void); /* poll OSK for a typed char, or 0 */ unsigned char sys_net(void *req); /* socket layer trap (see sock.h) */ +void gsleep(unsigned char units); /* delay units*(1/64)s (raw trap) */ +void msleep(unsigned int ms); /* delay ~ms milliseconds */ /* helpers (c/libc.c) */ void putu(unsigned int n); /* print decimal */ @@ -1,6 +1,15 @@ /* gbos libc C helpers (compiled once, linked into every program). */ #include "gbos.h" +/* sleep for approximately 'ms' milliseconds (cooperative; other procs run). + The kernel counts in 1/64-second units (~15.6 ms), so we round ms to that. */ +void msleep(unsigned int ms) { + unsigned int u = ms >> 4; /* ~ms/15.6, close enough for a delay */ + if (u > 255) u = 255; + if (u == 0 && ms) u = 1; + gsleep((unsigned char)u); +} + /* print an unsigned int in decimal */ void putu(unsigned int n) { char buf[5]; @@ -1,7 +1,7 @@ .module libc .globl _writes, _putc, _puts, _nl, _strlen, _readc, _sexit, _getpid, _getargs .globl _open, _close, _fgetc, _fputc, _flist, _fremove, _pipe, _ssend, _srecv, _srecv_nb, _pollin - .globl _sys_net + .globl _sys_net, _gsleep ;; SDCC sm83 sdcccall(1): 1st arg -> A (byte) or DE (pointer); ret A / BC. ;; rst $30 (the trap) clobbers BC/DE/HL; SDCC treats them caller-saved, so ;; wrappers need not preserve them - but we compute cleanly regardless. @@ -123,6 +123,13 @@ _sys_net: rst #0x30 ret + ;; void gsleep(unsigned char units /*A*/) - delay units*(1/64)s +_gsleep: + ld b, a + ld c, #34 ; SYS_SLEEP + rst #0x30 + ret + ;; unsigned char pollin(void) -> A (typed OSK char, or 0) _pollin: ld c, #32 ; SYS_POLLIN @@ -47,6 +47,7 @@ void main(void) { } else { puts(" seq="); putu(seq); puts(" timeout"); nl(); } + if (seq < 4) msleep(800); /* pace like real ping */ } puts("-- "); putu(got); puts("/4 received"); nl(); net_close(s); diff --git a/include/gbos.inc b/include/gbos.inc index 74eee8e..049a4bc 100644 --- a/include/gbos.inc +++ b/include/gbos.inc @@ -10,6 +10,7 @@ DEF GBOS_INC EQU 1 ; ----------------------------------------------------------------------------- DEF rIF EQU $FF0F ; interrupt flags DEF rIE EQU $FFFF ; interrupt enable +DEF rDIV EQU $FF04 ; divider register: free-running 8-bit @ 16384 Hz DEF rSVBK EQU $FF70 ; CGB WRAM bank select ($D000-$DFFF): 1..7 DEF wCurMask EQU $D589 ; term/osk: cursor underline bits OR'd into a tile's last row DEF OSK_DOCK EQU 3 ; on-screen keyboard height in rows (dock at the bottom) @@ -131,7 +132,8 @@ DEF SYS_SRECV EQU 30 ; link-port: receive one byte (-> A=byte, blocks) DEF SYS_SRECVNB EQU 31 ; link-port: receive, non-blocking (-> A=byte, CF=none) DEF SYS_POLLIN EQU 32 ; poll OSK for a typed char (-> A=char or 0) DEF SYS_NET EQU 33 ; socket layer (DE=&netreq -> A=result) -DEF SYS_MAX EQU 34 +DEF SYS_SLEEP EQU 34 ; cooperative delay (B = 1/64-second units) +DEF SYS_MAX EQU 35 ; ---- socket layer (SYS_NET) -------------------------------------------------- ; netreq struct (in caller RAM; DE points to it): diff --git a/src/kdata.asm b/src/kdata.asm index 6333f38..eece708 100644 --- a/src/kdata.asm +++ b/src/kdata.asm @@ -57,6 +57,9 @@ 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 wKillParent::DS 1 ; scratch for sys_kill wPsBuf:: DS 2 ; scratch for sys_ps wPnStart:: DS 2 ; scratch for sys_progname diff --git a/src/syscall.asm b/src/syscall.asm index 04e9661..1051a4d 100644 --- a/src/syscall.asm +++ b/src/syscall.asm @@ -68,6 +68,7 @@ SyscallTable: dw sys_srecv_nb ; 31 SRECVNB dw sys_pollin ; 32 POLLIN dw sys_net ; 33 NET + dw sys_sleep ; 34 SLEEP ; ----------------------------------------------------------------------------- sys_nosys: @@ -279,6 +280,46 @@ sys_yield: ret ; ----------------------------------------------------------------------------- +; sys_sleep(B = 1/64-second units) - cooperative delay driven by the DIV timer. +; DIV (FF04) free-runs at 16384 Hz, so 256 ticks = 1/64 s. We accumulate DIV +; deltas across SchedYields (other procs run) until enough units have elapsed. +; ----------------------------------------------------------------------------- +sys_sleep: + ld a, b + or a + ret z ; sleep 0 = no-op + ld [wSleepTarget], a + xor a + ld [wSleepAcc], a + ld [wSleepAcc+1], a + ld a, [rDIV] + ld [wSleepLastDiv], a +.loop + call SchedYield + ld a, [rDIV] + ld c, a ; current DIV + ld a, [wSleepLastDiv] + ld b, a ; previous DIV + ld a, c + ld [wSleepLastDiv], a + sub b ; A = DIV delta (mod 256) + ld e, a + ld a, [wSleepAcc] ; acc += delta + add e + ld [wSleepAcc], a + ld a, [wSleepAcc+1] + adc 0 + ld [wSleepAcc+1], a ; acc high byte = elapsed 1/64-second units + ld b, a + ld a, [wSleepTarget] + cp b + jr z, .done ; elapsed == target + jr c, .done ; elapsed > target + jr .loop +.done + ret + +; ----------------------------------------------------------------------------- ; sys_exit(code=B) -- become a zombie awaiting reap; never returns. ; - store exit code, set PS_ZOMBIE ; - reparent any children to init (so they can still be waited on) |
