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
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
; 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 ; край
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
6条答案
按热度按时间i34xakig1#
如果你已经在Linux上运行了,就不需要自己进行转换了,只需要使用printf即可:
字符串
请注意,
printf
使用cdecl调用约定,因此我们需要在之后恢复堆栈指针,即为传递给函数的每个参数添加4个字节。8qgya5xd2#
你必须把它转换成一个字符串;如果你谈论的是十六进制数字,这很容易。任何数字都可以这样表示:
字符串
所以当你有这个数字时,你必须像我展示的那样分割它,然后将每个“部分”转换为它的ASCII等价物。
通过一些位魔术可以很容易地得到这四个部分,特别是通过右移将我们感兴趣的部分移到前四位,然后用0xf与结果进行AND以将其与其余部分隔离开来。这就是我的意思(我们想要取3):
型
现在我们有了一个数字,我们必须把它转换成它的ASCII值。如果数字小于或等于9,我们可以只添加0的ASCII值(0x 30),如果它大于9,我们必须使用a的ASCII值(0x 61)。
在这里,现在我们只需要编码它:
型
**PS:**我知道这是16位代码,但我仍然使用旧的TASM:P
**PPS:**这是Intel语法,转换为AT&T语法并不难,看here。
kokeuurv3#
采用
printf
的Linux x86-64main.asm
字符串
GitHub upstream的一个。
然后又道:
型
输出量:
型
备注:
sub rsp, 8
:How to write assembly language hello world program for 64 bit Mac OS X using printf?的一个或多个字符xor eax, eax
:Why is %eax zeroed before a call to printf?的一个或多个字符-no-pie
:plaincall printf
在PIE可执行文件(-pie
)中不起作用,链接器只会自动为旧式可执行文件生成PLT存根。您的选择是:call printf wrt ..plt
通过PLT调用,与传统的call printf
一样call [rel printf wrt ..got]
完全不使用PLT,如gcc -fno-plt
。就像GAS的语法
call *printf@GOTPCREL(%rip)
一样。这两种方法在非PIE可执行文件中也是适用的,除非静态链接libc,否则不会导致任何效率低下。在这种情况下,
call printf
可以解析为直接指向libc的call rel32
,因为从代码到libc函数的偏移量在静态链接时是已知的。另请参阅:Can't call C standard library function on 64-bit Linux from assembly (yasm) code
如果你想在没有C库的情况下使用十六进制:使用汇编打印十六进制数字
在Ubuntu 18.10和NASM 2.13.03上进行了测试。
twh00eeo4#
这取决于您使用的架构/环境。
例如,如果我想在Linux上显示一个数字,ASM代码将与我在Windows上使用的代码不同。
编辑:
您可以参考THIS以获取转换示例。
inn6fuwd5#
我是一个相对较新的汇编,这显然不是最好的解决方案,但它的工作。主函数是_iprint,它首先检查eax中的数字是否为负数,如果是,则打印一个负号,然后通过调用函数_dprint为每个数字打印各个数字。其思想如下,如果我们有512,则它等于:512 =(5 * 10 + 1)* 10 + 2 = Q * 10 + R,所以我们可以通过将其除以10来找到数字的最后一位,并得到提醒R,但是如果我们在循环中进行,那么数字将以相反的顺序进行,所以我们使用堆栈来推送它们,之后当将它们写入标准输出时,它们将以正确的顺序弹出。
字符串
r55awzrz6#
因为你没有提到数字的表示,所以我写了下面的代码来表示任何基数的无符号数(当然不能太大),所以你可以使用它:
字符串
它没有针对基数为2的幂的数字进行优化,也没有使用
libc
中的printf
。函数
print
以新行输出数字。数字字符串在堆栈上形成。通过nasm编译。输出量:
型
https://github.com/tigertv/stackoverflow-answers/tree/master/8194141-how-to-print-a-number-in-assembly-nasm