2011-11-19 11 views
9

Supponiamo di avere un numero intero in un registro, come posso stamparlo? Puoi mostrare un semplice codice di esempio?Come stampare un numero nell'assemblaggio NASM?

So già come stampare una stringa come "ciao, mondo".

Sto sviluppando su Linux.

+0

Specificare il sistema operativo in cui verrà eseguito il programma. –

+1

Correlato: [convertire un numero intero in una stringa decimale ASCII in un buffer nello stack e stamparlo con la chiamata di sistema 'write' di Linux] (https://stackoverflow.com/a/46301894/224132), non usando' printf' o qualsiasi altra funzione. Con commenti e spiegazioni. –

risposta

10

Se si è già su Linux, non è necessario effettuare la conversione da soli. Basta usare printf invece:

; 
; 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 

Nota che printf utilizza il cdecl calling convention quindi abbiamo bisogno di ripristinare lo stack pointer dopo, vale a dire aggiungere 4 byte per il parametro passato alla funzione.

+0

Grazie, sembra essere quello che stavo cercando. Sai se funziona anche su Mac os X? – AR89

+0

Come compilarlo su 64 -bit? –

+0

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

1

Dipende dall'architettura/dall'ambiente che si sta utilizzando.

Ad esempio, se voglio visualizzare un numero su linux, il codice ASM sarà diverso da quello che userei su Windows.

Edit:

È possibile fare riferimento a THIS per un esempio di conversione.

+0

Un esempio di Linux andrebbe bene. – AR89

+0

@ AR89 è un brutto lavoro .. Devi prima convertire il numero in ASCII. Dai un'occhiata alla domanda modificata. – AlQafir

10

Devi convertirlo in una stringa; se stai parlando di numeri esadecimali è piuttosto facile. Qualsiasi numero può essere rappresentato in questo modo:

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

Quindi, quando si dispone di questo numero bisogna dividerla come ho mostrato poi convertire ogni "sezione" al suo equivalente ASCII.
Ottenere le quattro parti è facile con un po 'di magia, in particolare con uno spostamento verso destra per spostare la parte a cui siamo interessati nei primi quattro bit E il risultato con 0xf per isolarlo dal resto. Ecco quello che voglio dire (soppose vogliamo prendere la 3):

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

Ora che abbiamo un unico numero che dobbiamo convertirlo in suo valore ASCII. Se il numero è minore o uguale a 9, possiamo semplicemente aggiungere il valore ASCII di 0 (0x30), se è maggiore di 9 dobbiamo usare il valore ASCII di una (0x61).
Qui è, ora non ci resta che codificarlo:

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: So che questo è a 16 bit di codice, ma ho ancora utilizzare il vecchio TASM: P

PPS: questo è sintassi Intel, la conversione in AT & T sintassi non è difficile però, guarda here.

+0

@downvoter: motivo? – BlackBear

+0

Non * è necessario * sintassi AT & T per eseguire ciò su linux. –

+0

@ AndreiBârsan: hai ragione, risolto ... È una risposta così vecchia :) – BlackBear

3

x86-64 Linux con 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 

Poi:

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

I "punti duri" del System V AMD64 ABI convenzione di chiamata:

Se si desidera esadecimale senza la libreria C: https://stackoverflow.com/a/32756303/895245

0

Sono relativamente nuovo per il montaggio, e questo, ovviamente, non è la soluzione migliore, ma sta funzionando. La funzione principale è _iprint, in primo luogo controlla se il numero in eax è negativo e stampa un segno meno se così, che procede stampando i singoli numeri chiamando la funzione _dprint per ogni cifra. L'idea è la seguente, se abbiamo 512 di quanto sia uguale a: 512 = (5 * 10 + 1) * 10 + 2 = Q * 10 + R, quindi possiamo trovare l'ultima cifra di un numero dividendolo per 10, e ottenendo il promemoria R, ma se lo facciamo in un ciclo di cifre saranno in un ordine inverso , quindi usiamo lo stack per spingerli, e dopo che quando scrivendoli a stdout vengono visualizzati in ordine corretto.

; 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

È possibile aggiungere '0' e memorizzare le cifre in un buffer mentre le si produce. Utilizzare 'dec' spostare l'indicatore verso il basso. Quando hai finito, hai un puntatore all'ultima cifra memorizzata, quindi puoi passarlo a 'sys_write()' (insieme al numero di cifre) .Questo è molto più efficiente di una chiamata di sistema separata per ogni byte e in realtà non richiede più codice. È facile allocare un buffer sufficientemente lungo da contenere la più lunga stringa di cifre possibile e iniziare alla fine, perché si conosce il numero di cifre decimali 2^32. –

+0

related: Ho scritto un intero -> loop di stringhe come parte di questa [risposta codice-golf Fibonacci a precisione estesa] (https://codegolf.stackexchange.com/questions/133618/extreme-fibonacci/135618#135618). Vedi il ciclo '.toascii_digit:'. Ovviamente, questo è ottimizzato per le dimensioni, quindi usa un lento 'div' [invece di un trucco multiplo] (https://stackoverflow.com/questions/41183935/why-does-gcc-use-multiplication-by-a- strana-numero-in-attuazione-integer-divi). –

+1

Grazie, è decisamente preferibile che chiamare sys_write per ogni cifra :) – baz