【问题标题】:GCD function for Assembly in ARM not working propertlyARM 中汇编的 GCD 功能无法正常工作
【发布时间】:2016-11-19 08:44:32
【问题描述】:

我对汇编语言很陌生,我正在制作一个计算 GCD 的程序,但我遇到了麻烦。输入两个数字时,它不会返回正确答案。有什么建议么?我认为问题出在 gcd 函数中,但我不确定。

.text
.global main


     // Usage of the function: gcd(x,y), with the gcd returned in R0.
     // Parameters: Register R0 must contain x, and register R1 must contain y.
gcd:
     bal mod
     cmp r0, #0
     bne gcd
     // end of gcd function

     // Utility modulus function for you to use in your gcd function:
     // Usage: mod(x,y), returns the modulus of x and y in register R0.
     // Parameters: Register R0 must contain x, and register R1 must contain y.
mod:    push {LR}
    mov R3, R1
    mov R1, #0
    mov R2, #1
    //Shift the denominator left until greater than numerator, then shift back 
_shift_left:
        //R3<<=1; //Denominator shift left
    mov r3, r3, asl #1
        //R2<<=1; //Division shift left
    mov r2, r2, asl #1
        //if(R0>R3)goto _shift_left;//Shift Left until Decrement/Division Greater than Numerator
    cmp r0, r3 // compare r0 to r3
    bgt _shift_left // branch if greater than to _shift_left label
        //R3>>=1; //Shift Denominator right
    mov r3, r3, asr #1
        //R2>>=1; //Shift Division right
    mov r2, r2, asr #1
        //Loop and keep subtracting off the shifted Denominator 
_subtract: //if(R0<R3)goto _done;
    cmp r0, r3 // compare r0 to r3
    blt _done // branch if less than to _done
        //R1+=R2; //Increment division by the increment
    adds r1, r2
        //R0-=R3; //Subtract shifted Denominator with remainder of Numerator
    subs r0, r3 //Shift right until denominator is less than numerator
_shift_right:
        //if(R2==1) goto _subtract;
    cmp r2, #1 // compare r2 to #1
    beq _subtract // branch if equal to _subtract
        //if(R3<=R0)goto _subtract;
    cmp r3, r0 // compare r3 to r0
    ble _subtract // branch if less than or equal to _subtract
        //R2>>=1; //Shift Increment left
    mov r2, r2, asr #1
        //R3>>=1; //Shift Denominator left
    mov r3, r3, asr #1
        //goto _shift_right; //Shift Denominator until less than Numerator
    bal _shift_right
        //goto _subtract; //Keep Looping until the division is complete
    bal _subtract
_done:  pop {PC}
// end of mod function

main:
    push {LR}
    ldr R0, =welcome
    bl printf

    ldr R0, =prompt_a
    bl printf

    ldr R0, =scan_pat
    ldr R1, =a
    bl scanf

    ldr r0, =prompt_b
    bl printf

    ldr r0, =scan_pat
    ldr r1, =b
    bl scanf

    ldr r0, =a
    ldr r0, [r0]
    ldr r1, =b
    ldr r1, [r1]
    bl gcd
    ldr r1, =gcd_result
    str r0, [r1]

    ldr r0, =gcd_msg
    ldr r1, =a
    ldr r1, [r1]
    ldr r2, =b
    ldr r2, [r2]
    ldr r3, =gcd_result
    ldr r3, [r3]
    bl printf

    mov r0, #0
    pop {PC}
// end of main function

.data
welcome: .asciz "Welcome to the GCD Program!\n"
prompt_a: .asciz "Enter a value for A: "
prompt_b: .asciz "Enter a value for B: "
gcd_msg: .asciz "gcd(%d,%d) = %d\n"
scan_pat: .asciz "%d"

a: .word 0
b: .word 0
gcd_result: .word 0

【问题讨论】:

  • 我建议用高级语言编写函数,然后然后将其转换为汇编。 C 是一种易于手动编译为汇编的语言。另外,学习使用调试器。另外,我建议学习并遵守平台调用约定。

标签: assembly raspberry-pi arm raspbian raspberry-pi3


【解决方案1】:

下面是一个 ARMv7 GCD 宏。您应该能够将其编写为函数并忽略 .macro 指令等。我正在使用 GCC 指令。

我有一段时间没有测试代码,所以它可能包含一些错误,并且在某些情况下可能效率不高;尽管如此,它应该对您有所帮助。

        1:                                                                                                                                                              

        cmp     r1, r2          // r1 - r2 set cpsr                                                                                                                     
        subgt   r1, r1, r2      // sub if r1 > r2 and put result in r1                                                                                                  
        mov r0, r1                                                                                                                                                      
        sublt   r2, r2, r1      // else sub if r2 < r1 and put result in r2                                                                                             

        bne 1b                                                                                                                                                          

【讨论】:

  • 为什么不使用宏的寄存器 args 作为算法的暂存寄存器?如果调用者想要保存原件,他们可以这样做,但是如果您不需要推送/弹出部分,这种方式会阻止您有效地使用它。 (即,您可能会从 C 编译器中获得比以这种方式使用宏更好的结果)。
  • @PeterCordes - 对不起,我不明白你评论的重点。我的帖子并不是要向询问者展示如何编写宏;只是为 GCD 编写一些程序集的一种方法。请澄清。
  • 您的宏包括push {r1-r2} 和一个匹配的pop。它还包括两个 MOV 指令,用于将 args 放入 r1r2。与在出现r1 的宏中的任何地方使用\n1 相比,这些也是浪费指令,等等。(此外,如果你将它用作gcd r2, r1,你的宏会中断,因为第一个mov 会破坏第二个参数)。如果您只记录宏破坏其输入,并在第三个寄存器 arg 中产生输出(而不是硬编码 r0),那么最终您将获得更好的代码。
  • TL:DR 如果您要建议一个宏,请不要举一个设计低效方法的例子。
  • 如果你不打算建议使用宏,那么你绝对不应该把你的答案写成一个。它肯定会让一些人认为它实际上是编写 asm 的好方法。 (是的,SO 问题通常有比这更糟糕的代码,例如,在循环中保存/恢复寄存器而不是使用保留调用的 regs。所以不要以为读者会看到明显的部分,只接受有用的部分,如果他们已经编写 GCD 时遇到问题。)将其编写为宏与 @ inputs in r1, r2. @ result in r0 的评论相比只是噪音。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-23
  • 2015-06-28
  • 2013-11-06
  • 2013-10-29
  • 1970-01-01
相关资源
最近更新 更多