code wiki / _hdl_build / nx_meshpose.nx

nx_meshpose.nx source

↩ module page · 419 lines · 18090 B

1// nx_meshpose.nx -- THE POSE-MATCH INSTRUMENT (form ladder 1785935557; measured rung 2026-08-05: 2// the per-part table showed error growing MONOTONICALLY along each kinematic chain -- shoulder 10mm 3// to fingertips 357mm -- the signature of pose mismatch about the joint, not form error). 4// Rotates declared REGIONS of a mesh about declared PIVOTS, region and pivot and angle all DATA rows, 5// never code. Per-vertex selection (a boundary triangle STRETCHES rather than tears -- a crude skin). 6// v1 rotates about the Z axis (lateral raise in the XY plane) -- the measured need; other axes are 7// named rungs. Discipline: `report` measures the cluster ranges FIRST so pose rows are chosen from 8// measurement, not from eyeballing a render. 9// 10// nx_meshpose report <in.nxmesh> <ylo_pm> <yhi_pm> -> x-cluster RANGES (per-mille) in that band 11// nx_meshpose apply <in.nxmesh> <out.nxmesh> <posefile> -> rotated copy (layers/colors preserved) 12// nx_meshpose selftest 13// POSE ROWS (tab-separated): rot <xlo> <xhi> <ylo> <yhi> <pivotx_pm> <pivoty_pm> <deg> 14// bounds+pivot in per-mille of the mesh's own bbox; deg positive = counter-clockwise in +x-right/+y-up. 15// license_tier: ORIGINAL expect_exit: 0 16import "nx_syscalls.nx" 17import "nx_itrig.nx" 18const PZ_MAGIC_65536: i64 = 65536 19const PZ_MAGIC_4096: i64 = 4096 20const PZ_MAGIC_500000: i64 = 500000 21const PZ_MAGIC_100000: i64 = 100000 22const PZ_MAGIC_400000: i64 = 400000 23const PZ_MAGIC_98000: i64 = 98000 24const PZ_MAGIC_102000: i64 = 102000 25const PZ_MAGIC_402000: i64 = 402000 26const PZ_MAGIC_398000: i64 = 398000 27 28const PZ_CAP: i64 = 33554432 29const PZ_BINS: i64 = 128 30const PZ_GAP: i64 = 2 31const PZ_M8388607: i64 = 8388607 32const PZ_M8388608: i64 = 8388608 33const PZ_BIG: i64 = 4611686018427387903 34const PZ_TAB: i64 = 9 35const PZ_NL: i64 = 10 36const PZ_TWOPI: i64 = 25736 37 38func hw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 39func pn(v: i64) -> i64 { let b: *u8=sys_mmap(32) as *u8; var x: i64=v; var ng: i64=0; if x<0{ng=1;x=0-x} var i: i64=31; if x==0{b[i]=48 as u8;i=i-1} while x>0{b[i]=(48+x%10) as u8;x=x/10;i=i-1} if ng==1{b[i]=45 as u8;i=i-1} sys_write(1,(b as i64+i+1) as *u8,31-i); return 0 } 40func pz_u32(b: *u8, o: i64) -> i64 { return (b[o] as i64) + ((b[o+1] as i64)<<8) + ((b[o+2] as i64)<<16) + ((b[o+3] as i64)<<24) } 41func pz_w32(b: *u8, o: i64, v: i64) -> i64 { b[o]=(v&255) as u8; b[o+1]=((v>>8)&255) as u8; b[o+2]=((v>>16)&255) as u8; b[o+3]=((v>>24)&255) as u8; return 0 } 42// f32 bits (mm) -> um and back 43func pz_f2um(w: i64) -> i64 { 44 let sign: i64 = (w >> 31) & 1 45 let expo: i64 = (w >> 23) & 255 46 if expo == 0 { return 0 } 47 var mant: i64 = (w & PZ_M8388607) | PZ_M8388608 48 let sh: i64 = expo - 127 49 var v: i64 = 0 50 if sh >= 23 { if sh - 23 > 30 { return 0 } } 51 if sh >= 23 { v = mant * 1000 * (1 << (sh - 23)) } 52 if sh < 23 { if 23 - sh > 62 { return 0 } } 53 if sh < 23 { v = (mant * 1000) >> (23 - sh) } 54 if sign == 1 { return 0 - v } 55 return v 56} 57func pz_um2f(v: i64) -> i64 { 58 if v == 0 { return 0 } 59 var neg: i64 = 0 60 var m: i64 = v 61 if m < 0 { neg = 1; m = 0-m } 62 var e: i64 = 0 63 var num: i64 = m 64 var den: i64 = 1000 65 while num >= den*2 { den = den*2; e = e+1 } 66 while num < den { num = num*2; e = e-1 } 67 let frac: i64 = ((num - den)*PZ_M8388608)/den 68 var bits: i64 = ((e+127) << 23) | (frac & PZ_M8388607) 69 if neg == 1 { bits = bits | (1<<31) } 70 return bits 71} 72func pz_refuse(reason: *u8) -> i64 { hw("MESHPOSE REFUSED: " as *u8); hw(reason); hw("\n" as *u8); return 0 } 73func pz_readall(path: *u8, buf: *u8, cap: i64) -> i64 { 74 let fd: i64 = sys_openat_rd(path) 75 if fd < 0 { return 0 - 1 } 76 var n: i64 = 0 77 var go: i64 = 1 78 while go == 1 { 79 let r: i64 = sys_read(fd, ((buf as i64) + n) as *u8, cap - n) 80 if r <= 0 { go = 0 } else { n = n + r } 81 if n >= cap { go = 0 } 82 } 83 sys_close(fd) 84 return n 85} 86func pz_argint(s: *u8) -> i64 { 87 var v: i64 = 0 88 var neg: i64 = 0 89 var i: i64 = 0 90 if s[0] == (45 as u8) { neg = 1; i = 1 } 91 while s[i] != (0 as u8) { 92 let ch: i64 = s[i] as i64 93 if ch >= 48 { if ch <= 57 { v = v*10 + (ch-48) } } 94 i = i + 1 95 } 96 if neg == 1 { return 0 - v } 97 return v 98} 99func pz_int(b: *u8, a: i64, e: i64) -> i64 { 100 var v: i64 = 0 101 var neg: i64 = 0 102 var i: i64 = a 103 if i < e { if b[i] == (45 as u8) { neg = 1; i = i + 1 } } 104 while i < e { 105 let ch: i64 = b[i] as i64 106 if ch >= 48 { if ch <= 57 { v = v*10 + (ch-48) } } 107 i = i + 1 108 } 109 if neg == 1 { return 0 - v } 110 return v 111} 112func pz_eol(b: *u8, n: i64, i: i64) -> i64 { 113 var e: i64 = i 114 var f: i64 = 0 115 while f == 0 { if e >= n { f = 1 } else { if b[e] == (PZ_NL as u8) { f = 1 } else { e = e + 1 } } } 116 return e 117} 118func pz_cols(b: *u8, ls: i64, le: i64, col: *i64, maxc: i64) -> i64 { 119 var c: i64 = 0 120 var p: i64 = ls 121 while c < maxc { 122 var q: i64 = p 123 var f: i64 = 0 124 while f == 0 { if q >= le { f = 1 } else { if b[q] == (PZ_TAB as u8) { f = 1 } else { q = q + 1 } } } 125 col[c*2] = p 126 col[c*2+1] = q 127 if q >= le { c = c + 1; while c < maxc { col[c*2] = le; col[c*2+1] = le; c = c + 1 } return c } 128 p = q + 1 129 c = c + 1 130 } 131 return c 132} 133 134// header sanity + bbox over all vertex coords; fills bb[0..5] = mnx mxx mny mxy mnz mxz (um) 135func pz_bbox(b: *u8, n: i64, bb: *i64) -> i64 { 136 if n < 44 { return 0 - 1 } 137 if b[0] != (78 as u8) { return 0 - 1 } 138 if b[5] != (50 as u8) { return 0 - 1 } 139 let nlay: i64 = pz_u32(b, 8) 140 let nt: i64 = pz_u32(b, 12) 141 if nt <= 0 { return 0 - 1 } 142 let hdr: i64 = 16 + nlay*24 143 if hdr + nt*84 > n { return 0 - 1 } 144 bb[0] = PZ_BIG 145 bb[1] = 0-PZ_BIG 146 bb[2] = PZ_BIG 147 bb[3] = 0-PZ_BIG 148 bb[4] = PZ_BIG 149 bb[5] = 0-PZ_BIG 150 var t: i64 = 0 151 while t < nt { 152 var c: i64 = 0 153 while c < 3 { 154 let x: i64 = pz_f2um(pz_u32(b, hdr + t*84 + c*12)) 155 let y: i64 = pz_f2um(pz_u32(b, hdr + t*84 + c*12 + 4)) 156 let z: i64 = pz_f2um(pz_u32(b, hdr + t*84 + c*12 + 8)) 157 if x < bb[0] { bb[0] = x } 158 if x > bb[1] { bb[1] = x } 159 if y < bb[2] { bb[2] = y } 160 if y > bb[3] { bb[3] = y } 161 if z < bb[4] { bb[4] = z } 162 if z > bb[5] { bb[5] = z } 163 c = c + 1 164 } 165 t = t + 1 166 } 167 return nt 168} 169 170// report: x-cluster RANGES (per-mille) among vertices whose y falls in [ylo_pm, yhi_pm] 171func pz_report(path: *u8, ylo: i64, yhi: i64) -> i64 { 172 let b: *u8 = sys_mmap(PZ_CAP + 64) 173 let n: i64 = pz_readall(path, b, PZ_CAP) 174 let bb: *i64 = sys_mmap(64) as *i64 175 let nt: i64 = pz_bbox(b, n, bb) 176 if nt < 0 { pz_refuse("mesh unreadable or not NXMSH2" as *u8); return 3 } 177 let nlay: i64 = pz_u32(b, 8) 178 let hdr: i64 = 16 + nlay*24 179 var wid: i64 = bb[1] - bb[0] 180 if wid < 1 { wid = 1 } 181 var hgt: i64 = bb[3] - bb[2] 182 if hgt < 1 { hgt = 1 } 183 let occ: *i64 = sys_mmap(PZ_BINS*8) as *i64 184 var i: i64 = 0 185 while i < PZ_BINS { occ[i] = 0; i = i + 1 } 186 var t: i64 = 0 187 while t < nt { 188 var c: i64 = 0 189 while c < 3 { 190 let x: i64 = pz_f2um(pz_u32(b, hdr + t*84 + c*12)) 191 let y: i64 = pz_f2um(pz_u32(b, hdr + t*84 + c*12 + 4)) 192 let ypm: i64 = (y - bb[2])*1000/hgt 193 if ypm >= ylo { if ypm <= yhi { 194 var xi: i64 = (x - bb[0])*PZ_BINS/wid 195 if xi < 0 { xi = 0 } 196 if xi >= PZ_BINS { xi = PZ_BINS-1 } 197 occ[xi] = occ[xi] + 1 198 } } 199 c = c + 1 200 } 201 t = t + 1 202 } 203 hw("{\x22organ\x22:\x22nx_meshpose\x22,\x22verb\x22:\x22report\x22,\x22band_pm\x22:[" as *u8); pn(ylo) 204 hw("," as *u8); pn(yhi) 205 hw("]" as *u8) 206 hw(",\x22clusters\x22:[" as *u8) 207 var inrun: i64 = 0 208 var empty: i64 = 0 209 var lo: i64 = 0 210 var first: i64 = 1 211 i = 0 212 while i < PZ_BINS { 213 if occ[i] > 0 { 214 if inrun == 0 { lo = i; inrun = 1 } 215 empty = 0 216 } else { 217 if inrun == 1 { 218 empty = empty + 1 219 if empty >= PZ_GAP { 220 if first == 0 { hw("," as *u8) } 221 first = 0 222 hw("[" as *u8); pn(lo*1000/PZ_BINS); hw("," as *u8); pn((i-empty+1)*1000/PZ_BINS); hw("]" as *u8) 223 inrun = 0 224 } 225 } 226 } 227 i = i + 1 228 } 229 if inrun == 1 { 230 if first == 0 { hw("," as *u8) } 231 hw("[" as *u8); pn(lo*1000/PZ_BINS); hw("," as *u8); pn(1000); hw("]" as *u8) 232 } 233 hw("],\x22bbox_um\x22:[" as *u8); pn(bb[0]); hw("," as *u8); pn(bb[1]); hw("," as *u8); pn(bb[2]); hw("," as *u8); pn(bb[3]) 234 hw("]}\n" as *u8) 235 return 0 236} 237 238// apply: rotate region vertices about pivot in the XY plane; normals rotate too (no translation) 239func pz_apply(inp: *u8, outp: *u8, posep: *u8) -> i64 { 240 let b: *u8 = sys_mmap(PZ_CAP + 64) 241 let n: i64 = pz_readall(inp, b, PZ_CAP) 242 let bb: *i64 = sys_mmap(64) as *i64 243 let nt: i64 = pz_bbox(b, n, bb) 244 if nt < 0 { pz_refuse("mesh unreadable or not NXMSH2" as *u8); return 3 } 245 let pb: *u8 = sys_mmap(PZ_MAGIC_65536) 246 let pnn: i64 = pz_readall(posep, pb, PZ_MAGIC_65536) 247 if pnn <= 0 { pz_refuse("pose file unreadable or empty -- refusing to guess a pose" as *u8); return 4 } 248 let nlay: i64 = pz_u32(b, 8) 249 let hdr: i64 = 16 + nlay*24 250 var wid: i64 = bb[1] - bb[0] 251 if wid < 1 { wid = 1 } 252 var hgt: i64 = bb[3] - bb[2] 253 if hgt < 1 { hgt = 1 } 254 let col: *i64 = sys_mmap(16*2*8) as *i64 255 var nrot: i64 = 0 256 var moved: i64 = 0 257 var pi: i64 = 0 258 while pi < pnn { 259 let le: i64 = pz_eol(pb, pnn, pi) 260 if le > pi { 261 pz_cols(pb, pi, le, col, 8) 262 // rot row: fields 1..7 = xlo xhi ylo yhi pivx pivy deg 263 if pb[col[0]] == (114 as u8) { 264 nrot = nrot + 1 265 let xlo: i64 = pz_int(pb, col[2], col[3]) 266 let xhi: i64 = pz_int(pb, col[4], col[5]) 267 let ylo2: i64 = pz_int(pb, col[6], col[7]) 268 let yhi2: i64 = pz_int(pb, col[8], col[9]) 269 let pvx: i64 = bb[0] + pz_int(pb, col[10], col[11])*wid/1000 270 let pvy: i64 = bb[2] + pz_int(pb, col[12], col[13])*hgt/1000 271 let deg: i64 = pz_int(pb, col[14], col[15]) 272 let ang: i64 = deg * PZ_TWOPI / 360 273 let s: i64 = it_sin4096(ang) 274 let cz: i64 = it_cos4096(ang) 275 var t: i64 = 0 276 while t < nt { 277 var c: i64 = 0 278 while c < 3 { 279 let po: i64 = hdr + t*84 + c*12 280 let x: i64 = pz_f2um(pz_u32(b, po)) 281 let y: i64 = pz_f2um(pz_u32(b, po + 4)) 282 let xpm: i64 = (x - bb[0])*1000/wid 283 let ypm: i64 = (y - bb[2])*1000/hgt 284 var inr: i64 = 0 285 if xpm >= xlo { if xpm <= xhi { if ypm >= ylo2 { if ypm <= yhi2 { inr = 1 } } } } 286 if inr == 1 { 287 let dx: i64 = x - pvx 288 let dy: i64 = y - pvy 289 let nx: i64 = pvx + (cz*dx - s*dy)/PZ_MAGIC_4096 290 let ny: i64 = pvy + (s*dx + cz*dy)/PZ_MAGIC_4096 291 pz_w32(b, po, pz_um2f(nx)) 292 pz_w32(b, po + 4, pz_um2f(ny)) 293 // rotate the vertex normal too (offsets 36 + c*12 within the record) 294 let no: i64 = hdr + t*84 + 36 + c*12 295 let nxo: i64 = pz_f2um(pz_u32(b, no)) 296 let nyo: i64 = pz_f2um(pz_u32(b, no + 4)) 297 pz_w32(b, no, pz_um2f((cz*nxo - s*nyo)/PZ_MAGIC_4096)) 298 pz_w32(b, no + 4, pz_um2f((s*nxo + cz*nyo)/PZ_MAGIC_4096)) 299 moved = moved + 1 300 } 301 c = c + 1 302 } 303 t = t + 1 304 } 305 } 306 } 307 pi = le + 1 308 } 309 if nrot == 0 { pz_refuse("no rot rows in the pose file" as *u8); return 4 } 310 if moved == 0 { pz_refuse("rot rows matched ZERO vertices -- region bounds select nothing (measure with `report` first)" as *u8); return 5 } 311 let fd: i64 = sys_openat_wr(outp, 420) 312 if fd < 0 { pz_refuse("output unwritable" as *u8); return 6 } 313 sys_write(fd, b, n) 314 sys_close(fd) 315 hw("{\x22organ\x22:\x22nx_meshpose\x22,\x22verb\x22:\x22apply\x22,\x22rot_rows\x22:" as *u8); pn(nrot) 316 hw(",\x22vertices_moved\x22:" as *u8); pn(moved) 317 hw(",\x22tris\x22:" as *u8); pn(nt) 318 hw(",\x22note\x22:\x22per-vertex region selection: boundary triangles stretch (crude skin), never tear; layers/colors preserved byte-for-byte\x22}\n" as *u8) 319 return 0 320} 321 322// ---- teeth (literal-args discipline: nx_cc wrong-slot bug 1785936860) ---- 323func st_w32(b: *u8, o: i64, v: i64) -> i64 { return pz_w32(b, o, v) } 324func st_fix(path: *u8) -> i64 { 325 // one tri arm: (0,-500)(100,-500)(0,-400) mm hanging below origin 326 let b: *u8 = sys_mmap(256) 327 b[0]=78 as u8; b[1]=88 as u8; b[2]=77 as u8; b[3]=83 as u8 328 b[4]=72 as u8; b[5]=50 as u8; b[6]=0 as u8; b[7]=0 as u8 329 st_w32(b, 8, 1) 330 st_w32(b, 12, 1) 331 var q: i64 = 0 332 while q < 16 { b[16+q] = 0 as u8; q = q + 1 } 333 b[16]=115 as u8 334 st_w32(b, 32, 0) 335 st_w32(b, 36, 1) 336 var k: i64 = 0 337 while k < 21 { st_w32(b, 40 + k*4, 0); k = k + 1 } 338 st_w32(b, 40, 0) 339 st_w32(b, 44, pz_um2f(0-PZ_MAGIC_500000)) 340 st_w32(b, 52, pz_um2f(PZ_MAGIC_100000)) 341 st_w32(b, 56, pz_um2f(0-PZ_MAGIC_500000)) 342 st_w32(b, 64, 0) 343 st_w32(b, 68, pz_um2f(0-PZ_MAGIC_400000)) 344 st_w32(b, 124, 0) 345 let fd: i64 = sys_openat_wr(path, 420) 346 if fd < 0 { return 0 - 1 } 347 sys_write(fd, b, 128) 348 sys_close(fd) 349 return 0 350} 351func st_wr(path: *u8, s: *u8) -> i64 { 352 var n: i64 = 0 353 while s[n] != (0 as u8) { n = n + 1 } 354 let fd: i64 = sys_openat_wr(path, 420) 355 if fd < 0 { return 0 - 1 } 356 sys_write(fd, s, n) 357 sys_close(fd) 358 return 0 359} 360func pz_selftest() -> i64 { 361 var fails: i64 = 0 362 st_fix("/tmp/pz_arm.nxmesh" as *u8) 363 // whole mesh is the region; pivot at bbox top-left corner (0 pm, 1000 pm) = (0, -400mm); +90 deg 364 st_wr("/tmp/pz_pose.txt" as *u8, "rot\t0\t1000\t0\t1000\t0\t1000\t90\n" as *u8) 365 hw("T0 rotate hanging arm +90 about its shoulder -> tip must land at (-100, -500)->( -100? ): expect x flip\n" as *u8) 366 if pz_apply("/tmp/pz_arm.nxmesh" as *u8, "/tmp/pz_out.nxmesh" as *u8, "/tmp/pz_pose.txt" as *u8) != 0 { fails = fails + 1; hw("T0 FAIL apply refused\n" as *u8) } else { 367 let b2: *u8 = sys_mmap(PZ_MAGIC_4096) 368 let n2: i64 = pz_readall("/tmp/pz_out.nxmesh" as *u8, b2, PZ_MAGIC_4096) 369 if n2 < 128 { fails = fails + 1; hw("T0 FAIL output short\n" as *u8) } else { 370 // v0 was (0,-500) rel pivot (0,-400): d=(0,-100) -> +90 -> (100, 0) -> abs (100000, -400000) 371 let vx: i64 = pz_f2um(pz_u32(b2, 40)) 372 let vy: i64 = pz_f2um(pz_u32(b2, 44)) 373 var ok: i64 = 1 374 if vx < PZ_MAGIC_98000 { ok = 0 } 375 if vx > PZ_MAGIC_102000 { ok = 0 } 376 if vy < 0-PZ_MAGIC_402000 { ok = 0 } 377 if vy > 0-PZ_MAGIC_398000 { ok = 0 } 378 if ok == 1 { hw("T0 PASS v0 rotated to (~100mm, ~-400mm) analytic\n" as *u8) } else { fails = fails + 1; hw("T0 FAIL v0 at (" as *u8); pn(vx); hw("," as *u8); pn(vy); hw(") expected (~100000,~-400000)\n" as *u8) } 379 } 380 } 381 // ⚠first fixture used [0..10]pm^2 -- which CONTAINS the bbox corner vertex at (0pm,0pm). A region 382 // meant to select nothing must avoid the corners: the tri's vertices sit at (0,0)(1000,0)(0,1000)pm, 383 // so the centre square is honestly empty. 384 st_wr("/tmp/pz_none.txt" as *u8, "rot\t400\t600\t400\t600\t0\t0\t90\n" as *u8) 385 hw("T1 region selecting nothing must REFUSE (measure first, never silently no-op):\n" as *u8) 386 if pz_apply("/tmp/pz_arm.nxmesh" as *u8, "/tmp/pz_x.nxmesh" as *u8, "/tmp/pz_none.txt" as *u8) == 0 { fails = fails + 1; hw("T1 FAIL empty region accepted\n" as *u8) } else { hw("T1 PASS refused\n" as *u8) } 387 hw("T2 absent pose file must REFUSE:\n" as *u8) 388 if pz_apply("/tmp/pz_arm.nxmesh" as *u8, "/tmp/pz_y.nxmesh" as *u8, "/tmp/pz_absent_zz.txt" as *u8) == 0 { fails = fails + 1; hw("T2 FAIL\n" as *u8) } else { hw("T2 PASS\n" as *u8) } 389 hw("T3 report on the fixture: vertex-binned, so a 1-tri fixture reads its CORNERS as 2 clusters (real bodies are dense; the fixture is the degenerate case, stated not hidden):\n" as *u8) 390 if pz_report("/tmp/pz_arm.nxmesh" as *u8, 0, 1000) != 0 { fails = fails + 1; hw("T3 FAIL report refused\n" as *u8) } else { hw("T3 PASS\n" as *u8) } 391 if fails == 0 { hw("MESHPOSE-SELFTEST GREEN 4/4\n" as *u8); return 0 } 392 hw("MESHPOSE-SELFTEST RED fails=" as *u8); pn(fails); hw("\n" as *u8) 393 return 1 394} 395 396func main(argc: i64, argv: *i64) -> i64 { 397 if argc < 2 { 398 hw("usage: nx_meshpose report <in> <ylo_pm> <yhi_pm> | apply <in> <out> <posefile> | selftest\n" as *u8) 399 sys_exit(2) 400 return 2 401 } 402 let a1: *u8 = argv[1] as *u8 403 if a1[0] == (115 as u8) { let rc: i64 = pz_selftest(); sys_exit(rc); return rc } 404 if a1[0] == (114 as u8) { 405 if argc < 5 { hw("usage: nx_meshpose report <in> <ylo_pm> <yhi_pm>\n" as *u8); sys_exit(2); return 2 } 406 let rc2: i64 = pz_report(argv[2] as *u8, pz_argint(argv[3] as *u8), pz_argint(argv[4] as *u8)) 407 sys_exit(rc2) 408 return rc2 409 } 410 if a1[0] == (97 as u8) { 411 if argc < 5 { hw("usage: nx_meshpose apply <in> <out> <posefile>\n" as *u8); sys_exit(2); return 2 } 412 let rc3: i64 = pz_apply(argv[2] as *u8, argv[3] as *u8, argv[4] as *u8) 413 sys_exit(rc3) 414 return rc3 415 } 416 hw("unknown verb\n" as *u8) 417 sys_exit(2) 418 return 2 419}