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::Rngis required are in Traits. Ther#gen()syntax is Raw Identifiers.