use std::{
sync::{Arc, Mutex},
time::{Duration, Instant},
};
use crate::types::{CURRENT_SCANLINE, GameInput, INPUT_REGISTER, KeyState, LCD_CONTROL};
use debug_print::debug_println;
mod cpu;
mod graphics;
mod joypad;
mod mem;
mod sound;
pub struct Emulator {
screen: graphics::Screen,
cpu: cpu::CPU,
joypad: joypad::Joypad,
memory: mem::SharedMemory,
paused: bool,
}
impl Default for Emulator {
fn default() -> Self {
Self::new()
}
}
impl Emulator {
const MAXCYCLES: u32 = 69905;
const FRAME_DURATION: Duration = Duration::from_nanos(16_741_000);
pub fn new() -> Self {
let mem = Arc::new(Mutex::new(mem::Memory::new()));
mem.lock().unwrap().ram_startup();
Emulator {
screen: graphics::Screen::new(Arc::clone(&mem)),
cpu: cpu::CPU::new(Arc::clone(&mem)),
joypad: joypad::Joypad::new(Arc::clone(&mem)),
memory: mem,
paused: true,
}
}
pub fn update(&mut self) {
if self.paused {
return;
}
let frame_start = Instant::now();
let mut num_cycles: u32 = 0;
while num_cycles < Self::MAXCYCLES {
let cycles = self.cpu.execute_next_opcode(false);
num_cycles += cycles as u32;
self.cpu.timers.update_timers(cycles as i32);
self.screen.update_screen(cycles as i32);
self.cpu.handle_interrupts();
}
if let Some(remaining) = Self::FRAME_DURATION.checked_sub(frame_start.elapsed()) {
std::thread::sleep(remaining);
}
}
pub fn toggle_pause(&mut self) {
self.paused = !self.paused;
}
pub fn is_paused(&self) -> bool {
self.paused
}
pub fn load_rom(&mut self, path: &str) -> Result<(), String> {
let data = std::fs::read(path).map_err(|e| e.to_string())?;
let mut mem = self.memory.lock().unwrap();
mem.load_rom_data(&data);
mem.ram_startup();
self.cpu.reset();
self.paused = false;
Ok(())
}
pub fn get_display_buffer(&self) -> &[u8] {
&self.screen.buffer
}
pub fn dump_lcd_mem(&self) {
let mem = self.memory.lock().unwrap();
debug_println!("IDK: {:X}", mem.read_byte_forced(0xFF26));
debug_println!("LCD Control: {:X}", mem.read_byte_forced(LCD_CONTROL));
debug_println!("Scroll Y: {:X}", mem.read_byte_forced(0xFF42));
debug_println!("Scroll X: {:X}", mem.read_byte_forced(0xFF43));
debug_println!("BG Palette: {:X}", mem.read_byte_forced(0xFF47));
debug_println!("OBJ palette: {:X}", mem.read_byte_forced(0xFF48));
debug_println!(
"Current Scanline: {:X}",
mem.read_byte_forced(CURRENT_SCANLINE)
);
debug_println!("LCD Control: {:X}", mem.read_byte_forced(LCD_CONTROL));
debug_println!(
"Joystick Register: 0x{:X}",
mem.read_byte_forced(INPUT_REGISTER)
);
}
pub fn game_input(&mut self, input: GameInput, val: KeyState) {
self.joypad.log_input(input, val)
}
}