mirror of
https://github.com/italicsjenga/gba.git
synced 2024-12-23 19:01:30 +11:00
mgba panic handler 100% safe, justrelease only tho
This commit is contained in:
parent
7ab96c35f2
commit
7d8c82ddbc
|
@ -1,109 +1,30 @@
|
|||
#![no_std]
|
||||
#![feature(start)]
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn __clzsi2(mut x: usize) -> usize {
|
||||
let mut y: usize;
|
||||
let mut n: usize = 32;
|
||||
y = x >> 16;
|
||||
if y != 0 {
|
||||
n = n - 16;
|
||||
x = y;
|
||||
}
|
||||
y = x >> 8;
|
||||
if y != 0 {
|
||||
n = n - 8;
|
||||
x = y;
|
||||
}
|
||||
y = x >> 4;
|
||||
if y != 0 {
|
||||
n = n - 4;
|
||||
x = y;
|
||||
}
|
||||
y = x >> 2;
|
||||
if y != 0 {
|
||||
n = n - 2;
|
||||
x = y;
|
||||
}
|
||||
y = x >> 1;
|
||||
if y != 0 {
|
||||
n - 2
|
||||
} else {
|
||||
n - x
|
||||
}
|
||||
}
|
||||
use gba::{
|
||||
io::display::{DisplayControlMode, DisplayControlSetting, DISPCNT},
|
||||
video::Mode3,
|
||||
Color,
|
||||
};
|
||||
|
||||
#[panic_handler]
|
||||
fn panic(info: &core::panic::PanicInfo) -> ! {
|
||||
unsafe {
|
||||
const DEBUG_ENABLE_MGBA: *mut u16 = 0x4fff780 as *mut u16;
|
||||
const DEBUG_OUTPUT_BASE: *mut u8 = 0x4fff600 as *mut u8;
|
||||
const DEBUG_SEND_MGBA: *mut u16 = 0x4fff700 as *mut u16;
|
||||
const DEBUG_SEND_FLAG: u16 = 0x100;
|
||||
const DEBUG_FATAL: u16 = 0;
|
||||
const DEBUG_ERROR: u16 = 1;
|
||||
DEBUG_ENABLE_MGBA.write_volatile(0xC0DE);
|
||||
if DEBUG_ENABLE_MGBA.read_volatile() == 0x1DEA {
|
||||
// Give the location
|
||||
if let Some(location) = info.location() {
|
||||
let mut out_ptr = DEBUG_OUTPUT_BASE;
|
||||
let line = location.line();
|
||||
let mut line_bytes = [
|
||||
(line / 10000) as u8,
|
||||
((line / 1000) % 10) as u8,
|
||||
((line / 1000) % 10) as u8,
|
||||
((line / 10) % 10) as u8,
|
||||
(line % 10) as u8,
|
||||
];
|
||||
for line_bytes_mut in line_bytes.iter_mut() {
|
||||
*line_bytes_mut += b'0';
|
||||
}
|
||||
for b in "Panic: "
|
||||
.bytes()
|
||||
.chain(location.file().bytes())
|
||||
.chain(", Line ".bytes())
|
||||
.chain(line_bytes.iter().cloned())
|
||||
.take(255)
|
||||
{
|
||||
out_ptr.write_volatile(b);
|
||||
out_ptr = out_ptr.offset(1);
|
||||
}
|
||||
} else {
|
||||
let mut out_ptr = DEBUG_OUTPUT_BASE;
|
||||
for b in "Panic with no location info:".bytes().take(255) {
|
||||
out_ptr.write_volatile(b);
|
||||
out_ptr = out_ptr.offset(1);
|
||||
}
|
||||
}
|
||||
DEBUG_SEND_MGBA.write_volatile(DEBUG_SEND_FLAG + DEBUG_ERROR);
|
||||
// Give the payload
|
||||
if let Some(payload) = info.payload().downcast_ref::<&str>() {
|
||||
let mut out_ptr = DEBUG_OUTPUT_BASE;
|
||||
for b in payload.bytes().take(255) {
|
||||
out_ptr.write_volatile(b);
|
||||
out_ptr = out_ptr.offset(1);
|
||||
}
|
||||
} else {
|
||||
let mut out_ptr = DEBUG_OUTPUT_BASE;
|
||||
for b in "no payload".bytes().take(255) {
|
||||
out_ptr.write_volatile(b);
|
||||
out_ptr = out_ptr.offset(1);
|
||||
}
|
||||
}
|
||||
DEBUG_SEND_MGBA.write_volatile(DEBUG_SEND_FLAG + DEBUG_ERROR);
|
||||
DEBUG_SEND_MGBA.write_volatile(DEBUG_SEND_FLAG + DEBUG_FATAL);
|
||||
}
|
||||
use core::fmt::Write;
|
||||
use gba::mgba::{MGBADebug, MGBADebugLevel};
|
||||
if let Some(mut mgba) = MGBADebug::new() {
|
||||
let _ = write!(mgba, "{}", info);
|
||||
mgba.send(MGBADebugLevel::Fatal);
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[start]
|
||||
fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||
unsafe {
|
||||
(0x400_0000 as *mut u16).write_volatile(0x0403);
|
||||
(0x600_0000 as *mut u16).offset(120 + 80 * 240).write_volatile(0x001F);
|
||||
(0x600_0000 as *mut u16).offset(136 + 80 * 240).write_volatile(0x03E0);
|
||||
(0x600_0000 as *mut u16).offset(120 + 96 * 240).write_volatile(0x7C00);
|
||||
const SETTING: DisplayControlSetting = DisplayControlSetting::new().with_mode(DisplayControlMode::Bitmap3).with_display_bg2(true);
|
||||
DISPCNT.write(SETTING);
|
||||
Mode3::write_pixel(120, 80, Color::from_rgb(31, 0, 0));
|
||||
Mode3::write_pixel(136, 80, Color::from_rgb(0, 31, 0));
|
||||
Mode3::write_pixel(120, 96, Color::from_rgb(0, 0, 31));
|
||||
panic!("fumoffu!");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ pub mod base;
|
|||
pub(crate) use self::base::*;
|
||||
pub mod bios;
|
||||
pub mod io;
|
||||
|
||||
pub mod mgba;
|
||||
pub mod video;
|
||||
|
||||
newtype! {
|
||||
|
|
79
src/mgba.rs
Normal file
79
src/mgba.rs
Normal file
|
@ -0,0 +1,79 @@
|
|||
//! Special utils for if you're running on the mGBA emulator.
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[repr(u16)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum MGBADebugLevel {
|
||||
Fatal = 0,
|
||||
Error = 1,
|
||||
Warning = 2,
|
||||
Info = 3,
|
||||
Debug = 4,
|
||||
}
|
||||
|
||||
/// Allows writing to the `mGBA` debug output.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct MGBADebug {
|
||||
bytes_written: u8,
|
||||
}
|
||||
impl MGBADebug {
|
||||
const ENABLE_ADDRESS: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x4fff780) };
|
||||
const ENABLE_ADDRESS_INPUT: u16 = 0xC0DE;
|
||||
const ENABLE_ADDRESS_OUTPUT: u16 = 0x1DEA;
|
||||
|
||||
const OUTPUT_BASE: VolAddress<u8> = unsafe { VolAddress::new_unchecked(0x4fff600) };
|
||||
|
||||
const SEND_ADDRESS: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x4fff700) };
|
||||
const SEND_FLAG: u16 = 0x100;
|
||||
|
||||
/// Gives a new MGBADebug, if running within `mGBA`
|
||||
///
|
||||
/// # Fails
|
||||
///
|
||||
/// If you're not running in the `mGBA` emulator.
|
||||
pub fn new() -> Option<Self> {
|
||||
Self::ENABLE_ADDRESS.write(Self::ENABLE_ADDRESS_INPUT);
|
||||
if Self::ENABLE_ADDRESS.read() == Self::ENABLE_ADDRESS_OUTPUT {
|
||||
Some(MGBADebug { bytes_written: 0 })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Once output is buffered you must send it out with a level.
|
||||
///
|
||||
/// If the `Fatal` level is selected, the buffer is sent out as `Error`
|
||||
/// followed by a blank message being sent as `Error`. This is done because
|
||||
/// the `Fatal` message appears in a popup without showing up in the log, so
|
||||
/// it might accidentally be discarded.
|
||||
pub fn send(&mut self, level: MGBADebugLevel) {
|
||||
if level == MGBADebugLevel::Fatal {
|
||||
Self::SEND_ADDRESS.write(Self::SEND_FLAG | MGBADebugLevel::Error as u16);
|
||||
Self::SEND_ADDRESS.write(Self::SEND_FLAG | MGBADebugLevel::Fatal as u16);
|
||||
} else {
|
||||
Self::SEND_ADDRESS.write(Self::SEND_FLAG | level as u16);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Write for MGBADebug {
|
||||
fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> {
|
||||
unsafe {
|
||||
let mut current = Self::OUTPUT_BASE.offset(self.bytes_written as isize);
|
||||
let mut str_iter = s.bytes();
|
||||
while self.bytes_written < 255 {
|
||||
match str_iter.next() {
|
||||
Some(byte) => {
|
||||
current.write(byte);
|
||||
current = current.offset(1);
|
||||
self.bytes_written += 1;
|
||||
}
|
||||
None => return Ok(()),
|
||||
}
|
||||
}
|
||||
Err(core::fmt::Error)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue