From e9b62f183278001fb236e9ec29d38c9ae1534c79 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Tue, 4 Dec 2018 23:27:35 -0700 Subject: [PATCH] more writing, but the demo just shows white --- book/src/ch03/gba_prng.md | 2 +- book/src/ch03/memory_game.md | 210 ++++++++++++++++++++++ book/src/ch03/regular_backgrounds.md | 12 +- book/src/ch03/regular_objects.md | 26 +++ book/src/ch03/tile_data.md | 8 +- examples/memory_game.rs | 249 +++++++++++++++++++++------ src/core_extras.rs | 49 ++---- 7 files changed, 453 insertions(+), 103 deletions(-) diff --git a/book/src/ch03/gba_prng.md b/book/src/ch03/gba_prng.md index 6043707..6745517 100644 --- a/book/src/ch03/gba_prng.md +++ b/book/src/ch03/gba_prng.md @@ -297,7 +297,7 @@ do this as our first thing at startup, but we might show the title and like a ```rust /// Mucks with the settings of Timers 0 and 1. -fn u32_from_user_wait() -> u32 { +unsafe fn u32_from_user_wait() -> u32 { let mut t = TimerControl::default(); t.set_enabled(true); t.set_cascading(true); diff --git a/book/src/ch03/memory_game.md b/book/src/ch03/memory_game.md index e910087..ae854cd 100644 --- a/book/src/ch03/memory_game.md +++ b/book/src/ch03/memory_game.md @@ -1,2 +1,212 @@ # memory_game + +For this example to show off our new skills we'll make a "memory" game. The idea +is that there's some face down cards and you pick one, it flips, you pick a +second, if they match they both go away, if they don't match they both turn back +face down. The player keeps going until all the cards are gone, then we'll deal +the cards again. + +For this example, I started with the `light_cycle.rs` example and then just +copied it into a new file, `memory_game.rs`. Then I added most all the code from +the previous sections right into that file, so we'll assume that all those +definitions are in scope. + +## Getting Some Images + +First we need some images to show! Let's have one for our little selector thingy +that we'll move around to pick cards with. How about some little triangles at +the corner of a square like on a picture frame. + +```rust +#[rustfmt::skip] +pub const CARD_SELECTOR: Tile4bpp = Tile4bpp { + data : [ + 0x44400444, + 0x44000044, + 0x40000004, + 0x00000000, + 0x00000000, + 0x40000004, + 0x44000044, + 0x44400444 + ] +}; +``` + +That weird looking attribute keeps rustfmt from spreading out the values, so +that we can see it as an ASCII art. Now that we understand what an individual +tile looks like, let's add some mono-color squares. + +```rust +#[rustfmt::skip] +pub const FULL_ONE: Tile4bpp = Tile4bpp { + data : [ + 0x11111111, + 0x11111111, + 0x11111111, + 0x11111111, + 0x11111111, + 0x11111111, + 0x11111111, + 0x11111111, + ] +}; + +#[rustfmt::skip] +pub const FULL_TWO: Tile4bpp = Tile4bpp { + data : [ + 0x22222222, + 0x22222222, + 0x22222222, + 0x22222222, + 0x22222222, + 0x22222222, + 0x22222222, + 0x22222222 + ] +}; + +#[rustfmt::skip] +pub const FULL_THREE: Tile4bpp = Tile4bpp { + data : [ + 0x33333333, + 0x33333333, + 0x33333333, + 0x33333333, + 0x33333333, + 0x33333333, + 0x33333333, + 0x33333333 + ] +}; +``` + +We can control the rest with palbank selection. Since there's 16 palbanks, +that's 48 little colored squares we can make, and 16 different selector colors, +which should be plenty in both cases. + +## Setup The Images + +### Arrange the PALRAM + +Alright, so, as we went over, the first step is to make sure that we've got our +palette data in order. We'll be using this to set our palette values. + +```rust +pub fn set_bg_palette(slot: usize, color: u16) { + assert!(slot < 256); + unsafe { PALRAM_BG_BASE.offset(slot as isize).write(color) } +} +``` + +Should the type of `slot` be changed to `u8` instead of `usize`? Well, maybe. +Let's not worry about it at the moment. + +Of course, we don't need to set the color black, all the values start as black. +We just need to set the other colors we'll be wanting. For this demo, we'll just +use the same basic colors for both the BG and Object stuff. + +```rust +pub fn init_palette() { + // palbank 0: black/white/gray + set_bg_palette(2, rgb16(31, 31, 31)); + set_bg_palette(3, rgb16(15, 15, 15)); + // palbank 1 is reds + set_bg_palette(1 * 16 + 1, rgb16(31, 0, 0)); + set_bg_palette(1 * 16 + 2, rgb16(22, 0, 0)); + set_bg_palette(1 * 16 + 3, rgb16(10, 0, 0)); + // palbank 2 is greens + set_bg_palette(2 * 16 + 1, rgb16(0, 31, 0)); + set_bg_palette(2 * 16 + 2, rgb16(0, 22, 0)); + set_bg_palette(2 * 16 + 3, rgb16(0, 10, 0)); + // palbank 2 is blues + set_bg_palette(3 * 16 + 1, rgb16(0, 0, 31)); + set_bg_palette(3 * 16 + 2, rgb16(0, 0, 22)); + set_bg_palette(3 * 16 + 3, rgb16(0, 0, 10)); + + // Direct copy all BG selections into OBJ palette too + let mut bgp = PALRAM_BG_BASE; + let mut objp = PALRAM_OBJECT_BASE; + for _ in 0..(4 * 16) { + objp.write(bgp.read()); + bgp = bgp.offset(1); + objp = objp.offset(1); + } +} +``` + +### Arrange the Objects + +So, time to think about objects. I'm thinking we'll have 13 objects in use. One +for the selector, and then 12 for the cards (a simple grid that's 4 wide and 3 +tall). + +We want a way to easily clear away all the objects that we're not using, which +is all the slots starting at some index and then going to the end. + +```rust +pub fn clear_objects_starting_with(base_slot: usize) { + let mut obj = ObjectAttributes::default(); + obj.set_rendering(ObjectRenderMode::Disabled); + for s in base_slot..128 { + set_object_attributes(s, obj); + } +} +``` + +Next we set out the positions of our cards. We set the tile data we need, and +then assign the object attributes to go with it. For this, we'll make the +position finder function be its own thing since we'll also need it for the card +selector to move around. Finally, we set our selector as being at position 0,0 +of the card grid. + +```rust +pub fn position_of_card(card_col: usize, card_row: usize) -> (u16, u16) { + (10 + card_col as u16 * 17, 5 + card_row as u16 * 15) +} + +pub fn arrange_cards() { + set_obj_tile_4bpp(1, FULL_ONE); + set_obj_tile_4bpp(2, FULL_TWO); + set_obj_tile_4bpp(3, FULL_THREE); + let mut obj = ObjectAttributes::default(); + obj.set_tile_index(2); // along with palbank0, this is a white card + for card_row in 0..3 { + for card_col in 0..4 { + let (col, row) = position_of_card(card_col, card_row); + obj.set_column(col); + obj.set_row(row); + set_object_attributes(1 + card_col as usize + (card_row as usize * 3), obj); + } + } +} + +pub fn init_selector() { + set_obj_tile_4bpp(0, CARD_SELECTOR); + let mut obj = ObjectAttributes::default(); + let (col, row) = position_of_card(0, 0); + obj.set_column(col); + obj.set_row(row); + set_object_attributes(0, obj); +} +``` + +### Arrange the Background + +TODO + +## Shuffling The Cards + +TODO + +## Picking One Card + +TODO + +## Picking The Second Card + +TODO + +## Resetting The Game + TODO diff --git a/book/src/ch03/regular_backgrounds.md b/book/src/ch03/regular_backgrounds.md index cf1b286..29675a8 100644 --- a/book/src/ch03/regular_backgrounds.md +++ b/book/src/ch03/regular_backgrounds.md @@ -76,28 +76,28 @@ within, and then you add the index of the tile slot you're placing it into times the size of that type of tile. Like this: ```rust -pub fn bg_tile_4pp(base_block: usize, tile_index: usize) -> Tile4bpp { +pub fn bg_tile_4bpp(base_block: usize, tile_index: usize) -> Tile4bpp { assert!(base_block < 4); assert!(tile_index < 512); let address = VRAM + size_of::() * base_block + size_of::() * tile_index; unsafe { VolatilePtr(address as *mut Tile4bpp).read() } } -pub fn set_bg_tile_4pp(base_block: usize, tile_index: usize, tile: Tile4bpp) { +pub fn set_bg_tile_4bpp(base_block: usize, tile_index: usize, tile: Tile4bpp) { assert!(base_block < 4); assert!(tile_index < 512); let address = VRAM + size_of::() * base_block + size_of::() * tile_index; unsafe { VolatilePtr(address as *mut Tile4bpp).write(tile) } } -pub fn bg_tile_8pp(base_block: usize, tile_index: usize) -> Tile8bpp { +pub fn bg_tile_8bpp(base_block: usize, tile_index: usize) -> Tile8bpp { assert!(base_block < 4); assert!(tile_index < 256); let address = VRAM + size_of::() * base_block + size_of::() * tile_index; unsafe { VolatilePtr(address as *mut Tile8bpp).read() } } -pub fn set_bg_tile_8pp(base_block: usize, tile_index: usize, tile: Tile8bpp) { +pub fn set_bg_tile_8bpp(base_block: usize, tile_index: usize, tile: Tile8bpp) { assert!(base_block < 4); assert!(tile_index < 256); let address = VRAM + size_of::() * base_block + size_of::() * tile_index; @@ -154,7 +154,7 @@ appropriately. #[derive(Clone, Copy)] #[repr(transparent)] pub struct RegularScreenblock { - data: [RegularScreenblockEntry; 32 * 32], + pub data: [RegularScreenblockEntry; 32 * 32], } #[derive(Debug, Clone, Copy, Default)] @@ -207,7 +207,7 @@ impl RegularScreenblockEntry { } pub fn set_palbank_index(&mut self, palbank_index: u16) { self.0 &= 0b1111_1111_1111; - self.0 |= palbank_index; + self.0 |= palbank_index << 12; } } ``` diff --git a/book/src/ch03/regular_objects.md b/book/src/ch03/regular_objects.md index c5479c9..ddc661b 100644 --- a/book/src/ch03/regular_objects.md +++ b/book/src/ch03/regular_objects.md @@ -67,6 +67,32 @@ two object charblocks. However, in video modes 3, 4, and 5 the space for the background cuts into the lower charblock, so you can only safely use the upper charblock. +```rust +pub fn obj_tile_4bpp(tile_index: usize) -> Tile4bpp { + assert!(tile_index < 512); + let address = VRAM + size_of::() * 4 + 32 * tile_index; + unsafe { VolatilePtr(address as *mut Tile4bpp).read() } +} + +pub fn set_obj_tile_4bpp(tile_index: usize, tile: Tile4bpp) { + assert!(tile_index < 512); + let address = VRAM + size_of::() * 4 + 32 * tile_index; + unsafe { VolatilePtr(address as *mut Tile4bpp).write(tile) } +} + +pub fn obj_tile_8bpp(tile_index: usize) -> Tile8bpp { + assert!(tile_index < 512); + let address = VRAM + size_of::() * 4 + 32 * tile_index; + unsafe { VolatilePtr(address as *mut Tile8bpp).read() } +} + +pub fn set_obj_tile_8bpp(tile_index: usize, tile: Tile8bpp) { + assert!(tile_index < 512); + let address = VRAM + size_of::() * 4 + 32 * tile_index; + unsafe { VolatilePtr(address as *mut Tile8bpp).write(tile) } +} +``` + With backgrounds you picked every single tile individually with a bunch of screen entry values. Objects don't do that at all. Instead you pick a base tile, size, and shape, then it figures out the rest from there. However, you may diff --git a/book/src/ch03/tile_data.md b/book/src/ch03/tile_data.md index fd363ac..973c61c 100644 --- a/book/src/ch03/tile_data.md +++ b/book/src/ch03/tile_data.md @@ -26,13 +26,13 @@ Knowing this, we can write the following definitions: #[derive(Debug, Clone, Copy, Default)] #[repr(transparent)] pub struct Tile4bpp { - data: [u32; 8] + pub data: [u32; 8] } #[derive(Debug, Clone, Copy, Default)] #[repr(transparent)] pub struct Tile8bpp { - data: [u32; 16] + pub data: [u32; 16] } ``` @@ -65,13 +65,13 @@ tiles, and with 8bpp there's 256 tiles. So they'd be something like this: #[derive(Clone, Copy)] #[repr(transparent)] pub struct Charblock4bpp { - data: [Tile4bpp; 512], + pub data: [Tile4bpp; 512], } #[derive(Clone, Copy)] #[repr(transparent)] pub struct Charblock8bpp { - data: [Tile8bpp; 256], + pub data: [Tile8bpp; 256], } ``` diff --git a/examples/memory_game.rs b/examples/memory_game.rs index 97cf1e5..0a7c3e8 100644 --- a/examples/memory_game.rs +++ b/examples/memory_game.rs @@ -12,46 +12,14 @@ fn panic(_info: &core::panic::PanicInfo) -> ! { #[start] fn main(_argc: isize, _argv: *const *const u8) -> isize { unsafe { - DISPCNT.write(MODE3 | BG2); - } - - let mut px = SCREEN_WIDTH / 2; - let mut py = SCREEN_HEIGHT / 2; - let mut color = rgb16(31, 0, 0); - - loop { - // read the input for this frame - let this_frame_keys = key_input(); - - // adjust game state and wait for vblank - px += 2 * this_frame_keys.column_direction() as isize; - py += 2 * this_frame_keys.row_direction() as isize; - wait_until_vblank(); - - // draw the new game and wait until the next frame starts. - unsafe { - if px < 0 || py < 0 || px == SCREEN_WIDTH || py == SCREEN_HEIGHT { - // out of bounds, reset the screen and position. - mode3_clear_screen(0); - color = color.rotate_left(5); - px = SCREEN_WIDTH / 2; - py = SCREEN_HEIGHT / 2; - } else { - let color_here = mode3_read_pixel(px, py); - if color_here != 0 { - // crashed into our own line, reset the screen - mode3_clear_screen(0); - color = color.rotate_left(5); - } else { - // draw the new part of the line - mode3_draw_pixel(px, py, color); - mode3_draw_pixel(px, py + 1, color); - mode3_draw_pixel(px + 1, py, color); - mode3_draw_pixel(px + 1, py + 1, color); - } - } + init_palette(); + init_background(); + clear_objects_starting_with(13); + arrange_cards(); + init_selector(); + loop { + // TODO the game } - wait_until_vdraw(); } } @@ -180,25 +148,25 @@ pub fn wait_until_vdraw() { #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] #[repr(transparent)] pub struct Tile4bpp { - data: [u32; 8], + pub data: [u32; 8], } #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] #[repr(transparent)] pub struct Tile8bpp { - data: [u32; 16], + pub data: [u32; 16], } #[derive(Clone, Copy)] #[repr(transparent)] pub struct Charblock4bpp { - data: [Tile4bpp; 512], + pub data: [Tile4bpp; 512], } #[derive(Clone, Copy)] #[repr(transparent)] pub struct Charblock8bpp { - data: [Tile8bpp; 256], + pub data: [Tile8bpp; 256], } pub const PALRAM_BG_BASE: VolatilePtr = VolatilePtr(0x500_0000 as *mut u16); @@ -213,34 +181,87 @@ pub fn set_bg_palette(slot: usize, color: u16) { unsafe { PALRAM_BG_BASE.offset(slot as isize).write(color) } } -pub fn bg_tile_4pp(base_block: usize, tile_index: usize) -> Tile4bpp { +pub unsafe fn init_palette() { + // palbank 0: black/white/gray + set_bg_palette(2, rgb16(31, 31, 31)); + set_bg_palette(3, rgb16(15, 15, 15)); + // palbank 1 is reds + set_bg_palette(1 * 16 + 1, rgb16(31, 0, 0)); + set_bg_palette(1 * 16 + 2, rgb16(22, 0, 0)); + set_bg_palette(1 * 16 + 3, rgb16(10, 0, 0)); + // palbank 2 is greens + set_bg_palette(2 * 16 + 1, rgb16(0, 31, 0)); + set_bg_palette(2 * 16 + 2, rgb16(0, 22, 0)); + set_bg_palette(2 * 16 + 3, rgb16(0, 10, 0)); + // palbank 2 is blues + set_bg_palette(3 * 16 + 1, rgb16(0, 0, 31)); + set_bg_palette(3 * 16 + 2, rgb16(0, 0, 22)); + set_bg_palette(3 * 16 + 3, rgb16(0, 0, 10)); + + // Direct copy all BG selections into OBJ palette too + let mut bgp = PALRAM_BG_BASE; + let mut objp = PALRAM_OBJECT_BASE; + for _ in 0..(4 * 16) { + objp.write(bgp.read()); + bgp = bgp.offset(1); + objp = objp.offset(1); + } +} + +pub fn bg_tile_4bpp(base_block: usize, tile_index: usize) -> Tile4bpp { assert!(base_block < 4); assert!(tile_index < 512); let address = VRAM + size_of::() * base_block + size_of::() * tile_index; unsafe { VolatilePtr(address as *mut Tile4bpp).read() } } -pub fn set_bg_tile_4pp(base_block: usize, tile_index: usize, tile: Tile4bpp) { +pub fn set_bg_tile_4bpp(base_block: usize, tile_index: usize, tile: Tile4bpp) { assert!(base_block < 4); assert!(tile_index < 512); let address = VRAM + size_of::() * base_block + size_of::() * tile_index; unsafe { VolatilePtr(address as *mut Tile4bpp).write(tile) } } -pub fn bg_tile_8pp(base_block: usize, tile_index: usize) -> Tile8bpp { +pub fn bg_tile_8bpp(base_block: usize, tile_index: usize) -> Tile8bpp { assert!(base_block < 4); assert!(tile_index < 256); let address = VRAM + size_of::() * base_block + size_of::() * tile_index; unsafe { VolatilePtr(address as *mut Tile8bpp).read() } } -pub fn set_bg_tile_8pp(base_block: usize, tile_index: usize, tile: Tile8bpp) { +pub fn set_bg_tile_8bpp(base_block: usize, tile_index: usize, tile: Tile8bpp) { assert!(base_block < 4); assert!(tile_index < 256); let address = VRAM + size_of::() * base_block + size_of::() * tile_index; unsafe { VolatilePtr(address as *mut Tile8bpp).write(tile) } } +// + +pub fn obj_tile_4bpp(tile_index: usize) -> Tile4bpp { + assert!(tile_index < 512); + let address = VRAM + size_of::() * 4 + 32 * tile_index; + unsafe { VolatilePtr(address as *mut Tile4bpp).read() } +} + +pub fn set_obj_tile_4bpp(tile_index: usize, tile: Tile4bpp) { + assert!(tile_index < 512); + let address = VRAM + size_of::() * 4 + 32 * tile_index; + unsafe { VolatilePtr(address as *mut Tile4bpp).write(tile) } +} + +pub fn obj_tile_8bpp(tile_index: usize) -> Tile8bpp { + assert!(tile_index < 512); + let address = VRAM + size_of::() * 4 + 32 * tile_index; + unsafe { VolatilePtr(address as *mut Tile8bpp).read() } +} + +pub fn set_obj_tile_8bpp(tile_index: usize, tile: Tile8bpp) { + assert!(tile_index < 512); + let address = VRAM + size_of::() * 4 + 32 * tile_index; + unsafe { VolatilePtr(address as *mut Tile8bpp).write(tile) } +} + #[derive(Clone, Copy)] #[repr(transparent)] pub struct RegularScreenblock { @@ -284,7 +305,7 @@ impl RegularScreenblockEntry { } pub fn set_palbank_index(&mut self, palbank_index: u16) { self.0 &= 0b1111_1111_1111; - self.0 |= palbank_index; + self.0 |= palbank_index << 12; } } @@ -324,6 +345,69 @@ pub fn set_object_attributes(slot: usize, obj: ObjectAttributes) { } } +pub fn clear_objects_starting_with(base_slot: usize) { + let mut obj = ObjectAttributes::default(); + obj.set_rendering(ObjectRenderMode::Disabled); + for s in base_slot..128 { + set_object_attributes(s, obj); + } +} + +pub fn position_of_card(card_col: usize, card_row: usize) -> (u16, u16) { + (10 + card_col as u16 * 17, 5 + card_row as u16 * 15) +} + +pub fn arrange_cards() { + set_obj_tile_4bpp(1, FULL_ONE); + set_obj_tile_4bpp(2, FULL_TWO); + set_obj_tile_4bpp(3, FULL_THREE); + let mut obj = ObjectAttributes::default(); + obj.set_tile_index(2); // along with palbank0, this is a white card + for card_row in 0..3 { + for card_col in 0..4 { + let (col, row) = position_of_card(card_col, card_row); + obj.set_column(col); + obj.set_row(row); + set_object_attributes(1 + card_col as usize + (card_row as usize * 3), obj); + } + } +} + +pub fn init_selector() { + set_obj_tile_4bpp(0, CARD_SELECTOR); + let mut obj = ObjectAttributes::default(); + let (col, row) = position_of_card(0, 0); + obj.set_column(col); + obj.set_row(row); + set_object_attributes(0, obj); +} + +/// BG2 Control +pub const BG2CNT: VolatilePtr = VolatilePtr(0x400_000C as *mut u16); + +pub unsafe fn init_background() { + // put the bg tiles in charblock 0 + set_bg_tile_4bpp(0, 0, FULL_ONE); + set_bg_tile_4bpp(0, 1, FULL_THREE); + // make a checker pattern, place at screenblock 8 (aka the start of charblock 1) + let entry_black = RegularScreenblockEntry::default(); + let mut entry_gray = RegularScreenblockEntry::default(); + entry_gray.set_tile_id(1); + let mut using_black = true; + let mut screenblock: RegularScreenblock = core::mem::zeroed(); + for entry_mut in screenblock.data.iter_mut() { + *entry_mut = if using_black { entry_black } else { entry_gray }; + using_black = !using_black; + } + let p: VolatilePtr = VolatilePtr((VRAM + size_of::()) as *mut RegularScreenblock); + p.write(screenblock); + // turn on bg2 and configure it + let display_control_value = DISPCNT.read(); + DISPCNT.write(display_control_value | BG2); + const SCREEN_BASE_BLOCK_FIRST_BIT: u32 = 8; + BG2CNT.write(8 << SCREEN_BASE_BLOCK_FIRST_BIT); +} + #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] pub struct ObjectAttributes { attr0: u16, @@ -501,11 +585,11 @@ pub fn div_modulus(numerator: i32, denominator: i32) -> (i32, i32) { let mod_out: i32; unsafe { asm!(/* assembly template */ "swi 0x06" - :/* output operands */ "={r0}"(div_out), "={r1}"(mod_out) - :/* input operands */ "{r0}"(numerator), "{r1}"(denominator) - :/* clobbers */ "r3" - :/* options */ - ); + :/* output operands */ "={r0}"(div_out), "={r1}"(mod_out) + :/* input operands */ "{r0}"(numerator), "{r1}"(denominator) + :/* clobbers */ "r3" + :/* options */ + ); } (div_out, mod_out) } @@ -634,7 +718,7 @@ impl TimerControl { } /// Mucks with the settings of Timers 0 and 1. -fn u32_from_user_wait() -> u32 { +unsafe fn u32_from_user_wait() -> u32 { let mut t = TimerControl::default(); t.set_enabled(true); t.set_cascading(true); @@ -649,3 +733,60 @@ fn u32_from_user_wait() -> u32 { let high = TM1D.read() as u32; (high << 32) | low } + +/// For the user's "cursor" to select a card +#[rustfmt::skip] +pub const CARD_SELECTOR: Tile4bpp = Tile4bpp { + data : [ + 0x11100111, + 0x11000011, + 0x10000001, + 0x00000000, + 0x00000000, + 0x10000001, + 0x11000011, + 0x11100111 + ] +}; + +#[rustfmt::skip] +pub const FULL_ONE: Tile4bpp = Tile4bpp { + data : [ + 0x11111111, + 0x11111111, + 0x11111111, + 0x11111111, + 0x11111111, + 0x11111111, + 0x11111111, + 0x11111111, + ] +}; + +#[rustfmt::skip] +pub const FULL_TWO: Tile4bpp = Tile4bpp { + data : [ + 0x22222222, + 0x22222222, + 0x22222222, + 0x22222222, + 0x22222222, + 0x22222222, + 0x22222222, + 0x22222222 + ] +}; + +#[rustfmt::skip] +pub const FULL_THREE: Tile4bpp = Tile4bpp { + data : [ + 0x33333333, + 0x33333333, + 0x33333333, + 0x33333333, + 0x33333333, + 0x33333333, + 0x33333333, + 0x33333333 + ] +}; diff --git a/src/core_extras.rs b/src/core_extras.rs index aaf6197..fe82440 100644 --- a/src/core_extras.rs +++ b/src/core_extras.rs @@ -5,9 +5,6 @@ /// Read and Write are made to be volatile. Offset is made to be /// wrapping_offset. This makes it much easier to correctly work with IO /// Registers and all display related memory on the GBA. -/// -/// As a bonus, use of this type is mostly `cargo test` safe. Reads will return -/// a `zeroed()` value instead, and writes will do nothing. #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] #[repr(transparent)] pub struct VolatilePtr(pub *mut T); @@ -20,47 +17,23 @@ impl core::fmt::Pointer for VolatilePtr { } impl VolatilePtr { - /// Performs a volatile read. - /// - /// # Safety - /// - /// This method adds absolutely no additional safety, so all safety concerns - /// for a normal raw pointer volatile read apply. + /// Performs a `read_volatile`. pub unsafe fn read(&self) -> T { - #[cfg(not(test))] - { - core::ptr::read_volatile(self.0) - } - #[cfg(test)] - { - core::mem::zeroed::() - } + self.0.read_volatile() } - /// Performs a volatile write. - /// - /// # Safety - /// - /// This method adds absolutely no additional safety, so all safety concerns - /// for a normal raw pointer volatile write apply. + /// Performs a `write_volatile`. pub unsafe fn write(&self, data: T) { - #[cfg(not(test))] - { - core::ptr::write_volatile(self.0, data); - } - #[cfg(test)] - { - drop(data) - } + self.0.write_volatile(data); } - /// Performs a wrapping_offset by the number of slots given to a new position. - /// - /// # Safety - /// - /// This is a wrapping_offset, so all safety concerns of a normal raw pointer - /// wrapping_offset apply. - pub unsafe fn offset(self, count: isize) -> Self { + /// Performs a `wrapping_offset`. + pub fn offset(self, count: isize) -> Self { VolatilePtr(self.0.wrapping_offset(count)) } + + /// Performs a cast into some new pointer type. + pub fn cast(self) -> VolatilePtr { + VolatilePtr(self.0 as *mut Z) + } }