我试图编写一个嵌套循环并打印 10x10 的点网格。当我尝试在 _print 子例程中使用 PUSH 和 POP 命令时,汇编器会抛出错误。这是正确的方法还是我做错了什么。请帮忙
.equ WIDTH, 10
.equ HEIGHT, 10
.data
DOT: .ascii "."
BLOCK: .ascii "$"
NEW_LINE: .ascii "\n"
.text
.global _start
_start:
_renderFrame:
mov x0, HEIGHT
mov x1, WIDTH
bl _height
_height:
cmp x0, 0
beq _exit
sub x0, x0, 1
bl _width
ldr x3, =NEW_LINE
bl _print
bl _height
_width:
cmp x1, 0
beq _height
sub x1, x1, 1
ldr x3, =DOT
bl _print
bl _width
_print:
push {x0, x1, x2}
mov x8, 0x40
mov x0, 1
mov x1, x3
mov x2, 1
svc 0
pop {x0, x1, x2}
ret
_exit:
mov x8, 0x5d
mov x0, 0
svc 0
错误如下
main.asm: Assembler messages:
main.asm:35: Error: unknown mnemonic `push' -- `push {x0,x1,x2}'
main.asm:41: Error: unknown mnemonic `pop' -- `pop {x0,x1,x2}'
注意: 之前我尝试在 macOS 中运行汇编,但网上似乎没有太多关于 macOS 的支持文章。因此,我在带有内置汇编器和链接器的 Ubuntu Docker 容器中运行此代码。并且打印和退出系统调用工作正常。
ARM64 汇编中没有
push
或 pop
指令。也许你把它与 ARM32 搞混了,ARM32 确实有它们。
要在 ARM64 上压入和弹出堆栈,您可以在
sp
寄存器上使用正常的加载和存储指令以及预减/后增量寻址模式。请注意,您必须始终以 16 字节为增量调整 sp
,以便使用 ldp
加载对和 stp
存储对指令一次推送和弹出两个 64 位寄存器非常方便。
示例:
stp x0, x1, [sp, #-16]! // push x0 and x1
ldp x0, x1, [sp], #16 // pop them again
如果你想压入/弹出两个以上的寄存器,你可以重复这个,或者使用第一个/最后一个存储/加载来调整堆栈指针所有寄存器所需的总量,然后使用非前置/后置-增加其他人的存储和负载。
示例:
stp x0, x1, [sp, #-32]!
str x2, [sp, 16] // store x2 above x0 and x1
ldr x2, [sp, 16]
ldp x0, x1, [sp], #32
不相关,但在代码中使用
bl
似乎很混乱。 bl
用于函数调用;它将返回地址保存到链接寄存器x30
中,然后分支到目标地址,该目标地址应该是函数或子例程的第一条指令。然后,被调用的函数应以 ret
结束,分支回存储在 x30
中的地址,此时继续执行 bl
后面的指令。因此,不清楚为什么您使用它来跳转到 _width
和 _height
,它们不是子例程并且不会返回。如果您想要无条件分支,请使用 b
而不是 bl
。
但是在你的代码中如下:
_height:
cmp x0, 0
beq _exit
sub x0, x0, 1
bl _width
ldr x3, =NEW_LINE // this and following lines are unreachable
bl _print
bl _height
由于
_width
没有ret
指令,它不会返回(实际上它通过执行自己的x30
来覆盖bl
),因此bl _width
后面的代码永远无法执行。所以我认为你需要对程序的控制流程多加一些思考。