chip8/src/main.rs

212 lines
6.0 KiB
Rust

// 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 pixels::{wgpu::Surface, Pixels, SurfaceTexture};
use std::path::Path;
use std::time::{Duration, Instant};
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;
static OPCODES_PER_SECOND: u64 = 500;
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");
let mut start = Instant::now();
let frametime = Duration::from_nanos(1e+9 as u64 / OPCODES_PER_SECOND);
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);
}
if Instant::now().duration_since(start) > frametime {
chip8.execute_cycle();
start = Instant::now();
}
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);
}
}