#![feature( exclusive_range_pattern, let_chains, slice_flatten, async_closure, bigint_helper_methods )] use std::{ fs, io::{self, stdout, Write}, }; use gilrs::Gilrs; use minifb::Window; use once_cell::sync::OnceCell; use processor::{memory::Rom, Cpu}; use crate::processor::memory::Memory; mod constants; mod processor; mod util; #[macro_export] macro_rules! verbose_println { ($($tts:tt)*) => { if $crate::is_verbose() { println!($($tts)*); } }; } #[macro_export] macro_rules! verbose_print { ($($tts:tt)*) => { if $crate::is_verbose() { print!($($tts)*); } }; } 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( rom_path: String, bootrom_path: Option, mut window: Window, connect_serial: bool, tile_window: bool, verbose: bool, step_by: Option, cycle_count: bool, factor: usize, ) { VERBOSE.set(verbose).unwrap(); FACTOR.set(factor).unwrap(); crate::processor::memory::mmio::gpu::init_statics(); let rom: Rom = match fs::read(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 = bootrom_path.is_some(); let bootrom: Option> = if let Some(path) = 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, connect_serial, tile_window), bootrom_enabled, Gilrs::new().unwrap(), ); let mut cycle_num = 0; verbose_println!("\n\n Begin execution...\n"); match step_by { Some(step_size) => loop { for _ in 0..step_size { cycle_num += 1; if 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 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(); } } fn pause_then_step() { unsafe { if PAUSE_ENABLED { let line = pause(); PAUSE_QUEUED = !line.contains("continue"); } } } #[allow(dead_code)] fn pause_once() { println!("paused..."); pause(); } fn pause() -> String { let mut line = String::new(); match io::stdin().read_line(&mut line) { Ok(_) => line, Err(_) => String::from(""), } } fn print_cycles(cycles: &i32) { if *cycles % 45678 != 0 { return; } let instructions_per_second = 400000; print!( "cycle {} - approx {} seconds on real hardware\r", cycles, cycles / instructions_per_second ); stdout().flush().unwrap(); } fn is_verbose() -> bool { match VERBOSE.get() { Some(v) => *v, None => false, } }