Skip to content

Integer as Bit-Fields

Nothing here cannot be done via standard bit-manipulation (i.e. shifting and masking).

Built-in support is more elegant and performant since it usually replaces a sequence of multiple steps.

Since bit-wise operators are defined on integer numbers, individual bits can also be accessed and manipulated via an indexing syntax.

If a bit is set (i.e. 1), the index access returns true.

If a bit is not set (i.e. 0), the index access returns false.

When a range is used, the bits within the range are shifted and extracted as an integer value.

Bit-fields are very commonly used in embedded systems which must squeeze data into limited memory.

Built-in support makes handling them efficient.

Syntax

From Least-Significant Bit (LSB)

Bits in a bit-field are accessed with zero-based, non-negative integer indices:

integer [ index from 0 to 63 or 31 ]

integer [ index from 0 to 63 or 31 ] = true or false ;

[Ranges] can also be used:

integer [ start .. end ]
integer [ start ..= end ]

integer [ start .. end ] = new integer value ;
integer [ start ..= end ] = new integer value ;

The maximum bit number that can be accessed is one less than the number of bits for the system integer type (usually 63).

Bits outside of the range are ignored.

From Most-Significant Bit (MSB)

A negative index accesses a bit in the bit-field counting from the end, or from the most-significant bit, with −1 being the highest bit.

integer [ index from −1 to −64 or −32 ]

integer [ index from −1 to −64 or −32 ] = true or false ;

ranges always count from the least-significant bit (LSB) and has no support for negative positions.

The maximum bit number that can be accessed is negative the number of bits for the system integer type (usually −64).

Bit-Field Functions

The following standard functions operate on bit-fields.

Function Parameter(s) Description
get_bit bit number, counting from MSB if < 0 returns the state of a bit: true if 1, false if 0
set_bit
  1. bit number, counting from MSB if < 0
  2. new state: true if 1, false if 0
sets the state of a bit
get_bits
  1. starting bit number, counting from MSB if < 0
  2. number of bits to extract, none if < 1, to MSB if ≥ length
extracts a number of bits, shifted towards LSB
get_bits range of bits extracts a number of bits, shifted towards LSB
set_bits
  1. starting bit number, counting from MSB if < 0
  2. number of bits to set, none if < 1, to MSB if ≥ length
    3) new value
sets a number of bits from the new value
set_bits
  1. range of bits
  2. new value
sets a number of bits from the new value
bits method and property
  1. (optional) starting bit number, counting from MSB if < 0
  2. (optional) number of bits to extract, none if < 1, to MSB if ≥ length
allows iteration over the bits of a bit-field
bits range of bits allows iteration over the bits of a bit-field

Example

// Assume the following bits fields in a single 16-bit word:
// ┌─────────┬────────────┬──────┬─────────┐
// │  15-12  │    11-4    │  3   │   2-0   │
// ├─────────┼────────────┼──────┼─────────┤
// │    0    │ 0-255 data │ flag │ command │
// └─────────┴────────────┴──────┴─────────┘

let value = read_start_hw_register(42);

let command = value.get_bits(0, 3);         // Command = bits 0-2

let flag = value[3];                        // Flag = bit 3

let data = value[4..=11];                   // Data = bits 4-11
let data = value.get_bits(4..=11);          // <- above is the same as this

let reserved = value.get_bits(-4);          // Reserved = last 4 bits

if reserved != 0 {
    throw reserved;
}

switch command {
    0 => print(`Data = ${data}`),
    1 => value[4..=11] = data / 2,
    2 => value[3] = !flag,
    _ => print(`Unknown: ${command}`)
}