// I used: http://devernay.free.fr/hacks/chip8/C8TECH10.HTM#00E0 // and https://en.wikipedia.org/wiki/CHIP-8#Opcode_table // for information about how to implement a CHIP-8 Emulator use chip8::emu::Chip8; use std::path::Path; use pixels::{wgpu::Surface, Pixels, SurfaceTexture}; use winit::dpi::LogicalSize; use winit::event::{Event, VirtualKeyCode}; use winit::event_loop::{ControlFlow, EventLoop}; use winit::window::{Window, WindowBuilder}; use winit_input_helper::WinitInputHelper; static WIDTH: u32 = 64; static HEIGHT: u32 = 32; fn main() { let mut chip8: Chip8 = Default::default(); let mut input = WinitInputHelper::new(); let event_loop = EventLoop::new(); let window = init_window(&event_loop); let mut pixels = init_pixels(&window); let rom_path = Path::new("./games/c8games/INVADERS"); chip8.load_rom(rom_path).expect("Unable to load ROM"); event_loop.run(move |event, _, control_flow| { if let Event::RedrawRequested(_) = event { draw(&chip8.display.buf, pixels.get_frame()); if pixels .render() .map_err(|e| eprintln!("pixels.render() failed: {}", e)) .is_err() { *control_flow = ControlFlow::Exit; return; } } if input.update(&event) { handle_input(&mut chip8, &mut input, control_flow); if let Some(size) = input.window_resized() { pixels.resize(size.width, size.height); } chip8.execute_cycle(); if chip8.request_redraw { window.request_redraw(); } } }); } fn handle_input(chip8: &mut Chip8, input: &mut WinitInputHelper, control_flow: &mut ControlFlow) { if input.key_pressed(VirtualKeyCode::Escape) || input.quit() { *control_flow = ControlFlow::Exit; return; } // 1 -> 1 if input.key_pressed(VirtualKeyCode::Key1) { chip8.key.set_key(0x1); } if input.key_released(VirtualKeyCode::Key1) { chip8.key.unset_key(0x1); } // 2 -> 2 if input.key_pressed(VirtualKeyCode::Key2) { chip8.key.set_key(0x2); } if input.key_released(VirtualKeyCode::Key2) { chip8.key.unset_key(0x2); } // 3 -> 3 if input.key_pressed(VirtualKeyCode::Key3) { chip8.key.set_key(0x3); } if input.key_released(VirtualKeyCode::Key3) { chip8.key.unset_key(0x3); } // 4 -> C if input.key_pressed(VirtualKeyCode::Key4) { chip8.key.set_key(0xC); } if input.key_released(VirtualKeyCode::Key4) { chip8.key.unset_key(0xC); } // Q -> 4 if input.key_pressed(VirtualKeyCode::Q) { chip8.key.set_key(0x4); } if input.key_released(VirtualKeyCode::Q) { chip8.key.unset_key(0x4); } // W -> 5 if input.key_pressed(VirtualKeyCode::W) { chip8.key.set_key(0x5); } if input.key_released(VirtualKeyCode::W) { chip8.key.unset_key(0x5); } // E -> 6 if input.key_pressed(VirtualKeyCode::E) { chip8.key.set_key(0x6); } if input.key_released(VirtualKeyCode::E) { chip8.key.unset_key(0x6); } // R -> D if input.key_pressed(VirtualKeyCode::R) { chip8.key.set_key(0xD); } if input.key_released(VirtualKeyCode::R) { chip8.key.unset_key(0xD); } // A -> 7 if input.key_pressed(VirtualKeyCode::A) { chip8.key.set_key(0x7); } if input.key_released(VirtualKeyCode::A) { chip8.key.unset_key(0x7); } // S -> 8 if input.key_pressed(VirtualKeyCode::S) { chip8.key.set_key(0x8); } if input.key_released(VirtualKeyCode::S) { chip8.key.unset_key(0x8); } // D -> 9 if input.key_pressed(VirtualKeyCode::D) { chip8.key.set_key(0x9); } if input.key_released(VirtualKeyCode::D) { chip8.key.unset_key(0x9); } // F -> A if input.key_pressed(VirtualKeyCode::F) { chip8.key.set_key(0xE); } if input.key_released(VirtualKeyCode::F) { chip8.key.unset_key(0xE); } // Z -> A if input.key_pressed(VirtualKeyCode::Z) { chip8.key.set_key(0xA); } if input.key_released(VirtualKeyCode::Z) { chip8.key.unset_key(0xA); } // X -> 0 if input.key_pressed(VirtualKeyCode::X) { chip8.key.set_key(0x0); } if input.key_released(VirtualKeyCode::X) { chip8.key.unset_key(0x0); } // C -> B if input.key_pressed(VirtualKeyCode::C) { chip8.key.set_key(0xB); } if input.key_released(VirtualKeyCode::C) { chip8.key.unset_key(0xB); } // V -> F if input.key_pressed(VirtualKeyCode::V) { chip8.key.set_key(0xF); } if input.key_released(VirtualKeyCode::V) { chip8.key.unset_key(0xF); } } fn init_pixels(window: &Window) -> Pixels { let surface = Surface::create(window); let texture = SurfaceTexture::new(WIDTH, HEIGHT, surface); Pixels::new(WIDTH, HEIGHT, texture).unwrap() } fn init_window(event_loop: &EventLoop<()>) -> Window { const SCALE: u32 = 10; let min_size = LogicalSize::new(WIDTH as f64, HEIGHT as f64); let size = LogicalSize::new((WIDTH * SCALE) as f64, (HEIGHT * SCALE) as f64); WindowBuilder::new() .with_title("Chip8 Emulator") .with_inner_size(size) .with_min_inner_size(min_size) .build(event_loop) .unwrap() } fn draw(chip8_gfx: &[u8], frame: &mut [u8]) { for (i, pixel) in frame.chunks_exact_mut(4).enumerate() { let rgba = if chip8_gfx[i] == 1 { [0xFF; 4] } else { [0x00; 4] }; pixel.copy_from_slice(&rgba); } }