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}