Protocols
A protocol describes how a bit-level message is encoded and decoded. Protocols are used for things like infrared (IR) tag signals and peer-to-peer comms, where the device must read and write tightly-packed binary payloads. A protocol defines the fields in a payload, the checks that decide whether an incoming signal belongs to the protocol, and any parity/validation logic.
Protocol files can be compiled to optimised C++ encode/decode functions with the toCpp CLI command — see Getting Started.
Declaring a protocol
A protocol declares a name and the transport it is for, then a body of statements. Fields are declared with define, where the type is a sized integer (uint<N> / int<N>, with N bits) or a string.
protocol "swaptx" for "ir" {
define uint<4> "bulletTypeId";
define uint<4> "playerId";
define uint<2> "teamId";
define uint<8> "hitValue";
define uint<1> "isCritical";
}Steps and validation
A step block groups processing for a phase. It can be qualified with decode or encode. Inside a step you can assert conditions with require: adding the terminate flag rejects the signal immediately if the assertion fails (so it is not treated as belonging to this protocol).
step decode {
// The system pre-fills "bitLength" with the length of the incoming data.
// This protocol uses 22 bits; if that does not match, reject the signal.
require terminate "bitLength" == 22;
}The comparator in a require can be ==, !=, or a named comparator.
Parity and the bit cursor
A parity block performs parity/validation calculations. Within a step or parity block you can move the read position (the bit cursor) and combine bits:
offset n;— move the bit cursor to absolute positionn.forward n;/rewind n;— move the cursor relative to its current position.xor/xnorover abits<N>orbytes<N>read, storing the result under a name.rule— a conditional rule. The conditional?:operator is valid here (it is not available in ordinary expressions).
parity {
// Move the bit cursor to the start of the stream
offset 0;
// XOR the first 19 bits together and store under "parityBit"
xor bits<19> "parityBit";
// Derive a value from the parity result
rule uint<3> "parityValue" = "parityBit" == 0 ? 5 : 2;
offset 0;
}A complete example
Putting it together — a pulse-width IR protocol that validates the payload length, computes a parity value, then declares its fields:
protocol "swaptx" for "ir" {
step decode {
require terminate "bitLength" == 22;
}
parity {
offset 0;
xor bits<19> "parityBit";
rule uint<3> "parityValue" = "parityBit" == 0 ? 5 : 2;
offset 0;
}
define uint<4> "bulletTypeId";
define uint<4> "playerId";
define uint<2> "teamId";
define uint<8> "hitValue";
define uint<1> "isCritical";
}Statement reference
| Statement | Purpose |
|---|---|
protocol "name" for "transport" { } | Declares a protocol and the transport it targets. |
define <type> "field"; | Declares a payload field; type is uint<N>, int<N> or string. |
step [decode|encode] { } | A processing phase, optionally scoped to decoding or encoding. |
parity [decode|encode] { } | A parity / validation block. |
require [terminate] "field" <cmp> <expr>; | Asserts a condition; terminate rejects the signal on failure. |
xor / xnor <read> "name"; | Boolean op over a bits<N> / bytes<N> read. |
offset / forward / rewind n; | Move the bit cursor (absolute / relative forward / relative back). |
rule <type> "name" = ...; | A conditional rule (supports the ?: operator). |
end <bool>; | Marks the end of a phase. |
Related Topics
- battlecore::infrared — configure the IR hardware that protocols run on.
- Getting Started — compile a protocol to C++ with
toCpp.