/**************************************************************************** * * * NDSAUDIO * * * * Copyright (c) 2009, Mukunda Johnson (mukunda@mukunda.com) * * * * 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. * *--------------------------------------------------------------------------* ****************************************************************************/ // audio interface/mixer #include "na_macro.inc" #include "na_hwregs.inc" #include "na_sample.inc" #define breakp mov r11,r11 .global na_ch_mask /******************************************************************** * definitions ********************************************************************/ .equ NA_NVOICES, 32 //; number of voices .equ V_READ_FRAC, 10 //; soft sampler fraction size .equ CLK_DIV, 524288 //; 2^24 / 32 (based on 32khz) .equ SFRAC, 10 //; sampler fraction /******************************************************************** * voice struct * ********************************************************************/ STRUCT V24 V_SAMP V8 V_START // lower 7bit=target panning, msb=startbit V16 V_TPAN V16 V_CPAN V16 V_TVOL V16 V_CVOL V32 V_READ V16 V_FREQ V16 V_RESERVED // filters allowed in mode1: V16 V_FILTER // filter parameters (cache) V16 V_A0 // coefficients V16 V_B0 V16 V_B1 V16 V_S1 // previous samples V16 V_S2 ENDSTRUCT V_SIZE //----------------------------------------------------------------------------- .BSS .ALIGN //----------------------------------------------------------------------------- /******************************************************************** * voice data * ********************************************************************/ private data na_voices .space V_SIZE * NA_NVOICES /******************************************************************** * channel mask * * bitmask of which channels are unlocked ********************************************************************/ V32 na_ch_mask /******************************************************************** * channel mask * * bitmask of which channels are unlocked ********************************************************************/ V32 na_ramp_speed /******************************************************************** * output position for m2 * ********************************************************************/ V32 m2_output_position /******************************************************************** * "mod 3" frame counter for m2 * ********************************************************************/ V8 m2_mod3 /******************************************************************** * current audio mode * * 0/1/2 = MODE0/MODE1/MODE2 ********************************************************************/ V8 na_mode public data naxAudioResetFlag .space 1 .ALIGN /******************************************************************** * data buffer * * (this buffer is "unioned" between audio modes) ********************************************************************/ private data na_buffer .space 8544 na_buffer_end: /****************************************************************************** * MODE1 (interpolated) ******************************************************************************/ /******************************************************************** * sample fetch buffer size (256 samples, plus extra padding space) * ********************************************************************/ .equ M1_FETCH_SIZE, 256 .equ M1_FETCH_PADDING, 32 /******************************************************************** * MODE1 shadow buffer * ********************************************************************/ STRUCT M1_SH_VLEVEL: V8 M1_SH_VMUL V8 M1_SH_VSHIFT V8 M1_SH_PLEVEL V8 M1_SH_RESERVED ENDSTRUCT M1_SH_LEN /******************************************************************** * MODE1 data buffer definition * ********************************************************************/ STRUCT // 512 bytes/channel (128 samples, double buffered) M1_OUTPUT: .space 16 * 512 // buffer for storing temporary sample source copy M1_FETCH: .space M1_FETCH_SIZE + M1_FETCH_PADDING M1_SHADOW: .space M1_SH_LEN * 16 ENDSTRUCT M1_BUFFER_SIZE /****************************************************************************** * MODE2 (extended voices) * ******************************************************************************/ /******************************************************************** * MODE2 buffer sizes (specified in 'samples') * ********************************************************************/ .equ M2_FETCH_SIZE, 256 .equ M2_FETCH_PADDING, 16 .equ M2_BUFFERLENGTH, 172 .equ M2_CHUNKMAX, 88 /******************************************************************** * MODE2 shadow buffer definition * ********************************************************************/ STRUCT V32 M2_SH_CNT V32 M2_SH_SRC V16 M2_SH_TMR V16 M2_SH_PNT V32 M2_SH_LEN ENDSTRUCT M2_SH_SIZE /******************************************************************** * MODE2 data buffer definition * ********************************************************************/ STRUCT // 16-bit stereo output buffer M2_MIX_OUTPUT: .space M2_BUFFERLENGTH * 4 // mixing work memory M2_MIX_WMEM: .space M2_CHUNKMAX * 4 // sample fetch M2_FETCH: .space M2_FETCH_SIZE + M2_FETCH_PADDING // hardware shadow buffer //; *** this is shared with MODE0 M2_SHADOW: .space M2_SH_LEN ENDSTRUCT M2_BUFFER_SIZE //============================================================================= .TEXT ARM_CODE //============================================================================= /****************************************************************************** * slideMixingLevels( throttle ) * * slide volume and panning levels towards target levels for all voices ******************************************************************************/ private function slideMixingLevels //----------------------------------------------------------------------------- push {r4, r5, lr} // ldr r1,=na_voices+V_TPAN // r1 = voices ldr r4,=na_mode // r4 = voice counter ldrb r4, [r4] // (only 16 if not mode2) cmp r4, #2 // moveq r4, #NA_NVOICES // movne r4, #16 // .macro doSlide //----------------------------------------------------------------------------- cmp r3, r2 // if level < target bgt 1f // add r3, r0 // level += throttle cmp r3, r2 // +saturate movgt r3, r2 // b 2f // else 1: sub r3, r0 // level -= throttle cmp r3, r2 // +saturate movlt r3, r2 // 2: // //----------------------------------------------------------------------------- .endm //----------------------------------------------------------------------------- .sml_loop: //----------------------------------------------------------------------------- ldr r2, [r1], #2 //; slide panning mov r3, r2, lsr#16 //; r3=cpan bic r2, r3, lsl#16 //; r2=tpan doSlide //; strh r3, [r1], #2 //; write cpan, r1=tvol //----------------------------------------------------------------------------- ldr r2, [r1], #2 //; slide volume mov r3, r2, lsr#16 //; r2=target bic r2, r3, lsl#16 //; r3=current doSlide //; strh r3, [r1], #2+V_SIZE-8 //; write cvol, r1=next tpan //----------------------------------------------------------------------------- subs r4, #1 //; next voice bne .sml_loop // //----------------------------------------------------------------------------- pop {r4, r5, lr} //; return bx lr /****************************************************************************** *; void naxHardwareUpdates() *; *; do critical hardware updates. (call at start of timer tick!) ******************************************************************************/ public function naxHardwareUpdates //----------------------------------------------------------------------------- ldr r0,=na_mode //; get mixing mode ldrb r0, [r0] //; push {r4-r5} //; push regs ldr r4,=REG_SOUND0CNT //; r4 = sound registers ldr r3,=na_ch_mask //; r3 = hardware channel mask ldrh r3, [r3] cmp r0, #1 bne .nmp_mode2 //----------------------------------------------------------------------------- .nmp_mode1: //----------------------------------------------------------------------------- //; *** mode1: update volume + panning ldr r5,=na_buffer+M1_SHADOW //----------------------------------------------------------------------------- .nmp1_next: ldr r0, [r5], #4 //; r0 = shadow CNT orr r0, #0b10101000<<24 //; add enable + 16-bit + "loop" movs r3, r3, lsr#1 //; shfit out mask bit strcs r0, [r4] //; write cnt if bit is set add r4, #16 //; point to next channel bne .nmp1_next //; loop if bits remain in mask //----------------------------------------------------------------------------- pop {r4-r5} bx lr //----------------------------------------------------------------------------- .nmp_mode2: //----------------------------------------------------------------------------- //; *** mode0/2: update everything ldr r5,=na_buffer+M2_SHADOW b .nmp2_next //----------------------------------------------------------------------------- .nmp2_process: //----------------------------------------------------------------------------- ldr r1, [r5, #M2_SH_SRC] //; read SRC cmp r1, #0 // beq .skip_keyon //; (keyon if nonzero) //----------------------------------------------------------------------------- mov r0, #0 //; clear hardware CNT str r0, [r4, #CSOUND_CNT] //; reset SRC entry str r0, [r5, #M2_SH_SRC] // bmi .skip_keyon //----------------------------------------------------------------------------- str r1, [r4, #CSOUND_SAD] //; setup hardware source ldrh r2, [r5, #M2_SH_PNT] // strh r2, [r4, #CSOUND_PNT] // ldr r2, [r5, #M2_SH_LEN] // str r2, [r4, #CSOUND_LEN] // //----------------------------------------------------------------------------- ldr r1, [r5, #M2_SH_CNT] //; set complete CNT str r1, [r4, #CSOUND_CNT] // //----------------------------------------------------------------------------- .skip_keyon: ldrh r1, [r5, #M2_SH_TMR] //; copy TMR strh r1, [r4, #CSOUND_TMR] // //----------------------------------------------------------------------------- ldr r1, [r5], #M2_SH_SIZE //; write VOL+SHIFT, PANNING lsr r0, r1, #16 // strb r0, [r4, #2] // strh r1, [r4] // add r4, #16 //; (cant use post increment due to no$gba bug?) //----------------------------------------------------------------------------- .nmp2_next: movs r3, r3, lsr #1 //; test next bit bcs .nmp2_process // add r4, #16 // add r5, #M2_SH_SIZE // bne .nmp2_next // //----------------------------------------------------------------------------- pop {r4-r5} //; return bx lr /****************************************************************************** *; void naxMain() *; *; process voices and audio *; call each 256hz tick (non-critical timing) ******************************************************************************/ public function naxMain //----------------------------------------------------------------------------- push {lr} //----------------------------------------------------------------------------- ldr r0,=na_ramp_speed //; do volume ramping ldr r0, [r0] //; bl slideMixingLevels //; //----------------------------------------------------------------------------- ldr r0,=na_mode //; determine mixing mode ldrb r0, [r0] // cmp r0, #1 // beq .namix_1 // bgt .namix_2 // //----------------------------------------------------------------------------- .namix_0: bl naUpdateHardwareShadow b .namix_exit //----------------------------------------------------------------------------- .namix_1: bl naUpdateM1 b .namix_exit //----------------------------------------------------------------------------- .namix_2: bl naUpdateHardwareShadow bl naSoftwareMix //;todo: mix audio... .namix_exit: pop {lr} bx lr /****************************************************************************** * void naUpdateM1() * * update audio mode1 ******************************************************************************/ private function naUpdateM1 //----------------------------------------------------------------------------- push {r4-r11, lr} //----------------------------------------------------------------------------- ldr r0,=REG_DMA1SAD // setup dma dest (->fetch) ldr r1,=na_buffer+M1_FETCH // str r1, [r0, #4] // //----------------------------------------------------------------------------- ldr r11,=na_buffer+M1_SHADOW // r11 = mode1 shadow data ldr r10,=na_ch_mask // r10 = channel mask ldrh r10, [r10] // ldr r12,=na_voices // r12 = voices //----------------------------------------------------------------------------- movs r10, r10, lsr#1 // test first bit bcc .nm1_next // //----------------------------------------------------------------------------- .nm1_loop: ldr r1, [r12, #V_SAMP] //; get sample address bics r6, r1, #0xFF000000 // beq .nm1_disabled // .nm1_active: // //----------------------------------------------------------------------------- ldrb r0, [r12, #V_START] //; shift out start bit movs r0, r0, lsr#1 // bcc .nm1_continue // //----------------------------------------------------------------------------- .nm1_newnote: //; newnote: movs r0, r0, lsl#1 //; clear start bit strb r0, [r12, #V_START] //; init volume/panning levels ldrh r0, [r12, #V_TVOL] // strh r0, [r12, #V_CVOL] // ldrh r0, [r12, #V_TPAN] // strh r0, [r12, #V_CPAN] // //----------------------------------------------------------------------------- ldr r0, [r12, #V_READ] //; shift sample offset lsl r0, #8+SFRAC // str r0, [r12, #V_READ] // mov r1, #1 //;->add zero padding b 1f //----------------------------------------------------------------------------- .nm1_continue: mov r1, #0 1: push {r1} ldrh r0, [r12, #V_CVOL] cmp r0, #0 // turn off channel if stopbit is set and VOL =0 bne 1f // TODO: this is new here! verify properly working ldrb r1, [r12, #V_START] // tst r1, #2 // beq 1f mov r1, #0 // str r1, [r12, #V_SAMP] // pop {r1} // b .nm1_disabled // //----------------------------------------------------------------------------- 1: ldr r3,=NA_VOLUME_LUT ldrb r1, [r3, r0, lsr#7+5] // r1 = shift data add r2, r0, #16<<(7+5) ldrb r2, [r3, r2, lsr#7+5] // r2 = shift level add r2, #5 // ***add this to the table values instead movs r1, r1, lsl#8 // assemble data orr r0, r1, r0, lsr r2 //----------------------------------------------------------------------------- ldrh r4, [r12, #V_CPAN] // assemble volume|shift|panning lsr r4, #9 // orr r4, r0, r4, lsl#16 // //----------------------------------------------------------------------------- str r4, [r11] // write to shadow //----------------------------------------------------------------------------- bl na1_getdest // do resample pop {r1} // bl na1ResampleData // b .nm1_next // //----------------------------------------------------------------------------- .nm1_disabled: // disabled: mov r0, #64<<16 // write centerpan, zerovol str r0, [r11] // //----------------------------------------------------------------------------- bl na1_getdest // zerofill buffer bl na1ZerofillBuffer // //----------------------------------------------------------------------------- .nm1_next: add r11, #4 // increment pointers add r12, #V_SIZE // movs r10, r10, lsr#1 // shift out next bit bcs .nm1_loop // loop until finished bne .nm1_next // //----------------------------------------------------------------------------- ldr r0,=m2_output_position // switch output slice ldr r1, [r0] // eor r1, #1 // str r1, [r0] // //----------------------------------------------------------------------------- pop {r4-r11, lr} // return bx lr // //----------------------------------------------------------------------------- na1_getdest: //----------------------------------------------------------------------------- ldr r0,=na_buffer+M1_OUTPUT // calc destination address ldr r1,=na_buffer+M1_SHADOW // output + voice * 512 sub r1, r11, r1 // add r0, r0, r1, lsl#9-2 // ldr r1,=m2_output_position // swap mixing slice ldr r1, [r1] // add r0, r0, r1, lsl#8 // bx lr // /****************************************************************************** * na1ResampleData ******************************************************************************/ private function na1ResampleData #define m1 r0 // output data #define m2 r1 // output datae #define m3 r2 // output data #define m4 r3 // output data #define pos r4 // read position #define src r5 // data source #define count r6 // counter #define rate r7 // sampling rate #define curr r8 // current sample #define next r9 // next sample #define dest r10 // output address #define ta r11 // temp value #define tb r12 // temp value #define fa0 curr #define fb0 next #define fb1 ta #define fs1 m3 #define fs2 m4 //----------------------------------------------------------------------------- .macro rs_routine shift, routine, restart //----------------------------------------------------------------------------- mul r2, count, rate // mov r0, #M1_FETCH_SIZE<<(10-\shift) // rsb r1, pos, r3, lsl#(12-\shift) // bl calc_mixcount //; calculate how many samples to fetch & output //----------------------------------------------------------------------------- ldr r0,=REG_DMA1SAD //; setup dma source ldr r1, [src, #SAMPLE_POINT] //; sample+position cmp r1, #0 // addeq r1, src, #SAMPLE_DATA // add r1, pos, lsr#SFRAC // .if \shift == 1 // add r1, pos, lsr#SFRAC //; 16bit adjustment .endif // bic r1, #0b11 //; 32-bit alignment str r1, [r0, #0] // //----------------------------------------------------------------------------- mov r1, #DMA_ENABLE|DMA_32BIT // add r2, r2, #16384 //; add safety threshold add r1, r1, r2, lsr#10+(2-\shift) // str r1, [r0, #8] //; start dma //; [dma transfer] //----------------------------------------------------------------------------- bl \routine //----------------------------------------------------------------------------- ldmia src, {r0, r1} //; test if position >= length add r3, r0, r1 // cmp pos, r3, lsl#SFRAC+(2-\shift) // bcc 1f // //----------------------------------------------------------------------------- ldrb r0, [src, #SAMPLE_REP] //; branch according to loop type cmp r0, #1 // beq 2f // //----------------------------------------------------------------------------- // oneshot mov pos, #0 // clear sample str pos, [r12, #V_SAMP] // pop {count} // pop mixcount cmp count, #0 // beq 4f // 3: strh pos, [dest], #2 // fill remaining slice with subs count, #1 // zero bne 3b // b 4f // //----------------------------------------------------------------------------- 2: // forward loop sub pos, pos, r1, lsl#SFRAC+(2-\shift) // subtract loop length from pos //----------------------------------------------------------------------------- 1: pop {count} // restart if more samples to mix cmp count, #0 // bne \restart // str pos, [r12, #V_READ] // save position/return 4: pop {r10-r12, pc} // .endm //----------------------------------------------------------------------------- #define zeropad_size 32 // r11=shadow entry, r12 = channel data // r0 = dest, r1 = zeropad flag push {r10-r12, lr} // preserve regs //----------------------------------------------------------------------------- ldr src, [r12, #V_SAMP] // get sampl address bic src, #0xFF000000 // add src, #0x2000000 // //----------------------------------------------------------------------------- ldrh rate, [r12, #V_FREQ] // load vars into regs ldr pos, [r12, #V_READ] // mov dest, r0 // mov count, #128 // reset counter //----------------------------------------------------------------------------- cmp r1, #0 // zerofill first X samples beq .skip_zeropad // if flag is set sub count, #zeropad_size // mov r1, #0 // mov r0, #zeropad_size/2 // 2: stmia dest!, {r1} // subs r0, #1 // bne 2b // //----------------------------------------------------------------------------- .skip_zeropad: @breakp @ ldr r1,=8000 @ ldr r2,=-8000 @ ldr r0, testy @1: add r0, #1 @ and r0, #0x3F @ cmp r0, #20 @ strlth r1, [dest], #2 @ strgeh r2, [dest], #2 @ subs count, #1 @ bne 1b @ str r0, testy @ pop {r10-r12,pc} ldmia src, {r0, r1} add r3, r0, r1 ldrb r0, [src, #SAMPLE_FORMAT] cmp r0, #0 beq nm1_8bit @testy: @ .word 0 //----------------------------------------------------------------------------- nm1_16bit: //----------------------------------------------------------------------------- rs_routine 1, nm1_resamp_16bit, nm1_16bit //----------------------------------------------------------------------------- nm1_8bit: //----------------------------------------------------------------------------- rs_routine 0, nm1_resamp_8bit, nm1_8bit //============================================================================= .macro resample_data rout, exit, mode, dsize //============================================================================= .if \mode == 1 // mode1: increment before load subs pos, rate, lsl#32-SFRAC // subcc src, #\dsize // movcc next, curr // .endif //----------------------------------------------------------------------------- subs count, #8 // output 8 sample chunks: bmi 1f // //----------------------------------------------------------------------------- 2: \rout m1, 1 // build 1st word \rout m2, 1 // second \rout m3, 1 // third \rout m4, 1 // fourth stmia dest!, {m1-m4} // write to dest subs count, #8 // loop bpl 2b // //----------------------------------------------------------------------------- 1: adds count, #8 // output remaining samples beq \exit // //----------------------------------------------------------------------------- 1: \rout m1, 0 // output 1 half word strh m1, [dest], #2 // subs count, #1 // bne 1b // b \exit // .endm // //----------------------------------------------------------------------------- /****************************************************************************** * 16bit resample function ******************************************************************************/ private function nm1_resamp_16bit push {src, r12, lr} ldr src,=na_buffer+M1_FETCH mov m1, pos, lsr#10 // "save position integer" bic pos, m1, lsl#10 and m2, m1, #0b1 // get fetch + alignment offset add src, m2, lsl#1 // push {m1, src} ldrh m1, [r12, #V_FILTER] movs m1, m1, lsr#1 bcs nm1_resamp_16bit_filtered cmp rate, #1<<10 // use NN resampling for rates > 32khz blt nm1_resamp_16bit_linear //============================================================================= nm1_resamp_16bit_nearest: //============================================================================= //============================================================================= .macro m1_buildw16n target, double //============================================================================= and curr, ta, pos, lsr#10-1 // shift position & mask out low bit CHANGED add->and ??? bug ldrh \target, [src, curr] // read sample add pos, rate // increment pos //----------------------------------------------------------------------------- .if \double != 0 // repeat if dobule flagged and next, ta, pos, lsr#10-1 // ldrh next, [src, next] // add pos, rate // orr \target, \target, next, lsl#16 // .endif // .endm // //----------------------------------------------------------------------------- mvn ta, #1 // used for clering low bit in shifted position tst dest, #0b11 // align destination beq 1f bic m1, ta, pos, lsr#10-1 ldrh m1, [src, m1] add pos, rate strh m1, [dest], #2 subs count, #1 beq _m116n_exit 1: resample_data m1_buildw16n, _m116n_exit, 0, 2 //============================================================================= nm1_resamp_16bit_filtered: //============================================================================= //----------------------------------------------------------------------------- // load filter state + coefficients //----------------------------------------------------------------------------- ldrsh fa0, [r12, #V_A0] ldrsh fb0, [r12, #V_B0] ldrsh fb1, [r12, #V_B1] ldrsh fs1, [r12, #V_S1] ldrsh fs2, [r12, #V_S2] push {r12} mvn m1, #1 //----------------------------------------------------------------------------- .macro m1_build16f ts and m2, m1, pos, lsr#SFRAC-1 // read sample ldrsh \ts, [src, m2] // add pos, rate // increment position mul \ts, fa0, \ts // get filtered sample (a*a0+b*b0+c*b1) mla \ts, fb0, fs1, \ts // mla \ts, fb1, fs2, \ts // cmp \ts, #127<<22 // clamp result movge \ts, #127<<22 // cmp \ts, #-32768<<13 // movlt \ts, #-32768<<13 // mov fs2, fs1 // c=b, b=a mov fs1, \ts, asr#13 // .endm //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- subs count, #4 // output 4 sample chunks: bmi 1f // //----------------------------------------------------------------------------- 2: m1_build16f tb // 1 strh fs1, [dest], #2 // m1_build16f tb // 2 strh fs1, [dest], #2 // m1_build16f tb // 3 strh fs1, [dest], #2 // m1_build16f tb // 4 strh fs1, [dest], #2 // subs count, #4 // loop bpl 2b // //----------------------------------------------------------------------------- 1: adds count, #4 // output remaining samples beq m116f_exit // //----------------------------------------------------------------------------- 1: m1_build16f tb // output 1 half word strh fs1, [dest], #2 // subs count, #1 // bne 1b // m116f_exit: // save filter state pop {r12} strh fs1, [r12, #V_S1] strh fs2, [r12, #V_S2] pop {m1, m2} //pop old regs sub m2, src, m2 // "get difference" add pos, pos, m1, lsl#SFRAC // add old position add pos, pos, m2, lsl#SFRAC // add src difference //----------------------------------------------------------------------------- pop {src, r12, pc} // return //============================================================================= nm1_resamp_16bit_linear: //============================================================================= //============================================================================= .macro m1_buildw16 target, double //============================================================================= adds pos, rate, lsl#32-SFRAC // 1 add rate FRACTION to position FRACTION movcs curr, next // 1 load new sample on overflow ldrcssh next, [src, #2]! // 1/3 .. sub ta, next, curr // 1 calculate delta mov tb, pos, lsr#24 // 1 get top 8 bits of position fraction mul tb, ta, tb // 2 multiply delta * position add \target, curr, tb, asr#8 // 1 add base sample to product (shifted to 16 bits) .if \double != 0 adds pos, rate, lsl#32-SFRAC // 1 add rate FRACTION to position FRACTION movcs curr, next // 1 load new sample on overflow ldrcssh next, [src, #2]! // 1/3 .. sub ta, next, curr // 1 calculate delta mov tb, pos, lsr#24 // 1 get top 8 bits of position fraction mul tb, ta, tb // 2 multiply delta * position add ta, curr, tb, asr#8 // 1 add base sample to product (shifted to 16 bits) add \target, ta, lsl#16 // 1 .endif .endm //----------------------------------------------------------------------------- mov pos, pos, lsl#32-10 // shift out integer bits //----------------------------------------------------------------------------- tst dest, #0b11 // align destination beq 1f // ldrsh curr, [src] // ldrsh next, [src, #2] // sub ta, next, curr // mov tb, pos, lsr#24 // mul ta, tb, ta // add m1, curr, ta, asr#8 // strh m1, [dest], #2 // adds pos, rate, lsl#32-10 // addcs src, #2 // subs count, #1 // beq _m116_exit2 // 1: // //----------------------------------------------------------------------------- ldrsh curr, [src] ldrsh next, [src, #2]! resample_data m1_buildw16, _m116_exit, 1, 2 //----------------------------------------------------------------------------- _m116_exit: //----------------------------------------------------------------------------- sub src, #2 _m116_exit2: movs pos, pos, lsr#32-10 add pos, pos, rate //----------------------------------------------------------------------------- _m116n_exit: //----------------------------------------------------------------------------- pop {m1, m2} // pop old position, starting src sub m2, src, m2 // get difference add pos, pos, m1, lsl#10 // add old position add pos, pos, m2, lsl#10-1 // add src difference pop {src, r12, pc} // return /****************************************************************************** * 8bit resampling/expanding function ******************************************************************************/ private function nm1_resamp_8bit push {src, r12, lr} ldr src,=na_buffer+M1_FETCH mov m1, pos, lsr#SFRAC // save&clear position integer bic pos, m1, lsl#SFRAC // and m2, m1, #0b11 // mask alignment bits add src, m2 // add offset to fetch push {m1, src} ldrh m1, [r12, #V_FILTER] // run filtered if enabled. movs m1, m1, lsr#1 // bcs nm1_resamp_8bit_filtered // cmp rate, #1<= 1.0 blt nm1_resamp_8bit_linear //============================================================================= nm1_resamp_8bit_nearest: //============================================================================= //============================================================================= .macro m1_buildw8n target, double, alternate //============================================================================= ldrb \target, [src, pos, lsr#SFRAC] // read sample add pos, rate // .if \double != 0 // ldrb next, [src, pos, lsr#SFRAC] // read another sample add pos, rate // orr \target, next, lsl#16 // combine .endif // mov \target, \target, lsl#8 // expand to 16bit .endm //----------------------------------------------------------------------------- tst dest, #0b11 // 32bit align dest beq 1f // ldrb m1, [src, pos, lsr#SFRAC] // (output 1 samp if misaligned) add pos, rate // lsl m1, #8 // strh m1, [dest], #2 // subs count, #1 // beq m18n_exit // 1: // //----------------------------------------------------------------------------- resample_data m1_buildw8n, m18n_exit, 0, 1 //----------------------------------------------------------------------------- //============================================================================= nm1_resamp_8bit_filtered: //============================================================================= //----------------------------------------------------------------------------- // load filter state //----------------------------------------------------------------------------- ldrsh fa0, [r12, #V_A0] @ 0.13 ldrsh fb0, [r12, #V_B0] @ 1.13 ldrsh fb1, [r12, #V_B1] @ s.13 ldrsh fs1, [r12, #V_S1] ldrsh fs2, [r12, #V_S2] push {r12} lsl fa0, #8 @ 0.21 //----------------------------------------------------------------------------- .macro m1_build8f ts mov m1, pos, lsr#SFRAC @ ts = get_sample ldrsb \ts, [src, m1] @ add pos, rate @ mul \ts, fa0, \ts @ ts = ts * a0 (.7 * .21) mla \ts, fb0, fs1, \ts @ ts += s1 * b0 (.15 * .13) mla \ts, fb1, fs2, \ts @ ts += s2 * b1 (.15 * .13) cmp \ts, #127<<(28-7) @ saturate result movge \ts, #127<<(28-7) @ cmp \ts, #-128<<(28-7) @ movlt \ts, #-128<<(28-7) @ mov fs2, fs1 @ fs2 = fs1 mov fs1, \ts, asr#(28-15) @ fs1 = ts (adjusted/result) .endm //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- subs count, #4 // output 4 sample chunks: bmi 1f // //----------------------------------------------------------------------------- 2: m1_build8f tb // 1 strh fs1, [dest], #2 // m1_build8f tb // 2 strh fs1, [dest], #2 // m1_build8f tb // 3 strh fs1, [dest], #2 // m1_build8f tb // 4 strh fs1, [dest], #2 // subs count, #4 // loop bpl 2b // //----------------------------------------------------------------------------- 1: adds count, #4 // output remaining samples beq m18f_exit // //----------------------------------------------------------------------------- 1: m1_build8f tb // output 1 half word strh fs1, [dest], #2 // subs count, #1 // bne 1b // m18f_exit: pop {r12} // save filter state strh fs1, [r12, #V_S1] strh fs2, [r12, #V_S2] pop {m1, m2} //pop old regs sub m2, src, m2 // "get difference" add pos, pos, m1, lsl#SFRAC // add old position add pos, pos, m2, lsl#SFRAC // add src difference //----------------------------------------------------------------------------- pop {src, r12, pc} // return //============================================================================= nm1_resamp_8bit_linear: //============================================================================= mov pos, pos, lsl#32-SFRAC tst dest, #0b11 // align destination beq 1f // ldrsb curr, [src] // ldrsb next, [src, #1] // sub ta, next, curr // mov tb, pos, lsr#24 // mul ta, tb, ta // add m1, ta, curr, lsl#8 // strh m1, [dest], #2 // adds pos, rate, lsl#32-SFRAC // addcs src, #1 // subs count, #1 // beq m18_exit2 // 1: // //----------------------------------------------------------------------------- ldrsb curr, [src] // prime sampler ldrsb next, [src, #1]! // //----------------------------------------------------------------------------- mov r14, #0xFF0000 // for masking position //============================================================================= .macro m1_buildw8 target, double //============================================================================= adds pos, rate, lsl#32-SFRAC // 1 add rate FRACTION to position FRACTION movcs curr, next // 1 load new sample on overflow ldrcssb next, [src, #1]! // 1/3 .. sub ta, next, curr // 1 calculate delta mov tb, pos, lsr#24 // 1 get top 8 bits of position fraction mul ta, tb, ta // 2 multiply position * delta add \target, ta, curr, lsl#8 // 1 add base sample to product (shifted to 16 bits) .if \double != 0 adds pos, rate, lsl#32-SFRAC // 1 add rate FRACTION to position FRACTION movcs curr, next // 1 load new sample on overflow ldrcssb next, [src, #1]! // 1/3 .. sub ta, next, curr // 1 calculate delta and tb, r14, pos, lsr#8 // 1 get top 8 bits of position fraction, shifted into top hword mla \target, tb, ta, \target // 3 multiply position * delta, add to target add \target, \target, curr, lsl#24 // 1 add base sample to product .endif .endm resample_data m1_buildw8, m18_exit, 1, 1 //----------------------------------------------------------------------------- m18_exit: //----------------------------------------------------------------------------- sub src, #1 m18_exit2: movs pos, pos, lsr#32-SFRAC // shift position back to normal add pos, pos, rate // m18n_exit: pop {m1, m2} //pop old regs sub m2, src, m2 // "get difference" add pos, pos, m1, lsl#SFRAC // add old position add pos, pos, m2, lsl#SFRAC // add src difference //----------------------------------------------------------------------------- pop {src, r12, pc} // return /****************************************************************************** * void na1ZerofillBuffer() * * fill output buffer slice with zero ******************************************************************************/ private function na1ZerofillBuffer //----------------------------------------------------------------------------- push {r10-r12, lr} //----------------------------------------------------------------------------- mov dest, r0 // write to r0 mov count, #256 // 256 bytes mov m1, #0 // clear 64x each iteration mov m2, #0 // mov m3, #0 // mov m4, #0 // //----------------------------------------------------------------------------- 1: stmia dest!, {m1-m4} // clear bytes stmia dest!, {m1-m4} // stmia dest!, {m1-m4} // stmia dest!, {m1-m4} // subs count, #64 // bne 1b // //----------------------------------------------------------------------------- pop {r10-r12, pc} //----------------------------------------------------------------------------- calc_mixcount: //----------------------------------------------------------------------------- // returns r2 = sample count mov r3, #0 cmp r2, r0 movhi r2, r0 movhi r3, #1 cmp r2, r1 movhi r2, r1 bhi _value_clipped cmp r3, #0 beq _value_unclipped _value_clipped: mov r0, r2 push {lr} bl div19 pop {lr} b 1f _value_unclipped: mov r0, count 1: sub count, r0 push {count} mov count, r0 bx lr /****************************************************************************** *; void naUpdateHardwareShadow() *; *; update M2 hardware shadow with voice data ******************************************************************************/ private function naUpdateHardwareShadow //----------------------------------------------------------------------------- push {r4-r11, lr} ldr r12,=na_voices //; r12=voices ldr r11,=na_buffer+M2_SHADOW //; r11=shadow buffer ldr r10,=na_ch_mask //; r10=channel mask ldrh r10, [r10] //; r9= channel counter mov r9, #0 //; r8 = soundcnt offset ldr r8,=REG_SOUND0CNT //; r7 = volume LUT sub r8, r11 ldr r7,=NA_VOLUME_LUT //----------------------------------------------------------------------------- ldr r0,=na_mode //; if mode == 2 add software channels ldrb r0, [r0] // cmp r0, #2 // orreq r10, #0xFF0000 // orreq r10, #0xFF000000 // movs r10, r10, lsr#1 bcc .uhs_next //----------------------------------------------------------------------------- .uhs_loop: ldr r1, [r12, #V_SAMP] //; r1 = SAMP entry bics r6, r1, #0xFF000000 //; beq .uhs_disabled //----------------------------------------------------------------------------- add r6, #0x2000000 //; r6 = mainram address of source movs r1, r1, lsr#24+1 //; test START bcc .uhs_continue_voice //; //----------------------------------------------------------------------------- //; *** start new note //----------------------------------------------------------------------------- mov r1, r1, lsl#1 //; clear start bit strb r1, [r12, #V_START] // //----------------------------------------------------------------------------- ldrb r4, [r12, #V_READ] //; read sample offset mov r0, r4, lsl#V_READ_FRAC+8 //; shift sample offset for software mixer str r0, [r12, #V_READ] // //----------------------------------------------------------------------------- ldrh r0, [r12, #V_TVOL] //; set direct levels on keyon strh r0, [r12, #V_CVOL] //; ldrh r0, [r12, #V_TPAN] //; strh r0, [r12, #V_CPAN] //; //----------------------------------------------------------------------------- cmp r9, #16 bcs .uhs_next //----------------------------------------------------------------------------- cmp r4, #0 // beq .uhs_no_offset //; (skip if zero) ldrb r3, [r6, #SAMPLE_FORMAT] //; test sample format cmp r3, #1 // movgt r4, #0 //; adpcm = invalid moveq r4, r4, lsl#(9-2) //; 16-bit = LSL 1 movne r4, r4, lsl#(8-2) //; 8-bit = LSL 0 .uhs_no_offset: //----------------------------------------------------------------------------- ldr r2, [r6, #SAMPLE_POINT] //; r2 = source pointer cmp r2, #0 // addeq r2, r6, #SAMPLE_DATA // //----------------------------------------------------------------------------- add r2, r2, r4, lsl#2 //; add sample offset (convert to bytes) //----------------------------------------------------------------------------- ldrb r3, [r6, #SAMPLE_REP] //; check repeat mode cmp r3, #1 // bne .uhs_nonlooping // //----------------------------------------------------------------------------- .uhs_looping: ldrh r3, [r6, #SAMPLE_LSTART] //; get loopstart position subs r3, r3, r4 //; subtract sample offset addmi r2, r2, r3, lsl#2 //; adjust values if result goes negative movmi r3, #0 //; ldr r14, [r6, #SAMPLE_LEN] //; read length mov r3, r3, lsl#16 //; shift into top hword b .uhs_initsh //----------------------------------------------------------------------------- .uhs_nonlooping: ldr r5, [r6, #SAMPLE_LEN] //; read length subs r14, r5, r4 //; subtract sample offset submi r2, r2, r4, lsl#2 //; saturate negative results movmi r14, r5 mov r3, #0 //----------------------------------------------------------------------------- .uhs_initsh: ldrh r0, [r6, #SAMPLE_FORMAT] //; r1 = CNT (compute rep/fmt bits) mov r6, r0, lsr#8 //; (dont need r6 anymore) mov r0, r0, lsl#24 // orr r6, r6, r0, lsr#24-2 // mov r1, r6, lsl#27 // orr r1, #SOUND_ENABLE // stmia r11, {r1-r3, r14} //; write CNT, SAD, TMR=0, PNT, LEN b .uhs_newnote //; (SRC activates key-on update) //----------------------------------------------------------------------------- .uhs_continue_voice: cmp r9, #16 bcs .uhs_next ldr r1, [r11, r8] // tst r1, #SOUND_ENABLE //; reset voice if hardware channel ended moveq r1, #0 // streq r1, [r12, #V_SAMP] // beq .uhs_next // //----------------------------------------------------------------------------- .uhs_newnote: ldrh r1, [r12, #V_FREQ] //; TMR = FREQ (is a period actually) strh r1, [r11, #M2_SH_TMR] // //----------------------------------------------------------------------------- ldrh r0, [r12, #V_CVOL] //; set volume level cmp r0, #0 bne 1f ldrb r1, [r12, #V_START] // disable channel if stopbit and vol=0 tst r1, #2 // movne r1, #0 // strne r1, [r12, #V_SAMP] // 1: ldrb r1, [r7, r0, lsr#7+5] //; (translateVolume) add r2, r0, #16<<(7+5) //; ldrb r2, [r7, r2, lsr#7+5] //; add r2, #5 //; movs r1, r1, lsl#8 //; orr r0, r1, r0, lsr r2 //; strh r0, [r11, #M2_SH_CNT] //; //----------------------------------------------------------------------------- ldrh r0, [r12, #V_CPAN] //; set panning level mov r0, r0, lsr#9 // strb r0, [r11, #CSOUND_CNT+2] // //----------------------------------------------------------------------------- .uhs_next: add r11, #0x10 add r12, #V_SIZE add r9, #1 movs r10, r10, lsr#1 bcs .uhs_loop bne .uhs_next pop {r4-r11, lr} bx lr //----------------------------------------------------------------------------- .uhs_disabled: mov r0, #0x80000000 //; clear CNT.MSB, SRC = 'STOP' str r0, [r11, #M2_SH_SRC] // strb r0, [r11, #M2_SH_CNT+3] // b .uhs_next // .pool /****************************************************************************** * LUT for helping compute the hardware volume levels (volume/shift) ******************************************************************************/ NA_VOLUME_LUT: // divider values .byte 3,2,2,2,1,1,1,1,0,0,0,0,0,0,0,0 // shift values .byte 0,2,2,2,3,3,3,3,4,4,4,4,4,4,4,4 /****************************************************************************** * void naSoftwareMix() * * software mix extended channels into the streams ******************************************************************************/ private function naSoftwareMix //----------------------------------------------------------------------------- push {r4-r11, lr} ldr r0,=REG_DMA1SAD //; point DMA1 to sample fetch ldr r1,=na_buffer + M2_FETCH // str r1, [r0, #4] // //----------------------------------------------------------------------------- ldr r0,=na_buffer+M2_MIX_WMEM //; erase work memory mov r1, #0 // mov r2, r1, lsr#2 // mov r3, r1, lsr#3 // mov r4, r1, lsr#4 // mov r5, r1, lsr#5 // mov r6, r1, lsr#6 // mov r7, r1, lsr#7 // mov r8, r1, lsr#8 // mov r9, #M2_CHUNKMAX/16 // .clear_workmem: // stmia r0!, {r1-r8} // stmia r0!, {r1-r8} // subs r9, #1 // bne .clear_workmem // stmia r0!, {r1-r8} // clear remaining (88/16=5 .5) /* mov r1, #84 //; get mixcount ldr r0,=m2_mod3 // ldrb r0, [r0] // cmp r0, #2 // addeq r1, #4 // ldr r0,=0x00800080 str r0, volume_addition ldr r0,=na_buffer+M2_MIX_WMEM ldr r2, test_sine // pcmtest = 50 1: add r2, #5 and r2, #0xFF cmp r2, #0x80 ldrge r3,=0x05900590 ldrlt r3,=0x00700070 stmia r0!, {r3} subs r1, #1 bne 1b str r2, test_sine b .nsm_mixdown test_sine: .word 0 */ //----------------------------------------------------------------------------- #define rvoice r12 //; voice data #define cbits r11 //; active channel bits #define rsamp r10 //; sample pointer #define cvol r8 //; volume #define sfreq r7 //; sampling frequency #define mixc r6 //; mix counter #define mdest r5 //; mixing destination (work buffer) #define sread r4 //; sampling position //----------------------------------------------------------------------------- ldr rvoice,=na_voices+16*V_SIZE //; pointer to first software voice ldr cbits,=na_ch_mask ldrh cbits, [cbits, #2] ldr cbits,=0xFFFF // heh //----------------------------------------------------------------------------- mov r0, #0 //; reset volume adder str r0, volume_addition // movs cbits, cbits, lsr#1 // test first channel bit bcc .nsm_next // //----------------------------------------------------------------------------- .nsm_loop: //----------------------------------------------------------------------------- ldr rsamp, [rvoice, #V_SAMP] //; rsamp = sample address bics rsamp, #0xFF000000 //; exit if V_SAMP == 0 beq .nsm_next // add rsamp, #0x2000000 // //----------------------------------------------------------------------------- ldrh r0, [rvoice, #V_CPAN] //; r0=pan: 0..127 lsr r0, #9 // ldrh r1, [rvoice, #V_CVOL] //; r1=vol: 0..2047 movs r1, r1, lsr#5 // TODO: this is new stuff, verify. bne .nonzerovol ldrb r2, [rvoice, #V_START] tst r2, #2 movne r2, #0 strne r2, [rvoice, #V_SAMP] b .nsm_next //----------------------------------------------------------------------------- .nonzerovol: mul r9, r0, r1 //; calc volume levels mov r9, r9, lsr#10 // rsb r0, r0, #128 // mul cvol, r0, r1 // movs cvol, cvol, lsr#10 // orrs cvol, r9, lsl#16 // //----------------------------------------------------------------------------- ldrne r0, volume_addition // addne r0, r0, cvol // strne r0, volume_addition // //----------------------------------------------------------------------------- mov mixc, #84 //; get mixcount ldr r0,=m2_mod3 // ldrb r0, [r0] // cmp r0, #2 // addeq mixc, #4 // //----------------------------------------------------------------------------- ldr mdest,=na_buffer+M2_MIX_WMEM //; reset mdest //----------------------------------------------------------------------------- ldrh sfreq, [rvoice, #V_FREQ] //; get sampling rate ldr r0,=49212 //; adjust scale (32khz -> 21.8khz) mul sfreq, r0 // mov sfreq, sfreq, lsr#15 // //----------------------------------------------------------------------------- ldr sread, [rvoice, #V_READ] //; get sampling position ldmia rsamp, {r1, r2} //; read loop start, loop length add r3, r1, r2 //; r3 = length //----------------------------------------------------------------------------- .mix_segment: mul r2, mixc, sfreq //; r2 = count * rate ldrb r0, [rsamp, #SAMPLE_FORMAT] //; r0 = sample format mov r14, #0 //; r14 is a flag for later cmp r0, #0 beq .mix_8bit //----------------------------------------------------------------------------- //; next label -> .mix_16bit /****************************************************************************** * macro calc_lengths fshift, lshift * * input: * r2 = mix amount * * params: * calculate mixing length * fshift = fetch amount (amount to shift MM_FETCH by when comparing) * lshift = shift amount to convert words to samples (8bit=2, 16bit=1) ******************************************************************************/ .macro calc_lengths fshift, lshift //----------------------------------------------------------------------------- cmp r2, #M2_FETCH_SIZE<<\fshift //; saturate to fetch size movhi r2, #M2_FETCH_SIZE<<\fshift // movhi lr, #1 //; flag operation //----------------------------------------------------------------------------- rsb r0, sread, r3, lsl#(10+\lshift) //; test if fetch will exceed cmp r2, r0 //; sample length movhi r2, r0 //; and saturate bhi 1f //----------------------------------------------------------------------------- cmp lr, #0 //; test if either operation beq 2f //; above passed //----------------------------------------------------------------------------- 1: mov r0, r2 //; divide samples / freq bl div19 //; to get new mix length b 1f //----------------------------------------------------------------------------- 2: mov r0, mixc //; otherwise use full amount //----------------------------------------------------------------------------- 1: sub mixc, mixc, r0 //; subtract from counter //----------------------------------------------------------------------------- stmfd sp!, {mixc} //; push on stack mov mixc, r0 //; replace with temp value .endm /****************************************************************************** * macro copy_and_mix sh, mixer, eb, restart * * sh = shift [1=16bit] * mixer = mixing function * eb = ending branch * restart = label to jump to on remix ******************************************************************************/ .macro copy_and_mix sh, mixer, eb, restart //----------------------------------------------------------------------------- ldr r0,=REG_DMA1SAD ldr r1, [rsamp, #SAMPLE_POINT] //; dma:sad = cmp r1, #0 //; forcealign(sample+read) addeq r1, rsamp, #SAMPLE_DATA //; (32-bit alignment) add r1, sread, lsr#10-\sh // bic r1, #0b11 // str r1, [r0, #0] // //----------------------------------------------------------------------------- mov r1, #DMA_ENABLE|DMA_32BIT //; write dma control add r2, r2, #8192 //;-add threshold for safety! add r1, r1, r2, lsr#10+2-\sh // str r1, [r0, #8] //; start dma //; [bus locked while dma is active] //----------------------------------------------------------------------------- bl \mixer ldmia rsamp, {r0, r1} //; check if end of sample reached add r3, r0, r1 //; cmp sread, r3, lsl#10+2-\sh //; bcc 2f //; //----------------------------------------------------------------------------- ldrb r0, [rsamp, #SAMPLE_REP] //; branch loop type cmp r0, #1 // beq 1f // //----------------------------------------------------------------------------- // one shot: mov sread, #0 //; reset read str sread, [rvoice, #V_SAMP] //; reset SAMP pop {mixc} //; pop mixc mov sfreq, #0 //; mix zero for remaining samples ldr r1,=na_buffer+M2_FETCH //; str sread, [r1] //; bl na_mix_pcm8 //; b 3f //----------------------------------------------------------------------------- 1: //; forward loop: //; r1 = loop length //; subtract from read position sub sread, sread, r1, lsl#10+2-\sh //----------------------------------------------------------------------------- 2: pop {mixc} cmp mixc, #0 //; mix more samples? beq 3f //; yes: mul r2, mixc, sfreq //; get samps * freq mov r14, #0 //; clear flag b \restart //; jump to restart //; ! r3 is samp length //----------------------------------------------------------------------------- 3: str sread, [rvoice, #V_READ] //; save read position .if \eb == 1 b .nsm_next .endif .endm //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- .mix_16bit: //----------------------------------------------------------------------------- calc_lengths 9, 1 copy_and_mix 1, na_mix_pcm16, 1, .mix_16bit //----------------------------------------------------------------------------- .mix_8bit: //----------------------------------------------------------------------------- calc_lengths 10, 2 copy_and_mix 0, na_mix_pcm8, 0, .mix_8bit //----------------------------------------------------------------------------- .nsm_next: add rvoice, rvoice, #V_SIZE // loop through voices movs cbits, cbits, lsr#1 // bcs .nsm_loop // bne .nsm_next // //----------------------------------------------------------------------------- .nsm_mixdown: ldr r1,=na_buffer+M2_MIX_WMEM // r1= wmem ldr r3,=na_buffer+M2_MIX_OUTPUT // r3= output ldr r2,=m2_output_position ldr r2, [r2] add r3, r3, r2, lsl#1 mov r7, #M2_BUFFERLENGTH*2 mov r10, #0x10000 sub r10, #1 eor r6, r10, #0x8000 //----------------------------------------------------------------------------- ldr r4, volume_addition //; parse volume adder mov r10, r4, lsr#16 //; r10 = right volume mov r10, r10, lsl#3 //; r4 = left volume mov r4, r4, lsl#16 // mov r4, r4, lsr#16+1-4 // //----------------------------------------------------------------------------- ldr r12,=m2_mod3 //; r0 = size of mix (84,84,88) ldrb r0, [r12] //; increment mod3 data add r5, r0, #1 // cmp r0, #2 // moveq r5, #0 // moveq r0, #88 // movne r0, #84 // strb r5, [r12] // //----------------------------------------------------------------------------- mov r8, #0 //; r0 = first mix add r2, r0 //; r8 = second mix (on overflow) cmp r2, #M2_BUFFERLENGTH subge r2, #M2_BUFFERLENGTH movge r8, r2 subge r0, r8 ldr r5,=m2_output_position str r2, [r5] push {r8} .macro clamp_s s cmp \s, r6 movgt \s, r6 cmn \s, r6 rsblt \s, r6, #0 .endm .macro cad s // convert and assemble data //----------------------------------------------------------------------------- sub r11, \s, r4 // convert to signed (left) mov r11, r11, lsl#16 // sign extend mov r11, r11, asr#12 // shift to 16-bit clamp_s r11 // clamp rsb r12, r10, \s, lsr#16 // convert to signed (right) mov r12, r12, lsl#4 // convert to 16-bit clamp_s r12 // clamp .endm // mix16 format is 12-bit bl .output_mix pop {r0} cmp r0, #0 ldrne r3,=na_buffer+M2_MIX_OUTPUT blne .output_mix pop {r4-r11, pc} .output_mix: ldmia r1!, {r8,r9} cad r8 strh r12, [r3, r7] strh r11, [r3], #2 cad r9 strh r12, [r3, r7] strh r11, [r3], #2 subs r0, r0, #2 bne .output_mix bx lr volume_addition: .space 4 //============================================================================= #define m1 r0 #define m2 r1 #define m3 r2 #define m4 r3 #define m5 r9 #define m6 r11 #define m7 r12 #define m8 r14 #define rsrc rsamp #define smp1 m8 /****************************************************************************** * na_mix_pcm8(*) * * * * mix 8bit data into work buffer ******************************************************************************/ private function na_mix_pcm8 stmfd sp!, {rsamp, cbits, rvoice, lr} // preserve regs //----------------------------------------------------------------------------- cmp cvol, #0 // if volume == 0 muleq r0, sfreq, mixc // skip mixing addeq sread, sread, r0 // add freq8sampes to read position ldmeqfd sp!, {rsamp, cbits, rvoice, pc} // //----------------------------------------------------------------------------- ldr rsrc,=na_buffer+M2_FETCH // load rsrc with fetch pointer mov r0, sread, lsr#10 // get read position integer sub sread, r0, lsl#10 // clear integer in read and r1, r0, #0b11 // mask low 2 bits add rsrc, rsrc, r1 // add to fetch offset stmfd sp!, {r0} // save the old integer value for later /************************************************** * mix sample **************************************************/ .macro mix8w sa //----------------------------------------------------------------------------- ldrb smp1, [rsrc, sread, lsr#10] // read sample add sread, sfreq // increment sampler eor smp1, #0x80 // unsign sample mul smp1, cvol, smp1 // mul by volume (both l/r) bic smp1, #0x0F0000 // prepare for shift add \sa, smp1, lsr#4 // add shifted value to mix .endm //----------------------------------------------------------------------------- // mix large chunks //----------------------------------------------------------------------------- subs mixc, mixc, #7 bmi .mp8a_mixn_exit //----------------------------------------------------------------------------- .mp8a_mixn: ldmia mdest, {m1,m2,m3,m4,m5,m6,m7} // process 7 samples mix8w m1 // mix8w m2 // mix8w m3 // mix8w m4 // mix8w m5 // mix8w m6 // mix8w m7 // stmia mdest!, {m1,m2,m3,m4,m5,m6,m7} // subs mixc, mixc, #7 bpl .mp8a_mixn .mp8a_mixn_exit: //----------------------------------------------------------------------------- adds mixc, mixc, #7 beq .mp8a_mix1_exit .mp8a_mix1: //----------------------------------------------------------------------------- ldr m1, [mdest] // read 1 sample mix8w m1 // mix data str m1, [mdest], #4 // write 1 sample subs mixc, mixc, #2 // decrement 2 bmi .mp8a_mix1_exit // exit if past 0 ldr m1, [mdest] // mix one more sample mix8w m1 // str m1, [mdest], #4 // bne .mp8a_mix1 // loop if nonzero counter //----------------------------------------------------------------------------- .mp8a_mix1_exit: ldmfd sp!, {r0} // add old integer to sampler pos add sread, sread, r0, lsl#10 // ldmfd sp!, {rsamp, cbits, rvoice, pc} // return //----------------------------------------------------------------------------- /****************************************************************************** * na_mix_pcm16(*) * * * * mix 16-bit data into work buffer ******************************************************************************/ private function na_mix_pcm16 stmfd sp!, {rsamp, cbits, rvoice, lr} //----------------------------------------------------------------------------- cmp cvol, #0 // skip mixing if vol is 0 muleq r0, sfreq, mixc // addeq r0, sfreq, r0 // ldmeqfd sp!, {rsamp, cbits, rvoice, pc} // //----------------------------------------------------------------------------- ldr rsrc,=na_buffer+M2_FETCH // point to fetch mov r0, sread, lsr#10 // get read integer sub sread, sread, r0, lsl#10 // clear integer in read and r1, r0, #1 // mask low bit add rsrc, rsrc, r1, lsl#1 // add offset to fetch pointer stmfd sp!, {r0} add rsrc, rsrc, #1 // we will only use the hbyte of each sample... //----------------------------------------------------------------------------- .macro mix16w sa //----------------------------------------------------------------------------- mov smp1, sread, lsr#10 // 1 get read integer ldrb smp1, [rsrc, smp1, lsl#1] // 3 load byte from src + read 8 2 add sread, sfreq // 1 add rate to position eor smp1, #0x80 // 1 unsign sample :( mul smp1, cvol, smp1 // 2 multiply by volume (both l/r) bic smp1, #0x0F0000 // 1 prepare for shift add \sa, smp1, lsr#4 // 1 add shifted value to mix .endm //----------------------------------------------------------------------------- subs mixc, mixc, #7 bmi .mp16_mixn_exit .mp16_mixn: //----------------------------------------------------------------------------- ldmia mdest, {m1,m2,m3,m4,m5,m6,m7} // 9 read 7 mix-samples mix16w m1 // 10x7 mix data mix16w m2 // mix16w m3 // mix16w m4 // mix16w m5 // mix16w m6 // mix16w m7 // stmia mdest!, {m1,m2,m3,m4,m5,m6,m7} // 8 write 7 samples subs mixc, mixc, #7 // loop N times... bpl .mp16_mixn // //----------------------------------------------------------------------------- .mp16_mixn_exit: adds mixc, mixc, #7 // fix count beq .mp16_mix1_exit // exit if zero //----------------------------------------------------------------------------- .mp16_mix1: // mix single samples until finished ldmia mdest, {m1} // mix16w m1 // stmia mdest!, {m1} // subs mixc, mixc, #2 // bmi .mp16_mix1_exit // ldmia mdest, {m1} // mix16w m1 // stmia mdest!, {m1} // bne .mp16_mix1 // //----------------------------------------------------------------------------- .mp16_mix1_exit: ldmfd sp!, {r0} add sread, sread, r0, lsl#10 // add old read integer ldmfd sp!, {rsamp, cbits, rvoice, pc} //----------------------------------------------------------------------------- /****************************************************************************** * int div19( num, denom ) * * divide function, 40..68 cycles, 66 words of code * result is rounded upward * * denom is hacked to be "r7" ******************************************************************************/ private function div19 mov r3, #0 cmp r0, r7, lsl#10 // speed hack bcc 1f //----------------------------------------------------------------------------- .macro div_iter s cmp r0, r7, lsl#\s // 3 cycles subcs r0, r7, lsl#\s // addcs r3, r3, #1<<\s // .endm //----------------------------------------------------------------------------- div_iter 19 div_iter 18 div_iter 17 div_iter 16 div_iter 15 div_iter 14 div_iter 13 div_iter 12 div_iter 11 div_iter 10 1: div_iter 9 div_iter 8 div_iter 7 div_iter 6 div_iter 5 div_iter 4 div_iter 3 div_iter 2 div_iter 1 div_iter 0 //----------------------------------------------------------------------------- cmp r0, #1 // round up adc r0, r3, #0 // //----------------------------------------------------------------------------- bx lr .pool /******************************************************************************* * computeFilterCoefficients( cutoff, resonance ) * * cutoff = 0..127 * resonance = 0..127 * * returns: * r0 = a0 (gain) * r1 = b0 * r2 = b1 *******************************************************************************/ computeFilterCoefficients: //------------------------------------------------------------------------------ push {r4-r7} // adr r2, ITcutoffTable // read LUTs with inputs adr r3, ITresonanceTable // ldr r0, [r2, r0, lsl#2] // r0=x (0.32) ldr r4, [r3, r1, lsl#2] // r4=y (0.32) //------------------------------------------------------------------------------ // x *= mixingfreq / 65536 mov r0, r0, lsr#1 // mixingfreq = 32768 //------------------------------------------------------------------------------ umull r5, r3, r0, r0 // r5 = x^2 (.16*.16 =.16) mov r5, r5, lsr#16 // orr r5, r3, lsl#16 // //------------------------------------------------------------------------------ umull r2, r6, r0, r4 // r6 = x*y (.16*.32 =.16) //------------------------------------------------------------------------------ mov r4, r4, lsr#16 // convert y .32->.16 //------------------------------------------------------------------------------ mov r0, #0xFFFFFFFF>>1 // r0=fg=0xFFFFFFFF / (xx+xy+y) add r1, r4, r5 // (fg=.16) add r1, r6 // lsr r1, #1 // >>1 for unsigned division swi 0x090000 // DIVIDE //------------------------------------------------------------------------------ // r1=fb0 = fg*(2xx+xy+y-1.0): add r5, r6, r5, lsl#1 // r5=2xx+xy add r5, r4 // +y sub r5, #65536 // -1.0 umull r1, r2, r5, r0 // r1,r2 = fb0 << 16 mov r1, r1, lsr#16 // r1=fb0 orr r1, r1, r2, lsl#16 // //------------------------------------------------------------------------------ mov r2, #65536 // r2=fb1 = 1.0-fg-fb0 sub r2, r2, r0 // sub r2, r2, r1 // //------------------------------------------------------------------------------ // r0,r1,r2 = a0,b0,b1 // a0: 0.16 // a1: 1.16 // a2: s.16 signed, always negative //------------------------------------------------------------------------------ // convert results: mov r0, r0, lsr#3 // a0 = 0.13 mov r1, r1, lsr#3 // a1 = 1.13 mov r2, r2, asr#3 // a2 = signed 0.13 //------------------------------------------------------------------------------ pop {r4-r7} // return bx lr // //------------------------------------------------------------------------------ // 0.32 unsigned //------------------------------------------------------------------------------ ITcutoffTable: //------------------------------------------------------------------------------ .word 0x004FBC33,0x004D771B,0x004B428D,0x00491E12,0x00470934,0x00450381,0x00430C8B,0x004123E7 .word 0x003F492C,0x003D7BF5,0x003BBBDF,0x003A088B,0x0038619B,0x0036C6B5,0x00353782,0x0033B3AC .word 0x00323AE1,0x0030CCD0,0x002F692B,0x002E0FA5,0x002CBFF5,0x002B79D4,0x002A3CFC,0x00290929 .word 0x0027DE19,0x0026BB8D,0x0025A146,0x00248F09,0x0023849A,0x002281C0,0x00218646,0x002091F3 .word 0x001FA496,0x001EBDFA,0x001DDDEF,0x001D0445,0x001C30CD,0x001B635A,0x001A9BC1,0x0019D9D6 .word 0x00191D70,0x00186668,0x0017B495,0x001707D2,0x00165FFA,0x0015BCEA,0x00151E7E,0x00148494 .word 0x0013EF0C,0x00135DC6,0x0012D0A3,0x00124784,0x0011C24D,0x001140E0,0x0010C322,0x001048F9 .word 0x000FD24B,0x000F5EFD,0x000EEEF7,0x000E8222,0x000E1866,0x000DB1AD,0x000D4DE0,0x000CECEB .word 0x000C8EB8,0x000C3334,0x000BDA4A,0x000B83E9,0x000B2FFD,0x000ADE75,0x000A8F3F,0x000A424A .word 0x0009F786,0x0009AEE3,0x00096851,0x000923C2,0x0008E126,0x0008A070,0x00086191,0x0008247C .word 0x0007E925,0x0007AF7E,0x0007777B,0x00074111,0x00070C33,0x0006D8D6,0x0006A6F0,0x00067675 .word 0x0006475C,0x0006199A,0x0005ED25,0x0005C1F4,0x000597FE,0x00056F3A,0x0005479F,0x00052125 .word 0x0004FBC3,0x0004D771,0x0004B428,0x000491E1,0x00047093,0x00045038,0x000430C8,0x0004123E .word 0x0003F492,0x0003D7BF,0x0003BBBD,0x0003A088,0x00038619,0x00036C6B,0x00035378,0x00033B3A .word 0x000323AE,0x00030CCD,0x0002F692,0x0002E0FA,0x0002CBFF,0x0002B79D,0x0002A3CF,0x00029092 .word 0x00027DE1,0x00026BB8,0x00025A14,0x000248F0,0x00023849,0x0002281C,0x00021864,0x0002091F //------------------------------------------------------------------------------ // 0.32 unsigned //------------------------------------------------------------------------------ ITresonanceTable: //------------------------------------------------------------------------------ .word 0xFFFFFFFF,0xFA887400,0xF52ECB00,0xEFF26200,0xEAD29800,0xE5CED300,0xE0E67800,0xDC18F100 .word 0xD765AC00,0xD2CC1900,0xCE4BAC00,0xC9E3DC00,0xC5942100,0xC15BF800,0xBD3AE100,0xB9305C00 .word 0xB53BEF00,0xB15D2100,0xAD937C00,0xA9DE8C00,0xA63DE000,0xA2B10900,0x9F379A00,0x9BD12A00 .word 0x987D5000,0x953BA800,0x920BCD00,0x8EED5F00,0x8BDFFD00,0x88E34B00,0x85F6EE00,0x831A8C00 .word 0x804DCE00,0x7D905F00,0x7AE1E9FF,0x78421E00,0x75B0AB00,0x732D4200,0x70B79680,0x6E4F5D80 .word 0x6BF44D80,0x69A61E00,0x67648980,0x652F4A80,0x63061D80,0x60E8C080,0x5ED6F380,0x5CD07700 .word 0x5AD50D00,0x58E47900,0x56FE7F80,0x5522E700,0x53517680,0x5189F700,0x4FCC3100,0x4E17F080 .word 0x4C6D0100,0x4ACB2F7F,0x49324A00,0x47A22000,0x461A8200,0x449B4080,0x43242E80,0x41B51EFF .word 0x404DE5FF,0x3EEE5940,0x3D964E40,0x3C459C00,0x3AFC1A80,0x39B9A280,0x387E0D80,0x37493580 .word 0x361AF63F,0x34F32B3F,0x33D1B13F,0x32B66600,0x31A1277F,0x3091D480,0x2F884D00,0x2E8470FF .word 0x2D8621C0,0x2C8D40C0,0x2B99B080,0x2AAB53BF,0x29C20E00,0x28DDC3C0,0x27FE5940,0x2723B480 .word 0x264DBB00,0x257C5300,0x24AF643F,0x23E6D580,0x23228F80,0x22627A40,0x21A67F40,0x20EE8800 .word 0x203A7E40,0x1F8A4D00,0x1EDDDEFF,0x1E351F7F,0x1D8FFA9F,0x1CEE5C9F,0x1C503220,0x1BB5685F .word 0x1B1DECE0,0x1A89AD7F,0x19F89880,0x196A9CBF,0x18DFA93F,0x1857AD3F,0x17D298DF,0x17505BDF .word 0x16D0E6FF,0x16542ADF,0x15DA18A0,0x1562A1BF,0x14EDB800,0x147B4D80,0x140B545F,0x139DBF80 .word 0x133281C0,0x12C98E20,0x1262D860,0x11FE541F,0x119BF560,0x113BB080,0x10DD79E0,0x10814640 //============================================================================= .text THUMB_CODE //============================================================================= getVoiceAddress: lsl r0, #5 ldr r2,=na_voices add r0, r2 mov pc, r3 .macro mac_getVoice mov r3, pc b getVoiceAddress .endm /****************************************************************************** *; void naxSetSource( index, source ) *; *; Set voice source ******************************************************************************/ public function naxSetSource cmp r1, #0 beq naxStop mac_getVoice mov r3, #(1-2)&255 // r1 = source - (2<<24) + (1<<24) lsl r3, #24 // (mainram address + startbit) add r1, r3 // str r1, [r0, #V_SAMP] mov r1, #0 str r1, [r0, #V_S1] //; reset previous samples str r1, [r0, #V_READ] //; reset read position bx lr //; return /****************************************************************************** *; void naxSetOffset( index, source_offset ) *; *; Set voice sample offset ******************************************************************************/ public function naxSetOffset mac_getVoice str r1, [r0, #V_READ] bx lr //; return /****************************************************************************** *; void naxSetPitch( index, rate ) *; *; set voice pitch *; *; rate is a 3.10 fixed point number (32khz base, 2048 = +1 octave (64khz)) *; OR a DS period (depending on channel type/audio mode) ******************************************************************************/ public function naxSetPitch mac_getVoice strh r1, [r0, #V_FREQ] bx lr /****************************************************************************** * void naxStop( index ) * * stop voice *****************************************************************************/ public function naxStop mac_getVoice ldrb r1, [r0, #V_START] mov r2, #2 orr r1, r2 strb r1, [r0, #V_START] mov r1, #0 strh r1, [r0, #V_TVOL] bx lr /****************************************************************************** * void naxStopNow( index ) * * stop voice immediately ******************************************************************************/ public function naxStopNow push {r0} mac_getVoice mov r1, #0 str r1, [r0, #V_SAMP] pop {r0} cmp r0, #16 bge .not_hardwarech ldr r1,=na_mode ldrb r1, [r1] cmp r1, #1 beq .not_hardwarech ldr r1,=0x4000400 // reset hardware channel lsl r0, #4 // add r1, r0 // mov r0, #0 // str r0, [r1] // .not_hardwarech: bx lr /****************************************************************************** * int naxSourceMatch( index, source ) * * test if voice source matches ******************************************************************************/ public function naxSourceMatch ldr r2,=na_voices+V_SAMP lsl r0, #5 ldr r0, [r2, r0] lsl r0, #8 lsr r0, #8 ldr r2,=0x2000000 add r0, r2 sub r0, r1 bx lr /****************************************************************************** * bool(int) naxIsActive( index ) * * test active status of channel ******************************************************************************/ public function naxIsActive mac_getVoice ldr r0, [r0, #V_SAMP] lsl r0, #8 bx lr /****************************************************************************** * naxSetVolume( index, volume ) * * set voice volume * * volume 0..65535 ******************************************************************************/ public function naxSetVolume mac_getVoice strh r1, [r0, #V_TVOL] bx lr /****************************************************************************** * naxSetPanning( index, panning ) * * set channel panning * * panning 0..255 ******************************************************************************/ public function naxSetPanning mac_getVoice lsl r1, #8 strh r1, [r0, #V_TPAN] bx lr /****************************************************************************** * naxSetFilter( index, cutoff, resonance ) * * set channel FILTER * * cutoff 0..127 * resonance 0..127 * * filtering only used in MODE1 ******************************************************************************/ public function naxSetFilter push {r4-r6, lr} //----------------------------------------------------------------------------- lsl r0, #5 // get voice (r4) ldr r4,=na_voices // add r4, r0 // //----------------------------------------------------------------------------- ldrh r0, [r4, #V_FILTER] // test:quit if cached parameters match lsl r3, r2, #7 // orr r3, r1 // lsl r3, #1 // add r3, #1 // cmp r3, r0 // beq 1f // //----------------------------------------------------------------------------- strh r3, [r4, #V_FILTER] // save parameters/enable filter //----------------------------------------------------------------------------- mov r0, r1 // compute coefficients mov r1, r2 // ldr r2,=computeFilterCoefficients // bl _call_via_r2 // strh r0, [r4, #V_A0] // strh r1, [r4, #V_B0] // strh r2, [r4, #V_B1] // //----------------------------------------------------------------------------- 1: pop {r4-r6} pop {r0} bx r0 /****************************************************************************** *; naxDisableFilter( index ) *; *; disable channel FILTER ******************************************************************************/ public function naxDisableFilter // voice[index].FILTER = 0 mac_getVoice // mov r1, #0 // strh r1, [r0, #V_FILTER] // bx lr // /****************************************************************************** *; void naxLockVoices( bitmask:16 ) *; *; lock selected voices ******************************************************************************/ public function naxLockVoices //----------------------------------------------------------------------------- ldr r1, =na_ch_mask // mask out channels ldrh r2, [r1] // mov r3, r2 // bic r3, r0 // strh r3, [r1] // //----------------------------------------------------------------------------- .nlv_resetnew: eor r3, r2 // r3 = changed mask //----------------------------------------------------------------------------- ldr r0,=0x4000400 // reset channels that have been changed mov r1, #0 // lsr r3, #1 // bcc .nlv_skipchannel // .nlv_turnoffchannel: // str r1, [r0] // .nlv_skipchannel: // 1: add r0, #16 // lsr r3, #1 // bcs .nlv_turnoffchannel // bne .nlv_skipchannel // //----------------------------------------------------------------------------- bx lr // /****************************************************************************** *; void naxUnlockVoices( bitmask:16 ) *; *; unlock selected voices ******************************************************************************/ public function naxUnlockVoices //----------------------------------------------------------------------------- ldr r1, =na_ch_mask // set channels ldrh r2, [r1] // mov r3, r2 // orr r3, r0 // strh r3, [r1] // //----------------------------------------------------------------------------- b .nlv_resetnew public function naxGetMode ldr r0,=na_mode ldrb r0, [r0] ret public function naxSetRampingSpeed ldr r1,=na_ramp_speed str r0, [r1] ret //============================================================================= public function naxGetLevels //============================================================================= push {r4,r5,r6} //; ldr r1,=na_voices //; r1 = source (voices) mov r2, #30 //; r2 = counter ldr r6,=65535 //----------------------------------------------------------------------------- .loop: ldrh r3, [r1, #V_CPAN] //; r3 = pan (0..65535) ldrh r4, [r1, #V_CVOL] //; r4 = vol (0..65535) //----------------------------------------------------------------------------- mov r5, r4 //; compute right volume mul r5, r3 //; (x*p) lsr r5, #16+8 //; strb r5, [r0, #1] //; eor r3, r6 //; compute left volume mul r4, r3 //; (x*(1-p)) lsr r4, #16+8 //; strb r4, [r0] //; //----------------------------------------------------------------------------- add r0, #2 //; increment dest add r1, #V_SIZE //; increment source sub r2, #1 //; decrement counter bne .loop //; //----------------------------------------------------------------------------- pop {r4,r5,r6} bx lr public function naxTestUnlocked ldr r1,=na_ch_mask ldrh r1, [r1] mov r2, #1 lsl r2, r0 and r2, r1 mov r0, r2 bx lr /****************************************************************************** *; void naxReset( int mode ) *; *; reset and select audio mode ******************************************************************************/ public function naxReset //----------------------------------------------------------------------------- push {r4, r5, lr} push {r0} //----------------------------------------------------------------------------- ldr r0,=naxAudioResetFlag mov r1, #1 strb r1, [r0] ldr r0,=REG_TM0CNT_L // disable timer mov r1, #0 // str r1, [r0] // //----------------------------------------------------------------------------- mov r0, #32 // a delay ??! 1: sub r0, #1 // bne 1b // //----------------------------------------------------------------------------- ldr r1,=REG_SOUNDCNT // disable sound (preserving special settings) ldrh r2, [r1] // mov r3, #0x7F // lsl r3, #8 // and r2, r3 // strh r2, [r1] // //----------------------------------------------------------------------------- ldr r0,=na_buffer // erase na_buffer ldr r1,=na_buffer_end // sub r1, r0 // lsr r1, #2 // mov r2, #0 // 1: stmia r0!, {r2} // sub r1, #1 // bne 1b // //----------------------------------------------------------------------------- bl naSuspendIRQ_t //----------------------------------------------------------------------------- ldr r0,=na_mode // unlock SW mixer channels ldrb r1, [r0] // reset higher bits cmp r1, #2 // bne 1f // ldr r0,=na_ch_mask // ldrh r1, [r0] // mov r2, #0b11000000 // orr r1, r2 // str r1, [r0] // 1: // //----------------------------------------------------------------------------- ldr r0,=na_voices // reset all voices mov r1, #NA_NVOICES // mov r3, #0 // 1: str r3, [r0, #V_SAMP] // add r0, #V_SIZE // sub r1, #1 // bne 1b // //----------------------------------------------------------------------------- ldr r0,=REG_SOUND0CNT // reset hardware channels ldr r2,=na_ch_mask // ldrh r1, [r2] // str r1, [r2] //<--- reset SW channel mask mov r2, #0 // mov r3, #0 // b .rhc_next .rhc_clear: // stmia r0!, {r2, r3} // stmia r0!, {r2, r3} // .rhc_next: // lsr r1, #1 // bcs .rhc_clear // add r0, #16 // cmp r1, #0 // bne .rhc_next // //----------------------------------------------------------------------------- pop {r0} ldr r1,=na_mode strb r0, [r1] cmp r0, #1 beq .mode_1 bgt .mode_2 //----------------------------------------------------------------------------- .mode_0: bl enableSound ldr r1,=0x00C1F800 //; start update timer ldr r0,=REG_TM0CNT_L // str r1, [r0] // b _setup_complete //----------------------------------------------------------------------------- .mode_1: bl setupMode1 b _setup_complete //----------------------------------------------------------------------------- .mode_2: bl setupMode2 //----------------------------------------------------------------------------- _setup_complete: bl naRestoreIRQ_t pop {r4, r5} pop {r0} bx r0 /****************************************************************************** * void enableSound() * * enable sound via SOUNDCNT ******************************************************************************/ private function enableSound ldr r1,=REG_SOUNDCNT //; SOUNDCNT = full volume | enable ldr r2, [r1] // ldr r3,=0x807F // orr r2, r3 // str r2, [r1] // bx lr // /****************************************************************************** * void setupMode1() * * setup mode1 audio ******************************************************************************/ private function setupMode1 //----------------------------------------------------------------------------- push {r4-r6, lr} @ldr r7,=REG_TM0CNT_L // r7=timer //----------------------------------------------------------------------------- ldr r0,=REG_SOUND0CNT // R0=SOUNDREGS ldr r1,=na_ch_mask // R1=CHMASK ldrh r1, [r1] // ldr r2,=(0xA8 << 24) // CNT ldr r3,=na_buffer+M1_OUTPUT // SAD ldr r4,=0x0000FE00 // TMR, PNT ldr r5,=128 // LEN mov r6, #128 // R6 = 512 lsl r6, #2 //----------------------------------------------------------------------------- lsr r1, #1 // shift out maskbit bcc .sm1_nextvoice // //----------------------------------------------------------------------------- .sm1_setup_stream: // stmia r0!, {r2-r5} // init channel sub r0, r0, #16 // .sm1_nextvoice: //----------------------------------------------------------------------------- add r3, r6 // iterate through channels add r0, r0, #16 // lsr r1, #1 // bcs .sm1_setup_stream // bne .sm1_nextvoice // //----------------------------------------------------------------------------- ldr r4,=m2_output_position // reset output position mov r5, #0 // strb r5, [r4] // //----------------------------------------------------------------------------- bl enableSound // start sound output //----------------------------------------------------------------------------- ldr r0,=1024*16/4 // delay 12 samples 1: sub r0, #1 // (pcm startup time + extra) bne 1b // //----------------------------------------------------------------------------- ldr r0,=REG_TM0CNT_L // start update timer ldr r1,=0x00C1F800 // str r1, [r0] // //----------------------------------------------------------------------------- pop {r4-r6, pc} /****************************************************************************** * void setupMode2() * * setup audio mode2 ******************************************************************************/ private function setupMode2 //----------------------------------------------------------------------------- push {r4,r5, lr} ldr r0,=na_ch_mask //; lock channels 7&8 (stream channels) ldr r1, [r0] //; unlock software channels (16->31) mov r2, #0b11000000 // bic r1, r2 // ldr r2,=0xFFFF0000 // orr r1, r2 // str r1, [r0] // //----------------------------------------------------------------------------- ldr r1,=REG_SOUND0CNT+6*16 //; channels 6&7 are used for //; software mixed stream //----------------------------------------------------------------------------- mov r2, #0 //; reset output position ldr r3,=m2_output_position // str r2, [r3] // //----------------------------------------------------------------------------- str r2, [r1, #0] //; disable sound channels str r2, [r1, #16] // strh r2, [r1, #10] //; clear loop points strh r2, [r1, #16] // //----------------------------------------------------------------------------- ldr r2,=na_buffer+M2_MIX_OUTPUT //; setup channel sources str r2, [r1, #4] // ldr r2,=na_buffer+M2_MIX_OUTPUT+M2_BUFFERLENGTH*2 str r2, [r1, #4+16] //----------------------------------------------------------------------------- ldr r2,=-768 //; set sampling frequencies strh r2, [r1, #8] // strh r2, [r1, #8+16] // //----------------------------------------------------------------------------- ldr r2,=M2_BUFFERLENGTH/2 //; set source lengths str r2, [r1, #12] // str r2, [r1, #12+16] // //----------------------------------------------------------------------------- ldr r2,=127+(0 <<16)+(1<<27)+(1<<29)+(1<<31) // vol pan looped pcm16 enable ldr r3,=127+(127<<16)+(1<<27)+(1<<29)+(1<<31) //----------------------------------------------------------------------------- str r3, [r1, #0+16] //; enable channels str r2, [r1, #0] // bl enableSound // //----------------------------------------------------------------------------- ldr r2,=1024*16/4 //; delay some samples 1: sub r2, #1 // bne 1b // //----------------------------------------------------------------------------- ldr r1,=0x00C1F800 //; start update timer ldr r0,=REG_TM0CNT_L // str r1, [r0] // //----------------------------------------------------------------------------- pop {r4, r5, pc} .pool