diff --git a/agb/build.rs b/agb/build.rs index e2aa93a..9c8a3a6 100644 --- a/agb/build.rs +++ b/agb/build.rs @@ -1,7 +1,13 @@ use std::path; fn main() { - let asm = &["crt0.s", "interrupt_handler.s", "src/sound/mixer/mixer.s"]; + let asm = &[ + "crt0.s", + "interrupt_handler.s", + "src/sound/mixer/mixer.s", + "src/agbabi/memset.s", + "src/agbabi/memcpy.s", + ]; println!("cargo:rerun-if-changed=gba.ld"); println!("cargo:rerun-if-changed=gba_mb.ld"); @@ -50,6 +56,7 @@ fn main() { } let archive = format!("{out_dir}/agb.a"); + let _ = std::fs::remove_file(&archive); let ar_out = std::process::Command::new("arm-none-eabi-ar") .arg("-crs") .arg(&archive) diff --git a/agb/gba.ld b/agb/gba.ld index 0c46202..cd74016 100644 --- a/agb/gba.ld +++ b/agb/gba.ld @@ -4,6 +4,9 @@ OUTPUT_ARCH(arm) ENTRY(__start) EXTERN(__RUST_INTERRUPT_HANDLER) +EXTERN(__agbabi_memset) +EXTERN(__agbabi_memcpy) + MEMORY { ewram (w!x) : ORIGIN = 0x02000000, LENGTH = 256K iwram (w!x) : ORIGIN = 0x03000000, LENGTH = 32K diff --git a/agb/gba_mb.ld b/agb/gba_mb.ld index dd11007..238b5c4 100644 --- a/agb/gba_mb.ld +++ b/agb/gba_mb.ld @@ -4,6 +4,9 @@ OUTPUT_ARCH(arm) ENTRY(__start) EXTERN(__RUST_INTERRUPT_HANDLER) +EXTERN(__agbabi_memset) +EXTERN(__agbabi_memcpy) + MEMORY { ewram (w!x) : ORIGIN = 0x02000000, LENGTH = 256K iwram (w!x) : ORIGIN = 0x03000000, LENGTH = 32K diff --git a/agb/interrupt_handler.s b/agb/interrupt_handler.s index fbceea1..90f5272 100644 --- a/agb/interrupt_handler.s +++ b/agb/interrupt_handler.s @@ -2,7 +2,7 @@ @ An interrupt handler that simply acknowledges all interrupts .arm .global InterruptHandler - .section .iwram, "ax" + .section .iwram, "ax", %progbits .align InterruptHandler: mov r2, #0x04000000 @ interrupt enable register location @@ -50,7 +50,7 @@ InterruptHandler: .pool -.section .iwram +.section .iwram.program_counter .global agb_rs__program_counter .balign 4 agb_rs__program_counter: diff --git a/agb/src/agbabi/LICENSE.md b/agb/src/agbabi/LICENSE.md new file mode 100644 index 0000000..5b2b54f --- /dev/null +++ b/agb/src/agbabi/LICENSE.md @@ -0,0 +1,17 @@ +libagbabi is available under the [zlib license](https://www.zlib.net/zlib_license.html) : + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. diff --git a/agb/src/agbabi/README.md b/agb/src/agbabi/README.md new file mode 100644 index 0000000..7ea9b74 --- /dev/null +++ b/agb/src/agbabi/README.md @@ -0,0 +1,10 @@ +# agbabi + +GBA optimized library functions for common operations. + +Includes implementations for various aeabi functions. + +# Use in agb + +Restricted to the subset of replacement functions which make the biggest difference when using rust on the gba. +Minor modifications have been made around adding extra symbols to reference in the linker script. \ No newline at end of file diff --git a/agb/src/agbabi/memcpy.s b/agb/src/agbabi/memcpy.s new file mode 100644 index 0000000..0c7412b --- /dev/null +++ b/agb/src/agbabi/memcpy.s @@ -0,0 +1,115 @@ +/* +=============================================================================== + + ABI: + __aeabi_memcpy, __aeabi_memcpy4, __aeabi_memcpy8 + Standard: + memcpy + Support: + __agbabi_memcpy2 + + Copyright (C) 2021-2022 agbabi contributors + For conditions of distribution and use, see copyright notice in LICENSE.md + +=============================================================================== +*/ + + .arm + .align 4 + + .section .iwram.__aeabi_memcpy, "ax", %progbits + .global __agbabi_memcpy +__agbabi_memcpy: + .global __aeabi_memcpy +__aeabi_memcpy: + // Check pointer alignment + eor r3, r1, r0 + // JoaoBapt carry & sign bit test + movs r3, r3, lsl #31 + bmi .Lcopy1 + bcs .Lcopy2 + +.Lcopy4: + // Copy half and byte head + rsb r3, r0, #4 + movs r3, r3, lsl #31 + ldrmib r3, [r1], #1 + strmib r3, [r0], #1 + submi r2, r2, #1 + ldrcsh r3, [r1], #2 + strcsh r3, [r0], #2 + subcs r2, r2, #2 + // Fallthrough + + .global __aeabi_memcpy8 +__aeabi_memcpy8: + .global __aeabi_memcpy4 +__aeabi_memcpy4: + // Copy 8 words + movs r12, r2, lsr #5 + beq .Lskip32 + lsl r3, r12, #5 + sub r2, r2, r3 + push {r4-r10} +.LcopyWords8: + ldmia r1!, {r3-r10} + stmia r0!, {r3-r10} + subs r12, r12, #1 + bne .LcopyWords8 + pop {r4-r10} +.Lskip32: + + // Copy words + movs r12, r2, lsr #2 +.LcopyWords: + subs r12, r12, #1 + ldrhs r3, [r1], #4 + strhs r3, [r0], #4 + bhs .LcopyWords + + // Copy half and byte tail + movs r3, r2, lsl #31 + ldrcsh r3, [r1], #2 + strcsh r3, [r0], #2 + ldrmib r3, [r1] + strmib r3, [r0] + bx lr + +.Lcopy2: + // Copy byte head + tst r0, #1 + ldrneb r3, [r1], #1 + strneb r3, [r0], #1 + subne r2, r2, #1 + // Fallthrough + + .global __agbabi_memcpy2 +__agbabi_memcpy2: + // Copy halves + movs r12, r2, lsr #1 +.LcopyHalves: + subs r12, r12, #1 + ldrhsh r3, [r1], #2 + strhsh r3, [r0], #2 + bhs .LcopyHalves + + // Copy byte tail + tst r2, #1 + ldrneb r3, [r1] + strneb r3, [r0] + bx lr + +.Lcopy1: + subs r2, r2, #1 + ldrhsb r3, [r1], #1 + strhsb r3, [r0], #1 + bhs .Lcopy1 + bx lr + + .section .iwram.memcpy, "ax", %progbits + .global memcpy +memcpy: + push {r0, lr} + bl __aeabi_memcpy + pop {r0, lr} + bx lr diff --git a/agb/src/agbabi/memset.s b/agb/src/agbabi/memset.s new file mode 100644 index 0000000..b1a934c --- /dev/null +++ b/agb/src/agbabi/memset.s @@ -0,0 +1,107 @@ +/* +=============================================================================== + + ABI: + __aeabi_memset, __aeabi_memset4, __aeabi_memset8, + __aeabi_memclr, __aeabi_memclr4, __aeabi_memclr8 + Standard: + memset + Support: + __agbabi_wordset4 + + Copyright (C) 2021-2022 agbabi contributors + For conditions of distribution and use, see copyright notice in LICENSE.md + +=============================================================================== +*/ + + .arm + .balign 4 + + .section .iwram.__aeabi_memset, "ax", %progbits + .global __agbabi_memset +__agbabi_memset: + .global __aeabi_memclr +__aeabi_memclr: + mov r2, #0 + b .LskipShifts + + .global __aeabi_memset +__aeabi_memset: + mov r2, r2, lsl #24 + orr r2, r2, r2, lsr #8 + orr r2, r2, r2, lsr #16 + // Fallthrough + +.LskipShifts: + // JoaoBapt carry & sign bit test + rsb r3, r0, #4 + movs r3, r3, lsl #31 + // Set half and byte head + strmib r2, [r0], #1 + submi r1, r1, #1 + strcsh r2, [r0], #2 + subcs r1, r1, #2 + b __agbabi_wordset4 + + .global __aeabi_memclr8 +__aeabi_memclr8: + .global __aeabi_memclr4 +__aeabi_memclr4: + mov r2, #0 + b __agbabi_wordset4 + + .global __aeabi_memset8 +__aeabi_memset8: + .global __aeabi_memset4 +__aeabi_memset4: + mov r2, r2, lsl #24 + orr r2, r2, r2, lsr #8 + orr r2, r2, r2, lsr #16 + // Fallthrough + + .global __agbabi_wordset4 +__agbabi_wordset4: + // Set 8 words + movs r12, r1, lsr #5 + beq .Lskip32 + lsl r3, r12, #5 + sub r1, r1, r3 + push {r4-r9} + mov r3, r2 + mov r4, r2 + mov r5, r2 + mov r6, r2 + mov r7, r2 + mov r8, r2 + mov r9, r2 +.LsetWords8: + stmia r0!, {r2-r9} + subs r12, r12, #1 + bne .LsetWords8 + pop {r4-r9} +.Lskip32: + + // Set words + movs r12, r1, lsr #2 +.LsetWords: + subs r12, r12, #1 + strhs r2, [r0], #4 + bhs .LsetWords + + // Set half and byte tail + movs r3, r1, lsl #31 + strcsh r2, [r0], #2 + strmib r2, [r0] + bx lr + + .section .iwram.memset, "ax", %progbits + .global memset +memset: + mov r3, r1 + mov r1, r2 + mov r2, r3 + push {r0, lr} + bl __aeabi_memset + pop {r0, lr} + bx lr diff --git a/agb/src/asm_include.s b/agb/src/asm_include.s index c561d04..5508e4f 100644 --- a/agb/src/asm_include.s +++ b/agb/src/asm_include.s @@ -1,5 +1,5 @@ .macro agb_arm_func functionName:req -.section .iwram +.section .iwram.\functionName, "ax", %progbits .arm .align 2 .global \functionName diff --git a/agb/src/sound/mixer/mixer.s b/agb/src/sound/mixer/mixer.s index 8d4118c..33f5917 100644 --- a/agb/src/sound/mixer/mixer.s +++ b/agb/src/sound/mixer/mixer.s @@ -1,6 +1,6 @@ .include "src/asm_include.s" -.section .iwram +.section .iwram.buffer_size .global agb_rs__buffer_size .balign 4 agb_rs__buffer_size: @@ -26,7 +26,8 @@ modifications_fallback: orr r7, r7, r3, lsl #16 @ r7 now is the left channel followed by the right channel modifications. mov r5, #0 @ current index we're reading from - ldr r8, agb_rs__buffer_size @ the number of steps left + ldr r8, =agb_rs__buffer_size @ the number of steps left + ldr r8, [r8] 1: @@ -64,7 +65,8 @@ same_modification: bne 1b mov r5, #0 @ current index we're reading from - ldr r8, agb_rs__buffer_size @ the number of steps left + ldr r8, =agb_rs__buffer_size @ the number of steps left + ldr r8, [r8] 1: .rept 4 @@ -99,7 +101,8 @@ agb_arm_func agb_rs__mixer_add_stereo ldr r5, =0x00000FFF - ldr r8, agb_rs__buffer_size + ldr r8, =agb_rs__buffer_size + ldr r8, [r8] 1: .rept 4 ldrsh r6, [r0], #2 @ load the current sound sample to r6 @@ -138,34 +141,6 @@ agb_arm_func agb_rs__mixer_add_stereo agb_arm_end agb_rs__mixer_add_stereo -.section .iwram - .balign 4 -constant_zero: -.rept 4 - .word 0 -.endr - -agb_arm_func agb_rs__init_buffer - @ arguments: - @ r0 = target buffer - @ r1 = size in bytes (must be a multiple of 16) - push {r4-r5} - - @ zero registers r3-r5 - ldr r2, =constant_zero - ldm r2, {r3-r5,r12} - -1: - @ zero 4 words worth of the buffer - stmia r0!, {r3-r5,r12} - subs r1, r1, #(4 * 4) - @ loop if we haven't zeroed everything - bne 1b - - pop {r4-r5} - bx lr -agb_arm_end agb_rs__init_buffer - agb_arm_func agb_rs__mixer_collapse @ Arguments: @ r0 = target buffer (i8) @@ -184,7 +159,8 @@ SWAP_SIGN .req r11 ldr CONST_127, =127 ldr SWAP_SIGN, =0x80808080 - ldr r2, agb_rs__buffer_size @ loop counter + ldr r2, =agb_rs__buffer_size @ loop counter + ldr r2, [r2] mov r4, r2 @ The idea for this solution came from pimpmobile: diff --git a/agb/src/sound/mixer/sw_mixer.rs b/agb/src/sound/mixer/sw_mixer.rs index c8ed7b4..6dc61a2 100644 --- a/agb/src/sound/mixer/sw_mixer.rs +++ b/agb/src/sound/mixer/sw_mixer.rs @@ -1,6 +1,5 @@ use core::cell::RefCell; -use core::mem; -use core::mem::MaybeUninit; +use core::intrinsics::transmute; use bare_metal::{CriticalSection, Mutex}; @@ -29,8 +28,6 @@ extern "C" { fn agb_rs__mixer_add_stereo(sound_data: *const u8, sound_buffer: *mut Num); fn agb_rs__mixer_collapse(sound_buffer: *mut i8, input_buffer: *const Num); - - fn agb_rs__init_buffer(buffer: *mut MaybeUninit>, size_in_bytes: usize); } pub struct Mixer { @@ -235,28 +232,8 @@ impl MixerBuffer { } fn write_channels<'a>(&mut self, channels: impl Iterator) { - // This code is equivalent to: - // let mut buffer: [Num; constants::SOUND_BUFFER_SIZE * 2] = - // [Num::new(0); constants::SOUND_BUFFER_SIZE * 2]; - // but the above uses approximately 7% of the CPU time if running at 32kHz - let mut buffer: [Num; constants::SOUND_BUFFER_SIZE * 2] = { - // Create an uninitialized array of `MaybeUninit`. The `assume_init` is - // safe because the type we are claiming to have initialized here is a - // bunch of `MaybeUninit`s, which do not require initialization. - let mut data: [MaybeUninit>; constants::SOUND_BUFFER_SIZE * 2] = - unsafe { MaybeUninit::uninit().assume_init() }; - - // Actually init the array (by filling it with zeros) and then transmute it (which is safe because - // we have now zeroed everything) - unsafe { - agb_rs__init_buffer( - data.as_mut_ptr(), - mem::size_of::>() * data.len(), - ); - - mem::transmute(data) - } - }; + let mut buffer: [Num; constants::SOUND_BUFFER_SIZE * 2] = + unsafe { transmute([0i16; constants::SOUND_BUFFER_SIZE * 2]) }; for channel in channels { if channel.is_done { diff --git a/book/games/pong/gba.ld b/book/games/pong/gba.ld index 0c46202..cd74016 100644 --- a/book/games/pong/gba.ld +++ b/book/games/pong/gba.ld @@ -4,6 +4,9 @@ OUTPUT_ARCH(arm) ENTRY(__start) EXTERN(__RUST_INTERRUPT_HANDLER) +EXTERN(__agbabi_memset) +EXTERN(__agbabi_memcpy) + MEMORY { ewram (w!x) : ORIGIN = 0x02000000, LENGTH = 256K iwram (w!x) : ORIGIN = 0x03000000, LENGTH = 32K diff --git a/book/games/pong/gba_mb.ld b/book/games/pong/gba_mb.ld index dd11007..238b5c4 100644 --- a/book/games/pong/gba_mb.ld +++ b/book/games/pong/gba_mb.ld @@ -4,6 +4,9 @@ OUTPUT_ARCH(arm) ENTRY(__start) EXTERN(__RUST_INTERRUPT_HANDLER) +EXTERN(__agbabi_memset) +EXTERN(__agbabi_memcpy) + MEMORY { ewram (w!x) : ORIGIN = 0x02000000, LENGTH = 256K iwram (w!x) : ORIGIN = 0x03000000, LENGTH = 32K diff --git a/examples/the-hat-chooses-the-wizard/gba.ld b/examples/the-hat-chooses-the-wizard/gba.ld index 0c46202..cd74016 100644 --- a/examples/the-hat-chooses-the-wizard/gba.ld +++ b/examples/the-hat-chooses-the-wizard/gba.ld @@ -4,6 +4,9 @@ OUTPUT_ARCH(arm) ENTRY(__start) EXTERN(__RUST_INTERRUPT_HANDLER) +EXTERN(__agbabi_memset) +EXTERN(__agbabi_memcpy) + MEMORY { ewram (w!x) : ORIGIN = 0x02000000, LENGTH = 256K iwram (w!x) : ORIGIN = 0x03000000, LENGTH = 32K diff --git a/examples/the-purple-night/gba.ld b/examples/the-purple-night/gba.ld index 0c46202..cd74016 100644 --- a/examples/the-purple-night/gba.ld +++ b/examples/the-purple-night/gba.ld @@ -4,6 +4,9 @@ OUTPUT_ARCH(arm) ENTRY(__start) EXTERN(__RUST_INTERRUPT_HANDLER) +EXTERN(__agbabi_memset) +EXTERN(__agbabi_memcpy) + MEMORY { ewram (w!x) : ORIGIN = 0x02000000, LENGTH = 256K iwram (w!x) : ORIGIN = 0x03000000, LENGTH = 32K diff --git a/examples/the-purple-night/gba_mb.ld b/examples/the-purple-night/gba_mb.ld index dd11007..238b5c4 100644 --- a/examples/the-purple-night/gba_mb.ld +++ b/examples/the-purple-night/gba_mb.ld @@ -4,6 +4,9 @@ OUTPUT_ARCH(arm) ENTRY(__start) EXTERN(__RUST_INTERRUPT_HANDLER) +EXTERN(__agbabi_memset) +EXTERN(__agbabi_memcpy) + MEMORY { ewram (w!x) : ORIGIN = 0x02000000, LENGTH = 256K iwram (w!x) : ORIGIN = 0x03000000, LENGTH = 32K diff --git a/justfile b/justfile index a5467f6..79d0099 100644 --- a/justfile +++ b/justfile @@ -50,6 +50,10 @@ build-book: update-lockfiles: bash .github/scripts/update-lockfiles.sh +update-linker-scripts: + find -type f -name gba.ld | grep -v ./agb/gba.ld | xargs -n1 cp -v -- agb/gba.ld + find -type f -name gba_mb.ld | grep -v ./agb/gba_mb.ld | xargs -n1 cp -v -- agb/gba_mb.ld + _build-rom folder name: #!/usr/bin/env bash set -euxo pipefail diff --git a/template/gba.ld b/template/gba.ld index 0c46202..cd74016 100644 --- a/template/gba.ld +++ b/template/gba.ld @@ -4,6 +4,9 @@ OUTPUT_ARCH(arm) ENTRY(__start) EXTERN(__RUST_INTERRUPT_HANDLER) +EXTERN(__agbabi_memset) +EXTERN(__agbabi_memcpy) + MEMORY { ewram (w!x) : ORIGIN = 0x02000000, LENGTH = 256K iwram (w!x) : ORIGIN = 0x03000000, LENGTH = 32K diff --git a/template/gba_mb.ld b/template/gba_mb.ld index dd11007..238b5c4 100644 --- a/template/gba_mb.ld +++ b/template/gba_mb.ld @@ -4,6 +4,9 @@ OUTPUT_ARCH(arm) ENTRY(__start) EXTERN(__RUST_INTERRUPT_HANDLER) +EXTERN(__agbabi_memset) +EXTERN(__agbabi_memcpy) + MEMORY { ewram (w!x) : ORIGIN = 0x02000000, LENGTH = 256K iwram (w!x) : ORIGIN = 0x03000000, LENGTH = 32K