code wiki / _hdl_build / nx_input_abstract_part.nx

nx_input_abstract_part.nx source

↩ module page · 221 lines · 9483 B

1// nx_input_abstract_part.nx -- RENAMED FROM nx_input_abstract.nx 2026-07-31 (lib-reconcile). 2// PERMANENT FIX for a two-libraries-one-name collision. runtime/nx_input_abstract.nx is a DIFFERENT 3// program: the F1102 game-platform input CORE, whose API is ia_init(p) / ia_bind(p, code, act). 4// THIS file is the gamebench capability-43 composable PART, whose API is ia_init(s, nact) and 5// ia_bind(s, dev, act, raw) -- same names, INCOMPATIBLE arities. nx_cc binds an import to the 6// IMPORTER'S OWN DIRECTORY FIRST, so which API a caller got depended on WHICH DIRECTORY IT SAT IN, 7// and moving a file between directories silently swapped its input API. The rename removes the 8// ambiguity by construction: one name now denotes one program corpus-wide. 9// 10// WHY THIS PART EXISTS (measured, not guessed): nx_gamebench 2026-07-25 ranked platform reach and found 11// platform-desktop-native blocking 21 of 22 reference titles, vr-runtime a GAP for 4, and mobile only 12// PARTIAL -- while EVERY game in the ecosystem wired its own controls. One loop per platform is how a 13// codebase forks into four. This part is the single place a raw device code becomes a SEMANTIC ACTION, 14// so the game loop above it never learns which device the player used. 15// 16// THE CONTRACT (each clause is a gate tooth): 17// - DEVICE-INDEPENDENCE BY CONSTRUCTION: the same semantic action sequence delivered from keyboard, 18// gamepad, touch or a VR controller drives IDENTICAL downstream state. The game cannot tell them 19// apart because it never sees a device -- it sees actions. This is the flagship property. 20// - NO DEAD ACTION: an action reachable on one device but not another is a platform that cannot play. 21// ia_coverage MEASURES this per device; the gate requires 1000 permil on every bound device. 22// - UNAMBIGUOUS BINDINGS: binding a second action to a (device, raw-code) already in use is REFUSED 23// (IA_E_DUP) with state bit-identical. Ambiguous input is a bug you cannot debug later. 24// - AN UNBOUND CODE PRODUCES NOTHING: a stray device code can never fabricate an action. 25// - DETERMINISTIC REPLAY: the action trace replays to the same actions and the same checksum, and the 26// serializable span rides nx_gamesave like every other certified part (rpgstats T8 / entity T9). 27// Bindings are DATA, not code: a game declares its own action set and its own per-device codes, so a new 28// platform is a binding table, never a fork of the loop. 29// LIB, no main (ecosystem convention, cf. nx_swgpu.nx / nx_gamehud.nx). license_tier: ORIGINAL 30import "nx_syscalls.nx" 31 32// ---- devices (bit index). Deliberately the four the platform axes measure. ---- 33const IA_D_KEY: i64 = 0 34const IA_D_PAD: i64 = 1 35const IA_D_TOUCH: i64 = 2 36const IA_D_VR: i64 = 3 37const IA_NDEV: i64 = 4 38 39// ---- semantic actions the HUD/menu layer consumes (nx_gamehud gh_menu_move / gh_menu_sel). 40// A game may declare up to IA_MAXACT actions; 0..4 are the universal navigation set. 41const IA_A_PREV: i64 = 0 42const IA_A_NEXT: i64 = 1 43const IA_A_CONFIRM: i64 = 2 44const IA_A_CANCEL: i64 = 3 45const IA_A_PAUSE: i64 = 4 46const IA_A_NONE: i64 = 0-1 47const IA_MAXACT: i64 = 12 48 49// ---- state layout (i64 words; ALL serializable words first, IA_NSV of them) ---- 50const IA_F_NACT: i64 = 0 // declared action count 51const IA_F_NEV: i64 = 1 // events fed (lifetime) 52const IA_F_CK: i64 = 2 // order-sensitive rolling action checksum 53const IA_F_DEVM: i64 = 3 // bitmask of devices carrying at least one binding 54const IA_F_NTR: i64 = 4 // trace length 55const IA_O_BIND: i64 = 8 // binding table: IA_NDEV*IA_MAXACT raw codes -> 8 .. 8+48 = 56 56const IA_O_TRACE: i64 = 56 // recorded action trace -> 56 .. 56+64 = 120 57const IA_MAXTRACE: i64 = 64 58const IA_NSV: i64 = 120 // serializable span (IA_O_TRACE + IA_MAXTRACE) 59 60// ---- errors (negative; state is ALWAYS bit-identical on refusal) ---- 61const IA_E_DEV: i64 = 0-1 62const IA_E_ACT: i64 = 0-2 63const IA_E_RAW: i64 = 0-3 64const IA_E_DUP: i64 = 0-4 65const IA_E_FULL: i64 = 0-5 66 67const IA_CK_SEED: i64 = 1469598103 68const IA_CK_PRIME: i64 = 16777619 69const IA_CK_MASK: i64 = 4611686018427387903 // 2^62-1: keep the checksum bounded and positive 70 71func ia_bit(i: i64) -> i64 { var v: i64=1; var k: i64=0; while k<i { v=v*2; k=k+1 } return v } 72func ia_hasbit(m: i64, i: i64) -> i64 { let b: i64=ia_bit(i); if (m/b)%2==1 { return 1 } return 0 } 73 74// clear all state and declare how many semantic actions this game uses 75func ia_init(s: *i64, nact: i64) -> i64 { 76 if nact < 1 { return IA_E_ACT } 77 if nact > IA_MAXACT { return IA_E_ACT } 78 var i: i64 = 0 79 while i < IA_NSV { s[i]=0; i=i+1 } 80 s[IA_F_NACT] = nact 81 s[IA_F_CK] = IA_CK_SEED 82 return 0 83} 84 85// raw code currently bound to (dev, act); 0 means unbound 86func ia_binding(s: *i64, dev: i64, act: i64) -> i64 { 87 return s[IA_O_BIND + dev*IA_MAXACT + act] 88} 89 90// Bind a device raw-code to a semantic action. REFUSES a code already bound to a DIFFERENT action on 91// that device (IA_E_DUP) -- ambiguous input can never enter the table. Re-binding the same pair is a 92// no-op success so a binding table is idempotent to apply twice (rule 10). 93func ia_bind(s: *i64, dev: i64, act: i64, raw: i64) -> i64 { 94 if dev < 0 { return IA_E_DEV } 95 if dev >= IA_NDEV { return IA_E_DEV } 96 if act < 0 { return IA_E_ACT } 97 if act >= s[IA_F_NACT] { return IA_E_ACT } 98 if raw == 0 { return IA_E_RAW } 99 var a: i64 = 0 100 while a < s[IA_F_NACT] { 101 if s[IA_O_BIND + dev*IA_MAXACT + a] == raw { 102 if a != act { return IA_E_DUP } 103 } 104 a = a + 1 105 } 106 s[IA_O_BIND + dev*IA_MAXACT + act] = raw 107 if ia_hasbit(s[IA_F_DEVM], dev) == 0 { s[IA_F_DEVM] = s[IA_F_DEVM] + ia_bit(dev) } 108 return 0 109} 110 111// Translate a raw device code into a semantic action WITHOUT touching state. 112// An unbound code yields IA_A_NONE -- a stray code can never fabricate an action. 113func ia_lookup(s: *i64, dev: i64, raw: i64) -> i64 { 114 if dev < 0 { return IA_A_NONE } 115 if dev >= IA_NDEV { return IA_A_NONE } 116 if raw == 0 { return IA_A_NONE } 117 var a: i64 = 0 118 while a < s[IA_F_NACT] { 119 if s[IA_O_BIND + dev*IA_MAXACT + a] == raw { return a } 120 a = a + 1 121 } 122 return IA_A_NONE 123} 124 125// order-sensitive fold: a swapped pair of actions changes the checksum 126func ia_mixck(ck: i64, v: i64) -> i64 { 127 var c: i64 = ck 128 c = (c + v + 1) % IA_CK_MASK 129 c = (c * IA_CK_PRIME) % IA_CK_MASK 130 return c 131} 132 133// Feed a raw device event. Returns the semantic action (or IA_A_NONE). ONLY the action is recorded -- 134// the device is deliberately NOT part of the trace or the checksum, which is exactly why two devices 135// producing the same actions produce identical state. 136func ia_feed(s: *i64, dev: i64, raw: i64) -> i64 { 137 let act: i64 = ia_lookup(s, dev, raw) 138 if act == IA_A_NONE { return IA_A_NONE } 139 s[IA_F_NEV] = s[IA_F_NEV] + 1 140 s[IA_F_CK] = ia_mixck(s[IA_F_CK], act) 141 if s[IA_F_NTR] < IA_MAXTRACE { 142 s[IA_O_TRACE + s[IA_F_NTR]] = act 143 s[IA_F_NTR] = s[IA_F_NTR] + 1 144 } 145 return act 146} 147 148func ia_ck(s: *i64) -> i64 { return s[IA_F_CK] } 149func ia_nev(s: *i64) -> i64 { return s[IA_F_NEV] } 150func ia_trace_len(s: *i64) -> i64 { return s[IA_F_NTR] } 151func ia_trace_at(s: *i64, i: i64) -> i64 { 152 if i < 0 { return IA_A_NONE } 153 if i >= s[IA_F_NTR] { return IA_A_NONE } 154 return s[IA_O_TRACE + i] 155} 156 157// How much of the declared action set can this device actually reach, in permil. 158// Below 1000 means a player on that device cannot perform every action = that platform cannot play. 159func ia_coverage(s: *i64, dev: i64) -> i64 { 160 if dev < 0 { return 0 } 161 if dev >= IA_NDEV { return 0 } 162 let n: i64 = s[IA_F_NACT] 163 if n < 1 { return 0 } 164 var bound: i64 = 0 165 var a: i64 = 0 166 while a < n { 167 if s[IA_O_BIND + dev*IA_MAXACT + a] != 0 { bound = bound + 1 } 168 a = a + 1 169 } 170 return (bound*1000)/n 171} 172 173// device bitmask of every device that can reach this action 174func ia_reach(s: *i64, act: i64) -> i64 { 175 var m: i64 = 0 176 var d: i64 = 0 177 while d < IA_NDEV { 178 if s[IA_O_BIND + d*IA_MAXACT + act] != 0 { m = m + ia_bit(d) } 179 d = d + 1 180 } 181 return m 182} 183 184// Overall platform reach in permil: of every (bound device, declared action) pair, how many are bound. 185// This is the part's own honest self-score and the number the board's axis 43 is flipped against. 186func ia_reach_permil(s: *i64) -> i64 { 187 var got: i64 = 0 188 var tot: i64 = 0 189 var d: i64 = 0 190 while d < IA_NDEV { 191 if ia_hasbit(s[IA_F_DEVM], d) == 1 { 192 var a: i64 = 0 193 while a < s[IA_F_NACT] { 194 tot = tot + 1 195 if s[IA_O_BIND + d*IA_MAXACT + a] != 0 { got = got + 1 } 196 a = a + 1 197 } 198 } 199 d = d + 1 200 } 201 if tot < 1 { return 0 } 202 return (got*1000)/tot 203} 204 205// Replay a recorded trace into a fresh state: same actions, same checksum, no device involved. 206func ia_replay(src: *i64, dst: *i64) -> i64 { 207 let r: i64 = ia_init(dst, src[IA_F_NACT]) 208 if r != 0 { return r } 209 var i: i64 = 0 210 while i < src[IA_F_NTR] { 211 let act: i64 = src[IA_O_TRACE + i] 212 dst[IA_F_NEV] = dst[IA_F_NEV] + 1 213 dst[IA_F_CK] = ia_mixck(dst[IA_F_CK], act) 214 if dst[IA_F_NTR] < IA_MAXTRACE { 215 dst[IA_O_TRACE + dst[IA_F_NTR]] = act 216 dst[IA_F_NTR] = dst[IA_F_NTR] + 1 217 } 218 i = i + 1 219 } 220 return 0 221}