News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

Something I have forgotten how to do.

Started by hutch--, August 15, 2016, 01:42:32 PM

Previous topic - Next topic

hutch--

Long ago I knew how to force a nested macro to expand inline rather than storing the result before the main macro is called but I simply don't remember how its done any more.

This is the test macro.

    pt2$ MACRO var:REQ
      lea rax, var
      EXITM <rax>
    ENDM


This is what it looks like as code.

    LOCAL var1 :QWORD
    LOCAL var2 :QWORD
    LOCAL var3 :QWORD
    LOCAL var4 :QWORD
    LOCAL var5 :QWORD
    LOCAL var6 :QWORD

    invoke testit,pt2$(var1),pt2$(var2),pt2$(var3),pt2$(var4),pt2$(var5),pt2$(var6)

What I get as a disassembly is the following.

.text:000000014000100b 488D45B8                   lea rax, [rbp-0x48]
.text:000000014000100f 488D45B0                   lea rax, [rbp-0x50]
.text:0000000140001013 488D45A8                   lea rax, [rbp-0x58]
.text:0000000140001017 488D45A0                   lea rax, [rbp-0x60]
.text:000000014000101b 488D4598                   lea rax, [rbp-0x68]
.text:000000014000101f 488D4590                   lea rax, [rbp-0x70]
.text:0000000140001023 488BC8                     mov rcx, rax
.text:0000000140001026 488BD0                     mov rdx, rax
.text:0000000140001029 4C8BC0                     mov r8, rax
.text:000000014000102c 4C8BC8                     mov r9, rax
.text:000000014000102f 4889442420                 mov qword ptr [rsp+0x20], rax
.text:0000000140001034 4889442428                 mov qword ptr [rsp+0x28], rax
.text:0000000140001039 E82F000000                 call sub_14000106d

Each embedded macro is expanded before the main macro call and in this case they all resolve to RAX. There used to be a technique that would force the macro to be expanded inline instead of before the main macro. Does anyone know or remember how this is done. Vaguely I remembered that it was done with the expression operator "%" but I have not got it to work.

HSE

I don't know if you are tried something like this:

llamada macro proceso:REQ, vars:VARARG
   LOCAL cnt
      cnt = 0
      FOR item, <vars>
        lea rax, <item>
        if cnt eq 0
            mov rcx, rax
        endif
        if cnt eq 1
            mov rdx, rax
        endif
        if cnt eq 2
            mov r8, rax
        endif
        if cnt eq 3
            mov r9, rax
        endif
        if cnt eq 4
            mov qword ptr [rsp+0x20], rax
        endif
        if cnt eq 5
            mov qword ptr [rsp+0x28], rax
        endif
        cnt = cnt + 1
      ENDM
      call &proceso
endm

This machine is 32bit, I can't test: llamada testit,var1,var2,var3,var4,var5,var6

Perhaps a litlle more compact code with;llamada macro proceso:REQ, vars:VARARG
   LOCAL cnt
      cnt = 0
      FOR item, <vars>
        if cnt eq 0
            lea rcx, <item>
        endif
        if cnt eq 1
            lea rdx, <item>
        endif
        if cnt eq 2
            lea r8, <item>
        endif
        if cnt eq 3
            lea r9, <item>
        endif
        if cnt eq 4
            lea rax, <item>
            mov qword ptr [rsp+0x20], rax
        endif
        if cnt eq 5
            lea rax, <item>
            mov qword ptr [rsp+0x28], rax
        endif
        cnt = cnt + 1
      ENDM
      call &proceso
endm
Equations in Assembly: SmplMath

mabdelouahab

" Does anyone know or remember how this is done."
I don't know, But:

.invoke testit,&var1,&var2,&var3,&var4,&var5,&var6


.text:0000000140001005 sub rsp, 0x30
.text:0000000140001009 lea rcx, [rbp-8]
.text:000000014000100d lea rdx, [rbp-0x10]
.text:0000000140001011 lea r8, [rbp-0x18]
.text:0000000140001015 lea r9, [rbp-0x20]
.text:0000000140001019 lea rax, [rbp-0x28]
.text:000000014000101d mov qword ptr [rsp+0x20], rax
.text:0000000140001022 lea rax, [rbp-0x30]
.text:0000000140001026 mov qword ptr [rsp+0x28], rax
.text:000000014000102b call sub_140001000
.text:0000000140001030 add rsp, 0x30




qWord

Quote from: hutch-- on August 15, 2016, 01:42:32 PM
Long ago I knew how to force a nested macro to expand inline rather than storing the result before the main macro is called but I simply don't remember how its done any more.
The only ways I know are to enclose the arguments with angle brackets or precede the macro-name with an exclamation mark. If the arguments then are used in a FOR-loop, the calls (pt2 in above code) will be expanded successively regardless of the FOR-loop body.

invoke testit, <pt2$(var1)>,...
invoke testit, !pt2$(var1),...

MREAL macros - when you need floating point arithmetic while assembling!

hutch--

It seems that what I was after cannot be done. With this macro calling the main procedure macro,

    invoke2 MACRO FuncName:REQ,args:VARARG
      argstr equ <procedure FuncName>        ;; construct procedure call and function name
      FOR var,<args>                         ;; loop through all arguments
          argstr CATSTR argstr,<,prefix(reparg(var))>   ;; replace quotes and append p@arg
      ENDM
      argstr                                 ;; write the invoke macro
    ENDM

The macro "ptr$()" is already expanded before it gets to the invoke2 macro. The expansion is written before the code generated by the invoke2 macro and it has the same problem in multiple addresses resolving to RAX. It means you can use it once but the second will overwrite the first.

jj2007

We had a heated debated about this here. It is one of the worst habits of the Masm family. Consider this:

include \masm32\MasmBasic\MasmBasic.inc      ; download
  Init
  Let esi="Just a stupid little test"
  .While Instr_(esi, " ")
      Let esi=Left$(esi, edx-1)+Mid$(esi, edx+1)  ; Instr_ returns 1-based position in edx
  .Endw
  Inkey "Result: ", esi
EndOfCode


Won't work, and neither brackets nor ! help. But RichMasm will bark at you if you try using a macro with .While or .elseif (and if you ignore the warning, there will be e.g. a MsgBox saying HeapAlloc failure) 8)

Workaround is simple, though:
  .While 1
.Break .if !Instr_(esi, " ")
Let esi=Left$(esi, edx-1)+Mid$(esi, edx+1)
  .Endw


But I suspect it's not helpful for your case, Hutch. What exactly do you want to achieve?

hutch--

My problem is the underlying macro engine expands nested macros BEFORE you get to the main macro which renders a macro that returns RAX as only able to be used once. It looks like the only reliable way to get around the problem is to load each result into a LOCAL then call the macro with the LOCAL and any other address in their normal order.

What I am looking for here is consistency for a particular coding style and the lack of capacity to do inline expansion had limited what facilities can be provided.

sinsi

Maybe IF1 and IF2? ML is now multi-pass but IF2 still seems to be there.
Not sure what you are trying to do here, any chance of a program flow?

jj2007

#8
Quote from: sinsi on August 16, 2016, 07:01:27 PM
Maybe IF1 and IF2? ML is now multi-pass but IF2 still seems to be there.

Bad luck: error A2061: [ELSE]IF2/.ERR2 not allowed : single-pass assembler

Here is a simple testbed:include \masm32\include\masm32rt.inc ; plain Masm32 for the fans of pure assembler

.code
MyTest proc a0, a1, a2, a3, a4
  print a0, 13, 10
  print a1, 13, 10
  print a2, 13, 10
  print a3, 13, 10
  print a4, 13, 10
  ret
MyTest endp

v0 db "This is Variable 0", 0
v1 db "This is Variable 1", 0
v2 db "This is Variable 2", 0
v3 db "This is Variable 3", 0
v4 db "This is Variable 4", 0

pt MACRO arg
  if2
  lea eax, arg
  EXITM <eax>
  endif
endm
int 3
start: invoke MyTest, pt(v0), pt(v1), pt(v2), pt(v3), pt(v4)
exit
end start


And even that one fails miserably because of early expansion:004010C1             Ú.  8D05 61104000      lea eax, [401061]                 ; ASCII "This is Variable 0"
004010C7             ³.  8D05 74104000      lea eax, [401074]                 ; ASCII "This is Variable 1"
004010CD             ³.  8D05 87104000      lea eax, [401087]                 ; ASCII "This is Variable 2"
004010D3             ³.  8D05 9A104000      lea eax, [40109A]                 ; ASCII "This is Variable 3"
004010D9             ³.  8D05 AD104000      lea eax, [4010AD]                 ; ASCII "This is Variable 4"
<ModuleEntryPoint>   ³.  50                 push eax
004010E0             ³.  50                 push eax
004010E1             ³.  50                 push eax
004010E2             ³.  50                 push eax
004010E3             ³.  50                 push eax                          ; ÚArg1 => ASCII "This is Variable 4"
004010E4             ³.  E8 17FFFFFF        call 00401000                     ; ÀNewWin32.00401000


The solution is elsewhere - not in a macro for each parameter but rather in an invoke macro that handles them correctly:

include \Masm32\MasmBasic\Res\JBasic.inc      ; included in MasmBasic, see menu File/New Masm source
.code
MyTest proc a0:DefSize, a1:DefSize, a2:DefSize, a3:DefSize, a4:DefSize
  PrintLine a0
  PrintLine a1
  PrintLine a2
  PrintLine a3
  PrintLine a4
  ret
MyTest endp

v0      db "This is Variable 0", 0
v1      db "This is Variable 1", 0
v2      db "This is Variable 2", 0
v3      db "This is Variable 3", 0
v4      db "This is Variable 4", 0
j@start
  jinvoke MyTest, addr v0, addr v1, addr v2, addr v3, addr v4
  Inkey Chr$("This code was assembled with ", @AsmUsed$(1), " in ", jbit$, "-bit format")

j@end


53                                | push rbx                         | three dummy pushes to align the stack
53                                | push rbx                         |
53                                | push rbx                         |
4C 8D 15 D9 FF FF FF              | lea r10, qword ptr ds:[1400012C4 | 1400012C4:"This is Variable 4"
41 52                             | push r10                         |
4C 8D 0D BD FF FF FF              | lea r9, qword ptr ds:[1400012B1] | 1400012B1:"This is Variable 3"
41 51                             | push r9                          |
4C 8D 05 A1 FF FF FF              | lea r8, qword ptr ds:[14000129E] | 14000129E:"This is Variable 2"
41 50                             | push r8                          |
48 8D 15 85 FF FF FF              | lea rdx, qword ptr ds:[14000128B | 14000128B:"This is Variable 1"
52                                | push rdx                         |
48 8D 0D 6A FF FF FF              | lea rcx, qword ptr ds:[140001278 | 140001278:"This is Variable 0"
51                                | push rcx                         |
E8 ED FC FF FF                    | call skeldualconsole64.140001001 |
48 83 C4 40                       | add rsp, 40                      |


If it's important, I can check if using & instead of addr is difficult to implement.

EDIT: Done: MB 17 August takes on a fairly flexible approach - 4 pointers:
  jinvoke MyTest, &v0, *v1, addr v2, offset v3
:biggrin:

mabdelouahab

#9
 hutch, Try this might work.

pt2$ MACRO var:REQ
     EXITM <pt2__(var)>
    ENDM
pt2__ MACRO var:REQ
      lea rax, var
      EXITM <rax>
    ENDM
hutchInvoke macro fname:req,ProcArg:vararg
   c__ =0
   FOR argN,<ProcArg>
      c__=c__+1
      IF c__ eq 1
          mov rcx,argN
      ELSEIF  c__ eq 2
          mov rdx,argN
      ELSEIF  c__ eq 3
          mov r8,argN
      ELSEIF  c__ eq 4
          mov r9,argN
      ELSE
          mov [rsp+400],argN
      ENDIF
   ENDM
   call fname
endm


QuoteLOCAL var1 :QWORD
    LOCAL var2 :QWORD
    LOCAL var3 :QWORD
    LOCAL var4 :QWORD
    LOCAL var5 :QWORD
    LOCAL var6 :QWORD

       hutchInvoke testit,pt2$(var1),pt2$(var2),pt2$(var3),pt2$(var4),pt2$(var5)


.text:0000000140001005    lea rax, [rbp-8]
.text:0000000140001009    mov rcx, rax
.text:000000014000100c    lea rax, [rbp-0x10]
.text:0000000140001010    mov rdx, rax
.text:0000000140001013    lea rax, [rbp-0x18]
.text:0000000140001017    mov r8, rax
.text:000000014000101a    lea rax, [rbp-0x20]
.text:000000014000101e    mov r9, rax
.text:0000000140001021    lea rax, [rbp-0x28]
.text:0000000140001025    mov qword ptr [arg_5], rax
.text:000000014000102d    call sub_140001000



hutch--

Hi,

Thanks for the suggestion, I have one version that works OK at the moment but suffers a few restrictions and I am well on the way to the next version that does not use a FOR or WHILE loop so there is more you can do with it. The next version supports ADDR and that part of it is already working OK, I just have to finish off the arg5 and higher written to the stack in sizes other that QWORD.

When I get this one up and going I will have a play with your suggestion as I think I know how it works.