diff options
| author | user <user@clank> | 2026-07-18 10:10:04 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-07-18 10:10:17 +0200 |
| commit | 0cc9b0a8bdf774ee376937d5a6767ccdced51245 (patch) | |
| tree | 221326e7a86fa0121ae7eea1763a9f3ffa8100e6 /usr/uptime.c | |
| parent | kernel: dmesg-style boot spew before init spawns (diff) | |
| download | gbos-0cc9b0a8bdf774ee376937d5a6767ccdced51245.tar.gz gbos-0cc9b0a8bdf774ee376937d5a6767ccdced51245.tar.xz gbos-0cc9b0a8bdf774ee376937d5a6767ccdced51245.zip | |
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.
Diffstat (limited to '')
| -rw-r--r-- | usr/uptime.c | 40 |
1 files changed, 40 insertions, 0 deletions
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(); +} |
