From 26a1b8584172b72181014c0bc250bf5fd2007b9f Mon Sep 17 00:00:00 2001 From: Ivsucram Date: Thu, 22 Jul 2021 19:39:56 +0800 Subject: [PATCH] Chip-8 emulator Complete Chip-8 instructions. However, the user still needs to modify the code to select the ROM. Next-step will be to insert SUPER-CHIP and OX-CHIP instructions, as well as to give menu options to the user. --- .gitignore | 3 +- src/chip_8/chip_8_base_interpreter.rs | 609 ++++++++++++++++++++++---- src/chip_8/display.rs | 20 +- 3 files changed, 541 insertions(+), 91 deletions(-) diff --git a/.gitignore b/.gitignore index 0905014..52c8b31 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /target Cargo.lock -/src/SDL2.dll \ No newline at end of file +/src/SDL2.dll +/src/chip_8/roms \ No newline at end of file diff --git a/src/chip_8/chip_8_base_interpreter.rs b/src/chip_8/chip_8_base_interpreter.rs index 0da62a6..2c993cf 100644 --- a/src/chip_8/chip_8_base_interpreter.rs +++ b/src/chip_8/chip_8_base_interpreter.rs @@ -1,28 +1,42 @@ // CHIP-8 means Compact Hexadecimal Interpretive Programming - 8-bit use ::sdl2; -use crate::chip_8::display; use rand::Rng; +use std::io::Read; + const DISPLAY_WIDTH: usize = 64; const DISPLAY_HEIGHT: usize = 32; const DISPLAY_SCALE: u32 = 10; pub struct ProgramCounter(usize); -pub struct Registers([u8; 16]); +pub struct Registers([u8; 0x10]); pub struct Ram([u8; 4 * 1024]); -pub struct Timer(u8); -pub struct Timing(u16); pub struct DisplayBuffer([[bool; DISPLAY_HEIGHT]; DISPLAY_WIDTH]); -pub struct Keypad { - keys: [bool; 0xF], - layout: String + +pub struct Timer{ + tick: u8, + clock_hz: u128, + elapsed: std::time::SystemTime +} + +pub struct Keypad { + keys: [bool; 0x10], + // layout: String +} + +pub struct OpCodes { + opcode: u16, + nible_1: u8, + nible_2: u8, + nible_3: u8, + nible_4: u8 } impl ProgramCounter { fn init() -> ProgramCounter { ProgramCounter { - 0: 0 + 0: 0x200 } } @@ -30,6 +44,10 @@ impl ProgramCounter { self.0 += 2; } + fn decrement(&mut self) { + self.0 -= 2; + } + fn set(&mut self, value: usize) { self.0 = value; } @@ -47,17 +65,11 @@ impl Registers { } fn get(&self, register: usize) -> u8 { - if register <= 0xF { - self.0[register] - } else { - 0 - } + self.0[register] } fn set(&mut self, register: usize, value: u8) { - if register <= 0xF { - self.0[register] = value; - } + self.0[register] = value; } } @@ -69,7 +81,8 @@ impl Ram { } fn init_fonts(&mut self) { - const FONTS: [u8; 80] = [ + let font_address = 0x50; + let fonts: [u8; 80] = [ 0xF0, 0x90, 0x90, 0x90, 0xF0, // 0 0x20, 0x60, 0x20, 0x20, 0x70, // 1 0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2 @@ -86,8 +99,8 @@ impl Ram { 0xE0, 0x90, 0x90, 0x90, 0xE0, // D 0xF0, 0x80, 0xF0, 0x80, 0xF0, // E 0xF0, 0x80, 0xF0, 0x80, 0x80]; // F - for i in 0x50..0x80 { - self.0[i] = FONTS[i]; + for i in 0..fonts.len() { + self.0[i + font_address] = fonts[i]; } } @@ -99,70 +112,114 @@ impl Ram { } fn read8(&self, addr: usize) -> u8 { - assert!(addr <= self.0.len()); + assert!(addr <= self.0.len(), "addr = {}, self.0.len() = {}", addr, self.0.len()); self.0[addr] } fn read16(&self, addr: usize) -> u16 { - assert!(addr < self.0.len()); - // byteorder::LittleEndiar::read_u16(&self.ram[addr]) + assert!(addr < self.0.len(), "addr = {}, self.0.len() = {}", addr, self.0.len()); + // byteorder::LittleEndian::read_u16(&self.0[addr]); // u16::from_le_bytes(self.ram[addr..addr+2]) // self.ram[addr..addr+2] (self.0[addr] as u16) << 8 | self.0[addr+1] as u16 } + + fn write8(&mut self, addr: usize, value: u8) { + assert!(addr < self.0.len()); + self.0[addr] = value; + } } impl Timer { fn init() -> Timer { Timer { - 0: 0 + tick: 255, + clock_hz: 60, + elapsed: std::time::SystemTime::now() } } fn tick(&mut self) -> bool { - self.0 -= 1; - self.0 == 0 + let mut res: bool = false; + match self.elapsed.elapsed() { + Ok(elapsed) => { + if elapsed.as_secs_f32() >= 1./(self.clock_hz as f32) { + if self.tick > 0 { self.tick -= 1; } + self.reset_elapsed(); + res = true; + } + } + Err(e) => { + println!("Error: {:?}", e); + } + } + res + } + + fn reset_elapsed(&mut self) { + self.elapsed = std::time::SystemTime::now(); } } impl Keypad { fn init() -> Keypad { Keypad { - keys: [false; 0xF], - layout: String::from("123C456D789EA0BF") + keys: [false; 0x10], + // layout: String::from("123C456D789EA0BF") } } - fn set_layout(&mut self, layout: &str) { - assert_eq!(layout.len(), self.layout.len()); - self.layout = layout.to_string(); - } -} + fn compute_keycode(&self, keycode: sdl2::keyboard::Keycode) -> Option { + match keycode { + sdl2::keyboard::Keycode::Num1 => Some(0x1), + sdl2::keyboard::Keycode::Num2 => Some(0x2), + sdl2::keyboard::Keycode::Num3 => Some(0x3), + sdl2::keyboard::Keycode::Num4 => Some(0xC), -impl Timing { - fn init() -> Timing { - Timing { - 0: 1000 // 1 MHz + sdl2::keyboard::Keycode::Q => Some(0x4), + sdl2::keyboard::Keycode::W => Some(0x5), + sdl2::keyboard::Keycode::E => Some(0x6), + sdl2::keyboard::Keycode::R => Some(0xD), + + sdl2::keyboard::Keycode::A => Some(0x7), + sdl2::keyboard::Keycode::S => Some(0x8), + sdl2::keyboard::Keycode::D => Some(0x9), + sdl2::keyboard::Keycode::F => Some(0xE), + + sdl2::keyboard::Keycode::Z => Some(0xA), + sdl2::keyboard::Keycode::X => Some(0x0), + sdl2::keyboard::Keycode::C => Some(0xB), + sdl2::keyboard::Keycode::V => Some(0xF), + + _ => Option::None, } } - fn increase(&mut self) { - self.0 += 10; + // fn set_layout(&mut self, layout: &str) { + // assert_eq!(layout.len(), self.layout.len()); + // self.layout = layout.to_string(); + // } + + fn get(&mut self, pos: usize) -> bool { + self.keys[pos] } - fn decrease(&mut self) { - self.0 -= 10; - } - - fn format(&self) -> String { - let mut res = String::new(); - if self.0 >= 1000 { - res = format!("{:.2}{}", self.0/1000, "MHz"); - } else { - res = format!("{}{}", self.0, "Hz"); + fn being_pressed(&self) -> u8 { + for key in 0x0..0x10 { + if self.keys[key] { + return key as u8 + } } - res + return 0x10 + } + + fn press(&mut self, key: usize) { + self.keys[key] = true; + } + + fn release(&mut self, key: usize) { + self.keys[key] = false; } } @@ -176,57 +233,455 @@ impl DisplayBuffer { fn clear(&mut self) { self.0 = [[false; DISPLAY_HEIGHT]; DISPLAY_WIDTH]; } +} - fn random(&mut self) { - for (x, col) in self.0.iter_mut().enumerate() { - for (y, pixel) in col.iter_mut().enumerate() { - let mut rng = rand::thread_rng(); - *pixel = rng.gen_range(0..2) == 0; - } +impl OpCodes { + fn init(opcode: u16) -> OpCodes { + OpCodes { + opcode: opcode, + nible_1: ((opcode & 0xF000) >> 12) as u8, + nible_2: ((opcode & 0x0F00) >> 8) as u8, + nible_3: ((opcode & 0x00F0) >> 4) as u8, + nible_4: (opcode & 0x000F) as u8 + } + } + + fn get_x(&self) -> usize { + self.nible_2 as usize + } + + fn get_y(&self) -> usize { + self.nible_3 as usize + } + + fn get_nnn(&self) -> u16 { + self.opcode & 0xFFF + } + + fn get_nn(&self) -> u8 { + (self.opcode & 0xFF) as u8 + } + + fn get_n(&self) -> u8 { + (self.opcode & 0xF) as u8 + } +} + +struct CPU { + pc: ProgramCounter, + index_register: usize, + stack: Vec, + delay_timer: Timer, + sound_timer: Timer, + clock: Timer, + registers: Registers, + ram: Ram, + keypad: Keypad, + display_buffer: DisplayBuffer +} + +impl CPU { + fn init() -> CPU { + CPU { + pc: ProgramCounter::init(), + index_register: 0, + stack: vec![], + delay_timer: Timer::init(), + sound_timer: Timer::init(), + clock: Timer::init(), + registers: Registers::init(), + ram: Ram::init(), + keypad: Keypad::init(), + display_buffer: DisplayBuffer::init() } } } pub fn run() { - let mut pc = ProgramCounter::init(); - let mut index_register: u16 = 0; - let mut stack: Vec; - let mut delay_timer = Timer::init(); - let mut sound_timer = Timer::init(); - let mut registers = Registers::init(); - let mut ram = Ram::init(); - let mut keypad = Keypad::init(); - let mut timing = Timing::init(); - let mut display_buffer = DisplayBuffer::init(); + let mut cpu = CPU::init(); + cpu.ram.init_fonts(); + // load_rom("src/chip_8/roms/ibm_logo.ch8", &mut cpu); + load_rom("src/chip_8/roms/BC_test.ch8", &mut cpu); + // load_rom("src/chip_8/roms/test_opcode.ch8", &mut cpu); + // load_rom("src/chip_8/roms/HIDDEN.ch8", &mut cpu); // Good to test keyboard + // load_rom("src/chip_8/roms/CAVE.ch8", &mut cpu); // Good to test keyboard + // load_rom("src/chip_8/roms/TRON.ch8", &mut cpu); // Good to test keyboard + // load_rom("src/chip_8/roms/PUZZLE.ch8", &mut cpu); // Good to test keyboard? + // load_rom("src/chip_8/roms/TETRIS.ch8", &mut cpu); // Good to test keyboard? + // load_rom("src/chip_8/roms/delay_timer_test.ch8", &mut cpu); + // load_rom("src/chip_8/roms/random_number_test.ch8", &mut cpu); let sdl_context = sdl2::init().unwrap(); - let mut display = crate::chip_8::display::Display::init(&sdl_context, DISPLAY_SCALE); - let mut event_listener = sdl_context.event_pump().unwrap(); - let mut is_running = true; + cpu.clock.clock_hz = 60; + 'runner: loop { for event in event_listener.poll_iter() { match event { - sdl2::event::Event::Quit {..} | sdl2::event::Event::KeyDown { + sdl2::event::Event::Quit {..} => break 'runner, + sdl2::event::Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::Escape), .. } => {break 'runner}, + sdl2::event::Event::KeyDown { + keycode: Some(sdl2::keyboard::Keycode::RightBracket), .. } => { + println!("Increasing cpu clock from {} Hz to {} Hz", cpu.clock.clock_hz, cpu.clock.clock_hz + 10); + cpu.clock.clock_hz += 10; + }, + sdl2::event::Event::KeyDown { + keycode: Some(sdl2::keyboard::Keycode::LeftBracket), .. } => { + println!("Decreasing cpu clock from {} Hz to {} Hz", cpu.clock.clock_hz, cpu.clock.clock_hz - 10); + cpu.clock.clock_hz -= 10; + }, + sdl2::event::Event::KeyDown { keycode: Some(keycode), ..} => { + if let Some(key_index) = cpu.keypad.compute_keycode(keycode) { + cpu.keypad.press(key_index); + } + }, + sdl2::event::Event::KeyUp { keycode: Some(keycode), ..} => { + if let Some(key_index) = cpu.keypad.compute_keycode(keycode) { + cpu.keypad.release(key_index); + } + }, _ => {} } } - - display_buffer.random(); - display.draw(&display_buffer.0) + + cpu.delay_timer.tick(); + cpu.sound_timer.tick(); + if cpu.clock.tick() { + let opcode = OpCodes::init(fetch(&mut cpu)); + decode(&opcode, &mut cpu); + if opcode.nible_1 == 0xD { + display.draw(&cpu.display_buffer.0) + } + } } } -pub fn fetch(ram: Ram, pc: &mut ProgramCounter) -> u16{ - let opcode = ram.read16(pc.get()); - pc.increase(); +fn load_rom(filename: &str, cpu: &mut CPU) { + let mut f = std::fs::File::open(&filename).expect("no file found"); + 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.load_rom(&buffer); +} + +fn fetch(cpu: &mut CPU) -> u16 { + let opcode = cpu.ram.read16(cpu.pc.get()); + cpu.pc.increase(); opcode } -pub fn decode() {} +fn decode(opcode: &OpCodes, cpu: &mut CPU) { + if opcode.opcode == 0x00E0 { + op_00e0(cpu); + } else if opcode.nible_1 == 0x1 { + op1nnn(cpu, opcode.get_nnn() as usize); + } else if opcode.opcode == 0x00EE { + op00ee(cpu); + } else if opcode.nible_1 == 0x2 { + op2nnn(cpu, opcode.get_nnn() as usize); + } else if opcode.nible_1 == 0x3 { + op3xnn(cpu, opcode.get_x(), opcode.get_nn()); + } else if opcode.nible_1 == 0x4 { + op4xnn(cpu, opcode.get_x(), opcode.get_nn()); + } else if opcode.nible_1 == 0x5 { + op5xy0(cpu, opcode.get_x(), opcode.get_y()); + } else if opcode.nible_1 == 0x9 { + op9xy0(cpu, opcode.get_x(), opcode.get_y()); + } else if opcode.nible_1 == 0x6 { + op6xnn(cpu, opcode.get_x(), opcode.get_nn()); + } else if opcode.nible_1 == 0x7 { + op7xnn(cpu, opcode.get_x(), opcode.get_nn()); + } else if opcode.nible_1 == 0x8 && opcode.nible_4 == 0x0 { + op8xy0(cpu, opcode.get_x(), opcode.get_y()); + } else if opcode.nible_1 == 0x8 && opcode.nible_4 == 0x1 { + op8xy1(cpu, opcode.get_x(), opcode.get_y()); + } else if opcode.nible_1 == 0x8 && opcode.nible_4 == 0x2 { + op8xy2(cpu, opcode.get_x(), opcode.get_y()); + } else if opcode.nible_1 == 0x8 && opcode.nible_4 == 0x3 { + op8xy3(cpu, opcode.get_x(), opcode.get_y()); + } else if opcode.nible_1 == 0x8 && opcode.nible_4 == 0x4 { + op8xy4(cpu, opcode.get_x(), opcode.get_y()); + } else if opcode.nible_1 == 0x8 && opcode.nible_4 == 0x5 { + op8xy5(cpu, opcode.get_x(), opcode.get_y()); + } else if opcode.nible_1 == 0x8 && opcode.nible_4 == 0x6 { + op8xy6(cpu, opcode.get_x(), opcode.get_y()); + } else if opcode.nible_1 == 0x8 && opcode.nible_4 == 0x7 { + op8xy7(cpu, opcode.get_x(), opcode.get_y()); + } else if opcode.nible_1 == 0x8 && opcode.nible_4 == 0xE { + op8xye(cpu, opcode.get_x(), opcode.get_y()); + } else if opcode.nible_1 == 0xA { + opannn(cpu, opcode.get_nnn() as usize); + } else if opcode.nible_1 == 0xB { + opbnnn(cpu, opcode.get_nnn() as usize); + } else if opcode.nible_1 == 0xC { + opcxnn(cpu, opcode.get_x(), opcode.get_nn()); + } else if opcode.nible_1 == 0xD { + opdxyn(cpu, opcode.get_x(), opcode.get_y(), opcode.get_n()); + } else if opcode.nible_1 == 0xE && opcode.nible_3 == 0x9 && opcode.nible_4 == 0xE { + opex9e(cpu, opcode.get_x()); + } else if opcode.nible_1 == 0xE && opcode.nible_3 == 0x9 && opcode.nible_4 == 0xE { + opex9e(cpu, opcode.get_x()); + } else if opcode.nible_1 == 0xE && opcode.nible_3 == 0xA && opcode.nible_4 == 0x1 { + opexa1(cpu, opcode.get_x()); + } else if opcode.nible_1 == 0xF && opcode.nible_3 == 0x0 && opcode.nible_4 == 0x7 { + opfx07(cpu, opcode.get_x()); + } else if opcode.nible_1 == 0xF && opcode.nible_3 == 0x1 && opcode.nible_4 == 0x5 { + opfx15(cpu, opcode.get_x()); + } else if opcode.nible_1 == 0xF && opcode.nible_3 == 0x1 && opcode.nible_4 == 0x8 { + opfx18(cpu, opcode.get_x()); + } else if opcode.nible_1 == 0xF && opcode.nible_3 == 0x1 && opcode.nible_4 == 0xE { + opfx1e(cpu, opcode.get_x()); + } else if opcode.nible_1 == 0xF && opcode.nible_3 == 0x0 && opcode.nible_4 == 0xA { + opfx0a(cpu, opcode.get_x()); + } else if opcode.nible_1 == 0xF && opcode.nible_3 == 0x2 && opcode.nible_4 == 0x9 { + opfx29(cpu, opcode.get_x()); + } else if opcode.nible_1 == 0xF && opcode.nible_3 == 0x3 && opcode.nible_4 == 0x3 { + opfx33(cpu, opcode.get_x()); + } else if opcode.nible_1 == 0xF && opcode.nible_3 == 0x5 && opcode.nible_4 == 0x5 { + opfx55(cpu, opcode.get_x()); + } else if opcode.nible_1 == 0xF && opcode.nible_3 == 0x6 && opcode.nible_4 == 0x5 { + opfx65(cpu, opcode.get_x()); + } else { + println!{"Unknown instruction: {:04x}", opcode.opcode}; + } +} +// fn execute() {} -pub fn execute() {} +// fn op_0NNN() {}//ignore +// CLS +// Clear the display, turning all pixels off +fn op_00e0(cpu: &mut CPU) { + cpu.display_buffer.clear(); +} +// JMP +// Set PC to NNN, causing the program to jump to that memory location +fn op1nnn(cpu: &mut CPU, value: usize) { + cpu.pc.set(value); +} + +fn op00ee(cpu: &mut CPU) { + let value = cpu.stack.pop(); + match value { + Some(value) => { + cpu.pc.set(value); + } + _ => {} + } +} + +fn op2nnn(cpu: &mut CPU, value: usize) { + cpu.stack.push(cpu.pc.get()); + cpu.pc.set(value); +} + +fn op3xnn(cpu: &mut CPU, register_x: usize, value: u8) { + if cpu.registers.get(register_x) == value { + cpu.pc.increase(); + } +} + +fn op4xnn(cpu: &mut CPU, register_x: usize, value: u8) { + if cpu.registers.get(register_x) != value { + cpu.pc.increase(); + } +} + +fn op5xy0(cpu: &mut CPU, register_x: usize, register_y: usize) { + if cpu.registers.get(register_x) == cpu.registers.get(register_y) { + cpu.pc.increase(); + } +} + +fn op9xy0(cpu: &mut CPU, register_x: usize, register_y: usize) { + if cpu.registers.get(register_x) != cpu.registers.get(register_y) { + cpu.pc.increase(); + } +} + +// SET +// Set the register VX to the value NN +fn op6xnn(cpu: &mut CPU, register: usize, value: u8) { + cpu.registers.set(register, value); +} + +// ADD +// Add the value NN to VX. VF is ignored +fn op7xnn(cpu: &mut CPU, register: usize, value: u8) { + let nn = value as u16; + let vx = cpu.registers.get(register) as u16; + let value = (nn + vx) & 0xFF; + let value = value as u8; + cpu.registers.set(register, value); +} + +fn op8xy0(cpu: &mut CPU, register_x: usize, register_y: usize) { + cpu.registers.set(register_x, cpu.registers.get(register_y)); +} + +fn op8xy1(cpu: &mut CPU, register_x: usize, register_y: usize) { + let vx = cpu.registers.get(register_x); + let vy = cpu.registers.get(register_y); + cpu.registers.set(register_x, vx | vy); +} + +fn op8xy2(cpu: &mut CPU, register_x: usize, register_y: usize) { + let vx = cpu.registers.get(register_x); + let vy = cpu.registers.get(register_y); + cpu.registers.set(register_x, vx & vy); +} + +fn op8xy3(cpu: &mut CPU, register_x: usize, register_y: usize) { + let vx = cpu.registers.get(register_x); + let vy = cpu.registers.get(register_y); + cpu.registers.set(register_x, vx ^ vy); +} + +fn op8xy4(cpu: &mut CPU, register_x: usize, register_y: usize) { + let vx = cpu.registers.get(register_x) as u16; + let vy = cpu.registers.get(register_y) as u16; + let value = (vx + vy) & 0xFF; + cpu.registers.set(register_x, value as u8); +} + +fn op8xy5(cpu: &mut CPU, register_x: usize, register_y: usize) { + let vx = cpu.registers.get(register_x); + let vy = cpu.registers.get(register_y); + cpu.registers.set(register_x, vx.wrapping_sub(vy)); + cpu.registers.set(0xF, if vx > vy {1} else {0}); +} + +fn op8xy6(cpu: &mut CPU, register_x: usize, register_y: usize) { + let vy = cpu.registers.get(register_y); + let vx = cpu.registers.get(register_x); + cpu.registers.set(register_x, vy >> 1); + cpu.registers.set(0xF, vx & 0x1); +} + +fn op8xy7(cpu: &mut CPU, register_x: usize, register_y: usize) { + let vx = cpu.registers.get(register_x); + let vy = cpu.registers.get(register_y); + cpu.registers.set(register_x, vy.wrapping_sub(vx)); + cpu.registers.set(0xF, if vy > vx {1} else {0}); +} + +fn op8xye(cpu: &mut CPU, register_x: usize, register_y: usize) { + let vy = cpu.registers.get(register_y); + let vx = cpu.registers.get(register_x); + cpu.registers.set(register_x, vy << 1); + cpu.registers.set(0xF, (vx & 0x80) >> 7); + +} + +// IND +// Set index register I to the value NNN +fn opannn(cpu: &mut CPU, value: usize) { + cpu.index_register = value; +} + +fn opbnnn(cpu: &mut CPU, value: usize) { + let v0 = cpu.registers.get(0x0) as usize; + cpu.pc.set(value + v0); +} + +fn opcxnn(cpu: &mut CPU, register_x: usize, value: u8) { + let mut rng = rand::thread_rng(); + cpu.registers.set(register_x,rng.gen_range(0x0..0xFF) & value); +} + +// DIS +// Display +fn opdxyn(cpu: &mut CPU, register_x: usize, register_y: usize, value: u8) { + let mut vf: bool = false; + let value = value as usize; + let ori_x = cpu.registers.get(register_x) as usize % DISPLAY_WIDTH; + let ori_y = cpu.registers.get(register_y) as usize % DISPLAY_HEIGHT; + + for row in 0..value { + let y = ori_y + row; + if y >= DISPLAY_HEIGHT { + break; + } + + let sprite = cpu.ram.read8(cpu.index_register + row); + for pixel_position in 0..8 { + let x = ori_x + pixel_position; + if x >= DISPLAY_WIDTH { + break; + } + + let memory_pixel: bool = (sprite & (1 << (7 - pixel_position))) > 0; + let display_pixel: bool = cpu.display_buffer.0[x][y]; + cpu.display_buffer.0[x][y] = memory_pixel ^ display_pixel; + vf = (memory_pixel && display_pixel) || vf; + } + } + cpu.registers.set(0xF, if vf {1} else {0}); +} + +fn opex9e(cpu: &mut CPU, register_x: usize) { + if cpu.keypad.get(cpu.registers.get(register_x) as usize) { + cpu.pc.increase(); + } +} + +fn opexa1(cpu: &mut CPU, register_x: usize) { + if !cpu.keypad.get(cpu.registers.get(register_x) as usize) { + cpu.pc.increase(); + } +} + +fn opfx07(cpu: &mut CPU, register_x: usize) { + cpu.registers.set(register_x, cpu.delay_timer.tick); +} + +fn opfx15(cpu: &mut CPU, register_x: usize) { + cpu.delay_timer.tick = cpu.registers.get(register_x); +} + +fn opfx18(cpu: &mut CPU, register_x: usize) { + cpu.sound_timer.tick = cpu.registers.get(register_x); +} + +fn opfx1e(cpu: &mut CPU, register_x: usize) { + cpu.index_register += cpu.registers.get(register_x) as usize; +} + +fn opfx0a(cpu: &mut CPU, register_x: usize) { + if cpu.keypad.being_pressed() == 0x10 { + cpu.pc.decrement(); + } else { + cpu.registers.set(register_x, cpu.keypad.being_pressed()); + } +} + +fn opfx29(cpu: &mut CPU, register_x: usize) { + let char = (cpu.registers.get(register_x) & 0xF) as usize; + cpu.index_register = 0x50 + char * 5; +} + +fn opfx33(cpu: &mut CPU, register_x: usize) { + let vx = cpu.registers.get(register_x); + cpu.ram.write8(cpu.index_register, vx / 100); + cpu.ram.write8(cpu.index_register + 1, vx / 10 % 10); + cpu.ram.write8(cpu.index_register + 2, vx % 10); +} + +fn opfx55(cpu: &mut CPU, register_x: usize) { + let i = cpu.index_register; + for regs in 0x0..(register_x + 1) { + cpu.ram.write8(i + regs, cpu.registers.get(regs)); + } + // cpu.index_register += register_x + 1; +} + +fn opfx65(cpu: &mut CPU, register_x: usize) { + let i = cpu.index_register; + for regs in 0x0..(register_x + 1) { + cpu.registers.set(regs, cpu.ram.read8(i + regs)); + } + // cpu.index_register += register_x + 1; +} \ No newline at end of file diff --git a/src/chip_8/display.rs b/src/chip_8/display.rs index a3a331c..9e99059 100644 --- a/src/chip_8/display.rs +++ b/src/chip_8/display.rs @@ -1,21 +1,15 @@ -use sdl2::pixels::Color; -use sdl2::rect::Rect; -use sdl2::render::Canvas; -use sdl2::Sdl; -use sdl2::video::Window; - const WIDTH: usize = 64; const HEIGHT: usize = 32; pub struct Display { - canvas: Canvas, + canvas: sdl2::render::Canvas, scale: u32, - off_color: Color, - on_color: Color + off_color: sdl2::pixels::Color, + on_color: sdl2::pixels::Color } impl Display { - pub fn init(sdl_context: &Sdl, scale: u32) -> Display { + pub fn init(sdl_context: &sdl2::Sdl, scale: u32) -> Display { let video_subsystem = sdl_context.video().unwrap(); let window = video_subsystem.window("Chip-8", WIDTH as u32 * scale, @@ -29,8 +23,8 @@ impl Display { Display { canvas: canvas, scale: scale, - off_color: Color::RGB(0, 0, 0), - on_color: Color::RGB(255, 255, 255) + off_color: sdl2::pixels::Color::RGB(0, 0, 0), + on_color: sdl2::pixels::Color::RGB(255, 255, 255) } } @@ -46,7 +40,7 @@ impl Display { let y = (y as u32 * self.scale) as i32; let width = self.scale; let height = self.scale; - self.canvas.fill_rect(Rect::new(x, y, width, height)) + self.canvas.fill_rect(sdl2::rect::Rect::new(x, y, width, height)) .expect("Failed to draw pixel"); } }