dropped aas, moved to maxmod
[gbajam21] / libs / maxmod / mm_mixer_gba.S
diff --git a/libs/maxmod/mm_mixer_gba.S b/libs/maxmod/mm_mixer_gba.S
new file mode 100644 (file)
index 0000000..16bdeb7
--- /dev/null
@@ -0,0 +1,1367 @@
+/****************************************************************************
+ *                                                          __              *
+ *                ____ ___  ____ __  ______ ___  ____  ____/ /              *
+ *               / __ `__ \/ __ `/ |/ / __ `__ \/ __ \/ __  /               *
+ *              / / / / / / /_/ />  </ / / / / / /_/ / /_/ /                *
+ *             /_/ /_/ /_/\__,_/_/|_/_/ /_/ /_/\____/\__,_/                 *
+ *                                                                          *
+ *                            GBA Audio System                              *
+ *                                                                          *
+ *         Copyright (c) 2008, Mukunda Johnson (mukunda@maxmod.org)         *
+ *                                                                          *
+ * Permission to use, copy, modify, and/or distribute this software for any *
+ * purpose with or without fee is hereby granted, provided that the above   *
+ * copyright notice and this permission notice appear in all copies.        *
+ *                                                                          *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES *
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF         *
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR  *
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES   *
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN    *
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF  *
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.           *
+ ****************************************************************************/
+
+@ DEFINITIONS
+
+#include "mp_macros.inc"
+#include "mp_format_mas.inc"
+#include "mp_defs.inc"
+
+@ timer freq = 2^24 / mixfreq
+@ mixlen ~= mixfreq * 0.01673
+@ recommended mixing frequencies: 5734,7884,10512,13379,15768,18157,21024,26758,31536,36314,40137,42048
+@ other mixing frequencies may cause clicks
+@ mixlen must be divisible by 2
+
+@==================================================================
+@                          GLOBAL SYMBOLS
+@==================================================================
+
+
+       .global mmVBlank
+       .type   mmVBlank STT_FUNC
+       .global mmMixerMix
+       .type   mmMixerMix STT_FUNC
+
+       .global mmMixerSetSource
+       .type   mmMixerSetSource STT_FUNC
+       .global mmMixerSetRead
+       .type   mmMixerSetRead STT_FUNC
+       .global mmMixerSetFreq
+       .type   mmMixerSetFreq STT_FUNC
+       .global mmMixerStopChannel
+       .type   mmMixerStopChannel STT_FUNC
+       .global mmMixerInit
+       .type   mmMixerInit STT_FUNC
+       .global mmMixerChannelActive
+       .type   mmMixerChannelActive STT_FUNC
+       .global mmMixerMulFreq
+       .type   mmMixerMulFreq STT_FUNC
+       .global mmMixerSetVolume
+       .type   mmMixerSetVolume STT_FUNC
+       .global mmMixerSetPan
+       .type   mmMixerSetPan STT_FUNC
+
+//     .global mm_freqscalar
+       .global mm_mixlen
+       .global mm_bpmdv
+       .global mp_mix_seg
+       .global mp_writepos
+       .global mm_mixchannels
+
+@===============================================
+@ more definitions
+@===============================================
+
+.EQU   REG_SOUNDCNT_L, 0x4000080
+.EQU   REG_SOUNDCNT_H, 0x4000082
+.EQU   REG_SOUNDCNT_X, 0x4000084
+
+.EQU   REG_TM0CNT,     0x4000100
+
+.EQU   REG_DMA1SAD,    0x40000BC
+.EQU   REG_DMA1DAD,    0x40000C0
+.EQU   REG_DMA1CNT,    0x40000C4
+
+.EQU   REG_DMA2SAD,    0x40000C8
+.EQU   REG_DMA2DAD,    0x40000CC
+.EQU   REG_DMA2CNT,    0x40000D0
+
+.EQU   REG_DMA3SAD,    0x40000D4
+.EQU   REG_DMA3DAD,    0x40000D8
+.EQU   REG_DMA3CNT,    0x40000DC
+
+.EQU   REG_SGFIFOA,    0x40000A0
+.EQU   REG_SGFIFOB,    0x40000A4
+.EQU   REG_VCOUNT,     0x4000006
+
+@ MIXER CHANNEL FORMAT
+
+.equ   CHN_SIZE, 24
+
+.equ   CHN_SRC,0
+.equ   CHN_READ,4
+.equ   CHN_VOL,8
+.equ   CHN_PAN,9
+// 10
+// 11
+.equ   CHN_FREQ,12
+.equ   CHN_SIZE,16
+
+/////////////////////.equ      CHN_LOOP,8
+/////////////////////.equ      CHN_LEN,16
+
+@-------------------------------------
+
+.equ   FETCH_SIZE, 384
+.equ   FETCH_THRESHOLD, (6016)//7040           // frequency threshold (dont use fetch for high freqs!)
+
+@======================================================================
+@                               MEMORY
+@======================================================================
+
+.section .bss
+.align 2
+
+mp_writepos:   .space 4        @ wavebuffer write position
+
+mm_mixbuffer:  .space 4        @ dont move (see init)
+mm_mixchannels:        .space 4        @ dont move
+mm_wavebuffer: .space 4        @ dont move
+mm_mixlen:     .space 4        @ dont move
+mm_mixch_count:        .space 4        @ dont move
+mm_mixch_end:  .space 4        @ dont move
+mm_ratescale:  .space 4        @ dont move
+mm_timerfreq:  .space 4        @ dont move
+
+mm_bpmdv:      .space 4
+
+mp_mix_seg:    .space 1        @ mixing segment select
+
+.align 2
+
+mm_fetch:      .space FETCH_SIZE+16
+
+@ 11-bit mixed sample buffer
+@ data is interleaved
+@ left,left,right,right,left,left,etc...
+
+@===========================================================================
+@                                  PROGRAM
+@===========================================================================
+
+.section .iwram, "ax", %progbits
+
+.ARM
+.ALIGN 2
+
+mpm_nullsample:
+.byte  128
+
+.align 2
+@-----------------------------------------------------------------------------------------------------
+mmVBlank:                              @ vblank wrapper, used to reset dma...HIGH PRIORITY PLEASE!
+@-----------------------------------------------------------------------------------------------------
+       
+       b       .mpvb_disabled          @ disable until ready.. (overwrite this area with NOP)
+
+       ldr     r0,=mp_mix_seg          @ swap mixing segmentl
+       ldrsb   r1, [r0]
+       mvns    r1, r1
+       strb    r1, [r0]
+       beq     .mpvb_exit
+       
+       ldr     r1,=0x040000c6          @ dma control   RESTART DMA
+       ldr     r0,=0x0440              @ disable dma
+       strh    r0, [r1]
+       strh    r0, [r1,#12]
+       ldr     r0,=0xB600              @ restart dma
+       strh    r0, [r1]
+       strh    r0, [r1,#12]
+       
+       b       .mpvb_dontreset
+.mpvb_exit:
+       ldr     r0,=mp_writepos         @ restart write position
+@      ldr     r1,=mp_playbuffer_l     @ ...
+       ldr     r1,=mm_wavebuffer
+       ldr     r1, [r1]
+       str     r1, [r0]                @ ...
+.mpvb_dontreset:
+
+.mpvb_disabled:
+       ldr     r0,=0x3007FF8           @ acknowledge interrupt
+       ldrh    r2, [r0]
+       orr     r2, r2, #0x1
+       strh    r2, [r0]
+       
+       ldr     r0,=mm_vblank_function
+       ldr     r0, [r0]
+       cmp     r0, #0
+       bxeq    lr
+       bx      r0
+.pool
+
+.align 2
+@-------------------------------------------------------------------------
+mmMixerMix:                            @ params={ samples_count }
+@-------------------------------------------------------------------------
+       
+@ exit function if samples == 0
+@ it will malfunction.
+       
+       cmp     r0, #0
+       bne     .mp_zerocheck
+       bx      lr
+
+.mp_zerocheck:
+
+@ preserve registers
+
+       stmfd   sp!, {r4-r11,lr}
+
+       stmfd   sp!, {r0}               @ preserve mixing count
+
+@------------------------------------------------------------------------
+@ SECTOR 0, INITIALIZATION
+@------------------------------------------------------------------------
+
+@ clear mixing buffers
+
+       and     r10, r0, #7
+       mov     r2, r0, lsr#3           @ clearing samps*2*2 bytes (hword*stereo) 32 bytes at a time
+       ldr     r0,=mm_mixbuffer
+       ldr     r0,[r0]
+       mov     r1, #0                  @ zero variable
+       mov     r3, r1
+       mov     r4, r1
+       mov     r5, r1
+       mov     r6, r1
+       mov     r7, r1
+       mov     r8, r1
+       mov     r9, r1
+
+       cmp     r2, #0
+       beq     2f
+
+@ clear 32 bytes/write
+
+1:     stmia   r0!, {r1,r3-r9}
+       subs    r2, r2, #1
+       bne     1b
+2:
+       
+@ clear remainder
+       cmp     r10, #0
+       beq     2f
+
+1:     str     r1, [r0], #4
+       subs    r10, r10, #1
+       bne     1b
+2:
+
+@----------------------------------------------------------------------------------
+@ BEGIN MIXING ROUTINE
+@----------------------------------------------------------------------------------
+
+       ldr     r12,=mm_mixchannels
+       ldr     r12,[r12]
+       mov     r11, #0                 @ volume addition
+       
+@--------------------
+.mpm_cloop:
+@--------------------
+
+@----------------------------------------------------------------------
+@ SECTOR 1, CALCULATIONS
+@----------------------------------------------------------------------
+
+@ aliases
+#define rchan  r12
+#define rvolA  r11
+#define rsrc   r10
+#define        rfreq   r9
+#define rmixb  r8
+#define rread  r7
+#define rvolL  r5
+#define rvolR  r6
+#define rmixc  r4
+#define rmixcc r3
+
+@ read source address
+
+       ldr     rsrc, [rchan, #CHN_SRC]
+       cmp     rsrc, #0
+       bmi     .mpm_next               @ EXIT if MSB is set ------------>
+
+@ read frequency value
+       
+       ldr     rfreq, [rchan, #CHN_FREQ]
+       cmp     rfreq, #0
+       beq     .mpm_next               @ EXIT if zero ----------------=->
+       
+       ldr     r0,=mm_ratescale
+       ldr     r0, [r0]
+       mul     rfreq, r0
+       lsr     rfreq, #14
+       
+@ load mixing buffers
+
+       ldr     rmixb,=mm_mixbuffer
+       ldr     rmixb,[rmixb]
+
+@ get read position
+       
+       ldr     rread, [rchan, #CHN_READ]
+
+@ calculate volume
+
+       ldrb    rvolR,[rchan, #CHN_VOL] @ volume = 0-255
+       ldrb    r0, [rchan, #CHN_PAN]   @ pan = 0-255
+       
+       rsb     r0, r0, #256
+       mul     rvolL, r0, rvolR        @ (vol*pan) = right volume
+       mov     rvolL, rvolL, lsr#8
+       add     rvolA, rvolA, rvolL     @ add to volume counter
+       rsb     r0, r0, #256
+       mul     rvolR, r0, rvolR        @ calc left volume (256-pan)*vol
+       mov     rvolR, rvolR, lsr#8
+       add     rvolA, rvolA, rvolR, lsl#16
+       
+       ldr     rmixc, [sp]             @ get mix count
+       
+@****************************************************************
+.mpm_remix_test:
+@****************************************************************
+       
+       mov     r2, #0
+       mul     r1, rmixc, rfreq        // get number of samples that will be read
+       
+       cmp     rfreq, #FETCH_THRESHOLD
+       bge     1f
+       
+       cmp     r1, #FETCH_SIZE<<12     // check if its > fetch size
+       movhi   r1, #FETCH_SIZE<<12     // if so: clamp to fetch size and set flag
+       movhi   r2, #1
+       
+1:
+//     ldr     r0, [rchan, #CHN_LEN]   // now subtract length - read to get # samples remaining
+       ldr     r0, [rsrc, #-C_SAMPLE_DATA+C_SAMPLE_LEN]
+       rsb     r0, rread, r0, lsl#SAMPFRAC
+//     sub     r0, r0, rread           // in the source
+       cmp     r1, r0                  // clamp mix count
+       movhi   r1, r0
+       bhi     .calc_mix
+       cmp     r2, #0
+       beq     .mpm_mix_full
+       
+.calc_mix:
+       
+       
+       push    {r1}            // preserve sample count
+
+       
+//--------------------------------
+.macro DIV_ITER shift
+//--------------------------------
+       cmp     r0, rfreq, lsl#\shift
+       subcs   r0, rfreq, lsl#\shift
+       addcs   r2, #1<<\shift
+.endm
+//--------------------------------
+.macro DIVIDER shift
+//--------------------------------
+       DIV_ITER \shift
+.if \shift != 0
+       DIVIDER (\shift-1)
+.endif
+.endm
+//--------------------------------
+       
+       mov     r0, r1                  // divide samples / frequency (24bit/16bit)
+       mov     r2, #0
+       
+1:     subs    r0, rfreq, lsl#16       // divide top part
+       addcs   r2, #1<<16
+       bcs     1b
+       add     r0, rfreq, lsl#16
+       
+       DIVIDER 15                      // divide the rest...
+       
+       cmp     r0, #1                  // round up result
+       adc     r0, r2, #0
+       
+//     mov     r0,r1
+//     mov     r1,rfreq
+//     swi     0x060000
+//     cmp     r1, #0
+//     addne   r0, #1
+       
+       pop     {r1}            // restore sample count
+       sub     rmixc, r0       // subtract from mixcount
+       mov     rmixcc, r0
+       b       .mpm_mix_short
+
+@------------------------------------------------------------------------
+@ SECTOR 2, MIXING
+@------------------------------------------------------------------------
+       
+@-----------------------------------------
+.mpm_mix_full:
+@-----------------------------------------
+
+@ mix all samples
+       
+       mov     rmixcc, rmixc           @ <-- move mixing count
+       mov     rmixc, #0               @ clear mixing count
+.mpm_mix_short:
+       
+@------------------------------------------------------
+.mpm_remix:
+@------------------------------------------------------
+
+@ mix samples...
+@ preserve registers
+       stmfd   sp!, {rmixc,rvolA,rchan}
+
+@ zero mixing count??
+       cmp     rmixcc, #0
+       beq     .mpm_mix_complete       @ exit -------->
+       
+       cmp     rfreq, #FETCH_THRESHOLD
+       bge     .dont_use_fetch
+       
+       /*
+       ldr     r0,=mm_fetch            // transfer samples from ROM to RAM
+       add     r1, #2<<14              // add threshold for safety
+       ldr     r2,=REG_DMA3SAD
+       str     r0, [r2, #4]
+       add     r0, r10, rread, lsr#12
+       bic     r0, #0b11
+       str     r0, [r2, #0]
+       mov     r0, #(1<<31)+(1<<26)
+       add     r0, r1, lsr#14
+       str     r0, [r2, #8]
+       b       fooo
+       */
+       
+       // [cycle timings assume 3,1 ROM waitstates]
+       
+       push    {r3-r12}
+       // r10 is SRC!
+       ldr     r0,=mm_fetch            // destination
+       add     r10, r10, rread, lsr#12 // add read offset to source
+       bic     r10, #0b11              // align to 32 bits
+       add     r1, #4<<12              // add safety threshold
+       subs    r1, #40<<12             // subtract 36
+       
+       bcc     .exit_fetch             // skip large fetch if negative
+.fetch:        ldmia   r10!, {r2-r9,r11,r14}   // read 40 samples      [44 cycles]
+       stmia   r0!, {r2-r9,r11,r14}    // write 40 samples     [11 cycles]
+       subs    r1, #40<<12             // count                [1  cycle ]
+       bcc     .exit_fetch             // exit if done         [1  cycle ]
+       ldmia   r10!, {r2-r9,r11,r14}   // read 40 samples      [44 cycles]
+       stmia   r0!, {r2-r9,r11,r14}    // write 40 samples     [11 cycles]
+       subs    r1, #40<<12             // count                [1  cycle ]
+       bcc     .exit_fetch             // exit if done         [1  cycle ]
+       ldmia   r10!, {r2-r9,r11,r14}   // read 40 samples      [44 cycles]
+       stmia   r0!, {r2-r9,r11,r14}    // write 40 samples     [11 cycles]
+       subs    r1, #40<<12             // count                [1  cycle ]
+       bcs     .fetch                  // loop if remaining    [3  cycles]
+                                       //                      [173 cycles/120 samples]
+.exit_fetch:
+
+       adds    r1, #(40<<12)-(24<<12)
+       bmi     .end_medfetch
+.medfetch:
+       ldmia   r10!, {r2-r7}           // read 24 samples      [26]
+       stmia   r0!, {r2-r7}            // write 24 samples     [7 ]
+       subs    r1, #24<<12             // count                [1 ]
+       bcc     .end_medfetch           // exit if done         [1 ]
+       ldmia   r10!, {r2-r7}           // read 24 samples      [26]
+       stmia   r0!, {r2-r7}            // write 24 samples     [7 ]
+       subs    r1, #24<<12             // count                [1 ]
+       bcc     .end_medfetch           // exit if done         [1 ]
+       ldmia   r10!, {r2-r7}           // read 24 samples      [26]
+       stmia   r0!, {r2-r7}            // write 24 samples     [7 ]
+       subs    r1, #24<<12             // count                [1 ]
+       bcs     .medfetch               // loop                 [3 ]
+.end_medfetch:                         //                      [107]
+
+       adds    r1, #24<<12             // add 24 back
+       bmi     .end_fetch              // exit if <= 0
+.fetchsmall:
+       ldr     r2, [r10], #4           // read 4 samples       [8]
+       str     r2, [r0], #4            // write 4 samples      [2]
+       subs    r1, #4<<12              // count                [1]
+       ble     .end_fetch              // exit maybe           [1]
+       ldr     r2, [r10], #4           // read 4 samples       [8]
+       str     r2, [r0], #4            // write 4 samples      [2]
+       subs    r1, #4<<12              // count                [1]
+       bgt     .fetchsmall             // exit maybe           [3]
+.end_fetch:
+       
+       pop     {r3-r12}                // restore regs
+       
+fooo:
+       
+       mov     r0, rread, lsr#12       // get read integer
+       push    {r0, rsrc}              // preserve regs
+       bic     rread, r0, lsl#12       // clear integer
+       and     r0, #0b11               // mask low bits
+       ldr     rsrc,=mm_fetch          //
+       add     rsrc, rsrc, r0          // offset source (fetch is word aligned!)
+       
+.dont_use_fetch:
+       
+       tst     rmixb, #0b11            // test alignment of work output
+       beq     .mpm_aligned
+       
+#define rsamp1 r1 
+#define        rsampa  r0
+#define rsampb r2
+                                                       @ routine to WORD align mixing sector
+       ldrb    rsampa, [rsrc, rread, lsr#SAMPFRAC]     @ load sample
+       add     rread, rread, rfreq                     @ add frequency
+       mul     rsampb, rsampa, rvolL                   @ multiply by left volume
+       ldrh    rsamp1, [rmixb]                         @ add to mixing buffer (left)
+       add     rsamp1, rsamp1, rsampb, lsr#5           
+       strh    rsamp1, [rmixb], #4
+       mul     rsampb, rsampa, rvolR                   @ multiply by right volume
+       ldrh    rsamp1, [rmixb]                         @ add to mixing buffer (right)
+       add     rsamp1, rsamp1, rsampb, lsr#5
+       strh    rsamp1, [rmixb], #2
+       sub     rmixcc, rmixcc, #1                      @ decrement mix count
+       
+#undef rsamp1
+#undef rsampa
+#undef rsampb
+
+.mpm_aligned:
+                                       // determine mixing mode
+       cmp     rvolL, rvolR            // if volume_left == volume_right then assume 'center' mixing
+       beq     .mpm_mix_ac             
+       cmp     rvolL, #0               // if left volume is zero, do 'right' mixing
+       beq     .mpm_mix_ar
+       cmp     rvolR, #0               // if right volume is zero, do 'left' mixing
+       beq     .mpm_mix_al
+       b       mmMix_ArbPanning        // otherwise do arb mixing
+.mpm_mix_al:
+       b       mmMix_HardLeft
+.mpm_mix_ar:
+       b       mmMix_HardRight
+
+@ center mixing------------
+.mpm_mix_ac:
+       cmp     rvolL, #0               // check if volume is zero
+       bne     mmMix_CenteredPanning   // mix samples if not zero
+       b       mmMix_Skip              // skip samples if zero
+.mpm_mix_complete:
+
+       cmp     rfreq, #FETCH_THRESHOLD
+       poplt   {r0, rsrc}                      // restore regs
+       addlt   rread, rread, r0, lsl#12        // add old integer to read
+       ldmfd   sp!, {rmixc,rvolA,rchan}        // restore more regs
+       
+       ldr     r1, [rsrc, #-C_SAMPLE_DATA+C_SAMPLE_LEN]
+       lsl     r1, #SAMPFRAC
+       //ldr   r1, [rchan, #CHN_LEN]           // check length against position
+       cmp     r1, rread
+       bgt     .mpm_channelfinished
+       
+//     ldr     r1, [rchan, #CHN_LOOP]          // read channel loop
+       
+       ldr     r1, [rsrc, #-C_SAMPLE_DATA+C_SAMPLE_LOOP]
+       cmp     r1, #0
+//     movs    r1, r1, lsl#8                   // mask out high byte (that is VOLUME)
+       bmi     .mpm_channel_stop               // MSB = no loop, stop channel ->
+       
+
+       sub     rread,rread,r1,lsl#(SAMPFRAC)   // subtract loop length (<<SAMPFRAC) from position
+
+       cmp     rmixc, #0                       // mix more samples?    
+       ble     .mpm_channelfinished            // no ->
+       b       .mpm_remix_test                 // yes: loop
+
+@----------------------------------------------------------------
+.mpm_channel_stop:
+@----------------------------------------------------------------
+
+@ *** END OF SAMPLE
+       mov     r1, #1<<31                      // disable channel
+       str     r1, [rchan, #CHN_SRC]
+
+@ mix zero into the rest of the buffer
+       ldr     rsrc,=mpm_nullsample            // mix zero into the rest of the buffer
+       mov     rfreq, #0                       // zero freq
+       mov     rread, #0                       // zero freq
+       movs    rmixcc, rmixc                   // mix remaining amount
+       ble     .mpm_channelfinished            // (exit if zero)
+       mov     rmixc, #0
+       mov     r1, #0
+       b       .mpm_remix                      // mix 'zero' into the rest of the data
+       
+@---------------------------------------------------------------
+.mpm_channelfinished:
+@---------------------------------------------------------------
+       
+       cmp     rmixc, #0                       // mix more samples?
+       bne     .mpm_remix_test                 // yes: loop
+       
+@  *** END OF MIXING ***
+
+       str     rread, [rchan, #CHN_READ]       // save read position
+       
+@-----------------------
+.mpm_next:
+@-----------------------
+
+       add     rchan, rchan, #CHN_SIZE         // seek to next channel
+       ldr     r0,=mm_mixch_end                // compare with ending
+       ldr     r0,[r0]                         
+       cmp     rchan, r0                       
+       bne     .mpm_cloop                      // loop if !=
+
+@----------------------------------------------------------------------------------
+@ SECTOR 3, POST-PROCESSING
+@----------------------------------------------------------------------------------
+
+#define        prmixl          r0
+#define        prwritel        r2
+#define        prwriter        r3
+#define prcount                r4
+#define prvolR         r12
+#define prvolL         r11
+#define        prsamp1         r6
+#define prsamp2                r5
+#define prsamp3                r7
+       
+       ldr     prmixl,=mm_mixbuffer
+       ldr     prmixl,[prmixl]
+       
+       ldr     prwritel,=mp_writepos
+       ldr     prwritel, [prwritel]
+       ldr     prwriter,=mm_mixlen
+       ldr     prwriter, [prwriter]
+       add     prwriter, prwritel, prwriter, lsl#1 @#MP_MIXLEN*2
+       ldmfd   sp!, {prcount}
+
+@ get volume accumulators
+       
+       mov     prvolR, rvolA, lsr#16+1
+       mov     prvolR, prvolR, lsl#3
+       
+       mov     prvolL, rvolA, lsl#16
+       mov     prvolL, prvolL, lsr#16+1
+       mov     prvolL, prvolL, lsl#3
+       
+       subs    prcount, prcount, #1
+       ble     .mpm_copy2_end
+
+@--------------------------------------------------
+.mpm_copy2:
+@--------------------------------------------------
+
+@ ***************** LEFT OUTPUT ******************
+
+       ldr     prsamp1, [prmixl], #4           @ get 2 mixed samples
+       sub     prsamp2, prsamp1, prvolL        @ convert to signed
+
+       mov     prsamp2, prsamp2, lsl#16        @ mask low hword with sign extension
+       movs    prsamp2, prsamp2, asr#16+3      @ and convert 11-bit to 8-bit
+
+       cmp     prsamp2, #-128                  @ clamp
+       movlt   prsamp2, #-128                  @
+       cmp     prsamp2, #127                   @
+       movgt   prsamp2, #127                   @
+       
+                                               @ next sample...
+       rsbs    prsamp3, prvolL, prsamp1,lsr#16 @ convert to signed
+       movs    prsamp3, prsamp3, asr#3         @ convert 11-bit to 8-bit
+
+       cmp     prsamp3, #-128                  @ clamp
+       movlt   prsamp3, #-128                  @
+       cmp     prsamp3, #127                   @
+       movgt   prsamp3, #127                   @
+       
+       and     prsamp2, prsamp2, #255          @ write to output
+       orr     prsamp2, prsamp3, lsl#8         @
+       strh    prsamp2, [prwritel], #2         @
+
+@ **************** RIGHT OUTPUT ******************
+       
+       ldr     prsamp1, [prmixl], #4           @ get 2 mixed samples
+       sub     prsamp2, prsamp1, prvolR        @ convert to signed
+       
+       mov     prsamp2, prsamp2, lsl#16        @ mask low hword and convert 11-bit to 8-bit
+       movs    prsamp2, prsamp2, asr#16+3      @
+       
+       cmp     prsamp2, #-128                  @ clamp value
+       movlt   prsamp2, #-128                  @
+       cmp     prsamp2, #127                   @
+       movgt   prsamp2, #127                   @
+       
+                                               @ next sample...
+       rsbs    prsamp3, prvolR, prsamp1,lsr#16 @ convert to signed
+       movs    prsamp3, prsamp3, asr#3         @ convert 11-bit to 8-bit
+       
+       cmp     prsamp3, #-128                  @ clamp value
+       movlt   prsamp3, #-128                  @
+       cmp     prsamp3, #127                   @
+       movgt   prsamp3, #127                   @
+       
+       and     prsamp2, prsamp2, #255          @ write to output
+       orr     prsamp2, prsamp3, lsl#8         @
+       strh    prsamp2, [prwriter], #2         @
+
+       subs    prcount, prcount, #2            @ loop
+       bgt     .mpm_copy2                      @
+
+@------------------------------------------------------------------
+
+.mpm_copy2_end:
+       ldr     r0,=mp_writepos                 @ store new write position
+       str     prwritel, [r0]
+
+@------------------------------------------------------------------
+
+       ldmfd   sp!, {r4-r11,lr}                // restore registers
+       bx      lr                              // phew!
+
+.pool
+
+@================================================================================
+@                                 MIXING ROUTINES
+@================================================================================
+
+.macro READ_AND_INCREMENT reg
+       ldrb    \reg, [rsrc, rread, lsr#SAMPFRAC]
+       add     rread, rread, rfreq
+.endm
+
+.macro READ_D reg, tmp
+       READ_AND_INCREMENT \reg
+       READ_AND_INCREMENT \tmp
+       orr     \reg, \tmp, lsl#16
+.endm
+
+.macro MIX_D vol, tmp1, tmp2
+       READ_D  \tmp1, \tmp2                    // load&combine 2 samples
+       mul     \tmp2, \tmp1, \vol              // multiply by volume
+       bic     \tmp2, \tmp2, #0x1F0000         // prepare for shift
+.endm
+
+.macro MIX_DA vol, target, tmp1, tmp2
+       MIX_D   \vol, \tmp1, \tmp2
+       add     \target, \target, \tmp2, lsr#5  // add 11-bit values
+.endm
+
+.macro MIX_DC vol, target_a, target_b, tmp1, tmp2
+       MIX_D   \vol, \tmp1, \tmp2
+       add     \target_a, \target_a, \tmp2, lsr#5      // add 11-bit values
+       add     \target_b, \target_b, \tmp2, lsr#5
+.endm
+
+.macro MIX_DB  vol_l, vol_r, target_a, target_b, tmp1, tmp2
+       READ_D  \tmp1, \tmp2
+       
+.if \target_a != 0
+       mul     \tmp2, \tmp1, \vol_l                    // multiply by volume
+       bic     \tmp2, \tmp2, #0x1F0000                 // prepare for shift
+       add     \target_a, \target_a, \tmp2, lsr#5      // add 11-bit values
+.endif
+.if \target_b != 0
+       mul     \tmp2, \tmp1, \vol_r                    // multiply by volume
+       bic     \tmp2, \tmp2, #0x1F0000                 // prepare for shift
+       add     \target_b, \target_b, \tmp2, lsr#5      // add 11-bit values
+.endif
+.endm
+
+@-----------------------------------------------------------------------------------
+mmMix_Skip:
+@-----------------------------------------------------------------------------------
+
+@ mix nothing
+       mul     r0, rmixcc, rfreq       @ read += samples * frequency
+       add     rread, rread, r0
+       b       .mpm_mix_complete
+
+@-----------------------------------------------------------------------------------
+mmMix_HardLeft:
+@-----------------------------------------------------------------------------------
+@ mix hard panned left
+
+       bl      mmMix_SingleChannel     // mix left channel
+       bgt     mmMix_Remainder         // mix remaining amount
+       b       .mpm_mix_complete       // return
+
+@-----------------------------------------------------------------------------------
+mmMix_HardRight:
+@-----------------------------------------------------------------------------------
+@ hard panned right
+
+       mov     rvolL, rvolR            // move volume
+       add     rmixb, rmixb, #4        // offset to 'right' data
+       bl      mmMix_SingleChannel     // mix routine
+       mov     rvolL, #0               // clear left volume again
+       sub     rmixb, rmixb, #4        // remove offet
+       bgt     mmMix_Remainder         // mix remaining count
+       b       .mpm_mix_complete       // return
+
+@----------------------------------------
+mmMix_SingleChannel:
+@----------------------------------------
+
+#define rsamp1 r1 
+#define rsamp2 r2
+#define rsamp3 r11
+#define        rsamp4  r12
+#define        rsampa  r0
+#define rsampb r4
+
+@ hard panned mixing (single channel mono)
+@ interleaving really cuts this method's effectiveness :(
+
+@ mix 8 samples/loop
+
+       subs    rmixcc, rmixcc, #8
+       bmi     .mpmah_8e
+.mpmah_8:
+       ldmia   rmixb, {rsamp1, rsamp2, rsamp3}         // load data [2nd word not used]
+       MIX_DA  rvolL, rsamp1, rsampa, rsampb           // mix data
+       str     rsamp1, [rmixb], #8                     // store mixed word
+       MIX_DA  rvolL, rsamp3, rsampa, rsampb           // mix data
+       str     rsamp3, [rmixb], #8                     // store mixed word
+       ldmia   rmixb, {rsamp1, rsamp2, rsamp3}         // load data [2nd word not used]
+       MIX_DA  rvolL, rsamp1, rsampa, rsampb           // mix data
+       str     rsamp1, [rmixb], #8                     // store mixed word
+       MIX_DA  rvolL, rsamp3, rsampa, rsampb           // mix data
+       str     rsamp3, [rmixb], #8                     // store mixed word
+       subs    rmixcc, rmixcc, #8                      // decrement 8 samples
+       bpl     .mpmah_8                                // loop if >= 0
+.mpmah_8e:
+
+@ mix remainder samples
+
+       adds    rmixcc, rmixcc, #8                      // fix mixing count
+       bx      lr                                      // return
+
+#undef rsamp1
+#undef rsamp2
+#undef rsamp3
+#undef rsamp4
+#undef rsampa
+#undef rsampb
+
+@----------------------------------------------------------
+mmMix_CenteredPanning:
+@----------------------------------------------------------
+       
+#define rsamp1 r1 
+#define rsamp2 r2
+#define rsamp3 r4
+#define rsamp4 r6
+#define        rsamp5  r11
+#define rsamp6 r12
+#define        rsampa  r0
+#define rsampb lr
+
+@ mix center panning (double channel mono)
+       
+       subs    rmixcc, rmixcc, #6
+       bmi     .mpmac_6e
+.mpmac_6:
+       
+       ldmia   rmixb, {rsamp1,rsamp2,rsamp3,rsamp4,rsamp5,rsamp6}      // read words
+       MIX_DC  rvolL, rsamp1, rsamp2, rsampa, rsampb                   // mix data
+       MIX_DC  rvolL, rsamp3, rsamp4, rsampa, rsampb
+       MIX_DC  rvolL, rsamp5, rsamp6, rsampa, rsampb
+       stmia   rmixb!, {rsamp1,rsamp2,rsamp3,rsamp4,rsamp5,rsamp6}     // write words
+       subs    rmixcc, rmixcc, #6                                      // count
+       bpl     .mpmac_6                                                // loop
+.mpmac_6e:
+
+       mov     rvolR, rvolL                    // restore right volume (same as left)
+       adds    rmixcc, rmixcc, #6              // fix mix count
+       bgt     mmMix_Remainder                 // mix remaining amount
+       b       .mpm_mix_complete               // finished mixing segment
+       
+#undef rsamp1
+#undef rsamp2
+#undef rsamp3
+#undef rsamp4
+#undef rsamp5
+#undef rsamp6
+#undef rsampa
+#undef rsampb
+
+@---------------------------------------------------
+mmMix_ArbPanning:              // SLOWEST!
+@---------------------------------------------------
+
+#define rsamp1 r1 
+#define rsamp2 r2
+#define rsamp3 r4
+#define rsamp4 r11
+#define        rsampa  r0
+#define rsampb r12
+#define rsamp5 r14
+
+       subs    r3, r3, #10
+       bmi     .mpmaa_10e
+.mpmaa_10:
+       
+       ldmia   rmixb, {rsamp1,rsamp2,rsamp3,rsamp4,rsamp5}     // load bufferdata
+       MIX_DB  rvolL, rvolR, rsamp1, rsamp2, rsampa, rsampb    // mix
+       MIX_DB  rvolL, rvolR, rsamp3, rsamp4, rsampa, rsampb    // mix
+       MIX_DB  rvolL, rvolR, rsamp5, 0, rsampa, rsampb         // mix half
+       stmia   rmixb!, {rsamp1,rsamp2,rsamp3,rsamp4,rsamp5}    // store bufferdata
+       ldmia   rmixb, {rsamp1,rsamp2,rsamp3,rsamp4,rsamp5}     // load bufferdata
+       mul     rsampb, rsampa, rvolR                           // mix other half
+       bic     rsampb, rsampb, #0x1F0000                       // ..
+       add     rsamp1, rsamp1, rsampb, lsr#5                   // ..
+       MIX_DB  rvolL, rvolR, rsamp2, rsamp3, rsampa, rsampb    // mix
+       MIX_DB  rvolL, rvolR, rsamp4, rsamp5, rsampa, rsampb    // mix
+       stmia   rmixb!, {rsamp1,rsamp2,rsamp3,rsamp4,rsamp5}    // store bufferdata
+       subs    rmixcc, rmixcc, #10                             // count
+       bpl     .mpmaa_10                                       // loop
+.mpmaa_10e:
+       
+       adds    rmixcc, rmixcc, #10
+       bgt     mmMix_Remainder
+       b       .mpm_mix_complete
+
+.pool
+
+#undef rsamp1  
+#undef rsamp2 
+#undef rsamp3
+#undef rsamp4
+#undef rsampa
+#undef rsampb
+
+//---------------------------------------------------------------------------
+mmMix_Remainder:
+//---------------------------------------------------------------------------
+// (slow function to mix remaining amount of samples)
+// assumes mix count isn't zero!
+
+#define rsamp1 r1
+#define rsamp2 r2
+#define rvol   r11
+#define rsampa r0
+#define rsampb r4
+
+       orr     rvol, rvolL, rvolR, lsl#16
+       
+.mix_remaining:
+/*     ldrb    rsampa, [rsrc, rread, lsr#SAMPFRAC]     @ 3 load sample
+       add     rread, rread, rfreq                     @ 1 add frequency
+       mul     rsampb, rsampa, rvolL                   @ 2 multiply by volume
+       ldrh    rsamp1, [rmixb]                         @ 3 load mix buffer entry
+       add     rsamp1, rsamp1, rsampb, lsr#5           @ 1 add
+       strh    rsamp1, [rmixb], #2                     @ 2 store
+       ldrh    rsamp1, [rmixb, #2]                     @ 3
+       mul     rsampb, rsampa, rvolR                   @ 2
+       add     rsamp1, rsamp1, rsampb, lsr#5           @ 1
+       strh    rsamp1, [rmixb, #2]                     @ 2
+*/
+       
+       ldrb    rsampa, [rsrc, rread, lsr#SAMPFRAC]     @ 3 load sample
+       add     rread, rread, rfreq                     @ 1 add frequency
+       mul     rsampb, rvol, rsampa                    @ 2 multiply by volume
+       ldrh    rsamp1, [rmixb]                         @ 3 load mix buffer entry (left)
+       bic     rsamp2, rsampb, #0xFF0000               @ 1 prep for shift
+       add     rsamp1, rsamp1, rsamp2, lsr#5           @ 1 add
+       strh    rsamp1, [rmixb], #2                     @ 2 store (left)
+       ldrh    rsamp1, [rmixb, #2]                     @ 3 load (right)
+       add     rsamp1, rsamp1, rsampb, lsr#16+5        @ 1 add values
+       strh    rsamp1, [rmixb, #2]                     @ 2 store (right)
+       
+       subs    rmixcc, rmixcc, #2                      @ 2
+       blt     .end_mix_remaining                      @ 1/exit
+
+/*     ldrb    rsampa, [rsrc, rread, lsr#SAMPFRAC]     @ load sample
+       add     rread, rread, rfreq                     @ add frequency
+       mul     rsampb, rsampa, rvolL                   @ multiply by volume
+       ldrh    rsamp1, [rmixb]                         @ load mix buffer entry
+       add     rsamp1, rsamp1, rsampb, lsr#5           @ add
+       strh    rsamp1, [rmixb], #4                     @ store
+       ldrh    rsamp1, [rmixb]
+       mul     rsampb, rsampa, rvolR
+       add     rsamp1, rsamp1, rsampb, lsr#5
+       strh    rsamp1, [rmixb], #2
+*/
+
+       ldrb    rsampa, [rsrc, rread, lsr#SAMPFRAC]     @ 3 load sample
+       add     rread, rread, rfreq                     @ 1 add frequency
+       mul     rsampb, rvol, rsampa                    @ 2 multiply by volume
+       ldrh    rsamp1, [rmixb]                         @ 3 load mix buffer entry (left)
+       bic     rsamp2, rsampb, #0xFF0000               @ 1 prep for shift
+       add     rsamp1, rsamp1, rsamp2, lsr#5           @ 1 add
+       strh    rsamp1, [rmixb], #4                     @ 2 store (left)
+       ldrh    rsamp1, [rmixb]                         @ 3 load (right)
+       add     rsamp1, rsamp1, rsampb, lsr#16+5        @ 1 add values
+       strh    rsamp1, [rmixb], #2                     @ 2 store (right)
+
+       bgt     .mix_remaining
+.end_mix_remaining:
+       
+       b       .mpm_mix_complete
+       
+#undef rsamp1
+#undef rsamp2
+#undef rvol
+#undef rsampa
+#undef rsampb
+
+@============================================================================
+@                                 END OF MIXER
+@============================================================================
+
+       .TEXT
+       .THUMB
+       .ALIGN 2
+
+/****************************************************************************
+ * mmMixerSetSource( channel, p_sample )
+ *
+ * Set channel source
+ ****************************************************************************/
+                                       .thumb_func
+mmMixerSetSource: 
+
+       mov     r2, #CHN_SIZE           // get channel pointer from index
+       mul     r0, r2                  //
+       ldr     r2,=mm_mixchannels      //
+       ldr     r2, [r2]                //
+       add     r0, r2                  //
+       
+       add     r1, #C_SAMPLE_DATA      // set sample data address
+       str     r1, [r0, #CHN_SRC]      //
+       
+       mov     r1, #0                  // reset read position
+       str     r1, [r0, #CHN_READ]     //
+       
+       bx      lr
+
+/****************************************************************************
+ * mmMixerSetRead( channel, value )
+ *
+ * Set channel read position
+ ****************************************************************************/
+                                       .thumb_func
+mmMixerSetRead:
+
+       mov     r2, #CHN_SIZE           // get channel pointer from index
+       mul     r0, r2                  //
+       ldr     r2,=mm_mixchannels      //
+       ldr     r2,[r2]                 //
+       add     r0, r2                  //
+       
+       str     r1, [r0, #CHN_READ]     // store new offset
+       bx      lr                      //
+
+/****************************************************************************
+ * mmMixerSetFreq
+ *
+ * Set channel mixing rate
+ ****************************************************************************/
+                                       .thumb_func
+mmMixerSetFreq:
+
+//     push    {r3, r4}                // FIXME: why would you preserve r3?
+       
+       mov     r2, #CHN_SIZE           // get channel pointer from index
+       mul     r0, r2, r0              // 
+       ldr     r2,=mm_mixchannels      //
+       ldr     r2,[r2]                 //
+       add     r0, r0, r2              //
+       
+       lsl     r1, #2 // ...
+       
+       strh    r1, [r0, #CHN_FREQ]
+       bx      lr
+       
+/*
+       ldr     r4,=mm_freqscalar       // r4=scale
+       ldr     r4,[r4]                 //
+
+       add     r2, pc, #0              // switch to arm for a nice long multiply
+       bx      r2
+       
+       .ARM
+       
+       //------------------------------------
+       // fix frequency to match mixing rate
+       // a = specified frequency
+       // hz = a*2y13 / pr
+       //------------------------------------
+       
+       umull   r3, r2, r1, r4          // long multiply
+       add     r3, r3, #32768          // shift, etc..
+       mov     r3, r3, lsr#16          //
+       orr     r3, r3, r2, lsl#16      //
+       
+       str     r3, [r0, #CHN_FREQ]     // set chan frequency
+       
+       ldmfd   sp!, {r3,r4}            // pop registers
+       bx      lr                      // return
+       */
+       .THUMB
+
+/****************************************************************************
+ * mmMixerMulFreq( channel, value )
+ *
+ * Scale mixing frequency
+ ****************************************************************************/
+                                       .thumb_func
+mmMixerMulFreq:
+
+       mov     r2, #CHN_SIZE           // get channel pointer from index
+       mul     r0, r2                  //
+       ldr     r2,=mm_mixchannels      //
+       ldr     r2, [r2]                //
+       add     r0, r2                  //
+
+       ldr     r3, [r0, #CHN_FREQ]     // scale
+       mul     r3, r1                  //
+       lsr     r3, #10                 //
+       str     r3, [r0, #CHN_FREQ]     //
+       bx      lr
+
+/****************************************************************************
+ * mmMixerStopChannel( channel )
+ *
+ * Stop mixing channel
+ ****************************************************************************/
+                                       .thumb_func
+mmMixerStopChannel:
+
+       mov     r1, #CHN_SIZE           // get channel pointer from index
+       mul     r0, r1                  //
+       ldr     r1,=mm_mixchannels      //
+       ldr     r1,[r1]                 //
+       add     r0, r1                  //
+               
+       mov     r1, #1                  // set MSB (disable) of source
+       lsl     r1, #31                 //
+       str     r1, [r0]                //
+       bx      lr
+
+/****************************************************************************
+ * mmMixerChannelActive( channel )
+ *
+ * Test if mixing channel is active
+ ****************************************************************************/
+                                       .thumb_func
+mmMixerChannelActive:
+
+       mov     r1, #CHN_SIZE           // get channel pointer from index
+       mul     r0, r1                  //
+       ldr     r1,=mm_mixchannels      //
+       ldr     r1,[r1]                 //
+       add     r0, r1                  //
+       
+mp_Mixer_ChannelEnabledA:
+       ldr     r0, [r0, #CHN_SRC]      // nonzero (-1) = enabled
+       asr     r0, #31                 // zero         = disabled
+       mvn     r0, r0                  //
+       bx      lr
+
+/****************************************************************************
+ * mmMixerSetVolume( channel, volume )
+ * 
+ * Set channel volume
+ ****************************************************************************/
+                                       .thumb_func
+mmMixerSetVolume:
+
+       mov     r2, #CHN_SIZE           // get channel pointer from index
+       mul     r0, r2                  //
+       ldr     r2,=mm_mixchannels      //
+       ldr     r2,[r2]                 //
+       add     r0, r2                  //
+       
+       strb    r1, [r0, #CHN_VOL]      // set volume
+       
+       bx      lr
+       
+/****************************************************************************
+ * mmMixerSetPan( channel, panning )
+ *
+ * Set channel panning
+ ****************************************************************************/
+                                       .thumb_func
+mmMixerSetPan:
+
+       mov     r2, #CHN_SIZE           // get channel pointer from index
+       mul     r0, r2                  //
+       ldr     r2,=mm_mixchannels      //
+       ldr     r2,[r2]                 //
+       add     r0, r2                  //
+       
+       strb    r1, [r0, #CHN_PAN]      // set panning
+       bx      lr
+       
+/****************************************************************************
+ * mmMixerInit( system )
+ *
+ * Initialize mixer
+ ****************************************************************************/
+                                       .thumb_func
+mmMixerInit:
+
+       ldr     r2,=mm_mixbuffer
+       ldr     r1, [r0,#MM_GBA_SYSTEM_ACH_COUNT]
+       str     r1, [r2,#16]
+       
+       mov     r3, #CHN_SIZE
+       mul     r1, r3
+       ldr     r3, [r0,#MM_GBA_SYSTEM_MIXCH]
+       str     r3, [r2,#4]
+       add     r3, r1
+       str     r3, [r2,#20]
+       ldr     r1, [r0,#MM_GBA_SYSTEM_MIXMEM]
+       str     r1, [r2,#0]
+       ldr     r1, [r0,#MM_GBA_SYSTEM_WAVEMEM]
+       str     r1, [r2,#8]
+       ldr     r1, [r0,#MM_GBA_SYSTEM_MODE]
+       lsl     r1, #1
+       adr     r3, mp_mixing_lengths
+       ldrh    r3, [r3,r1]
+       str     r3, [r2,#12]
+       adr     r3, mp_rate_scales
+       ldrh    r3, [r3, r1]
+       str     r3, [r2, #24]
+       adr     r3, mp_timing_sheet
+       ldrh    r3, [r3, r1]
+       str     r3, [r2, #28]
+       adr     r3, mp_bpm_divisors
+       lsl     r1, #1
+       ldr     r3, [r3,r1]
+
+       ldr     r2,=mm_bpmdv
+       str     r3, [r2,#0]
+
+       
+       ldr     r0,=mm_wavebuffer               @ clear wave buffer
+       ldr     r0,[r0]
+       ldr     r1,=mm_mixlen
+       ldr     r1, [r1]
+       mov     r2, #0                          @ ..
+.mpi_loop1:                                    @ ..
+       stmia   r0!, {r2}                       @ ..
+       sub     r1, r1, #1                      @ ..
+       bne     .mpi_loop1                      @ ..
+       
+       ldr     r0,=mp_mix_seg                  @ reset mixing segment
+       strb    r2, [r0]                        @ ..
+       
+       ldr     r0,=mm_mixchannels              @ disable mixing channels
+       ldr     r1,[r0,#12]@ nchannels
+       ldr     r0,[r0]
+       ldr     r3,=1<<31
+.mpi_loop2:
+       str     r3, [r0, #CHN_SRC]
+       add     r0, #CHN_SIZE
+       sub     r1, #1
+       bne     .mpi_loop2
+
+       ldr     r0,=mmVBlank                    @ enable vblank routine
+       ldr     r1,=0xE1A00000                  @ ..
+       str     r1, [r0]                        @ ..
+       
+       ldr     r0,=REG_SGFIFOA                 @ clear fifo data
+       str     r2, [r0]                        @ ..
+       str     r2, [r0, #4]                    @ ..
+       ldr     r0,=REG_SOUNDCNT_H              @ reset direct sound
+       strh    r2, [r0]                        @ ..
+       ldr     r1,=0x9A0C                      @ setup sound [DIRECT SOUND A/B reset,timer0,A=left,B=right,volume=100%]
+       strh    r1, [r0]                        @ ..
+       ldr     r0,=REG_DMA1SAD                 @ setup DMA source addresses (playback buffers)
+       ldr     r1,=mm_wavebuffer
+       ldr     r2, [r1, #4]@mixlen
+       ldr     r1, [r1]
+       
+       @ldr    r1,=mp_playbuffer_l             @ ..
+       str     r1, [r0]                        @ ..
+
+       add     r1,r2
+       add     r1,r2
+
+@      ldr     r1,=mp_playbuffer_r             @ ..
+       str     r1, [r0, #12]                   @ ..
+       
+       ldr     r1,=REG_SGFIFOA                 @ setup DMA destination (sound fifo)
+       str     r1, [r0, #4]                    @ ..
+       add     r1, #4                          @ ..
+       str     r1, [r0, #16]                   @ ..
+       
+       ldr     r1,=0xB6000000                  @ enable DMA (enable,fifo request,32-bit,repeat)
+       str     r1, [r0, #8]                    @ ..
+       str     r1, [r0, #20]                   @ ..
+       
+       ldr     r0,=REG_SOUNDCNT_X              @ master sound enable
+       mov     r1, #0x80                       @ ..
+       strh    r1, [r0]                        @ ..
+       
+       ldr     r0,=REG_VCOUNT                  @ wait for new frame
+.mpi_vsync:                                    @ ..
+       ldrh    r1, [r0]                        @ skip current vblank period
+       cmp     r1, #160                        @ ..
+       bge     .mpi_vsync                      @ ..
+.mpi_vsync2:
+       ldrh    r1, [r0]                        @ wait for new one
+       cmp     r1, #160                        @ ..
+       blt     .mpi_vsync2                     @ ..
+       
+.mpi_vsync_2:                                  @ pass#2
+       ldrh    r1, [r0]                        @ skip current vblank period
+       cmp     r1, #160                        @ ..
+       bge     .mpi_vsync_2                    @ ..
+.mpi_vsync2_2:
+       ldrh    r1, [r0]                        @ wait for new one
+       cmp     r1, #160                        @ ..
+       blt     .mpi_vsync2_2                   @ ..
+       
+       ldr     r0,=REG_TM0CNT                          @ enable sampling timer
+       ldr     r1,=mm_timerfreq
+       ldr     r1,[r1]
+       mov     r2, #0x80
+       lsl     r2, #16
+       orr     r1, r2
+       @ldr    r1,=(-MP_TIMERFREQ&0xFFFF) | (0x80<<16) @ ..
+       str     r1, [r0]                                @ ..
+       bx      lr                                      @ finished
+       
+
+// round(rate / 59.737)
+.align 2
+mp_mixing_lengths:
+       .hword  136,  176,  224,  264,  304,  352,  448,  528
+       @      8khz,10khz,13khz,16khz,18khz,21khz,27khz,32khz
+
+.align 2
+//mp_freq_scales:              @ (16khz -> real)
+//     .hword 33056, 25536, 20064, 17024, 14784, 12768
+
+// 15768*16384 / rate
+mp_rate_scales:
+       .hword 31812, 24576, 19310, 16384, 14228, 12288,  9655,  8192
+       @       8khz, 10khz, 13khz, 16khz, 18khz, 21khz, 27khz, 32khz
+       @       8121, 10512, 13379, 15768, 18157, 21024, 26758, 31536,
+
+.align 2
+// gbaclock / rate
+mp_timing_sheet:
+       .hword -2066,-1596,-1254,-1064, -924, -798, -627, -532
+       @       8khz,10khz,13khz,16khz,18khz,21khz,27khz,32khz
+
+.align 2
+// rate * 2.5
+mp_bpm_divisors:
+       .word 20302,26280,33447,39420,45393,52560,66895,78840
+
+       
+.end