ASM Tip and Tough Challenge
Posted: Mon Apr 19, 2004 7:07 pm
Code updated for 5.20+
Here is a tip for anyone what wants to multiply a number by ten, and to do it fast.
Rather than multiply the number by 10, which is comparitively slow, you can do the same operation with a series of shifts and adds, like so:
This effectively adds somenbr*8+somenbr*2 ==> somenbr*10, so that if you did it correctly, DEBUG somenber should show 123450. This trick is commonly used when converting string values into numeric form, so that as you go through each digit character, you subtract 48 ("0" through "9" are coded as 48 through 57 in ascii), multiply the accumulated value by ten (using the technique above), then adding the new digit value to the total.
Now here is the challenge: Although multiply is slower than shifts and adds, it is not near as slow as division is. So if you wanted to reverse the process, of converting numeric form back into a string, you would want a way to extract each decimal digit, commonly by dividing by ten and using the remainder, and convert it back into an ascii character by adding 48 to it. And whereas you worked from left to right in processing the string value originally, you would build up the string in this process by adding the characters from right to left.
Now both the Mod operator (%) and the division step (/) involve divisions, and this means this effort is much slower than the first process. Yet in concept, the second method is nearly the exact reverse of the first one. In fact, if we go into ASM where we can take advantage of each instruction and register, we can attempt to consolidate the (%) and (/) outcomes together, reducing the number of divisions by half.
Unfortunately for us, the design of the x86 architecture destroys the original contents of the registers, so whether our division involved the ax, dx,ax pair, eax, or edx,eax pair, we are unable to repeat the process of division to obatain additional digits. Instead, we still have to use other registers, the stack, or some temporary memory location to track interum results. This is not efficient.
So the challenge is, to try to come up with a reversal of the original approach -- instead of using shift left and adds to effectively multiply by 10, devise a way to use right shifts and subtracts to effectively divide by 10.
I think you will find, as I have this far, that this is far from a trivial effort. It seems to enforce the concept that some processes cannot be reversed.
It's not that I am setting forth an impossible challenge - as far as I know, it should be achievable - but it is certainly not trivial in nature.
Here is a tip for anyone what wants to multiply a number by ten, and to do it fast.
Rather than multiply the number by 10, which is comparitively slow, you can do the same operation with a series of shifts and adds, like so:
Code: Select all
EnableASM
somenbr.l=12345
MOV eax,somenbr
ASL eax,1
MOV ebx,eax
ASL eax,2
ADD eax,ebx
MOV somenbr,eax
Now here is the challenge: Although multiply is slower than shifts and adds, it is not near as slow as division is. So if you wanted to reverse the process, of converting numeric form back into a string, you would want a way to extract each decimal digit, commonly by dividing by ten and using the remainder, and convert it back into an ascii character by adding 48 to it. And whereas you worked from left to right in processing the string value originally, you would build up the string in this process by adding the characters from right to left.
Code: Select all
somenbr.l=12345
s.s=""
Repeat
digit.b=somenbr % 10
s=str(digit)+s
somenbr=Int(somenbr/10)
Until somenbr=0
Debug s
Unfortunately for us, the design of the x86 architecture destroys the original contents of the registers, so whether our division involved the ax, dx,ax pair, eax, or edx,eax pair, we are unable to repeat the process of division to obatain additional digits. Instead, we still have to use other registers, the stack, or some temporary memory location to track interum results. This is not efficient.
So the challenge is, to try to come up with a reversal of the original approach -- instead of using shift left and adds to effectively multiply by 10, devise a way to use right shifts and subtracts to effectively divide by 10.
I think you will find, as I have this far, that this is far from a trivial effort. It seems to enforce the concept that some processes cannot be reversed.
It's not that I am setting forth an impossible challenge - as far as I know, it should be achievable - but it is certainly not trivial in nature.