flat assembler
Message board for the users of flat assembler.
Index
> Tutorials and Examples > Simple Conversion Routines (EAX to ASCII, Win32) Goto page 1, 2 Next |
Author |
|
AsmGuru62 02 May 2013, 23:26
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.
Last edited by AsmGuru62 on 03 May 2013, 01:28; edited 1 time in total |
|||||||||||
02 May 2013, 23:26 |
|
zhak 02 May 2013, 23:46
Hmmm and where's EAX2ASCII.asm?
|
|||
02 May 2013, 23:46 |
|
AsmGuru62 03 May 2013, 01:27
Looks like I attached the wrong file.
Fixing it now. |
|||
03 May 2013, 01:27 |
|
AsmGuru62 03 May 2013, 14:44
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. |
|||
03 May 2013, 14:44 |
|
MHajduk 03 May 2013, 15:40
AsmGuru62 wrote: I may note, however, that since we're coding in Asm - the prologue/epilogue are kind |
|||
03 May 2013, 15:40 |
|
uart777 31 May 2013, 14:30
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. |
|||
31 May 2013, 14:30 |
|
revolution 31 May 2013, 15:04
uart777 wrote: It is extremely unoptimized ... 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. |
|||
31 May 2013, 15:04 |
|
HaHaAnonymous 31 May 2013, 15:58
[ Post removed by author. ]
Last edited by HaHaAnonymous on 28 Feb 2015, 20:15; edited 1 time in total |
|||
31 May 2013, 15:58 |
|
uart777 31 May 2013, 22:00
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. 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 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; _sinto: db '2147483648', 0 _digits: db '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 esi, edi ; until middle jb @b pop edi ret ; convert signed 32BIT integer to text i2t: test eax, 80000000h ; 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 cl, cl jnz @b dec edi ret .convert: call u2t ret ; convert unsigned 32BIT number to text u2t: cmp eax, 9 ; 0-9? single digit? ja @f ; convert fast and return add eax, 30h mov [edi], ax ; '#', 0 inc edi ret @@: mov esi, edi ; save start mov ecx, 1999999Ah ; ((2^32)/10)+1 @@: mov ebx, eax ; n/10... mul ecx mov eax, edx lea edx, [edx*4+edx] add edx, edx sub ebx, edx ; remainder add bl, '0' ; *t++=c mov [edi], bl inc edi test eax, eax jnz @b mov byte [edi], 0 call reverse.digits ret ; convert 32BIT hexadecimal number to text h2t: cmp eax, 9 ; 0-9? convert fast ja @f ; and return add eax, 30h mov [edi], ax ; '#', 0 inc edi ret @@: mov esi, edi ; save start @@: movzx ecx, al ; *t++=*(_hex+(n&15)) and ecx, 15 movzx ecx,\ byte [_digits+ecx] mov [edi], cl inc edi shr eax, 4 ; n/16 jnz @b mov byte [edi], 0 call reverse.digits ret ; convert 32BIT binary number to text b2t: cmp eax, 1 ; 0-1? convert fast ja @f ; and return add eax, 30h mov [edi], ax ; '#', 0 inc edi ret @@: mov esi, edi ; save start @@: mov cl, al ; *t++=(n&1)+'0' and cl, 1 add cl, '0' mov [edi], cl inc edi shr eax, 1 ; n/2 jnz @b mov byte [edi], 0 call reverse.digits ret ; t2i ; convert text to signed 32BIT integer t2i: xor ecx, ecx cmp byte [esi], '-' jne .convert inc ecx ; yes, negate inc esi mov eax, esi mov edx, _sinto @@: ; signed minimum? mov cl, [eax] mov ch, [edx] inc eax inc edx cmp cl, ch ; while *a++=*b++ jne .convert or cl, ch ; and both nonzero jnz @b or cl, ch ; both must=0 at end jnz .convert mov eax,\ -2147483648 ret .convert: push ecx call t2u pop ecx test ecx, ecx jz @f neg eax @@: ret ; t2u ; convert text to unsigned 32BIT integer t2u: xor eax, eax @@: movzx ecx, byte [esi] inc esi test ecx, ecx jz @f lea eax, [eax+eax*4] ; n=n*10+*t++-'0' lea eax, [eax+eax-'0'] add eax, ecx jmp @b @@: ret ; t2h ; convert text to 32BIT hexadecimal number t2h: xor eax, eax @@: movzx ecx,\ byte [esi] inc esi test cl, cl jz @f shl eax, 4 ; 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 eax, ecx jmp @b @@: ret ; t2b ; convert text to 32BIT binary number t2b: xor eax, eax @@: movzx ecx, byte [esi] inc esi test ecx, ecx jz @f shl eax, 1 ; n=n*2+*t++-'0' lea eax, [eax+ecx-'0'] jmp @b @@: ret |
|||
31 May 2013, 22:00 |
|
revolution 01 Jun 2013, 03:50
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. |
|||
01 Jun 2013, 03:50 |
|
baldr 02 Jun 2013, 00:25
uart777 wrote:
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 ) 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. |
|||
02 Jun 2013, 00:25 |
|
uart777 05 Jun 2013, 07:44
revolution: I merely presented a different technique. My older code uses CCCCCCCDh for signed conversions.
baldr: Jealously is the only motivation you have 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. 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 eax, 9 ; 0-9? convert fast ja @f ; and return add eax, 30h mov [edi], ax ; '#', 0 inc edi ret @@: mov esi, edi ; save start @@: movzx ecx, al ; *t++=*(_hex+(n&15)) and ecx, 31 movzx ecx,\ byte [_digits+ecx] mov [edi], cl inc edi shr eax, 5 ; n/32 jnz @b mov byte [edi], 0 call reverse.digits ret ; t2z ; convert text to 32BIT zexadecimal number t2z: xor eax, eax @@: movzx ecx,\ byte [esi] inc esi test cl, cl jz @f shl eax, 5 ; 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 eax, ecx jmp @b @@: ret |
|||
05 Jun 2013, 07:44 |
|
edfed 05 Jun 2013, 16:38
i'd prefer the hexadecimal.
or base64. if really you want to cover a long set of digits. uart777, baldr: Ready? Fight!!!!! |
|||
05 Jun 2013, 16:38 |
|
baldr 06 Jun 2013, 17:59
edfed wrote: Ready? Code: format PE console include "Win32AX.Inc" .code ; some macros to shorten source and ensure fair play macro __PUTS buffer*, length* { invoke WriteFile, STD_OUTPUT_HANDLE, buffer, length, esp, NULL } macro _PUTS buffer*, length { match , length \{ __PUTS buffer, sizeof.\#buffer \} match any, length \{ __PUTS buffer, length \} } macro PUTS [pair*] { _PUTS pair common invoke WriteFile, STD_OUTPUT_HANDLE, _crlf, sizeof._crlf, esp, NULL } macro TEST there*, and_back_again* { xor ebx, ebx invoke GetTickCount mov [_start], eax there#_again: mov eax, ebx mov edi, _buffer call there mov ebp, edi mov esi, _buffer call and_back_again sub ebp, _buffer cmp eax, ebx je .proceed PUTS _#there, _failed, <_buffer, ebp> .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 ebp, edi sub ebp, _buffer PUTS _#there, <_buffer, ebp> } start_tests: TEST d2b, b2d TEST b2t, t2b _PUTS _final invoke ExitProcess, 0 align 16 d2b: mov edx, eax bsr ecx, eax inc ecx ror edx, cl .next_bit: xor al, al shl edx, 1 adc al, '0' stosb dec ecx jnz .next_bit mov [edi], dl ret sizeof.d2b = $-d2b b2d: xor eax, eax xor edx, edx .next_char: lodsb test al, al jz .done lea edx, [edx*2+eax-'0'] jmp .next_char .done: mov eax, edx ret sizeof.b2d = $-b2d align 16 b2t: cmp eax, 1 ; 0-1? convert fast ja @f ; and return add eax, 30h mov [edi], ax ; '#', 0 inc edi ret @@: mov esi, edi ; save start @@: mov cl, al ; *t++=(n&1)+'0' and cl, 1 add cl, '0' mov [edi], cl inc edi shr eax, 1 ; n/2 jnz @b mov byte [edi], 0 call reverse.digits ret sizeof.b2t = $-b2t align 16 t2b: xor eax, eax @@: movzx ecx, byte [esi] inc esi test ecx, ecx jz @f shl eax, 1 ; 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 esi, edi ; 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 name, d2b 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", 13, 10,\ "uart777's: ", '0'+sizeof.uart777/10, '0'+sizeof.uart777 mod 10, " bytes", 13, 10 _crlf sizeof'd db 13, 10 sizeof._final = $-_final .end start_tests Code: d2b(): 10110010000000001101 b2t(): 10111110101001011111 baldr's: 39 bytes uart777's: 76 bytes 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. |
|||
06 Jun 2013, 17:59 |
|
edfed 07 Jun 2013, 18:45
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). |
|||
07 Jun 2013, 18:45 |
|
uart777 07 Jun 2013, 19:44
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 "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. |
|||
07 Jun 2013, 19:44 |
|
AsmGuru62 07 Jun 2013, 19:52
@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_dest, r32_src { ; ; PERFORMING: r32_dest = 10 * r32_src ; lea r32_dest, [r32_src + r32_src*8] add r32_dest, r32_src } ... ; ; Registers must be different: ; mov ecx, 73827 MUL_10 eax, ecx ; 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. |
|||
07 Jun 2013, 19:52 |
|
revolution 07 Jun 2013, 19:53
uart777 wrote: My point was that we should always avoid division. |
|||
07 Jun 2013, 19:53 |
|
baldr 08 Jun 2013, 02:35
uart777 wrote: "Your" code is clearly copied and pasted from another source due to inconsistent/mixed styles. All in all, is it faster or smaller (or both)? ----8<---- AsmGuru62 wrote: ; Registers must be different: Code: macro MUL_10 r32_dest, r32_src { ; ; PERFORMING: r32_dest = 10 * r32_src ; lea r32_dest, [r32_src*5]; let fasm decide add r32_dest, r32_dest; shl may be used too } |
|||
08 Jun 2013, 02:35 |
|
Goto page 1, 2 Next < Last Thread | Next Thread > |
Forum Rules:
|
Copyright © 1999-2024, Tomasz Grysztar. Also on GitHub, YouTube.
Website powered by rwasa.