【问题标题】:How to print numbers from 1 to n using assembly language?如何使用汇编语言打印从 1 到 n 的数字?
【发布时间】:2018-02-05 07:50:05
【问题描述】:

我是汇编语言的新手,我正在尝试使用汇编语言打印 1 到 n 个数字,而不使用 C 库。我正在使用 qemu 作为我的树莓派模拟器。

这是我尝试过的示例代码

    .global _start
_start:
MOV R7, #3 @ Syscall read from keyboard
MOV R0, #0 @ Input stream keyboard
MOV R2, #1  @ Read 1 characters
LDR R1, =character @ Put number in R1
MOV R6,R1 @Holding value of R1 i.e. the input number 'n'
MOV R1,#0 @initialiing value 
MOV R2, #1 @counter
SWI 0

_loop:
AND R1, R1, R2 @Add one to each value of R1
B _write @print the value
SWI 0


_write:
MOV R7,#4 @ Syscall for output
MOV R0,#1 @ Output stream for R1
MOV R2,#1
CMP R1,R6 @Compare if the value is equal to 'n'
BNE _loop @if less than, then add again and print
SWI 0

end:
MOV R7,#1
SWI 0

.data
character:
.word 0

请告诉我,应该怎么做。

谢谢

【问题讨论】:

  • 你需要解释你的程序实际做了什么,而不仅仅是它应该做什么。 minimal reproducible example。它会组装吗?它会崩溃吗?它是否打印了一些东西但不是你想要的?尝试运行strace ./a.out来跟踪系统调用,或者在gdb下运行。
  • 你卡在哪里了?
  • 上面的程序不行,应该是输入一个n,依次打印出从0到n的所有值。例如:如果我输入 5,它应该打印 1 2 3 4 5。我搜索了很多,但我无法弄清楚问题是什么。有什么可以参考的例子吗?

标签: assembly raspberry-pi arm qemu


【解决方案1】:

我注意到一些不正确的事情。当调用例程 (SWI) 时,可以更改寄存器,因此您应该将值保存在您正在使用的寄存器中 (push {r1, r2, r6, lr}) 并在例程 (pop {r1, r2, r6, lr}) 之后恢复它们,期望 r1 和 r2。 AND 指令不是一个好的加法器,请使用 add。在_loop: 中有一个额外的SWI 0 指令。在_write:中需要有character的地址在寄存器r1中,并且要写入的数据需要在内存中标记为character作为可打印字符(0x00000001不是可打印字符,添加0x30或十进制48 将使其成为可打印字符)。

我对您的程序的修改是小写的。如果您希望使用gdb,我将main 添加到顶部。我跳过/绕过了输入并对输入进行了硬编码。

    .global _start
main:
_start:
@MOV R7, #3 @ Syscall read from keyboard
@MOV R0, #0 @ Input stream keyboard
@MOV R2, #1  @ Read 1 characters
@LDR R1, =character @ Put number in R1
@MOV R6,R1 @Holding value of R1 i.e. the input number 'n'
@MOV R1,#0 @initialiing value
@MOV R2, #1 @counter
@SWI 0
mov  r6, #5
mov  r1, #0
mov  r2, #1

_loop:
@AND R1, R1, R2 @Add one to each value of R1
add  r1, r1, r2
B _write @print the value
@SWI 0


_write:
push {r1, r2, r6, lr}
add  r3, r1, #48
ldr  r0, =character
str  r3, [r0]
MOV R7,#4 @ Syscall for output
MOV R0,#1 @ Output stream for R1
ldr r1, =character
MOV R2,#1
SWI 0
pop {r1, r2, r6, lr}
CMP R1,R6 @Compare if the value is equal to 'n'
BNE _loop @if less than, then add again and print

end:
MOV R7,#1
SWI 0

.data
character:
.word 0

组装、链接和输出

as -o count2.o count2.s
ld -o count2 count2.o
./count2
12345

补充:

我没有使用过 QEMU,但是查看https://azeria-labs.com/load-and-store-multiple-part-5/ 我认为问题可能是访问内存存储。试试这个,看看它是否有所作为。我还更改了pushpop 指令。

    .global _start
_start:
@MOV R7, #3 @ Syscall read from keyboard
@MOV R0, #0 @ Input stream keyboard
@MOV R2, #1  @ Read 1 characters
@LDR R1, =character @ Put number in R1
@MOV R6,R1 @Holding value of R1 i.e. the input number 'n'
@MOV R1,#0 @initialiing value
@MOV R2, #1 @counter
@SWI 0
mov  r6, #5
mov  r1, #0
mov  r2, #1

_loop:
@AND R1, R1, R2 @Add one to each value of R1
add  r1, r1, r2
B _write @print the value
@SWI 0


_write:
@push {r1, r2, r6, lr}
stmdb sp!, {r1, r2, r6, lr}
add  r3, r1, #48
@ldr  r0, =character
ldr  r0, character_address
str  r3, [r0]
MOV R7,#4 @ Syscall for output
MOV R0,#1 @ Output stream for R1
@ldr r1, =character
ldr r1, character_address
MOV R2,#1
SWI 0
@pop {r1, r2, r6, lr}
ldmia sp!, {r1, r2, r6, lr}
CMP R1,R6 @Compare if the value is equal to 'n'
BNE _loop @if less than, then add again and print

end:
MOV R7,#1
SWI 0

character_address:
.word character

.data
character:
.word 0

【讨论】:

  • 你有参考Linux系统在ARM EABI中调用clobber的寄存器吗?在大多数平台上,Linux 系统调用 clobber 尽可能少的寄存器(通常只有返回值,但 x86-64 也会破坏 rcxr11 因为 syscall 使这不可避免。)如果 ARM Linux 我会感到惊讶EABI 破坏了它所有的输入寄存器,但这当然是可能的。
  • 如果您还是要将寄存器推入堆栈,您最好只传递角色的堆栈地址,而不是为其使用静态位置。
  • @Peter Cordes 第 14 页,infocenter.arm.com/help/topic/com.arm.doc.ihi0042f/… 提供了 ARM 寄存器使用指南。寄存器 r0 - r3 处于危险之中。
  • 这就是 function 调用约定。它没有提到 SWI,并指出它不是完整的 ABI(在该文档中搜索“linux”)。您应该期望它们有所不同,因为例如 i386 Linux (System V psABI) 在函数调用上会破坏 eaxecxedx,但在系统调用上只会破坏 eax(带有返回值)。
  • 嗨!运行上述代码时出现分段错误。你运行过这个吗?
猜你喜欢
  • 1970-01-01
  • 2017-03-01
  • 2010-12-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多