code wiki / (root) / nx_charmem.nx

nx_charmem.nx source

↩ module page · 490 lines · 19378 B

1// nx_charmem.nx -- SOVEREIGN CHARACTER MEMORY (the Nomi/Replika-class relationship state, in Nishi). 2// Operator 2026-08-05: "SQLite-backed character memory (affection/trust/drift/learned user 3// preferences), we want in nishi sota." 4// 5// DESIGN: EVENT-SOURCED, APPEND-ONLY (rule 13 -- history is sacred). The Python original mutates 6// SQLite rows; this organ never mutates anything: every interaction APPENDS one line to 7// knowledge/status/charmem_<char>.jrnl and current state is a FOLD over the journal at read time. 8// That buys what the mutable version cannot: full audit (why is affection 72? read the lines), 9// time-travel debugging, and no lost-update races between writers. 10// 11// event <char> <kind> <args...> append one event. kinds: 12// aff|trust|intim|excit <+/-N> relationship metric delta (fold clamps to 0..100) 13// mood <word> current mood (latest wins) 14// pref <key>=<value> learned user preference (latest value per key wins) 15// mem <imp 1-9> <text...> an important memory with importance weight 16// state <char> fold -> metrics, mood, counts (machine-parseable line) 17// ctx <char> fold -> the LLM-ready system-prompt block (top prefs+memories) 18// selftest <char> hermetic assertions on a throwaway fixture journal 19// 20// Char names sanitize to [a-z0-9_] max 32 (boundary defense: the name builds a path). 21// license_tier: ORIGINAL module: nishi-core.companion.charmem 22import "nx_syscalls.nx" 23import "nx_lane_conf.nx" 24 25const CM_BUF: i64 = 1048576 26const CM_LINE: i64 = 4096 27const CM_PREF_MAX: i64 = 64 28const CM_MEM_MAX: i64 = 256 29// absent-conf fallbacks only (rule 11) -- live values are knowledge/write_lane.conf rows. 30const CM_CTX_PREFS: i64 = 6 31const CM_CTX_MEMS: i64 = 6 32const CM_DEF_AFF: i64 = 50 33const CM_DEF_TRUST: i64 = 50 34const CM_DEF_INTIM: i64 = 30 35const CM_DEF_EXCIT: i64 = 50 36 37func cm_def(slot: i64) -> i64 { 38 if slot==0 { return lc_write_lane("charmem_default_affection" as *u8, CM_DEF_AFF) } 39 if slot==1 { return lc_write_lane("charmem_default_trust" as *u8, CM_DEF_TRUST) } 40 if slot==2 { return lc_write_lane("charmem_default_intimacy" as *u8, CM_DEF_INTIM) } 41 return lc_write_lane("charmem_default_excitement" as *u8, CM_DEF_EXCIT) 42} 43 44func cm_len(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n } 45func cm_w(s: *u8) -> i64 { sys_write(1, s, cm_len(s)); return 0 } 46func cm_cat(dst: *u8, off: i64, s: *u8) -> i64 { var i: i64=0; while s[i]!=(0 as u8){dst[off+i]=s[i];i=i+1} return off+i } 47func cm_u(dst: *u8, off: i64, v: i64) -> i64 { 48 var m: i64=v 49 if m<0 { dst[off]=45 as u8; return cm_u(dst, off+1, 0-m) } 50 let t: *u8=sys_mmap(28); var k: i64=0 51 if m==0 { t[0]=48 as u8; k=1 } 52 while m>0 { t[k]=(48+(m-(m/10)*10)) as u8; m=m/10; k=k+1 } 53 var o: i64=off; var i: i64=0 54 while i<k { dst[o]=t[k-1-i]; o=o+1; i=i+1 } 55 return o 56} 57func cm_eq(a: *u8, b: *u8) -> i64 { 58 var i: i64=0 59 while 1==1 { 60 if a[i]!=b[i] { return 0 } 61 if a[i]==(0 as u8) { return 1 } 62 i=i+1 63 } 64 return 0 65} 66// parse signed int from s at *pi, advance past digits; returns value (0 if none) 67func cm_int(s: *u8, pi: *i64) -> i64 { 68 var i: i64=pi[0] 69 var neg: i64=0 70 if s[i]==(45 as u8) { neg=1; i=i+1 } 71 if s[i]==(43 as u8) { i=i+1 } 72 var v: i64=0 73 var run: i64=1 74 while run==1 { 75 if s[i]<(48 as u8) { run=0 } else { 76 if s[i]>(57 as u8) { run=0 } else { v=v*10+((s[i] as i64)-48); i=i+1 } 77 } 78 } 79 pi[0]=i 80 if neg==1 { return 0-v } 81 return v 82} 83func cm_clamp(v: i64) -> i64 { if v<0 { return 0 } if v>100 { return 100 } return v } 84 85// advance idx past the current line's newline; STOPS (does not loop) on NUL. returns new idx. 86// u2605the third-time-banked law: a loop that signals termination by overwriting its cursor destroys 87// the position it exists to report -- this helper owns line-skip so no caller re-spells it wrong. 88func cm_eol(buf: *u8, idx: i64) -> i64 { 89 var i: i64=idx 90 var run: i64=1 91 while run==1 { 92 if buf[i]==(10 as u8) { run=0; i=i+1 } else { 93 if buf[i]==(0 as u8) { run=0 } else { i=i+1 } 94 } 95 } 96 return i 97} 98// copy payload from buf[idx] until newline/NUL into dst (cap chars), NUL-terminate. returns len. 99func cm_line_copy(buf: *u8, idx: i64, dst: *u8, cap: i64) -> i64 { 100 var i: i64=idx 101 var o: i64=0 102 var run: i64=1 103 while run==1 { 104 if buf[i]==(10 as u8) { run=0 } else { 105 if buf[i]==(0 as u8) { run=0 } else { 106 if o<cap { dst[o]=buf[i]; o=o+1 } 107 i=i+1 108 } 109 } 110 } 111 dst[o]=0 as u8 112 return o 113} 114 115// sanitize char name -> out ([a-z0-9_] max 32). returns len, 0 = invalid. 116func cm_sane(s: *u8, out: *u8) -> i64 { 117 var i: i64=0 118 while s[i]!=(0 as u8) { 119 if i>=32 { return 0 } 120 let ch: u8=s[i] 121 var ok: i64=0 122 if ch>=(97 as u8) { if ch<=(122 as u8) { ok=1 } } 123 if ch>=(48 as u8) { if ch<=(57 as u8) { ok=1 } } 124 if ch==(95 as u8) { ok=1 } 125 if ok==0 { return 0 } 126 out[i]=ch 127 i=i+1 128 } 129 out[i]=0 as u8 130 return i 131} 132 133func cm_path(char: *u8, out: *u8) -> i64 { 134 var o: i64=cm_cat(out, 0, "knowledge/status/charmem_" as *u8) 135 o=cm_cat(out, o, char) 136 o=cm_cat(out, o, ".jrnl" as *u8) 137 out[o]=0 as u8 138 return o 139} 140 141func cm_now() -> i64 { let ts: *i64=sys_mmap(16) as *i64; ts[0]=0; sys_clock_gettime_real(ts); return ts[0] } 142 143// known kind? 1=metric (aff/trust/intim/excit) 2=mood 3=pref 4=mem, 0=unknown 144func cm_kind(k: *u8) -> i64 { 145 if cm_eq(k, "aff" as *u8)==1 { return 1 } 146 if cm_eq(k, "trust" as *u8)==1 { return 1 } 147 if cm_eq(k, "intim" as *u8)==1 { return 1 } 148 if cm_eq(k, "excit" as *u8)==1 { return 1 } 149 if cm_eq(k, "mood" as *u8)==1 { return 2 } 150 if cm_eq(k, "pref" as *u8)==1 { return 3 } 151 if cm_eq(k, "mem" as *u8)==1 { return 4 } 152 return 0 153} 154 155// append one event line: <epoch>|<kind>|<joined args>\n 156func cm_event(char: *u8, argc: i64, argv: *i64) -> i64 { 157 let kind: *u8=argv[3] as *u8 158 if cm_kind(kind)==0 { cm_w("CHARMEM REFUSED: unknown kind (aff|trust|intim|excit|mood|pref|mem)\n" as *u8); return 3 } 159 if argc<5 { cm_w("CHARMEM REFUSED: event needs an argument after the kind\n" as *u8); return 3 } 160 let path: *u8=sys_mmap(256) 161 cm_path(char, path) 162 let line: *u8=sys_mmap(CM_LINE) 163 var o: i64=cm_u(line, 0, cm_now()) 164 line[o]=124 as u8; o=o+1 165 o=cm_cat(line, o, kind) 166 line[o]=124 as u8; o=o+1 167 var ai: i64=4 168 while ai<argc { 169 if ai>4 { line[o]=32 as u8; o=o+1 } 170 var si: i64=0 171 let s: *u8=argv[ai] as *u8 172 while s[si]!=(0 as u8) { 173 var ch: u8=s[si] 174 // one event = one line, fields = pipes: strip both from payloads 175 if ch==(10 as u8) { ch=32 as u8 } 176 if ch==(124 as u8) { ch=47 as u8 } 177 if o<CM_LINE-2 { line[o]=ch; o=o+1 } 178 si=si+1 179 } 180 ai=ai+1 181 } 182 line[o]=10 as u8; o=o+1 183 let fd: i64=sys_openat_append(path, 420) 184 if fd<0 { cm_w("CHARMEM ERROR: cannot open journal for append\n" as *u8); return 4 } 185 sys_write(fd, line, o) 186 sys_close(fd) 187 cm_w("CHARMEM-EVENT OK char=" as *u8); cm_w(char) 188 cm_w(" appended\n" as *u8) 189 return 0 190} 191 192// fold: metrics m[0..3] (aff trust intim excit), m[4]=last epoch, m[5]=events; latest mood; buf 193// receives the whole journal (NUL-terminated). Returns journal bytes. 194func cm_fold(char: *u8, m: *i64, moodbuf: *u8, buf: *u8) -> i64 { 195 m[0]=cm_def(0); m[1]=cm_def(1); m[2]=cm_def(2); m[3]=cm_def(3); m[4]=0; m[5]=0 196 moodbuf[0]=0 as u8 197 let path: *u8=sys_mmap(256) 198 cm_path(char, path) 199 let fd: i64=sys_openat_rd(path) 200 if fd<0 { return 0 } 201 var total: i64=0 202 var r: i64=1 203 while r>0 { 204 r=sys_read(fd, ((buf as i64)+total) as *u8, CM_BUF-1-total) 205 if r>0 { total=total+r } 206 if total>=CM_BUF-1 { r=0 } 207 } 208 sys_close(fd) 209 buf[total]=0 as u8 210 let ip: *i64=sys_mmap(16) as *i64 211 let kb: *u8=sys_mmap(16) 212 var i: i64=0 213 while i<total { 214 ip[0]=i 215 let ep: i64=cm_int(buf, ip) 216 i=ip[0] 217 if buf[i]==(124 as u8) { 218 i=i+1 219 var ko: i64=0 220 var krun: i64=1 221 var kok: i64=1 222 while krun==1 { 223 if buf[i]==(124 as u8) { krun=0; i=i+1 } else { 224 if buf[i]==(10 as u8) { krun=0; kok=0 } else { 225 if buf[i]==(0 as u8) { krun=0; kok=0 } else { 226 if ko<12 { kb[ko]=buf[i]; ko=ko+1; i=i+1 } else { krun=0; kok=0 } 227 } 228 } 229 } 230 } 231 if kok==1 { 232 kb[ko]=0 as u8 233 m[4]=ep 234 m[5]=m[5]+1 235 let kt: i64=cm_kind(kb) 236 if kt==1 { 237 ip[0]=i 238 let dv: i64=cm_int(buf, ip) 239 var slot: i64=0 240 if cm_eq(kb, "trust" as *u8)==1 { slot=1 } 241 if cm_eq(kb, "intim" as *u8)==1 { slot=2 } 242 if cm_eq(kb, "excit" as *u8)==1 { slot=3 } 243 m[slot]=cm_clamp(m[slot]+dv) 244 } 245 if kt==2 { cm_line_copy(buf, i, moodbuf, 30) } 246 } 247 } 248 i=cm_eol(buf, i) 249 } 250 return total 251} 252 253func cm_state(char: *u8) -> i64 { 254 let m: *i64=sys_mmap(64) as *i64 255 let mood: *u8=sys_mmap(40) 256 let buf: *u8=sys_mmap(CM_BUF) 257 let total: i64=cm_fold(char, m, mood, buf) 258 let out: *u8=sys_mmap(CM_LINE) 259 var o: i64=cm_cat(out, 0, "CHARMEM-STATE char=" as *u8) 260 o=cm_cat(out, o, char) 261 o=cm_cat(out, o, " affection=" as *u8); o=cm_u(out, o, m[0]) 262 o=cm_cat(out, o, " trust=" as *u8); o=cm_u(out, o, m[1]) 263 o=cm_cat(out, o, " intimacy=" as *u8); o=cm_u(out, o, m[2]) 264 o=cm_cat(out, o, " excitement=" as *u8); o=cm_u(out, o, m[3]) 265 o=cm_cat(out, o, " mood=" as *u8) 266 if mood[0]==(0 as u8) { o=cm_cat(out, o, "neutral" as *u8) } else { o=cm_cat(out, o, mood) } 267 o=cm_cat(out, o, " events=" as *u8); o=cm_u(out, o, m[5]) 268 o=cm_cat(out, o, " last_epoch=" as *u8); o=cm_u(out, o, m[4]) 269 o=cm_cat(out, o, " journal_bytes=" as *u8); o=cm_u(out, o, total) 270 out[o]=10 as u8; o=o+1 271 sys_write(1, out, o) 272 return 0 273} 274 275// ctx: the LLM-ready block. prefs latest-wins by key; memories ranked importance desc. 276func cm_ctx(char: *u8) -> i64 { 277 let m: *i64=sys_mmap(64) as *i64 278 let mood: *u8=sys_mmap(40) 279 let buf: *u8=sys_mmap(CM_BUF) 280 cm_fold(char, m, mood, buf) 281 let pk: *i64=sys_mmap(CM_PREF_MAX*8) as *i64 // pref payload offsets (latest wins per key) 282 let pkey: *u8=sys_mmap(CM_PREF_MAX*64) // keys, 64 bytes each 283 var np: i64=0 284 let mm: *i64=sys_mmap(CM_MEM_MAX*8) as *i64 // mem text offsets 285 let mimp: *i64=sys_mmap(CM_MEM_MAX*8) as *i64 // importances 286 var nm: i64=0 287 let ip: *i64=sys_mmap(16) as *i64 288 let kb: *u8=sys_mmap(16) 289 var i: i64=0 290 while buf[i]!=(0 as u8) { 291 ip[0]=i 292 cm_int(buf, ip) 293 i=ip[0] 294 if buf[i]==(124 as u8) { 295 i=i+1 296 var ko: i64=0 297 var krun: i64=1 298 var kok: i64=1 299 while krun==1 { 300 if buf[i]==(124 as u8) { krun=0; i=i+1 } else { 301 if buf[i]==(10 as u8) { krun=0; kok=0 } else { 302 if buf[i]==(0 as u8) { krun=0; kok=0 } else { 303 if ko<12 { kb[ko]=buf[i]; ko=ko+1; i=i+1 } else { krun=0; kok=0 } 304 } 305 } 306 } 307 } 308 if kok==1 { 309 kb[ko]=0 as u8 310 if cm_eq(kb, "pref" as *u8)==1 { 311 // key = chars up to '='; a pref line without '=' is skipped 312 let keyb: *u8=sys_mmap(64) 313 var kyo: i64=0 314 var j: i64=i 315 var prun: i64=1 316 var pok: i64=0 317 while prun==1 { 318 if buf[j]==(61 as u8) { prun=0; pok=1 } else { 319 if buf[j]==(10 as u8) { prun=0 } else { 320 if buf[j]==(0 as u8) { prun=0 } else { 321 if kyo<48 { keyb[kyo]=buf[j]; kyo=kyo+1 } 322 j=j+1 323 } 324 } 325 } 326 } 327 keyb[kyo]=0 as u8 328 if pok==1 { if kyo>0 { 329 var found: i64=0-1 330 var q: i64=0 331 while q<np { if cm_eq(((pkey as i64)+q*64) as *u8, keyb)==1 { found=q; q=np } else { q=q+1 } } 332 if found>=0 { pk[found]=i } else { 333 if np<CM_PREF_MAX { cm_cat(((pkey as i64)+np*64) as *u8, 0, keyb); pk[np]=i; np=np+1 } 334 } 335 } } 336 } 337 if cm_eq(kb, "mem" as *u8)==1 { 338 ip[0]=i 339 let imp: i64=cm_int(buf, ip) 340 var j2: i64=ip[0] 341 if buf[j2]==(32 as u8) { j2=j2+1 } 342 if nm<CM_MEM_MAX { mm[nm]=j2; mimp[nm]=imp; nm=nm+1 } 343 } 344 } 345 } 346 i=cm_eol(buf, i) 347 } 348 let out: *u8=sys_mmap(CM_BUF) 349 let lb: *u8=sys_mmap(CM_LINE) 350 var o: i64=cm_cat(out, 0, "[RELATIONSHIP STATE] affection=" as *u8) 351 o=cm_u(out, o, m[0]) 352 o=cm_cat(out, o, "/100 trust=" as *u8); o=cm_u(out, o, m[1]) 353 o=cm_cat(out, o, "/100 intimacy=" as *u8); o=cm_u(out, o, m[2]) 354 o=cm_cat(out, o, "/100 excitement=" as *u8); o=cm_u(out, o, m[3]) 355 o=cm_cat(out, o, "/100 mood=" as *u8) 356 if mood[0]==(0 as u8) { o=cm_cat(out, o, "neutral" as *u8) } else { o=cm_cat(out, o, mood) } 357 o=cm_cat(out, o, "\n" as *u8) 358 if np>0 { 359 o=cm_cat(out, o, "[FACTS ABOUT THEM -- weave in naturally; when one is relevant use the EXACT detail below, never an invented variant]\n" as *u8) 360 var p2: i64=np-lc_write_lane("charmem_ctx_prefs" as *u8, CM_CTX_PREFS) 361 if p2<0 { p2=0 } 362 while p2<np { 363 o=cm_cat(out, o, "- " as *u8) 364 cm_line_copy(buf, pk[p2], lb, CM_LINE-2) 365 o=cm_cat(out, o, lb) 366 o=cm_cat(out, o, "\n" as *u8) 367 p2=p2+1 368 } 369 } 370 if nm>0 { 371 o=cm_cat(out, o, "[SHARED MEMORIES -- things you both lived; reference them with their real details]\n" as *u8) 372 let used: *i64=sys_mmap(CM_MEM_MAX*8) as *i64 373 let ctxmems: i64=lc_write_lane("charmem_ctx_mems" as *u8, CM_CTX_MEMS) 374 var emitted: i64=0 375 var erun: i64=1 376 while erun==1 { 377 if emitted>=ctxmems { erun=0 } else { 378 var best: i64=0-1 379 var bi: i64=0-1 380 var q2: i64=0 381 while q2<nm { 382 if used[q2]==0 { if mimp[q2]>=best { best=mimp[q2]; bi=q2 } } 383 q2=q2+1 384 } 385 if bi<0 { erun=0 } else { 386 used[bi]=1 387 o=cm_cat(out, o, "- " as *u8) 388 cm_line_copy(buf, mm[bi], lb, CM_LINE-2) 389 o=cm_cat(out, o, lb) 390 o=cm_cat(out, o, "\n" as *u8) 391 emitted=emitted+1 392 } 393 } 394 } 395 } 396 sys_write(1, out, o) 397 return 0 398} 399 400func cm_file_size(path: *u8) -> i64 { 401 let fd: i64=sys_openat_rd(path) 402 if fd<0 { return 0 } 403 let sz: i64=sys_lseek(fd, 0, 2) 404 sys_close(fd) 405 return sz 406} 407 408func cm_selftest(char: *u8) -> i64 { 409 // fresh fixture: truncate the journal (test fixture only -- live journals are never truncated) 410 let path: *u8=sys_mmap(256) 411 cm_path(char, path) 412 let tf: i64=sys_openat_wr(path, 420) 413 if tf>=0 { sys_close(tf) } 414 var pass: i64=0 415 var fail: i64=0 416 let m: *i64=sys_mmap(64) as *i64 417 let mood: *u8=sys_mmap(40) 418 let buf: *u8=sys_mmap(CM_BUF) 419 // u2605expectations DERIVED from the same conf the fold reads -- a gate pinned to literal 50/30 420 // would go RED the day the operator retunes a default, accusing a correct organ (a RED from an 421 // impossible expectation is a defect in the ruler). 422 // T1 defaults on empty journal 423 cm_fold(char, m, mood, buf) 424 if m[0]==cm_def(0) { if m[2]==cm_def(2) { pass=pass+1 } else { fail=fail+1 } } else { fail=fail+1 } 425 // T2 delta applies: aff +10 426 let av: *i64=sys_mmap(64) as *i64 427 av[3]="aff" as i64 428 av[4]="+10" as i64 429 cm_event(char, 5, av) 430 cm_fold(char, m, mood, buf) 431 let exp2: i64=cm_clamp(cm_def(0)+10) 432 if m[0]==exp2 { pass=pass+1 } else { fail=fail+1 } 433 // T3 clamp high: a +90 on top must clamp at 100, never exceed it 434 av[4]="+90" as i64 435 cm_event(char, 5, av) 436 cm_fold(char, m, mood, buf) 437 if m[0]==cm_clamp(exp2+90) { pass=pass+1 } else { fail=fail+1 } 438 // T4 clamp low: trust deep-negative must clamp at 0 439 av[3]="trust" as i64 440 av[4]="-999" as i64 441 cm_event(char, 5, av) 442 cm_fold(char, m, mood, buf) 443 if m[1]==0 { pass=pass+1 } else { fail=fail+1 } 444 // T5 NEGATIVE CONTROL: unknown kind refuses AND the journal does not grow 445 let szb: i64=cm_file_size(path) 446 av[3]="bogus" as i64 447 av[4]="1" as i64 448 let rc: i64=cm_event(char, 5, av) 449 let sza: i64=cm_file_size(path) 450 if rc==3 { if sza==szb { pass=pass+1 } else { fail=fail+1 } } else { fail=fail+1 } 451 // T6 append-only: accepted events strictly grow the journal 452 av[3]="mood" as i64 453 av[4]="playful" as i64 454 cm_event(char, 5, av) 455 if cm_file_size(path)>sza { pass=pass+1 } else { fail=fail+1 } 456 // T7 mood latest-wins through the fold 457 cm_fold(char, m, mood, buf) 458 if cm_eq(mood, "playful" as *u8)==1 { pass=pass+1 } else { fail=fail+1 } 459 // T8 NEGATIVE CONTROL: path-injection char name rejected by the sanitizer 460 let sb: *u8=sys_mmap(40) 461 if cm_sane("../evil" as *u8, sb)==0 { pass=pass+1 } else { fail=fail+1 } 462 cm_w("CHARMEM-SELFTEST pass=" as *u8) 463 let nb: *u8=sys_mmap(32); var nn: i64=cm_u(nb, 0, pass); sys_write(1, nb, nn) 464 cm_w(" fail=" as *u8) 465 nn=cm_u(nb, 0, fail); sys_write(1, nb, nn) 466 if fail==0 { cm_w(" verdict=GREEN\n" as *u8); return 0 } 467 cm_w(" verdict=RED\n" as *u8) 468 return 1 469} 470 471func main(argc: i64, argv: *i64) -> i64 { 472 if argc<3 { 473 cm_w("usage: nx_charmem {event <char> <kind> <args...> | state <char> | ctx <char> | selftest <char>}\n" as *u8) 474 cm_w(" kinds: aff|trust|intim|excit <+/-N> . mood <word> . pref <k>=<v> . mem <imp1-9> <text>\n" as *u8) 475 return 2 476 } 477 let verb: *u8=argv[1] as *u8 478 let raw: *u8=argv[2] as *u8 479 let char: *u8=sys_mmap(40) 480 if cm_sane(raw, char)==0 { cm_w("CHARMEM REFUSED: char name must be [a-z0-9_] max 32\n" as *u8); return 3 } 481 if cm_eq(verb, "event" as *u8)==1 { 482 if argc<4 { cm_w("CHARMEM REFUSED: event needs a kind\n" as *u8); return 3 } 483 return cm_event(char, argc, argv) 484 } 485 if cm_eq(verb, "state" as *u8)==1 { return cm_state(char) } 486 if cm_eq(verb, "ctx" as *u8)==1 { return cm_ctx(char) } 487 if cm_eq(verb, "selftest" as *u8)==1 { return cm_selftest(char) } 488 cm_w("CHARMEM REFUSED: unknown verb\n" as *u8) 489 return 2 490}