diff --git a/agb/examples/allocation.rs b/agb/examples/allocation.rs index 64f0ddd3..b1b9a8b8 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::EWRAM_ALLOC); let b = Box::new(1); - agb::println!("dynamic allocation made to {:?}", &*b as *const _); + 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/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/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..22230650 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,8 +43,59 @@ 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 +/// #![feature(allocator_api)] +/// # #![no_std] +/// # #![no_main] +/// # use agb::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 +/// #![feature(allocator_api)] +/// # #![no_std] +/// # #![no_main] +/// # use agb::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..0x0300_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 { + start: iwram_data_end, + end: || IWRAM_END, + }) +}; + #[cfg(any(test, feature = "testing"))] -pub unsafe fn number_of_blocks() -> u32 { +pub(crate) unsafe fn number_of_blocks() -> u32 { GLOBAL_ALLOC.number_of_blocks() } @@ -56,6 +108,16 @@ fn alloc_error(layout: Layout) -> ! { ); } +fn iwram_data_end() -> usize { + extern "C" { + static __iwram_end: usize; + } + + // TODO: This seems completely wrong, but without the &, rust generates + // a double dereference :/. Maybe a bug in nightly? + (unsafe { &__iwram_end }) as *const _ as usize +} + fn data_end() -> usize { extern "C" { static __ewram_data_end: usize; @@ -150,4 +212,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 + ); + } } diff --git a/agb/src/lib.rs b/agb/src/lib.rs index 16fb1044..31657fd9 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)] @@ -189,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)] 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;