code wiki / (root) / nx_q14_format.nx

nx_q14_format.nx source

↩ module page · 125 lines · 3854 B

1// nx_q14_format.nx -- Q14 fixed-point i64 -> decimal ASCII string. 2// 3// Required by: 4// - nx_gcode_emit (every X/Y/Z/E coordinate is a decimal text token) 5// - audit dashboards + debug logs (Q14 coords must be human-readable) 6// - operator-facing print summaries ("305.250mm × 305.250mm × ...") 7// 8// Two entry points: 9// nx_int_to_decimal(value, buf, cap) 10// basic signed i64 -> ASCII (no fractional part) 11// nx_q14_to_decimal(value_q14, buf, cap, frac_digits) 12// signed Q14 i64 -> "<int>.<frac>" with frac_digits decimals 13// 14// Behaviour: 15// - Both write to caller-owned buf; do NOT NUL-terminate; return 16// byte count written. 17// - Negative inputs prefix "-". 18// - frac_digits = 0 produces no decimal point. 19// - Capacity check: returns -1 if buf would overflow. 20// - Round-toward-zero on the fractional part (no rounding). 21// Slicer + G-code use canonical Q14 representation throughout; 22// rounding policy lives in the slicer, not the formatter. 23// 24// Q14 1 unit = 1/16384 = ~0.000061. Three fractional digits captures 25// most operator-relevant precision (0.001 mm = 16.384 Q14 units; well 26// above the formatter's truncation error). 27// 28// license_tier: ORIGINAL 29 30import "nx_syscalls.nx" 31 32const NX_Q14_ONE: i64 = 16384 33 34// Reverse a byte range in place. 35func nx_fmt_reverse(buf: *u8, lo: i64, hi: i64) -> i64 { 36 var i: i64 = lo 37 var j: i64 = hi - 1 38 while i < j { 39 let t: i64 = buf[i] as i64 40 buf[i] = buf[j] 41 buf[j] = t & 0xff 42 i = i + 1 43 j = j - 1 44 } 45 return 0 46} 47 48// Integer to decimal. Returns byte count written or -1 on overflow. 49func nx_int_to_decimal(value: i64, buf: *u8, capacity: i64) -> i64 { 50 if capacity <= 0 { return -1 } 51 var v: i64 = value 52 var neg: i64 = 0 53 if v < 0 { 54 neg = 1 55 v = 0 - v 56 } 57 var pos: i64 = 0 58 if neg == 1 { 59 if pos >= capacity { return -1 } 60 buf[pos] = 45 // '-' 61 pos = pos + 1 62 } 63 let start: i64 = pos 64 if v == 0 { 65 if pos >= capacity { return -1 } 66 buf[pos] = 48 // '0' 67 pos = pos + 1 68 } 69 while v > 0 { 70 if pos >= capacity { return -1 } 71 let d: i64 = v - (v / 10) * 10 // v % 10 via div+sub 72 buf[pos] = (48 + d) & 0xff // '0' + d 73 pos = pos + 1 74 v = v / 10 75 } 76 nx_fmt_reverse(buf, start, pos) 77 return pos 78} 79 80// Q14 to decimal with frac_digits fractional places. 81func nx_q14_to_decimal(value_q14: i64, buf: *u8, capacity: i64, 82 frac_digits: i64) -> i64 { 83 if capacity <= 0 { return -1 } 84 if frac_digits < 0 { return -1 } 85 86 var v: i64 = value_q14 87 var neg: i64 = 0 88 if v < 0 { 89 neg = 1 90 v = 0 - v 91 } 92 93 let int_part: i64 = v / NX_Q14_ONE 94 var frac_bits: i64 = v - int_part * NX_Q14_ONE // v mod NX_Q14_ONE 95 96 var pos: i64 = 0 97 if neg == 1 { 98 if pos >= capacity { return -1 } 99 buf[pos] = 45 // '-' 100 pos = pos + 1 101 } 102 103 let n_int: i64 = nx_int_to_decimal(int_part, ((buf as i64) + pos) as *u8, 104 capacity - pos) 105 if n_int < 0 { return -1 } 106 pos = pos + n_int 107 108 if frac_digits > 0 { 109 if pos >= capacity { return -1 } 110 buf[pos] = 46 // '.' 111 pos = pos + 1 112 var k: i64 = 0 113 while k < frac_digits { 114 if pos >= capacity { return -1 } 115 // Extract next decimal digit: frac_bits * 10 / 16384 116 frac_bits = frac_bits * 10 117 let d: i64 = frac_bits / NX_Q14_ONE 118 buf[pos] = (48 + d) & 0xff 119 pos = pos + 1 120 frac_bits = frac_bits - d * NX_Q14_ONE 121 k = k + 1 122 } 123 } 124 return pos 125}