简体   繁体   English

如何在装配NASM中打印数字?

[英]How to print a number in assembly NASM?

Suppose that I have an integer number in a register, how can I print it? 假设我在寄存器中有一个整数,我该如何打印? Can you show a simple example code? 你能展示一个简单的示例代码吗?

I already know how to print a string such as "hello, world". 我已经知道如何打印一个字符串,如“你好,世界”。

I'm developing on Linux. 我正在Linux上开发。

You have to convert it in a string; 你必须用字符串转换它; if you're talking about hex numbers it's pretty easy. 如果你在谈论十六进制数字,这很容易。 Any number can be represented this way: 任何数字都可以这样表示:

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

So when you have this number you have to split it like I've shown then convert every "section" to its ASCII equivalent. 因此,当你有这个数字时,你必须像我展示的那样拆分它,然后将每个“部分”转换为它的ASCII等价物。
Getting the four parts is easily done with some bit magic, in particular with a right shift to move the part we're interested in in the first four bits then AND the result with 0xf to isolate it from the rest. 获得这四个部分很容易完成一些魔术,特别是右移以在前四位移动我们感兴趣的部分,然后用0xf将结果与其余部分隔离。 Here's what I mean (soppose we want to take the 3): 这就是我的意思(我们希望采用3):

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

Now that we have a single number we have to convert it into its ASCII value. 现在我们只有一个数字,我们必须将其转换为ASCII值。 If the number is smaller or equal than 9 we can just add 0's ASCII value (0x30), if it's greater than 9 we have to use a's ASCII value (0x61). 如果数字小于或等于9,我们可以添加0的ASCII值(0x30),如果它大于9,我们必须使用ASCII值(0x61)。
Here it is, now we just have to code it: 在这里,现在我们只需编写代码:

    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: I know this is 16 bit code but I still use the old TASM :P PS:我知道这是16位代码,但我仍然使用旧的TASM:P

PPS: this is Intel syntax, converting to AT&T syntax isn't difficult though, look here . PPS:这是英特尔语法,虽然转换为AT&T语法并不困难,但请看这里

If you're already on Linux, there's no need to do the conversion yourself. 如果您已经在Linux上,则无需自行进行转换。 Just use printf instead: 只需使用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

Note that printf uses the cdecl calling convention so we need to restore the stack pointer afterwards, ie add 4 bytes per parameter passed to the function. 请注意, printf使用cdecl调用约定,因此我们需要在之后恢复堆栈指针,即每个传递给函数的参数添加4个字节。

Linux x86-64 with printf 带有printf的Linux x86-64

main.asm main.asm中

default rel            ; make [rel format] the default, you always want this.
extern printf, exit    ; NASM requires declarations of external symbols, unlike GAS
section .rodata
    format db "%#x", 10, 0   ; C 0-terminated string: "%#x\n" 
section .text
global main
main:
    sub   rsp, 8             ; re-align the stack to 16 before calling another function

    ; Call printf.
    mov   esi, 0x12345678    ; "%x" takes a 32-bit unsigned int
    lea   rdi, [rel format]
    xor   eax, eax           ; AL=0  no FP args in XMM regs
    call  printf

    ; Return from main.
    xor   eax, eax
    add   rsp, 8
    ret

GitHub upstream . GitHub上游

Then: 然后:

nasm -f elf64 -o main.o main.asm
gcc -no-pie -o main.out main.o
./main.out

Output: 输出:

0x12345678

Notes: 笔记:

If you want hex without the C library: Printing Hexadecimal Digits with Assembly 如果您想要不带C库的十六进制使用汇编打印十六进制数字

Tested on Ubuntu 18.10, NASM 2.13.03. 在Ubuntu 18.10,NASM 2.13.03上测试。

It depends on the architecture/environment you are using. 这取决于您使用的架构/环境。

For instance, if I want to display a number on linux, the ASM code will be different from the one I would use on windows. 例如,如果我想在linux上显示一个数字,ASM代码将与我在Windows上使用的代码不同。

Edit: 编辑:

You can refer to THIS for an example of conversion. 您可以参考THIS获取转换示例。

I'm relatively new to assembly, and this obviously is not the best solution, but it's working. 我对装配比较陌生,这显然不是最好的解决方案,但它有效。 The main function is _iprint, it first checks whether the number in eax is negative, and prints a minus sign if so, than it proceeds by printing the individual numbers by calling the function _dprint for every digit. 主要功能是_iprint,它首先检查eax中的数字是否为负数,如果是,则打印减号,而不是通过为每个数字调用函数_dprint来打印单个数字。 The idea is the following, if we have 512 than it is equal to: 512 = (5 * 10 + 1) * 10 + 2 = Q * 10 + R, so we can found the last digit of a number by dividing it by 10, and getting the reminder R, but if we do it in a loop than digits will be in a reverse order, so we use the stack for pushing them, and after that when writing them to stdout they are popped out in right order. 这个想法如下,如果我们有512而不是它等于:512 =(5 * 10 + 1)* 10 + 2 = Q * 10 + R,所以我们可以通过除以它来找到数字的最后一位数10,并获得提醒R,但如果我们在循环中执行它而不是数字将以相反的顺序,所以我们使用堆栈来推送它们,然后在将它们写入stdout时它们以正确的顺序弹出。

; 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      ; край

Because you didn't say about number representation I wrote the following code for unsigned number with any base(of course not too big), so you could use it: 因为你没有说过数字表示,我用任何基数(当然不是太大)写了下面的无符号数代码,所以你可以使用它:

BITS 32
global _start

section .text
_start:

mov eax, 762002099 ; unsigned number to print
mov ebx, 36        ; base to represent the number, do not set it too big
call print

;exit
mov eax, 1
xor ebx, ebx
int 0x80

print:
mov ecx, esp
sub esp, 36   ; reserve space for the number string, for base-2 it takes 33 bytes with new line, aligned by 4 bytes it takes 36 bytes.

mov edi, 1
dec ecx
mov [ecx], byte 10

print_loop:

xor edx, edx
div ebx
cmp dl, 9     ; if reminder>9 go to use_letter
jg use_letter

add dl, '0'
jmp after_use_letter

use_letter:
add dl, 'W'   ; letters from 'a' to ... in ascii code

after_use_letter:
dec ecx
inc edi
mov [ecx],dl
test eax, eax
jnz print_loop

; system call to print, ecx is a pointer on the string
mov eax, 4    ; system call number (sys_write)
mov ebx, 1    ; file descriptor (stdout)
mov edx, edi  ; length of the string
int 0x80

add esp, 36   ; release space for the number string

ret

It's not optimised for numbers with base of power of two and doesn't use printf from libc . 它没有针对基数为2的数字进行优化,并且不使用来自libc printf

The function print outputs the number with a new line. 功能print输出一个新行的数字。 The number string is formed on stack. 数字串形成在堆栈上。 Compile by nasm. 由nasm编译。

Output: 输出:

clockz

https://github.com/tigertv/stackoverflow-answers/tree/master/8194141-how-to-print-a-number-in-assembly-nasm https://github.com/tigertv/stackoverflow-answers/tree/master/8194141-how-to-print-a-number-in-assembly-nasm

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM