#![feature( exclusive_range_pattern, let_chains, slice_flatten, async_closure, bigint_helper_methods )] use crate::{ processor::memory::Memory, util::{pause, print_cycles}, }; use connect::{AudioOutput, EmulatorMessage, Renderer}; use once_cell::sync::OnceCell; use processor::{memory::Rom, Cpu}; use std::{ fs::{self}, io::{stdout, Write}, path::PathBuf, process::exit, str::FromStr, sync::mpsc::Receiver, }; use util::pause_then_step; pub mod connect; mod constants; mod processor; pub mod util; pub struct Options { pub rom_path: String, pub save_path: Option, pub no_save: bool, pub bootrom_path: Option, pub connect_serial: bool, pub verbose: bool, pub step_by: Option, pub cycle_count: bool, } 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; pub fn init( receiver: Receiver, options: Options, mut window: Box, output: AudioOutput, tile_window: Option>, ) { VERBOSE.set(options.verbose).unwrap(); let maybe_save = if options.no_save { None } else { Some(if let Some(path) = options.save_path { PathBuf::from_str(&path).unwrap() } else { PathBuf::from_str(&options.rom_path) .unwrap() .with_extension("sav") }) }; let rom: Rom = match fs::read(options.rom_path) { Ok(data) => Rom::load(data, maybe_save), Err(e) => { println!("Error reading ROM: {e}"); return; } }; window.prepare(WIDTH, HEIGHT); window.set_title(format!("{} on {}", rom.get_title(), rom.mbc_type())); 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, output, options.connect_serial, tile_window, ), bootrom_enabled, ); let mut cycle_num = 0; verbose_println!("\n\n Begin execution...\n"); match options.step_by { Some(step_size) => loop { process_messages(&receiver, &mut cpu); 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 { process_messages(&receiver, &mut cpu); cycle_num += 1; if options.cycle_count { print_cycles(&cycle_num); } run_cycle(&mut cpu); }, } } fn process_messages(receiver: &Receiver, cpu: &mut Cpu) { while let Ok(msg) = receiver.try_recv() { match msg { EmulatorMessage::Stop => { cpu.memory.flush_rom(); exit(0); } } } } 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(); } }