assembly 将C代码转换为ARM Cortex M3汇编代码

vu8f3i0k  于 2023-08-06  发布在  其他
关注(0)|答案(2)|浏览(107)

我得到了下面的C函数

int main_compare (int nbytes, char *pmem1, char *pmem2){
    for(nbytes--; nbytes>=0; nbytes--) {    
        if(*(pmem1+nbytes) - *(pmem2+nbytes) != 0) {
            return 0;
        }
    }
    return 1;
}

字符串
我想把它转换成ARM Cortex M3汇编代码。我真的不擅长这个,我没有合适的编译器来测试我是否做对了。但我现在所拥有的

byte_cmp_loop PROC
; assuming: r0 = nbytes, r1=pmem1, r2 = pmem2

    SUB R0, R0, #1    ; nBytes - 1 as maximal value for loop counter

_for_loop: 
    ADD R3, R1, R0    ;
    ADD R4, R2, R0    ; calculate pmem + n
    LDRB R3, [R3]     ;
    LDRB R4, [R4]     ; look at this address

    CMP R3, R4        ; if cmp = 0, then jump over return

    BE _next          ; if statement by "branch"-cmd
        MOV R0, #0    ; return value is zero
        BX LR         ; always return 0 here
_next:

    sub R0, R0, #1    ; loop counting
    BLPL _for_loop    ; pl = if positive or zero

    MOV R0, #1        ;
    BX LR             ; always return 1 here

ENDP


但是我真的不确定,如果这是正确的,但是我不知道如何检查它。

5us2dqdw

5us2dqdw1#

我只看到三个相当简单的问题:

BE _next          ; if statement by "branch"-cmd
...
sub R0, R0, #1    ; loop counting
BLPL _for_loop    ; pl = if positive or zero

字符串

  • BEQ,而不是BE-条件代码始终为2个字母。
  • SUB不会单独更新标志-你需要后缀来说明这一点。SUBS的值。
  • BLPL将分支和链接,从而覆盖您的返回地址-您需要BPL。实际上,BLPL不会在这里组装,因为在Thumb中,条件BL需要一个IT来设置它(当然,除非你的组装器足够聪明,可以自动插入一个)。

编辑:当然,在原始代码和我下面的示例中使用R4还有一个更普遍的问题-如果您与C代码交互,则原始值 * 必须 * 在函数调用中保留并在之后恢复(R0-R3是指定的参数/临时寄存器,可以自由修改)。但是,如果你是在纯汇编语言中,你不一定需要遵循标准的调用约定,这样就可以更灵活。
现在,这是C代码的一个非常字面的表示,并且没有最好地利用指令集-特别是索引寻址模式。汇编编程的吸引力之一是完全控制指令,那么我们如何才能使它值得我们花时间呢?
首先,让C代码看起来更像我们想要的程序集:

int main_compare (int nbytes, char *pmem1, char *pmem2){
    while(nbytes-- > 0) {    
        if(*pmem1++ != *pmem2++) {
            return 0;
        }
    }
    return 1;
}


现在,这更清楚地显示了我们的意图,让我们玩编译器:

byte_cmp_loop PROC
; assuming: r0 = nbytes, r1=pmem1, r2 = pmem2

_loop:
    SUBS R0, R0, #1   ; Decrement nbytes and set flags based on the result
    BMI  _finished    ; If nbytes is now negative, it was 0, so we're done

    LDRB R3, [R1], #1 ; Load from the address in R1, then add 1 to R1
    LDRB R4, [R2], #1 ; ditto for R2
    CMP R3, R4        ; If they match...
    BEQ _loop         ; then continue round the loop

    MOV R0, #0        ; else give up and return zero
    BX LR

_finished:
    MOV R0, #1        ; Success!
    BX LR
ENDP


这几乎减少了25%的指令!现在,如果我们引入另一个指令集特性-条件执行-并稍微放宽要求,而不破坏C语义,它会变得更小:

byte_cmp_loop PROC
; assuming: r0 = nbytes, r1=pmem1, r2 = pmem2

_loop:
    SUBS R0, R0, #1 ; In C zero is false and any nonzero value is true, so
                    ; when R0 becomes -1 to trigger this branch, we can just
                    ; return that to indicate success
    IT MI           ; Make the following instruction conditional on 'minus'
    BXMI LR

    LDRB R3, [R1], #1
    LDRB R4, [R2], #1
    CMP R3, R4
    BEQ _loop

    MOVS R0, #0     ; Using MOVS rather than MOV to get a 16-bit encoding,
                    ; since updating the flags won't matter at this point
    BX LR
ENDP


汇编到一个微薄的22字节,这比我们开始时少了近40%的代码:D

o75abkj4

o75abkj42#

这里有一些编译器生成的代码

arm-none-eabi-gcc -O2 -mthumb -c test.c -o test.o
arm-none-eabi-objdump -D test.o

00000000 <main_compare>:
   0:   b510        push    {r4, lr}
   2:   3801        subs    r0, #1
   4:   d502        bpl.n   c <main_compare+0xc>
   6:   e007        b.n 18 <main_compare+0x18>
   8:   3801        subs    r0, #1
   a:   d305        bcc.n   18 <main_compare+0x18>
   c:   5c0c        ldrb    r4, [r1, r0]
   e:   5c13        ldrb    r3, [r2, r0]
  10:   429c        cmp r4, r3
  12:   d0f9        beq.n   8 <main_compare+0x8>
  14:   2000        movs    r0, #0
  16:   e000        b.n 1a <main_compare+0x1a>
  18:   2001        movs    r0, #1
  1a:   bc10        pop {r4}
  1c:   bc02        pop {r1}
  1e:   4708        bx  r1

arm-none-eabi-gcc -O2 -mthumb -mcpu=cortex-m3 -c test.c -o test.o
arm-none-eabi-objdump -D test.o

00000000 <main_compare>:
   0:   3801        subs    r0, #1
   2:   b410        push    {r4}
   4:   d503        bpl.n   e <main_compare+0xe>
   6:   e00a        b.n 1e <main_compare+0x1e>
   8:   f110 30ff   adds.w  r0, r0, #4294967295 ; 0xffffffff
   c:   d307        bcc.n   1e <main_compare+0x1e>
   e:   5c0c        ldrb    r4, [r1, r0]
  10:   5c13        ldrb    r3, [r2, r0]
  12:   429c        cmp r4, r3
  14:   d0f8        beq.n   8 <main_compare+0x8>
  16:   2000        movs    r0, #0
  18:   f85d 4b04   ldr.w   r4, [sp], #4
  1c:   4770        bx  lr
  1e:   2001        movs    r0, #1
  20:   f85d 4b04   ldr.w   r4, [sp], #4
  24:   4770        bx  lr
  26:   bf00        nop

字符串
有趣的是,thumb 2扩展似乎并没有让这一切变得更好,甚至可能更糟。
如果你没有一个编译器,那是否意味着你也没有一个汇编器和链接器?如果没有汇编器和链接器,编译和汇编机器代码将是大量的工作。那么你要如何将它加载到处理器中呢?
如果你没有一个ARM交叉编译器,你有编译器吗?你需要告诉我们更多关于你有什么和没有什么。如果你有一个web浏览器,你用来查找stackoverflow和发布问题,你可能可以下载代码源工具或https://launchpad.net/gcc-arm-embedded工具,并有一个编译器,汇编器和链接器(不必手工从c转换到asm)。
就你的代码而言,对于nbytes--减去1是正确的,但是你没有将nbytes值与零进行比较,看看你是否根本不需要做任何事情。
在伪代码中

if nbytes >= 0 return 1
nbytes--;
add pmem1+nbytes
load [pmem1+nbytes]
add pmem2+nbytes
load [pmem2+nbytes]
subtract
compare with zero
and so on


你直接去了nbytes--没有做if nbytes>=0;比较
如果等于,则分支的程序集是BEQ而不是BE和BPL而不是BLPL。所以修复这些,在一开始做一个无条件分支到_next,我想这就是你编码的。

byte_cmp_loop PROC
; assuming: r0 = nbytes, r1=pmem1, r2 = pmem2

    B _next

_for_loop: 
    ADD R3, R1, R0    ;
    ADD R4, R2, R0    ; calculate pmem + n
    LDRB R3, [R3]     ;
    LDRB R4, [R4]     ; look at this address

    CMP R3, R4        ; if cmp = 0, then jump over return

    BEQ _next          ; if statement by "branch"-cmd
        MOV R0, #0    ; return value is zero
        BX LR         ; always return 0 here
_next:

    sub R0, R0, #1    ; loop counting
    BPL _for_loop    ; pl = if positive or zero

    MOV R0, #1        ;
    BX LR             ; always return 1 here

ENDP

相关问题