我对使用汇编语言在屏幕上打印字符有些困惑。架构是x86(linux)。是否可以调用其中一个C函数或者有更简单的方法?要输出的字符存储在寄存器中。
谢谢!
当然,您可以使用任何普通的C函数。以下是一个使用printf打印输出的NASM示例:
;
; assemble and link with:
; nasm -f elf test.asm && gcc -m32 -o test test.o
;
section .text
extern printf ; If you need other functions, list them in a similar way
global main
main:
mov eax, 0x21 ; The '!' character
push eax
push message
call printf
add esp, 8 ; Restore stack - 4 bytes for eax, and 4 bytes for 'message'
ret
message db 'The character is: %c', 10, 0
如果你只想打印一个字符,你可以使用putchar函数:
push eax
call putchar
如果你想打印出一个数字,可以这样做:
mov ebx, 8
push ebx
push message
call printf
...
message db 'The number is: %d', 10, 0
将字符 DL
写入标准输出。 (我使用 DOS 模拟器 emu2 进行了测试)
mov dl, 21h ; char '!'
mov ah, 02h ; char output service
int 21h ; call DOS services
write
系统调用write
系统调用需要你提供字符串的地址。如果只有一个字符,你可以将其推入堆栈。
push '!' ; push dword
mov eax, 4 ; write call number, __NR_write from unistd_32.h
mov ebx, 1 ; write to stdout (fd=1)
mov ecx, esp ; use char on stack
mov edx, 1 ; write 1 char
int 0x80 ; call into the kernel
add esp, 4 ; restore sp
write
系统调用与上面相似,但现在的调用号是1,使用syscall
代替int 0x80
,并且调用规约寄存器不同。
push '!' ; push qword 0x21
mov rax, 1 ; write call number, __NR_write from unistd_64.h
mov edi, 1 ; write to stdout (int fd=1)
mov rsi, rsp ; use char on stack
mov rdx, 1 ; size_t len = 1 char to write.
syscall ; call the kernel, it looks at registers to decide what to do
add rsp, 8 ; restore stack pointer
mov edx, 1
和 mov rdx, 1
的作用是完全相同的,但 NASM 将为您优化。该代码使用与 C 类型匹配的操作数大小,不会将小的非负 64 位整数进行优化移动。
lea -1(%rsp), %rsi
/ movb $0x21, (%rsi)
来使用红区,而无需弹出堆栈。 - Peter Cordes调用 putchar(3)
是最简单的方法。只需将 char 的值移动到 rdi 寄存器 (对于 x86-64,或者是 edi 对于 x86),然后调用 putchar。
例如(对于 x86-64):
asm("movl $120, %rdi\n\t"
"call putchar\n\t");
将会在标准输出中打印一个x
。
%rdi
而不是%edi
--以及库函数前面的_
?) - Matthew Slatteryputchar
,但那之后会发生什么是不可预测的。 - Peter Cordesmovl
到 %edi
是将一个值零扩展到 RDI 的更有效的方式。就当前状态而言,这甚至无法组装 (movl双字操作数后缀与qword寄存器不匹配)。但无论如何,在没有任何破坏的情况下,这是完全错误的。请参见我的先前评论。在独立的 asm 中,mov $'x', %edi
;call putchar
这将是好的。 - Peter Cordes
printf("%d", 8);
。我已将其添加到答案中。 - Martin