ADD Vx, byte — Adding Without Carry

7XNN adds the value NN to register Vx. If the result overflows 255, it wraps around silently — VF is not affected.

Encoding

7XNN
│││└─ NN: 8-bit value to add
││└── X: register index (0–F)
└──── opcode category 7

Example: 0x7105 means "add 5 to V1".

Decode and execute

#![allow(unused)]
fn main() {
0x7 => {
    let x  = ((opcode & 0x0F00) >> 8) as usize;
    let nn = (opcode & 0x00FF) as u8;
    self.v[x] = self.v[x].wrapping_add(nn);
}
}

The extraction is identical to 6XNN. The only difference is the operation: wrapping_add instead of direct assignment.

Why wrapping_add?

Using plain + in debug builds would panic on overflow. wrapping_add wraps silently and consistently in both debug and release:

#![allow(unused)]
fn main() {
let x: u8 = 0xFF;
x.wrapping_add(1)  // → 0x00, never panics
}

This is the correct CHIP-8 behavior: the spec says 7XNN wraps without setting any flag.

VF is intentionally not affected

This distinguishes 7XNN from 8XY4 (ADD Vx, Vy), which does write the carry to VF. The silent overflow in 7XNN is by design — programs that want to detect overflow use 8XY4 instead.

In the test program

0x71, 0x05,  // ADD V1, 5 — advance x position by 5 after each digit
0x70, 0x01,  // ADD V0, 1 — increment digit index

Each iteration of the loop moves the drawing position 5 pixels right and increments the digit counter. Both use 7XNN because they do not need carry detection.


Rust concepts used here: wrapping_add is covered in Integer Overflow and Wrapping — specifically why + is not used directly in emulator arithmetic. The contrast with 8XY4 (which uses overflowing_add to capture the carry) is shown in 8XY* — Arithmetic and Logic.