Chapter 3: Subregister Model

This chapter is normative. It fixes how each 64-bit register is sliced into subregisters, the subregister selector encoding, and the write-merge semantics.

3.1 The subregister layout

Every register is 64 bits and is addressable as smaller fields: bytes (B, 8 bits), quarter-words (Q, 16 bits), half-words (H, 32 bits), and the full word (W, 64 bits). The full register Rn is a synonym for Rn.W0. The fields tile the register from the low end:

byte offset   7   6   5   4   3   2   1   0
            [B7][B6][B5][B4][B3][B2][B1][B0]
            [ Q3   ][ Q2   ][ Q1   ][ Q0   ]
            [   H1         ][   H0         ]
            [           W0                 ]

For the value $FEDC_BA98_7654_3210 in R0:

FieldValueFieldValue
R0 / R0.W0$FEDCBA9876543210R0.Q0$3210
R0.H1$FEDCBA98R0.B7$FE
R0.H0$76543210R0.B3$76
R0.Q3$FEDCR0.B1$32
R0.Q2$BA98R0.B0$10
R0.Q1$7654

B0 is the least-significant byte, B7 the most-significant; H0 is the low half-word, H1 the high half-word; Q0..Q3 run low to high. The layout is a view over a single backing word, so it is host-endianness-independent: the numbering above is the architectural truth regardless of the host.

3.2 The subregister selector encoding

An operand byte's low nibble is the subregister selector (the high nibble is the register; Chapter 5). The fifteen defined selectors:

SelectorFieldWidth
$0B01 byte
$1B11 byte
$2B21 byte
$3B31 byte
$4B41 byte
$5B51 byte
$6B61 byte
$7B71 byte
$8Q02 bytes
$9Q12 bytes
$AQ22 bytes
$BQ32 bytes
$CH04 bytes
$DH14 bytes
$EW08 bytes

3.3 The $F selector: illegal operand

Selector $F is an undefined subregister encoding. It is a deterministic illegal-operand trap (cause 0; Chapter 10): an operand naming subregister $F raises the illegal-instruction / illegal-operand fault rather than accessing any field. An assembler will not emit $F; a hand-crafted or corrupt image that carries it traps.

The reference VM does not yet trap this encoding (it currently reads zero through an out-of-range table access); a VM correction to raise the trap is tracked separately. The frozen contract is the trap, and a conforming implementation must raise it.

3.4 Write-merge semantics

Writing a named subregister preserves the rest of the register. CP $01 R0.B0 writes only the low byte of R0 and leaves B1..B7 untouched. Writing the full register (.W0, or a bare Rn) replaces all 64 bits.

This merge rule is uniform across the machine:

  • A load takes its width from the destination subregister and lands the bytes in exactly that field, preserving the rest: LD @addr R0.B0 reads one byte, LD @addr R0.H0 reads four, LD @addr R0 reads eight (Chapter 7 section 7.1).
  • A copy that is narrower than its destination extends to the destination's full width: CP sign-extends, CPZ zero-extends (Chapter 5 section 5.6, Chapter 7 section 7.1). Naming a narrower destination subregister instead writes only that field and preserves the rest.
  • SETcc and CLR follow the same rule: a bare register destination writes the full W0 (a clean value with no stale upper bits); an explicit subregister writes only that field.

3.5 Subregisters and floating-point

Under the Zfinx floating-point model (Chapter 8), the subregister field also selects the floating-point format of an FP operand: H0 or H1 selects binary32 (single), and W0 selects binary64 (double). A B* or Q* subregister on a floating-point operand is illegal and traps (cause 0, illegal operand). This is the only place where the subregister field carries a type rather than merely a width; see Chapter 8.

Sourcing

  • Field layout and numbering: README "Sub-registers" and the graphical map; src/maize_cpu.h subreg_enum (lines 112-128), subreg_mask_enum (lines 131-147), and the subword_ref view (lines 155-181).
  • Selector encoding: README "Sub-register bit field"; the reference decode maps in src/cpu.cpp (subreg_*_map tables). The undefined $F selector is a frozen illegal-operand trap (Chapter 10); the reference VM's current out-of-range read is a known divergence corrected separately.
  • Write-merge: src/cpu.cpp write_subreg_bits / copy_regval_reg / copy_regval_reg_zext (lines ~615-688); README "Copy width".
  • FP width selection: src/cpu.cpp fp_width_from_subreg (lines ~979-989); Chapter 8.