From 002ccc64dde831de3cebabebbe4657355543256c Mon Sep 17 00:00:00 2001 From: Corwin Date: Sat, 30 Jul 2022 16:39:14 +0100 Subject: [PATCH 1/8] implement allocator api so we can allocate to (ew/iw)ram --- agb/examples/allocation.rs | 7 ++++++- agb/src/agb_alloc/block_allocator.rs | 24 +++++++++++++++++++++--- agb/src/agb_alloc/mod.rs | 21 +++++++++++++++++++++ agb/src/lib.rs | 3 ++- 4 files changed, 50 insertions(+), 5 deletions(-) diff --git a/agb/examples/allocation.rs b/agb/examples/allocation.rs index 64f0ddd3..a9e9b80d 100644 --- a/agb/examples/allocation.rs +++ b/agb/examples/allocation.rs @@ -1,5 +1,6 @@ #![no_std] #![no_main] +#![feature(allocator_api)] extern crate alloc; @@ -8,7 +9,11 @@ use alloc::boxed::Box; #[agb::entry] fn main(_gba: agb::Gba) -> ! { loop { + let a = Box::new_in(1, agb::agb_alloc::EWRAM_ALLOC); let b = Box::new(1); - agb::println!("dynamic allocation made to {:?}", &*b as *const _); + let c = Box::new_in(3, agb::agb_alloc::IWRAM_ALLOC); + agb::println!("ewram allocation made to {:?}", &*a as *const _); + agb::println!("global allocation made to {:?}", &*b as *const _); + agb::println!("iwram allocation made to {:?}", &*c as *const _); } } diff --git a/agb/src/agb_alloc/block_allocator.rs b/agb/src/agb_alloc/block_allocator.rs index a601131e..5e7b45f8 100644 --- a/agb/src/agb_alloc/block_allocator.rs +++ b/agb/src/agb_alloc/block_allocator.rs @@ -3,7 +3,7 @@ //! the linked list in order of pointer. Blocks are then merged after every //! free. -use core::alloc::{GlobalAlloc, Layout}; +use core::alloc::{Allocator, GlobalAlloc, Layout}; use core::cell::RefCell; use core::convert::TryInto; @@ -43,13 +43,13 @@ struct BlockAllocatorState { first_free_block: Option>, } -pub(crate) struct BlockAllocator { +pub struct BlockAllocator { inner_allocator: BumpAllocator, state: Mutex>, } impl BlockAllocator { - pub const unsafe fn new(start: StartEnd) -> Self { + pub(crate) const unsafe fn new(start: StartEnd) -> Self { Self { inner_allocator: BumpAllocator::new(start), state: Mutex::new(RefCell::new(BlockAllocatorState { @@ -216,3 +216,21 @@ unsafe impl GlobalAlloc for BlockAllocator { self.dealloc(ptr, layout); } } + +unsafe impl Allocator for BlockAllocator { + fn allocate(&self, layout: Layout) -> Result, core::alloc::AllocError> { + match unsafe { self.alloc(layout) } { + None => Err(core::alloc::AllocError), + Some(p) => Ok(unsafe { + NonNull::new_unchecked(core::ptr::slice_from_raw_parts_mut( + p.as_ptr(), + layout.size(), + )) + }), + } + } + + unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { + self.dealloc(ptr.as_ptr(), layout); + } +} diff --git a/agb/src/agb_alloc/mod.rs b/agb/src/agb_alloc/mod.rs index ecffeb72..28d40bda 100644 --- a/agb/src/agb_alloc/mod.rs +++ b/agb/src/agb_alloc/mod.rs @@ -33,6 +33,7 @@ impl DerefMut for SendNonNull { } const EWRAM_END: usize = 0x0204_0000; +const IWRAM_END: usize = 0x0300_8000; #[global_allocator] static GLOBAL_ALLOC: BlockAllocator = unsafe { @@ -42,7 +43,17 @@ static GLOBAL_ALLOC: BlockAllocator = unsafe { }) }; +pub static EWRAM_ALLOC: &BlockAllocator = &GLOBAL_ALLOC; +pub static IWRAM_ALLOC: &BlockAllocator = &__IWRAM_ALLOC; +static __IWRAM_ALLOC: BlockAllocator = unsafe { + BlockAllocator::new(StartEnd { + start: iwram_data_end, + end: || IWRAM_END, + }) +}; + #[cfg(any(test, feature = "testing"))] +#[cfg(test)] pub unsafe fn number_of_blocks() -> u32 { GLOBAL_ALLOC.number_of_blocks() } @@ -56,6 +67,16 @@ fn alloc_error(layout: Layout) -> ! { ); } +fn iwram_data_end() -> usize { + extern "C" { + static __iwram_data_end: usize; + } + + // TODO: This seems completely wrong, but without the &, rust generates + // a double dereference :/. Maybe a bug in nightly? + (unsafe { &__iwram_data_end }) as *const _ as usize +} + fn data_end() -> usize { extern "C" { static __ewram_data_end: usize; diff --git a/agb/src/lib.rs b/agb/src/lib.rs index 16fb1044..7b6448c5 100644 --- a/agb/src/lib.rs +++ b/agb/src/lib.rs @@ -11,6 +11,7 @@ reexport_test_harness_main = "test_main" )] #![feature(alloc_error_handler)] +#![feature(allocator_api)] #![warn(clippy::all)] #![deny(clippy::must_use_candidate)] #![deny(clippy::trivially_copy_pass_by_ref)] @@ -161,7 +162,7 @@ pub use agb_macros::entry; pub use agb_sound_converter::include_wav; extern crate alloc; -mod agb_alloc; +pub mod agb_alloc; mod agbabi; mod bitarray; From 433fad15a1122ddf4d06e982b31f00d0d0e79b20 Mon Sep 17 00:00:00 2001 From: Corwin Date: Sat, 30 Jul 2022 16:52:41 +0100 Subject: [PATCH 2/8] mark function as pub crate so it's not used outside agb --- agb/src/agb_alloc/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agb/src/agb_alloc/mod.rs b/agb/src/agb_alloc/mod.rs index 28d40bda..741154d2 100644 --- a/agb/src/agb_alloc/mod.rs +++ b/agb/src/agb_alloc/mod.rs @@ -54,7 +54,7 @@ static __IWRAM_ALLOC: BlockAllocator = unsafe { #[cfg(any(test, feature = "testing"))] #[cfg(test)] -pub unsafe fn number_of_blocks() -> u32 { +pub(crate) unsafe fn number_of_blocks() -> u32 { GLOBAL_ALLOC.number_of_blocks() } From a3de583d40e369b6d3fadda77cb71a2ec56a141e Mon Sep 17 00:00:00 2001 From: Corwin Date: Sat, 30 Jul 2022 17:07:18 +0100 Subject: [PATCH 3/8] fix --- agb/src/agb_alloc/mod.rs | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/agb/src/agb_alloc/mod.rs b/agb/src/agb_alloc/mod.rs index 741154d2..310096ea 100644 --- a/agb/src/agb_alloc/mod.rs +++ b/agb/src/agb_alloc/mod.rs @@ -53,7 +53,6 @@ static __IWRAM_ALLOC: BlockAllocator = unsafe { }; #[cfg(any(test, feature = "testing"))] -#[cfg(test)] pub(crate) unsafe fn number_of_blocks() -> u32 { GLOBAL_ALLOC.number_of_blocks() } @@ -74,7 +73,7 @@ fn iwram_data_end() -> usize { // TODO: This seems completely wrong, but without the &, rust generates // a double dereference :/. Maybe a bug in nightly? - (unsafe { &__iwram_data_end }) as *const _ as usize + (unsafe { &__iwram_data_end }) as *const _ as usize + 0x200 } fn data_end() -> usize { @@ -171,4 +170,28 @@ mod test { "data end should be smaller than 0x0203_0000" ); } + + #[test_case] + fn should_return_data_end_somewhere_in_iwram(_gba: &mut crate::Gba) { + let data_end = iwram_data_end(); + + assert!( + (0x0300_0000..0x0300_8000).contains(&data_end), + "iwram data end should be in iwram, instead was {}", + data_end + ); + crate::println!("data end was {:#010X}", data_end); + } + + #[test_case] + fn allocate_to_iwram_works(_gba: &mut crate::Gba) { + let a = Box::new_in(1, IWRAM_ALLOC); + let p = &*a as *const i32; + let addr = p as usize; + assert!( + (0x0300_0000..0x0300_8000).contains(&addr), + "address of allocation should be within iwram, instead at {:?}", + p + ); + } } From cd000ca76727ab67274368a9c2e4aad8f3d28469 Mon Sep 17 00:00:00 2001 From: Corwin Date: Sat, 30 Jul 2022 17:21:46 +0100 Subject: [PATCH 4/8] use the end of all assigned iwram data --- agb/gba.ld | 1 + agb/gba_mb.ld | 1 + agb/src/agb_alloc/mod.rs | 4 ++-- book/games/pong/gba.ld | 1 + book/games/pong/gba_mb.ld | 1 + examples/hyperspace-roll/gba.ld | 1 + examples/hyperspace-roll/gba_mb.ld | 1 + examples/the-hat-chooses-the-wizard/gba.ld | 1 + examples/the-purple-night/gba.ld | 1 + examples/the-purple-night/gba_mb.ld | 1 + template/gba.ld | 1 + template/gba_mb.ld | 1 + 12 files changed, 13 insertions(+), 2 deletions(-) diff --git a/agb/gba.ld b/agb/gba.ld index cd74016a..5665df24 100644 --- a/agb/gba.ld +++ b/agb/gba.ld @@ -65,6 +65,7 @@ SECTIONS { .bss : { *(.bss .bss.*); . = ALIGN(4); + __iwram_end = ABSOLUTE(.); } > iwram __iwram_rom_length_bytes = __iwram_data_end - __iwram_data_start; diff --git a/agb/gba_mb.ld b/agb/gba_mb.ld index 238b5c4c..1547960f 100644 --- a/agb/gba_mb.ld +++ b/agb/gba_mb.ld @@ -63,6 +63,7 @@ SECTIONS { .bss : { *(.bss .bss.*); . = ALIGN(4); + __iwram_end = ABSOLUTE(.); } > iwram __iwram_rom_length_bytes = __iwram_data_end - __iwram_data_start; diff --git a/agb/src/agb_alloc/mod.rs b/agb/src/agb_alloc/mod.rs index 310096ea..763b9705 100644 --- a/agb/src/agb_alloc/mod.rs +++ b/agb/src/agb_alloc/mod.rs @@ -68,12 +68,12 @@ fn alloc_error(layout: Layout) -> ! { fn iwram_data_end() -> usize { extern "C" { - static __iwram_data_end: usize; + static __iwram_end: usize; } // TODO: This seems completely wrong, but without the &, rust generates // a double dereference :/. Maybe a bug in nightly? - (unsafe { &__iwram_data_end }) as *const _ as usize + 0x200 + (unsafe { &__iwram_end }) as *const _ as usize } fn data_end() -> usize { diff --git a/book/games/pong/gba.ld b/book/games/pong/gba.ld index cd74016a..5665df24 100644 --- a/book/games/pong/gba.ld +++ b/book/games/pong/gba.ld @@ -65,6 +65,7 @@ SECTIONS { .bss : { *(.bss .bss.*); . = ALIGN(4); + __iwram_end = ABSOLUTE(.); } > iwram __iwram_rom_length_bytes = __iwram_data_end - __iwram_data_start; diff --git a/book/games/pong/gba_mb.ld b/book/games/pong/gba_mb.ld index 238b5c4c..1547960f 100644 --- a/book/games/pong/gba_mb.ld +++ b/book/games/pong/gba_mb.ld @@ -63,6 +63,7 @@ SECTIONS { .bss : { *(.bss .bss.*); . = ALIGN(4); + __iwram_end = ABSOLUTE(.); } > iwram __iwram_rom_length_bytes = __iwram_data_end - __iwram_data_start; diff --git a/examples/hyperspace-roll/gba.ld b/examples/hyperspace-roll/gba.ld index cd74016a..5665df24 100644 --- a/examples/hyperspace-roll/gba.ld +++ b/examples/hyperspace-roll/gba.ld @@ -65,6 +65,7 @@ SECTIONS { .bss : { *(.bss .bss.*); . = ALIGN(4); + __iwram_end = ABSOLUTE(.); } > iwram __iwram_rom_length_bytes = __iwram_data_end - __iwram_data_start; diff --git a/examples/hyperspace-roll/gba_mb.ld b/examples/hyperspace-roll/gba_mb.ld index 238b5c4c..1547960f 100644 --- a/examples/hyperspace-roll/gba_mb.ld +++ b/examples/hyperspace-roll/gba_mb.ld @@ -63,6 +63,7 @@ SECTIONS { .bss : { *(.bss .bss.*); . = ALIGN(4); + __iwram_end = ABSOLUTE(.); } > iwram __iwram_rom_length_bytes = __iwram_data_end - __iwram_data_start; diff --git a/examples/the-hat-chooses-the-wizard/gba.ld b/examples/the-hat-chooses-the-wizard/gba.ld index cd74016a..5665df24 100644 --- a/examples/the-hat-chooses-the-wizard/gba.ld +++ b/examples/the-hat-chooses-the-wizard/gba.ld @@ -65,6 +65,7 @@ SECTIONS { .bss : { *(.bss .bss.*); . = ALIGN(4); + __iwram_end = ABSOLUTE(.); } > iwram __iwram_rom_length_bytes = __iwram_data_end - __iwram_data_start; diff --git a/examples/the-purple-night/gba.ld b/examples/the-purple-night/gba.ld index cd74016a..5665df24 100644 --- a/examples/the-purple-night/gba.ld +++ b/examples/the-purple-night/gba.ld @@ -65,6 +65,7 @@ SECTIONS { .bss : { *(.bss .bss.*); . = ALIGN(4); + __iwram_end = ABSOLUTE(.); } > iwram __iwram_rom_length_bytes = __iwram_data_end - __iwram_data_start; diff --git a/examples/the-purple-night/gba_mb.ld b/examples/the-purple-night/gba_mb.ld index 238b5c4c..1547960f 100644 --- a/examples/the-purple-night/gba_mb.ld +++ b/examples/the-purple-night/gba_mb.ld @@ -63,6 +63,7 @@ SECTIONS { .bss : { *(.bss .bss.*); . = ALIGN(4); + __iwram_end = ABSOLUTE(.); } > iwram __iwram_rom_length_bytes = __iwram_data_end - __iwram_data_start; diff --git a/template/gba.ld b/template/gba.ld index cd74016a..5665df24 100644 --- a/template/gba.ld +++ b/template/gba.ld @@ -65,6 +65,7 @@ SECTIONS { .bss : { *(.bss .bss.*); . = ALIGN(4); + __iwram_end = ABSOLUTE(.); } > iwram __iwram_rom_length_bytes = __iwram_data_end - __iwram_data_start; diff --git a/template/gba_mb.ld b/template/gba_mb.ld index 238b5c4c..1547960f 100644 --- a/template/gba_mb.ld +++ b/template/gba_mb.ld @@ -63,6 +63,7 @@ SECTIONS { .bss : { *(.bss .bss.*); . = ALIGN(4); + __iwram_end = ABSOLUTE(.); } > iwram __iwram_rom_length_bytes = __iwram_data_end - __iwram_data_start; From feea7d2af13eec23ea68697236cbc2ca825b2ecd Mon Sep 17 00:00:00 2001 From: Corwin Date: Sat, 30 Jul 2022 19:34:30 +0100 Subject: [PATCH 5/8] privatise agb_alloc crate --- agb/examples/allocation.rs | 4 ++-- agb/src/lib.rs | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/agb/examples/allocation.rs b/agb/examples/allocation.rs index a9e9b80d..b1b9a8b8 100644 --- a/agb/examples/allocation.rs +++ b/agb/examples/allocation.rs @@ -9,9 +9,9 @@ use alloc::boxed::Box; #[agb::entry] fn main(_gba: agb::Gba) -> ! { loop { - let a = Box::new_in(1, agb::agb_alloc::EWRAM_ALLOC); + let a = Box::new_in(1, agb::EWRAM_ALLOC); let b = Box::new(1); - let c = Box::new_in(3, agb::agb_alloc::IWRAM_ALLOC); + let c = Box::new_in(3, agb::IWRAM_ALLOC); agb::println!("ewram allocation made to {:?}", &*a as *const _); agb::println!("global allocation made to {:?}", &*b as *const _); agb::println!("iwram allocation made to {:?}", &*c as *const _); diff --git a/agb/src/lib.rs b/agb/src/lib.rs index 7b6448c5..31657fd9 100644 --- a/agb/src/lib.rs +++ b/agb/src/lib.rs @@ -162,7 +162,7 @@ pub use agb_macros::entry; pub use agb_sound_converter::include_wav; extern crate alloc; -pub mod agb_alloc; +mod agb_alloc; mod agbabi; mod bitarray; @@ -190,6 +190,8 @@ pub mod syscall; /// Interactions with the internal timers pub mod timer; +pub use {agb_alloc::EWRAM_ALLOC, agb_alloc::IWRAM_ALLOC}; + #[cfg(not(any(test, feature = "testing")))] #[panic_handler] #[allow(unused_must_use)] From 7f760d440412660d1590ff6716de102a3b593fca Mon Sep 17 00:00:00 2001 From: Corwin Date: Sat, 30 Jul 2022 19:34:40 +0100 Subject: [PATCH 6/8] add some doc comments to explain the allocators --- agb/src/agb_alloc/mod.rs | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/agb/src/agb_alloc/mod.rs b/agb/src/agb_alloc/mod.rs index 763b9705..87345c55 100644 --- a/agb/src/agb_alloc/mod.rs +++ b/agb/src/agb_alloc/mod.rs @@ -43,7 +43,47 @@ static GLOBAL_ALLOC: BlockAllocator = unsafe { }) }; +/// This is the allocator for the External Working Ram. This is currently +/// equivalent to the Global Allocator (where things are allocated if no allocator is provided). This implements the allocator trait, so +/// is meant to be used in specifying where certain structures should be +/// allocated. +/// +/// ```rust,no_run +/// # #![no_std] +/// # #![no_main] +/// # use crate::EWRAM_ALLOC; +/// # extern crate alloc; +/// # use alloc::vec::Vec; +/// # fn foo(gba: &mut agb::Gba) { +/// let mut v = Vec::new_in(EWRAM_ALLOC); +/// v.push("hello, world"); +/// assert!( +/// (0x0200_0000..0x0204_0000).contains(&(v.as_ptr() as usize)), +/// "the address of the vector is inside ewram" +/// ); +/// # } +/// ``` pub static EWRAM_ALLOC: &BlockAllocator = &GLOBAL_ALLOC; + +/// This is the allocator for the Internal Working Ram. This implements the +/// allocator trait, so is meant to be used in specifying where certain +/// structures should be allocated. +/// +/// ```rust,no_run +/// # #![no_std] +/// # #![no_main] +/// # use crate::IWRAM_ALLOC; +/// # extern crate alloc; +/// # use alloc::vec::Vec; +/// # fn foo(gba: &mut agb::Gba) { +/// let mut v = Vec::new_in(IWRAM_ALLOC); +/// v.push("hello, world"); +/// assert!( +/// (0x0300_0000..0x0200_8000).contains(&(v.as_ptr() as usize)), +/// "the address of the vector is inside iwram" +/// ); +/// # } +/// ``` pub static IWRAM_ALLOC: &BlockAllocator = &__IWRAM_ALLOC; static __IWRAM_ALLOC: BlockAllocator = unsafe { BlockAllocator::new(StartEnd { From 0c25eb6beef0bc43a4cf5caab3e0a2dd0c0728e8 Mon Sep 17 00:00:00 2001 From: Corwin Date: Sat, 30 Jul 2022 19:47:34 +0100 Subject: [PATCH 7/8] fix doc examples --- agb/src/agb_alloc/mod.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/agb/src/agb_alloc/mod.rs b/agb/src/agb_alloc/mod.rs index 87345c55..83a3c1b8 100644 --- a/agb/src/agb_alloc/mod.rs +++ b/agb/src/agb_alloc/mod.rs @@ -49,9 +49,10 @@ static GLOBAL_ALLOC: BlockAllocator = unsafe { /// allocated. /// /// ```rust,no_run +/// #![feature(allocator_api)] /// # #![no_std] /// # #![no_main] -/// # use crate::EWRAM_ALLOC; +/// # use agb::EWRAM_ALLOC; /// # extern crate alloc; /// # use alloc::vec::Vec; /// # fn foo(gba: &mut agb::Gba) { @@ -70,9 +71,10 @@ pub static EWRAM_ALLOC: &BlockAllocator = &GLOBAL_ALLOC; /// structures should be allocated. /// /// ```rust,no_run +/// #![feature(allocator_api)] /// # #![no_std] /// # #![no_main] -/// # use crate::IWRAM_ALLOC; +/// # use agb::IWRAM_ALLOC; /// # extern crate alloc; /// # use alloc::vec::Vec; /// # fn foo(gba: &mut agb::Gba) { From b39b6f43b3e960751a277ee5bab9a6b294aa93ea Mon Sep 17 00:00:00 2001 From: Corwin Date: Sat, 30 Jul 2022 22:49:26 +0100 Subject: [PATCH 8/8] correct end address of range --- agb/src/agb_alloc/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agb/src/agb_alloc/mod.rs b/agb/src/agb_alloc/mod.rs index 83a3c1b8..22230650 100644 --- a/agb/src/agb_alloc/mod.rs +++ b/agb/src/agb_alloc/mod.rs @@ -81,7 +81,7 @@ pub static EWRAM_ALLOC: &BlockAllocator = &GLOBAL_ALLOC; /// let mut v = Vec::new_in(IWRAM_ALLOC); /// v.push("hello, world"); /// assert!( -/// (0x0300_0000..0x0200_8000).contains(&(v.as_ptr() as usize)), +/// (0x0300_0000..0x0300_8000).contains(&(v.as_ptr() as usize)), /// "the address of the vector is inside iwram" /// ); /// # }