8XY* — Arithmetic and Logic
Nine opcodes share the 0x8 prefix. All operate on two registers Vx and Vy, with the last nibble identifying the specific operation. They are the CPU's complete set of register-to-register arithmetic.
Encoding
8XYN
│││└─ N: operation (0–7, E)
││└── Y: source register
│└─── X: destination register
└──── opcode category 8
Decode structure
All nine share the same setup:
#![allow(unused)] fn main() { 0x8 => { let x = ((opcode & 0x0F00) >> 8) as usize; let y = ((opcode & 0x00F0) >> 4) as usize; let op = opcode & 0x000F; match op { 0x0 => { /* LD */ } 0x1 => { /* OR */ } // ... } } }
The nine operations
0 — LD Vx, Vy — copy Vy into Vx.
1 — OR Vx, Vy
#![allow(unused)] fn main() { self.v[x] |= self.v[y]; }
2 — AND Vx, Vy
#![allow(unused)] fn main() { self.v[x] &= self.v[y]; }
3 — XOR Vx, Vy
#![allow(unused)] fn main() { self.v[x] ^= self.v[y]; }
4 — ADD Vx, Vy — adds with carry flag:
#![allow(unused)] fn main() { let (result, carry) = self.v[x].overflowing_add(self.v[y]); self.v[x] = result; self.v[0xF] = carry as u8; // 1 if overflow, 0 if not }
5 — SUB Vx, Vy — Vx = Vx - Vy, VF = 1 if no borrow:
#![allow(unused)] fn main() { let (result, borrow) = self.v[x].overflowing_sub(self.v[y]); self.v[x] = result; self.v[0xF] = !borrow as u8; // inverted: 1 means "did NOT borrow" }
6 — SHR Vx — shift right by 1, VF = bit shifted out:
#![allow(unused)] fn main() { self.v[0xF] = self.v[x] & 0x1; // save LSB before it disappears self.v[x] >>= 1; }
7 — SUBN Vx, Vy — Vx = Vy - Vx, VF = 1 if no borrow (reverse of SUB).
E — SHL Vx — shift left by 1, VF = bit shifted out:
#![allow(unused)] fn main() { self.v[0xF] = (self.v[x] >> 7) & 0x1; // save MSB before it disappears self.v[x] <<= 1; }
The VF ordering rule
For SHR and SHL, VF must be set before the shift — not after. The bit to save exists only before the operation; after the shift, it is gone. Getting this order wrong produces a subtly incorrect implementation that passes most tests but fails on edge cases.
Rust concepts used here: Compound assignments (
|=,&=,^=,>>=,<<=) are in Compound Assignment.overflowing_add/overflowing_subreturning(value, bool)uses Tuples and Destructuring. The VF ordering dependency is the same one called out in Compound Assignment.