From e47f2eb23b624428323bcab0fb6781cc6d107e38 Mon Sep 17 00:00:00 2001 From: Astatin Date: Tue, 22 Jul 2025 22:24:53 +0200 Subject: Add different verbosity levels --- src/desktop/input.rs | 21 ++++++++++----------- src/desktop/load_save.rs | 11 ++++++----- src/desktop/window.rs | 9 +++++---- src/io.rs | 15 ++++++--------- src/logs.rs | 47 +++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 18 +++++++++--------- src/mmio.rs | 9 +++++---- src/opcodes.rs | 13 ++++++------- src/state.rs | 21 +++++++-------------- 9 files changed, 101 insertions(+), 63 deletions(-) create mode 100644 src/logs.rs diff --git a/src/desktop/input.rs b/src/desktop/input.rs index ed604e2..4653d3c 100644 --- a/src/desktop/input.rs +++ b/src/desktop/input.rs @@ -5,6 +5,7 @@ use crate::desktop::window::Keys; use crate::io::Input; use gilrs::{Button, GamepadId, Gilrs}; use winit::keyboard::KeyCode; +use crate::logs::{elog, log, LogLevel}; pub struct Gamepad { gilrs: Gilrs, @@ -17,10 +18,10 @@ impl Gamepad { let gilrs = Gilrs::new().unwrap(); let gamepad_id = if let Some((gamepad_id, _gamepad)) = gilrs.gamepads().next() { - println!("Found Gamepad id: {:?}", gamepad_id); + log(LogLevel::Infos, format!("Gamepad connected: {:?}", gamepad_id)); Some(gamepad_id) } else { - println!("No gamepad found"); + log(LogLevel::Infos, format!("No gamepad found")); None }; @@ -44,12 +45,12 @@ impl Input for Gamepad { fn update_events(&mut self, _cycles: u128) -> Option { if let Some(gamepad_id) = self.gamepad_id { if self.gilrs.connected_gamepad(gamepad_id).is_none() { - println!("Gamepad (id = {:?}) disconnected", gamepad_id); + log(LogLevel::Infos, format!("Gamepad disconnected: {:?}", gamepad_id)); self.gamepad_id = None; } } else { if let Some((gamepad_id, _gamepad)) = self.gilrs.gamepads().next() { - println!("Found Gamepad id: {:?}", gamepad_id); + log(LogLevel::Infos, format!("Gamepad connected: {:?}", gamepad_id)); self.gamepad_id = Some(gamepad_id); } } @@ -226,21 +227,21 @@ impl Input for GamepadRecorder { let new_direction_reg = self.input.get_direction_gamepad_reg(); if self.action_reg != new_action_reg || self.direction_reg != new_direction_reg { - println!( + log(LogLevel::Debug, format!( "input update on cycle {} ! 0x{:02x} 0x{:02x}", cycles, new_action_reg, new_direction_reg - ); + )); if let Err(err) = self.record_file.write_all(&cycles.to_le_bytes()) { - eprintln!("Failed to write to record file: {}", err); + elog(LogLevel::Error, format!("Failed to write to record file: {}", err)); }; if let Err(err) = self .record_file .write_all(&[new_action_reg, new_direction_reg]) { - eprintln!("Failed to write to record file: {}", err); + elog(LogLevel::Error, format!("Failed to write to record file: {}", err)); } if let Err(err) = self.record_file.flush() { - eprintln!("Failed to flush record file writes: {}", err); + elog(LogLevel::Error, format!("Failed to flush record file writes: {}", err)); } } @@ -331,8 +332,6 @@ impl Input for GamepadReplay { return false; } if self.next_cycle_update == None { - println!("SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE"); - self.last_save_state = true; return true; } diff --git a/src/desktop/load_save.rs b/src/desktop/load_save.rs index a7eadfc..ec99d94 100644 --- a/src/desktop/load_save.rs +++ b/src/desktop/load_save.rs @@ -2,6 +2,7 @@ use crate::io::{Audio, LoadSave, Serial}; use crate::state::GBState; use std::fs::File; use std::io::{Read, Write}; +use crate::logs::{elog, log, LogLevel}; #[derive(Debug)] pub struct FSLoadSave { @@ -37,8 +38,8 @@ impl LoadSave for FSLoadSave { } fn load_bootrom(&self, boot_rom: &mut [u8]) -> Result<(), std::io::Error> { - println!("MBC: {:02x}", boot_rom[0x147]); - println!("CGB: {:02x}", boot_rom[0x143]); + log(LogLevel::Debug, format!("MBC: {:02x}", boot_rom[0x147])); + log(LogLevel::Debug, format!("CGB: {:02x}", boot_rom[0x143])); if boot_rom[0x143] == 0x80 || boot_rom[0x143] == 0xc0 { unimplemented!("CGB Boot rom is not implemented"); @@ -61,7 +62,7 @@ impl LoadSave for FSLoadSave { f.read(external_ram)?; - println!("Save file loaded from \"{}\"!", self.save_file); + log(LogLevel::Infos, format!("Save file loaded from \"{}\"!", self.save_file)); Ok(()) } @@ -71,7 +72,7 @@ impl LoadSave for FSLoadSave { f.write_all(&external_ram)?; - println!("Save written to \"{}\"!", self.save_file); + log(LogLevel::Infos, format!("Save written to \"{}\"!", self.save_file)); Ok(()) } @@ -146,7 +147,7 @@ impl LoadSave for FSLoadSave { state_file.write_all(&state.cpu.sp.to_le_bytes())?; state_file.write_all(&[state.mem.boot_rom_on.into(), state.mem.ime.into()])?; } else { - eprintln!("Tried to save state without state_file specified"); + elog(LogLevel::Error, format!("Tried to save state without state_file specified")); } Ok(()) } diff --git a/src/desktop/window.rs b/src/desktop/window.rs index 06b672d..93c5aba 100644 --- a/src/desktop/window.rs +++ b/src/desktop/window.rs @@ -4,6 +4,7 @@ use std::sync::mpsc::{channel, Receiver, Sender}; use std::thread; use crate::io::{Window, WindowSignal}; +use crate::logs::{elog, LogLevel}; use pixels::{Error, Pixels, SurfaceTexture}; use winit::dpi::LogicalSize; @@ -71,7 +72,7 @@ impl DesktopWindow { } draw(pixels.frame_mut(), &fb); if let Err(err) = pixels.render() { - eprintln!("Error during render: {}", err); + elog(LogLevel::Error, format!("Error during render: {}", err)); return; } } @@ -101,14 +102,14 @@ impl DesktopWindow { if input.close_requested() { elwt.exit(); if let Err(err) = signal_send.send(WindowSignal::Exit) { - eprintln!("window signal send failed with error {}", err); + elog(LogLevel::Error, format!("window signal send failed with error {}", err)); } return; } if let Some(size) = input.window_resized() { if let Err(err) = pixels.resize_surface(size.width, size.height) { - eprintln!("Error during resize: {}", err); + elog(LogLevel::Error, format!("Error during resize: {}", err)); return; } } @@ -130,7 +131,7 @@ impl DesktopWindow { impl Window for DesktopWindow { fn update(&mut self, fb: Box<[u32; 160 * 144]>) -> Option { if let Err(err) = self.fb_send.send(fb) { - eprintln!("Framebuffer channel send failed with error: {}", err); + elog(LogLevel::Error, format!("Framebuffer channel send failed with error: {}", err)); } if let Ok(signal) = self.signal_recv.try_recv() { diff --git a/src/io.rs b/src/io.rs index 06775a2..8cc7b22 100644 --- a/src/io.rs +++ b/src/io.rs @@ -4,6 +4,7 @@ use std::{thread, time}; use crate::audio::MutableWave; use crate::consts; use crate::state::GBState; +use crate::logs::{log, elog, LogLevel}; pub trait Input { fn update_events(&mut self, cycles: u128) -> Option; @@ -108,10 +109,10 @@ impl Gameboy Gameboy Gameboy Gameboy Gameboy> = OnceLock::new(); + +pub fn set_log_level(verbosity: String) { + let mut set: HashSet = HashSet::new(); + for level in verbosity.split(",") { + match level { + "infos" => { set.insert(LogLevel::Infos); }, + "debug" => { set.insert(LogLevel::Debug); }, + "opcode_dump" => { set.insert(LogLevel::OpcodeDump); }, + "halt_cycles" => { set.insert(LogLevel::HaltCycles); }, + "errors" => { set.insert(LogLevel::Error); }, + "none" => {}, + _ => panic!("Unknown log level \"{}\"", level), + } + } + if let Err(value) = LOG_LEVEL.set(set) { + panic!("Log level is already set with value {:?}", value); + } +} + +pub fn log(level: LogLevel, s: String) { + if let Some(set) = LOG_LEVEL.get() { + if set.contains(&level) { + println!("[{:?}] {}", level, s); + } + } +} + +pub fn elog(level: LogLevel, s: String) { + if let Some(set) = LOG_LEVEL.get() { + if set.contains(&level) { + eprintln!("[{:?}] {}", level, s); + } + } +} diff --git a/src/main.rs b/src/main.rs index 821a76d..84167aa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,7 +7,9 @@ pub mod io; pub mod mmio; pub mod opcodes; pub mod state; +pub mod logs; +use crate::logs::{log, LogLevel}; use crate::desktop::input::{Gamepad, GamepadRecorder, GamepadReplay, Keyboard}; use crate::desktop::load_save::FSLoadSave; use crate::io::{Serial, Input}; @@ -51,10 +53,6 @@ struct Cli { #[arg(short, long, default_value_t = 1.0)] speed: f32, - /// Will print all of the opcodes executed (WARNING: THERE ARE MANY) - #[arg(short, long, default_value_t = false)] - debug: bool, - /// Skip bootrom (will start the execution at 0x100 with all registers empty #[arg(long, default_value_t = false)] skip_bootrom: bool, @@ -78,12 +76,18 @@ struct Cli { /// Don't send (or expect) a byte as a response to a serial transfer #[arg(long, default_value_t = false)] no_response: bool, + + /// Verbosity. Coma separated values (possible values: infos/debug/opcode_dump/halt_cycles/errors,none) + #[arg(short, long, default_value = "infos,errors")] + verbosity: String, } fn main() { let cli = Cli::parse(); - println!("Starting {:?}...", &cli.rom); + logs::set_log_level(cli.verbosity); + + log(LogLevel::Infos, format!("Starting {:?}...", &cli.rom)); let window = desktop::window::DesktopWindow::new(cli.title).unwrap(); @@ -119,10 +123,6 @@ fn main() { cli.speed as f64, ); - if cli.debug { - gameboy.debug(); - } - if cli.load_state { gameboy.load_state().unwrap(); } diff --git a/src/mmio.rs b/src/mmio.rs index d9f2a5a..8c8739f 100644 --- a/src/mmio.rs +++ b/src/mmio.rs @@ -1,10 +1,11 @@ use crate::io::{Audio, Serial}; use crate::state::Memory; +use crate::logs::{log, LogLevel}; impl Memory { pub fn r_io(&self, addr: u8) -> u8 { if addr > 0x50 { - println!("Reading from 0xff{:02x} not implemented yet", addr); + log(LogLevel::Debug, format!("Reading from 0xff{:02x} not implemented yet", addr)); } match addr { 0x00 => { @@ -55,7 +56,7 @@ impl Memory { } } _ => { - println!("Reading from 0xff{:02x} not implemented yet", addr); + log(LogLevel::Debug, format!("Reading from 0xff{:02x} not implemented yet", addr)); self.io[addr as usize] } } @@ -295,10 +296,10 @@ impl Memory { } _ => { if addr != 0x25 && addr != 0x24 && addr != 0x26 && addr < 0x30 && addr > 0x3f { - println!( + log(LogLevel::Debug, format!( "Writing to 0xff{:02x} not implemented yet ({:02x})", addr, value - ); + )); } } } diff --git a/src/opcodes.rs b/src/opcodes.rs index 606634a..6405726 100644 --- a/src/opcodes.rs +++ b/src/opcodes.rs @@ -1,5 +1,6 @@ use crate::io::{Audio, Serial}; use crate::state::{flag, reg, GBState}; +use crate::logs::{log, LogLevel}; // The opcodes functions are returning the number of cycles used. @@ -160,7 +161,7 @@ impl GBState { let res = self.pop(); if res == 0 { - println!("DEBUG: {:?}", self.cpu); + log(LogLevel::Debug, format!("CPU: {:?}", self.cpu)); panic!("RET to start"); } @@ -835,12 +836,10 @@ impl GBState { pub fn exec_opcode(&mut self) -> u64 { let opcode = self.mem.r(self.cpu.pc); - if self.is_debug { - println!( - "{:02x}:{:04x} = {:02x} (IME: {})", - self.mem.rom_bank, self.cpu.pc, opcode, self.mem.ime - ); - } + log(LogLevel::OpcodeDump, format!( + "{:02x}:{:04x} = {:02x} (IME: {})", + self.mem.rom_bank, self.cpu.pc, opcode, self.mem.ime + )); self.cpu.pc += 1; diff --git a/src/state.rs b/src/state.rs index 2c54a44..8c0832b 100644 --- a/src/state.rs +++ b/src/state.rs @@ -2,6 +2,7 @@ use crate::audio::Channels; use crate::consts::{PROGRAM_START_ADDRESS, STACK_START_ADDRESS}; use crate::display::Display; use crate::io::{Audio, Serial}; +use crate::logs::{log, LogLevel}; pub mod reg { pub const B: u8 = 0; @@ -87,7 +88,7 @@ impl CPU { } pub fn print_debug(&mut self) { - println!( + log(LogLevel::Debug, format!( "PC: 0x{:04x}, SP: 0x{:04x}, A: 0x{:02x}, BC: 0x{:04x}, DE: 0x{:04x}, HL: 0x{:04x}, F: 0x{:02x}. Since last dbg: {} cycles", self.pc, self.sp, @@ -97,7 +98,7 @@ impl CPU { self.r16(reg::HL), self.r[reg::F as usize], self.dbg_cycle_counter, - ); + )); self.dbg_cycle_counter = 0; } @@ -240,10 +241,10 @@ impl Memory { } else if addr == 0xffff { self.interrupts_register } else { - println!( + log(LogLevel::Debug, format!( "Trying to read at address 0x{:04x} which is unimplemented", addr - ); + )); 0 } } @@ -274,10 +275,10 @@ impl Memory { } else if addr == 0xffff { self.interrupts_register = value; } else { - println!( + log(LogLevel::Debug, format!( "Trying to write at address 0x{:04x} which is unimplemented (value: {:02x})", addr, value - ); + )); } } } @@ -285,7 +286,6 @@ impl Memory { pub struct GBState { pub cpu: CPU, pub mem: Memory, - pub is_debug: bool, pub is_stopped: bool, pub div_cycles: u64, @@ -299,7 +299,6 @@ impl GBState { Self { cpu: CPU::new(), mem, - is_debug: false, is_stopped: false, div_cycles: 0, @@ -330,10 +329,4 @@ impl GBState { panic!("r_i must be a 3 bits register input number") } } - - pub fn debug(&self, s: &str) -> () { - if self.is_debug { - println!("{}", s); - } - } } -- cgit v1.2.3-70-g09d2