在不绑定到 C 函数的情况下打印 nasm 中的浮点数

Print floats in nasm without binding to C functions

我想知道如何在 nasm 中仅使用 linux 中的系统调用来打印浮点数。我有以下代码,但它只打印 @

section .data
  num dq 2.0
  len equ $ - num

section .text
global _start
_start:
  mov edx, len
  mov ecx, num
  mov ebx, 1
  mov eax, 4
  int 80h

  mov eax, 1
  int 80h

谁做对了?

没有直接打印浮点数的系统调用。自己将其转换为字符串,然后调用 write.

您可以使用 FPU 将浮点数转换为可写字符串。下面的例子取PI(一个位数比较多的数字)和

  • 将浮点数分成整数部分和小数部分,
  • FBSTP,
  • 将整数部分转成BCD数
  • 将BCD码数转换为ASCII码字符串,
  • 通过重复乘以 10 并转换生成的整数部分,将小数部分附加为 ASCII 字符串,
  • 使用内核函数 4 (sys-write) 写入字符串。

将整数部分转换为 BCD 数字并不能覆盖浮点数的整个范围。此外,希望在一定数量的步骤后停止转换小数部分。而且没有错误检查。

global _start

section .bss
    dec_str: resb 512

section .text

double2dec:                     ; Args: ST(0): FPU-register to convert, EDI: pointer to string
%define CONTROL_WORD    word [ebp-2]
%define TEN             word [ebp-4]
%define TEMP            word [ebp-4]
%define INTEGER         qword [ebp-12]

    push ebp
    mov ebp, esp
    sub esp, 12

    ; modifying rounding mode
    fstcw CONTROL_WORD
    mov ax, CONTROL_WORD
    or ah, 0b00001100           ; Set RC=11: truncating rounding mode
    mov TEMP, ax
    fldcw TEMP                  ; Load new rounding mode

    ; Separate integer and fractional part & convert integer part into ASCII
    fst
    frndint                     ; ST(0) to integer
    fsub st1, st0               ; Integral part in ST(0), fractional part in ST(1)
    call fpu2bcd2dec
    fabs                        ; Make fractional positive (not guaranteed by fsub)

    mov byte [edi], '.'         ; Decimal point
    add edi, 1

    ; Move 10 to st(1)
    mov TEN, 10
    fild TEN
    fxch

    ; isolate digits of fractional part and store ASCII
    .get_fractional:
    fmul st0, st1               ; Multiply by 10 (shift one decimal digit into integer part)
    fist word TEMP              ; Store digit
    fisub word TEMP             ; Clear integer part
    mov al, byte TEMP           ; Load digit
    or al, 0x30                 ; Convert digit to ASCII
    mov byte [edi], al          ; Append it to string
    add edi, 1                  ; Increment pointer to string
    fxam                        ; ST0 == 0.0?
    fstsw ax
    sahf
    jnz .get_fractional         ; No: once more
    mov byte [edi], 0           ; Null-termination for ASCIIZ

    ; clean up FPU
    ffree st0                   ; Empty ST(0)
    ffree st1                   ; Empty ST(1)
    fldcw CONTROL_WORD          ; Restore old rounding mode

    leave
    ret                             ; Return: EDI points to the null-termination of the string

fpu2bcd2dec:                    ; Args: ST(0): FPU-register to convert, EDI: target string

    push ebp
    mov ebp, esp
    sub esp, 10                 ; 10 bytes for local tbyte variable

    fbstp [ebp-10]

    mov ecx, 10                 ; Loop counter
    lea esi, [ebp - 1]          ; bcd + 9 (last byte)
    xor bl, bl                  ; Checker for leading zeros

    ; Handle sign
    btr word [ebp-2], 15        ; Move sign bit into carry flag and clear it
    jnc .L1                     ; Negative?
    mov byte [edi], '-'         ; Yes: store a minus character
    add edi, 1

    .L1:
        mov al, byte [esi]
        mov ah, al
        shr ah, 4               ; Isolate left nibble
        or bl, ah               ; Check for leading zero
        jz .1
        or ah, 30h              ; Convert digit to ASCII
        mov [edi], ah
        add edi, 1
        .1:
        and al, 0Fh             ; Isolate right nibble
        or bl, al               ; Check for leading zero
        jz .2
        or al, 30h              ; Convert digit to ASCII
        mov [edi], al
        add edi, 1
        .2:
        sub esi, 1
        loop .L1

    test bl, bl                 ; BL remains 0 if all digits were 0
    jnz .R1                     ; Skip next line if integral part > 0
    mov byte [edi], '0'
    add edi, 1

    .R1:
    mov byte [edi], 0           ; Null-termination for ASCIIZ
    leave
    ret                         ; Return: EDI points to the null-termination of the string

_start:

    fldpi                       ; Load PI
    fchs                        ; Change sign

    mov edi, dec_str
    call double2dec

    mov eax,4                   ; Kernel function sys-out
    mov ebx,1                   ; Stdout
    mov ecx,dec_str             ; Pointer to string
    mov edx, edi                ; EDI points to the null-termination of the string
    sub edx, dec_str            ; Length of the string
    int 0x80                    ; Call kernel

    mov eax,1                   ; Kernel function sys-exit
    mov ebx,0                   ; Exit code, 0=normal
    int 0x80                    ; Call kernel

您会发现数字很快就会失去精度。这是 IEEE-754 编码浮点数的一个特殊特性。