3

我正在为 UNI 做一个项目,但遇到了问题。

我们正在学习如何在调用子程序时将寄存器压入/弹出堆栈。

我需要编写一个子程序将十进制数 0-15 转换为相应的十六进制 ASCII 码,并保留所有寄存器的值,除了r0应该存储结果的位置。我有一个 ASCII 代码表,基本上只需将number*4ASCII 表的起始地址添加,然后将值存储回r0.

我得到了正确的结果,但子程序循环。它不停地从跳到ldmfdsub。知道为什么吗?

main:
adr r0,num
adr r1,ascii
ldr r2,[r0]
bl  sub
sub:  stmfd sp!,{r1-r2,lr}
      ldr r0,[r1,r2,LSL #2]
      ldmfd sp!,{r1-r2,pc}

/* variables here */
num:    .word 15
ascii:  .word 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x41,0x42,0x43,0x44,0x45,0x46
/* end variables */
4

3 回答 3

10

请注意,您已将子例程直接放在调用它的指令之后:

bl  sub
sub:  stmfd sp!,{r1-r2,lr}
      ldr r0,[r1,r2,LSL #2]
      ldmfd sp!,{r1-r2,pc}

那么当你从第一次调用返回时会发生什么sub?它将返回到 之后的指令bl,即stmfd sp!,{r1-r2,lr}。因此子程序有效地返回到其自身的开头。并且它将继续这样做,因为没有额外的隐式或显式写入lr.

于 2013-04-05T12:51:57.267 回答
5

您的代码有几个问题。

  1. 主代码永远不会返回任何地方,这意味着在您的代码从子代码返回后,它将再次从“子:”开始,因为那是您的返回点。由于 lr 没有改变,它会卡在 stmfd/ldmfd 之间的循环中。您需要调用系统的退出功能。
  2. 没有理由保存 r1、r2、lr。您不会在代码中更改它们,而且 r0-r3 和 r12 无论如何都是调用者保存的寄存器。
于 2013-04-05T13:45:50.400 回答
-1

我不是 ARM 汇编专家,但我至少可以阅读一些本地编译器输出。

似乎 gcc 不喜欢存储lr在堆栈上(这是有道理的,寄存器的目的是尽可能避免堆栈访问)。

相反,子程序通过在链接寄存器上执行一个分支来结束:

bx  lr

注意:似乎bx(分支交换)指令仅在支持 Thumb 的处理器上可用,因此它可能不适用于您。

于 2013-04-05T12:41:22.607 回答