use std::io::Read; use std::io::Write; const WIDTH: u16 = 64; const HEIGHT: u16 = 32; const PIXELS: usize = (WIDTH * HEIGHT) as usize; fn unknown_opcode(opcode: u16) { eprintln!("Unknwown opcode {:#06x}", opcode); std::process::exit(1); } pub struct Chip8 { v: [u8; 4096], 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, } impl Chip8 { pub fn new() -> Self { Self { v: [0; 4096], 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, } } 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; match opcode & 0xF000 { 0x0000 => match opcode & 0x000F { 0x0000 => { eprintln!("CLS"); self.screen = [0; PIXELS]; self.draw = true; self.pc += 2; } 0x000E => { eprintln!("RET"); self.pc += 2; } _ => unknown_opcode(opcode), }, 0x1000 => { let addr = opcode & 0x0FFF; eprintln!("JP {}", addr); self.pc = addr; } 0x6000 => { let x = (opcode & 0x0F00) >> 8; let value = opcode & 0x00FF; eprintln!("LD V{}, {}", x, value); self.v[x as usize] = value as u8; self.pc += 2; } 0x7000 => { let x = (opcode & 0x0F00) >> 8; let value = opcode & 0x00FF; eprintln!("ADD V{}, {}", x, value); self.v[x as usize] += value as u8; self.pc += 2; } 0xA000 => { self.i = opcode & 0x0FFF; self.pc += 2; eprintln!("LD I, {}", self.i); } 0xD000 => { let x = (opcode & 0x0F00) >> 8; let y = (opcode & 0x00F0) >> 4; let nibble = opcode & 0x000F; eprintln!("DRW V{}, V{}, {}", x, y, nibble); let vx = self.v[x as usize] as u16; let vy = self.v[y as usize] as u16; self.v[0xF] = 0; for yy in 0..nibble { 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; self.pc += 2; } _ => 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() } }