core: Implement mGBA log support.

It's quite an handy way to log messages from ROMs, so I thought it'd be nice to add it.


Former-commit-id: 6869bdb58cfa883ac1ca6832f0bbeeab0edcf552
Former-commit-id: 89d7d826c7a906bbb68f9f4305bb92cd50bb2296
This commit is contained in:
Michel Heily 2020-10-24 05:54:31 -07:00 committed by MishMish
parent b28fc3108f
commit fb32b9a671
4 changed files with 124 additions and 13 deletions

View file

@ -6,6 +6,7 @@ use super::gpu::regs::WindowFlags;
use super::gpu::*; use super::gpu::*;
use super::interrupt::{InterruptConnect, InterruptController, SharedInterruptFlags}; use super::interrupt::{InterruptConnect, InterruptController, SharedInterruptFlags};
use super::keypad; use super::keypad;
use super::mgba_debug::DebugPort;
use super::sched::{SchedulerConnect, SharedScheduler}; use super::sched::{SchedulerConnect, SharedScheduler};
use super::sound::SoundController; use super::sound::SoundController;
use super::sysbus::SysBusPtr; use super::sysbus::SysBusPtr;
@ -33,6 +34,7 @@ pub struct IoDevices {
pub post_boot_flag: bool, pub post_boot_flag: bool,
pub waitcnt: WaitControl, // TODO also implement 4000800 pub waitcnt: WaitControl, // TODO also implement 4000800
pub haltcnt: HaltState, pub haltcnt: HaltState,
pub debug: DebugPort,
// HACK // HACK
// my ownership design sucks // my ownership design sucks
@ -59,6 +61,7 @@ impl IoDevices {
haltcnt: HaltState::Running, haltcnt: HaltState::Running,
keyinput: keypad::KEYINPUT_ALL_RELEASED, keyinput: keypad::KEYINPUT_ALL_RELEASED,
waitcnt: WaitControl(0), waitcnt: WaitControl(0),
debug: DebugPort::new(),
sysbus_ptr: Default::default(), sysbus_ptr: Default::default(),
} }
@ -91,9 +94,10 @@ impl Bus for IoDevices {
fn read_16(&mut self, addr: Addr) -> u16 { fn read_16(&mut self, addr: Addr) -> u16 {
let io = self; let io = self;
let io_addr = addr + IO_BASE; let io_addr = addr + IO_BASE;
if addr > 0x0800 { // if addr > 0x0800 {
return 0; // return 0;
} // }
match io_addr { match io_addr {
REG_DISPCNT => io.gpu.dispcnt.0, REG_DISPCNT => io.gpu.dispcnt.0,
REG_DISPSTAT => io.gpu.dispstat.0, REG_DISPSTAT => io.gpu.dispstat.0,
@ -133,6 +137,8 @@ impl Bus for IoDevices {
REG_HALTCNT => 0, REG_HALTCNT => 0,
REG_KEYINPUT => io.keyinput as u16, REG_KEYINPUT => io.keyinput as u16,
x if DebugPort::is_debug_access(x) => io.debug.read(io_addr),
_ => { _ => {
trace!( trace!(
"Unimplemented read from {:x} {}", "Unimplemented read from {:x} {}",
@ -155,9 +161,9 @@ impl Bus for IoDevices {
fn write_16(&mut self, addr: Addr, value: u16) { fn write_16(&mut self, addr: Addr, value: u16) {
let mut io = self; let mut io = self;
if addr > 0x0800 { // if addr > 0x0800 {
return; // return;
} // }
let io_addr = addr + IO_BASE; let io_addr = addr + IO_BASE;
macro_rules! write_reference_point { macro_rules! write_reference_point {
@ -276,6 +282,8 @@ impl Bus for IoDevices {
} }
} }
x if DebugPort::is_debug_access(x) => io.debug.write(io_addr, value),
_ => { _ => {
trace!( trace!(
"Unimplemented write to {:x} {}", "Unimplemented write to {:x} {}",
@ -446,6 +454,10 @@ pub mod consts {
pub const REG_IME: Addr = 0x0400_0208; // 2 R/W Interrupt Master Enable Register pub const REG_IME: Addr = 0x0400_0208; // 2 R/W Interrupt Master Enable Register
pub const REG_POSTFLG: Addr = 0x0400_0300; // 1 R/W Undocumented - Post Boot Flag pub const REG_POSTFLG: Addr = 0x0400_0300; // 1 R/W Undocumented - Post Boot Flag
pub const REG_HALTCNT: Addr = 0x0400_0301; // 1 W Undocumented - Power Down Control pub const REG_HALTCNT: Addr = 0x0400_0301; // 1 W Undocumented - Power Down Control
pub const REG_DEBUG_STRING: Addr = 0x04FF_F600;
pub const REG_DEBUG_FLAGS: Addr = 0x04FF_F700;
pub const REG_DEBUG_ENABLE: Addr = 0x04FF_F780;
} }
pub fn io_reg_string(addr: u32) -> &'static str { pub fn io_reg_string(addr: u32) -> &'static str {
@ -554,6 +566,9 @@ pub fn io_reg_string(addr: u32) -> &'static str {
REG_IME => "REG_IME", REG_IME => "REG_IME",
REG_POSTFLG => "REG_POSTFLG", REG_POSTFLG => "REG_POSTFLG",
REG_HALTCNT => "REG_HALTCNT", REG_HALTCNT => "REG_HALTCNT",
REG_DEBUG_STRING => "REG_DEBUG_STRING",
REG_DEBUG_FLAGS => "REG_DEBUG_FLAGS",
REG_DEBUG_ENABLE => "REG_DEBUG_ENABLE",
_ => "UNKNOWN", _ => "UNKNOWN",
} }
} }

View file

@ -55,6 +55,7 @@ pub mod dma;
pub mod keypad; pub mod keypad;
pub mod timer; pub mod timer;
pub use bus::*; pub use bus::*;
mod mgba_debug;
pub(crate) mod overrides; pub(crate) mod overrides;
#[cfg(feature = "gdb")] #[cfg(feature = "gdb")]

95
core/src/mgba_debug.rs Normal file
View file

@ -0,0 +1,95 @@
/// mGBA 0.8.1 Debug peripheral support
use std::str;
use log::log;
use log::Level;
use super::iodev::consts::{REG_DEBUG_ENABLE, REG_DEBUG_FLAGS, REG_DEBUG_STRING};
pub const DEBUG_STRING_SIZE: usize = 0x100;
#[derive(Clone, Serialize, Deserialize)]
pub struct DebugPort {
enable: bool,
flags: DebugFlags,
debug_string: Box<[u8]>,
}
impl DebugPort {
pub fn new() -> DebugPort {
DebugPort {
enable: false,
flags: DebugFlags(0),
debug_string: vec![0; DEBUG_STRING_SIZE].into_boxed_slice(),
}
}
#[inline]
pub fn is_debug_access(x: u32) -> bool {
x == REG_DEBUG_ENABLE
|| x == REG_DEBUG_FLAGS
|| (x >= REG_DEBUG_STRING && x <= REG_DEBUG_STRING + (DEBUG_STRING_SIZE as u32))
}
pub fn read(&self, addr: u32) -> u16 {
match addr {
REG_DEBUG_ENABLE => {
if self.enable {
0x1DEA
} else {
0
}
}
_ => 0,
}
}
pub fn write(&mut self, addr: u32, value: u16) {
match addr {
REG_DEBUG_ENABLE => self.enable = value == 0xC0DE,
REG_DEBUG_FLAGS => {
if self.enable {
self.flags.0 = value;
self.debug();
}
}
x if x >= REG_DEBUG_STRING && x <= REG_DEBUG_STRING + (DEBUG_STRING_SIZE as u32) => {
let index = (addr - REG_DEBUG_STRING) as usize;
self.debug_string[index] = (value & 0xff) as u8;
self.debug_string[index + 1] = (value >> 8) as u8;
}
_ => unreachable!(),
}
}
fn debug(&mut self) {
if self.flags.send() {
let message = str::from_utf8(&self.debug_string)
.expect("Failed to parse log message to valid utf8");
let level: Level = match self.flags.level() {
0 | 1 => Level::Error,
2 => Level::Warn,
3 => Level::Info,
4 => Level::Debug,
_ => panic!("invalid log level"),
};
log!(level, "[mGBA mLOG]: {}", message);
for i in self.debug_string.iter_mut() {
*i = 0;
}
self.flags.set_send(false);
}
}
}
bitfield! {
#[derive(Clone, Serialize, Deserialize)]
pub struct DebugFlags(u16);
impl Debug;
u16;
pub into usize, level, _: 3, 0;
pub send, set_send: 8;
}

View file

@ -325,7 +325,7 @@ impl Bus for SysBus {
let addr = if addr & 0xfffc == 0x8000 { let addr = if addr & 0xfffc == 0x8000 {
0x800 0x800
} else { } else {
addr & 0x7fc addr & 0x00fffffc
}; };
self.io.read_32(addr) self.io.read_32(addr)
} }
@ -355,7 +355,7 @@ impl Bus for SysBus {
let addr = if addr & 0xfffe == 0x8000 { let addr = if addr & 0xfffe == 0x8000 {
0x800 0x800
} else { } else {
addr & 0x7fe addr & 0x00fffffe
}; };
self.io.read_16(addr) self.io.read_16(addr)
} }
@ -385,7 +385,7 @@ impl Bus for SysBus {
let addr = if addr & 0xffff == 0x8000 { let addr = if addr & 0xffff == 0x8000 {
0x800 0x800
} else { } else {
addr & 0x7ff addr & 0x00ffffff
}; };
self.io.read_8(addr) self.io.read_8(addr)
} }
@ -409,7 +409,7 @@ impl Bus for SysBus {
let addr = if addr & 0xfffc == 0x8000 { let addr = if addr & 0xfffc == 0x8000 {
0x800 0x800
} else { } else {
addr & 0x7fc addr & 0x00fffffc
}; };
self.io.write_32(addr, value) self.io.write_32(addr, value)
} }
@ -434,7 +434,7 @@ impl Bus for SysBus {
let addr = if addr & 0xfffe == 0x8000 { let addr = if addr & 0xfffe == 0x8000 {
0x800 0x800
} else { } else {
addr & 0x7fe addr & 0x00fffffe
}; };
self.io.write_16(addr, value) self.io.write_16(addr, value)
} }
@ -459,7 +459,7 @@ impl Bus for SysBus {
let addr = if addr & 0xffff == 0x8000 { let addr = if addr & 0xffff == 0x8000 {
0x800 0x800
} else { } else {
addr & 0x7ff addr & 0x00ffffff
}; };
self.io.write_8(addr, value) self.io.write_8(addr, value)
} }
@ -485,7 +485,7 @@ impl DebugRead for SysBus {
let addr = if addr & 0xffff == 0x8000 { let addr = if addr & 0xffff == 0x8000 {
0x800 0x800
} else { } else {
addr & 0x7ff addr & 0x00ffffff
}; };
self.io.debug_read_8(addr) self.io.debug_read_8(addr)
} }