2

我正在尝试编写一个 asm 函数,它将 16 个样本生成到一个缓冲区中,该缓冲区从编解码器以 48k 输出。每个样本将 16 个正弦波加在一起,每个正弦波或部分正弦波都有一个 ADSR 包络,带有额外的延迟阶段(等待)来控制幅度。换句话说,它的加法合成。无论如何,我很生气,因为 CooCox 中的编译器实际上可以比我在 ASM 中的所有汗水更快地做到这一点。我能做些什么来优化它并让它更快?涉及到很多控制参数,这会导致全局数组变量的大量负载,从而减慢速度。这是针对STM32F4 BTW的。

    @ ARM function definition
    @ void get_sine(void)
        .align 2                    @ Align to word boundary
        .global get_sine       @ This makes it a real symbol
        .syntax unified @ Remember this!
        .type get_sine STT_FUNC    @ Declare to be a function.
        .equ bufsize, 1024
        .equ partials, 16
        .equ MAX_EG, 524288
        .data
    count16: .word 0x0


get_sine:                  @ Start of function definition
        push    {r4-r12}
        ldr     r2,=sineLUT     @   sine_tab base addy
        ldr     r9,=atk
        ldr     r10,=dcy
        ldr     r11,=sus
        ldr     r12,=rel
        ldr     r6,=env_1
        ldr     r1,=count16
        mov     r0,#0
        str     r0,[r1,#0]
outloop:
        ldr     r7,=ph_inc      @   pitch val into r7
        ldr     r7,[r7,#0]      @   get current phase
        ldr     r3,=phase       @   phase address to r3
        ldr     r1,[r3,#0]      @   get current phase
        add     r1,r1,r7        @   add current phase and ph_inc
        str     r1,[r3,#0]      @   store phase
        mov     r7,#0           @   set to 1 for r7 to be inner loop counter
        mov     r5,#0           @   clear sum reg
        ldr     r8,=flag
        ldr     r3,=EG_stage

innerloop:
        ldr     r0,[r3,r7,lsl #2]   @get EG_stage r0
        cmp     r0,#0               @ if zero goto wait
        beq     waitj
        cmp     r0,#1               @ if 1 jump attack
        beq     attackj
        cmp     r0,#2               @ if 2 jump decay
        beq     decayj
        b       releasej                @ if 3 jump release

waitj:
        ldr     r1,=wait_temp       @get wait_temp array addr
        ldr     r0,[r1,r7,lsl #2]   @load value to r0
        sub     r0,r0,#1            @subtract
        mov     r4,#1               @ load one for next
        cmp     r0,#0               @compare if gt or equal to zero
        ite     ge
        strge   r0,[r1,r7,lsl #2]   @ store wait state if >= 0
        strlt   r4,[r3,r7,lsl #2]   @ store EG_stage value if less than
        b break

attackj:
        ldr     r0,[r6,r7,lsl #2]   @ get env1 value into r0
        ldr     r1,[r9,r7,lsl #2]   @ get attack value
        add     r0,r0,r1            @   env_1[par] += atk[par];
        mov     r4,#2
        mov     r1,MAX_EG
        cmp     r0,r1
        itte    GE                  @   if (env_1[par] >= MAX)
        strge   r4,[r3,r7,lsl #2]   @   EG_stage[par] = 2, env_1[par] = MAX;
        strge   r1,[r6,r7,lsl #2]
        strlt   r0,[r6,r7,lsl #2]
        b break

decayj:
        ldr     r0,[r6,r7,lsl #2]   @ get env1 value into r0
        ldr     r1,[r10,r7,lsl #2]  @ decay value
        sub     r0,r0,r1            @   env_1[par] -= dcy[par];
        str     r0,[r6,r7,lsl #2]   @ update env_1 now in case
        ldr     r4,[r11,r7,lsl #2]  @   get sus value
        add     r1,r4,r1            @ add decay and sus value for compare
        cmp     r0,r1               @ if < sus[par]+dcy[par] || env_1[par]<0)
        ittt    lt
        movlt   r0,r4               @env_1[par] = (sus[par]);
        strlt   r4,[r6,r7,lsl #2]   @ store to env_1
        blt     break
        cmp     r0,#0
        itt     lt                  @|| env_1[par]<0)
        movlt   r0,r4               @env_1[par] = (sus[par]);
        strlt   r4,[r6,r7,lsl #2]   @ store to env_1
        b break

releasej:
        ldr     r0,[r6,r7,lsl #2]   @ get env1 value into r0
        ldr     r1,[r12,r7,lsl #2]  @ release value
        sub     r0,r0,r1            @ env_1[par] -= rel[par];
        str     r0,[r6,r7,lsl #2]   @ update env_1 now in case
        mov     r1,#0
        cmp     r0,#0               @
        it      lt                  @ if (env_1[par]<0)
        strlt   r1,[r6,r7,lsl #2]   @ env_1[par] = 0;

break:
        mov     r4,#0
        add     r4,r7,#1
        ldr     r1,=phase           @ phase address to r3
        ldr     r1,[r1,#0]
        umull   r0,r4,r1,r4         @ multiply phase for each partial
        lsr     r0,r0,#18           @ shift it right by 18 into r0 for sine_tab lookup
        ldr     r0,[r2,r0,lsl #2]   @ lookup sine val with r0 into r1 and sign extend
        ldr     r4,[r6,r7,lsl #2]   @ get envelope value into r4
        lsr     r4,r4,#4            @ shift it to 16bit range   
        smulbb  r0,r0,r4            @ signed multiply of sine table * envelope for scaling
        asr     r0,r0,#15           @ asr shift back to 16bit
        ldr     r4,[r8,r7,lsl #2]   @ get flag if withing bandwidth
        cmp     r4,#0               
        it      ne                  @ if 1 add it to sum
        addne   r5,r5,r0            
        add     r7,r7,#1
        cmp     r7,#16              @ compare loop index with 16 (i=0;i<16;i++)
        bne     innerloop

        asr     r0,r5,#5
        pkhbt   r0,r0,r0,lsl #16    @   pack R+L channel in r0
        ldr     r5,=writePos    @   get writepos addr
        ldr     r1,[r5,#0]      @   get writePos
        lsl     r3,r1,#2        @   align address 4
        ldr     r4,=WaveBuffer  @   storage array addy
        str     r0,[r4,r3]      @   store sine to WaveBuffer
        add     r1,r1,#1        @   increment array pointer writepos
        mov     r3,bufsize      @   load BUFFERSIZE compare
        cmp     r1,r3           @   skip if less than BUFFERSIZE
        it      hs
        movhs   r1,#0           @   clr writepos if >=BUFFERSIZE
        str     r1,[r5,#0]      @   store writepos value

        ldr     r0,=dataSize    @   get datasize counter addr
        ldr     r1,[r0,#0]      @   get val
        add     r1,r1,#1        @   increment datasize counter
        str     r1,[r0,#0]      @   store counter
        ldr     r1,=count16
        ldr     r0,[r1,#0]
        add     r0,r0,#1        @   increment loop counter
        str     r0,[r1,#0]
        cmp     r0,#16      @   compare with 16 (i=0;i<16;i++)
        bne     outloop
        pop     {r4-r12}
        bx      lr



    .section .rodata
        sineLUT:
        @ Array goes in here. Type can be .byte, .hword or .word
        @ NOTE! No comma at the end of a line! This is important

    .word   0x0000,0x000c,0x0018,0x0024,0x0030,0x003c,0x0048,0x0054
    .word   0x0064,0x0070,0x007c,0x0088,0x0094,0x00a0,0x00ac,0x00bc
    .word   0x00c8,0x00d4,0x00e0,0x00ec,0x00f8,0x0104,0x0114,0x0120
    .word   0x012c,0x0138,0x0144,0x0150,0x015c,0x016c,0x0178,0x0184
    .word   0x0190,0x019c,0x01a8,0x01b4,0x01c4,0x01d0,0x01dc,0x01e8
    .word   0x01f4,0x0200,0x020c,0x021c,0x0228,0x0234,0x0240,0x024c
4

1 回答 1

3

了解编译器如何做到这一点的最佳方法是在该编译器中编译代码,然后查看它输出的程序集。它可能会令人困惑,因为它已经过优化,但你可能会学到一些技巧。

一个非常不可读但可能有帮助的技巧是实现某种“跳转表”功能。在内部循环中,不是执行 3 个比较语句,而是将 (r0*X) 添加到当前指令指针。在适当的目的地,有一个无条件跳转指令到你需要去的任何地方(攻击、衰减j、释放j)。X 的值将取决于需要多少字节来保存跳转指令。这只是我的一个想法,你需要自己测试它的有效性。

于 2012-06-14T02:03:53.977 回答