Signed Odometer
Big Numbers
A vast signed odometer ticking through zero on a Super Nintendo — a chip with
no hardware divide at all. Each
value is split into decimal digits by the classic v%10 / v/=10 loop,
which on a negative number exercises the sign-corrected 64-bit divide+modulo the compiler
provides as a runtime call. Runs automatically; no joypad needed. Written in C and compiled with
the llvm-mos-based 65816 toolchain
(+mos-a16, 16-bit accumulator mode).
Self-running demo — the odometer tape ticks upward through zero.
Click the screen to focus, then watch. Tab away and it pauses.
What it is
To print a number in decimal you peel off digits from the right: the last digit is the value mod 10, then you divide by 10 and repeat. For a 64-bit signed value on a CPU with no divide instruction, each of those steps is a call into the compiler's math runtime, and the sign has to be handled carefully:
for (i = 0; i < 18; i++) {
d = v % 10; // signed modulo — negative when v < 0
digit[i] = |d|;
v = v / 10; // signed divide — truncates toward zero
}
// clang merges the two into ONE call: __divmoddi4 The odometer sweeps from deeply negative up through zero, so the digit-peeling runs on both signs. It stays bit-exact across every codegen mode — the value shown on the tape is identical to the host reference, three ways over.
Compiler stress-test
| Item | What it exercises |
|---|---|
| signed 64-bit | A 64-bit signed integer split into decimal digits. On the 65816 there is no hardware divide at all — 64-bit signed division is a compiler-runtime call, and the sign has to be corrected around the unsigned core. |
| __divmoddi4 | Because the loop uses both v/10 and v%10 on the same value, clang merges them into the single combined signed call __divmoddi4 — quotient and remainder in one shot. Distinct from an unsigned 64-bit divide (__udivdi3); the differential confirms no unsigned form appears. |
| through zero | The odometer starts deeply negative and steps up past zero into positive territory. That sweep exercises the sign-correction in both directions: C truncates division toward zero and gives a non-positive remainder for a negative operand, so the digit extraction of negatives is a distinct path from positives. |
| bit-exact | Every value on the scrolling tape is computed the same way on the host reference and on all three codegen modes. The gate folds the digits of 60 swept values; host == default == +mos-a16 == +mos-xy16 == bsnes-jg, byte for byte. |
Written in C with the llvm-mos 65816 toolchain and verified against bsnes-jg
(and MAME where the SPC700 IPL is present). Hit Verify fidelity to reproduce the build
gate's WRAM assert (gate hash 0xD2A2 — a fold of the digits of 60 swept signed
values) live in this tab. No far pointers — the same source passes every way: host == default ==
+mos-a16 == +mos-xy16 == bsnes-jg, -verify clean.