flat assembler
Message board for the users of flat assembler.
 Home   FAQ   Search   Register 
 Profile   Log in to check your private messages   Log in 
flat assembler > Examples and Tutorials > Simple Conversion Routines (EAX to ASCII, Win32)

Goto page 1, 2  Next
Author
Thread Post new topic Reply to topic
AsmGuru62



Joined: 28 Jan 2004
Posts: 1381
Location: Toronto, Canada
Simple Conversion Routines (EAX to ASCII, Win32)
Four functions:

1. EAX into ASCII (unsigned)
2. EAX into ASCII (signed)
3. Same for unsigned padded from left by any character
4. Same for signed padded from left by spaces

All commented and easy to read.


Description:
Download
Filename: EAX2ASCII.Asm
Filesize: 4.23 KB
Downloaded: 108 Time(s)



Last edited by AsmGuru62 on 03 May 2013, 01:28; edited 1 time in total
Post 02 May 2013, 23:26
View user's profile Send private message Send e-mail Reply with quote
zhak



Joined: 12 Apr 2005
Posts: 453
Location: Belarus
Hmmm and where's EAX2ASCII.asm?
Post 02 May 2013, 23:46
View user's profile Send private message Reply with quote
AsmGuru62



Joined: 28 Jan 2004
Posts: 1381
Location: Toronto, Canada
Looks like I attached the wrong file.
Fixing it now.
Post 03 May 2013, 01:27
View user's profile Send private message Send e-mail Reply with quote
MHajduk



Joined: 30 Mar 2006
Posts: 5832
Location: Poland
AsmGuru62

I like your style of programming, it's very neat and straightforward to me. Smile

If I could change anything in your code, I would wrap the "naked" code of your procedures with standard procedure prologue end epilogue and use stack to store procedure arguments in order to obtain more universal code (but this is only a matter of convention, of course).

I wondered also if it would be simpler if the 'EAX2ASCII_UInt32' procedure could use change of direction flag DF ('std' and 'cld' instructions) to reverse the representation string instead of pushing everything to the stack. It's another approach and maybe a simpler one. Smile
Post 03 May 2013, 14:18
View user's profile Send private message Send e-mail Visit poster's website Reply with quote
AsmGuru62



Joined: 28 Jan 2004
Posts: 1381
Location: Toronto, Canada
For sure, the code can be improved.
I have no restrictions on this.

I may note, however, that since we're coding in Asm - the prologue/epilogue are kind
of HLL approach, so I pass values in registers in about 95% of cases.

But, by all means, if you can wrap it as you say -- you can do it and make another post with new file, then
anyone can use the routines in both incarnations: with/without prologue/epilogue.
It will only add to the usefullness, I think.
Post 03 May 2013, 14:44
View user's profile Send private message Send e-mail Reply with quote
MHajduk



Joined: 30 Mar 2006
Posts: 5832
Location: Poland

AsmGuru62 wrote:
I may note, however, that since we're coding in Asm - the prologue/epilogue are kind
of HLL approach, so I pass values in registers in about 95% of cases.

Yes, I know. I was talking about wrapping in case somebody would like to put your routines in a DLL, so one might expect that these procedures would be called accordingly to the 'stdcall' calling convention, hence my suggestion. Smile
Post 03 May 2013, 15:40
View user's profile Send private message Send e-mail Visit poster's website Reply with quote
uart777



Joined: 17 Jan 2012
Posts: 369
I do not recommend using this code. It is extremely unoptimized with repetitive divisions per digit in the loop, below average quality. Any experienced ASM programmer would replace i/div by constant with multiply by reciprocal. In addition, it does pusha/popa all registers in such a common routine that may be called 1,000s of times in a tight loop, totally defeating the purpose of "fastcall" (with slow execution).

Why are there no "proc", locals or parameters in Guru's code? He uses the excuse, "That's HL!", but the real reason is because Guru is a slow, lazy thinker and he's too stubborn to learn how to write macros. If he would put aside his pride for a moment and ask how to create a simple proc, class, method, etc, I would be more than willing to help.

Quote for the day: "Normal" people have a serious mental problem, a learning deficiency. That's why it takes them 2-4+ years (of school/college) to learn what a fast, dedicated learner could within 2-4 weeks of reading books.
Post 31 May 2013, 14:30
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 14584
Location: Planet Dirt

uart777 wrote:
It is extremely unoptimized ...

I think you missed the point of the topic:

Simple Conversion Routines ...

Not everything needs to be optimised, it is situation dependant.

However I do suggest that, if you want, you can post another topic with an example of "Optimised Conversion Routines ..." so that people that want that can then choose which is appropriate for them.
Post 31 May 2013, 15:04
View user's profile Send private message Visit poster's website Reply with quote
HaHaAnonymous



Joined: 02 Dec 2012
Posts: 1173
Location: Unknown
Stupid post removed.


Last edited by HaHaAnonymous on 28 Feb 2015, 20:15; edited 1 time in total
Post 31 May 2013, 15:58
View user's profile Send private message Reply with quote
uart777



Joined: 17 Jan 2012
Posts: 369

Quote:
However I do suggest that, if you want, you can post another topic with an example of "Optimised Conversion Routines ..." so that people that want that can then choose which is appropriate for them.

Ok, here are some I threw together real quick:

Code:
; Fast Text/Number Conversions: DEC/HEX/BIN

; * 100% ASM, fast-call, standalone. Alters all
;   registers, caller/function must preserve

; * Optimized: Single digits 0-9 are converted
;   fast. No division. Replaced with shifts
;   and multiply by reciprocal

;;;;;;;;;;;;;;;;;;; CONVERT.INC ;;;;;;;;;;;;;;;;;;

; convert number (EAX) to text (EDI)...

; i2t ; signed 32BIT integer to text
; u2t ; unsigned 32BIT integer to text
; h2t ; 32BIT hexadecimal number to text
; b2t ; 32BIT binary number to text

; convert text (ESI) to number (EAX)...

; t2i ; text to signed 32BIT integer
; t2u ; text to unsigned 32BIT integer
; t2h ; text to 32BIT hexadecimal number
; t2b ; text to 32BIT binary number

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

_sintodb '2147483648'0
_digitsdb '0123456789ABCDEFGHIJKLMNOPQRSTUV'

; reverse number. esi=start, edi=end/0.
; after, edi=end/0 (for s/print/f)

reverse.digits:
push edi
dec edi
@@:
 mov cl, [edi]        ; exchange edi/esi
 mov ch, [esi]
 mov [edi], ch
 mov [esi], cl
 dec edi
 inc esi
 cmp esiedi         ; until middle
jb @b
pop edi
ret

; convert signed 32BIT integer to text

i2t:
test eax80000000h   ; negative?
 jz .convert
 mov byte [edi], '-'
 inc edi
 neg eax
 cmp eax, -2147483648 ; signed minimum?
 jne .convert
 mov edx_sinto
 @@:
  mov cl, [edx]
  mov [edi], cl
  inc edx
  inc edi
  test clcl
 jnz @b
 dec edi
 ret
.convert:
call u2t
ret

; convert unsigned 32BIT number to text

u2t:
cmp eax9            ; 0-9? single digit?
 ja @f                ; convert fast and return
 add eax30h
 mov [edi], ax        ; '#', 0
 inc edi
 ret
@@:
mov esiedi          ; save start
mov ecx1999999Ah    ; ((2^32)/10)+1
@@:
 mov ebxeax         ; n/10...
 mul ecx
 mov eaxedx
 lea edx, [edx*4+edx]
 add edxedx
 sub ebxedx         ; remainder
 add bl'0'          ; *t++=c
 mov [edi], bl
 inc edi
 test eaxeax
jnz @b
mov byte [edi], 0
call reverse.digits
ret

; convert 32BIT hexadecimal number to text

h2t:
cmp eax9            ; 0-9? convert fast
 ja @f                ; and return
 add eax30h
 mov [edi], ax        ; '#', 0
 inc edi
 ret
@@:
mov esiedi          ; save start
@@:
 movzx ecxal        ; *t++=*(_hex+(n&15))
 and ecx15
 movzx ecx,\
  byte [_digits+ecx]
 mov [edi], cl
 inc edi
 shr eax4           ; n/16
jnz @b
mov byte [edi], 0
call reverse.digits
ret

; convert 32BIT binary number to text

b2t:
cmp eax1            ; 0-1? convert fast
 ja @f                ; and return
 add eax30h
 mov [edi], ax        ; '#', 0
 inc edi
 ret
@@:
mov esiedi          ; save start
@@:
 mov clal           ; *t++=(n&1)+'0'
 and cl1
 add cl'0'
 mov [edi], cl
 inc edi
 shr eax1           ; n/2
jnz @b
mov byte [edi], 0
call reverse.digits
ret

; t2i ; convert text to signed 32BIT integer

t2i:
xor ecxecx
cmp byte [esi], '-'
 jne .convert
 inc ecx            ; yes, negate
 inc esi
 mov eaxesi
 mov edx_sinto
 @@:                ; signed minimum?
  mov cl, [eax]
  mov ch, [edx]
  inc eax
  inc edx
  cmp clch        ; while *a++=*b++
  jne .convert
  or clch         ; and both nonzero
 jnz @b
 or clch          ; both must=0 at end
 jnz .convert
 mov eax,\
  -2147483648
 ret
.convert:
push ecx
call t2u
pop ecx
test ecxecx
 jz @f
 neg eax
@@:
ret

; t2u ; convert text to unsigned 32BIT integer

t2u:
xor eaxeax
@@:
 movzx ecxbyte [esi]
 inc esi
 test ecxecx
 jz @f
 lea eax, [eax+eax*4]   ; n=n*10+*t++-'0'
 lea eax, [eax+eax-'0']
 add eaxecx
jmp @b
@@:
ret

; t2h ; convert text to 32BIT hexadecimal number

t2h:
xor eaxeax
@@:
 movzx ecx,\
  byte [esi]
 inc esi
 test clcl
 jz @f
 shl eax4       ; n=n*16+c2h(*t++)
 cmp cl'9'      ; 0-9
 jle .n
 cmp cl'a'      ; A-F
  jl .a
  sub cl'a'-10  ; a-f
  jmp .next
 .a:
  sub cl'A'-10
  jmp .next
 .n:
  sub cl'0'
 .next:
  add eaxecx
jmp @b
@@:
ret

; t2b ; convert text to 32BIT binary number

t2b:
xor eaxeax
@@:
 movzx ecxbyte [esi]
 inc esi
 test ecxecx
 jz @f
 shl eax1             ; n=n*2+*t++-'0'
 lea eax, [eax+ecx-'0']
jmp @b
@@:
ret

Post 31 May 2013, 22:00
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 14584
Location: Planet Dirt
uart777: Your conversion routine is not accurate. Try it with 1073741829

If you use a constant of 0xCCCCCCCD and apply a right shift of 3 after the multiply then all 32 bit values can be converted correctly.
Post 01 Jun 2013, 03:50
View user's profile Send private message Visit poster's website Reply with quote
baldr



Joined: 19 Mar 2008
Posts: 1651

uart777 wrote:

revolution wrote:
However I do suggest that, if you want, you can post another topic with an example of "Optimised Conversion Routines ..." so that people that want that can then choose which is appropriate for them.

Ok, here are some I threw together real quick…

Perhaps you were too quick. Are you calling that code optimized?

test eax, 80000000h / jz .convert is a silly way to test sign. test eax, eax / jns .convert is faster and shorter.

Probably you don't understand two's complement representation completely. 0x8000'0000 is a -2'147'438'648 signed, and (incidentally Wink) 2'147'438'648 unsigned. You don't need to handle special case for it (as it's not special). Well, let's take it specifically… in i2t() you haven't had to use cmp eax, MIN_INT to detect it, neg eax has already set OF to indicate that (single) case. Then you do silly copy of static ASCIIZ, detecting zero terminator. movsd*2 / movsw / movsb will copy 11 bytes using only 5 bytes of code instead of 10.

Special case for eax<=9 in u2t is somewhat surprising, why not extend it to eax<=99 case? aam is a cheap trick.

Division loop in u2t() exits with eax==0, you may use mov [edi], al instead of mov byte [edi], 0 for another byte off.

The whole string reversing business seems to be unneccessary if you fill buffer backwards (you don't check its size anyway), then pull result to buffer start, if need to do so. For hex and binary representation string reversing is outright dumb.

h2t()'s prologue have the very same code as u2t() does. Why duplicate?

_digits seems to be twice as long than needed.

Duty calls, maybe I'll look more into it. Wink
Post 02 Jun 2013, 00:25
View user's profile Send private message Reply with quote
uart777



Joined: 17 Jan 2012
Posts: 369
revolution: I merely presented a different technique. My older code uses CCCCCCCDh for signed conversions.

baldr: Jealously is the only motivation you have Razz Why don't you make a program for once in your lifetime? How many decades have you been programming and yet you still have nothing to show? Where is your code? Where are your programs? They don't exist anywhere outside of your imagination.


Quote:
_digits seems to be twice as long than needed.

For base 32, triacontakaidecimal, but you don't know anything about this, dumb ass Razz


Code:
; zexadecimal = base 32, 0-9+A-V.
; standard name is: triacontakaidecimal.
; examples: 16=Gz, 31=Vz, 123456789=3LNJ8Lz,
; 2147483647=1VVVVVVz, FFFFFFFFh=3VVVVVVz   

; z2t ; 32BIT zexadecimal number to text
; t2z ; text to 32BIT zexadecimal number  

; convert 32BIT zexadecimal number to text

z2t:
cmp eax9            ; 0-9? convert fast
 ja @f                ; and return
 add eax30h
 mov [edi], ax        ; '#', 0
 inc edi
 ret
@@:
mov esiedi          ; save start
@@:
 movzx ecxal        ; *t++=*(_hex+(n&15))
 and ecx31
 movzx ecx,\
  byte [_digits+ecx]
 mov [edi], cl
 inc edi
 shr eax5           ; n/32
jnz @b
mov byte [edi], 0
call reverse.digits
ret  

; t2z ; convert text to 32BIT zexadecimal number

t2z:
xor eaxeax
@@:
 movzx ecx,\
  byte [esi]
 inc esi
 test clcl
 jz @f
 shl eax5       ; n=n*32+c2z(*t++)
 cmp cl'9'      ; 0-9
 jle .n
 cmp cl'a'      ; A-V
  jl .a
  sub cl'a'-10  ; a-v
  jmp .next
 .a:
  sub cl'A'-10
  jmp .next
 .n:
  sub cl'0'
 .next:
  add eaxecx
jmp @b
@@:
ret

Post 05 Jun 2013, 07:44
View user's profile Send private message Reply with quote
edfed



Joined: 20 Feb 2006
Posts: 4155
Location: Thank you :D
i'd prefer the hexadecimal.

or base64. if really you want to cover a long set of digits.


uart777, baldr:

Ready?
Fight!!!!!
Sad
Post 05 Jun 2013, 16:38
View user's profile Send private message Visit poster's website Reply with quote
baldr



Joined: 19 Mar 2008
Posts: 1651

edfed wrote:
Ready?
Fight!!!!!

Here you go:

Code:
        format  PE console
        include "Win32AX.Inc"

        .code
; some macros to shorten source and ensure fair play
macro __PUTS buffer*, length* {
        invoke  WriteFileSTD_OUTPUT_HANDLEbufferlengthespNULL
}
macro _PUTS buffer*, length {
  match , length \{ __PUTS buffersizeof.\#buffer \}
  match anylength \{ __PUTS bufferlength \}
}
macro PUTS [pair*] {
  _PUTS pair
common
        invoke  WriteFileSTD_OUTPUT_HANDLE_crlfsizeof._crlfespNULL
}
macro TEST there*, and_back_again* {
        xor     ebxebx
        invoke  GetTickCount
        mov     [_start], eax
there#_again:
        mov     eaxebx
        mov     edi_buffer
        call    there
        mov     ebpedi
        mov     esi_buffer
        call    and_back_again
        sub     ebp_buffer
        cmp     eaxebx
        je      .proceed
        PUTS    _#there_failed, <_bufferebp>
  .proceed:
        inc     ebx
        jnz     there#_again
        invoke  GetTickCount
        sub     eax, [_start]
        mov     edi_buffer
        call    b2t; here I opt for opponent's routine (to thwart bias accusations ;-)
        mov     ebpedi
        sub     ebp_buffer
        PUTS    _#there, <_bufferebp>
}
start_tests:
        TEST    d2bb2d
        TEST    b2tt2b
        _PUTS   _final
        invoke  ExitProcess0

        align 16
d2b:    mov     edxeax
        bsr     ecxeax
        inc     ecx
        ror     edxcl
  .next_bit:
        xor     alal
        shl     edx1
        adc     al'0'
        stosb
        dec     ecx
        jnz     .next_bit
        mov     [edi], dl
        ret
sizeof.d2b = $-d2b
b2d:
        xor     eaxeax
        xor     edxedx
  .next_char:
        lodsb
        test    alal
        jz      .done
        lea     edx, [edx*2+eax-'0']
        jmp     .next_char
  .done:
        mov     eaxedx
        ret
sizeof.b2d = $-b2d

        align 16
b2t:
cmp eax1            ; 0-1? convert fast
 ja @f                ; and return
 add eax30h
 mov [edi], ax        ; '#', 0
 inc edi
 ret
@@:
mov esiedi          ; save start
@@:
 mov clal           ; *t++=(n&1)+'0'
 and cl1
 add cl'0'
 mov [edi], cl
 inc edi
 shr eax1           ; n/2
jnz @b
mov byte [edi], 0
call reverse.digits
ret
sizeof.b2t = $-b2t

        align 16
t2b:
xor eaxeax
@@:
 movzx ecxbyte [esi]
 inc esi
 test ecxecx
 jz @f
 shl eax1             ; n=n*2+*t++-'0'
 lea eax, [eax+ecx-'0']
jmp @b
@@:
ret
sizeof.t2b = $-t2b

        align 16
reverse.digits:
push edi
dec edi
@@:
 mov cl, [edi]        ; exchange edi/esi
 mov ch, [esi]
 mov [edi], ch
 mov [esi], cl
 dec edi
 inc esi
 cmp esiedi         ; until middle
jb @b
pop edi
ret
sizeof.reverse.digits = $-reverse.digits

sizeof.baldr = sizeof.d2b+sizeof.b2d
sizeof.uart777 = sizeof.b2t+sizeof.t2b+sizeof.reverse.digits

        .data
struc sizeof'd [arg*] {; I'm tired of those 'sizeof's
common
  . arg
  sizeof.#. = $-.
}
_start rd 1
        align 16
_buffer rb 33
irps named2b b2t { _#name sizeof'd db `name"(): " }
_failed sizeof'd db "failed at "
_final db "baldr's: "'0'+sizeof.baldr/10'0'+sizeof.baldr mod 10" bytes"1310,\
          "uart777's: "'0'+sizeof.uart777/10'0'+sizeof.uart777 mod 10" bytes"1310
_crlf   sizeof'd db 1310
sizeof._final = $-_final

        .end    start_tests

Results:

Code:
d2b(): 10110010000000001101
b2t(): 10111110101001011111
baldr's39 bytes
uart777's76 bytes

Not only smaller, but faster too.

P.S. uart777, please keep the shit in your head together (it's safe there), don't let it out, it's harsh world out here.
Post 06 Jun 2013, 17:59
View user's profile Send private message Reply with quote
edfed



Joined: 20 Feb 2006
Posts: 4155
Location: Thank you :D
what about the next step in this sort of contest?

i see a lot of posts with the basics, but after?

i say, ok, now, we have the data to ascii conversion routines.

now, we need to parse datas from text, and then, extract them as datas in ram.
set a sort of cool parsing library to extract infos from save files, and save info to these files from our programs.


Code:

main:
;your application code here
.save;this part is for saving the state in file
mov eax,[application.save]
call saveit!
;

.load;and this part to reload
mov eax,[application.save]
call loadit!
;

ret

application:
.parameter1 dd 123
.value1 dd 656454
.name db "hahaha",0

;HERE START THE PARSING USE:
.saver:
save in .file
save .parameter1 ;here, save is a macro
save .name 




as a result file for this, i see a lot of possible solutions like xml, properties, asm source, etc...
as a resulting lib, i imagine a parse lib, with all the code into to parse data, and connect them to the applications.

it can lead to a log lib, and also a saving lib.

the next would be to make the faster string copy all over the world.
and mix it with the ascii convertion routines to get a parser lib.

all the solutions are acceptable.

it can be fun to make a lib with a version from everybody inside.
the use of this lib would be switchable at run time to change the selected pack.

the functions should always be error resistant (means, give no case for possible error).

Smile
Post 07 Jun 2013, 18:45
View user's profile Send private message Visit poster's website Reply with quote
uart777



Joined: 17 Jan 2012
Posts: 369
baldr: My point was that we should always avoid division. It's easy to search for code to copy and paste, but it's a little harder to do that with programs Razz "Your" code is clearly copied and pasted from another source due to inconsistent/mixed styles. Unlike you, I write my own code. A fair game would involve programmers in separate rooms with no internet or resources. But I don't perceive you as a challenge. In my mind, people like John and you (not so much Guru) are just obstacles standing in the path of my destiny, preventing me from helping graphics programmers.

My objective is to develop a universal macro language+graphics+GUI that is compatible with all CPUs/OSs for true portability. If anyone else is interested, PM. Now, I've got work to do and will only return to check PMs.
Post 07 Jun 2013, 19:44
View user's profile Send private message Reply with quote
AsmGuru62



Joined: 28 Jan 2004
Posts: 1381
Location: Toronto, Canada
@edfed:
In order to parse the ASCII back into EAX -- the multiplication by 10 may be needed.
I use the following macro to do it. Cool! I use macros too!

Code:

macro MUL_10 r32_destr32_src
{
        ;
        ; PERFORMING: r32_dest = 10 * r32_src
        ;
        lea     r32_dest, [r32_src + r32_src*8]
        add     r32_destr32_src
}

...

;
; Registers must be different:
;
mov     ecx73827
MUL_10  eaxecx    ; EAX = 738270



The bad thing is that LEA will NOT set CF on overflow, so long input, like "627902510092824462"
will overflow the result, but there will be no way to detect it, I think.

Otherwise, multiplying in two instructions is quite fast.
Post 07 Jun 2013, 19:52
View user's profile Send private message Send e-mail Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 14584
Location: Planet Dirt

uart777 wrote:
My point was that we should always avoid division.

I don't agree here. It is situation dependant as to whether div is harmful or inconsequential. A statement like that can make some programmers feel that even for code that executes once only during the lifetime of the program that they should be coding up complex solutions to a non-problem single div instruction.
Post 07 Jun 2013, 19:53
View user's profile Send private message Visit poster's website Reply with quote
baldr



Joined: 19 Mar 2008
Posts: 1651

uart777 wrote:
"Your" code is clearly copied and pasted from another source due to inconsistent/mixed styles.

That's heavy. Do you have evidence handy (apart from your mind tricks)? I rarely post foreign code without proper reference to the author. Code indentation differences are because parts © you (do you have to pay for each space used?), and I use different rules for macros.

All in all, is it faster or smaller (or both)?

----8<----

AsmGuru62 wrote:
; Registers must be different:

With a slight change, they don't have to:

Code:
macro MUL_10 r32_destr32_src
{
        ;
        ; PERFORMING: r32_dest = 10 * r32_src
        ;
        lea     r32_dest, [r32_src*5]; let fasm decide
        add     r32_destr32_dest; shl may be used too
}

Post 08 Jun 2013, 02:35
View user's profile Send private message Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  
Goto page 1, 2  Next

< Last Thread | Next Thread >

Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Powered by phpBB © 2001-2005 phpBB Group.

Main index   Download   Documentation   Examples   Message board
Copyright © 2004-2016, Tomasz Grysztar.