Author Topic: FTOA example  (Read 5168 times)

Offline avcaballero

  • Full Member
  • **
  • Posts: 130
  • Country: es
    • Abre los Ojos al Ensamblador
FTOA example
« on: February 10, 2012, 12:32:33 PM »
Hello, from time to time always someone ask for any FTOA example, here it is my version.

Code: [Select]
; *************************************************************
; *  Print a real number on the screen                        *
; *  (c) Alfonso Víctor Caballero Hurtado                     *
; *  http://www.abreojosensamblador.net/                      *
; *  You can use it but, credit ;), don't be a lamer          *
; *************************************************************

; 1.005
; 0 01111111 00000001010001111010111b = 3F 80 A3 D7h

; 0.0005
; 0 01110100 00000110001001001101111b = 3A 03 12 6Fh
; 2^(116-127) * 1.00000110001001001101111

; 1 10000111 00110001101000000000000b = C3 98 D0 00h
; -1 * 2^(135-127) * 1.00110001101 = -100110001.101 = -305.625


[org 100h]
[section .text]
mov    eax, [vdNumero]

; Recogemos el signo (1 bit: extremo izquierda)
mov    byte [rbSigno], 0  ; Suponemos sin signo
shl    eax, 1
jnc    @Sgte1
  mov    byte [rbSigno], 1
@Sgte1:

; Recuperamos el exponente (8 bits siguientes)
mov    ebx, eax
shr    ebx, 23+1
sub    ebx, 127
mov    word [rwExpon], bx

xor    cx, cx       ; Suponemos exponente positivo
or     bx, bx
jns    @ExpPositivo
  mov    cx, bx
  neg    cx
  mov    word [rwExpon], 0
@ExpPositivo:

; Recuperamos la mantisa
shl    eax, 7      ; Eliminamos el exponente
or     eax, 10000000000000000000000000000000b  ; Ponemos el 1.
shr    eax, 9      ; Lo ponemos su sitio, ya sin exponente
call   CheckExpNeg
mov    ecx, 0
@Sin_Exp:          ; Eliminamos los ceros a la derecha
  inc    ecx
  mov    ebx, eax
  shr    eax, 1
jnc    @Sin_Exp
mov    eax, ebx
mov    dword [vdLonMantisa], 23
sub    dword [vdLonMantisa], ecx

; Recogemos la parte entera
mov    ebx, eax                 ; eax ya tiene el 1. y no tiene ceros a la derecha
mov    cx, word [vdLonMantisa]
sub    cx, word [rwExpon]
and    ecx, 0FFFFh              ; Eliminamos la parte de arriba, por si acaso
mov    dword [vdLonDecimal], ecx
shr    ebx, cl                  ; Eliminamos decimales, sólo queda parte entera
mov    word [rwEntera], bx      ; Parte entera

; Recogemos la parte decimal
mov    ebx, eax                 ; eax ya tiene el 1. y no tiene ceros a la derecha
mov    cx, 32   ; 16
sub    cx, word [vdLonDecimal]
shl    ebx, cl                  ; Eliminamos la parte entera
shr    ebx, cl                  ; Ponemos parte decimal contra borde derecho

; ** Aquí ya hemos desmenuzado el número real,
; ** ahora hay que convertirlo a ascii
; Parte decimal
call   InitSum
mov    ecx, dword [vdLonDecimal] ; Contador de posiciones
@B_Decimal:
  shr    ebx, 1
  jnc    @B_Sgte1
    call   Divide               ; Resultado en vmBufDivision
    call   Suma                 ; Resultado en vdSumaFinal
  @B_Sgte1:
  dec    ecx
jnz    @B_Decimal
call   Sum2ASCII                ; Convierte la suma en ASCII
call   CopiaSumaMatFinal        ; Copiamos la suma en la matriz final

; Separador
mov    byte [di], '.'
dec    di

; Parte entera
mov    ax, [rwEntera]
call   Bin2ASCII

; Signo
cmp    byte [rbSigno], 1
jnz    @Fin
  mov    byte [di], '-'
@Fin:

; Imprimimos el número
mov    ah, 9
mov    dx, di
int    21h

; Salimos al DOS
ret

CheckExpNeg:
  ; Propósito: Chequeamos si el exponente fuera negativo para acomodar mantisa
  ; Entrada  : eax: Número, cx: Exponente (0 si positivo, otro caso negativo)
  ;            eax ya tiene el 1 a la izquierda y sin exponente
  ; Salida   : eax
  ; Destruye : eax
  or     cx, cx
  jz     @cen_Fin
    shr    eax, cl
  @cen_Fin:
ret

BaseDecimal:
  ; Propósito: devuelve la base para los decimales
  ; Entrada  : eax
  ; Salida   : edx: la base
  ; Destruye : Ninguna
  push   eax
  push   ebx
  push   esi
  mov    ebx, 10
  @b_bucle:
    mul    ebx
    dec    esi
  jnz    @b_bucle
  mov    edx, eax
  pop    esi
  pop    ebx
  pop    eax
ret

Bin2ASCII:
  ; Propósito: convierte binario a ascii y lo va metiendo en di
  ; Entrada  : eax: num a convertir, di: donde empieza a meter en el búfer
  ; Salida   : di donde ha terminado de meter en el búfer
  ; Destruye : eax, edi
  push   ebx
  push   edx
  mov    ebx, 10
  @Bucle1:
    cmp    eax, 0
    jz     @Salir
    xor    edx, edx
    div    ebx
    or     dl, 30h
    mov    [di], dl
    dec    di
  jmp    @Bucle1
  @Salir:
  pop    edx
  pop    ebx
ret

Sum2ASCII:
  ; Propósito: Inicializa con ceros el búfer de la suma (8 bytes)
  ; Entrada  : Ninguna
  ; Salida   : Ninguna
  ; Destruye : eax
  or      dword [vdSumaFinal], 30303030h
  or      dword [vdSumaFinal+4], 30303030h
ret

CopiaSumaMatFinal:
  ; Propósito: Copia el búfer de la suma en rbNumASCII
  ; Entrada  : Ninguna
  ; Salida   : edi
  ; Destruye : esi, edi
  std                             ; Decrementamos
  mov    esi, vdSumaFinal + 7
  mov    edi, rbNumASCII + 19
  mov    ecx, 7
  rep    movsb
ret

InitSum:
  ; Propósito: Inicializa con ceros el búfer de la suma (8 bytes)
  ; Entrada  : Ninguna
  ; Salida   : Ninguna
  ; Destruye : eax
  push    ecx
  push    edi
  mov     eax, 0
  mov     ecx, 2
  mov     edi, vdSumaFinal
  rep     stosd
  pop     edi
  pop     ecx
ret

Suma:
  ; Propósito: Suma en bytes vmBufDivision con vdSumaFinal y lo deja aquí
  ; Entrada  : Ninguna
  ; Salida   : vdSumaFinal
  ; Destruye : eax
  push    ebx
  push    ecx
  push    edx
  push    esi
  push    edi
  mov     esi, vmBufDivision+7
  mov     edi, vdSumaFinal+7
  mov     ecx, 8
  mov     dl, 0
  .Bucle:
    mov     al, byte [esi]
    mov     ah, byte [edi]
    add     al, ah
    add     al, dl
    mov     dl, 0
    cmp     al, 10
    jb      .Next
      mov     dl, 1
      sub     al, 10
    .Next:
    mov     byte [edi], al
    dec     edi
    dec     esi
  loop    .Bucle
  pop     edi
  pop     esi
  pop     edx
  pop     ecx
  pop     ebx
ret

Divide:
  ; Propósito: Divide 10000000 por 2^cl y lo trocea en bytes
  ; Entrada  : ecx: exponente de 2
  ; Salida   : vmBufDivision
  ; Destruye : eax
  push  ebx
  push  ecx
  push  edx
  push  edi
  ; Limpiamos el búfer
  push  ecx
  mov   edi, vmBufDivision
  mov   eax, 0
  mov   ecx, 8/4
  rep   stosd
  pop   ecx
  ;
  mov   eax, 10000000     ; Esto es lo que dividiremos
  ; mov   ecx, 3            ; Parámetro que indicará por cuánto dividimos
  shr   eax, cl           ; Hademos la división por 2^cl
  ; Ahora troceamos la división en bytes
  mov   edi, vmBufDivision+7 ; Dirección donde empezamos a escribir
  mov   ebx, 10           ; Establecemos el divisor
  .Bucle:
    cmp   eax, 0          ; ¿Hemos terminado?
    jz    .Fin
    xor   edx, edx        ; Limpiamos de nuevo la parte superior
    div   ebx             ; Divisimos -> cociente en edx
    mov   byte [edi], dl  ; Guardamos el byte
    dec   edi             ; Actualizamos puntero
  jmp   .Bucle
  .Fin:
  pop   edi
  pop   edx
  pop   ecx
  pop   ebx
ret

[section .data]
; vdNumero      dd 305.625
vdNumero      dd    0.0005
vdAcumDec     dd    0
rbNumASCII:   times 20 db ' '
              db    "$"

[section .bss]
vdSumaFinal   resb    8
vmBufDivision resb    8
vdLonMantisa  resd    1
vdLonDecimal  resd    1
rwEntera      resw    1
rdDecimal     resw    1
rwExpon       resw    1
rbSigno       resb    1

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2437
  • Country: us
Re: FTOA example
« Reply #1 on: February 11, 2012, 05:19:45 AM »
Nice! I've got a half-baked "ftoa" (and an "atof") that I've used (to file federal employment taxes, even!), but I think I like yours better. Mine depends on "fbstp" and "fbld", yours is pure bit-bashing.

I think I see something that might become a problem... if we were to go on and try to convert a second number, for example...

Code: [Select]
CopiaSumaMatFinal:
  ; Propósito: Copia el búfer de la suma en rbNumASCII
  ; Entrada  : Ninguna
  ; Salida   : edi
  ; Destruye : esi, edi
  std                             ; Decrementamos
  mov    esi, vdSumaFinal + 7
  mov    edi, rbNumASCII + 19
  mov    ecx, 7
  rep    movsb
ret

You set the direction flag "down"... and leave it there. I think it might be an "improvement" to add a "cld" after the "rep movsb", just before the "ret". What do you think?

I'll see if I can dig up an example of mine to post, just for comparison, but I think I like this better.

Best,
Frank


Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2437
  • Country: us
Re: FTOA example
« Reply #2 on: February 11, 2012, 05:51:16 AM »
Here we go... This might be useful to discuss "How cold IS it?". After an early snowstorm at the end of October, it's been pretty mild here this winter. I understand it's been "cold as blazes" in Europe!

Code: [Select]
;-----------------------------------------------
; converts centigrade to fahrenheit, & visa versa
;
; nasm -f bin -o myfile.com myfile.asm
;--------------------------------------

org 100h

BUFSIZ equ 8

section .data

    prompt0 db 13, 10, 7, 'Pay Attention!!!', 13, 10, '$'
    prompt1 db 13, 10, 'This humble program will convert', 13, 10
            db 'degrees centigrade to degrees Fahrenheit,', 13, 10
            db 'or Fahrenheit to centigrade (Celsius).', 13, 10
            db 'Please enter "C" or "F" to indicate the', 13, 10
            db 'temperature units in which you will INPUT', 13, 10
            db 'the value to be converted.', 13, 10, 10  , '$'
    promptc db 'Please enter degrees C to convert to F:', 13, 10, '$'
    promptf db 'Please enter degrees F to convert to C:', 13, 10, '$'
    that    db 13, 10, "That's $"
    degrees db ' degrees $'
    bye     db 13, 10, 10, "Thanks for playing!", 13, 10, '$'


    word_10 dw 10   ; multiplier for the float-to-ascii routine

                    ; for C->F, F->C routines
    float_32 dq 32.0
    float_5  dq 5.0
    float_9  dq 9.0

section .bss

    buffer resb BUFSIZ + 2     ; buffer for our input string
    result_units resb 1        ; holds 'C' or 'F'
    numbuf resb 20h            ; buffer for ftoa
    bcdbuf rest 1              ; 10 byte scratch area for ftoa
;--------------------

section .text

; cheap-a** cls - reset text mode
    mov ax, 3
    int 10h

instruct:
; tell 'em what we're gonna do, and ask for input
    mov dx, prompt1
    mov ah, 9
    int 21h

; get input - one key, no echo
    mov ah, 8
    int 21h

; force uppercase
    and al, 0DFh

; did they get it right?
    cmp al, 'C'
    je do_c2f

    cmp al, 'F'
    je do_f2c

; smack their a**, and make 'em do it over
    mov dx, prompt0
    mov ah, 9
    int 21h
    jmp short instruct

; store the letter we'll display at the end
; we also use it as a "flag" to decide f->c or c->f
; and set up appropriate prompt
do_c2f:
    mov byte [result_units], 'F'
    mov dx, promptc
    jmp short both
do_f2c:
    mov byte [result_units], 'C'
    mov dx, promptf

both:
; display the prompt, "C" or "F", asking for input
    mov ah, 9
    int 21h

; get string input, set "max size" first
    mov byte [buffer], BUFSIZ
    mov ax, 0C0Ah
    mov dx, buffer
    int 21h

; since atof expects a zero-terminated string, do so.
    mov bl, [buffer + 1]
    xor bh, bh
    mov byte [buffer + 2 + bx], 0

; convert the string to a float in st(0)
; remember that input text starts at 2 bytes into the buffer
    mov si, buffer + 2
    call atof
    jnc good_num

; if they gave us trash, give 'em trash back
; and make 'em do it over
    mov dx, prompt0
    int 21h
    mov dx, promptc
    cmp byte [result_units], 'F'
    je both
    mov dx, promptf
    jmp short both

good_num:
; okay, they did right, do the calculation they want
    cmp byte [result_units], 'C'
    jne calc_c2f

    fsub qword [float_32]
    fmul qword [float_5]
    fdiv qword [float_9]
    jmp short display

calc_c2f:
    fmul qword [float_9]
    fdiv qword [float_5]
    fadd qword [float_32]
; Whew! That was tough!

display:
; convert st(0) to ascii string at di - cx decimal places
    mov cx, 2
    mov di, numbuf
    call ftoa

; ftoa returns a zero-terminated string,
; fix it for int 21h/9 - '$'-terminate
    dec di
reterminate:
    cmp byte [di + 1], 1
    inc di
    jnc reterminate
    mov byte [di], '$'

; tell 'em what we're going to do
    mov ah, 9
    mov dx, that
    int 21h

 ; tell 'em the number
    mov dx, numbuf
    int 21h

; and the units
    mov dx, degrees
    int 21h

    mov ah, 2
    mov dl, [result_units]
    int 21h

; say Buhbye - enhancement - go again?
    mov ah, 9
    mov dx, bye
    int 21h

exit:
    mov ah,4Ch
    int 21h

;------------------------------------------------
; atof - converts ascii string to float
; expects: si pointed to zero-terminated string
; returns: float in st(0)
;          carry set if invalid digit encountered
;-------------------------------------------------
atof:
    push ax
    push bx
    push cx
    push dx
    push di
    push si

    xor bx, bx

    xor cx, cx
    dec cx                    ; set cx FFFF - no point found

    xor dx, dx
    mov byte [bcdbuf + 9], 0  ; assume positive

    mov di, si                ; save our "beginning of buffer"

.af0:
    mov al, [si]
    inc si
    cmp al, '-'
    jnz .notneg
    mov byte [bcdbuf + 9], 80h
    jmp short .af0
.notneg:
    cmp al, '.'
    jnz .notpt
    inc cx
    jmp short .af0
.notpt:
    cmp al, ' '
    jz .af0
    cmp cx, 0FFFFh
    jz .af05
    inc cx
.af05
    or al, al        ; end of string?
    jnz .af0
    dec si
    dec si
    cmp cx, 0FFFFh
    jnz .af07
    xor cx, cx
    jmp short .af1
.af07
    dec cx

.af1:
    cmp si, di
    jc .done
    mov al, [si]
    dec si
    cmp al, ' '
    jz .af1
    cmp al, '-'
    jz .af1
    cmp al, '.'
    jz .af1
    cmp al, 3Ah
    jnc .invalid
    cmp al, 30h
    jc .invalid
    and al, 0Fh
    or dh, dh
    jnz .notfirst
    or dl, al
    inc dh
    jmp short .af1
.notfirst:
    shl al, 4
    or dl, al
    mov [bcdbuf + bx], dl
    xor dx, dx
    inc bx
    jmp .af1
.invalid
    stc
    jmp short .done3

.done
    or dh, dh
    jz .padbcd
    and dl, 0Fh
    mov [bcdbuf + bx], dl
    inc bx

.padbcd
    cmp bx, byte 9
    jz .done2
    mov byte [bcdbuf + bx], 0
    inc bx
    jmp short .padbcd

.done2
    fbld [bcdbuf]
    or cx, cx
    jz .done25
.tendiv:
    fidiv word [word_10]
    loop .tendiv
.done25
    clc
.done3:

    pop si
    pop di
    pop dx
    pop cx
    pop bx
    pop ax
    ret
;--------------------------------------------


;-----------------------------------------------------
; ftoa - converts floating point to string
;
; Based on some code "sponged" from a post to clax
; From: "Jim Morrison" <astrolabe at ntplx dot net>
;
; Expects: Number to convert is on stack top - st(0)
;          di points to buffer to store string
;          cx = Decimal Places.
;-------------------------------------------------------------

ftoa:
    push ax
    push cx
    push dx
    push di
    push si

    mov dx, cx           ; save a copy of dec places
                         ; if no decimal (integer)
    jcxz .f2a2           ; skip multiply by ten loop
.f2a1:                   ; else loop to "scale" number
    fimul word [word_10]
    loop .f2a1
.f2a2:
    fbstp [bcdbuf]       ; convert to bcd and store
    mov si, bcdbuf       ; we'll pull digits from there
    mov cx, 9
.f2a3:
    lodsb                ; get a pair of digits
    mov ah, al           ; move a copy to ah
    shr ah, 4            ; shift out low nibble, keeping high
    and ax, 0F0Fh        ; mask out the digits we want
    add ax, 3030h        ; convert 'em both to ascii
    push ax
    mov al, ah           ; swap and store the other digit
    push ax
    loop .f2a3           ; until done

    cmp byte [bcdbuf+9], 0 ; sign flag at bcdbuf + 9 ?
    je .f2a6
    mov al, '-'           ; minus sign if we need it
    stosb                 ; store it at front of our string
.f2a6:

    mov cx, 18
    xor dh, dh
    inc dl
.poploop
    pop ax
    cmp al, '0'
    jnz .store
    or dh, dh
    jnz .store
    jmp short .nostore
.store
    stosb
    inc dh
.nostore
    cmp cl, dl
    jne .nopoint
    or dh, dh       ; if we haven't encountered a non-zero
    jnz .nolz
    mov al, '0'     ; put a zero before the decimal point
    stosb
.nolz
    mov al, '.'           ; decimal point
    stosb                 ; and store it
    inc dh
.nopoint
    loop .poploop

    mov al, 0
    stosb

    pop si
    pop di
    pop dx
    pop cx
    pop ax

    ret
;--------------------------------------------------

These routines don't check for overflow, underflow, denormals, NANs, etc. They are known to return garbage if the number exceeds certain limits. I try to avoid this in this example by limiting the number of characters that can be entered. :)

I have a "Linux version", too, which I could post if anyone's interested. Note that this one works on "double precision" floats (qwords not dwords). Although it's been "tested", no guarantee that it gives a correct answer!

Best,
Frank


Offline avcaballero

  • Full Member
  • **
  • Posts: 130
  • Country: es
    • Abre los Ojos al Ensamblador
Re: FTOA example
« Reply #3 on: February 13, 2012, 08:38:06 AM »
Hello, Frank, sorry for answer late, but I'm ussually offline on weekends... I did it as an example, so, it is posible that it need some modifications in order to work well as an subroutine, plz let me take it a look

Offline avcaballero

  • Full Member
  • **
  • Posts: 130
  • Country: es
    • Abre los Ojos al Ensamblador
Re: FTOA example
« Reply #4 on: February 14, 2012, 09:26:22 AM »
Yes, you were right, as usual, but I think is better use cld/std before it is needed, also pushf/popf to avoid problems...

Comments in spanish, sorry.

Code: [Select]
; *************************************************************
; *  Print a real number on the screen                        *
; *  (c) Alfonso Víctor Caballero Hurtado                     *
; *  http://www.abreojosensamblador.net/                      *
; *  You can use it but, credit ;), don't be a lamer          *
; *************************************************************

; 1.005
; 0 01111111 00000001010001111010111b = 3F 80 A3 D7h

; 0.0005
; 0 01110100 00000110001001001101111b = 3A 03 12 6Fh
; 2^(116-127) * 1.00000110001001001101111

; 1 10000111 00110001101000000000000b = C3 98 D0 00h
; -1 * 2^(135-127) * 1.00110001101 = -100110001.101 = -305.625

cbFinChar    EQU   "$"
cbNFloats    EQU   5

%MACRO PrintFloat 1
  mov    ah, 9
  mov    dx, %1
  int    21h
%ENDMACRO

[org 100h]
[section .text]
  mov         ecx, cbNFloats
  mov         esi, vdNumero
  @Bucle:
    call        FTOA
    PrintFloat  di
    add         esi, 4
  loop        @Bucle

  ; Salimos al DOS
  ret

  FTOA:
    ; Propósito: Convierte float number en ASCII
    ; Entrada  : esi: número en coma flotante
    ; Salida   : di: dirección donde empezar a imprimir
    ; Destruye : Ninguna
    push   ebx
    push   ecx
    push   edx
    push   esi
    mov    eax, [esi]

    ; Recogemos el signo (1 bit: extremo izquierda)
    mov    byte [rbSigno], 0  ; Suponemos sin signo
    shl    eax, 1
    jnc    .Sgte1
      mov    byte [rbSigno], 1
    .Sgte1:

    ; Recuperamos el exponente (8 bits siguientes)
    mov    ebx, eax
    shr    ebx, 23+1
    sub    ebx, 127
    mov    word [rwExpon], bx

    xor    cx, cx       ; Suponemos exponente positivo
    or     bx, bx
    jns    .ExpPositivo
      mov    cx, bx
      neg    cx
      mov    word [rwExpon], 0
    .ExpPositivo:

    ; Recuperamos la mantisa
    shl    eax, 7      ; Eliminamos el exponente
    or     eax, 10000000000000000000000000000000b  ; Ponemos el 1.
    shr    eax, 9      ; Lo ponemos su sitio, ya sin exponente
    call   CheckExpNeg
    mov    ecx, 0
    .Sin_Exp:          ; Eliminamos los ceros a la derecha
      inc    ecx
      mov    ebx, eax
      shr    eax, 1
    jnc    .Sin_Exp
    mov    eax, ebx
    mov    dword [vdLonMantisa], 23
    sub    dword [vdLonMantisa], ecx

    ; Recogemos la parte entera
    mov    ebx, eax                 ; eax ya tiene el 1. y no tiene ceros a la derecha
    mov    cx, word [vdLonMantisa]
    sub    cx, word [rwExpon]
    and    ecx, 0FFFFh              ; Eliminamos la parte de arriba, por si acaso
    mov    dword [vdLonDecimal], ecx
    shr    ebx, cl                  ; Eliminamos decimales, sólo queda parte entera
    mov    word [rwEntera], bx      ; Parte entera

    ; Recogemos la parte decimal
    mov    ebx, eax                 ; eax ya tiene el 1. y no tiene ceros a la derecha
    mov    cx, 32   ; 16
    sub    cx, word [vdLonDecimal]
    shl    ebx, cl                  ; Eliminamos la parte entera
    shr    ebx, cl                  ; Ponemos parte decimal contra borde derecho

    ; ** Aquí ya hemos desmenuzado el número real,
    ; ** ahora hay que convertirlo a ascii
    ; Parte decimal
    call   InitSum
    mov    ecx, dword [vdLonDecimal] ; Contador de posiciones
    .B_Decimal:
      shr    ebx, 1
      jnc    .B_Sgte1
        call   Divide               ; Resultado en vmBufDivision
        call   Suma                 ; Resultado en vdSumaFinal
      .B_Sgte1:
      dec    ecx
    jnz    .B_Decimal
    call   Sum2ASCII                ; Convierte la suma en ASCII
    call   CopiaSumaMatFinal        ; Copiamos la suma en la matriz final

    ; Separador
    mov    byte [di], '.'
    dec    di

    ; Parte entera
    mov    ax, [rwEntera]
    call   Bin2ASCII

    ; Signo
    cmp    byte [rbSigno], 1
    jnz    .Fin
      mov    byte [di], '-'
    .Fin:
    pop    esi
    pop    edx
    pop    ecx
    pop    ebx
  ret

  CheckExpNeg:
    ; Propósito: Chequeamos si el exponente fuera negativo para acomodar mantisa
    ; Entrada  : eax: Número, cx: Exponente (0 si positivo, otro caso negativo)
    ;            eax ya tiene el 1 a la izquierda y sin exponente
    ; Salida   : eax
    ; Destruye : eax
    or     cx, cx
    jz     .cen_Fin
      shr    eax, cl
    .cen_Fin:
  ret

  BaseDecimal:
    ; Propósito: devuelve la base para los decimales
    ; Entrada  : eax
    ; Salida   : edx: la base
    ; Destruye : Ninguna
    push   eax
    push   ebx
    push   esi
    mov    ebx, 10
    .b_bucle:
      mul    ebx
      dec    esi
    jnz    .b_bucle
    mov    edx, eax
    pop    esi
    pop    ebx
    pop    eax
  ret

  Bin2ASCII:
    ; Propósito: convierte binario a ascii y lo va metiendo en di
    ; Entrada  : eax: num a convertir, di: donde empieza a meter en el búfer
    ; Salida   : di donde ha terminado de meter en el búfer
    ; Destruye : eax, edi
    push   ebx
    push   edx
    mov    ebx, 10
    .Bucle1:
      cmp    eax, 0
      jz     .Salir
      xor    edx, edx
      div    ebx
      or     dl, 30h
      mov    [di], dl
      dec    di
    jmp    .Bucle1
    .Salir:
    pop    edx
    pop    ebx
  ret

  Sum2ASCII:
    ; Propósito: Inicializa con ceros el búfer de la suma (8 bytes)
    ; Entrada  : Ninguna
    ; Salida   : Ninguna
    ; Destruye : eax
    or      dword [vdSumaFinal], 30303030h
    or      dword [vdSumaFinal+4], 30303030h
  ret

  CopiaSumaMatFinal:
    ; Propósito: Copia el búfer de la suma en rbNumASCII
    ; Entrada  : Ninguna
    ; Salida   : edi
    ; Destruye : esi, edi
    pushf
    mov    esi, vdSumaFinal + 7
    mov    edi, rbNumASCII + 19
    mov    ecx, 7
    std                             ; Decrementamos
    rep    movsb
    popf
  ret

  InitSum:
    ; Propósito: Inicializa con ceros el búfer de la suma (8 bytes)
    ; Entrada  : Ninguna
    ; Salida   : Ninguna
    ; Destruye : eax
    pushf
    push    ecx
    push    edi
    mov     eax, 0
    mov     ecx, 2
    mov     edi, vdSumaFinal
    cld
    rep     stosd
    pop     edi
    pop     ecx
    popf
  ret

  Suma:
    ; Propósito: Suma en bytes vmBufDivision con vdSumaFinal y lo deja aquí
    ; Entrada  : Ninguna
    ; Salida   : vdSumaFinal
    ; Destruye : eax
    push    ebx
    push    ecx
    push    edx
    push    esi
    push    edi
    mov     esi, vmBufDivision+7
    mov     edi, vdSumaFinal+7
    mov     ecx, 8
    mov     dl, 0
    .Bucle:
      mov     al, byte [esi]
      mov     ah, byte [edi]
      add     al, ah
      add     al, dl
      mov     dl, 0
      cmp     al, 10
      jb      .Next
        mov     dl, 1
        sub     al, 10
      .Next:
      mov     byte [edi], al
      dec     edi
      dec     esi
    loop    .Bucle
    pop     edi
    pop     esi
    pop     edx
    pop     ecx
    pop     ebx
  ret

  Divide:
    ; Propósito: Divide 10000000 por 2^cl y lo trocea en bytes
    ; Entrada  : ecx: exponente de 2
    ; Salida   : vmBufDivision
    ; Destruye : eax
    pushf
    push  ebx
    push  ecx
    push  edx
    push  edi
    ; Limpiamos el búfer
    push  ecx
    mov   edi, vmBufDivision
    mov   eax, 0
    mov   ecx, 8/4
    cld
    rep   stosd
    pop   ecx
    ;
    mov   eax, 10000000     ; Esto es lo que dividiremos
    ; mov   ecx, 3            ; Parámetro que indicará por cuánto dividimos
    shr   eax, cl           ; Hademos la división por 2^cl
    ; Ahora troceamos la división en bytes
    mov   edi, vmBufDivision+7 ; Dirección donde empezamos a escribir
    mov   ebx, 10           ; Establecemos el divisor
    .Bucle:
      cmp   eax, 0          ; ¿Hemos terminado?
      jz    .Fin
      xor   edx, edx        ; Limpiamos de nuevo la parte superior
      div   ebx             ; Divisimos -> cociente en edx
      mov   byte [edi], dl  ; Guardamos el byte
      dec   edi             ; Actualizamos puntero
    jmp   .Bucle
    .Fin:
    pop   edi
    pop   edx
    pop   ecx
    pop   ebx
    popf
  ret

[section .data]
  ; vdNumero      dd 305.625
  vdNumero      dd    0.0005
                dd    1.0015
                dd  100.0275
                dd  230.0175
                dd  -35.0178
               
  vdAcumDec     dd    0
  rbNumASCII:   times 20 db ' '
                db    13, 10, cbFinChar

[section .bss]
  vdSumaFinal   resb    8
  vmBufDivision resb    8
  vdLonMantisa  resd    1
  vdLonDecimal  resd    1
  rwEntera      resw    1
  rdDecimal     resw    1
  rwExpon       resw    1
  rbSigno       resb    1
 
; >realcn01
;  .0004998
;  1.0014996
;  100.0274962
;  230.0174863
; -35.0177992