diff --git a/examples/game.rs b/examples/game.rs index 93b0530..6477baa 100644 --- a/examples/game.rs +++ b/examples/game.rs @@ -5,8 +5,9 @@ use gba::prelude::*; #[panic_handler] fn panic_handler(info: &core::panic::PanicInfo) -> ! { - use core::fmt::Write; + #[cfg(debug_assertions)] if let Ok(mut logger) = MgbaBufferedLogger::try_new(MgbaMessageLevel::Fatal) { + use core::fmt::Write; writeln!(logger, "{info}").ok(); } loop {} @@ -25,7 +26,7 @@ struct Rect { h: u16, } impl Rect { - pub fn intersect(self, other: Self) -> bool { + fn intersect(self, other: Self) -> bool { self.x < other.x + other.w && self.x + self.w > other.x && self.y < other.y + other.h @@ -50,7 +51,7 @@ extern "C" fn main() -> ! { creatures[0].x = 11; creatures[0].y = 14; // - creatures[1].x = 41; + creatures[1].x = 44; creatures[1].y = 38; creatures[2].x = 100; creatures[2].y = 23; @@ -61,11 +62,16 @@ extern "C" fn main() -> ! { let mut world = [[0_u8; 32]; 32]; for i in 0..32 { - world[0][i] = b'z'; - world[31][i] = b'z'; - world[i][0] = b'z'; - world[i][31] = b'z'; + world[0][i] = Cga8x8Thick::BOX_HORIZONTAL; + world[19][i] = Cga8x8Thick::BOX_HORIZONTAL; + world[i][0] = Cga8x8Thick::BOX_VERTICAL; + world[i][29] = Cga8x8Thick::BOX_VERTICAL; } + world[0][0] = Cga8x8Thick::BOX_UPPER_LEFT; + world[0][29] = Cga8x8Thick::BOX_UPPER_RIGHT; + world[19][0] = Cga8x8Thick::BOX_LOWER_LEFT; + world[19][29] = Cga8x8Thick::BOX_LOWER_RIGHT; + // world[1][3] = b'B'; world[2][3] = b'G'; world[3][3] = b'0'; @@ -77,12 +83,14 @@ extern "C" fn main() -> ! { TIMER0_CONTROL.write(TimerControl::new().with_enabled(true)); + // bg BG_PALETTE.index(1).write(Color::MAGENTA); - OBJ_PALETTE.index(1).write(Color::CYAN); - OBJ_PALETTE.index(16 * 1 + 1).write(Color::GREEN); - OBJ_PALETTE.index(16 * 2 + 1).write(Color::RED); - OBJ_PALETTE.index(16 * 3 + 1).write(Color::BLUE); - OBJ_PALETTE.index(16 * 4 + 1).write(Color::YELLOW); + // obj + let colors = + [Color::CYAN, Color::GREEN, Color::RED, Color::BLUE, Color::YELLOW]; + for (pal, color) in colors.iter().enumerate() { + obj_palbank(pal).index(1).write(*color); + } Cga8x8Thick.bitunpack_4bpp(CHARBLOCK0_4BPP.as_region(), 0); Cga8x8Thick.bitunpack_4bpp(OBJ_TILES.as_region(), 0); @@ -141,56 +149,56 @@ extern "C" fn main() -> ! { if keys.up() { let new_p = Position { x: player.x, y: player.y - 1 }; let new_r = Rect { x: new_p.x, y: new_p.y, w: 8, h: 8 }; - if new_r + let terrain_clear = new_r .iter_tiles() - .all(|(tx, ty)| allows_movement(world[ty as usize][tx as usize])) - && enemies.iter().all(|enemy| { - let enemy_r = Rect { x: enemy.x, y: enemy.y, w: 8, h: 8 }; - !new_r.intersect(enemy_r) - }) - { + .all(|(tx, ty)| allows_movement(world[ty as usize][tx as usize])); + let enemy_clear = enemies.iter().all(|enemy| { + let enemy_r = Rect { x: enemy.x, y: enemy.y, w: 8, h: 8 }; + !new_r.intersect(enemy_r) + }); + if terrain_clear && enemy_clear { *player = new_p; } } if keys.down() { let new_p = Position { x: player.x, y: player.y + 1 }; let new_r = Rect { x: new_p.x, y: new_p.y, w: 8, h: 8 }; - if new_r + let terrain_clear = new_r .iter_tiles() - .all(|(tx, ty)| allows_movement(world[ty as usize][tx as usize])) - && enemies.iter().all(|enemy| { - let enemy_r = Rect { x: enemy.x, y: enemy.y, w: 8, h: 8 }; - !new_r.intersect(enemy_r) - }) - { + .all(|(tx, ty)| allows_movement(world[ty as usize][tx as usize])); + let enemy_clear = enemies.iter().all(|enemy| { + let enemy_r = Rect { x: enemy.x, y: enemy.y, w: 8, h: 8 }; + !new_r.intersect(enemy_r) + }); + if terrain_clear && enemy_clear { *player = new_p; } } if keys.left() { let new_p = Position { x: player.x - 1, y: player.y }; let new_r = Rect { x: new_p.x, y: new_p.y, w: 8, h: 8 }; - if new_r + let terrain_clear = new_r .iter_tiles() - .all(|(tx, ty)| allows_movement(world[ty as usize][tx as usize])) - && enemies.iter().all(|enemy| { - let enemy_r = Rect { x: enemy.x, y: enemy.y, w: 8, h: 8 }; - !new_r.intersect(enemy_r) - }) - { + .all(|(tx, ty)| allows_movement(world[ty as usize][tx as usize])); + let enemy_clear = enemies.iter().all(|enemy| { + let enemy_r = Rect { x: enemy.x, y: enemy.y, w: 8, h: 8 }; + !new_r.intersect(enemy_r) + }); + if terrain_clear && enemy_clear { *player = new_p; } } if keys.right() { let new_p = Position { x: player.x + 1, y: player.y }; let new_r = Rect { x: new_p.x, y: new_p.y, w: 8, h: 8 }; - if new_r + let terrain_clear = new_r .iter_tiles() - .all(|(tx, ty)| allows_movement(world[ty as usize][tx as usize])) - && enemies.iter().all(|enemy| { - let enemy_r = Rect { x: enemy.x, y: enemy.y, w: 8, h: 8 }; - !new_r.intersect(enemy_r) - }) - { + .all(|(tx, ty)| allows_movement(world[ty as usize][tx as usize])); + let enemy_clear = enemies.iter().all(|enemy| { + let enemy_r = Rect { x: enemy.x, y: enemy.y, w: 8, h: 8 }; + !new_r.intersect(enemy_r) + }); + if terrain_clear && enemy_clear { *player = new_p; } } diff --git a/examples/hello.rs b/examples/hello.rs index 1a94d13..183b9d4 100644 --- a/examples/hello.rs +++ b/examples/hello.rs @@ -6,6 +6,7 @@ use gba::prelude::*; #[panic_handler] fn panic_handler(info: &core::panic::PanicInfo) -> ! { + #[cfg(debug_assertions)] if let Ok(mut logger) = MgbaBufferedLogger::try_new(MgbaMessageLevel::Fatal) { writeln!(logger, "{info}").ok(); } diff --git a/src/builtin_art/cga_8x8_thick.rs b/src/builtin_art/cga_8x8_thick.rs index 1f333a7..a6fbf89 100644 --- a/src/builtin_art/cga_8x8_thick.rs +++ b/src/builtin_art/cga_8x8_thick.rs @@ -7,6 +7,107 @@ use crate::{ video::{Tile4, Tile8}, }; +macro_rules! glyph { + ($name:ident = $id:expr) => { + pub const $name: u8 = $id; + }; +} + +#[derive(Debug, Clone, Copy)] +pub struct Cga8x8Thick; + +impl Cga8x8Thick { + // 0x0? + glyph!(NULL = 0x00); + glyph!(FACE = 0x01); + glyph!(FACE_INVERSE = 0x02); + glyph!(HEART = 0x03); + glyph!(DIAMOND = 0x04); + glyph!(CLUB = 0x05); + glyph!(SPADE = 0x06); + glyph!(BULLET = 0x07); + glyph!(BULLET_INVERSE = 0x08); + glyph!(CIRCLE = 0x09); + glyph!(CIRCLE_INVERSE = 0x0A); + glyph!(MALE = 0x0B); + glyph!(FEMALE = 0x0C); + glyph!(NOTE = 0x0D); + glyph!(NOTE_DOUBLE = 0x0E); + glyph!(SOLAR = 0x0F); + + // 0x1? + glyph!(POINTER_RIGHT = 0x10); + glyph!(POINTER_LEFT = 0x11); + glyph!(ARROW_UP_DOWN = 0x12); + glyph!(BANG_DOUBLE = 0x13); + glyph!(PARAGRAPH = 0x14); + glyph!(SECTION = 0x15); + glyph!(UNDERLINE_THICK = 0x16); + glyph!(ARROW_UP_DOWN_UNDERLINED = 0x17); + glyph!(ARROW_UP = 0x18); + glyph!(ARROW_DOWN = 0x19); + glyph!(ARROW_RIGHT = 0x1A); + glyph!(ARROW_LEFT = 0x1B); + glyph!(RIGHT_ANGLE = 0x1C); + glyph!(ARROW_LEFT_RIGHT = 0x1D); + glyph!(POINTER_UP = 0x1E); + glyph!(POINTER_DOWN = 0x1F); + + // Box drawing + glyph!(BOX_VERTICAL = 0xB3); + glyph!(BOX_HORIZONTAL = 0xC4); + glyph!(BOX_UPPER_RIGHT = 0xBF); + glyph!(BOX_UPPER_LEFT = 0xDA); + glyph!(BOX_LOWER_RIGHT = 0xD9); + glyph!(BOX_LOWER_LEFT = 0xC0); + + /// Bit unpacks the data (4bpp depth) to the location given. + /// + /// * `offset_and_touch_zero`: Works like the [`BitUnpackInfo`] field. By + /// default you should usually pass 0 here. + /// + /// ## Panics + /// * Requires at least 256 elements of space within the region. + #[inline] + pub fn bitunpack_4bpp( + self, b: VolRegion, offset_and_touch_zero: u32, + ) { + assert!(b.len() >= 256); + let src = CGA_8X8_THICK.as_ptr(); + let dest = b.index(0).as_usize() as *mut u32; + let info = BitUnpackInfo { + src_byte_len: size_of_val(&CGA_8X8_THICK) as u16, + src_elem_width: 1, + dest_elem_width: 4, + offset_and_touch_zero, + }; + unsafe { BitUnPack(src.cast(), dest, &info) }; + } + + /// Bit unpacks the data (8bpp depth) to the location given. + /// + /// * `offset_and_touch_zero`: Works like the [`BitUnpackInfo`] field. By + /// default you should usually pass 0 here. + /// + /// ## Panics + /// * Requires at least 256 elements of space within the region. + #[inline] + pub fn bitunpack_8bpp( + self, b: VolRegion, offset_and_touch_zero: u32, + ) { + assert!(b.len() >= 256); + let src = CGA_8X8_THICK.as_ptr(); + let dest = b.index(0).as_usize() as *mut u32; + let info = BitUnpackInfo { + src_byte_len: size_of_val(&CGA_8X8_THICK) as u16, + src_elem_width: 1, + dest_elem_width: 8, + offset_and_touch_zero, + }; + unsafe { BitUnPack(src.cast(), dest, &info) }; + } +} + /// The CGA [Code Page 437][cp437] type face, with thick lines. /// /// There's 256 tiles, packed down to 1bpp. @@ -126,105 +227,3 @@ pub static CGA_8X8_THICK: [u32; 512] = [ 0x3636361E, 0x00000036, 0x060C180E, 0x0000001E, 0x3C3C0000, 0x00003C3C, 0x00000000, 0x00000000, ]; - -macro_rules! glyph_name { - ($name:ident = $x:expr) => { - pub const $name: u8 = $x; - }; -} - -#[derive(Debug, Clone, Copy)] -pub struct Cga8x8Thick; -impl Cga8x8Thick { - // 0x0? series - glyph_name!(NULL = 0x00); - glyph_name!(SMILEY = 0x01); - glyph_name!(SMILEY_INVERSE = 0x02); - glyph_name!(HEART = 0x03); - glyph_name!(DIAMOND = 0x04); - glyph_name!(CLUB = 0x05); - glyph_name!(SPADE = 0x06); - glyph_name!(BULLET = 0x07); - glyph_name!(BULLET_INVERSE = 0x08); - glyph_name!(BULLET_BIG = 0x09); - glyph_name!(BULLET_INVERSE_BIG = 0x0A); - glyph_name!(MALE = 0x0B); - glyph_name!(FEMALE = 0x0C); - glyph_name!(NOTE = 0x0D); - glyph_name!(DOUBLE_NOTE = 0x0E); - glyph_name!(SOLAR = 0x0F); - // 0x1? series - glyph_name!(POINTER_RIGHT = 0x10); - glyph_name!(POINTER_LEFT = 0x11); - glyph_name!(ARROW_UP_DOWN = 0x12); - glyph_name!(DOUBLE_BANG = 0x13); - glyph_name!(PARAGRAPH = 0x14); - glyph_name!(SECTION = 0x15); - glyph_name!(UNDERLINE_THICK = 0x16); - glyph_name!(ARROW_UP_DOWN_UNDERLINED = 0x17); - glyph_name!(ARROW_UP = 0x18); - glyph_name!(ARROW_DOWN = 0x19); - glyph_name!(ARROW_RIGHT = 0x1A); - glyph_name!(ARROW_LEFT = 0x1B); - glyph_name!(RIGHT_ANGLE = 0x1C); - glyph_name!(ARROW_LEFT_RIGHT = 0x1D); - glyph_name!(POINTER_UP = 0x1E); - glyph_name!(POINTER_DOWN = 0x1F); - // 0x7? series - glyph_name!(HOUSE = 0x7F); - // - glyph_name!(BOX_LIGHT_HORIZONTAL = 0xC4); - glyph_name!(BOX_LIGHT_VERTICAL = 0xB3); - glyph_name!(BOX_LIGHT_UPPER_LEFT = 0xDA); - glyph_name!(BOX_LIGHT_UPPER_RIGHT = 0xBF); - glyph_name!(BOX_LIGHT_LOWER_LEFT = 0xC0); - glyph_name!(BOX_LIGHT_LOWER_RIGHT = 0xD9); -} - -impl Cga8x8Thick { - /// Bit unpacks the data (4bpp depth) to the location given. - /// - /// * `offset_and_touch_zero`: Works like the [`BitUnpackInfo`] field. By - /// default you should usually pass 0 here. - /// - /// ## Panics - /// * Requires at least 256 elements of space within the region. - #[inline] - pub fn bitunpack_4bpp( - self, b: VolRegion, offset_and_touch_zero: u32, - ) { - assert!(b.len() >= 256); - let src = CGA_8X8_THICK.as_ptr(); - let dest = b.index(0).as_usize() as *mut u32; - let info = BitUnpackInfo { - src_byte_len: size_of_val(&CGA_8X8_THICK) as u16, - src_elem_width: 1, - dest_elem_width: 4, - offset_and_touch_zero, - }; - unsafe { BitUnPack(src.cast(), dest, &info) }; - } - - /// Bit unpacks the data (8bpp depth) to the location given. - /// - /// * `offset_and_touch_zero`: Works like the [`BitUnpackInfo`] field. By - /// default you should usually pass 0 here. - /// - /// ## Panics - /// * Requires at least 256 elements of space within the region. - #[inline] - pub fn bitunpack_8bpp( - self, b: VolRegion, offset_and_touch_zero: u32, - ) { - assert!(b.len() >= 256); - let src = CGA_8X8_THICK.as_ptr(); - let dest = b.index(0).as_usize() as *mut u32; - let info = BitUnpackInfo { - src_byte_len: size_of_val(&CGA_8X8_THICK) as u16, - src_elem_width: 1, - dest_elem_width: 8, - offset_and_touch_zero, - }; - unsafe { BitUnPack(src.cast(), dest, &info) }; - } -} diff --git a/src/mmio.rs b/src/mmio.rs index 6690c30..3825dea 100644 --- a/src/mmio.rs +++ b/src/mmio.rs @@ -201,6 +201,21 @@ def_mmio!(0x0500_0000 = BACKDROP_COLOR: VolAddress; "Color th def_mmio!(0x0500_0000 = BG_PALETTE: VolBlock; "Background tile palette entries."); def_mmio!(0x0500_0200 = OBJ_PALETTE: VolBlock; "Object tile palette entries."); +#[inline] +#[must_use] +#[cfg_attr(feature="track_caller", track_caller)] +pub const fn bg_palbank(bank: usize) -> VolBlock { + let u = BG_PALETTE.index(bank * 16).as_usize(); + unsafe { VolBlock::new(u) } +} +#[inline] +#[must_use] +#[cfg_attr(feature="track_caller", track_caller)] +pub const fn obj_palbank(bank: usize) -> VolBlock { + let u = OBJ_PALETTE.index(bank * 16).as_usize(); + unsafe { VolBlock::new(u) } +} + // Video RAM (VRAM) /// The VRAM byte offset per screenblock index.