在nasm中打印浮点数而不绑定到C函数

时间:2022-11-08 02:52:39

I'm wondering, how to print float numbers in nasm using only syscalls in linux. I have the following code, but it prints only @

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

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

Who to make it right?

谁做对了?

2 个解决方案

#1


You can use the FPU to convert a float into a writeable string. The following example takes PI (a number with quite a few digits) and

您可以使用FPU将float转换为可写字符串。以下示例采用PI(具有相当多位数的数字)和

  • separates the float into an integral and a fractional part,
  • 将浮子分成一个整体和一个分数部分,

  • converts the integral part into a BCD number by using FBSTP,
  • 使用FBSTP将积分部分转换为BCD编号,

  • converts the BCD number into an ASCII string,
  • 将BCD编号转换为ASCII字符串,

  • appends the fractional part as ASCII string by repeatedly multiplying by 10 and converting the produced integral part,
  • 通过重复乘以10并转换生成的整数部分,将小数部分作为ASCII字符串附加,

  • writes the string with kernel function 4 (sys-write).
  • 用内核函数4(sys-write)写入字符串。

Converting the integral part into a BCD number doesn't cover the whole range of a float. Also, it is desirable to stop converting the fractional part after a certain amount of steps. And there is no error check.

将积分部分转换为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

You will see that the number quickly lose precision. This is a special peculiarity of IEEE-754 coded floating point numbers.

您将看到该数字很快就会失去精确度。这是IEEE-754编码浮点数的特殊特性。

#2


There is no syscall to print floats directly. Convert it to a string yourself then call write.

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

#1


You can use the FPU to convert a float into a writeable string. The following example takes PI (a number with quite a few digits) and

您可以使用FPU将float转换为可写字符串。以下示例采用PI(具有相当多位数的数字)和

  • separates the float into an integral and a fractional part,
  • 将浮子分成一个整体和一个分数部分,

  • converts the integral part into a BCD number by using FBSTP,
  • 使用FBSTP将积分部分转换为BCD编号,

  • converts the BCD number into an ASCII string,
  • 将BCD编号转换为ASCII字符串,

  • appends the fractional part as ASCII string by repeatedly multiplying by 10 and converting the produced integral part,
  • 通过重复乘以10并转换生成的整数部分,将小数部分作为ASCII字符串附加,

  • writes the string with kernel function 4 (sys-write).
  • 用内核函数4(sys-write)写入字符串。

Converting the integral part into a BCD number doesn't cover the whole range of a float. Also, it is desirable to stop converting the fractional part after a certain amount of steps. And there is no error check.

将积分部分转换为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

You will see that the number quickly lose precision. This is a special peculiarity of IEEE-754 coded floating point numbers.

您将看到该数字很快就会失去精确度。这是IEEE-754编码浮点数的特殊特性。

#2


There is no syscall to print floats directly. Convert it to a string yourself then call write.

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