RND — Random Numbers

CXNN generates a random byte, applies a bitmask, and stores the result in Vx.

Vx = random_byte & NN

Encoding

CXNN
│││└─ NN: mask applied to the random byte
││└── X: destination register
└──── opcode category C

Decode and execute

#![allow(unused)]
fn main() {
0xC => {
    let x: usize = ((opcode & 0x0F00) >> 8) as usize;
    let nn: u8   = (opcode & 0x00FF) as u8;
    let rand: u8 = rand::thread_rng().r#gen();
    self.v[x] = rand & nn;
}
}

rand::thread_rng().r#gen() generates a random u8. The r#gen() syntax is required because gen became a reserved keyword in Rust 2024 — see Raw Identifiers.

The AND with NN restricts which bits can be set:

mask 0xFF → all bits random (0–255)
mask 0x0F → only lower 4 bits random (0–15)
mask 0x00 → result is always 0

Adding the rand crate

This opcode requires the first external dependency:

[dependencies]
rand = "0.8"

And in the source file:

#![allow(unused)]
fn main() {
use rand::Rng;
}

The Rng trait must be in scope for .r#gen() to be available — importing the crate alone is not enough.

Testing non-deterministic behavior

Since the result is random, tests cannot assert a specific value. Instead, they verify the invariant — that the mask was applied:

#![allow(unused)]
fn main() {
// mask 0x0F → upper 4 bits must always be zero
assert_eq!(cpu.v[0] & 0xF0, 0);

// mask 0x00 → result is always 0
assert_eq!(cpu.v[0], 0x00);
}

Rust concepts used here: External crates and how to add them are in External Crates and Cargo.toml. Traits and why use rand::Rng is required are in Traits. The r#gen() syntax is Raw Identifiers.