65816-llvm-mos/demos/timeProbe.c
Scott Duensing 3388f3c5a5 More updates
2026-06-03 20:46:31 -05:00

90 lines
3.6 KiB
C

// timeProbe.c - GS/OS smoke for the IIgs RTC surface. Exercises
// three layers of the time stack:
//
// 1. iigsReadTimeHex (Misc Tool $0D03) - the raw hardware read.
// 2. time() (libc.c) - epoch-second conversion.
// 3. gettimeofday() (extras.c) - the new POSIX shim added
// alongside this demo.
//
// All three paths must return non-zero on real GS/OS (the system
// clock is set during boot from the battery-backed clock chip; sec
// is always non-deterministic, hour/year are usually non-zero).
//
// Headless verification - we cannot pin specific values without
// knowing what MAME's emulated RTC will return, so we set marker
// bytes at $70+ that reflect "the call returned + the bytes look
// plausible":
//
// $70 = 0x99 if iigsReadTimeHex wrote something to b[] AND time()
// returned a non-zero value AND gettimeofday() returned 0
// with tv_sec != 0.
// $71 = b[2] (hour) -- non-zero on real boot, MAME returns 0 in the
// first emulated second so the smoke ONLY
// checks $70=0x99.
//
// Build with: bash demos/build.sh timeProbe
// Run with: bash scripts/runViaFinder.sh demos/timeProbe.omf
// --check 0x70=0x99
#include "iigs/misc.h"
#include "iigs/toolbox.h"
#include "sys/time.h"
#include <time.h>
int main(void) {
// Layer 1: raw ReadTimeHex. The buffer is preloaded with a
// sentinel pattern (0xAA) so we can detect that the tool actually
// overwrote SOMETHING -- even on a freshly booted MAME (clock
// starts at Jan 1 1904 internally) the toolset is expected to
// write all 8 bytes, and at least one of them differs from 0xAA
// (day-of-week=Sunday=1, day-of-month=1, etc).
unsigned char b[8];
for (int i = 0; i < 8; i++) {
b[i] = 0xAA;
}
iigsReadTimeHex(b);
int layer1Ok = 0;
for (int i = 0; i < 8; i++) {
if (b[i] != 0xAA) {
layer1Ok = 1;
break;
}
}
// Save the hour byte for diagnostic (not part of the smoke check).
*(volatile unsigned char *)0x71 = b[2];
// Layer 2: time(). libc.c's iigsToolboxInit() arms the internal
// gate that protects time() from being called before the Tool
// Locator is up; safe to call unconditionally. time() returns 0
// if the RTC year is < 1970 (Unix epoch) -- on MAME that means a
// freshly reset emulator returns 0 here. We don't gate the smoke
// on a non-zero return; we only confirm the call returned cleanly
// (didn't crash or hang) by reaching layer 3.
iigsToolboxInit();
(void)time((time_t *)0);
// Layer 3: gettimeofday(). Even when time() returns 0 (epoch
// floor), gettimeofday must return -1 in that case per the shim's
// contract. We assert the call returned (didn't crash) and tv_usec
// ended up == 0 (the shim always sets it to 0, no sub-second hw).
struct timeval tv;
tv.tv_sec = 0xDEADBEEFL;
tv.tv_usec = 0xCAFE0000L;
int r = gettimeofday(&tv, (void *)0);
// Either r==0 with tv_sec!=0 (real clock past 1970) OR r==-1 with
// tv_sec==0 (epoch floor / MAME default). Both are valid call
// completion signals. Reject only the "tv untouched" outcome.
int layer3Ok = (tv.tv_usec == 0) && ((r == 0 && tv.tv_sec != 0L) || (r == -1 && tv.tv_sec == 0));
if (layer1Ok && layer3Ok) {
*(volatile unsigned char *)0x70 = 0x99;
} else {
*(volatile unsigned char *)0x70 = 0x43;
}
// Linger so the snapshot harness can sample the marker.
for (volatile unsigned long s = 0; s < 600000UL; s++) { }
return 0;
}