liveBook · Manning

U16

As explained in Rust in Action: Systems programming concepts and techniques

u16 is Rust's unsigned 16-bit integer type, representing values from 0 through 65,535.

u16 is Rust’s unsigned 16-bit integer type. Its values run from 0 through 65,535. Each value has a fixed number of bits: zero is sixteen 0s, while 65,535 is sixteen 1s. There is no u16 bit pattern for 65,536. section 5.2

Count past the limit

Start with `let mut i: u16 = 0` and repeatedly run `i += 1000`. After printing 65,000, the next addition exceeds the type’s range. With the shown default `rustc` build, the program panics with `attempt to add with overflow`. The problem is not that the number is imprecise: it cannot fit. section 5.2

A fixed width is useful when a value represents a fixed-width instruction. A CHIP-8 opcode is a u16 made of two bytes, or four 4-bit nibbles. For example, `0x8014` can be split into `8`, `0`, `1`, and `4`; those parts help an emulator choose an operation and its registers. section 5.7.1

Listing 5.24 Reading an opcode from memory
 8 fn read_opcode(&self) -> u16 {
 9   let p = self.position_in_memory;
10   let op_byte1 = self.memory[p] as u16;
11   let op_byte2 = self.memory[p + 1] as u16;
12  
13   op_byte1 << 8 | op_byte2       #1
14 }

Follow how `read_opcode()` casts two adjacent memory bytes to u16, shifts the first byte left by eight bits, and combines them with `|` into one opcode. section 5.7.3

Once the opcode is assembled, masks and right shifts select its parts. For `0x71E4`, `(opcode & 0xF000) >> 12` gives `0x7`, while `opcode & 0x0FFF` gives the 12-bit value `0x1E4`. The same emulator keeps its calculation registers as u8 values and uses `overflowing_add()` when their sums might exceed that smaller type. section 5.7.1 section 5.7.3

Instructions can also be written as a `[u16; 3]` array, then separated into six bytes for storage in memory. A call stack uses `[u16; 16]` to hold return addresses, converting between u16 addresses and the CPU’s `usize` memory position. section 5.7.4

How Rust in Action: Systems programming concepts and techniques covers it

How deep it goes
Works through bit patterns, overflow, byte assembly, and opcode decoding rather than giving comprehensive coverage of Rust integer types.
Examples
Shows an overflowing counter, hexadecimal opcode masks, two-byte instruction assembly, and a u16 return-address stack.
What it assumes
Assumes earlier exposure to i32, u8, and usize, then demonstrates u16 bit patterns and opcode operations with examples.
What you can do after
Build it from scratch: assemble and decode two-byte opcodes in a small Rust CPU emulator.
How it connects
Connects u16 to integer overflow, bitwise operations, CHIP-8 opcodes, memory, and the call stack.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Rust in Action: Systems programming concepts and techniques. The section links point to the parts of the book it draws on.