NASM - The Netwide Assembler
NASM Forum => Example Code => Topic started by: JoeCoder on June 15, 2011, 01:31:44 PM
-
Is there a standard way to do this or does everybody roll his own?
-
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.
-
Thanks Keith.
-
; 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
-
thank you
-
There are a lot of different ways to do this. This isn't particularly "good", but it's what I usually use.
;---------------------------------
; 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
-
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...
;-------------------
; 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
-
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!
-
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
; -----------------------------------------------------------------------------
-
Yeesh... code blocks plz...
-
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 ;)
-
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