more writing, but the demo just shows white

This commit is contained in:
Lokathor 2018-12-04 23:27:35 -07:00
parent b40ee565d0
commit e9b62f1832
7 changed files with 453 additions and 103 deletions

View file

@ -297,7 +297,7 @@ do this as our first thing at startup, but we might show the title and like a
```rust ```rust
/// Mucks with the settings of Timers 0 and 1. /// 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(); let mut t = TimerControl::default();
t.set_enabled(true); t.set_enabled(true);
t.set_cascading(true); t.set_cascading(true);

View file

@ -1,2 +1,212 @@
# memory_game # 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 TODO

View file

@ -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: the size of that type of tile. Like this:
```rust ```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!(base_block < 4);
assert!(tile_index < 512); assert!(tile_index < 512);
let address = VRAM + size_of::<Charblock4bpp>() * base_block + size_of::<Tile4bpp>() * tile_index; let address = VRAM + size_of::<Charblock4bpp>() * base_block + size_of::<Tile4bpp>() * tile_index;
unsafe { VolatilePtr(address as *mut Tile4bpp).read() } 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!(base_block < 4);
assert!(tile_index < 512); assert!(tile_index < 512);
let address = VRAM + size_of::<Charblock4bpp>() * base_block + size_of::<Tile4bpp>() * tile_index; let address = VRAM + size_of::<Charblock4bpp>() * base_block + size_of::<Tile4bpp>() * tile_index;
unsafe { VolatilePtr(address as *mut Tile4bpp).write(tile) } 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!(base_block < 4);
assert!(tile_index < 256); assert!(tile_index < 256);
let address = VRAM + size_of::<Charblock8bpp>() * base_block + size_of::<Tile8bpp>() * tile_index; let address = VRAM + size_of::<Charblock8bpp>() * base_block + size_of::<Tile8bpp>() * tile_index;
unsafe { VolatilePtr(address as *mut Tile8bpp).read() } 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!(base_block < 4);
assert!(tile_index < 256); assert!(tile_index < 256);
let address = VRAM + size_of::<Charblock8bpp>() * base_block + size_of::<Tile8bpp>() * tile_index; let address = VRAM + size_of::<Charblock8bpp>() * base_block + size_of::<Tile8bpp>() * tile_index;
@ -154,7 +154,7 @@ appropriately.
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
#[repr(transparent)] #[repr(transparent)]
pub struct RegularScreenblock { pub struct RegularScreenblock {
data: [RegularScreenblockEntry; 32 * 32], pub data: [RegularScreenblockEntry; 32 * 32],
} }
#[derive(Debug, Clone, Copy, Default)] #[derive(Debug, Clone, Copy, Default)]
@ -207,7 +207,7 @@ impl RegularScreenblockEntry {
} }
pub fn set_palbank_index(&mut self, palbank_index: u16) { pub fn set_palbank_index(&mut self, palbank_index: u16) {
self.0 &= 0b1111_1111_1111; self.0 &= 0b1111_1111_1111;
self.0 |= palbank_index; self.0 |= palbank_index << 12;
} }
} }
``` ```

View file

@ -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 background cuts into the lower charblock, so you can only safely use the upper
charblock. charblock.
```rust
pub fn obj_tile_4bpp(tile_index: usize) -> Tile4bpp {
assert!(tile_index < 512);
let address = VRAM + size_of::<Charblock4bpp>() * 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::<Charblock4bpp>() * 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::<Charblock8bpp>() * 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::<Charblock8bpp>() * 4 + 32 * tile_index;
unsafe { VolatilePtr(address as *mut Tile8bpp).write(tile) }
}
```
With backgrounds you picked every single tile individually with a bunch of 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, 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 size, and shape, then it figures out the rest from there. However, you may

View file

@ -26,13 +26,13 @@ Knowing this, we can write the following definitions:
#[derive(Debug, Clone, Copy, Default)] #[derive(Debug, Clone, Copy, Default)]
#[repr(transparent)] #[repr(transparent)]
pub struct Tile4bpp { pub struct Tile4bpp {
data: [u32; 8] pub data: [u32; 8]
} }
#[derive(Debug, Clone, Copy, Default)] #[derive(Debug, Clone, Copy, Default)]
#[repr(transparent)] #[repr(transparent)]
pub struct Tile8bpp { 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)] #[derive(Clone, Copy)]
#[repr(transparent)] #[repr(transparent)]
pub struct Charblock4bpp { pub struct Charblock4bpp {
data: [Tile4bpp; 512], pub data: [Tile4bpp; 512],
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
#[repr(transparent)] #[repr(transparent)]
pub struct Charblock8bpp { pub struct Charblock8bpp {
data: [Tile8bpp; 256], pub data: [Tile8bpp; 256],
} }
``` ```

View file

@ -12,46 +12,14 @@ fn panic(_info: &core::panic::PanicInfo) -> ! {
#[start] #[start]
fn main(_argc: isize, _argv: *const *const u8) -> isize { fn main(_argc: isize, _argv: *const *const u8) -> isize {
unsafe { unsafe {
DISPCNT.write(MODE3 | BG2); init_palette();
} init_background();
clear_objects_starting_with(13);
let mut px = SCREEN_WIDTH / 2; arrange_cards();
let mut py = SCREEN_HEIGHT / 2; init_selector();
let mut color = rgb16(31, 0, 0); loop {
// TODO the game
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);
}
}
} }
wait_until_vdraw();
} }
} }
@ -180,25 +148,25 @@ pub fn wait_until_vdraw() {
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
#[repr(transparent)] #[repr(transparent)]
pub struct Tile4bpp { pub struct Tile4bpp {
data: [u32; 8], pub data: [u32; 8],
} }
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
#[repr(transparent)] #[repr(transparent)]
pub struct Tile8bpp { pub struct Tile8bpp {
data: [u32; 16], pub data: [u32; 16],
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
#[repr(transparent)] #[repr(transparent)]
pub struct Charblock4bpp { pub struct Charblock4bpp {
data: [Tile4bpp; 512], pub data: [Tile4bpp; 512],
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
#[repr(transparent)] #[repr(transparent)]
pub struct Charblock8bpp { pub struct Charblock8bpp {
data: [Tile8bpp; 256], pub data: [Tile8bpp; 256],
} }
pub const PALRAM_BG_BASE: VolatilePtr<u16> = VolatilePtr(0x500_0000 as *mut u16); pub const PALRAM_BG_BASE: VolatilePtr<u16> = 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) } 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!(base_block < 4);
assert!(tile_index < 512); assert!(tile_index < 512);
let address = VRAM + size_of::<Charblock4bpp>() * base_block + size_of::<Tile4bpp>() * tile_index; let address = VRAM + size_of::<Charblock4bpp>() * base_block + size_of::<Tile4bpp>() * tile_index;
unsafe { VolatilePtr(address as *mut Tile4bpp).read() } 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!(base_block < 4);
assert!(tile_index < 512); assert!(tile_index < 512);
let address = VRAM + size_of::<Charblock4bpp>() * base_block + size_of::<Tile4bpp>() * tile_index; let address = VRAM + size_of::<Charblock4bpp>() * base_block + size_of::<Tile4bpp>() * tile_index;
unsafe { VolatilePtr(address as *mut Tile4bpp).write(tile) } 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!(base_block < 4);
assert!(tile_index < 256); assert!(tile_index < 256);
let address = VRAM + size_of::<Charblock8bpp>() * base_block + size_of::<Tile8bpp>() * tile_index; let address = VRAM + size_of::<Charblock8bpp>() * base_block + size_of::<Tile8bpp>() * tile_index;
unsafe { VolatilePtr(address as *mut Tile8bpp).read() } 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!(base_block < 4);
assert!(tile_index < 256); assert!(tile_index < 256);
let address = VRAM + size_of::<Charblock8bpp>() * base_block + size_of::<Tile8bpp>() * tile_index; let address = VRAM + size_of::<Charblock8bpp>() * base_block + size_of::<Tile8bpp>() * tile_index;
unsafe { VolatilePtr(address as *mut Tile8bpp).write(tile) } 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::<Charblock4bpp>() * 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::<Charblock4bpp>() * 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::<Charblock8bpp>() * 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::<Charblock8bpp>() * 4 + 32 * tile_index;
unsafe { VolatilePtr(address as *mut Tile8bpp).write(tile) }
}
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
#[repr(transparent)] #[repr(transparent)]
pub struct RegularScreenblock { pub struct RegularScreenblock {
@ -284,7 +305,7 @@ impl RegularScreenblockEntry {
} }
pub fn set_palbank_index(&mut self, palbank_index: u16) { pub fn set_palbank_index(&mut self, palbank_index: u16) {
self.0 &= 0b1111_1111_1111; 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<u16> = 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<RegularScreenblock> = VolatilePtr((VRAM + size_of::<Charblock8bpp>()) 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)] #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct ObjectAttributes { pub struct ObjectAttributes {
attr0: u16, attr0: u16,
@ -501,11 +585,11 @@ pub fn div_modulus(numerator: i32, denominator: i32) -> (i32, i32) {
let mod_out: i32; let mod_out: i32;
unsafe { unsafe {
asm!(/* assembly template */ "swi 0x06" asm!(/* assembly template */ "swi 0x06"
:/* output operands */ "={r0}"(div_out), "={r1}"(mod_out) :/* output operands */ "={r0}"(div_out), "={r1}"(mod_out)
:/* input operands */ "{r0}"(numerator), "{r1}"(denominator) :/* input operands */ "{r0}"(numerator), "{r1}"(denominator)
:/* clobbers */ "r3" :/* clobbers */ "r3"
:/* options */ :/* options */
); );
} }
(div_out, mod_out) (div_out, mod_out)
} }
@ -634,7 +718,7 @@ impl TimerControl {
} }
/// Mucks with the settings of Timers 0 and 1. /// 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(); let mut t = TimerControl::default();
t.set_enabled(true); t.set_enabled(true);
t.set_cascading(true); t.set_cascading(true);
@ -649,3 +733,60 @@ fn u32_from_user_wait() -> u32 {
let high = TM1D.read() as u32; let high = TM1D.read() as u32;
(high << 32) | low (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
]
};

View file

@ -5,9 +5,6 @@
/// Read and Write are made to be volatile. Offset is made to be /// 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 /// wrapping_offset. This makes it much easier to correctly work with IO
/// Registers and all display related memory on the GBA. /// 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)] #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)] #[repr(transparent)]
pub struct VolatilePtr<T>(pub *mut T); pub struct VolatilePtr<T>(pub *mut T);
@ -20,47 +17,23 @@ impl<T> core::fmt::Pointer for VolatilePtr<T> {
} }
impl<T> VolatilePtr<T> { impl<T> VolatilePtr<T> {
/// Performs a volatile read. /// Performs a `read_volatile`.
///
/// # Safety
///
/// This method adds absolutely no additional safety, so all safety concerns
/// for a normal raw pointer volatile read apply.
pub unsafe fn read(&self) -> T { pub unsafe fn read(&self) -> T {
#[cfg(not(test))] self.0.read_volatile()
{
core::ptr::read_volatile(self.0)
}
#[cfg(test)]
{
core::mem::zeroed::<T>()
}
} }
/// Performs a volatile write. /// Performs a `write_volatile`.
///
/// # Safety
///
/// This method adds absolutely no additional safety, so all safety concerns
/// for a normal raw pointer volatile write apply.
pub unsafe fn write(&self, data: T) { pub unsafe fn write(&self, data: T) {
#[cfg(not(test))] self.0.write_volatile(data);
{
core::ptr::write_volatile(self.0, data);
}
#[cfg(test)]
{
drop(data)
}
} }
/// Performs a wrapping_offset by the number of slots given to a new position. /// Performs a `wrapping_offset`.
/// pub fn offset(self, count: isize) -> Self {
/// # 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 {
VolatilePtr(self.0.wrapping_offset(count)) VolatilePtr(self.0.wrapping_offset(count))
} }
/// Performs a cast into some new pointer type.
pub fn cast<Z>(self) -> VolatilePtr<Z> {
VolatilePtr(self.0 as *mut Z)
}
} }