flat assembler
Message board for the users of flat assembler.
Index
> DOS > shortest way to display decimal number ? Goto page 1, 2 Next |
Author |
|
LocoDelAssembly 22 May 2006, 20:41
Slai wrote: In my variant I am using recursion, but I want to use the code in a macro so recursion is no good. Why not? Code: org 100h macro OutputDigit { add dl, '0' mov ah, 2 int 21h } firstShowDec = 1 macro ShowDec { if firstShowDec firstShowDec = 0 push ..endShowDec ..ShowDec: xor edx, edx ; edx = 0 mov ecx, 10 div ecx test eax, eax jz ..print push dx call ..ShowDec pop dx ..print: OutputDigit ; displays number from 0 to 9 that is in dl ret ..endShowDec: else call ..ShowDec end if } mov eax, 12345678 ShowDec mov eax, 87654321 ShowDec int $20 Not sure if this is what you want though |
|||
22 May 2006, 20:41 |
|
Slai 23 May 2006, 01:18
yes this looks better, just I have a plan to use a tempoary variable that contains the number 10 instead of a register, and I thought that the easyest way will be to use a tempoary variable in the stack that will be something like dword [esp - 4] I guess ..
Anyways I came up with a new code that pushes a negative number, than pushes all the reminders from the divisions in the stack, and then it pops them untill the negative value. But something isn't right in my new code because it displays only the last digit from the number Code: org 100h macro ShowDec { local nextDigit, showDigit, done push word -1 nextDigit: xor edx, edx ; edx = 0 mov ecx, 10 div ecx push dx test eax, eax jz nextDigit pop dx showDigit: add dl, '0' mov ah, 2 int 21h pop dx jns showDigit } mov eax, 1234567890 ShowDec ret |
|||
23 May 2006, 01:18 |
|
LocoDelAssembly 23 May 2006, 01:55
POP doesn't update flags so you need to test dx before JNS instruction. And about replacing DIV with MUL there is a trick to do that but I don't remember how to calculate the multiplicator number to simulate division.
[edit] I found it but you can't get the reminder with this method... http://www.agner.org/assem/pentopt.pdf [section 18.7 Division (all processors)][/edit] |
|||
23 May 2006, 01:55 |
|
Slai 23 May 2006, 04:28
yes, before posting the code I tryed to test it in few different ways like :
pop dx test dx,dx jns showDigit and than pop dx cmp dx,-1 jne showDigit , but they all gave me only the last digit of the number. I am missing something else I guess .. |
|||
23 May 2006, 04:28 |
|
LocoDelAssembly 23 May 2006, 12:55
you are right, there is another error, replace "jz nextDigit" with "jNz nextDigit". I suggest you replace "push word -1" with just "push -1", it encodes in less bytes and has the same meaning.
|
|||
23 May 2006, 12:55 |
|
Slai 23 May 2006, 21:29
Great it worked ! thanks a lot
|
|||
23 May 2006, 21:29 |
|
Slai 23 May 2006, 21:49
and here is the result for now : (33 bytes)
Code: macro ShowDec { local nextDigit, showDigit, done push -1 mov ecx, 10 nextDigit: xor edx, edx ; edx = 0 div ecx push dx test eax, eax jnz nextDigit pop dx showDigit: add dl, '0' mov ah, 2 int 21h pop dx test dx, dx jns showDigit } |
|||
23 May 2006, 21:49 |
|
LocoDelAssembly 23 May 2006, 23:22
replace "mov ecx, 10" with "xor ecx, ecx"/"mov cl, 10" to save an extra byte.
|
|||
23 May 2006, 23:22 |
|
Matrix 24 May 2006, 01:26
xor edx, edx ; edx = 0 - 3 bytes
cdq ; - 2 bytes , so overall, let me post it: 29 bytes: Code: macro ShowDec { local nextDigit, showDigit, done xor ebx, ebx xor cx,cx mov bl, 10 nextDigit: cdq div ebx push dx inc cx test eax, eax jnz nextDigit showDigit: pop dx add dl, '0' mov ah, 2 int 21h loopw showDigit } |
|||
24 May 2006, 01:26 |
|
LocoDelAssembly 24 May 2006, 02:02
the problem with CDQ is that this macro is intended to work with unsigned numbers so making a CDQ with EAX above to $7FFFFFFF will set EDX to $FFFFFFFF which will overflow the division
[edit] An additional problem is that you use an extra register with respect to the previous code and Slai wants to use the less amount of registers as possible. Destroying EBX I think it's a bad idea because it doesn't follow the common calling conventions which says that EAX, EDX and ECX are the only volatile GPR but that's just my preference, maybe Sai has no problem in destroying it [/edit] |
|||
24 May 2006, 02:02 |
|
Matrix 24 May 2006, 03:18
whoops, true, cdq will have that overflow bug, with sign extend, so only can be used with word write routine...
30 bytes then. int21h has some functions that return some undefined register contents too, maeby you should not use it either then, or save registers to stack. ebx seems neccesary to use cx as counter. doing the same using bios interrupt has the adventages of can be used wthout dos, and smaller by 1 byte: because of add al, '0' Code: macro ShowDec { local nextDigit, showDigit, done xor ebx, ebx xor cx,cx mov bl, 10 nextDigit: xor edx,edx div ebx push dx inc cx test eax, eax jnz nextDigit showDigit: pop ax add al, '0' mov ah, 0xe int 10h loopw showDigit } if you whould like to use the least registers you whould have to use fpu for division, - that is not small, or make it the smallest, and pushad, popad... |
|||
24 May 2006, 03:18 |
|
Borsuc 26 May 2006, 20:16
Okay, probably off topic, and I know it's not small (but it's fast).. It's a conversion to base10 in strings. A lot of the stuff here is very off topic, but it might be some inspiration. And by the way, it doesn't use divs at all, only a single mul per each digit! I'm sure it can be optimized and adjusted for your macro.
70 bytes Code: ; esi must point at the string buffer that will receive the base10 digits ; eax is the integer mov ecx, 01999999Ah mov edi, esi rep_loop: mov ebx, eax mul ecx mov eax, edx lea edx, [edx*4+edx] add ebx, '0' add edx, edx sub ebx, edx mov [esi], bl inc esi test eax, eax jne rep_loop mov eax, esi sub eax, edi reverse_digits: dec esi mov cl, [edi] mov dl, [esi] mov [esi], cl mov [edi], dl inc edi cmp edi, esi jb reverse_digits Hope it helps |
|||
26 May 2006, 20:16 |
|
LocoDelAssembly 26 May 2006, 22:00
I'm interested in this
Is there any link explaining it? Regards PS: By "shortest way" can also mean shortest time |
|||
26 May 2006, 22:00 |
|
Borsuc 26 May 2006, 22:46
The algo is like this (the "input number" is the number that gets converted):
Code: 1. fill "ecx" with the MAGIC number 2. set "ebx" to the value of the current input number 3. multiply "eax" with "ecx"; "eax" being the current input number this will yield "eax/10" in the register "edx" because of that MAGIC number's magic 4. now we will set "eax" to "current input number" which effectively means we just "divided eax by 10" 5. next, we will multiply "edx" (not "eax") by 10 this is what those "lea" and "add edx, edx" are all about 6. now we have in "edx" the number truncated to 10 boundary this means that, by subtracting we will get the last digit I used an "add ebx, '0'" before the subtraction -- it is ok it doesn't matter in which order you do it. That addition is just for ASCII stuff anyway 7. next we put that digit in "ebx" into the string 8. repeat from 2 9. ... Let's see an example: Code: input number: 951 eax = 951 1. ecx = 01999999Ah 2. ebx = 951 (eax, in fact) 3. multiply eax with MAGIC ecx, so edx = 951/10 = 95 4. eax = 95 5. edx = 950 6. digit = ebx - edx = 951 - 950 = 1 :wink: 7. ... repeat 2. ebx = 95 (remember, step 4 above) 3. multiply eax with MAGIC ecx, so edx = 95/10 = 9 4. eax = 9 5. edx = 90 6. digit = ebx - edx = 95 - 90 = 5 :wink: 7. ... repeat 2. ebx = 9 3. multiply eax with MAGIC ecx, so edx = 9/10 = 0 4. eax = 0 5. edx = 0 6. digit = ebx - edx = 9 - 0 = 9 :wink: 7. ... Oh, and that MAGIC number... well, I understood it from "The Art of Assembly Language Programming", it explains it somewhere.. I think here. PS: 6554 is "199A". Guess what 1999999A is... Cheers |
|||
26 May 2006, 22:46 |
|
Tomasz Grysztar 26 May 2006, 23:06
There was once an interesting discussion about this on the other board - http://www.asmcommunity.net/board/index.php?topic=21308.0
|
|||
26 May 2006, 23:06 |
|
LocoDelAssembly 26 May 2006, 23:28
Thanks, great explanation!! The part I didn't understand was how to calculate the magic number but well, it's magic and I didn't pay attention that you calculate MOD like "mod = x div y * y - x" (this because I'm very silly in maths and I can't see such simple things fast )
Searching 01999999Ah in Software Optimization Guide for AMD64 Processors I found this: Quote:
I'd tested with values greater than 4000_0005h and I had no problems, unsigned division seems to have no restrictions. Regards [edit] Complete BS, the code HAS problems with values greater than 4000_0005h and I don't know from where "unsigned division seems to have no restrictions" comes. [/edit] Last edited by LocoDelAssembly on 06 Feb 2008, 17:38; edited 1 time in total |
|||
26 May 2006, 23:28 |
|
Slai 01 Jun 2006, 04:09
I used the windows scientific calculator to divide 1 0000 0000 h by A , and I got 19999999h , so I guess this is an easy way to get the magic number, just I don't know where the extra 1 comes from .. I guess because the result is something like 19999999.9999999 ... h
|
|||
01 Jun 2006, 04:09 |
|
Slai 01 Jun 2006, 04:58
about the showDec macro, I first started it because I wanted to test an algorithm for finding the sqare root of integer, but than I wanted to modify it for the assembler/compiler that I am planing to do, where "Output eax" will be compiled not as a call to a function, but directly to this code, so thats why I wanted it to use least space and least registers as possible. So I tryed to replace ebx with "dword [esp-4]" but the code came up something like 42 bytes, so I gave up on that idea.
And with the todays high-speed/size tehnologies I am still wondering whitch one is better, optimising for speed, or for space... |
|||
01 Jun 2006, 04:58 |
|
Borsuc 01 Jun 2006, 16:34
Slai wrote: I used the windows scientific calculator to divide 1 0000 0000 h by A , and I got 19999999h , so I guess this is an easy way to get the magic number, just I don't know where the extra 1 comes from .. I guess because the result is something like 19999999.9999999 ... h Slai wrote: And with the todays high-speed/size tehnologies I am still wondering whitch one is better, optimising for speed, or for space... Sorry to scare you off with my opinions. |
|||
01 Jun 2006, 16:34 |
|
Goto page 1, 2 Next < Last Thread | Next Thread > |
Forum Rules:
|
Copyright © 1999-2024, Tomasz Grysztar. Also on GitHub, YouTube.
Website powered by rwasa.