Linux 如何在程序集 NASM 中打印一个数字?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8194141/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 02:23:06  来源:igfitidea点击:

How to print a number in assembly NASM?

linuxassemblyx86nasm

提问by AR89

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".

我已经知道如何打印诸如“hello, world”之类的字符串。

I'm developing on Linux.

我正在 Linux 上开发。

采纳答案by Martin

If you're already on Linux, there's no need to do the conversion yourself. Just use printfinstead:

如果您已经在使用 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

Note that printfuses the cdecl calling conventionso we need to restore the stack pointer afterwards, i.e. add 4 bytes per parameter passed to the function.

请注意,printf使用cdecl 调用约定,因此我们需要在之后恢复堆栈指针,即为传递给函数的每个参数添加 4 个字节。

回答by AlQafir

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 THISfor an example of conversion.

你可以参考这个转换的例子。

回答by BlackBear

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.
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. Here's what I mean (soppose we want to take the 3):

所以当你有这个数字时,你必须像我展示的那样拆分它,然后将每个“部分”转换为它的 ASCII 等价物。
使用一些位魔法很容易获得四个部分,特别是右移将我们感兴趣的部分移动到前四位然后用 0xf 将结果与其他部分隔离开来。这就是我的意思(假设我们要取 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. 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).
Here it is, now we just have to code it:

现在我们有了一个数字,我们必须将它转换成它的 ASCII 值。如果数字小于或等于 9,我们可以添加 0 的 ASCII 值(0x30),如果它大于 9,我们必须使用 a 的 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: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:这是 Intel 语法,转换为 AT&T 语法并不困难,请看这里

回答by baz

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

我对组装还比较陌生,这显然不是最好的解决方案,但它确实有效。主要函数是 _iprint,它首先检查 eax 中的数字是否为负数,如果是,则打印一个减号,然后通过调用函数 _dprint 为每个数字打印单个数字。想法如下,如果我们有 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      ; край

回答by TigerTV.ru

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 printffrom libc.

它没有针对以 2 的幂为底的数字进行优化,并且不使用printffrom libc

The function printoutputs the number with a new line. The number string is formed on stack. Compile by nasm.

该函数print输出带有新行的数字。数字串在堆栈上形成。由 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