From b11f484e7d07f7118bdefa2e617e7f63d7050ed6 Mon Sep 17 00:00:00 2001 From: lif <> Date: Sat, 9 Dec 2023 01:34:16 -0800 Subject: [PATCH] a useful panic handler --- src/halfwidth.rs | 136 ++++++++++++++++++++++++++++++++++++++++++++ src/lifont-3x5.4bpp | Bin 0 -> 1536 bytes src/main.rs | 43 +++++++++++++- 3 files changed, 177 insertions(+), 2 deletions(-) create mode 100644 src/halfwidth.rs create mode 100644 src/lifont-3x5.4bpp diff --git a/src/halfwidth.rs b/src/halfwidth.rs new file mode 100644 index 0000000..ec27a42 --- /dev/null +++ b/src/halfwidth.rs @@ -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)); + } +} diff --git a/src/lifont-3x5.4bpp b/src/lifont-3x5.4bpp new file mode 100644 index 0000000000000000000000000000000000000000..07ccbc16c8a48a86113e1a6db70cee3e3c4c7640 GIT binary patch literal 1536 zcmZ{kffd^z3`B({Af5o&f5`q1b`41@KP2#1p^b&KlSoZie%Pb{x@Gk5d8KHlz zf%dGWMT=2B(IeUOT4(*@X;~)HkLZY+VB6z-x5i}T%$|w=zqu=@^keX;R2BX3KIlbH z84m^sV-$OQjXA_4J01Wfm~W;w7#!C=b{u#Yr^``Ps-bk-qE)`Lj)tY1X4W}2FtK}W z`L7Nk1#3c=|7wqEFAd(2P0xq#*UWiQ^^yd=tHz54dNhD5hAH&S58T=kr$hZE($5YWsN;;UGx^0C46tjeb;QG-?2MJ4ucN-o+MDZo=X?CP^&bTYZl2W_ zZV-Ku2Nw2u;H*5%m+wcs!OK+HF0UV4Ud{Y>rZa!b%KAJf-efrAVdky&@mHu$yRkAA ze^nV@@!-))r8R;XpC25JY{BL^$Fo0N4#p%+teRlIz@;m4@d8#H=4ZU zp9=oP<{l|UqCC3Mo5!@D0Qt;7o+M?{$ac51s@f761SM literal 0 HcmV?d00001 diff --git a/src/main.rs b/src/main.rs index bf04120..91bc484 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,17 +1,52 @@ // from https://github.com/rust-console/gba/blob/main/examples/hello.rs #![no_std] #![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 gba::prelude::*; +#[allow(unused_must_use)] #[panic_handler] 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)] 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"] @@ -94,5 +129,9 @@ extern "C" fn main() -> ! { if k.right() { x_off = x_off.wrapping_sub(1); } + + if k.start() { + panic!("pressed start") + } } }