Skip to content
Snippets Groups Projects
main.rs 5.44 KiB
Newer Older
  • Learn to ignore specific revisions
  • use chip_ahoyto::chip8::{Chip8, SCREEN_PIXEL_HEIGHT, SCREEN_PIXEL_WIDTH};
    
    use sdl2::{event::Event, keyboard::Keycode, pixels::PixelFormatEnum, surface::Surface, image::LoadSurface};
    use std::{fs::File, io::Read};
    
    
    const PIXEL_SET: [u8; 3] = [80, 203, 147];
    const SYSTEM_HZ: u32 = 240;
    const SCREEN_SCALE: f32 = 15.0;
    
    const TITLE: &str = "CHIP-Ahoyto - Drag and drop the ROM file to play";
    
    
    fn main() {
        let sdl = sdl2::init().unwrap();
        let video_subsystem = sdl.video().unwrap();
        let mut timer_subsystem = sdl.timer().unwrap();
    
    
    João Magalhães's avatar
    João Magalhães committed
        // creates the system window that is going to be used to
        // show the emulator and sets it to the central are o screen
    
        let mut window = video_subsystem
    
            .window(
                TITLE,
                SCREEN_SCALE as u32 * SCREEN_PIXEL_WIDTH as u32,
                SCREEN_SCALE as u32 * SCREEN_PIXEL_HEIGHT as u32,
            )
            .resizable()
            .position_centered()
            .build()
            .unwrap();
    
    
        // updates the icon of the window to reflect the image
        // and style of the emulator
    
        let surface = Surface::from_file("./resources/icon.png").unwrap();
        window.set_icon(&surface);
    
        let mut canvas = window.into_canvas().build().unwrap();
        canvas.set_scale(SCREEN_SCALE, SCREEN_SCALE).unwrap();
        canvas.clear();
        canvas.present();
    
        let texture_creator = canvas.texture_creator();
        let mut texture = texture_creator
            .create_texture_streaming(
                PixelFormatEnum::RGB24,
                SCREEN_PIXEL_WIDTH as u32,
                SCREEN_PIXEL_HEIGHT as u32,
            )
            .unwrap();
    
    
        // creates a texture for the surface and presents it to
        // to the screen creating a call to action to drag and
        // drop the image into the screen
        let background = texture_creator.create_texture_from_surface(&surface).unwrap();
        canvas.copy(&background, None, None).unwrap();
        canvas.present();
    
        // builds the CHIP-8 machine, this is the instance that
        // is going to logically represent the CHIP-8
    
        let mut chip8 = Chip8::new();
        let mut game_loaded = false;
    
        let tick_interval = 1000 / SYSTEM_HZ;
        let mut last_update_time = 0;
        let mut event_pump = sdl.event_pump().unwrap();
    
        'main: loop {
            while let Some(event) = event_pump.poll_event() {
                match event {
                    Event::Quit { .. } => break 'main,
                    Event::KeyDown {
                        keycode: Some(Keycode::Escape),
                        ..
                    } => break 'main,
    
                    Event::DropFile { filename, .. } => {
                        let rom = read_file(&filename);
                        chip8 = Chip8::new();
                        chip8.load_rom(&rom);
                        chip8.reset();
                        game_loaded = true;
                        canvas
                            .window_mut()
                            .set_title(&format!("{} [Currently playing: {}]", TITLE, filename))
                            .unwrap();
                        None
                    }
    
                    Event::KeyDown {
                        keycode: Some(keycode),
                        ..
                    } if game_loaded => key_to_btn(keycode).map(|btn| chip8.key_press(btn)),
    
                    Event::KeyUp {
                        keycode: Some(keycode),
                        ..
                    } if game_loaded => key_to_btn(keycode).map(|btn| chip8.key_lift(btn)),
    
                    _ => None,
                };
            }
    
            let current_time = timer_subsystem.ticks();
            let delta_t = current_time - last_update_time;
            if game_loaded && tick_interval > delta_t {
    
                // runs the tick operation in the CHIP-8 system,
                // effectively changing the logic state of the machine
    
                chip8.clock();
                chip8.clock_dt();
                chip8.clock_st();
    
    
                // @todo not sure if the delay should come here
    
                timer_subsystem.delay(tick_interval - delta_t);
    
    
                // @todo this looks to be very slow!
                // we should use a callback on pixel buffer change
                // to make this a faster thing
    
                let mut rgb_pixels = vec![];
                for p in chip8.pixels() {
                    rgb_pixels.extend_from_slice(&[
                        p * PIXEL_SET[0],
                        p * PIXEL_SET[1],
                        p * PIXEL_SET[2],
                    ])
                }
    
    
                // creates a texture based on the RGB pixel buffer
                // and copies that to the canvas for presentation
    
                texture
                    .update(None, &rgb_pixels, SCREEN_PIXEL_WIDTH as usize * 3)
                    .unwrap();
                canvas.copy(&texture, None, None).unwrap();
                canvas.present();
            }
            last_update_time = current_time;
        }
    }
    
    fn key_to_btn(keycode: Keycode) -> Option<u8> {
        match keycode {
            Keycode::Num1 => Some(0x01),
            Keycode::Num2 => Some(0x02),
            Keycode::Num3 => Some(0x03),
            Keycode::Num4 => Some(0x0C),
            Keycode::Q => Some(0x04),
            Keycode::W => Some(0x05),
            Keycode::E => Some(0x06),
            Keycode::R => Some(0x0D),
            Keycode::A => Some(0x07),
            Keycode::S => Some(0x08),
            Keycode::D => Some(0x09),
            Keycode::F => Some(0x0E),
            Keycode::Z => Some(0x0A),
            Keycode::X => Some(0x00),
            Keycode::C => Some(0x0B),
            Keycode::V => Some(0x0F),
            _ => None,
        }
    }
    
    fn read_file(path: &str) -> Vec<u8> {
        let mut file = File::open(path).unwrap();
        let mut rom = Vec::new();
        file.read_to_end(&mut rom).unwrap();
        rom
    }