From 0cc9b0a8bdf774ee376937d5a6767ccdced51245 Mon Sep 17 00:00:00 2001 From: user Date: Sat, 18 Jul 2026 10:10:04 +0200 Subject: kernel: a real timer source (64 Hz tick IRQ) + uptime(1) The kernel had no clock: TimerISR was a reti stub, IEF_TIMER masked, and IME was never enabled - the vectors were decorative. sys_sleep just polls DIV deltas per-process; nothing counted globally. Now: TAC runs the hardware timer at 16384 Hz with TMA=0, so TIMA overflows at exactly 64 Hz; TimerISR increments a monotonic 32-bit wTicks (wraps after ~2.1 years). Scheduling stays cooperative - the ISR is transparent. Enabling IME in a kernel written for zero interrupts needs care wherever SP points into memory whose bank is being switched (an IRQ pushes onto SP): - read_block/write_block map the disk bank over the $A000 window that holds the caller's stack -> di/ei around the transfer (~2ms, well under the 15.6ms tick period, so no tick is ever lost) - hSwitchTo switches SVBK + cart-RAM banks under the outgoing stack -> di on entry, ei once the incoming stack is mapped - fork already runs on KSTACK_TOP2 (fixed WRAM) - safe as-is - term_putc's SVBK switch only remaps $Dxxx, stacks live in $Axxx/$Cxxx SYS_UPTIME (36) copies the counter (4B LE, di/ei so the read can't tear) to a user buffer; libc gticks(); usr/uptime.c formats 'up [Nd] H:MM:SS'. uptime avoids SDCC long div/shift entirely: sm83.lib modules link into their own areas that land in the $A000 RAM window (latent build.sh trap, documented there) - bytewise >>6 plus bounded subtraction loops instead. --- usr/build.sh | 7 ++++++- usr/gbos.h | 1 + usr/libc.s | 8 +++++++- usr/uptime.c | 40 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 usr/uptime.c (limited to 'usr') diff --git a/usr/build.sh b/usr/build.sh index 93815d2..5d494cf 100755 --- a/usr/build.sh +++ b/usr/build.sh @@ -10,7 +10,12 @@ sdasgb -o "$B/crt0.rel" usr/crt0.s sdasgb -o "$B/libc.rel" usr/libc.s sdcc -msm83 -c -Iusr usr/libc.c -o "$B/libc_c.rel" sdcc -msm83 -c -Iusr "$SRC" -o "$B/prog.rel" -# link: crt0 first (=> _start at 0x4000), then libc, program, and sm83 lib +# link: crt0 first (=> _start at 0x4000), then libc, program, and sm83 lib. +# TRAP: only _CODE/_DATA have pinned bases. sm83.lib modules (long div/mod/ +# shift helpers) link into their own areas, which sdldgb places after _DATA - +# i.e. code in the $A000 RAM window ("buffer too small" from makebin, or +# garbage at runtime). Don't use long division/shifts in programs; see +# usr/uptime.c for the inlined-arithmetic workaround. sdldgb -n -m -w -j -i "$B/prog.ihx" \ -b _CODE=0x4000 -b _DATA=0xa000 \ -k "$LIBDIR" -l sm83 \ diff --git a/usr/gbos.h b/usr/gbos.h index 2350665..5fd897a 100644 --- a/usr/gbos.h +++ b/usr/gbos.h @@ -21,6 +21,7 @@ unsigned char pollcon(void); /* poll console ring (link-injected input, filled by net pumps), 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 gticks(unsigned char buf[4]); /* 64 Hz uptime ticks, 32-bit LE */ void msleep(unsigned int ms); /* delay ~ms milliseconds */ /* helpers (c/libc.c) */ diff --git a/usr/libc.s b/usr/libc.s index 5b176d9..2366cd8 100644 --- a/usr/libc.s +++ b/usr/libc.s @@ -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, _pollcon - .globl _sys_net, _gsleep + .globl _sys_net, _gsleep, _gticks ;; 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. @@ -130,6 +130,12 @@ _gsleep: rst #0x30 ret + ;; void gticks(unsigned char buf[4] /*DE*/) - 64 Hz uptime ticks, LE +_gticks: + ld c, #36 ; SYS_UPTIME + rst #0x30 + ret + ;; unsigned char pollin(void) -> A (typed OSK char, or 0) _pollin: ld c, #32 ; SYS_POLLIN diff --git a/usr/uptime.c b/usr/uptime.c new file mode 100644 index 0000000..1062217 --- /dev/null +++ b/usr/uptime.c @@ -0,0 +1,40 @@ +#include "gbos.h" +/* uptime: time since boot, from the kernel's 64 Hz tick (SYS_UPTIME). + + No long division/shifts: SDCC pulls those from sm83.lib, whose modules + link into their own areas that land in the $A000 RAM window (see + usr/build.sh). Everything here is inlined 8/32-bit add/sub/compare: + ticks>>6 done bytewise, then day/hour/minute by bounded subtraction. */ + +static void two(unsigned int v) { /* zero-padded 2-digit field */ + if (v < 10) putc('0'); + putu(v); +} + +void main(void) { + unsigned char t[4]; + union { unsigned long l; unsigned char b[4]; } u; /* sm83 is LE */ + unsigned long sec; + unsigned int d, h, m; + + gticks(t); /* 32-bit LE tick count, 64 Hz */ + + /* seconds = ticks >> 6, composed a byte at a time (24 bits is 194 days) */ + u.b[0] = (unsigned char)((t[0] >> 6) | (t[1] << 2)); + u.b[1] = (unsigned char)((t[1] >> 6) | (t[2] << 2)); + u.b[2] = (unsigned char)((t[2] >> 6) | (t[3] << 2)); + u.b[3] = 0; + sec = u.l; + + d = 0; + while (sec >= 86400UL) { sec -= 86400UL; d++; } /* <= 194 rounds */ + h = 0; + while (sec >= 3600UL) { sec -= 3600UL; h++; } /* <= 23 rounds */ + m = 0; + while (sec >= 60UL) { sec -= 60UL; m++; } /* <= 59 rounds */ + + puts("up "); + if (d) { putu(d); puts("d "); } + putu(h); putc(':'); two(m); putc(':'); two((unsigned int)sec); + nl(); +} -- cgit v1.3.1-sl0p