use std::io::Read; use std::io::Write; const WIDTH: u16 = 64; const HEIGHT: u16 = 32; const PIXELS: usize = (WIDTH * HEIGHT) as usize; const FONT: [u8; 80] = [ 0xF0, 0x90, 0x90, 0x90, 0xF0, //0 0x20, 0x60, 0x20, 0x20, 0x70, //1 0xF0, 0x10, 0xF0, 0x80, 0xF0, //2 0xF0, 0x10, 0xF0, 0x10, 0xF0, //3 0x90, 0x90, 0xF0, 0x10, 0x10, //4 0xF0, 0x80, 0xF0, 0x10, 0xF0, //5 0xF0, 0x80, 0xF0, 0x90, 0xF0, //6 0xF0, 0x10, 0x20, 0x40, 0x40, //7 0xF0, 0x90, 0xF0, 0x90, 0xF0, //8 0xF0, 0x90, 0xF0, 0x10, 0xF0, //9 0xF0, 0x90, 0xF0, 0x90, 0x90, //A 0xE0, 0x90, 0xE0, 0x90, 0xE0, //B 0xF0, 0x80, 0x80, 0x80, 0xF0, //C 0xE0, 0x90, 0x90, 0x90, 0xE0, //D 0xF0, 0x80, 0xF0, 0x80, 0xF0, //E 0xF0, 0x80, 0xF0, 0x80, 0x80, //F ]; fn unknown_opcode(opcode: u16) { eprintln!("Unknwown opcode {:#06x}", opcode); std::process::exit(1); } struct RandomGenerator { random: u8, } impl RandomGenerator { fn new() -> Self { Self { random: 92 } } } impl Iterator for RandomGenerator { type Item = u8; fn next(&mut self) -> Option { self.random ^= self.random << 6; self.random ^= self.random >> 4; self.random ^= self.random << 1; Some(self.random) } } pub struct Chip8 { v: [u8; 16], i: u16, pc: u16, stack: [u16; 16], sp: u16, memory: [u8; 4096], screen: [u8; PIXELS], delay_timer: u8, sound_timer: u8, key: [u8; 16], draw: bool, random: RandomGenerator, } impl Chip8 { pub fn new() -> Self { let mut r = Self { v: [0; 16], i: 0, pc: 0x200, stack: [0; 16], sp: 0, memory: [0; 4096], screen: [0; PIXELS], delay_timer: 0, sound_timer: 0, key: [0; 16], draw: false, random: RandomGenerator::new(), }; r.memory[..80].copy_from_slice(&FONT); r } pub fn load_rom(&mut self, mut reader: R) -> std::io::Result<()> { reader.read(&mut self.memory[0x200..])?; Ok(()) } pub fn cycle(&mut self) { let pc = self.pc as usize; let opcode = ((self.memory[pc] as u16) << 8) | self.memory[pc + 1] as u16; let x = ((opcode & 0x0F00) >> 8) as usize; let y = ((opcode & 0x00F0) >> 4) as usize; let n = opcode & 0x000F; let nn = (opcode & 0x00FF) as u8; let nnn = opcode & 0x0FFF; self.pc += 2; match opcode & 0xF000 { 0x0000 => match opcode & 0x000F { 0x0000 => { eprintln!("CLS"); self.screen = [0; PIXELS]; self.draw = true; } 0x000E => { eprintln!("RET"); self.sp -= 1; self.pc = self.stack[self.sp as usize]; } _ => unknown_opcode(opcode), }, 0x1000 => { eprintln!("JP {}", nnn); self.pc = nnn; } 0x2000 => { eprintln!("CALL {}", nnn); self.stack[self.sp as usize] = self.pc; self.sp += 1; self.pc = nnn; } 0x3000 => { eprintln!("SE V{}, {}", x, nn); if self.v[x] == nn { self.pc += 2; } } 0x4000 => { eprintln!("SNE V{}, {}", x, nn); if self.v[x] != nn { self.pc += 2; } } 0x5000 => { eprintln!("SE V{}, V{}", x, y); if self.v[x] == self.v[y] { self.pc += 2; } } 0x6000 => { eprintln!("LD V{}, {}", x, nn); self.v[x] = nn; } 0x7000 => { eprintln!("ADD V{}, {}", x, nn); self.v[x] = self.v[x].wrapping_add(nn); } 0x8000 => match opcode & 0x000F { 0x0000 => { eprintln!("LD V{}, V{}", x, y); self.v[x] = self.v[y]; } 0x0001 => { eprintln!("OR V{}, V{}", x, y); self.v[x] |= self.v[y]; } 0x0002 => { eprintln!("AND V{}, V{}", x, y); self.v[x] &= self.v[y]; } 0x0003 => { eprintln!("XOR V{}, V{}", x, y); self.v[x] ^= self.v[y]; } 0x0004 => { eprintln!("ADD V{}, V{}", x, y); let vx = self.v[x] as u16; let vy = self.v[y] as u16; let v = vx + vy; self.v[0xF] = if v > 255 { 1 } else { 0 }; self.v[x] = v as u8; } 0x0005 => { eprintln!("SUB V{}, V{}", x, y); self.v[0xF] = if self.v[x] > self.v[y] { 1 } else { 0 }; self.v[x] = self.v[x].wrapping_sub(self.v[y]); } 0x0006 => { eprintln!("SHR V{}", x); self.v[0xF] = if self.v[x] & 1 == 1 { 1 } else { 0 }; self.v[x] /= 2; } 0x0007 => { eprintln!("SUBN V{}, V{}", x, y); self.v[0xF] = if self.v[y] > self.v[x] { 1 } else { 0 }; self.v[x] = self.v[y].wrapping_sub(self.v[x]); } 0x000E => { eprintln!("SHL V{}", x); self.v[0xF] = if (self.v[x] >> 7) & 1 == 1 { 1 } else { 0 }; self.v[x] = self.v[x].wrapping_mul(2); } _ => unknown_opcode(opcode), }, 0x9000 => { eprintln!("SNE V{}, V{}", x, y); if self.v[x] != self.v[y] { self.pc += 2; } } 0xA000 => { eprintln!("LD I, {}", nnn); self.i = nnn; } 0xC000 => { eprintln!("RND V{}, {}", x, nn); self.v[x] = self.random.next().unwrap_or(0) & nn; } 0xD000 => { eprintln!("DRW V{}, V{}, {}", x, y, n); let vx = self.v[x] as u16; let vy = self.v[y] as u16; self.v[0xF] = 0; for yy in 0..n { let pixel = self.memory[(self.i + yy) as usize]; for xx in 0..8 { if (pixel & (0x80 >> xx)) != 0 { let si = vx + xx + ((vy + yy) * WIDTH); if self.screen[si as usize] == 1 { self.v[0xF] = 1; } self.screen[si as usize] ^= 1; } } } self.draw = true; } 0xE000 => match opcode & 0x00FF { 0x009E => { eprintln!("SKP V{}", x); if self.key[self.v[x] as usize] == 1 { self.pc += 2; } } 0x00A1 => { eprintln!("SKNP V{}", x); if self.key[self.v[x] as usize] == 0 { self.pc += 2; } } _ => unknown_opcode(opcode), }, 0xF000 => match opcode & 0x00FF { 0x0007 => { eprintln!("LD V{}, DT", x); self.v[x] = self.delay_timer; } 0x0015 => { eprintln!("LD DT, V{}", x); self.delay_timer = self.v[x]; } 0x0018 => { eprintln!("LD ST, V{}", x); self.sound_timer = self.v[x]; } 0x001E => { eprintln!("ADD, I, V{}", x); self.i += self.v[x] as u16; } 0x0029 => { eprintln!("LD F, V{}", x); self.i = x as u16 * 5; } 0x0033 => { eprintln!("LD, B, V{}", x); let i = self.i as usize; self.memory[i] = self.v[x] / 100; self.memory[i + 1] = (self.v[x] / 10) % 10; self.memory[i + 2] = (self.v[x] % 100) % 10; } 0x0055 => { eprintln!("LD [I], V{}", x); let i = self.i as usize; self.memory[i..=i + x].copy_from_slice(&self.v[..=x]); } 0x0065 => { eprintln!("LD V{}, [I]", x); let i = self.i as usize; self.v[..=x].copy_from_slice(&self.memory[i..=i + x]); } _ => unknown_opcode(opcode), }, _ => unknown_opcode(opcode), }; if self.delay_timer > 0 { self.delay_timer -= 1; } if self.sound_timer > 0 { if self.sound_timer == 1 { eprintln!("BEEP"); } self.sound_timer -= 1; } } pub fn display(&mut self) -> std::io::Result<()> { if self.draw { self.draw = false; self.draw() } else { Ok(()) } } fn draw(&self) -> std::io::Result<()> { let stdout = std::io::stdout(); let mut handle = stdout.lock(); handle.write_all(b"\x1B[2J\n")?; for y in 0..HEIGHT { for x in 0..WIDTH { let si = x + y * WIDTH; let buf = if self.screen[si as usize] == 0 { "░".as_bytes() } else { "█".as_bytes() }; handle.write_all(buf)?; } handle.write_all(b"\n")?; } handle.flush() } }