2011-11-19 53 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