News:

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

Fun with Masm

Started by jj2007, January 31, 2010, 09:38:34 AM

Previous topic - Next topic

jj2007

Here is a little exercise: Find a macro that saves 5 arguments to memory for later use. The macro should have no more than 20 lines, and their should be no restrictions regarding the 32-bit registers used as arguments. Global DWORD variables are also allowed. Hint: It is possible :bg

include \masm32\include\masm32rt.inc

Store5 Macro arg1, arg2, arg3, arg4, arg5
...
ENDM

.data?
MyDword dd ?
Store5Buffer dd 64 dup(?)

.code
start:
... ; 10 bytes overhead
Store5 eax, ecx, edx, esi, edi ; 12 plus one per reg32, 6 per global var
Store5 MyDword, ecx, edx, esi, edi ; 12+6+4=22 bytes
Store5 MyDword, ecx, edx, esi, MyDword ; 12+2*6+3=27
inkey "No crash so far"
exit

end start

hutch--

 :bg


push arg1
push arg2
push arg3
push arg4
push arg5


Later.


pop arg5
pop arg4
pop arg3
pop arg2
pop arg1


:P
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

dedndave

i like the second one Hutch   :P

jj2007

Ok, I see that was too liberal, so let me introduce a constraint: You need the content of memory later on deep inside a proc, and you have no idea what happened to the stack in the meantime. Use Store5Buffer.
:green2

dedndave


        push    arg5
        pop     Store5Buffer[16]
        push    arg4
        pop     Store5Buffer[12]
        push    arg3
        pop     Store5Buffer[8]
        push    arg2
        pop     Store5Buffer[4]
        push    arg1
        pop     Store5Buffer

with a little word-play, you can make it support a variable number of arguments
you could also add some verbage to use MOV if the argument is a register
you know me, Jochen - macro comes on a plate with cheesy sauce   :bg

qWord

What ever the sense is ... look at this  :bg
Store5 Macro arg1, arg2, arg3, arg4, arg5
IFNDEF s5_cntr
s5_cntr = 0
ENDIF
IF s5_cntr+5 GT ((SIZEOF Store5Buffer)/4)
%echo ERR: overflow ;)
EXITM
ENDIF
cntr = 0
REPEAT 5
% IF (OPATTR getarg(%(cntr+1),<arg1,arg2,arg3,arg4,arg5>)) AND 010000y
mov Store5Buffer[s5_cntr*4+cntr*4], getarg(%(cntr+1),<arg1,arg2,arg3,arg4,arg5>)
ELSE
push getarg(%(cntr+1),<arg1,arg2,arg3,arg4,arg5>)
pop Store5Buffer[s5_cntr*4+cntr*4]
ENDIF
cntr = cntr + 1
ENDM
s5_cntr = s5_cntr + 5
ENDM
FPU in a trice: SmplMath
It's that simple!

jj2007

Not bad, really not bad... :U

17 bytes for macJ
35 bytes for macD
29 bytes for macQ

qWord

Quote from: jj2007 on January 31, 2010, 06:05:50 PM
17 bytes for macJ
17 bytes ... :
Store5 Macro arg1, arg2, arg3, arg4, arg5
IFNDEF s5_cntr
s5_cntr = 0
.data
_esp dd OFFSET Store5Buffer + (sizeof Store5Buffer)  
.code
ENDIF
IF s5_cntr+5 GT ((SIZEOF Store5Buffer)/4)
%echo ERR: overflow ;)
EXITM
ENDIF
cntr = 1
xchg _esp,esp
REPEAT 5
push getarg(%(cntr),<arg1,arg2,arg3,arg4,arg5>)
cntr = cntr + 1
ENDM
xchg esp,_esp
s5_cntr = s5_cntr + 5
ENDM
FPU in a trice: SmplMath
It's that simple!

jj2007

Thanks, Dave & qWord - you were pretty close :toothy
include \masm32\include\masm32rt.inc

Store5J Macro arg1, arg2, arg3, arg4, arg5
  xchg esp, Store5Buffer[20]
  push arg1
  push arg2
  push arg3
  push arg4
  push arg5
  add esp, 20
  xchg esp, [esp]
ENDM

Store5D Macro arg1, arg2, arg3, arg4, arg5
  push arg5
  pop Store5Buffer[16]
  push arg4
  pop Store5Buffer[12]
  push arg3
  pop Store5Buffer[8]
  push arg2
  pop Store5Buffer[4]
  push arg1
  pop Store5Buffer
ENDM

Store5Q Macro arg1, arg2, arg3, arg4, arg5
  IFNDEF s5_cntr
s5_cntr = 0
  .data
_esp dd OFFSET Store5Buffer + (sizeof Store5Buffer) 
  .code
  ENDIF
  IF s5_cntr+5 GT ((SIZEOF Store5Buffer)/4)
%echo ERR: overflow ;)
EXITM
  ENDIF
  cntr = 1
  xchg _esp,esp
  REPEAT 5
push getarg(%(cntr),<arg1,arg2,arg3,arg4,arg5>)
cntr = cntr + 1
  ENDM
  xchg esp,_esp
  s5_cntr = s5_cntr + 5
ENDM

.data?
MyDword1 dd ?
MyDword2 dd ?
Store5Buffer dd 6 dup(?)

.code
start:
mov Store5Buffer[20], offset Store5Buffer+5*4 ; 10 bytes overhead for Store5J
mov ebx, esp ; just a little test whether the stack is still OK after the macros

mjs:
REPEAT 5
Store5J MyDword1, ecx, edx, esi, MyDword2 ; 12 plus one per reg32, 6 per global var: 12+2*6+3=27
Store5J eax, ecx, edx, esi, edi ; 12 plus one per reg32, 6 per global var: 12+5=17
ENDM
mje:

mds:
REPEAT 5
Store5D MyDword1, ecx, edx, esi, MyDword2
Store5D eax, ecx, edx, esi, edi
ENDM
mde:

mqs:
REPEAT 5
Store5Q MyDword1, ecx, edx, esi, MyDword2
Store5Q eax, ecx, edx, esi, edi
ENDM
mqe:

sub ebx, esp ; just a little test whether the stack is still OK after the macros
je @F
print "Somebody just crashed the stack, shame on you!", 13, 10
@@:
mov eax, mje
sub eax, mjs
print str$(eax), " bytes for 10*macJ", 13, 10
mov eax, mde
sub eax, mds
print str$(eax), " bytes for 10*macD", 13, 10
mov eax, mqe
sub eax, mqs
print str$(eax), " bytes for 10*macQ, qWord cheats!!!", 13, 10, 10
inkey "No crash so far, thanks"
exit

end start

qWord

Quote from: jj2007 on January 31, 2010, 10:00:06 PM
  xchg esp, Store5Buffer[20]
  ...
  add esp, 20
  xchg esp, [esp]
tricky  one :clap:

qWord
FPU in a trice: SmplMath
It's that simple!

jj2007

Quote from: qWord on January 31, 2010, 10:20:31 PM
tricky  one :clap:

qWord

Thanks :bg

Here is the general form for any number of arguments:
Store2Mem MACRO TheArgs:VARARG
LOCAL argct, arg
  argct = 0
  FOR arg, <TheArgs>
argct = argct + 1
  ENDM
  MyBuf CATSTR <Store2MemBuffer>, %argct ; we check if the buffer has been created
  % ifndef MyBuf
.data?
  MyBuf dd argct+1 dup(?)
.code
  mov MyBuf[argct*4], offset MyBuf+argct*4 ; 10 bytes overhead per argct
  endif
  xchg esp, MyBuf[argct*4]
  FOR arg, <TheArgs>
push arg
  ENDM
  add esp, argct*4
  xchg esp, [esp]
ENDM


Usage:
REPEAT 5 ; per argct 10 bytes overhead; then x per macro call: 10+5*(27+17)=230
Store2Mem MyDword1, ecx, edx, esi, MyDword2 ; 12 plus one per reg32, 6 per global var: 12+2*6+3=27
Store2Mem eax, ecx, edx, esi, edi ; 12 plus one per reg32, 6 per global var: 12+5=17
ENDM

REPEAT 5 ; per argct 10 bytes overhead; then x per macro call: 10+5*(19+14)=175
Store2Mem MyDword1, ecx ; 12 plus one per reg32, 6 per global var: 12+6+1=19
Store2Mem eax, ecx ; 12 plus one per reg32, 6 per global var: 12+2=14
ENDM


230 bytes for 10*Store2Mem, 5 args
175 bytes for 10*Store2Mem, 2 args

Full code attached. Its practical usage might be in writing prologues for 64-bit Masm etc...(?)
I got inspired to write this because it's difficult to have macros that accept regs and globals but don't trash any regs. And push/pop are the only mem to mem instructions that require only one register (the others being movsxx).