Reading a scattered register field#

Register layouts often split a field around fixed bits that sit in the middle of it — a status flag or a reserved bit wedged between the high and low halves of a value. The value’s bits stay in order, they just aren’t adjacent, so a plain slice can’t read them.

extract() gathers the bits picked out by a mask and packs them together; Mutibs.deposit() writes a value back into those positions, leaving everything else untouched. They are the mask-driven, order-preserving counterparts of the x86 PEXT/PDEP instructions, and the bit-level analogue of reading and writing a contiguous field with field().

This example treats a 16-bit ADC result register whose 10-bit sample is interrupted by a 2-bit status field, reads the sample, increments it, and writes it back without disturbing the status bits.

from tibs import Mutibs, Tibs


# Register layouts often split a field around fixed bits that sit in the middle
# of it. Here a 16-bit ADC result register holds a 10-bit sample interrupted by a
# 2-bit status field:
#
#   bit (MSB0)  0 1 2 3 4 5   6 7    8 9 10 11    12 13 14 15
#   field       sample[9:4]   status  sample[3:0]  reserved
#
# The sample's bits are contiguous within each run and in order overall, just not
# adjacent. `extract` gathers them with a mask; `deposit` writes them back,
# leaving the status and reserved bits untouched.

SAMPLE = Tibs("0b1111_1100_1111_0000")   # the ten bits that make up the sample
STATUS = Tibs("0b0000_0011_0000_0000")   # the two status bits in the middle

reg = Mutibs.from_bytes(bytes.fromhex("B3 A4"))
assert reg.bin == "1011001110100100"

# Read the scattered sample as a single value.
sample = reg.extract(SAMPLE)
assert sample.bin == "1011001010"
assert sample.u == 714

# The status field is a separate, ordinary contiguous read.
assert reg.extract(STATUS).bin == "11"

# Bump the sample and write it back. deposit only touches the masked positions,
# so status and reserved bits are preserved.
reg.deposit(Tibs.from_u(sample.u + 1, 10), SAMPLE)
assert reg.extract(SAMPLE).u == 715
assert reg.extract(STATUS).bin == "11"          # status untouched
assert reg.bin == "1011001110110100"

# The immutable Tibs.deposited returns a new value instead of mutating.
frozen = Tibs.from_bytes(bytes.fromhex("B3 A4"))
updated = frozen.deposited(Tibs.from_u(0, 10), SAMPLE)
assert updated.extract(SAMPLE).u == 0
assert frozen.extract(SAMPLE).u == 714          # original unchanged

# extract and deposit are inverses: writing back what you read is a no-op.
assert frozen.deposited(frozen.extract(SAMPLE), SAMPLE) == frozen

Note that this only works because the sample’s bits stay in order. extract and deposit never reorder — for a field whose bits are permuted as well as scattered (a RISC-V immediate, say) you would gather with a mask and then apply the permutation separately, or select the bits by explicit index with View.from_indices().