NASM - The Netwide Assembler

NASM Forum => Example Code => Topic started by: JoeCoder on June 15, 2011, 01:31:44 PM

Title: How do you convert a binary number to ASCII decimal digits?
Post by: JoeCoder on June 15, 2011, 01:31:44 PM
Is there a standard way to do this or does everybody roll his own?
Title: Re: How do you convert a binary number to ASCII decimal digits?
Post by: Keith Kanios on June 15, 2011, 03:14:48 PM
The x86 has minimal support for assisting this in the form of AAA (http://pdos.csail.mit.edu/6.828/2006/readings/i386/AAA.htm) and friends, but those are not available in long mode.

There are other ways to do this. One pure data way on the x86 would be to use a 256 byte lookup table and XLATB (http://pdos.csail.mit.edu/6.828/2006/readings/i386/XLAT.htm).

In general, it is a roll-your-own (or someone else's) situation... with emphasis on optimization (mostly eliminating inner loops) depending on how generic (e.g. printf) the conversion process needs to be.
Title: Re: How do you convert a binary number to ASCII decimal digits?
Post by: JoeCoder on June 15, 2011, 05:43:45 PM
Thanks Keith.
Title: Re: How do you convert a binary number to ASCII decimal digits?
Post by: avcaballero on June 27, 2011, 11:37:31 AM
; avch
; A simple example. Surely it could be vastly enhanced
; FASM sintax
use16
org 100h
mov bx, 10
@MainLoop:
  mov si, CRLF
  mov ax, word [Number]
  @InnerLoop:
    dec si
    xor dx, dx
    div bx
    or dl, 30h
    mov byte [si], dl
    or ax, ax
  jnz @InnerLoop
  mov dx, si
  mov ah, 9
  int 21h
  dec word [Number]
jnz @MainLoop
ret
Number dw 0FFFFh
Text rb(7)
CRLF db 13, 10, "$"
; Execute with:
; w2ascii > Numbers.txt
Title: Re: How do you convert a binary number to ASCII decimal digits?
Post by: JoeCoder on June 27, 2011, 06:08:30 PM
thank you
Title: Re: How do you convert a binary number to ASCII decimal digits?
Post by: Frank Kotler on June 28, 2011, 04:56:31 PM
There are a lot of different ways to do this. This isn't particularly "good", but it's what I usually use.

Code: [Select]
;---------------------------------
; showeaxd - print a decimal representation of eax to stdout
; for "debug purposes only"... mostly
; expects: number in eax
; returns: nothing useful
;--------------------------------------
showeaxd:
    push eax
    push ebx
    push ecx
    push edx
    push esi
   
    sub esp, 10h ; an arbitrary "round number"
    lea ecx, [esp + 12]  ; another arbitrary number
    mov ebx, 10  ; we want to divide by 10
    xor esi, esi  ; counter
    mov byte [ecx], 0 ; why do I do this? I dunno.
.top:   
    dec ecx
    xor edx, edx
    div ebx
    add dl, '0'
    mov [ecx], dl
    inc esi
    or eax, eax
    jnz .top
   
    mov edx, esi
    mov ebx, 1
    mov eax, 4
    int 80h
   
    add esp, 10h

    pop esi
    pop edx
    pop ecx
    pop ebx
    pop eax

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

I'll be posting other methods, as the spirit moves me. I have on my drive a "fast" method written by Terje Mathison. Depends on multiplying by the reciprocal instead of the "div" (which is very slow). It is in the form of an "inline asm" C function. Needs a little "tweaking" before it's ready to post... and I really ought to figure out how it works! Not right now...

Best,
Frank

Title: Re: How do you convert a binary number to ASCII decimal digits?
Post by: Frank Kotler on June 28, 2011, 05:09:42 PM
This is not strictly "on topic" for this thread, as it "goes the other way" - from ascii digits to a number. Happened to have it on hand...

Code: [Select]
;-------------------
; atoi - converts string to (unsigned!) integer
; expects: buffer in edx
; returns: number in eax - carry-flag set
; if a non-digit (except 0 and linefeed) was encountered
atoi:
    push ecx
    push edx
    xor eax, eax        ; clear "result"
.top:
    movzx ecx, byte [edx]
    inc edx

    cmp ecx, byte 0
    jz .done
    cmp ecx, byte 10
    jz .done
   
    cmp ecx, byte '0'
    jb .invalid
    cmp ecx, byte '9'
    ja .invalid
   
    ; we have a valid character - multiply
    ; result-so-far by 10, subtract '0'
    ; from the character to convert it to
    ; a number, and add it to result.
   
    lea eax, [eax + eax * 4]
    lea eax, [eax * 2 + ecx - '0']

    jmp short .top
.invalid:
    stc
.done:
    pop edx
    pop ecx
    ret
;--------------

This should be called "atou", not "atoi". Change it if you care.

Best,
Frank

Title: Re: How do you convert a binary number to ASCII decimal digits?
Post by: JoeCoder on June 30, 2011, 08:52:44 AM
Thanks Frank and everybody else. I'm in the middle of a bunch of stuff and haven't had time to get back to x86 for awhile. I hope I can soon. I was looking at AAA as I believe Keith suggested. I will look over your examples when I can. thanks to everybody for the help. Nice group of people!
Title: Re: How do you convert a binary number to ASCII decimal digits?
Post by: ReturnInfinity on July 15, 2011, 04:58:43 PM
I use the following in 64-bit mode:

; -----------------------------------------------------------------------------
; os_int_to_string -- Convert a binary interger into an string
;  IN:   RAX = binary integer
;   RDI = location to store string
; OUT:   RDI = points to end of string
;   All other registers preserved
; Min return value is 0 and max return value is 18446744073709551615 so your
; string needs to be able to store at least 21 characters (20 for the digits
; and 1 for the string terminator).
; Adapted from http://www.cs.usfca.edu/~cruse/cs210s09/rax2uint.s
os_int_to_string:
   push rdx
   push rcx
   push rbx
   push rax

   mov rbx, 10               ; base of the decimal system
   xor ecx, ecx               ; number of digits generated
os_int_to_string_next_divide:
   xor edx, edx               ; RAX extended to (RDX,RAX)
   div rbx                  ; divide by the number-base
   push rdx               ; save remainder on the stack
   inc rcx                  ; and count this remainder
   cmp rax, 0               ; was the quotient zero?
   jne os_int_to_string_next_divide      ; no, do another division

os_int_to_string_next_digit:
   pop rax                  ; else pop recent remainder
   add al, '0'               ; and convert to a numeral
   stosb                  ; store to memory-buffer
   loop os_int_to_string_next_digit      ; again for other remainders
   xor al, al
   stosb                  ; Store the null terminator at the end of the string

   pop rax
   pop rbx
   pop rcx
   pop rdx
   ret
; -----------------------------------------------------------------------------


; -----------------------------------------------------------------------------
; os_string_to_int -- Convert a string into a binary interger
;  IN:   RSI = location of string
; OUT:   RAX = integer value
;   All other registers preserved
; Adapted from http://www.cs.usfca.edu/~cruse/cs210s09/uint2rax.s
os_string_to_int:
   push rsi
   push rdx
   push rcx
   push rbx

   xor eax, eax         ; initialize accumulator
   mov rbx, 10         ; decimal-system's radix
os_string_to_int_next_digit:
   mov cl, [rsi]         ; fetch next character
   cmp cl, '0'         ; char preceeds '0'?
   jb os_string_to_int_invalid   ; yes, not a numeral
   cmp cl, '9'         ; char follows '9'?
   ja os_string_to_int_invalid   ; yes, not a numeral
   mul rbx            ; ten times prior sum
   and rcx, 0x0F         ; convert char to int
   add rax, rcx         ; add to prior total
   inc rsi            ; advance source index
   jmp os_string_to_int_next_digit   ; and check another char
   
os_string_to_int_invalid:
   pop rbx
   pop rcx
   pop rdx
   pop rsi
   ret
; -----------------------------------------------------------------------------
Title: Re: How do you convert a binary number to ASCII decimal digits?
Post by: Keith Kanios on July 15, 2011, 06:43:08 PM
Yeesh... code blocks plz...
Title: Re: How do you convert a binary number to ASCII decimal digits?
Post by: brethren on July 16, 2011, 07:35:48 PM
i posted a really fast signed dword to ascii routine
http://forum.nasm.us/index.php?topic=1042.0

challenge: just for kicks can anyone make a faster version ;)
Title: Re: How do you convert a binary number to ASCII decimal digits?
Post by: avcaballero on August 03, 2011, 06:39:04 AM
A venerable mathematician said: "If I cannot draw it, it is that I do not understand it". Okay, life is too short to hook your routines in an example ;), although with no doubt that it is faster than mine. I let you an example in MS-DOS that prints 65535*2 signed numbers in 2.56 seconds inside DosBOX.

Greetings from Madrid