QueryPerformanceCounter arithmetic, 2026-09-21
The existing handler advertises 1 MHz but computed ticks_ms * 1000 + perf_counter_lo in i32 and wrote zero into the output high DWORD. The first
unsigned product carry occurs at 4,294,967.296 milliseconds (about 71.6 minutes).
Crossing 2^31 microseconds alone does not make the stored LARGE_INTEGER
negative: its high DWORD was zero. The earlier messageboard estimate of
35.8 minutes confused the signed low-word boundary with the full count.
The handler now zero-extends both unsigned inputs before multiplying/adding in i64, and stores the complete eight-byte result after one guest-pointer translation. QueryPerformanceFrequency remains 1,000,000 Hz. Microsoft's API contract uses LARGE_INTEGER output; this correction is not a new native Win98 timing measurement or a claim to emulate a specific hardware timer.
node test/test-performance-counter.js compiles the current handlers and
checks both DWORDs against BigInt at the signed and unsigned microsecond
boundaries, later carries, unsigned millisecond inputs, and a carry contributed
by the existing poll adjustment. It also checks the single clock sample,
output sentinels, return values, frequency, and stdcall cleanup.
Final tests pass on main and the rsync-updated isolated workspace. Substituting
the pre-fix handler source in memory fails at 4,294,968 ms: actual 704, expected
4,294,968,000. The initial fixture referenced a nonexistent $esp global;
after correcting it to the canonical register-memory slot, the positive and
negative tests both reached the intended assertions. Fragment validation and
test-tier discovery pass. No full build-gate or browser gameplay run was made
for this arithmetic-only change.
Remaining clock work is substantial: the shared host get_ticks import masks to 31 bits and can use Date.now; its rollover/backward jumps are not solved by wide multiplication. The per-instance synthetic i32 poll increment still wraps and makes results depend on polling frequency and thread state. A shared monotonic clock authority is needed for those issues. This change deliberately does not claim that QPC is now fully monotonic or high-resolution. The reported Organic Art animation freeze was independently ruled out as caused by this overflow; no game-performance or animation improvement is claimed here.