65816-llvm-mos/demos/cxxChronoProbe.cpp
Scott Duensing da095402ec Updated
2026-06-02 23:17:57 -05:00

81 lines
3.7 KiB
C++

// cxxChronoProbe.cpp - exercise the etl::chrono surface (Phase 5.3).
//
// Takes two steady_clock::now() readings around a busy loop, verifies
// that the second is >= the first (clock is monotonic), then prints
// the duration count via printf. Also validates the chrono rep is
// `int32_t` (the etl_profile.h override) so that i64 libcalls don't
// creep into chrono::now() comparison paths.
//
// Marker layout (16-bit little-endian at $025xxx unless noted):
// $025010 = 0xBEEF main entered
// $025012 = 1 if sizeof(steady_clock::duration::rep) == 4 (i32 rep)
// 0 otherwise
// $025014 = 1 if t1 >= t0 (monotonic), 0 otherwise
// $025016 = (uint16_t)(t1.time_since_epoch().count() & 0xFFFF)
// low 16 bits of the second reading; used by smoke to
// confirm a non-zero value (clock is actually ticking)
// $025018 = (uint16_t)(elapsed_ms & 0xFFFF)
// low 16 bits of (t1 - t0).count() in milliseconds; will
// be small but ETL chrono::duration_cast<milliseconds>
// returning the raw rep means even tiny elapsed values
// write *something* here (proves the subtract path works)
// $025000 = 0xC0DE reached end-of-main (sentinel for runInGno --check)
#include <stdint.h>
#include <stdio.h>
#include "etl/chrono.h"
int main(void) {
*(volatile uint16_t *)0x025010UL = 0xBEEF;
// Compile-time contract: clock-rep is i32 (etl_profile.h override).
// Any chrono lib that snuck i64 back in would FAIL HERE — caught
// on the demo build, not silently bloating the .omf.
static_assert(sizeof(etl::chrono::steady_clock::duration::rep) == 4,
"etl::chrono::steady_clock::rep must be i32 — check "
"ETL_CHRONO_STEADY_CLOCK_DURATION in etl_profile.h");
static_assert(sizeof(etl::chrono::system_clock::duration::rep) == 4,
"etl::chrono::system_clock::rep must be i32");
static_assert(sizeof(etl::chrono::high_resolution_clock::duration::rep) == 4,
"etl::chrono::high_resolution_clock::rep must be i32");
*(volatile uint16_t *)0x025012UL = 1;
// Two readings around a busy loop. Loop is sized to take long
// enough that one VBL tick (16.67 ms) reliably elapses, but short
// enough that the demo completes well within the runInGno timeout.
auto t0 = etl::chrono::steady_clock::now();
// Busy-spin. volatile keeps the optimizer from collapsing it.
volatile uint16_t spin = 0;
for (uint16_t i = 0; i < 20000; i++) {
spin = (uint16_t)(spin + 1);
}
auto t1 = etl::chrono::steady_clock::now();
// Monotonic check. Use the raw count() so the comparison is on
// i32 reps, not the time_point operator< (which works too — just
// belt-and-braces).
long c0 = t0.time_since_epoch().count();
long c1 = t1.time_since_epoch().count();
*(volatile uint16_t *)0x025014UL = (uint16_t)((c1 >= c0) ? 1 : 0);
// Low 16 bits of the absolute reading. If the VBL counter
// wasn't ticking at all both readings would be 0 — make that
// visible to the host.
*(volatile uint16_t *)0x025016UL = (uint16_t)(c1 & 0xFFFFL);
// Elapsed milliseconds. steady_clock's rep is already
// milliseconds (period = etl::milli), so the count delta IS
// the elapsed ms; no duration_cast required.
long elapsed_ms = c1 - c0;
*(volatile uint16_t *)0x025018UL = (uint16_t)(elapsed_ms & 0xFFFFL);
// Human-readable. GNO redirects stdout to fd 1; printf %ld is
// fully supported (Phase 1 audit closed the printf gaps).
printf("steady_clock t0 = %ld ms\n", c0);
printf("steady_clock t1 = %ld ms\n", c1);
printf("elapsed = %ld ms\n", elapsed_ms);
*(volatile uint16_t *)0x025000UL = 0xC0DE;
return 0;
}