code wiki / (root) / nx_fx.nx

nx_fx.nx

buildroot/runtime/nx_fx.nx

9507 B255 linesdepth 2pulls 2 transitivereach 0 importersview sourcekind tooltopic fx
docsdependenciesstructsconstsfunctions

about

fx.nx -- deterministic Q16.16 fixed-point math. Why fixed-point and not IEEE-754 float: Rollback netcode (GGPO 2006, Skullgirls, Killer Instinct) requires bit-identical simulation across machines. IEEE-754 + Math.sin cannot deliver this -- different JS engines (V8, SpiderMonkey, JavaScriptCore) ship different sin/cos implementations, and even addition order can diverge under JIT inlining. Fixed-point integer math is the only way to guarantee "same input → same bits on every machine." Representation: Q16.16 - 64-bit signed integer: high 16 bits integer part, low 16 bits fractional part. Negative values use two's complement. - Range: ±32767.99998... blocks. Precision: 1/65536 ≈ 15 μm at 1 m = 1 block. Adequate for voxel games, physics, gameplay sim up to ~32 km arenas. Invariants (enforced, not hoped): FX1 No IEEE-754 anywhere. NishiLang has no float type today, so this is trivial -- but if floats ever land, fx.nx does not use them. FX2 fx_mul and fx_div round toward zero (arithmetic shift, not round-to-nearest). Deterministic and simple; callers needing round-to-nearest do `(a + (b >> 1)) / b` explicitly. FX3 sin/cos via CORDIC algorithm -- 16 iterations, ~Q16.16 precision, no Math.sin dependency. Converges through a precomputed atan table. Every implementation (NishiVM, native C VM, future Nishi silicon) produces bit-identical results by construction. FX4 Overflow in fx_mul is possible if both operands exceed 16-bit integer range. Callers are responsible for clamping; we do NOT implement saturation arithmetic by default (silent saturation masks bugs; explicit clamp is better). References: - Volder 1959, "The CORDIC Trigonometric Computing Technique" - Kota-Kuroda-Shimamura 1989, "A High-Speed Fixed-Point Multiplier Using Carry-Propagation-Free Adders" - GGPO 2006 whitepaper (rollback determinism rationale)

dependencies 1 imports · 0 importers

nx_syscalls.nx nx_fx.nx

imports: nx_syscalls.nx

imported by: nobody (leaf or entry point)

call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown

main fx_sin sys_mmap fx_sin_cos fx_normalize_angle fx_cordic_atan fx_cos sys_mmap ↻ fx_sin_cos ↻ fx_mul fx_div fx_yaw_u8_to_angle

structs

none

consts

49const FX_MAGIC_51472: i64 = 51472
50const FX_MAGIC_30386: i64 = 30386
51const FX_MAGIC_16055: i64 = 16055
52const FX_MAGIC_8150: i64 = 8150
53const FX_MAGIC_4091: i64 = 4091
54const FX_MAGIC_2047: i64 = 2047
55const FX_MAGIC_1024: i64 = 1024
59const FX_SHIFT: i64 = 16
60const FX_ONE: i64 = 65536 // 2^16
61const FX_HALF: i64 = 32768 // 0.5 in Q16.16
62const FX_FRAC_MASK: i64 = 0xFFFF // low 16 bits
63const FX_TWO_PI: i64 = 411775 // round(2π * 65536); 1 rev in Q16.16
64const FX_PI: i64 = 205887 // round(π * 65536)
65const FX_HALF_PI: i64 = 102944 // round(π/2 * 65536)
69const FX_CORDIC_K: i64 = 39797

functions

75func fx_add(a: i64, b: i64) -> i64 { return a + b }
76func fx_sub(a: i64, b: i64) -> i64 { return a - b }
77func fx_neg(a: i64) -> i64 { return 0 - a }
82func fx_mul(a: i64, b: i64) -> i64
called by 1: main
89func fx_div(a: i64, b: i64) -> i64
called by 1: main
95func fx_from_int(n: i64) -> i64 { return n << FX_SHIFT }
98func fx_to_int(a: i64) -> i64 { return a >> FX_SHIFT }
102func fx_from_frac(num: i64, den: i64) -> i64
115func fx_cordic_atan(i: i64) -> i64
called by 1: fx_sin_cos
136func fx_normalize_angle(a: i64) -> i64
called by 1: fx_sin_cos
151func fx_sin_cos(theta_raw: i64, sin_out: *i64, cos_out: *i64) -> i64
198func fx_sin(theta: i64) -> i64
called by 1: main calls 2: sys_mmapfx_sin_cos
207func fx_cos(theta: i64) -> i64
called by 1: main calls 2: sys_mmapfx_sin_cos
224func fx_yaw_u8_to_angle(yaw: i64) -> i64
called by 1: main
234func main() -> i64