png screenshot support
[drnoksnes] / dspMixer.s
1         .TEXT
2         .ARM
3         .ALIGN
4
5 #include "mixrate.h"
6
7 @@@@@@@@@@@@@@@@@@@@@@@@@@@@
8 @ Function called with:
9 @ r0 - Raw brr data (s8*)
10 @ r1 - Decoded sample data (s16*)
11 @ r2 - DspChannel *channel
12 @@@@@@@@@@@@@@@@@@@@@@@@@@@@
13 @ Function Data:
14 @ r4 - r4
15 @ r5 - r5
16 @ r6,r7 - tmp
17 @ r8 - shift amount
18 @ r9 - number of iterations left
19 @ r10 - 0xf
20 @ r11 - low clip
21 @ r12 - high clip
22
23
24 .GLOBAL brrHash
25 brrHash:
26 .word 0
27
28 .GLOBAL DecodeSampleBlockAsm
29 DecodeSampleBlockAsm:
30     stmfd sp!, {r4-r12,r14}
31
32     @ Save the channel pointer
33     mov r14, r2
34     
35 /*    @ Hash the block, and skip the decode if we can
36     ldmia r0, {r4-r7}
37     ldr r8, =0x050C5D1F @ (2166136261 * 16777619)
38     ldr r9, =16777619
39     eor r8, r8, r4
40     mul r8, r9, r8
41     eor r8, r8, r5
42     mul r8, r9, r8
43     eor r8, r8, r6
44
45     @ r8 is the hashed value
46
47     ldr r4, brrHash
48
49     @ Compute the actual brr location minus the apu ram base
50     ldr r6, =APU_MEM
51     ldr r6, [r6]
52     sub r6, r0, r6
53
54     @ Compute the address of the cached samples in brrHash
55     add r3, r4, #0x8000
56     mov r6, r6, lsr #3
57     add r3, r3, r6, lsl #5
58
59     @ Load the previous hash value
60     ldr r5, [r4, r6, lsl #2]
61     str r8, [r4, r6, lsl #2]
62     cmp r5, r8
63     bne doDecode
64
65     @ Load the cached samples
66     ldmia r3, {r4-r11}
67     stmia r1!, {r4-r11}
68
69     ldrsh r2, [r1, #-2]
70     ldrsh r3, [r1, #-4]
71
72     b doneDecodeCached
73
74 doDecode:
75     stmfd sp!, {r3}
76 */
77     @ Load r2 and r3
78     ldrsh r2, [r14, #62]
79     ldrsh r3, [r14, #64]
80     
81     ldrb r4, [r0], #1
82     @ Compute the index into the brrTab to load the bytes from
83     mov r9, r4, lsr #4
84     ldr r8, =brrTab
85     add r8, r8, r9, lsl #5 @ brrTabPtr = brrTab + (r4 * 32)
86
87     mov r10, #0xf << 1
88     ldr r11, =0xffff8000
89     ldr r12, =0x7fff
90
91     @ 16 samples to decode, but do two at a time
92     mov r9, #8
93     @ Figure out the type of decode filter
94     mov r4, r4, lsr #2
95     and r4, r4, #3
96     ldr pc, [pc, r4, lsl #2]
97     nop
98 .word case0
99 .word case1
100 .word case2
101 .word case3
102 case0:
103     ldrb r4, [r0], #1
104     and r5, r10, r4, lsl #1
105     ldrsh r5, [r8, r5]
106     and r4, r10, r4, lsr #3
107     ldrsh r4, [r8, r4]
108
109     mov r4, r4, lsl #1
110     mov r5, r5, lsl #1
111     strh r4, [r1], #2
112     strh r5, [r1], #2
113
114     subs r9, r9, #1
115     bne case0
116
117     @ Set up r2 and r3
118     ldrsh r2, [r1, #-2]
119     ldrsh r3, [r1, #-4]
120     
121     b doneDecode
122
123 case1:
124     ldrb r4, [r0], #1
125     and r5, r10, r4, lsl #1
126     ldrsh r5, [r8, r5]
127     and r4, r10, r4, lsr #3
128     ldrsh r4, [r8, r4]
129
130     @ r3 = r4 + (last1 >> 1) - (last1 >> 5)    
131     add r3, r4, r2, asr #1
132     sub r3, r3, r2, asr #5
133     
134     cmp r3, r12
135     movgt r3, r12
136     cmp r3, r11
137     movlt r3, r11
138
139     mov r3, r3, lsl #1
140     strh r3, [r1], #2
141     ldrsh r3, [r1, #-2]
142
143     @ same for r2 now
144     add r2, r5, r3, asr #1
145     sub r2, r2, r3, asr #5
146
147     cmp r2, r12
148     movgt r2, r12
149     cmp r2, r11
150     movlt r2, r11
151
152     mov r2, r2, lsl #1
153     strh r2, [r1], #2
154     ldrsh r2, [r1, #-2]
155
156     subs r9, r9, #1
157     bne case1
158
159     b doneDecode
160
161 case2:
162     ldrb r4, [r0], #1
163     and r5, r10, r4, lsl #1
164     ldrsh r5, [r8, r5]
165     and r4, r10, r4, lsr #3
166     ldrsh r4, [r8, r4]
167
168     @ Sample 1
169     mov r6, r3, asr #1
170     rsb r6, r6, r3, asr #5
171     mov r3, r2
172     add r7, r2, r2, asr #1
173     rsb r7, r7, #0
174     add r6, r6, r7, asr #5
175     add r7, r4, r2
176     add r2, r6, r7
177
178     cmp r2, r12
179     movgt r2, r12
180     cmp r2, r11
181     movlt r2, r11
182     mov r2, r2, lsl #1
183     strh r2, [r1], #2
184     ldrsh r2, [r1, #-2]
185
186     @ Sample 2
187     mov r6, r3, asr #1
188     rsb r6, r6, r3, asr #5
189     mov r3, r2
190     add r7, r2, r2, asr #1
191     rsb r7, r7, #0
192     add r6, r6, r7, asr #5
193     add r7, r5, r2
194     add r2, r6, r7
195
196     cmp r2, r12
197     movgt r2, r12
198     cmp r2, r11
199     movlt r2, r11
200     mov r2, r2, lsl #1
201     strh r2, [r1], #2
202     ldrsh r2, [r1, #-2]
203     
204     subs r9, r9, #1
205     bne case2
206
207     b doneDecode
208
209 case3:
210     ldrb r4, [r0], #1
211     and r5, r10, r4, lsl #1
212     ldrsh r5, [r8, r5]
213     and r4, r10, r4, lsr #3
214     ldrsh r4, [r8, r4]
215
216     @ Sample 1
217     add r6, r3, r3, asr #1
218     mov r6, r6, asr #4
219     sub r6, r6, r3, asr #1
220     mov r3, r2
221     add r7, r2, r2, lsl #2
222     add r7, r7, r2, lsl #3
223     rsb r7, r7, #0
224     add r6, r6, r7, asr #7
225     add r6, r6, r2
226     add r2, r4, r6
227
228     cmp r2, r12
229     movgt r2, r12
230     cmp r2, r11
231     movlt r2, r11
232     mov r2, r2, lsl #1
233     strh r2, [r1], #2
234     ldrsh r2, [r1, #-2]
235
236     @ Sample 2
237     add r6, r3, r3, asr #1
238     mov r6, r6, asr #4
239     sub r6, r6, r3, asr #1
240     mov r3, r2
241     add r7, r2, r2, lsl #2
242     add r7, r7, r2, lsl #3
243     rsb r7, r7, #0
244     add r6, r6, r7, asr #7
245     add r6, r6, r2
246     add r2, r5, r6
247
248     cmp r2, r12
249     movgt r2, r12
250     cmp r2, r11
251     movlt r2, r11
252     mov r2, r2, lsl #1
253     strh r2, [r1], #2
254     ldrsh r2, [r1, #-2]
255
256     subs r9, r9, #1
257     bne case3
258
259 doneDecode:
260 /*    sub r1, r1, #32
261     ldmia r1, {r4-r11}
262     ldmfd sp!, {r1}
263     stmia r1, {r4-r11}*/
264
265 doneDecodeCached:
266     @ Store r2 and r3
267     strh r2, [r14, #62]
268     strh r3, [r14, #64]
269
270     ldmfd sp!, {r4-r12,r14}
271     bx lr
272
273 #define ENVSTATE_INCREASE       6
274 #define ENVSTATE_BENTLINE       7
275 #define ENVSTATE_DECREASE       8
276 #define ENVSTATE_DECEXP         9
277
278 @@@@@@@@@@@@@@@@@@@@@@@@@@@@
279 @ Function called with:
280 @ r0 - int Number of samples to mix
281 @ r1 - u16* mix buffer (left first, right is always 4000 * 4 bytes ahead
282 @@@@@@@@@@@@@@@@@@@@@@@@@@@@
283
284 #define PREVDECODE_OFFSET 16
285 #define BLOCKPOS_OFFSET 66
286 #define KEYWAIT_OFFSET 76
287
288 @ r0 - channel structure base
289 @ r1 - mix buffer
290 @ r2 - echo buffer ptr
291 @ r3 - numSamples
292 @ r4 - sampleSpeed
293 @ r5 - samplePos
294 @ r6 - envCount
295 @ r7 - envSpeed
296 @ r8 - sampleValue (value of the current sample)
297 @ r9 - tmp
298 @ r10 - leftCalcVol
299 @ r11 - rightCalcVol
300 @ r12 - tmp
301 @ r13 - tmp
302 @ r14 - tmp
303
304 .GLOBAL DspMixSamplesStereo
305 .FUNC DspMixSamplesStereo
306 DspMixSamplesStereo:
307     stmfd sp!, {r4-r12, lr}
308
309     mov r3, #0
310     strb r3, channelNum
311     str r0, numSamples
312
313     @ Store the original mix buffer for use later
314     stmfd sp!, {r1}
315
316     @ Clear the left and right mix buffers, saving their initial positions
317     ldr r1, =r1
318     ldr r2, =echoBuffer
319     mov r3, #0
320     mov r4, #0
321     mov r5, #0
322     mov r6, #0
323 clearLoop:
324     stmia r1!, {r3-r6}
325     stmia r1!, {r3-r6}
326     stmia r2!, {r3-r6}
327     stmia r2!, {r3-r6}
328     subs r0, r0, #4
329     cmp r0, #0
330     bgt clearLoop
331
332     @ Load the initial mix buffer and echo position
333     ldr r1, =r1
334     ldr r2, =echoBuffer
335
336     ldr r0, =channels
337 channelLoopback:
338     @ Check if active == 0, then next
339     ldrb r3, [r0, #77]
340     cmps r3, #0
341     beq nextChannelNothingDone
342
343     @ Save the start position of the mix buffer & echo buffer
344     stmfd sp!, {r1,r2}
345
346     @ Get echo enabled, then replace the opcode there if it's enabled
347     ldrb r14, [r0, #79]
348     cmp r14, #1
349     ldr r3, =0x01A00000 @ mov r0, r0
350     streq r3, branchLocation
351
352     ldrb r3, numSamples
353     @ Load the important variables into registers
354     ldmia r0, {r4-r7}
355     ldrsh r10, [r0, #68]
356     ldrsh r11, [r0, #70]
357
358 mixLoopback:
359
360     @ Commence the mixing
361     subs r6, r6, r7
362     bpl noEnvelopeUpdate
363
364     @ Update envelope
365     mov r6, #0x7800
366
367     ldrsh r9, [r0, #60]
368     ldrb r12, [r0, #72]
369
370     ldr pc, [pc, r12, lsl #2]
371     nop
372 @ Jump table for envelope handling
373 .word noEnvelopeUpdate
374 .word envStateAttack
375 .word envStateDecay
376 .word envStateSustain
377 .word envStateRelease
378 .word noEnvelopeUpdate      @ Actually direct, but we don't need to do anything
379 .word envStateIncrease
380 .word envStateBentline
381 .word envStateDecrease
382 .word envStateSustain       @ Actually decrease exponential, but it's the same code
383
384 envStateAttack:
385     add r9, r9, #4 << 8
386
387     cmp r9, #0x7f00
388     ble storeEnvx
389     @ envx = 0x7f, state = decay, speed = decaySpeed
390     mov r9, #0x7f00
391     mov r12, #2
392     strb r12, [r0, #72]
393     ldrh r7, [r0, #56]
394     b storeEnvx
395     
396 envStateDecay:
397     rsb r9, r9, r9, lsl #8
398     mov r9, r9, asr #8
399
400     ldrb r12, [r0, #73]
401     cmp r9, r12, lsl #8
402     bge storeEnvx
403     @ state = sustain, speed = sustainSpeed
404     mov r12, #3
405     strb r12, [r0, #72]
406     ldrh r7, [r0, #58]
407     
408     @ Make sure envx > 0
409     cmp r9, #0
410     bge storeEnvx
411     
412     @ If not, end channel, then go to next channel
413     stmfd sp!, {r0-r3, r14}
414     ldrb r0, channelNum
415     bl DspSetEndOfSample
416     ldmfd sp!, {r0-r3, r14}
417     b nextChannel
418     
419 envStateSustain:
420     rsb r9, r9, r9, lsl #8
421     mov r9, r9, asr #8
422
423     @ Make sure envx > 0
424     cmp r9, #0
425     bge storeEnvx
426
427     @ If not, end channel, then go to next channel
428     stmfd sp!, {r0-r3,r14}
429     ldrb r0, channelNum
430     bl DspSetEndOfSample
431     ldmfd sp!, {r0-r3,r14}
432     b nextChannel
433
434 envStateRelease:
435     sub r9, r9, #1 << 8
436
437     @ Make sure envx > 0
438     cmp r9, #0
439     bge storeEnvx
440
441     @ If not, end channel, then go to next channel
442     stmfd sp!, {r0-r3,r14}
443     ldrb r0, channelNum
444     bl DspSetEndOfSample
445     ldmfd sp!, {r0-r3,r14}
446     b nextChannel
447
448 envStateIncrease:
449     add r9, r9, #4 << 8
450
451     cmp r9, #0x7f00
452     ble storeEnvx
453     @ envx = 0x7f, state = direct, speed = 0
454     mov r9, #0x7f00
455     mov r12, #5
456     strb r12, [r0, #72]
457     mov r7, #0
458     b storeEnvx
459
460 envStateBentline:
461     cmp r9, #0x5f << 8
462     addgt r9, r9, #1 << 8
463     addle r9, r9, #4 << 8
464
465     cmp r9, #0x7f00
466     blt storeEnvx
467     @ envx = 0x7f, state = direct, speed = 0
468     mov r9, #0x7f00
469     mov r12, #5
470     strb r12, [r0, #72]
471     mov r7, #0
472     b storeEnvx
473
474 envStateDecrease:
475     sub r9, r9, #4 << 8
476
477     @ Make sure envx > 0
478     cmp r9, #0
479     bge storeEnvx
480     
481     @ If not, end channel, then go to next channel
482     stmfd sp!, {r0-r3,r14}
483     ldrb r0, channelNum
484     bl DspSetEndOfSample
485     ldmfd sp!, {r0-r3,r14}
486     b nextChannel
487
488 storeEnvx:
489     strh r9, [r0, #60]
490
491     @ Recalculate leftCalcVol and rightCalcVol
492     ldrsb r10, [r0, #74]
493     mul r10, r9, r10
494     mov r10, r10, asr #7
495
496     ldrsb r11, [r0, #75]
497     mul r11, r9, r11
498     mov r11, r11, asr #7
499     
500 noEnvelopeUpdate:
501     add r5, r5, r4
502     cmp r5, #16 << 12
503     blo noSampleUpdate
504     
505     @ Decode next 16 bytes...
506     sub r5, r5, #16 << 12
507
508     @ Decode the sample block, r0 = DspChannel*
509     stmfd sp!, {r0-r3, r14}
510     bl DecodeSampleBlock
511     cmps r0, #1
512     ldmfd sp!, {r0-r3, r14}
513     beq nextChannel
514
515 noSampleUpdate:
516     @ This is really a >> 12 then << 1, but since samplePos bit 0 will never be set, it's safe.
517     @ Must ensure that sampleSpeed bit 0 is never set, and samplePos is never set to anything but 0
518     @ TODO - The speed up hack doesn't work.  Find out why
519     mov r12, r5, lsr #12
520     add r12, r0, r12, lsl #1
521     ldrsh r8, [r12, #24]
522
523 branchLocation:
524     b mixEchoDisabled
525
526 mixEchoEnabled:
527     @ Echo mixing
528     ldr r9, [r2]
529     mla r9, r8, r10, r9
530     str r9, [r2], #4
531
532     ldr r9, [r2]
533     mla r9, r8, r11, r9
534     str r9, [r2], #4
535
536 mixEchoDisabled:
537     ldr r9, [r1]
538     mla r9, r8, r10, r9
539     str r9, [r1], #4
540
541     ldr r9, [r1]
542     mla r9, r8, r11, r9
543     str r9, [r1], #4
544
545     subs r3, r3, #1
546     bne mixLoopback
547
548 nextChannel:
549
550     @ Set ENVX and OUTX
551     ldrb r3, channelNum
552     ldr r12, =DSP_MEM
553     add r12, r12, r3, lsl #4
554
555     @ Set ENVX
556     ldrsh r9, [r0, #60]
557     mov r9, r9, asr #8
558     strb r9, [r12, #0x8]
559
560     @ Set OUTX
561     mul r9, r8, r9
562     mov r9, r9, asr #15
563     strb r9, [r12, #0x9]
564     
565     strh r10, [r0, #68]
566     strh r11, [r0, #70]
567
568     @ Store changing values
569     stmia r0, {r4-r7}
570
571     @ Reload mix&echo buffer position
572     ldmfd sp!, {r1,r2}
573
574 nextChannelNothingDone:
575     @ Move to next channel
576     add r0, r0, #80
577
578     @ Increment channelNum
579     ldrb r3, channelNum
580     add r3, r3, #1
581     strb r3, channelNum
582     cmps r3, #8
583     blt channelLoopback
584
585 @ This is the end of normal mixing
586
587 #ifdef NEVER
588     @ Store the original mix & echo buffers, cause we trash these regs
589     stmfd sp!, {r1, r2}
590
591     @ r0 - 
592     @ r1 - 
593     @ r2 - 
594     @ r3 - 
595     @ r4 - 
596     @ r5 - echo volume (right)
597     @ r6 - numSamples
598     @ r7 - echo in apu ram (r/w)
599     @ r8 - echo mix buffer (r/w)
600     @ r9 - end of echo in apu ram
601     @ r10 - echo volume (left)
602     @ r11 - echo feedback
603     @ r12 - FIR coefficients in DSP ram
604     @ r13 - FIR table base
605     @ r14 - FIR offset
606
607 @ Process the echo filter stuff
608 echoMixSetup:
609     mov r8, r2
610
611     ldr r0, =DSP_MEM
612
613     ldrsb r10, [r0, #0x2C] @ Get left echo volume
614     mov r10, r10, lsl #7
615     ldrsb r5, [r0, #0x3C] @ Get right echo volume
616     mov r5, r5, lsl #7
617
618     @ Get echo feedback
619     ldrsb r11, [r0, #0x0D]
620
621     @ Check if echo is enabled
622     ldrb r1, [r0, #0x6C]
623     strb r1, echoEnabled
624     @ Get echo base (APU_MEM + DSP_ESA << 8)
625     ldr r7, =echoBase
626     ldr r7, [r7]
627     str r7, echoBufferStart
628     @ Set up end of echo delay area in r8
629     ldr r0, =echoDelay
630     ldrh r0, [r0]
631     add r9, r7, r0
632
633     @ Set up current echo cursor location
634     ldr r0, =echoCursor
635     ldrh r0, [r0]
636     add r7, r7, r0
637
638 @    str r13, tmpSp
639
640     ldr r14, =firOffset
641     ldrb r14, [r14]
642
643     @ Offset firTable to start at FIR #7
644     ldr r12, =DSP_MEM
645     add r12, r12, #0x7F
646
647     ldr r6, numSamples
648
649 echoMixLoopback:
650     @ Load the old echo value (l,r)
651     ldrsh r0, [r7]
652     ldrsh r1, [r7, #2]
653
654 /*    @ Increment and wrap firOffset
655     add r14, r14, #2
656     and r14, r14, #(8 * 2) - 1
657
658     @ Get &firTable[firOffset + 8] into r13
659     ldr r13, =firTable + ((8 * 2) * 4)
660     add r13, r13, r14, lsl #2
661
662     @ Store the computed samples in the FIR ring buffer
663     str r0, [r13]
664     str r1, [r13, #4]
665     str r0, [r13, #-8 * 2 * 4]
666     str r1, [r13, #(-8 * 2 * 4) + 4]
667
668     @ Process FIR sample 0 (special)
669     ldr r2, [r13], #4
670     ldr r3, [r13], #-12
671     ldrsb r4, [r12], #-0x10
672     mul r0, r2, r4
673     mul r1, r3, r4
674
675 .MACRO processFir
676     ldr r2, [r13], #4
677     ldr r3, [r13], #-12
678     ldrsb r4, [r12], #-0x10
679     mla r0, r2, r4, r0
680     mla r1, r3, r4, r1
681 .ENDM
682     processFir
683     processFir
684     processFir
685     processFir
686     processFir
687     processFir
688
689     @ Last FIR sample (special)
690     ldr r2, [r13], #4
691     ldr r3, [r13], #-12
692     ldrsb r4, [r12], #0x70
693
694     mla r0, r2, r4, r0
695     mla r1, r3, r4, r1
696     
697     @ Get rid of volume multiplication stuff
698     mov r0, r0, asr #7
699     mov r1, r1, asr #7*/
700
701     @ r0,r1 contains the filtered samples
702     ldr r2, [r8]
703     @ Left channel = (feedback * filtered) >> 7
704     mla r2, r11, r0, r2
705     mov r2, r2, asr #15
706
707     ldr r3, [r8, #4]
708     @ Right channel = (feedback * filtered) >> 7
709     mla r3, r11, r1, r3
710     mov r3, r3, asr #15
711
712     @ Store (filtered * echoFB) + echobuffer into echobuffer
713     ldrb r5, echoEnabled
714     tst r5, #0x20
715     streqh r2, [r7], #2
716     streqh r3, [r7], #2
717     cmp r7, r9
718     ldrge r7, echoBufferStart
719
720     @ Store (filtered * echoVol) into echomix
721     mul r2, r10, r0
722     str r2, [r8], #4
723     mul r2, r5, r1
724     str r2, [r8], #4
725
726     subs r6, r6, #1
727     bne echoMixLoopback
728     
729 doneEchoMix:
730
731 /*    ldr r13, tmpSp
732     
733     @ Store changed values
734     ldr r0, =firOffset
735     strb r14, [r0]*/
736
737     ldr r3, echoBufferStart
738     sub r7, r7, r3
739     ldr r0, =echoCursor
740     strh r7, [r0]
741
742     @ Reload mix buffer & echo positions
743     ldmfd sp!, {r1, r2}
744     
745 #endif
746     
747 clipAndMix:
748     @ Put the original output buffer into r3
749     ldmfd sp!, {r3}
750     
751     @ Set up the preamp & overall volume
752     ldr r8, =dspPreamp
753     ldrh r8, [r8]
754
755     ldr r9, =DSP_MEM
756     ldrsb r4, [r9, #0x0C] @ Main left volume
757     ldrsb r6, [r9, #0x1C] @ Main right volume
758     
759     mul r4, r8, r4
760     mov r4, r4, asr #7
761     mul r6, r8, r6
762     mov r6, r6, asr #7
763
764     @ r0 - numSamples
765     @ r1 - mix buffer
766     @ r2 - echo buffer
767     @ r3 - output buffer
768     @ r4 - left volume
769     @ r5 - TMP (assigned to sample value)
770     @ r6 - right volume
771     @ r7 - TMP
772     @ r8 - preamp
773     @ r9 - 
774     @ r10 - 
775     @ r11 - 
776     @ r12 - 
777     @ r14 - 
778
779     @ Do volume multiplication, mix in echo buffer and clipping here
780     ldr r0, numSamples
781
782 mixClipLoop:
783     @ Load and scale by volume (LEFT)
784     ldr r5, [r1], #4
785     mov r5, r5, asr #15
786     mul r5, r4, r5
787     ldr r7, [r2], #4
788     add r5, r5, r7, asr #7
789     mov r5, r5, asr #7
790
791     @ Clip and store
792     cmp r5, #0x7f00
793     movgt r5, #0x7f00
794     cmn r5, #0x7f00
795     movlt r5, #0x8100
796     strh r5, [r3]
797     add r3, r3, #4000 * 4
798
799     @ Load and scale by volume (RIGHT)
800     ldr r5, [r1], #4
801     mov r5, r5, asr #15
802     mul r5, r6, r5
803     ldr r7, [r2], #4
804     add r5, r5, r7, asr #7
805     mov r5, r5, asr #7
806
807     @ Clip and store
808     cmp r5, #0x7f00
809     movgt r5, #0x7f00
810     cmn r5, #0x7f00
811     movlt r5, #0x8100
812     strh r5, [r3], #2
813     sub r3, r3, #4000 * 4
814
815     subs r0, r0, #1
816     bne mixClipLoop
817
818 doneMix:
819     ldmfd sp!, {r4-r12, lr}
820     bx lr
821 .ENDFUNC
822
823 .GLOBAL channelNum
824
825 tmpSp:
826 .word 0
827 echoBufferStart:
828 .word 0
829 numSamples:
830 .word 0
831 channelNum:
832 .byte 0
833 echoEnabled:
834 .byte 0
835
836 .align
837 .pool