#![feature( exclusive_range_pattern, let_chains, slice_flatten, async_closure, bigint_helper_methods )] use crate::{ processor::memory::Memory, util::{pause, print_cycles}, }; use gilrs::Gilrs; use minifb::Window; use once_cell::sync::OnceCell; use processor::{memory::Rom, Cpu}; use std::{ fs, io::{stdout, Write}, }; use util::pause_then_step; mod constants; mod processor; pub mod util; pub struct Options { pub rom_path: String, pub bootrom_path: Option, pub connect_serial: bool, pub verbose: bool, pub step_by: Option, pub cycle_count: bool, pub factor: usize, } static mut PAUSE_ENABLED: bool = false; static mut PAUSE_QUEUED: bool = false; static VERBOSE: OnceCell = OnceCell::new(); pub const WIDTH: usize = 160; pub const HEIGHT: usize = 144; static FACTOR: OnceCell = OnceCell::new(); #[allow(clippy::too_many_arguments)] pub fn init(options: Options, mut window: Window, tile_window: bool) { VERBOSE.set(options.verbose).unwrap(); FACTOR.set(options.factor).unwrap(); crate::processor::memory::mmio::gpu::init_statics(); let rom: Rom = match fs::read(options.rom_path) { Ok(data) => Rom::load(data), Err(e) => { println!("Error reading ROM: {e}"); return; } }; window.set_title(format!("{} on {}", rom.get_title(), rom.mbc_type()).as_str()); let bootrom_enabled = options.bootrom_path.is_some(); let bootrom: Option> = if let Some(path) = options.bootrom_path { match fs::read(path) { Ok(data) => Some(data), Err(e) => { println!("Error reading bootROM: {e}"); return; } } } else { None }; let mut cpu = Cpu::new( Memory::init(bootrom, rom, window, options.connect_serial, tile_window), bootrom_enabled, Gilrs::new().unwrap(), ); let mut cycle_num = 0; verbose_println!("\n\n Begin execution...\n"); match options.step_by { Some(step_size) => loop { for _ in 0..step_size { cycle_num += 1; if options.cycle_count { print_cycles(&cycle_num); } run_cycle(&mut cpu); } print!(" ...{cycle_num} cycles - press enter to continue\r"); stdout().flush().unwrap(); pause(); }, None => loop { cycle_num += 1; if options.cycle_count { print_cycles(&cycle_num); } run_cycle(&mut cpu); }, } } fn run_cycle(cpu: &mut Cpu) { let will_pause = unsafe { PAUSE_QUEUED }; let pause_enabled = unsafe { PAUSE_ENABLED }; cpu.exec_next(); if !pause_enabled && cpu.reg.pc >= 0x100 { unsafe { PAUSE_ENABLED = true }; } if will_pause { pause_then_step(); } }