在 qemu 上使用arm64。
我想检查用户输入的值是负数、正数还是零。我有一条 SUBS 指令,后跟三个 B.Cond 指令。我使用 %d 格式的 scanf 来获取变量,并使用 printf 来输出变量。这是在一个玩具程序中发生的,没有其他功能。
如果我更改 B.Cond 指令的顺序,以防由于我自己的无知或缺乏对细节的关注而导致某些默认行为,则会运行 B.GT 指令。
如果我更改用于与用户值进行比较的值,以防万一我缺少条件分支指令的一些更精细的点,则会运行 B.GT 指令。
如果我将零检查指令更改为 CBZ,以防再次出现 B.Cond 指令的某些方面,则运行 B.GT 指令。
以下是相关代码。我省略了与打印哪个分支相关的部分。
.section .data
input_number: .asciz "Please enter a number: "
input_spec: .asciz "%d"
error: .asciz "Please input a positive number \n"
negative: .asciz "Input is NEGATIVE.\n"
zero: .asciz "Input is ZERO.\n"
positive: .asciz "Input is POSITIVE.\n"
.section .text
.global main
main:
LDR x0, =input_number
BL printf
SUB SP, SP, #16
LDR x0, =input_spec
MOV x1, SP
BL scanf
LDUR x9, [SP, #0]
ADD SP, SP, #16
CMP x9, xzr
B.GT POSITIVE
B.LT NEGATIVE
CBZ x10, ZERO
B exit
NEGATIVE:
LDR x0, =negative
BL printf
B exit
ZERO:
LDR x0, =zero
BL printf
B exit
POSITIVE:
LDR x0, =positive
BL printf
B exit
exit:
mov x0, 0
mov x8, 93
svc 0
ret
根据建议进行编辑并回复。
@Nate Eldridge:我在分支末尾添加了执行代码以及关联的全局变量。
我已将堆栈增量和减量编辑为 16 的倍数。我在其他地方读过该内容,但这样做会导致我的原始递归程序中无限减法,就好像它从未识别出所跟踪的值何时一样变为零,因此无限递归。
我知道
MOV
和 CMP
宏(说明?)。由于我仍在学习指令集,因此我想在使用更简单的方法之前先学习标准指令。为了便于阅读,我已根据您的建议更改了代码。CBZ
设为 B.EQ
,但出于测试目的对其进行了更改。我觉得表明多种类型的条件分支指令受到影响可能很有用,而不仅仅是B.Cond
。
您对
%d
使用 scanf
格式说明符,它需要一个指向 32 位字的指针。 但是,您随后可以使用 LDUR x9, [SP, #0]
将其从堆栈中加载,这是一个 64 位加载。 因此 x9
(也称为 w9
)的低 32 位将包含数字,但高 32 位将包含垃圾。 如果该垃圾的最高位恰好为零(这很可能是因为所有堆栈内存在程序开始时都被清零),那么生成的有符号 64 位数字将为正数。
修复它的一些方法:
将 32 位加载到带有符号扩展的 64 位寄存器中:
LDRSW x9, [SP]
。 (如果您愿意,也可以使用 LDURSW
。当它为零时,您无需写入立即偏移量 #0
。)然后 x9
包含 64 位值,该值在数值上等于堆栈中的 32 位值,您的比较将按预期进行。
将 32 位加载到 32 位寄存器中,然后只需使用 32 位指令:
LDR w9, [SP]
后跟 CMP w9, wzr
(或原始版本中的 SUBS w10, w9, wzr
)。
让 scanf 解析 64 位
long int
值:使用 %ld
而不是 %d
作为格式字符串。 然后其余代码可以保持不变。