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:
parent
b28fc3108f
commit
fb32b9a671
|
@ -6,6 +6,7 @@ use super::gpu::regs::WindowFlags;
|
|||
use super::gpu::*;
|
||||
use super::interrupt::{InterruptConnect, InterruptController, SharedInterruptFlags};
|
||||
use super::keypad;
|
||||
use super::mgba_debug::DebugPort;
|
||||
use super::sched::{SchedulerConnect, SharedScheduler};
|
||||
use super::sound::SoundController;
|
||||
use super::sysbus::SysBusPtr;
|
||||
|
@ -33,6 +34,7 @@ pub struct IoDevices {
|
|||
pub post_boot_flag: bool,
|
||||
pub waitcnt: WaitControl, // TODO also implement 4000800
|
||||
pub haltcnt: HaltState,
|
||||
pub debug: DebugPort,
|
||||
|
||||
// HACK
|
||||
// my ownership design sucks
|
||||
|
@ -59,6 +61,7 @@ impl IoDevices {
|
|||
haltcnt: HaltState::Running,
|
||||
keyinput: keypad::KEYINPUT_ALL_RELEASED,
|
||||
waitcnt: WaitControl(0),
|
||||
debug: DebugPort::new(),
|
||||
|
||||
sysbus_ptr: Default::default(),
|
||||
}
|
||||
|
@ -91,9 +94,10 @@ impl Bus for IoDevices {
|
|||
fn read_16(&mut self, addr: Addr) -> u16 {
|
||||
let io = self;
|
||||
let io_addr = addr + IO_BASE;
|
||||
if addr > 0x0800 {
|
||||
return 0;
|
||||
}
|
||||
// if addr > 0x0800 {
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
match io_addr {
|
||||
REG_DISPCNT => io.gpu.dispcnt.0,
|
||||
REG_DISPSTAT => io.gpu.dispstat.0,
|
||||
|
@ -133,6 +137,8 @@ impl Bus for IoDevices {
|
|||
REG_HALTCNT => 0,
|
||||
REG_KEYINPUT => io.keyinput as u16,
|
||||
|
||||
x if DebugPort::is_debug_access(x) => io.debug.read(io_addr),
|
||||
|
||||
_ => {
|
||||
trace!(
|
||||
"Unimplemented read from {:x} {}",
|
||||
|
@ -155,9 +161,9 @@ impl Bus for IoDevices {
|
|||
|
||||
fn write_16(&mut self, addr: Addr, value: u16) {
|
||||
let mut io = self;
|
||||
if addr > 0x0800 {
|
||||
return;
|
||||
}
|
||||
// if addr > 0x0800 {
|
||||
// return;
|
||||
// }
|
||||
let io_addr = addr + IO_BASE;
|
||||
|
||||
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!(
|
||||
"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_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_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 {
|
||||
|
@ -554,6 +566,9 @@ pub fn io_reg_string(addr: u32) -> &'static str {
|
|||
REG_IME => "REG_IME",
|
||||
REG_POSTFLG => "REG_POSTFLG",
|
||||
REG_HALTCNT => "REG_HALTCNT",
|
||||
REG_DEBUG_STRING => "REG_DEBUG_STRING",
|
||||
REG_DEBUG_FLAGS => "REG_DEBUG_FLAGS",
|
||||
REG_DEBUG_ENABLE => "REG_DEBUG_ENABLE",
|
||||
_ => "UNKNOWN",
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,6 +55,7 @@ pub mod dma;
|
|||
pub mod keypad;
|
||||
pub mod timer;
|
||||
pub use bus::*;
|
||||
mod mgba_debug;
|
||||
pub(crate) mod overrides;
|
||||
|
||||
#[cfg(feature = "gdb")]
|
||||
|
|
95
core/src/mgba_debug.rs
Normal file
95
core/src/mgba_debug.rs
Normal 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;
|
||||
}
|
|
@ -325,7 +325,7 @@ impl Bus for SysBus {
|
|||
let addr = if addr & 0xfffc == 0x8000 {
|
||||
0x800
|
||||
} else {
|
||||
addr & 0x7fc
|
||||
addr & 0x00fffffc
|
||||
};
|
||||
self.io.read_32(addr)
|
||||
}
|
||||
|
@ -355,7 +355,7 @@ impl Bus for SysBus {
|
|||
let addr = if addr & 0xfffe == 0x8000 {
|
||||
0x800
|
||||
} else {
|
||||
addr & 0x7fe
|
||||
addr & 0x00fffffe
|
||||
};
|
||||
self.io.read_16(addr)
|
||||
}
|
||||
|
@ -385,7 +385,7 @@ impl Bus for SysBus {
|
|||
let addr = if addr & 0xffff == 0x8000 {
|
||||
0x800
|
||||
} else {
|
||||
addr & 0x7ff
|
||||
addr & 0x00ffffff
|
||||
};
|
||||
self.io.read_8(addr)
|
||||
}
|
||||
|
@ -409,7 +409,7 @@ impl Bus for SysBus {
|
|||
let addr = if addr & 0xfffc == 0x8000 {
|
||||
0x800
|
||||
} else {
|
||||
addr & 0x7fc
|
||||
addr & 0x00fffffc
|
||||
};
|
||||
self.io.write_32(addr, value)
|
||||
}
|
||||
|
@ -434,7 +434,7 @@ impl Bus for SysBus {
|
|||
let addr = if addr & 0xfffe == 0x8000 {
|
||||
0x800
|
||||
} else {
|
||||
addr & 0x7fe
|
||||
addr & 0x00fffffe
|
||||
};
|
||||
self.io.write_16(addr, value)
|
||||
}
|
||||
|
@ -459,7 +459,7 @@ impl Bus for SysBus {
|
|||
let addr = if addr & 0xffff == 0x8000 {
|
||||
0x800
|
||||
} else {
|
||||
addr & 0x7ff
|
||||
addr & 0x00ffffff
|
||||
};
|
||||
self.io.write_8(addr, value)
|
||||
}
|
||||
|
@ -485,7 +485,7 @@ impl DebugRead for SysBus {
|
|||
let addr = if addr & 0xffff == 0x8000 {
|
||||
0x800
|
||||
} else {
|
||||
addr & 0x7ff
|
||||
addr & 0x00ffffff
|
||||
};
|
||||
self.io.debug_read_8(addr)
|
||||
}
|
||||
|
|
Reference in a new issue