对于输入的每个用户输入数字,程序始终输出“1”(ARM 汇编器)

问题描述 投票:0回答:1

我正在用 ARM 汇编编写一个 main.s 程序,并将其与 checkPrimeNumber.c 程序一起运行。程序提示用户输入两个正数并输出它们之间的素数。例如,如果输入 2 和 6,则输出的素数将为 3 和 5。 main 中调用 checkPrimeNumber 函数来检查给定整数 n 是否为素数。我遇到一个问题,如果我输入两个数字,每次尝试运行程序时都会得到 1 的值。

我在调试器中运行程序并修复了一些寄存器,以便它们不占用程序中的空间,但这并没有改变结果。我不确定这是否是内存分配问题,或者我是否没有在程序中正确进行比较。作为参考,我提供了 main.s 代码和 checkPrimeNumber.c 代码

主.s

.cpu cortex-a53
.fpu neon-fp-armv8

@ Data section
.data

inp1:   .asciz "Enter two positive numbers"
inp2:   .asciz "%d %d"
outp:   .asciz "Prime numbers between %d and %d are: %d\n "

.balign             @ Align the data section to 4 bytes

n1:   .word 0
n2:   .word 0
i:    .word 0
flag: .word 0

.text
.align 2               @ Align the code section to 2 bytes
.global main           @ Declare 'main' as a global symbol
.type main, %function   @ Specify the type of the 'main' symbol as a function

main:
    push {fp, lr}      @ Save the frame pointer (fp) and link register (lr) to the stack
    add fp, sp, #4     @ Set the frame pointer (fp) to the current stack pointer (sp)

    @ Print the prompt "Enter two positive numbers"
    ldr r0, =inp1
    bl printf

    @ Scanf ("%d %d", &n1, &n2)
    ldr r0, =inp2
    ldr r1, =n1        @ Load the memory address of n1 into r1
    ldr r2, =n2        @ Load the memory address of n2 into r2
    bl scanf

    @ Initialize flag = 1 (to assume that numbers are prime)
    ldr r0, =flag
    mov r5, #1
    str r5, [r0]

    @ Load the input values from memory into r1 and r2
    ldr r1, =n1        @ Load the value of n1 into r1
    ldr r8, [r1]       @ Load the content of the memory address pointed by r1 into r1
    ldr r2, =n2        @ Load the value of n2 into r2
    ldr r6, [r2]       @ Load the content of the memory address pointed by r2 into r2

    @ Initialize i = n1 + 1
    add r3, r8, #1

forloop:              @ Loop label (start of the loop)
    @Check if i < n2
    cmp r3, r2         @ Compare i with n2
    bgt done           @ If i is greater than or equal to n2, exit the loop

    @ Call the function checkPrimeNumber(i) and store the result in 'flag'
    mov r0, r3         @ Move the current value of i to r0 (the function argument)
    bl checkPrimeNumber
    

    @ Check if flag == 1 (if the number is prime)
    cmp r0, #1
    bne increment_num  @ If not equal to 1, go to increment_num label and proceed to the next iteration

    
   @ Print the output statement "Prime numbers between %d and %d are: "
    ldr r0, =outp     @ print out end prompt 
    mov r1, r8        @ Move the first range value (n1) to r1 (for printf)
    mov r2, r6         @ Move the second range value (n2) to r2 (for printf)
    mov r4, r3        @move the resulting prime numbers (i) to r4 (for printf)
    bl printf
   
 increment_num:    @ Increment the loop variable (i) and continue to the next iteration
    add r3, r3, #1
    b forloop          @ Branch back to the forloop label

done:
    sub sp, fp, #4     @ Restore the stack pointer (sp) to its original value
    pop {fp, pc}       @ Restore the frame pointer (fp) and program counter (pc) to return from the function


检查PrimeNumber.c


#include <stdio.h>
int checkPrimeNumber(int n);
int main()
{
int n1, n2, i, flag;
printf("Enter two positive integers: ");
scanf("%d %d", &n1, &n2);
printf("Prime numbers between %d and %d are: ", n1, n2);
for(i=n1+1; i<n2; ++i)
{
// i is a prime number, flag will be equal to 1
flag = checkPrimeNumber(i);
if(flag == 1)
printf("%d ",i);
}
return 0;
}

assembly raspberry-pi arm
1个回答
0
投票

您的代码不遵守 AAPCS ABI 规定的调用约定的寄存器使用规则

子程序必须保留寄存器 r4-r8、r10、r11 和 SP 的内容(以及 PCS 变体中将 r9 指定为 v6 的 r9)的内容。

所以:

  • 您必须假设 r0-r3 被任何函数调用覆盖(除非您自己用纯 asm 编写的函数)。它们是“被调用破坏的寄存器”。在您的代码中,您使用 r2 和 r3 进行循环索引,但它们可能会被对

    checkPrimeNumber
    printf
    的调用覆盖。

  • r4-r8 和 r10 不会被函数调用覆盖(也就是说,如果函数使用这些寄存器,它将在返回之前恢复其原始值)。它们是“呼叫保留的”。因此它们适合循环索引等值。但是,您的

    main
    函数本身受 ABI 规则的约束,因此您必须确保在使用任何这些寄存器时,在
    main
    返回之前恢复其原始值。最简单的方法是将它们添加到函数入口和出口处的
    push
    pop
    指令的寄存器列表中。

另一个注意事项是,每当调用函数时,堆栈指针都必须与 8 字节对齐。输入

main
时保证对齐,因此确保这一点的一个简单方法是确保在函数的入口/出口时推送/弹出偶数个寄存器(前提是您不修改
 sp
其他任何地方)。现在您的代码满足了这一点,但在添加到推送/弹出列表以保留您使用的调用保留寄存器时请注意这一点。如果最终得到的是奇数,您可以简单地在列表中添加一个额外的寄存器,例如
r1

其他一些评论:

  • 您的asm代码不包含C代码中的

    return 0
    。在
    mov r0, #0
    之后添加
    done:

  • 实际上没有必要将

    i
    flag
    保留在记忆中。事实上,您的代码已经没有真正使用它们;您在那里存储永远不会加载的值。您只需选择一个用于
    i
    的调用保留寄存器并在整个过程中使用它。并且
    flag
    根本不需要存在,因为它纯粹是暂时的。你在
    checkPrimeNumber
    中得到
    r0
    的返回值,然后简单地执行
    cmp r0, #1
    就可以了。这类似于将 C 代码编写为
    if (checkPrimeNumber(i) == 1)
    ,而没有不必要的
    flag
    变量。

  • C 代码中的变量

    n1, n2
    main
    的本地变量,但在您的 asm 代码中,您已将它们设为全局变量。无论哪种方式都可以正常工作,但是将它们放在本地会更干净一些,因此您可以尝试更改 asm 版本以将它们保留在堆栈上而不是数据部分中。您可能会发现这也会稍微缩短您的代码。例如,如果
    n1
    位于
    sp+4
    ,那么您只需执行
    ldr r1, =n1
    ,而不是
    add r1, sp, 4
    ;您可以将
    ldr r1, =n1 ; ldr r8, [r1]
    替换为
    ldr r8, [sp, #4]

© www.soinside.com 2019 - 2024. All rights reserved.