// 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 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; }