Merge commit '581c7f0e12b1fa39f73d683e54d6ecda0772c5a9'
[ffmpeg.git] / libavcodec / arm / mlpdsp_armv5te.S
1 /*
2  * Copyright (c) 2014 RISC OS Open Ltd
3  * Author: Ben Avison <bavison@riscosopen.org>
4  *
5  * This file is part of FFmpeg.
6  *
7  * FFmpeg is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * FFmpeg is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with FFmpeg; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21
22 #include "libavutil/arm/asm.S"
23
24 #define MAX_CHANNELS        8
25 #define MAX_FIR_ORDER       8
26 #define MAX_IIR_ORDER       4
27 #define MAX_RATEFACTOR      4
28 #define MAX_BLOCKSIZE       (40 * MAX_RATEFACTOR)
29
30 PST     .req    a1
31 PCO     .req    a2
32 AC0     .req    a3
33 AC1     .req    a4
34 CO0     .req    v1
35 CO1     .req    v2
36 CO2     .req    v3
37 CO3     .req    v4
38 ST0     .req    v5
39 ST1     .req    v6
40 ST2     .req    sl
41 ST3     .req    fp
42 I       .req    ip
43 PSAMP   .req    lr
44
45
46 // Some macros that do loads/multiplies where the register number is determined
47 // from an assembly-time expression. Boy is GNU assembler's syntax ugly...
48
49 .macro load  group, index, base, offset
50        .altmacro
51        load_ \group, %(\index), \base, \offset
52        .noaltmacro
53 .endm
54
55 .macro load_ group, index, base, offset
56         ldr     \group\index, [\base, #\offset]
57 .endm
58
59 .macro loadd  group, index, base, offset
60        .altmacro
61        loadd_ \group, %(\index), %(\index+1), \base, \offset
62        .noaltmacro
63 .endm
64
65 .macro loadd_ group, index0, index1, base, offset
66 A .if \offset >= 256
67 A       ldr     \group\index0, [\base, #\offset]
68 A       ldr     \group\index1, [\base, #(\offset) + 4]
69 A .else
70         ldrd    \group\index0, \group\index1, [\base, #\offset]
71 A .endif
72 .endm
73
74 .macro multiply  index, accumulate, long
75         .altmacro
76         multiply_ %(\index), \accumulate, \long
77         .noaltmacro
78 .endm
79
80 .macro multiply_  index, accumulate, long
81  .if \long
82   .if \accumulate
83         smlal   AC0, AC1, CO\index, ST\index
84   .else
85         smull   AC0, AC1, CO\index, ST\index
86   .endif
87  .else
88   .if \accumulate
89         mla     AC0, CO\index, ST\index, AC0
90   .else
91         mul     AC0, CO\index, ST\index
92   .endif
93  .endif
94 .endm
95
96 // A macro to update the load register number and load offsets
97
98 .macro inc  howmany
99   .set LOAD_REG, (LOAD_REG + \howmany) & 3
100   .set OFFSET_CO, OFFSET_CO + 4 * \howmany
101   .set OFFSET_ST, OFFSET_ST + 4 * \howmany
102   .if FIR_REMAIN > 0
103     .set FIR_REMAIN, FIR_REMAIN - \howmany
104     .if FIR_REMAIN == 0
105       .set OFFSET_CO, 4 * MAX_FIR_ORDER
106       .set OFFSET_ST, 4 * (MAX_BLOCKSIZE + MAX_FIR_ORDER)
107     .endif
108   .elseif IIR_REMAIN > 0
109     .set IIR_REMAIN, IIR_REMAIN - \howmany
110   .endif
111 .endm
112
113 // Macro to implement the inner loop for one specific combination of parameters
114
115 .macro implement_filter  mask_minus1, shift_0, shift_8, iir_taps, fir_taps
116   .set TOTAL_TAPS, \iir_taps + \fir_taps
117
118   // Deal with register allocation...
119   .set DEFINED_SHIFT, 0
120   .set DEFINED_MASK, 0
121   .set SHUFFLE_SHIFT, 0
122   .set SHUFFLE_MASK, 0
123   .set SPILL_SHIFT, 0
124   .set SPILL_MASK, 0
125   .if TOTAL_TAPS == 0
126     // Little register pressure in this case - just keep MASK where it was
127     .if !\mask_minus1
128       MASK .req ST1
129       .set DEFINED_MASK, 1
130     .endif
131   .else
132     .if \shift_0
133       .if !\mask_minus1
134         // AC1 is unused with shift 0
135         MASK .req AC1
136         .set DEFINED_MASK, 1
137         .set SHUFFLE_MASK, 1
138       .endif
139     .elseif \shift_8
140       .if !\mask_minus1
141         .if TOTAL_TAPS <= 4
142         // All coefficients are preloaded (so pointer not needed)
143           MASK .req PCO
144           .set DEFINED_MASK, 1
145           .set SHUFFLE_MASK, 1
146         .else
147           .set SPILL_MASK, 1
148         .endif
149       .endif
150     .else // shift not 0 or 8
151       .if TOTAL_TAPS <= 3
152         // All coefficients are preloaded, and at least one CO register is unused
153         .if \fir_taps & 1
154           SHIFT .req CO0
155           .set DEFINED_SHIFT, 1
156           .set SHUFFLE_SHIFT, 1
157         .else
158           SHIFT .req CO3
159           .set DEFINED_SHIFT, 1
160           .set SHUFFLE_SHIFT, 1
161         .endif
162         .if !\mask_minus1
163           MASK .req PCO
164           .set DEFINED_MASK, 1
165           .set SHUFFLE_MASK, 1
166         .endif
167       .elseif TOTAL_TAPS == 4
168         // All coefficients are preloaded
169         SHIFT .req PCO
170         .set DEFINED_SHIFT, 1
171         .set SHUFFLE_SHIFT, 1
172         .if !\mask_minus1
173           .set SPILL_MASK, 1
174         .endif
175       .else
176         .set SPILL_SHIFT, 1
177         .if !\mask_minus1
178           .set SPILL_MASK, 1
179         .endif
180       .endif
181     .endif
182   .endif
183   .if SPILL_SHIFT
184     SHIFT .req ST0
185     .set DEFINED_SHIFT, 1
186   .endif
187   .if SPILL_MASK
188     MASK .req ST1
189     .set DEFINED_MASK, 1
190   .endif
191
192         // Preload coefficients if possible
193   .if TOTAL_TAPS <= 4
194     .set OFFSET_CO, 0
195     .if \fir_taps & 1
196       .set LOAD_REG, 1
197     .else
198       .set LOAD_REG, 0
199     .endif
200     .rept \fir_taps
201         load    CO, LOAD_REG, PCO, OFFSET_CO
202       .set LOAD_REG, (LOAD_REG + 1) & 3
203       .set OFFSET_CO, OFFSET_CO + 4
204     .endr
205     .set OFFSET_CO, 4 * MAX_FIR_ORDER
206     .rept \iir_taps
207         load    CO, LOAD_REG, PCO, OFFSET_CO
208       .set LOAD_REG, (LOAD_REG + 1) & 3
209       .set OFFSET_CO, OFFSET_CO + 4
210     .endr
211   .endif
212
213         // Move mask/shift to final positions if necessary
214         // Need to do this after preloading, because in some cases we
215         // reuse the coefficient pointer register
216   .if SHUFFLE_SHIFT
217         mov     SHIFT, ST0
218   .endif
219   .if SHUFFLE_MASK
220         mov     MASK, ST1
221   .endif
222
223         // Begin loop
224 01:
225   .if TOTAL_TAPS == 0
226         // Things simplify a lot in this case
227         // In fact this could be pipelined further if it's worth it...
228         ldr     ST0, [PSAMP]
229         subs    I, I, #1
230     .if !\mask_minus1
231         and     ST0, ST0, MASK
232     .endif
233         str     ST0, [PST, #-4]!
234         str     ST0, [PST, #4 * (MAX_BLOCKSIZE + MAX_FIR_ORDER)]
235         str     ST0, [PSAMP], #4 * MAX_CHANNELS
236         bne     01b
237   .else
238     .if \fir_taps & 1
239       .set LOAD_REG, 1
240     .else
241       .set LOAD_REG, 0
242     .endif
243     .set LOAD_BANK, 0
244     .set FIR_REMAIN, \fir_taps
245     .set IIR_REMAIN, \iir_taps
246     .if FIR_REMAIN == 0 // only IIR terms
247       .set OFFSET_CO, 4 * MAX_FIR_ORDER
248       .set OFFSET_ST, 4 * (MAX_BLOCKSIZE + MAX_FIR_ORDER)
249     .else
250       .set OFFSET_CO, 0
251       .set OFFSET_ST, 0
252     .endif
253     .set MUL_REG, LOAD_REG
254     .set COUNTER, 0
255     .rept TOTAL_TAPS + 2
256         // Do load(s)
257      .if FIR_REMAIN != 0 || IIR_REMAIN != 0
258       .if COUNTER == 0
259        .if TOTAL_TAPS > 4
260         load    CO, LOAD_REG, PCO, OFFSET_CO
261        .endif
262         load    ST, LOAD_REG, PST, OFFSET_ST
263         inc     1
264       .elseif COUNTER == 1 && (\fir_taps & 1) == 0
265        .if TOTAL_TAPS > 4
266         load    CO, LOAD_REG, PCO, OFFSET_CO
267        .endif
268         load    ST, LOAD_REG, PST, OFFSET_ST
269         inc     1
270       .elseif LOAD_BANK == 0
271        .if TOTAL_TAPS > 4
272         .if FIR_REMAIN == 0 && IIR_REMAIN == 1
273         load    CO, LOAD_REG, PCO, OFFSET_CO
274         .else
275         loadd   CO, LOAD_REG, PCO, OFFSET_CO
276         .endif
277        .endif
278        .set LOAD_BANK, 1
279       .else
280        .if FIR_REMAIN == 0 && IIR_REMAIN == 1
281         load    ST, LOAD_REG, PST, OFFSET_ST
282         inc     1
283        .else
284         loadd   ST, LOAD_REG, PST, OFFSET_ST
285         inc     2
286        .endif
287        .set LOAD_BANK, 0
288       .endif
289      .endif
290
291         // Do interleaved multiplies, slightly delayed
292      .if COUNTER >= 2
293         multiply MUL_REG, COUNTER > 2, !\shift_0
294       .set MUL_REG, (MUL_REG + 1) & 3
295      .endif
296      .set COUNTER, COUNTER + 1
297     .endr
298
299         // Post-process the result of the multiplies
300     .if SPILL_SHIFT
301         ldr     SHIFT, [sp, #9*4 + 0*4]
302     .endif
303     .if SPILL_MASK
304         ldr     MASK, [sp, #9*4 + 1*4]
305     .endif
306         ldr     ST2, [PSAMP]
307         subs    I, I, #1
308     .if \shift_8
309         mov     AC0, AC0, lsr #8
310         orr     AC0, AC0, AC1, lsl #24
311     .elseif !\shift_0
312         rsb     ST3, SHIFT, #32
313         mov     AC0, AC0, lsr SHIFT
314 A       orr     AC0, AC0, AC1, lsl ST3
315 T       mov     AC1, AC1, lsl ST3
316 T       orr     AC0, AC0, AC1
317     .endif
318     .if \mask_minus1
319         add     ST3, ST2, AC0
320     .else
321         add     ST2, ST2, AC0
322         and     ST3, ST2, MASK
323         sub     ST2, ST3, AC0
324     .endif
325         str     ST3, [PST, #-4]!
326         str     ST2, [PST, #4 * (MAX_BLOCKSIZE + MAX_FIR_ORDER)]
327         str     ST3, [PSAMP], #4 * MAX_CHANNELS
328         bne     01b
329   .endif
330         b       99f
331
332   .if DEFINED_SHIFT
333     .unreq SHIFT
334   .endif
335   .if DEFINED_MASK
336     .unreq MASK
337   .endif
338 .endm
339
340 .macro switch_on_fir_taps  mask_minus1, shift_0, shift_8, iir_taps
341 A       ldr     CO0, [pc, a3, lsl #2]   // firorder is in range 0-(8-iir_taps)
342 A       add     pc,  pc,  CO0
343 T       tbh     [pc, a3, lsl #1]
344 0:
345 A       .word   (70f - 0b) - 4, (71f - 0b) - 4, (72f - 0b) - 4, (73f - 0b) - 4, (74f - 0b) - 4
346 T       .hword  (70f - 0b) / 2, (71f - 0b) / 2, (72f - 0b) / 2, (73f - 0b) / 2, (74f - 0b) / 2
347  .if \iir_taps <= 3
348 A       .word   (75f - 0b) - 4
349 T       .hword  (75f - 0b) / 2
350   .if \iir_taps <= 2
351 A       .word   (76f - 0b) - 4
352 T       .hword  (76f - 0b) / 2
353    .if \iir_taps <= 1
354 A       .word   (77f - 0b) - 4
355 T       .hword  (77f - 0b) / 2
356     .if \iir_taps == 0
357 A       .word   (78f - 0b) - 4
358 T       .hword  (78f - 0b) / 2
359     .endif
360    .endif
361   .endif
362  .endif
363 70:     implement_filter  \mask_minus1, \shift_0, \shift_8, \iir_taps, 0
364 71:     implement_filter  \mask_minus1, \shift_0, \shift_8, \iir_taps, 1
365 72:     implement_filter  \mask_minus1, \shift_0, \shift_8, \iir_taps, 2
366 73:     implement_filter  \mask_minus1, \shift_0, \shift_8, \iir_taps, 3
367 74:     implement_filter  \mask_minus1, \shift_0, \shift_8, \iir_taps, 4
368  .if \iir_taps <= 3
369 75:     implement_filter  \mask_minus1, \shift_0, \shift_8, \iir_taps, 5
370   .if \iir_taps <= 2
371 76:     implement_filter  \mask_minus1, \shift_0, \shift_8, \iir_taps, 6
372    .if \iir_taps <= 1
373 77:     implement_filter  \mask_minus1, \shift_0, \shift_8, \iir_taps, 7
374     .if \iir_taps == 0
375 78:     implement_filter  \mask_minus1, \shift_0, \shift_8, \iir_taps, 8
376     .endif
377    .endif
378   .endif
379  .endif
380 .endm
381
382 .macro switch_on_iir_taps  mask_minus1, shift_0, shift_8
383 A       ldr     CO0, [pc, a4, lsl #2]   // irorder is in range 0-4
384 A       add     pc,  pc,  CO0
385 T       tbh     [pc, a4, lsl #1]
386 0:
387 A       .word   (60f - 0b) - 4, (61f - 0b) - 4, (62f - 0b) - 4, (63f - 0b) - 4, (64f - 0b) - 4
388 T       .hword  (60f - 0b) / 2, (61f - 0b) / 2, (62f - 0b) / 2, (63f - 0b) / 2, (64f - 0b) / 2
389 60:     switch_on_fir_taps  \mask_minus1, \shift_0, \shift_8, 0
390 61:     switch_on_fir_taps  \mask_minus1, \shift_0, \shift_8, 1
391 62:     switch_on_fir_taps  \mask_minus1, \shift_0, \shift_8, 2
392 63:     switch_on_fir_taps  \mask_minus1, \shift_0, \shift_8, 3
393 64:     switch_on_fir_taps  \mask_minus1, \shift_0, \shift_8, 4
394 .endm
395
396 /* void ff_mlp_filter_channel_arm(int32_t *state, const int32_t *coeff,
397  *                                int firorder, int iirorder,
398  *                                unsigned int filter_shift, int32_t mask,
399  *                                int blocksize, int32_t *sample_buffer);
400  */
401 function ff_mlp_filter_channel_arm, export=1
402         push    {v1-fp,lr}
403         add     v1, sp, #9*4 // point at arguments on stack
404         ldm     v1, {ST0,ST1,I,PSAMP}
405         cmp     ST1, #-1
406         bne     30f
407         movs    ST2, ST0, lsl #29 // shift is in range 0-15; we want to special-case 0 and 8
408         bne     20f
409         bcs     10f
410         switch_on_iir_taps 1, 1, 0
411 10:     switch_on_iir_taps 1, 0, 1
412 20:     switch_on_iir_taps 1, 0, 0
413 30:     movs    ST2, ST0, lsl #29 // shift is in range 0-15; we want to special-case 0 and 8
414         bne     50f
415         bcs     40f
416         switch_on_iir_taps 0, 1, 0
417 40:     switch_on_iir_taps 0, 0, 1
418 50:     switch_on_iir_taps 0, 0, 0
419 99:     pop     {v1-fp,pc}
420 endfunc
421
422         .unreq  PST
423         .unreq  PCO
424         .unreq  AC0
425         .unreq  AC1
426         .unreq  CO0
427         .unreq  CO1
428         .unreq  CO2
429         .unreq  CO3
430         .unreq  ST0
431         .unreq  ST1
432         .unreq  ST2
433         .unreq  ST3
434         .unreq  I
435         .unreq  PSAMP
436
437 /********************************************************************/
438
439 PSA     .req    a1 // samples
440 PCO     .req    a2 // coeffs
441 PBL     .req    a3 // bypassed_lsbs
442 INDEX   .req    a4
443 CO0     .req    v1
444 CO1     .req    v2
445 CO2     .req    v3
446 CO3     .req    v4
447 SA0     .req    v5
448 SA1     .req    v6
449 SA2     .req    sl
450 SA3     .req    fp
451 AC0     .req    ip
452 AC1     .req    lr
453 NOISE   .req    SA0
454 LSB     .req    SA1
455 DCH     .req    SA2 // dest_ch
456 MASK    .req    SA3
457
458     // INDEX is used as follows:
459     // bits 0..6   index2 (values up to 17, but wider so that we can
460     //               add to index field without needing to mask)
461     // bits 7..14  i (values up to 160)
462     // bit 15      underflow detect for i
463     // bits 25..31 (if access_unit_size_pow2 == 128)  \ index
464     // bits 26..31 (if access_unit_size_pow2 == 64)   /
465
466 .macro implement_rematrix  shift, index_mask, mask_minus1, maxchan
467     .if \maxchan == 1
468         // We can just leave the coefficients in registers in this case
469         ldrd    CO0, CO1, [PCO]
470     .endif
471 1:
472     .if \maxchan == 1
473         ldrd    SA0, SA1, [PSA]
474         smull   AC0, AC1, CO0, SA0
475     .elseif \maxchan == 5
476         ldr     CO0, [PCO, #0]
477         ldr     SA0, [PSA, #0]
478         ldr     CO1, [PCO, #4]
479         ldr     SA1, [PSA, #4]
480         ldrd    CO2, CO3, [PCO, #8]
481         smull   AC0, AC1, CO0, SA0
482         ldrd    SA2, SA3, [PSA, #8]
483         smlal   AC0, AC1, CO1, SA1
484         ldrd    CO0, CO1, [PCO, #16]
485         smlal   AC0, AC1, CO2, SA2
486         ldrd    SA0, SA1, [PSA, #16]
487         smlal   AC0, AC1, CO3, SA3
488         smlal   AC0, AC1, CO0, SA0
489     .else // \maxchan == 7
490         ldr     CO2, [PCO, #0]
491         ldr     SA2, [PSA, #0]
492         ldr     CO3, [PCO, #4]
493         ldr     SA3, [PSA, #4]
494         ldrd    CO0, CO1, [PCO, #8]
495         smull   AC0, AC1, CO2, SA2
496         ldrd    SA0, SA1, [PSA, #8]
497         smlal   AC0, AC1, CO3, SA3
498         ldrd    CO2, CO3, [PCO, #16]
499         smlal   AC0, AC1, CO0, SA0
500         ldrd    SA2, SA3, [PSA, #16]
501         smlal   AC0, AC1, CO1, SA1
502         ldrd    CO0, CO1, [PCO, #24]
503         smlal   AC0, AC1, CO2, SA2
504         ldrd    SA0, SA1, [PSA, #24]
505         smlal   AC0, AC1, CO3, SA3
506         smlal   AC0, AC1, CO0, SA0
507     .endif
508         ldm     sp, {NOISE, DCH, MASK}
509         smlal   AC0, AC1, CO1, SA1
510     .if \shift != 0
511       .if \index_mask == 63
512         add     NOISE, NOISE, INDEX, lsr #32-6
513         ldrb    LSB, [PBL], #MAX_CHANNELS
514         ldrsb   NOISE, [NOISE]
515         add     INDEX, INDEX, INDEX, lsl #32-6
516       .else // \index_mask == 127
517         add     NOISE, NOISE, INDEX, lsr #32-7
518         ldrb    LSB, [PBL], #MAX_CHANNELS
519         ldrsb   NOISE, [NOISE]
520         add     INDEX, INDEX, INDEX, lsl #32-7
521       .endif
522         sub     INDEX, INDEX, #1<<7
523         adds    AC0, AC0, NOISE, lsl #\shift + 7
524         adc     AC1, AC1, NOISE, asr #31
525     .else
526         ldrb    LSB, [PBL], #MAX_CHANNELS
527         sub     INDEX, INDEX, #1<<7
528     .endif
529         add     PSA, PSA, #MAX_CHANNELS*4
530         mov     AC0, AC0, lsr #14
531         orr     AC0, AC0, AC1, lsl #18
532     .if !\mask_minus1
533         and     AC0, AC0, MASK
534     .endif
535         add     AC0, AC0, LSB
536         tst     INDEX, #1<<15
537         str     AC0, [PSA, DCH, lsl #2]  // DCH is precompensated for the early increment of PSA
538         beq     1b
539         b       98f
540 .endm
541
542 .macro switch_on_maxchan  shift, index_mask, mask_minus1
543         cmp     v4, #5
544         blo     51f
545         beq     50f
546         implement_rematrix  \shift, \index_mask, \mask_minus1, 7
547 50:     implement_rematrix  \shift, \index_mask, \mask_minus1, 5
548 51:     implement_rematrix  \shift, \index_mask, \mask_minus1, 1
549 .endm
550
551 .macro switch_on_mask  shift, index_mask
552         cmp     sl, #-1
553         bne     40f
554         switch_on_maxchan  \shift, \index_mask, 1
555 40:     switch_on_maxchan  \shift, \index_mask, 0
556 .endm
557
558 .macro switch_on_au_size  shift
559   .if \shift == 0
560         switch_on_mask  \shift, undefined
561   .else
562         teq     v6, #64
563         bne     30f
564         orr     INDEX, INDEX, v1, lsl #32-6
565         switch_on_mask  \shift, 63
566 30:     orr     INDEX, INDEX, v1, lsl #32-7
567         switch_on_mask  \shift, 127
568   .endif
569 .endm
570
571 /* void ff_mlp_rematrix_channel_arm(int32_t *samples,
572  *                                  const int32_t *coeffs,
573  *                                  const uint8_t *bypassed_lsbs,
574  *                                  const int8_t *noise_buffer,
575  *                                  int index,
576  *                                  unsigned int dest_ch,
577  *                                  uint16_t blockpos,
578  *                                  unsigned int maxchan,
579  *                                  int matrix_noise_shift,
580  *                                  int access_unit_size_pow2,
581  *                                  int32_t mask);
582  */
583 function ff_mlp_rematrix_channel_arm, export=1
584         push    {v1-fp,lr}
585         add     v1, sp, #9*4 // point at arguments on stack
586         ldm     v1, {v1-sl}
587         teq     v4, #1
588         itt     ne
589         teqne   v4, #5
590         teqne   v4, #7
591         bne     99f
592         teq     v6, #64
593         it      ne
594         teqne   v6, #128
595         bne     99f
596         sub     v2, v2, #MAX_CHANNELS
597         push    {a4,v2,sl}          // initialise NOISE,DCH,MASK; make sp dword-aligned
598         movs    INDEX, v3, lsl #7
599         beq     98f                 // just in case, do nothing if blockpos = 0
600         subs    INDEX, INDEX, #1<<7 // offset by 1 so we borrow at the right time
601         adc     lr, v1, v1          // calculate index2 (C was set by preceding subs)
602         orr     INDEX, INDEX, lr
603         // Switch on matrix_noise_shift: values 0 and 1 are
604         // disproportionately common so do those in a form the branch
605         // predictor can accelerate. Values can only go up to 15.
606         cmp     v5, #1
607         beq     11f
608         blo     10f
609 A       ldr     v5,  [pc,  v5,  lsl #2]
610 A       add     pc,  pc,  v5
611 T       tbh     [pc, v5, lsl #1]
612 0:
613 A       .word   0, 0, (12f - 0b) - 4, (13f - 0b) - 4, (14f - 0b) - 4, (15f - 0b) - 4, (16f - 0b) - 4, (17f - 0b) - 4, (18f - 0b) - 4, (19f - 0b) - 4, (20f - 0b) - 4, (21f - 0b) - 4, (22f - 0b) - 4, (23f - 0b) - 4, (24f - 0b) - 4, (25f - 0b) - 4
614 T       .hword  0, 0, (12f - 0b) / 2, (13f - 0b) / 2, (14f - 0b) / 2, (15f - 0b) / 2
615 T       .hword  (16f - 0b) / 2, (17f - 0b) / 2, (18f - 0b) / 2, (19f - 0b) / 2
616 T       .hword  (20f - 0b) / 2, (21f - 0b) / 2, (22f - 0b) / 2, (23f - 0b) / 2, (24f - 0b) / 2, (25f - 0b) / 2
617 10:     switch_on_au_size  0
618 11:     switch_on_au_size  1
619 12:     switch_on_au_size  2
620 13:     switch_on_au_size  3
621 14:     switch_on_au_size  4
622 15:     switch_on_au_size  5
623 16:     switch_on_au_size  6
624 17:     switch_on_au_size  7
625 18:     switch_on_au_size  8
626 19:     switch_on_au_size  9
627 20:     switch_on_au_size  10
628 21:     switch_on_au_size  11
629 22:     switch_on_au_size  12
630 23:     switch_on_au_size  13
631 24:     switch_on_au_size  14
632 25:     switch_on_au_size  15
633
634 98:     add     sp, sp, #3*4
635         pop     {v1-fp,pc}
636 99:     // Can't handle these parameters, drop back to C
637         pop     {v1-fp,lr}
638         b       X(ff_mlp_rematrix_channel)
639 endfunc
640
641         .unreq  PSA
642         .unreq  PCO
643         .unreq  PBL
644         .unreq  INDEX
645         .unreq  CO0
646         .unreq  CO1
647         .unreq  CO2
648         .unreq  CO3
649         .unreq  SA0
650         .unreq  SA1
651         .unreq  SA2
652         .unreq  SA3
653         .unreq  AC0
654         .unreq  AC1
655         .unreq  NOISE
656         .unreq  LSB
657         .unreq  DCH
658         .unreq  MASK