nx_crc16.nx source
↩ module page · 115 lines · 3327 B
1// crc16.nx -- CRC-16 variants (CCITT, IBM, MODBUS).
2//
3// Used by serial / wire protocols where 16-bit checksums are
4// adequate (link-layer error detection). Three common variants
5// shipped:
6//
7// CCITT-FALSE (poly 0x1021, init 0xFFFF, no reflection)
8// Used by: XMODEM, MAVLink, DNP3, Bluetooth HCI
9// IBM (poly 0x8005, init 0x0000, reflected)
10// Used by: Modbus RTU, USB token CRC, ISO/IEC 13239
11// MODBUS (poly 0x8005, init 0xFFFF, reflected)
12// Used by: Modbus serial framing
13//
14// All NOT cryptographic. Burst-error detection only.
15//
16// Invariants:
17// CR1 Pure functions; no global state; deterministic.
18// CR2 Result fits in low 16 bits of i64.
19// CR3 Bit-by-bit implementation (no 256-entry table) -- slower
20// than tabulated variants but ~50 LoC instead of 50 LoC +
21// 256-byte data + per-variant table.
22
23// nx_safety_envelope:
24// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
25// sil_target: SIL1
26// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail]
27// verdict: NOT_YET_EVALUATED
28
29import "nx_syscalls.nx"
30
31const CR_POLY_CCITT: i64 = 0x1021
32const CR_POLY_IBM: i64 = 0x8005
33const CR_INIT_FFFF: i64 = 0xFFFF
34const CR_INIT_0000: i64 = 0x0000
35const CR_U16_MASK: i64 = 0xFFFF
36
37// Reflect 8 bits (LSB <-> MSB).
38func crc16_reflect8(b: i64) -> i64 {
39 var v: i64 = b
40 var r: i64 = 0
41 var i: i64 = 0
42 while i < 8 {
43 r = (r << 1) | (v & 1)
44 v = (v >> 1) & 0x7F
45 i = i + 1
46 }
47 return r
48}
49
50// Reflect 16 bits.
51func crc16_reflect16(x: i64) -> i64 {
52 var v: i64 = x & CR_U16_MASK
53 var r: i64 = 0
54 var i: i64 = 0
55 while i < 16 {
56 r = (r << 1) | (v & 1)
57 v = (v >> 1) & 0x7FFF
58 i = i + 1
59 }
60 return r
61}
62
63// CRC-16/CCITT-FALSE: poly 0x1021, init 0xFFFF, no reflection,
64// no final XOR. Standard XMODEM / MAVLink usage.
65func crc16_ccitt(bytes: *u8, n: i64) -> i64 {
66 var crc: i64 = CR_INIT_FFFF
67 var i: i64 = 0
68 while i < n {
69 crc = crc ^ (bytes[i] << 8)
70 var bit: i64 = 0
71 while bit < 8 {
72 if (crc & 0x8000) != 0 {
73 crc = ((crc << 1) ^ CR_POLY_CCITT) & CR_U16_MASK
74 } else {
75 crc = (crc << 1) & CR_U16_MASK
76 }
77 bit = bit + 1
78 }
79 i = i + 1
80 }
81 return crc & CR_U16_MASK
82}
83
84// CRC-16/MODBUS: poly 0x8005 reflected (0xA001), init 0xFFFF,
85// reflected input + output, no final XOR. The Modbus RTU
86// framing checksum.
87func crc16_modbus(bytes: *u8, n: i64) -> i64 {
88 var crc: i64 = CR_INIT_FFFF
89 var i: i64 = 0
90 while i < n {
91 crc = crc ^ bytes[i]
92 var bit: i64 = 0
93 while bit < 8 {
94 if (crc & 1) != 0 {
95 crc = ((crc >> 1) ^ 0xA001) & CR_U16_MASK
96 } else {
97 crc = (crc >> 1) & CR_U16_MASK
98 }
99 bit = bit + 1
100 }
101 i = i + 1
102 }
103 return crc & CR_U16_MASK
104}
105
106// Compile-only smoke. Known KAT for CRC-16/CCITT-FALSE on
107// "123456789" = 0x29B1.
108func main() -> i64 {
109 let msg: *u8 = "123456789"
110 let c: i64 = crc16_ccitt(msg, 9)
111 if c == 0 { return 1 }
112 let m: i64 = crc16_modbus(msg, 9)
113 if m == c { return 2 } // different polys -> different output
114 return 0
115}