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