2015-04-04 115 views
0

我試圖使用NASM風格的x86彙編打印出一些32位浮點數。這是我想要做的最小工作示例:使用printf在x86 nasm中打印浮點數32位

global main 
extern printf, scanf 

section .data 
    scan_format: db "%f",0 
    print_format: db "%f",0xA,0 

section .bss 
    result_num: resb 4 

section .text 
main: 
    push result_num 
    push scan_format 
    call scanf 
    add esp, 8 

    push dword [result_num] 
    push print_format 
    call printf 
    add esp, 8 
    ret 

當我運行它,我得到一些奇怪的輸出:

$ nasm -felf32 -g printf_test.asm 
$ gcc printf_test.o -o printf_test.out 
$ ./printf_test.out <<< 1234 
-0.000000 

如果我嘗試檢查,而該計劃的價值它似乎是正確的:

$ gdb ./printf_test.out 
(gdb) disassemble *main 
Dump of assembler code for function main: 
    0x08048420 <+0>:  push 0x804a028 
    0x08048425 <+5>:  push 0x804a018 
    0x0804842a <+10>: call 0x8048330 <[email protected]> 
    0x0804842f <+15>: add esp,0x8 
    0x08048432 <+18>: push DWORD PTR ds:0x804a028 
    0x08048438 <+24>: push 0x804a01b 
    0x0804843d <+29>: call 0x8048320 <[email protected]> 
    0x08048442 <+34>: add esp,0x8 
    0x08048445 <+37>: ret 
    0x08048446 <+38>: nop 
    0x08048447 <+39>: nop 
    0x08048448 <+40>: nop 
    0x08048449 <+41>: nop 
    0x0804844a <+42>: nop 
    0x0804844b <+43>: nop 
    0x0804844c <+44>: nop 
    0x0804844d <+45>: nop 
    0x0804844e <+46>: nop 
    0x0804844f <+47>: nop 
End of assembler dump. 
(gdb) break *main+34 
Breakpoint 1 at 0x8048442 
(gdb) r 
Starting program: /vagrant/project_03/printf_test.out 
1234 
-0.000000 

Breakpoint 1, 0x08048442 in main() 
(gdb) p /f result_num 
$1 = 1234 

我在做什麼錯在這裏?

編輯

如果我嘗試使用雙打,它甚至不會安裝,該程序:

global main 
extern printf, scanf 

section .data 
    scan_format: db "%f",0 
    print_format: db "%f",0xA,0 

section .bss 
    result_num: resb 4 
    result_num_dub: resb 8 

section .text 
main: 
    push result_num 
    push scan_format 
    call scanf 
    add esp, 8 

    fld dword [result_num] 
    fstp qword [result_num_dub] 

    push qword [result_num_dub] ;ASSEMBLER ERROR HERE 
    push print_format 
    call printf 
    add esp, 8 
    ret 

生成的輸出:

$ nasm -felf32 -g printf_test.asm 
printf_test.asm:22: error: instruction not supported in 32-bit mode 

如果我嘗試去直接從浮動堆棧到內存堆棧,我得到一個段錯誤,即使正確的東西似乎存儲在內存中。

fld dword [result_num] 
fstp qword [esp] 
push format 

call printf 

它看起來就在GDB:

(gdb) p *((double*)($esp)) 
$9 = 2.5 

但它產生的printf的調用的中間段錯誤。我不得不丟失一些東西。

+1

不''printf'希望'%f'說明符有'double'參數嗎? (請參閱「說明符」表[這裏](http://www.cplusplus.com/reference/cstdio/printf/)) – Michael 2015-04-04 08:59:40

+0

@邁克爾剛剛編輯以反映嘗試使用雙打時的錯誤 – 2015-04-04 16:13:19

+0

有關無法使用的更多詳細信息要將'float'直接傳遞給'printf',請參閱https://stackoverflow.com/questions/37082784/how-to-print-a-single-precision-float-with-printf獲取C默認參數提升規則。 – 2017-12-23 16:50:19

回答

2

正如邁克爾指出,%fprintf需要一個double,所以你的電話號碼必須轉換成一個double只是推動它的堆棧上printf前:

global main 
extern printf, scanf 

section .data 
    scan_format: db "%f",0 
    print_format: db "Result: %f",0xA,0 

section .bss 
    result_num: resb 4 

section .text 
main: 
    push result_num 
    push scan_format 
    call scanf 
    add esp, 8 

    sub esp,8 ;reserve stack for a double in stack 
    mov ebx,result_num 
    fld dword [ebx] ;load float 
    fstp qword [esp] ;store double (8087 does the conversion internally) 
    push print_format 
    call printf 
    add esp, 12 
    ret 

推四字[result_num_dub];彙編錯誤在這裏

你不能做64位操作,就像在32位模式下一次按64位一樣。這就是我使用sub esp方法保留堆棧空間的原因。你的第二個程序只是需要這個:

section .text 
main: 
    push result_num 
    push scan_format 
    call scanf 
    add esp, 8 

    fld dword [result_num] 
    fstp qword [result_num_dub] 
    push dword [result_num_dub+4] ;pushes 32 bits (MSB) 
    push dword [result_num_dub] ;pushes 32 bits (LSB) 
    push print_format 
    call printf 
    add esp, 12 ;<-- 12 bytes, not 8. 
    ret