Integer as Bit-Fields

Note

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 ;

Number of bits

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.

Number of bits

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.

FunctionParameter(s)Description
get_bitbit number, counting from MSB if < 0returns 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_bitsrange of bitsextracts 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
bitsrange of bitsallows 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}`)
}