diff --git a/audio.asm b/audio.asm index 96a7c72..3683d73 100644 --- a/audio.asm +++ b/audio.asm @@ -3,96 +3,248 @@ ; use ffmpeg to convert an audio file for playback: ; ffmpeg -i input.mp3 -f s16le -ac 1 -ar 22050 audio.raw -; fox32's audio output works by swapping between two buffers -; interrupt 0xFE (vector 0x000003F8) is fired when it's time to swap buffers -; when that interrupt fires, it calls `refill_buffer` which fills up the buffer - -const CURRENT_BUFFER: 0x01FFFF00 -const AUDIO_POINTER: 0x01FFFF01 -const AUDIO_LENGTH: 0x01FFFF05 -const OLD_BUFFER_SWAP_VECTOR: 0x01FFFF09 +const AUDIO_POINTER_0: 0x01FFFF00 ; 4 bytes +const AUDIO_POINTER_1: 0x01FFFF04 ; 4 bytes +const AUDIO_POINTER_2: 0x01FFFF08 ; 4 bytes +const AUDIO_POINTER_3: 0x01FFFF0C ; 4 bytes +const AUDIO_LENGTH_0: 0x01FFFF10 ; 4 bytes +const AUDIO_LENGTH_1: 0x01FFFF14 ; 4 bytes +const AUDIO_LENGTH_2: 0x01FFFF18 ; 4 bytes +const AUDIO_LENGTH_3: 0x01FFFF1C ; 4 bytes +const OLD_BUFFER_SWAP_VECTOR_0: 0x01FFFF20 ; 4 bytes +const OLD_BUFFER_SWAP_VECTOR_1: 0x01FFFF24 ; 4 bytes +const OLD_BUFFER_SWAP_VECTOR_2: 0x01FFFF28 ; 4 bytes +const OLD_BUFFER_SWAP_VECTOR_3: 0x01FFFF2C ; 4 bytes ; play an audio clip (does not block) ; inputs: ; r0: pointer to audio clip ; r1: length of audio clip in bytes (must be a multiple of 32768 bytes) ; r2: audio sample rate +; r3: audio channel (0-3) ; outputs: ; none play_audio: push r0 push r1 - ; set the interrupt vector for interrupt 0xFE and save the old one - mov [OLD_BUFFER_SWAP_VECTOR], [0x000003F8] - mov [0x000003F8], refill_buffer + cmp r3, 0 + ifz jmp play_audio_0 + cmp r3, 1 + ifz jmp play_audio_1 + cmp r3, 2 + ifz jmp play_audio_2 + cmp r3, 3 + ifz jmp play_audio_3 + jmp play_audio_done +play_audio_0: + ; set the interrupt vector for interrupt 0xFE + ; save the old one only if it wasn't already saved earlier + cmp [OLD_BUFFER_SWAP_VECTOR_0], refill_buffer_0 + ifnz mov [OLD_BUFFER_SWAP_VECTOR_0], [0x000003F8] + mov [0x000003F8], refill_buffer_0 ; store audio pointer - mov [AUDIO_POINTER], r0 + mov [AUDIO_POINTER_0], r0 ; store audio length ; floor it to the nearest multiple of 32768 and r1, 0xFFFF8000 - mov [AUDIO_LENGTH], r1 - - ; reset the current buffer to zero - ; fox32 resets its current buffer to zero internally, so reset it to zero here to stay in sync - ; refill_buffer will bitwise not this before checking it, - ; so set it to 0xFF so that it becomes zero when checked for the first time - mov.8 [CURRENT_BUFFER], 0xFF + mov [AUDIO_LENGTH_0], r1 ; enable audio playback and set sample rate mov r0, 0x80000600 out r0, r2 + ; initial buffer fill + int 0xFE + + jmp play_audio_done +play_audio_1: + ; set the interrupt vector for interrupt 0xFD + ; save the old one only if it wasn't already saved earlier + cmp [OLD_BUFFER_SWAP_VECTOR_1], refill_buffer_1 + ifnz mov [OLD_BUFFER_SWAP_VECTOR_1], [0x000003F4] + mov [0x000003F4], refill_buffer_1 + + ; store audio pointer + mov [AUDIO_POINTER_1], r0 + + ; store audio length + ; floor it to the nearest multiple of 32768 + and r1, 0xFFFF8000 + mov [AUDIO_LENGTH_1], r1 + + ; enable audio playback and set sample rate + mov r0, 0x80000601 + out r0, r2 + + ; initial buffer fill + int 0xFD + + jmp play_audio_done +play_audio_2: + ; set the interrupt vector for interrupt 0xFC + ; save the old one only if it wasn't already saved earlier + cmp [OLD_BUFFER_SWAP_VECTOR_2], refill_buffer_2 + ifnz mov [OLD_BUFFER_SWAP_VECTOR_2], [0x000003F0] + mov [0x000003F0], refill_buffer_2 + + ; store audio pointer + mov [AUDIO_POINTER_2], r0 + + ; store audio length + ; floor it to the nearest multiple of 32768 + and r1, 0xFFFF8000 + mov [AUDIO_LENGTH_2], r1 + + ; enable audio playback and set sample rate + mov r0, 0x80000602 + out r0, r2 + + ; initial buffer fill + int 0xFC + + jmp play_audio_done +play_audio_3: + ; set the interrupt vector for interrupt 0xFB + ; save the old one only if it wasn't already saved earlier + cmp [OLD_BUFFER_SWAP_VECTOR_3], refill_buffer_3 + ifnz mov [OLD_BUFFER_SWAP_VECTOR_3], [0x000003EC] + mov [0x000003EC], refill_buffer_3 + + ; store audio pointer + mov [AUDIO_POINTER_3], r0 + + ; store audio length + ; floor it to the nearest multiple of 32768 + and r1, 0xFFFF8000 + mov [AUDIO_LENGTH_3], r1 + + ; enable audio playback and set sample rate + mov r0, 0x80000603 + out r0, r2 + + ; initial buffer fill + int 0xFB +play_audio_done: pop r1 pop r0 ret -refill_buffer: - push r0 - push r1 - push r31 - - not.8 [CURRENT_BUFFER] - cmp.8 [CURRENT_BUFFER], 0 - ifnz jmp refill_buffer_1 -refill_buffer_0: - mov r31, 8192 ; 32768 bytes = 8192 words - mov r0, [AUDIO_POINTER] - mov r1, 0x0212C000 ; buffer 0 address -refill_buffer_loop: - mov [r1], [r0] - add r0, 4 - add r1, 4 - loop refill_buffer_loop - mov [AUDIO_POINTER], r0 - sub [AUDIO_LENGTH], 32768 - ifz call stop_audio - - pop r31 - pop r1 - pop r0 - reti -refill_buffer_1: - mov r31, 8192 ; 32768 bytes = 8192 words - mov r0, [AUDIO_POINTER] - mov r1, 0x02134000 ; buffer 1 address - jmp refill_buffer_loop ; stop audio playback ; inputs: -; none +; r0: audio channel (0-3) ; outputs: ; none stop_audio: push r0 ; disable audio playback - mov r0, 0x80000600 + or r0, 0x80000600 out r0, 0 - ; restore the old buffer swap vector - mov [0x000003F8], [OLD_BUFFER_SWAP_VECTOR] + ; restore the old buffer refill vector + cmp.8 r0, 0 + ifz mov [0x000003F8], [OLD_BUFFER_SWAP_VECTOR_0] + cmp.8 r0, 1 + ifz mov [0x000003F4], [OLD_BUFFER_SWAP_VECTOR_1] + cmp.8 r0, 2 + ifz mov [0x000003F0], [OLD_BUFFER_SWAP_VECTOR_2] + cmp.8 r0, 3 + ifz mov [0x000003EC], [OLD_BUFFER_SWAP_VECTOR_3] pop r0 ret + +refill_buffer_0: + push r0 + push r1 + push r31 + + mov r31, 8192 ; 32768 bytes = 8192 words + mov r0, [AUDIO_POINTER_0] + mov r1, 0x0212C000 ; buffer 0 address +refill_buffer_0_loop: + mov [r1], [r0] + add r0, 4 + add r1, 4 + loop refill_buffer_0_loop + mov [AUDIO_POINTER_0], r0 + sub [AUDIO_LENGTH_0], 32768 + ifz mov r0, 0 + ifz call stop_audio + + pop r31 + pop r1 + pop r0 + reti + +refill_buffer_1: + push r0 + push r1 + push r31 + + mov r31, 8192 ; 32768 bytes = 8192 words + mov r0, [AUDIO_POINTER_1] + mov r1, 0x02134000 ; buffer 1 address +refill_buffer_1_loop: + mov [r1], [r0] + add r0, 4 + add r1, 4 + loop refill_buffer_1_loop + mov [AUDIO_POINTER_1], r0 + sub [AUDIO_LENGTH_1], 32768 + ifz mov r0, 1 + ifz call stop_audio + + pop r31 + pop r1 + pop r0 + reti + +refill_buffer_2: + push r0 + push r1 + push r31 + + mov r31, 8192 ; 32768 bytes = 8192 words + mov r0, [AUDIO_POINTER_2] + mov r1, 0x02290000 ; buffer 2 address +refill_buffer_2_loop: + mov [r1], [r0] + add r0, 4 + add r1, 4 + loop refill_buffer_2_loop + mov [AUDIO_POINTER_2], r0 + sub [AUDIO_LENGTH_2], 32768 + ifz mov r0, 2 + ifz call stop_audio + + pop r31 + pop r1 + pop r0 + reti + +refill_buffer_3: + push r0 + push r1 + push r31 + + mov r31, 8192 ; 32768 bytes = 8192 words + mov r0, [AUDIO_POINTER_3] + mov r1, 0x02298000 ; buffer 3 address +refill_buffer_3_loop: + mov [r1], [r0] + add r0, 4 + add r1, 4 + loop refill_buffer_3_loop + mov [AUDIO_POINTER_3], r0 + sub [AUDIO_LENGTH_3], 32768 + ifz mov r0, 3 + ifz call stop_audio + + pop r31 + pop r1 + pop r0 + reti diff --git a/main.asm b/main.asm index 9c994b9..1ad1644 100644 --- a/main.asm +++ b/main.asm @@ -3,7 +3,7 @@ org 0xF0000000 const FOX32ROM_VERSION_MAJOR: 0 -const FOX32ROM_VERSION_MINOR: 1 +const FOX32ROM_VERSION_MINOR: 2 const FOX32ROM_VERSION_PATCH: 0 const SYSTEM_STACK: 0x01FFF800