gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

ASM > Optimization ideas for software mixing.

#171736 - Ruben - Fri Dec 18, 2009 10:42 am

Hi.

So I decided to see if you guys have any optimization ideas for my GBA mixer, which I intend to also port to the DS for 30 channel output. So... here's the latest code. (It's a bit messy due to tabs and quite long >_<')

Code:
/**********************************************/

#include "sasInternal.inc"

/**********************************************/

.global sasMain

/**********************************************/

.text
.align
.thumb
.thumb_func

sasMain:
    ldr     r0, =sasVar
    ldrb    r1, [r0]
    lsr     r1, #0x01   @ if(mixer == off)
    bcc     2f          @   return
    push    {lr}
    ldr     r0, =sasUpdate
    bl      1f          @ update songs
    pop     {r0}
    mov     lr, r0
    ldr     r0, =sasARM @ could probably incorporate
                        @ into sasUpdate but meh.
1:  bx      r0
2:  bx      lr

.align
.size sasMain, .-sasMain

/**********************************************/

.section .iwram, "ax", %progbits
.align
.arm

sasARM:
    stmfd    sp!, {r4-r11,lr}
   
@ clear 16-bit interleaved stereo mixing buffer
    ldr        r0, =.LMixBuffer
    ldr        r1, =SAS_BUFSIZE / 8
    mov        r2, #0x00
    mov        r3, #0x00
    mov        r4, #0x00
    mov        r5, #0x00
    mov        r6, #0x00
    mov        r7, #0x00
    mov        r8, #0x00
    mov        r9, #0x00
1:  subs       r1, r1, #0x01
    stmhsia    r0!, {r2-r9}
    bne        1b
   
    ldr        ip, =sasVar
    ldmia      ip, {r0-r2}              @ {xxPPVVxx,LLLLxxxx,RRRRRRRR}
   
    mov        r0, r0, lsr #0x08        @ 00xxPPVV
    bic        r0, r0, #0xFF0000        @ 0000PPVV
    orr        r0, r0, r0, lsl #0x08    @ 00PPxxVV
    bic        r0, r0, #0xFF00          @ 00PP00VV
    subs       r0, r0, #0x010000        @ if(--pp < 0)
    addmi      r0, r0, #0x010000        @   pp = 0
    mov        r1, r1, lsr #0x10        @ 0000LLLL
    ldr        r3, =.LMixBuffer
    ldr        r4, =sasChan

/******************************\
 * r0-L16 * Volume            *
 * r0-H16 * Polyphony         *
 * r1-X32 * Buffer length     *
 * r2-X32 * Reciprocal        *
 * r3-X32 * Mixing buffer     *
 * r4-X32 * Channel           *
\******************************/

.LOLChan:
    ldr        r5, [r4], #SAS_CHANSIZE
    tst        r5, #sasOn + sasNoteOn
    beq        .LOLChanContinue @ if(channel == off || no note on) continue
    stmfd      sp!, {r0-r4}
    sub        r4, r4, #SAS_CHANSIZE

.LILChanEnv:
    mov        r6, r5, lsr #0x04
    and        r6, r6, #0x0F
    ldr        r7, [r4, #0x18]                  @ env {a,d,s,r}, 8-bit
    mov        r9, #0xFF
    and        r8, r9, r5, lsr #0x08            @ env_val
    bic        r5, r5, r8, lsl #0x08            @ env_val = 0
    tst        r5, #sasNoteOn
    bne        .LILChanNoteOn
    tst        r5, #sasNoteOff
    bne        .LILChanNoteOff

.LILChanEnv000:
    cmp        r6, #sasAttack                    @ if(env_state != attack)
    bne        .LILChanEnv001                    @   continue
   
    and        ip, r9, r7
    add        r8, r8, ip                        @ env += env_val
    cmp        r8, #0xFF
    ble        .LILChanEnvEnd                    @ if(env <= 255) goto mix
   
    tst        r7, #0xFF00                       @ if(env[decay]) {
    movne      r8, #0xFF                         @   env_val   = 255,
    movne      r6, #sasDecay                     @   env_state = decay
    andeq      r8, r9, r7, lsr #0x10             @ } else {
    moveq      r6, #sasSustain                   @   env_val = sustain_val, env_state = sustain
    b          .LILChanEnvEnd                    @ } goto mix

.LILChanEnv001:
    cmp        r6, #sasDecay
    bne        .LILChanEnv002
   
    and        ip, r9, r7, lsr #0x08
    subs       r8, r8, ip                       @ if(env_val -= env[decay] <= 0)
    ble        .LIllegalChanState               @   chan->stop()
    and        ip, r9, r7, lsr #0x10
    cmp        r8, ip                           @ if(env_val <= env[sustain])
    movle      r6, #sasSustain                  @   env_state = sustain
    b          .LILChanEnvEnd

.LILChanEnv002:
    cmp        r6, #sasSustain
    bne        .LILChanEnv003
    and        r8, r9, r7, lsr #0x10            @ env = env[sustain]
    b          .LILChanEnvEnd

.LILChanEnv003:
    cmp        r6, #sasRelease
    bne        .LIllegalChanState
    subs       r8, r8, r7, lsr #0x18            @ if(env_val -= env[release] <= 0)
    bgt        .LILChanEnvEnd
    ble        .LIllegalChanState               @   chan->stop()

.LILChanNoteOn:
    ldr     r11, [ r4, #0x20]    @ &wave
   
    ldr     r12, [r11, #0x0C]   @ wave->length
#if SAS_MIXMODE == SAS_MIXMODE_NN
    rsb     r14,  r12, #0x00    @ -length (negated for mixing trick)
    str     r14, [ r4, #0x10]
#else
    str     r12, [ r4, #0x10]
#endif
    add     r14,  r12, r11      @ &wave + length
    add     r14,  r14, #0x10    @ wave->data + length
    str     r14, [ r4, #0x04]
    ldrb    r14, [r11]
    ands    r14, r14, #sasLooped
    ldrne   r14, [r11, #0x08]   @ wave->loop
    rsbne   r14, r14, r12       @   store loop size
    str     r14, [ r4, #0x14]   @ else store 0
    mov     r14, #0x00
    str     r14, [ r4, #0x08]   @ remainder = 0
   
    bic      r5, r5, #0xFF      @ env_stat = 0, stat = 0
    orr      r5, r5, #sasOn     @ stat = on
    tst      r7, #0xFF
    movne    r6, #sasAttack
    movne    r8, #0x00
    bne     .LILChanEnv000
    tst      r7, #0xFF00
    movne    r6, #sasDecay
    movne    r8, #0xFF
    bne     .LILChanEnv001
    tst      r7, #0xFF0000
    movne    r6, #sasSustain
    bne     .LILChanEnv002
    tst      r7, #0xFF000000
    movne    r6, #sasRelease
    movne    r8, #0xFF
    bne     .LILChanEnv003
    beq     .LIllegalChanState

.LILChanNoteOff:
    tst        r7, #0xFF000000
    movne      r6, #sasRelease
    bne        .LILChanEnv003
    beq        .LIllegalChanState

.LILChanEnvEnd:
    orr        r5, r5, r8, lsl #0x08                @ env_val = new env_val
    bic        r5, r5, #0xF0+sasNoteOn+sasNoteOff   @ env_stat = 0, clear note on/off
    orr        r5, r5, r6, lsl #0x04                @ env_stat = new env_stat
    strh       r5, [r4]
   
    mov        ip, r5, lsr #0x18                @ panning
    mov        r5, r5, lsr #0x10
    and        r5, r5, #0xFF                    @ volume
    mul        r5, r8, r5                       @ volume * env
    and        r0, r0, #0xFF
    mul        r6, r5, r0                       @ volume * env * mvol
    movs       r5, r6, lsr #0x08 + 7
    beq        .LIllegalChanState
    rsb        r6, ip, #0x7F
    mul        r6, r5, r6                       @ vol * (127 - pan)
    mov        r6, r6, lsr #0x07
    mul        r5, ip, r5                       @ vol * (  0 + pan)
    mov        r5, r5, lsr #0x07
    orr        r5, r5, r6, lsl #0x10            @ lvol + rvol<<16
   
    ldmib    r4, {r7-r11}
    umull    r2, r9, r9, r2
#if SAS_MIXMODE == SAS_MIXMODE_NN
    cmp     r11, #0x00
    beq     .LILChanMixOS

.macro MixFL or
    ldrsb   r14, [r7, r10]
    mla     \or,  r5, r14, \or @ smp*vols + out
    adds     r8,  r8,  r2      @ sub += subinc
    adcs    r10, r10,  r9      @ adc to true position
    subpl   r10, r10, r11      @ if <0 loop
.endm

.macro MixOS or
    ldrsb   r14, [r7, r10]
    mla     \or,  r5, r14, \or @ smp*vols + out
    adds     r8,  r8,  r2
    adcs    r10, r10,  r9
    bpl     .LOutOfData        @ kill on finish
.endm

.LILChanMixFL:
    ldmia    r3 , {r0,r4,r6,ip}
    MixFL    r0
    MixFL    r4
    MixFL    r6
    MixFL    ip
    stmia    r3!, {r0,r4,r6,ip}
    subs     r1, r1, #0x04
    bne      .LILChanMixFL
    beq      .LILChanMixEnd

.LILChanMixOS:
    ldmia    r3 , {r0,r4,r6,ip}
    MixOS    r0
    MixOS    r4
    MixOS    r6
    MixOS    ip
    stmia    r3!, {r0,r4,r6,ip}
    subs     r1, r1, #0x04
    bne      .LILChanMixOS

#else

    str        r11, .LoopSize         @ store loop length
    add        r11,  r7, #0x01        @ src + 1 (for interpolation)
    mov        r2,  r2, lsr #0x20-23  @ cfreq<<23/sfreq
    orr        r2,  r2, r9, lsl #0x17 @ (add integer portion << 23)

.macro Mix or
    ldrsb    r14, [ r7, -r10]           @ s0
    ldrsb     r9, [r11, -r10]           @ s1
    sub       r9,   r9,  r14            @ s1-s0
    mul       r9,   r8,   r9            @ subpos(s1-s0)
    add      r14,  r14,   r9, asr #0x17 @ f(s1-s0) ~= s0.sub
    mla      \or,   r5,  r14, \or       @ smp*vols + out
    add       r8,   r8,   r2            @ pos += inc
    subs     r10,  r10,   r8, lsr #0x17 @ smpleft -= pos >> 23
    bic       r8,   r8, #0x3F800000     @ smpleft -= integer
    blls    .LNoDat                     @ no data left
.endm

.LILChanMix:
    ldmia    r3 , {r0,r4,r6,ip}
    Mix      r0
    Mix      r4
    Mix      r6
    Mix      ip
    stmia    r3!, {r0,r4,r6,ip}
    subs     r1, r1, #0x04
    bne      .LILChanMix

#endif

.LILChanMixEnd:
    ldr        r0, [sp, #0x04*4]
    str        r8, [r0, #0x08-SAS_CHANSIZE]
    str        sl, [r0, #0x10-SAS_CHANSIZE]

.LILChanDone:
    ldmfd    sp!, {r0-r4}

.LOLChanContinue:
    subs    r0, r0, #0x010000
    bhs     .LOLChan

/******************************\
 * r0-X32 * Destination       *
 * r1-X32 * Buffer length     *
 * r2-X32 * Mask 00FF00FF     *
 * r3-X32 * Mixing buffer     *
\******************************/

.LDownsample:
    ldr        r0, =sasBuffer
    ldr        r2, =sasVar
    ldrb       r2, [r2, #0x03]
    cmp        r2, #0x02  @ mix to buffer 2?
    addeq      r0, r0, r1 @   yes.
    mov        r2, #0xFF
    orr        r2, r2, r2, lsl #0x10
   
1:  ldmia      r3!, {r4-r7}
   
    and        r4, r2, r4, lsr #0x08
    and        r5, r2, r5, lsr #0x08
    orr        r4, r4, r5, lsl #0x08
   
    and        r6, r2, r6, lsr #0x08
    and        r7, r2, r7, lsr #0x08
    orr        r6, r6, r7, lsl #0x08
   
    eor        r6, r6, r4, lsr #0x10
    eor        r4, r4, r6, lsl #0x10
    eor        r6, r6, r4, lsr #0x10
   
    str        r6, [r0, #SAS_BUFSIZE*2]
    str        r4, [r0], #0x04
   
    subs       r1, r1, #0x04
    bne        1b

.LExit:
    ldmfd      sp!, {r4-r11,lr}
    bx        lr

.LIllegalChanState:
    mov     r5, #0x00
    strb    r5, [r4]
    b       .LILChanDone

#if SAS_MIXMODE == SAS_MIXMODE_LI

.LoopSize:    .word 0
.LNoDat:
    ldr        r9, .LoopSize
    adds       sl, sl, r9
    movgt      pc, lr
    ldr        r4, [sp, #0x04*4]
    str        r9, [r4, #-SAS_CHANSIZE]
    b         .LILChanDone

#endif
#if SAS_MIXMODE == SAS_MIXMODE_NN

.LOutOfData:
    ldr      r4, [sp, #0x04*4]
    strb    r11, [r4, #-SAS_CHANSIZE]
    b      .LILChanDone

#endif

.LMixBuffer:
    .space    SAS_BUFSIZE * 4

.align
.size sasARM, .-sasARM

/**********************************************\
 * EOF                                        *
\**********************************************/


Any tips, specially on the hotspot (mixing)?