a useful panic handler
This commit is contained in:
parent
d250bb5c46
commit
b11f484e7d
136
src/halfwidth.rs
Normal file
136
src/halfwidth.rs
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
use core::fmt::Write;
|
||||||
|
use gba::prelude::*;
|
||||||
|
|
||||||
|
const FONT_WIDTH: usize = 4;
|
||||||
|
const FONT_HEIGHT: usize = 6;
|
||||||
|
const TAB_CHARS: usize = 4;
|
||||||
|
|
||||||
|
const MAP_WIDTH: usize = 32;
|
||||||
|
const MAP_HEIGHT: usize = 32;
|
||||||
|
const WRAP_COL: usize = 60;
|
||||||
|
|
||||||
|
// 96 printable ascii chars, each using half of a 8x8 4bpp tile
|
||||||
|
#[link_section = ".ewram"]
|
||||||
|
static LIFONT: [u8; 96 * 16] = *include_bytes!("lifont-3x5.4bpp");
|
||||||
|
|
||||||
|
type DoubleHalfTile = [u16; 2 * 8];
|
||||||
|
|
||||||
|
fn font() -> &'static [DoubleHalfTile] {
|
||||||
|
unsafe { core::slice::from_raw_parts(LIFONT.as_ptr() as *const DoubleHalfTile, 96 / 2) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn charblock_write(tile_index: usize, tile_row_index: usize, value: u16) {
|
||||||
|
unsafe {
|
||||||
|
let tile_ptr = (CHARBLOCK0_4BPP.as_ptr() as *mut DoubleHalfTile).add(tile_index);
|
||||||
|
(*tile_ptr)
|
||||||
|
.as_mut_ptr()
|
||||||
|
.add(tile_row_index)
|
||||||
|
.write_volatile(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: support for drawing to a region of the charblock/screenblock
|
||||||
|
// (i.e. not necessarily taking over the whole display)
|
||||||
|
pub struct TextPainter {
|
||||||
|
pixel_row: usize,
|
||||||
|
pixel_col: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::fmt::Write for TextPainter {
|
||||||
|
fn write_str(&mut self, text: &str) -> core::fmt::Result {
|
||||||
|
for c in text
|
||||||
|
.chars()
|
||||||
|
.map(|c| c.as_ascii().map(|a| a.to_u8()).unwrap_or(0x7f) as usize)
|
||||||
|
{
|
||||||
|
if self.pixel_col >= FONT_WIDTH * WRAP_COL {
|
||||||
|
self.pixel_row += FONT_HEIGHT;
|
||||||
|
self.pixel_col = 0;
|
||||||
|
}
|
||||||
|
if (self.pixel_row >> 3) > MAP_HEIGHT {
|
||||||
|
return Err(core::fmt::Error::default());
|
||||||
|
}
|
||||||
|
if c < 0x20 {
|
||||||
|
match c as u8 {
|
||||||
|
// '\b', backspace
|
||||||
|
8 => {
|
||||||
|
self.pixel_col = self.pixel_col.saturating_sub(FONT_WIDTH);
|
||||||
|
}
|
||||||
|
b'\t' => {
|
||||||
|
self.pixel_col =
|
||||||
|
(self.pixel_col + 1).next_multiple_of(TAB_CHARS * FONT_WIDTH);
|
||||||
|
}
|
||||||
|
b'\n' => {
|
||||||
|
self.pixel_row += FONT_HEIGHT;
|
||||||
|
self.pixel_col = 0; // assume cooked
|
||||||
|
}
|
||||||
|
b'\r' => {
|
||||||
|
self.pixel_col = 0;
|
||||||
|
}
|
||||||
|
_ => {} // TODO?
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let font_tile = &font()[(c - 0x20) >> 1];
|
||||||
|
let out_tile_col = self.pixel_col >> 3;
|
||||||
|
let mut out_pixel_row = self.pixel_row;
|
||||||
|
for font_row in font_tile
|
||||||
|
.iter()
|
||||||
|
.skip(c & 1)
|
||||||
|
.step_by(2)
|
||||||
|
.take(FONT_HEIGHT)
|
||||||
|
.copied()
|
||||||
|
{
|
||||||
|
let out_tile_row = out_pixel_row >> 3;
|
||||||
|
let out_tile_pixel_row =
|
||||||
|
((out_pixel_row & 7) << 1) + ((self.pixel_col & 4) >> 2);
|
||||||
|
let idx = out_tile_row * MAP_WIDTH + out_tile_col;
|
||||||
|
charblock_write(idx, out_tile_pixel_row, font_row);
|
||||||
|
out_pixel_row += 1;
|
||||||
|
}
|
||||||
|
self.pixel_col += FONT_WIDTH;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TextPainter {
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
pixel_row: 0,
|
||||||
|
pixel_col: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup_display(&mut self) {
|
||||||
|
self.pixel_row = 0;
|
||||||
|
self.pixel_col = 0;
|
||||||
|
let dispcnt = DisplayControl::new().with_show_bg0(true);
|
||||||
|
DISPCNT.write(dispcnt);
|
||||||
|
let bg0cnt = BackgroundControl::new()
|
||||||
|
.with_size(0)
|
||||||
|
.with_charblock(0)
|
||||||
|
.with_screenblock(16);
|
||||||
|
BG0CNT.write(bg0cnt);
|
||||||
|
BG0HOFS.write(0);
|
||||||
|
BG0VOFS.write(0);
|
||||||
|
let screenblock = TEXT_SCREENBLOCKS.get_frame(16).unwrap();
|
||||||
|
let mut x = 0;
|
||||||
|
for r in 0..32 {
|
||||||
|
let row = screenblock.get_row(r).unwrap();
|
||||||
|
for cell in row.iter() {
|
||||||
|
cell.write(TextEntry::new().with_tile(x));
|
||||||
|
x += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for tile in CHARBLOCK0_4BPP.iter() {
|
||||||
|
tile.write([0; 8]);
|
||||||
|
}
|
||||||
|
BG_PALETTE
|
||||||
|
.index(1)
|
||||||
|
.write(Color::new().with_red(22).with_green(16).with_blue(16));
|
||||||
|
BG_PALETTE
|
||||||
|
.index(2)
|
||||||
|
.write(Color::new().with_red(31).with_green(31).with_blue(31));
|
||||||
|
BG_PALETTE.index(0).write(Color::new().with_red(8));
|
||||||
|
}
|
||||||
|
}
|
BIN
src/lifont-3x5.4bpp
Normal file
BIN
src/lifont-3x5.4bpp
Normal file
Binary file not shown.
43
src/main.rs
43
src/main.rs
|
@ -1,17 +1,52 @@
|
||||||
// from https://github.com/rust-console/gba/blob/main/examples/hello.rs
|
// from https://github.com/rust-console/gba/blob/main/examples/hello.rs
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
|
#![feature(ascii_char)]
|
||||||
|
#![feature(panic_info_message)]
|
||||||
|
// rustc is simply wrong about core::fmt::Write being unused,
|
||||||
|
// it's used in the panic handler.
|
||||||
|
#![allow(unused_imports)]
|
||||||
|
|
||||||
|
mod halfwidth;
|
||||||
|
|
||||||
use core::fmt::Write;
|
use core::fmt::Write;
|
||||||
|
|
||||||
use gba::prelude::*;
|
use gba::prelude::*;
|
||||||
|
|
||||||
|
#[allow(unused_must_use)]
|
||||||
#[panic_handler]
|
#[panic_handler]
|
||||||
fn panic_handler(info: &core::panic::PanicInfo) -> ! {
|
fn panic_handler(info: &core::panic::PanicInfo) -> ! {
|
||||||
|
let mut text_painter = halfwidth::TextPainter::new();
|
||||||
|
text_painter.setup_display();
|
||||||
|
|
||||||
|
text_painter.write_str("panic at ");
|
||||||
|
if let Some(location) = info.location() {
|
||||||
|
write!(&mut text_painter, "{}:{}", location.file(), location.line());
|
||||||
|
} else {
|
||||||
|
text_painter.write_str("unknown location");
|
||||||
|
};
|
||||||
|
if let Some(msg) = info.message() {
|
||||||
|
write!(&mut text_painter, ":\n");
|
||||||
|
core::fmt::write(&mut text_painter, *msg);
|
||||||
|
}
|
||||||
|
if let Some(s) = info.payload().downcast_ref::<&str>() {
|
||||||
|
text_painter.write_str("\n");
|
||||||
|
text_painter.write_str(s);
|
||||||
|
}
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
if let Ok(mut logger) = MgbaBufferedLogger::try_new(MgbaMessageLevel::Fatal) {
|
if let Ok(mut logger) = MgbaBufferedLogger::try_new(MgbaMessageLevel::Fatal) {
|
||||||
writeln!(logger, "{info}").ok();
|
for _ in 0..4 {
|
||||||
|
VBlankIntrWait();
|
||||||
|
}
|
||||||
|
if let Some(args) = info.message() {
|
||||||
|
logger.write_fmt(*args);
|
||||||
|
} else {
|
||||||
|
writeln!(logger, "{info}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loop {
|
||||||
|
VBlankIntrWait();
|
||||||
}
|
}
|
||||||
loop {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[link_section = ".ewram"]
|
#[link_section = ".ewram"]
|
||||||
|
@ -94,5 +129,9 @@ extern "C" fn main() -> ! {
|
||||||
if k.right() {
|
if k.right() {
|
||||||
x_off = x_off.wrapping_sub(1);
|
x_off = x_off.wrapping_sub(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if k.start() {
|
||||||
|
panic!("pressed start")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue