News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

"Micro-division" with AAM

Started by Larry Hammick, December 09, 2007, 05:31:11 AM

Previous topic - Next topic

Larry Hammick

The mnemonic AAM (Ascii Adjust after Multiply) in machine code is two bytes:
db 0D4h,0Ah
For binary coded decimal arithmetic, it unpacks a number from 0 to 99 in AL into its two digits decimal digits, the more significant going to AH and the other to AL. But I just learned that AAM is really a divide-with-remainder operation, and the divisor does not need to be 10. Try putting this in a code section:
mov al,101   ;decimal
db 0D4h,0Bh  ;divide AL by eleven
and behold, you get 9 in AH and 2 (the remainder) in AL. It works for any byte in AL and any immediate divisor except zero in the instruction
db 0D4h,(divisor)
The flags ZF, SF, and PF end up set or clear according to the remainer (in AL), it seems. Intel's Instruction Set Reference explicitly says that non-10 "bases" are supported, but they have no mnemonic except for base 10, and "To adjust to values in another number base, the instruction must be hand coded in machine code (D4 imm8)."

Larry Hammick

AAD is a sort of reverse of AAM:
db 0D5h,0Ah
and again the immediate byte does not need to be ten. The nameless opcode
db 0D5h,immed8
multiplies AH by immed8 and adds the result to AL. Intel:
QuoteOperation
tempAL <- AL;
tempAH <- AH;
AL <- (tempAL + (tempAH * imm8)) AND FFH; (* imm8 is set to 0AH for the AAD mnemonic *)
AH <- 0
The immediate value (imm8) is taken from the second byte of the instruction.

donkey

About the only use I ever found for AAM was to convert byte values to ascii...

mov eax, [SomeNumber] ; 0-99
aam
add eax,3030h
and eax,0FFFFh
bswap eax
shr eax,16
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

jj2007

Works perfectly as long as eax is byte, i.e. below 256. Attached a console app with the DivEax macro...
.nolist
include   \masm32\include\masm32rt.inc
.list

.code

AppName   db   "Tiny console application",0
UsrPrompt   db   "Number divided: ",13,10,0
P99_3      db   13,10,"99/3=",0
P120_4   db   13,10,"120/4=",0
P120_4hs   db   13,10,"120/4= (eax highword=1) ",0
P255_5   db   13,10,"255/5=",0
P255_51   db   13,10,"255/51=",0
P256_4   db   13,10,"256/4=",0
P500_5   db   13,10,"500/5=",0
P1275_255   db   13,10,"1275/255=",0

DivEax MACRO div:REQ
  db 0D4h,div   ;divide AL
  shr eax,8      ; mov result to AL
EXITM
ENDM


start:   invoke SetConsoleTitle,addr AppName
   print offset UsrPrompt

   print offset P99_3
   mov   eax,99
   DivEax 3
   print str$(eax)

   print offset P120_4
   mov   eax,120
   DivEax 4
   print str$(eax)

   print offset P120_4hs
   mov   eax,120+65536
   DivEax 4
   print str$(eax)

   print offset P255_5
   mov   eax,255
   DivEax 5
   print str$(eax)

   print offset P255_51
   mov   eax,255
   DivEax 51
   print str$(eax)

   print offset P256_4
   mov   eax,256
   DivEax 4
   print str$(eax)

   print offset P500_5
   mov   eax,500
   DivEax 5
   print str$(eax)

   print offset P1275_255
   mov   eax,1275
   DivEax 255
   print str$(eax)

   print chr$(13,10)
   call ret_key
   invoke ExitProcess,0
end start


[attachment deleted by admin]

Larry Hammick

Quote from: jj2007 on December 09, 2007, 11:17:58 AM
Works perfectly as long as eax is byte, i.e. below 256.
It seems to work for any input eax. AH is ignored in the input and the upper two bytes of eax are unchanged. E.g.
mov eax,-1
db 0D4h,11h
and you get eax=FFFF0F00 as expected. Likewise
mov eax,-1
db 0D4h,1
and there is no overflow. You get eax=FFFFFF00: quotient AL/1 goes to AH, and remainder to AL.

jj2007

Quote from: Larry Hammick on December 09, 2007, 08:06:13 PM
Quote from: jj2007 on December 09, 2007, 11:17:58 AM
Works perfectly as long as eax is byte, i.e. below 256.
It seems to work for any input eax. AH is ignored in the input and the upper two bytes of eax are unchanged. E.g.
mov eax,-1
db 0D4h,11h
and you get eax=FFFF0F00 as expected. Likewise
mov eax,-1
db 0D4h,1
and there is no overflow. You get eax=FFFFFF00: quotient AL/1 goes to AH, and remainder to AL.

Well, if the purpose is to have an elegant little macro with 5 bytes only, then one should restrict the range to 0...255. Attached a test of the implementation with two macros, DivByte and MulByte.


include \masm32\include\masm32rt.inc

DivByte MACRO div:REQ
  db 0D4h,div ;divide AL
  shr eax,8 ; mov result to AL
EXITM
ENDM

MulByte MACRO mul:REQ
  shl eax,8 ; mov into AH
  db 0D5h,mul ; multiply AL
EXITM
ENDM

start: invoke SetConsoleTitle,chr$("Divide and multiply eax:")

; ### Divide ###

print chr$(13,10,13,10,"Divide eax:")

print chr$(13,10,"99/3=")
mov eax,99
DivByte 3
print str$(eax)
...
print chr$(13,10,13,10,"Multiply eax:")

print chr$(13,10,"0*99=")
mov eax,0
MulByte 99
print str$(eax)
print chr$(13,10)
call ret_key
invoke ExitProcess,0
end start

[attachment deleted by admin]

Mark Jones

Perhaps about the only thing easier could be:

    mov eax,MulByte(0,99)

...but then this starts to look more like some HLL and less like assembler. :lol
"To deny our impulses... foolish; to revel in them, chaos." MCJ 2003.08

jj2007

Quote from: Mark Jones on December 10, 2007, 04:45:11 PM
Perhaps about the only thing easier could be:

    mov eax,MulByte(0,99)

...but then this starts to look more like some HLL and less like assembler. :lol

Sorry, Mark, I could not refrain from taking you seriously...  :8)
Here it is, as "GetCell" (full code attached, console):


MyFlatBuffer:
dd 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28
RowLen dd 7

print chr$(13,10,"Getting the value of cell row:col \
in a flat buffer (0, 1, 2, ... n):",13,10)

print chr$(13,10,"Cell 2:3 = ")
print str$(GetCell(2,3))


[attachment deleted by admin]

VLaaD

Well... I just felt somehow  :bg that there is no other purpose in "Micro-division" with AAM other than to obfuscate the code  :boohoo:
If that's the point (excuse me if it is not, please move the post + don't shoot the MSN + I'm new here + I promise that I'll be good  ::) )
...here is my $0.01 (generated by some function of mine that "I can't find right now"  :green (seriously, it was a fast hack, really a shame to show to someone  :( ) ):

;(~(A AND B) AND ~(~A AND ~B)) => using only AND and bitwise negation to get XOR
obfuscated_XOR1:
   mov edx, eax         ;eax contains A, ebx contains B
   and eax, ebx
   not eax
   xchg edx, eax
   not eax
   not ebx
   and eax, ebx
   not eax
   and eax, edx         ;eax now contains "xor eax, ebx"
   not ebx         ;ebx now contains original ebx, you can continue your rampage :)
   retn

;(~(A OR ~B) OR ~(~A OR B)) => using only AND and bitwise negation to get XOR
obfuscated_XOR2:
   mov edx, eax
   not ebx
   or eax, ebx
   not eax
   xchg edx, eax
   not eax
   not ebx         ;ebx now contains original ebx, beware (it's visible) or use it (intended!)
   or eax, ebx
   not eax
   or eax, edx         ;eax now contains "xor eax, ebx"
   retn

;(~(~A AND ~B) AND ~(~B AND ~A)) => using only AND and bitwise negation to get OR
obfuscated_OR:
   mov esi, eax         ;eax contains A, ebx contains B
   not eax
   not ebx
   and eax, ebx
   not eax
   xchg esi, eax
   not eax
   and eax, ebx
   not eax
   and eax, esi         ;eax now contains "or eax, ebx"
   not ebx         ;ebx now contains original B, beware (it's visible) or use it (intended!)
   retn

;(~(~A OR ~B) OR ~(~B OR ~A)) => using only OR and bitwise negation to get AND
obfuscated_AND:
   mov edi, eax         ;eax contains A, ebx contains B
   not eax
   not ebx
   or eax, ebx
   not eax
   xchg edi, eax
   not eax
   or eax, ebx
   not eax
   or eax, edi         ;eax now contains "and eax, ebx"
   not ebx         ;ebx now contains original B, beware (it's visible) or use it (intended!)
   retn