diff --git a/src/chip_8/chip_8.rs b/src/chip_8/chip_8.rs index 07c1695..14718c5 100644 --- a/src/chip_8/chip_8.rs +++ b/src/chip_8/chip_8.rs @@ -5,12 +5,10 @@ use std::io::Read; pub fn run() { let mut cpu = CPU::new(); - cpu.ram.init_fonts(); //TODO: load in the RAM - cpu.clock.clock_hz = 60.; load_rom("src/chip_8/roms/CAVE.ch8", &mut cpu); let sdl_context = sdl2::init().unwrap(); - let mut display = Display::init(&sdl_context, cpu.db.scale); + let mut display = Display::init(&sdl_context, cpu.get_display_scale()); let mut event_listener = sdl_context.event_pump().unwrap(); 'runner: loop { @@ -25,57 +23,48 @@ pub fn run() { keycode: Some(sdl2::keyboard::Keycode::RightBracket), .. } => { - println!( - "Increasing cpu clock from {:5} Hz to {:5} Hz", - cpu.clock.clock_hz, - cpu.clock.clock_hz + 10. - ); - cpu.clock.clock_hz += 10.; + cpu.increase_clock(true); } sdl2::event::Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::LeftBracket), .. } => { - println!( - "Decreasing cpu clock from {:5} Hz to {:5} Hz", - cpu.clock.clock_hz, - cpu.clock.clock_hz - 10. - ); - cpu.clock.clock_hz -= 10.; + cpu.decrease_clock(true); } sdl2::event::Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::Backspace), .. } => { - cpu.pc.0 = 0x200; + cpu.reset_rom(); } sdl2::event::Event::KeyDown { keycode: Some(keycode), .. } => { - if let Some(key_index) = cpu.keypad.compute_keycode(keycode) { - cpu.keypad.press(key_index); + if let Some(key_index) = cpu.compute_keycode(keycode) { + cpu.press_key(key_index); } } sdl2::event::Event::KeyUp { keycode: Some(keycode), .. } => { - if let Some(key_index) = cpu.keypad.compute_keycode(keycode) { - cpu.keypad.release(key_index); + if let Some(key_index) = cpu.compute_keycode(keycode) { + cpu.release_key(key_index); } } _ => {} } } - cpu.dt.tick(); - cpu.st.tick(); - if cpu.clock.tick() { + cpu.tick_delay_timer(); + cpu.tick_sound_timer(); + if cpu.tick() { cpu.fetch(); cpu.decode(); - if cpu.decode_match("D???") { - display.draw(&cpu.db.db) + if cpu.should_redraw { + display.draw(&cpu.get_display_buffer()); + cpu.should_redraw = false; } } } @@ -86,5 +75,5 @@ fn load_rom(filename: &str, cpu: &mut CPU) { let metadata = std::fs::metadata(&filename).expect("unable to read metadata"); let mut buffer = vec![0; metadata.len() as usize]; f.read(&mut buffer).expect("buffer overflow"); - cpu.ram.write_rom(&buffer); + cpu.load_rom(&buffer); } diff --git a/src/chip_8/cpu.rs b/src/chip_8/cpu.rs index 8299370..5526b90 100644 --- a/src/chip_8/cpu.rs +++ b/src/chip_8/cpu.rs @@ -4,6 +4,5 @@ mod clock; mod display_buffer; mod keypad; mod opcodes; -mod program_counter; mod ram; mod registers; diff --git a/src/chip_8/cpu/clock.rs b/src/chip_8/cpu/clock.rs index 7ee67c5..5cc3d5d 100644 --- a/src/chip_8/cpu/clock.rs +++ b/src/chip_8/cpu/clock.rs @@ -2,7 +2,7 @@ use std::time::SystemTime; pub struct Clock { pub tick: u8, - pub clock_hz: f64, + clock_hz: f64, elapsed: SystemTime, } @@ -34,11 +34,35 @@ impl Clock { res } - pub fn reset_elapsed(&mut self) { + fn reset_elapsed(&mut self) { self.elapsed = SystemTime::now(); } pub fn clock_hz_as_secs_f64(&self) -> f64 { 1. / self.clock_hz } + + pub fn increase_clock(&mut self, is_printing: bool) { + if is_printing { + println!( + "Increasing cpu clock from {:5} Hz to {:5} Hz", + self.clock_hz, + self.clock_hz + 10. + ); + } + self.clock_hz += 10.; + } + + pub fn decrease_clock(&mut self, is_printing: bool) { + if self.clock_hz > 10. { + if is_printing { + println!( + "Decreasing cpu clock from {:5} Hz to {:5} Hz", + self.clock_hz, + self.clock_hz - 10. + ); + } + self.clock_hz -= 10.; + } + } } diff --git a/src/chip_8/cpu/cpu.rs b/src/chip_8/cpu/cpu.rs index a36846d..695e259 100644 --- a/src/chip_8/cpu/cpu.rs +++ b/src/chip_8/cpu/cpu.rs @@ -2,46 +2,48 @@ use super::clock::Clock; use super::display_buffer::{DisplayBuffer, DISPLAY_HEIGHT, DISPLAY_WIDTH}; use super::keypad::Keypad; use super::opcodes::OpCodes; -use super::program_counter::ProgramCounter; use super::ram::RAM; use super::registers::Registers; +use sdl2::keyboard::Keycode; + use rand::Rng; pub struct CPU { - pub pc: ProgramCounter, - pub i: usize, - pub stack: Vec, - pub dt: Clock, - pub st: Clock, - pub clock: Clock, - pub registers: Registers, - pub ram: RAM, - pub keypad: Keypad, - pub db: DisplayBuffer, - pub op: OpCodes, + stack: Vec, // Function Stack + dt: Clock, // Delay Timer + st: Clock, // Sound Timer + clock: Clock, // CPU Clock + regs: Registers, // Registers + ram: RAM, // RAM + keypad: Keypad, // Keypad + db: DisplayBuffer, // Display Buffer + op: OpCodes, // Operation Code, + pub should_redraw: bool // Boolean indicating Display Buffer update } impl CPU { pub fn new() -> CPU { + let mut ram = RAM::new(); + ram.init_fonts(); + CPU { - pc: ProgramCounter::new(), - i: 0, stack: vec![], dt: Clock::new(), st: Clock::new(), clock: Clock::new(), - registers: Registers::new(), - ram: RAM::new(), + regs: Registers::new(), + ram: ram, keypad: Keypad::new(), db: DisplayBuffer::new(10), op: OpCodes::new(0000), + should_redraw: false } } pub fn fetch(&mut self) { - self.op = OpCodes::new(self.ram.read16(self.pc.0)); - self.pc.increment(); + self.op = OpCodes::new(self.ram.read16(self.regs.pc)); + self.regs.increment_pc(); } pub fn decode(&mut self) { @@ -92,6 +94,7 @@ impl CPU { op_cxnn(self); } else if self.decode_match("D???") { op_dxyn(self); + self.should_redraw = true; } else if self.decode_match("E?9E") { op_ex9e(self); } else if self.decode_match("E?A1") { @@ -119,7 +122,7 @@ impl CPU { } } - pub fn decode_match(&self, hex_code: &str) -> bool { + fn decode_match(&self, hex_code: &str) -> bool { assert!( hex_code.len() == 4, "Instruction with wrong size. All chip-8 instructions have 16 bits" @@ -155,6 +158,66 @@ impl CPU { _ => false, } } + + pub fn reset_rom(&mut self) { + self.regs.reset_pc(); + } + + pub fn increase_clock(&mut self, is_printing: bool) { + self.clock.increase_clock(is_printing); + } + + pub fn decrease_clock(&mut self, is_printing: bool) { + self.clock.decrease_clock(is_printing); + } + + pub fn compute_keycode(&mut self, keycode: Keycode) -> Option { + self.keypad.compute_keycode(keycode) + } + + pub fn press_key(&mut self, key_index: usize) { + self.keypad.press(key_index); + } + + pub fn release_key(&mut self, key_index: usize) { + self.keypad.release(key_index); + } + + pub fn get_delay_timer(&self) -> u8 { + self.dt.tick + } + + pub fn set_delay_timer(&mut self, tick: u8) { + self.dt.tick = tick; + } + + pub fn set_sound_timer(&mut self, tick: u8) { + self.st.tick = tick; + } + + pub fn tick_delay_timer(&mut self) -> bool { + self.dt.tick() + } + + pub fn tick_sound_timer(&mut self) -> bool { + self.st.tick() + } + + pub fn tick(&mut self) -> bool { + self.clock.tick() + } + + pub fn load_rom(&mut self, rom: &[u8]) { + self.ram.load_rom(rom); + } + + pub fn get_display_scale(&self) -> u32 { + self.db.scale + } + + pub fn get_display_buffer(&self) -> [[bool; DISPLAY_HEIGHT]; DISPLAY_WIDTH] { + self.db.db + } } fn op_00e0(cpu: &mut CPU) { @@ -162,133 +225,133 @@ fn op_00e0(cpu: &mut CPU) { } fn op_1nnn(cpu: &mut CPU) { - cpu.pc.0 = cpu.op.nnn; + cpu.regs.pc = cpu.op.nnn; } fn op_00ee(cpu: &mut CPU) { let value = cpu.stack.pop(); match value { Some(value) => { - cpu.pc.0 = value; + cpu.regs.pc = value; } _ => {} } } fn op_2nnn(cpu: &mut CPU) { - cpu.stack.push(cpu.pc.0); - cpu.pc.0 = cpu.op.nnn; + cpu.stack.push(cpu.regs.pc); + cpu.regs.pc = cpu.op.nnn; } fn op_3xnn(cpu: &mut CPU) { - if cpu.registers.get(cpu.op.x) == cpu.op.nn { - cpu.pc.increment(); + if cpu.regs.get(cpu.op.x) == cpu.op.nn { + cpu.regs.increment_pc(); } } fn op_4xnn(cpu: &mut CPU) { - if cpu.registers.get(cpu.op.x) != cpu.op.nn { - cpu.pc.increment(); + if cpu.regs.get(cpu.op.x) != cpu.op.nn { + cpu.regs.increment_pc(); } } fn op_5xy0(cpu: &mut CPU) { - if cpu.registers.get(cpu.op.x) == cpu.registers.get(cpu.op.y) { - cpu.pc.increment(); + if cpu.regs.get(cpu.op.x) == cpu.regs.get(cpu.op.y) { + cpu.regs.increment_pc(); } } fn op_9xy0(cpu: &mut CPU) { - if cpu.registers.get(cpu.op.x) != cpu.registers.get(cpu.op.y) { - cpu.pc.increment(); + if cpu.regs.get(cpu.op.x) != cpu.regs.get(cpu.op.y) { + cpu.regs.increment_pc(); } } fn op_6xnn(cpu: &mut CPU) { - cpu.registers.set(cpu.op.x, cpu.op.nn); + cpu.regs.set(cpu.op.x, cpu.op.nn); } fn op_7xnn(cpu: &mut CPU) { - let vx = cpu.registers.get(cpu.op.x); - cpu.registers.set(cpu.op.x, cpu.op.nn.wrapping_add(vx)); + let vx = cpu.regs.get(cpu.op.x); + cpu.regs.set(cpu.op.x, cpu.op.nn.wrapping_add(vx)); } fn op_8xy0(cpu: &mut CPU) { - let vy = cpu.registers.get(cpu.op.y); - cpu.registers.set(cpu.op.x, vy); + let vy = cpu.regs.get(cpu.op.y); + cpu.regs.set(cpu.op.x, vy); } fn op_8xy1(cpu: &mut CPU) { - let vx = cpu.registers.get(cpu.op.x); - let vy = cpu.registers.get(cpu.op.y); - cpu.registers.set(cpu.op.x, vx | vy); + let vx = cpu.regs.get(cpu.op.x); + let vy = cpu.regs.get(cpu.op.y); + cpu.regs.set(cpu.op.x, vx | vy); } fn op_8xy2(cpu: &mut CPU) { - let vx = cpu.registers.get(cpu.op.x); - let vy = cpu.registers.get(cpu.op.y); - cpu.registers.set(cpu.op.x, vx & vy); + let vx = cpu.regs.get(cpu.op.x); + let vy = cpu.regs.get(cpu.op.y); + cpu.regs.set(cpu.op.x, vx & vy); } fn op_8xy3(cpu: &mut CPU) { - let vx = cpu.registers.get(cpu.op.x); - let vy = cpu.registers.get(cpu.op.y); - cpu.registers.set(cpu.op.x, vx ^ vy); + let vx = cpu.regs.get(cpu.op.x); + let vy = cpu.regs.get(cpu.op.y); + cpu.regs.set(cpu.op.x, vx ^ vy); } fn op_8xy4(cpu: &mut CPU) { - let vx = cpu.registers.get(cpu.op.x); - let vy = cpu.registers.get(cpu.op.y); - cpu.registers.set(cpu.op.x, vx.wrapping_add(vy)); + let vx = cpu.regs.get(cpu.op.x); + let vy = cpu.regs.get(cpu.op.y); + cpu.regs.set(cpu.op.x, vx.wrapping_add(vy)); } fn op_8xy5(cpu: &mut CPU) { - let vx = cpu.registers.get(cpu.op.x); - let vy = cpu.registers.get(cpu.op.y); - cpu.registers.set(cpu.op.x, vx.wrapping_sub(vy)); - cpu.registers.x_f = if vx > vy { 1 } else { 0 }; + let vx = cpu.regs.get(cpu.op.x); + let vy = cpu.regs.get(cpu.op.y); + cpu.regs.set(cpu.op.x, vx.wrapping_sub(vy)); + cpu.regs.set(0xF, if vx > vy { 1 } else { 0 }); } fn op_8xy6(cpu: &mut CPU) { - let vx = cpu.registers.get(cpu.op.x); - let vy = cpu.registers.get(cpu.op.y); - cpu.registers.set(cpu.op.x, vy >> 1); - cpu.registers.x_f = vx & 0x1; + let vx = cpu.regs.get(cpu.op.x); + let vy = cpu.regs.get(cpu.op.y); + cpu.regs.set(cpu.op.x, vy >> 1); + cpu.regs.set(0xF, vx & 0x1); } fn op_8xy7(cpu: &mut CPU) { - let vx = cpu.registers.get(cpu.op.x); - let vy = cpu.registers.get(cpu.op.y); - cpu.registers.set(cpu.op.x, vy.wrapping_sub(vx)); - cpu.registers.x_f = if vy > vx { 1 } else { 0 }; + let vx = cpu.regs.get(cpu.op.x); + let vy = cpu.regs.get(cpu.op.y); + cpu.regs.set(cpu.op.x, vy.wrapping_sub(vx)); + cpu.regs.set(0xF, if vy > vx { 1 } else { 0 }); } fn op_8xye(cpu: &mut CPU) { - let vx = cpu.registers.get(cpu.op.x); - let vy = cpu.registers.get(cpu.op.y); - cpu.registers.set(cpu.op.x, vy << 1); - cpu.registers.x_f = (vx & 0x80) >> 7; + let vx = cpu.regs.get(cpu.op.x); + let vy = cpu.regs.get(cpu.op.y); + cpu.regs.set(cpu.op.x, vy << 1); + cpu.regs.set(0xF, (vx & 0x80) >> 7); } fn op_annn(cpu: &mut CPU) { - cpu.i = cpu.op.nnn; + cpu.regs.i = cpu.op.nnn; } fn op_bnnn(cpu: &mut CPU) { - cpu.pc.0 = cpu.op.nnn + cpu.registers.x_0 as usize; + cpu.regs.pc = cpu.op.nnn + cpu.regs.get(0x0) as usize; } fn op_cxnn(cpu: &mut CPU) { let mut rng = rand::thread_rng(); - cpu.registers + cpu.regs .set(cpu.op.x, rng.gen_range(0x0..0xFF) & cpu.op.nn); } fn op_dxyn(cpu: &mut CPU) { let mut vf: bool = false; let value = cpu.op.n as usize; - let ori_x = cpu.registers.get(cpu.op.x) as usize % DISPLAY_WIDTH; - let ori_y = cpu.registers.get(cpu.op.y) as usize % DISPLAY_HEIGHT; + let ori_x = cpu.regs.get(cpu.op.x) as usize % DISPLAY_WIDTH; + let ori_y = cpu.regs.get(cpu.op.y) as usize % DISPLAY_HEIGHT; for row in 0..value { let y = ori_y + row; @@ -296,7 +359,7 @@ fn op_dxyn(cpu: &mut CPU) { break; } - let sprite = cpu.ram.read8(cpu.i + row); + let sprite = cpu.ram.read8(cpu.regs.i + row); for pixel_position in 0..8 { let x = ori_x + pixel_position; if x >= DISPLAY_WIDTH { @@ -309,70 +372,70 @@ fn op_dxyn(cpu: &mut CPU) { vf = (memory_pixel && display_pixel) || vf; } } - cpu.registers.x_f = if vf { 1 } else { 0 }; + cpu.regs.set(0xF, if vf { 1 } else { 0 }); } fn op_ex9e(cpu: &mut CPU) { - if cpu.keypad.get(cpu.registers.get(cpu.op.x) as usize) { - cpu.pc.increment(); + if cpu.keypad.get_status(cpu.regs.get(cpu.op.x) as usize) { + cpu.regs.increment_pc(); } } fn op_exa1(cpu: &mut CPU) { - if !cpu.keypad.get(cpu.registers.get(cpu.op.x) as usize) { - cpu.pc.increment(); + if !cpu.keypad.get_status(cpu.regs.get(cpu.op.x) as usize) { + cpu.regs.increment_pc(); } } fn op_fx07(cpu: &mut CPU) { - cpu.registers.set(cpu.op.x, cpu.dt.tick); + cpu.regs.set(cpu.op.x, cpu.get_delay_timer()); } fn op_fx15(cpu: &mut CPU) { - cpu.dt.tick = cpu.registers.get(cpu.op.x); + cpu.set_delay_timer(cpu.regs.get(cpu.op.x)); } fn op_fx18(cpu: &mut CPU) { - cpu.st.tick = cpu.registers.get(cpu.op.x); + cpu.set_sound_timer(cpu.regs.get(cpu.op.x)); } fn op_fx1e(cpu: &mut CPU) { - cpu.i += cpu.registers.get(cpu.op.x) as usize; + cpu.regs.i += cpu.regs.get(cpu.op.x) as usize; } fn op_fx0a(cpu: &mut CPU) { match cpu.keypad.being_pressed() { Some(key) => { - cpu.registers.set(cpu.op.x, key); + cpu.regs.set(cpu.op.x, key); } _ => { - cpu.pc.decrement(); + cpu.regs.decrement_pc(); } } } fn op_fx29(cpu: &mut CPU) { - let char = (cpu.registers.get(cpu.op.x) & 0xF) as usize; - cpu.i = cpu.ram.font_address + char * 5; + let char = (cpu.regs.get(cpu.op.x) & 0xF) as usize; + cpu.regs.i = cpu.ram.get_font_address() + char * 5; } fn op_fx33(cpu: &mut CPU) { - let vx = cpu.registers.get(cpu.op.x); - cpu.ram.write(cpu.i, vx / 100); - cpu.ram.write(cpu.i + 1, vx / 10 % 10); - cpu.ram.write(cpu.i + 2, vx % 10); + let vx = cpu.regs.get(cpu.op.x); + cpu.ram.write8(cpu.regs.i, vx / 100); + cpu.ram.write8(cpu.regs.i + 1, vx / 10 % 10); + cpu.ram.write8(cpu.regs.i + 2, vx % 10); } fn op_fx55(cpu: &mut CPU) { - let i = cpu.i; + let i = cpu.regs.i; for regs in 0x0..(cpu.op.x + 1) { - cpu.ram.write(i + regs, cpu.registers.get(regs)); + cpu.ram.write8(i + regs, cpu.regs.get(regs)); } } fn op_fx65(cpu: &mut CPU) { - let i = cpu.i; + let i = cpu.regs.i; for regs in 0x0..(cpu.op.x + 1) { - cpu.registers.set(regs, cpu.ram.read8(i + regs)); + cpu.regs.set(regs, cpu.ram.read8(i + regs)); } -} +} \ No newline at end of file diff --git a/src/chip_8/cpu/keypad.rs b/src/chip_8/cpu/keypad.rs index fb70353..a1ae97c 100644 --- a/src/chip_8/cpu/keypad.rs +++ b/src/chip_8/cpu/keypad.rs @@ -41,7 +41,7 @@ impl Keypad { } } - pub fn get(&mut self, pos: usize) -> bool { + pub fn get_status(&mut self, pos: usize) -> bool { self.key_status[pos] } diff --git a/src/chip_8/cpu/program_counter.rs b/src/chip_8/cpu/program_counter.rs deleted file mode 100644 index db19657..0000000 --- a/src/chip_8/cpu/program_counter.rs +++ /dev/null @@ -1,15 +0,0 @@ -pub struct ProgramCounter(pub usize); - -impl ProgramCounter { - pub fn new() -> ProgramCounter { - ProgramCounter { 0: 0x200 } - } - - pub fn increment(&mut self) { - self.0 += 2; - } - - pub fn decrement(&mut self) { - self.0 -= 2; - } -} diff --git a/src/chip_8/cpu/ram.rs b/src/chip_8/cpu/ram.rs index 53540b5..63b0636 100644 --- a/src/chip_8/cpu/ram.rs +++ b/src/chip_8/cpu/ram.rs @@ -1,6 +1,6 @@ pub struct RAM { ram: [u8; 4 * 1024], - pub font_address: usize, + font_address: usize, rom_address: usize, } @@ -37,7 +37,11 @@ impl RAM { } } - pub fn write_rom(&mut self, rom: &[u8]) { + pub fn get_font_address(&self) -> usize { + self.font_address + } + + pub fn load_rom(&mut self, rom: &[u8]) { assert!( rom.len() <= self.ram.len() - 0x200, "ROM is bigger than Chip-8 RAM" @@ -67,7 +71,7 @@ impl RAM { (self.ram[addr] as u16) << 8 | self.ram[addr + 1] as u16 } - pub fn write(&mut self, addr: usize, value: u8) { + pub fn write8(&mut self, addr: usize, value: u8) { assert!( addr < self.ram.len(), "addr = {}, self.0.len() = {}", diff --git a/src/chip_8/cpu/registers.rs b/src/chip_8/cpu/registers.rs index b457eac..ec3d57e 100644 --- a/src/chip_8/cpu/registers.rs +++ b/src/chip_8/cpu/registers.rs @@ -1,5 +1,7 @@ +const INITIAL_PC: usize = 0x200; + pub struct Registers { - pub x_0: u8, + x_0: u8, x_1: u8, x_2: u8, x_3: u8, @@ -14,7 +16,9 @@ pub struct Registers { x_c: u8, x_d: u8, x_e: u8, - pub x_f: u8, + x_f: u8, // Flag register + pub pc: usize, // Program Counter + pub i: usize // Index register } impl Registers { @@ -36,6 +40,8 @@ impl Registers { x_d: 0, x_e: 0, x_f: 0, + pc: INITIAL_PC, + i: 0, } } @@ -84,4 +90,16 @@ impl Registers { _ => {} } } + + pub fn increment_pc(&mut self) { + self.pc += 2; + } + + pub fn decrement_pc(&mut self) { + self.pc -= 2; + } + + pub fn reset_pc(&mut self) { + self.pc = INITIAL_PC; + } }