diff --git a/agb/examples/i-will-not-let-you-let-me-down.raw b/agb/examples/i-will-not-let-you-let-me-down.raw new file mode 100644 index 0000000..9ebfb04 Binary files /dev/null and b/agb/examples/i-will-not-let-you-let-me-down.raw differ diff --git a/agb/examples/mixer_basic.rs b/agb/examples/mixer_basic.rs new file mode 100644 index 0000000..41f9200 --- /dev/null +++ b/agb/examples/mixer_basic.rs @@ -0,0 +1,20 @@ +#![no_std] +#![no_main] + +extern crate agb; + +use agb::sound; + +const I_WILL_NOT_LET_YOU_LET_ME_DOWN: &'static [u8] = + include_bytes!("i-will-not-let-you-let-me-down.raw"); + +#[no_mangle] +pub fn main() -> ! { + let gba = agb::Gba::new(); + let mixer = gba.mixer; + mixer.enable(); + + mixer.play_sound_starting_at(&I_WILL_NOT_LET_YOU_LET_ME_DOWN[0], 8000); + + loop {} +} diff --git a/agb/src/lib.rs b/agb/src/lib.rs index b080109..998c88a 100644 --- a/agb/src/lib.rs +++ b/agb/src/lib.rs @@ -51,6 +51,7 @@ static mut GBASINGLE: single::Singleton = single::Singleton::new(unsafe { G pub struct Gba { pub display: display::Display, pub sound: sound::dmg::Sound, + pub mixer: sound::mixer::Mixer, } impl Gba { @@ -62,6 +63,7 @@ impl Gba { Self { display: display::Display::new(), sound: sound::dmg::Sound::new(), + mixer: sound::mixer::Mixer::new(), } } } diff --git a/agb/src/sound/mixer.rs b/agb/src/sound/mixer.rs new file mode 100644 index 0000000..72aec10 --- /dev/null +++ b/agb/src/sound/mixer.rs @@ -0,0 +1,65 @@ +use crate::memory_mapped::MemoryMapped; + +#[non_exhaustive] +pub struct Mixer {} + +impl Mixer { + pub(crate) const unsafe fn new() -> Self { + Mixer {} + } + + pub fn enable(&self) { + set_sound_control_register_for_mixer(); + } + + pub fn play_sound_starting_at(&self, sound_memory: *const u8, frequency: i32) { + set_timer_counter_for_frequency_and_enable(frequency); + enable_dma1_for_sound(sound_memory); + } +} + +// Once we have proper DMA support, we should use that rather than hard coding these here too +const DMA1_SOURCE_ADDR: MemoryMapped = unsafe { MemoryMapped::new(0x0400_00bc) }; +const DMA1_DEST_ADDR: MemoryMapped = unsafe { MemoryMapped::new(0x0400_00c0) }; +const _DMA1_WORD_COUNT: MemoryMapped = unsafe { MemoryMapped::new(0x0400_00c4) }; // sound ignores this for some reason +const DMA1_CONTROL: MemoryMapped = unsafe { MemoryMapped::new(0x0400_00c6) }; + +const FIFOA_DEST_ADDR: u32 = 0x0400_00a0; + +// Similarly for proper timer support +const TIMER0_COUNTER: MemoryMapped = unsafe { MemoryMapped::new(0x0400_0100) }; +const TIMER0_CONTROL: MemoryMapped = unsafe { MemoryMapped::new(0x0400_0102) }; + +const SOUND_CONTROL: MemoryMapped = unsafe { MemoryMapped::new(0x0400_0082) }; +const SOUND_CONTROL_X: MemoryMapped = unsafe { MemoryMapped::new(0x0400_0084) }; + +fn enable_dma1_for_sound(sound_memory: *const u8) { + let dest_fixed: u16 = 2 << 5; // dest addr control = fixed + let repeat: u16 = 1 << 9; + let transfer_type: u16 = 1 << 10; // transfer in words + let dma_start_timing: u16 = 3 << 12; // sound fifo timing + let enable: u16 = 1 << 15; // enable + + DMA1_SOURCE_ADDR.set(sound_memory as u32); + DMA1_DEST_ADDR.set(FIFOA_DEST_ADDR); + DMA1_CONTROL.set(dest_fixed | repeat | transfer_type | dma_start_timing | enable); +} + +fn set_sound_control_register_for_mixer() { + let sound_a_volume_100: u16 = 1 << 2; + let sound_a_rout: u16 = 1 << 8; + let sound_a_lout: u16 = 1 << 9; + let sound_a_fifo_reset: u16 = 1 << 11; + + SOUND_CONTROL.set(sound_a_volume_100 | sound_a_rout | sound_a_lout | sound_a_fifo_reset); + + // master sound enable + SOUND_CONTROL_X.set(1 << 7); +} + +fn set_timer_counter_for_frequency_and_enable(frequency: i32) { + let counter = 65536 - (16777216 / frequency); + TIMER0_COUNTER.set(counter as u16); + + TIMER0_CONTROL.set(1 << 7); // enable the timer +} diff --git a/agb/src/sound/mod.rs b/agb/src/sound/mod.rs index 53d463f..1236802 100644 --- a/agb/src/sound/mod.rs +++ b/agb/src/sound/mod.rs @@ -1 +1,2 @@ pub mod dmg; +pub mod mixer;