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
How to print a number in assembly NASM?
提问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 printf
uses 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 printf
from libc
.
它没有针对以 2 的幂为底的数字进行优化,并且不使用printf
from libc
。
The function print
outputs the number with a new line. The number string is formed on stack. Compile by nasm.
该函数print
输出带有新行的数字。数字串在堆栈上形成。由 nasm 编译。
Output:
输出:
clockz