2011-11-19 54 views
9

假设我在一个寄存器中有一个整数,我该如何打印它?你可以展示一个简单的示例代码吗?如何在组件NASM中打印数字?

我已经知道如何打印字符串,如“你好,世界”。

我在Linux上开发。

+0

请指定程序运行的操作系统。 –

+1

相关:[将整数转换为栈中缓冲区中的ASCII十进制字符串并使用Linux'write'系统调用](https://stackoverflow.com/a/46301894/224132)打印,而不是使用'printf'或任何其他功能。有评论和解释。 –

回答

10

如果您已经在Linux上,则无需自己进行转换。只需使用printf代替:

; 
; assemble and link with: 
; nasm -f elf printf-test.asm && gcc -m32 -o printf-test printf-test.o 
; 
section .text 
global main 
extern printf 

main: 

    mov eax, 0xDEADBEEF 
    push eax 
    push message 
    call printf 
    add esp, 8 
    ret 

message db "Register = %08X", 10, 0 

注意printf使用cdecl calling convention所以我们需要事后恢复堆栈指针,即每增加传递给函数的参数4个字节。

+0

谢谢,它似乎是我在寻找的东西。你知道它是否也适用于Mac OS X吗? – AR89

+0

如何在64编译它-bit? –

+0

@FigenGüngörhttp://stackoverflow.com/a/32853546/895245 –

1

这取决于您使用的架构/环境。例如,如果我想在Linux上显示一个数字,ASM代码将与我在Windows上使用的代码不同。

编辑:

您可以参考THIS转换的一个例子。

+0

一个Linux例子会很好。 – AR89

+0

@ AR89这是一个糟糕的工作..你必须先将数字转换为ASCII码。看看编辑过的问题。 – AlQafir

10

你必须将它转换成一个字符串;如果你在谈论十六进制数字,那很简单。任何数字都代表这样说:

0xa31f = 0xf * 16^0 + 0x1 * 16^1 + 3 * 16^2 + 0xa * 16^3 

所以,当你有这个号码,你必须把它分解像我展示之后每“节”转换为字符的ASCII等价的。获得这四个部分很容易完成,具有一些魔力,特别是在右前移四个部分,然后用0xf移动我们感兴趣的部分,并将结果与​​其余部分隔离。这里就是我的意思(soppose我们想拿3):

0xa31f -> shift right by 8 = 0x00a3 -> AND with 0xf = 0x0003 

现在,我们有我们必须把它转换成字符的ASCII值单号。如果数字小于或等于9,我们可以添加0的ASCII值(0x30),如果它大于9,我们必须使用ASCII值(0x61)。
这,现在我们只需要编写它:

mov si, ???   ; si points to the target buffer 
    mov ax, 0a31fh  ; ax contains the number we want to convert 
    mov bx, ax   ; store a copy in bx 
    xor dx, dx   ; dx will contain the result 
    mov cx, 3   ; cx's our counter 

convert_loop: 
    mov ax, bx   ; load the number into ax 
    and ax, 0fh   ; we want the first 4 bits 
    cmp ax, 9h   ; check what we should add 
    ja greater_than_9 
    add ax, 30h   ; 0x30 ('0') 
    jmp converted 

greater_than_9: 
    add ax, 61h   ; or 0x61 ('a') 

converted: 
    xchg al, ah  ; put a null terminator after it 
    mov [si], ax  ; (will be overwritten unless this 
    inc si    ; is the last one) 

    shr bx, 4   ; get the next part 
    dec cx    ; one less to do 
    jnz convert_loop 

    sub di, 4   ; di still points to the target buffer 

PS:我知道这是16位代码,但我仍然使用旧的TASM:P

PPS:这是英特尔语法,转换为AT & T语法虽然不难,但请看here

+0

@downvoter:原因? – BlackBear

+0

你不需要* AT&T语法来在linux上运行这个。 –

+0

@AndreiBârsan:你说得对,修复了..这是一个旧的答案:) – BlackBear

3

的Linux的x86-64与printf的

extern printf, exit 
section .data 
    format db "%x", 10, 0 
section .text 
    global main 
    main: 
     sub rsp, 8 
     mov rsi, 0x12345678 
     mov rdi, format 
     xor rax, rax 
     call printf 
     mov rdi, 0 
     call exit 

然后:

nasm -f elf64 main.asm 
gcc main.o 
./a.out 

System V的AMD64 ABI调用约定的 “硬点”:

如果你想不十六进制的C库:https://stackoverflow.com/a/32756303/895245

0

我是比较新的组件,而这显然不是最好的解决办法,但 它的工作。主要功能是_iprint,它首先检查eax中的 号码是否为负值,如果是,则打印负号,然后通过调用每个数字的 调用函数_dprint打印单个号码来执行 。这个想法如下,如果我们有512比它等于:512 =(5 * 10 + 1)* 10 + 2 = Q * 10 + R,所以我们可以找到一个数字的最后一位数字除以10,和 得到提醒R,但如果我们在一个循环中执行比数字将在 倒序,所以我们使用堆栈推动它们,然后当 写入他们的标准输出他们弹出正确的顺序。

; Build  : nasm -f elf -o baz.o baz.asm 
;    ld -m elf_i386 -o baz baz.o 
section .bss 
c: resb 1 ; character buffer 
section .data 
section .text 
; writes an ascii character from eax to stdout 
_cprint: 
    pushad  ; push registers 
    mov [c], eax ; store ascii value at c 
    mov eax, 0x04 ; sys_write 
    mov ebx, 1 ; stdout 
    mov ecx, c ; copy c to ecx 
    mov edx, 1 ; one character 
    int 0x80  ; syscall 
    popad   ; pop registers 
    ret   ; bye 
; writes a digit stored in eax to stdout 
_dprint: 
    pushad  ; push registers 
    add eax, '0' ; get digit's ascii code 
    mov [c], eax ; store it at c 
    mov eax, 0x04 ; sys_write 
    mov ebx, 1 ; stdout 
    mov ecx, c ; pass the address of c to ecx 
    mov edx, 1 ; one character 
    int 0x80  ; syscall 
    popad   ; pop registers 
    ret   ; bye 
; now lets try to write a function which will write an integer 
; number stored in eax in decimal at stdout 
_iprint: 
    pushad  ; push registers 
    cmp eax, 0 ; check if eax is negative 
    jge Pos  ; if not proceed in the usual manner 
    push eax  ; store eax 
    mov eax, '-' ; print minus sign 
    call _cprint ; call character printing function 
    pop eax  ; restore eax 
    neg eax  ; make eax positive 
Pos: 
    mov ebx, 10 ; base 
    mov ecx, 1 ; number of digits counter 
Cycle1: 
    mov edx, 0 ; set edx to zero before dividing otherwise the 
    ; program gives an error: SIGFPE arithmetic exception 
    div ebx  ; divide eax with ebx now eax holds the 
    ; quotent and edx the reminder 
    push edx ; digits we have to write are in reverse order 
    cmp eax, 0 ; exit loop condition 
    jz EndLoop1 ; we are done 
    inc ecx  ; increment number of digits counter 
    jmp Cycle1 ; loop back 
EndLoop1: 
; write the integer digits by poping them out from the stack 
Cycle2: 
    pop eax  ; pop up the digits we have stored 
    call _dprint ; and print them to stdout 
    dec ecx  ; decrement number of digits counter 
    jz EndLoop2 ; if it's zero we are done 
    jmp Cycle2 ; loop back 
EndLoop2: 
    popad ; pop registers 
    ret ; bye 
global _start 
_start: 
    nop   ; gdb break point 
    mov eax, -345 ; 
    call _iprint ; 
    mov eax, 0x01 ; sys_exit 
    mov ebx, 0 ; error code 
    int 0x80  ; край 
+0

你可以'添加'0''并将你的数字存储在缓冲区中,当你产生它们时,使用'dec'将指针向下移动。当你完成后,你有一个指向你存储的最后一位数字的指针,所以你可以将它传递给'sys_write()'(以及数字计数)。这比为每个字节进行单独的系统调用,并没有真正需要更多的代码。很容易分配足够长的缓冲区以保存最长的数字串,并从最后开始,因为您知道2^32有多少个小数位。 –

+0

相关:我写了一个整数 - >字符串循环作为[扩展精度斐波那契代码 - 高尔夫答案](https://codegolf.stackexchange.com/questions/133618/extreme-fibonacci/135618#135618)的一部分。请参阅'.toascii_digit:'循环。当然,这是针对大小进行优化的,所以它使用了一个缓慢的'div' [而不是一个多重技巧](https://stackoverflow.com/questions/41183935/why-does-gcc-use-multiplication-by-a-陌生的号码,在-实施-整数迪维)。 –

+1

谢谢你肯定比调用每个数字的sys_write更好:) – baz