News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

Multiple instructions on one line macro.

Started by hutch--, October 09, 2016, 12:06:37 PM

Previous topic - Next topic

hutch--

Here is a simple test that shows that it works OK calling a procedure which can process inline content. It seems that its only a problem with inline macros that are embedded in a parent macro.

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

    include \masm32\include64\masm64rt.inc

    .code

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

entry_point proc

    LOCAL var   :QWORD

    mi \
    mov rax, 100 : add rax, 100 : shl rax, 1 : mov var, rax : fn showit,var
    conout str$(var)," inline macro",lf

    waitkey

    invoke ExitProcess,0

    ret

entry_point endp

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

showit proc var:QWORD

    conout str$(var)," showit proc",lf

    ret

showit endp

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

    end

jj2007

Quote from: hutch-- on October 10, 2016, 08:55:06 PMIt seems that its only a problem with inline macros that are embedded in a parent macro.

More precisely, function() macros, i.e. those that return something, get expanded in the moment when they are being passed as an argument, like e.g. Hex$() below. But even those can be kept on one line, provided they are <passed as strings>:
include \masm32\MasmBasic\MasmBasic.inc
      Init
      Dim PtrSSE() As DWORD
      For_ ct=0 To 99      ; 100 aligned pointers
            mcs Alloc16 Rand(10000) : mov PtrSSE(ct), eax : <Print Hex$(al), " ">
      Next
EndOfCode


Question is if users read the manual:

      - mcs allows to put several commands on one line, separated by a colon as in Basic
      - if macros depend on previous commands, like e.g. Str$(...), <put them in brackets>

hutch--

Problem is it does not work.

    LOCAL var   :QWORD

    mi \
    mov rax, 100 : add rax, 100 : shl rax, 1 : mov var, rax : fn showit,var
    conout str$(var)," inline macro",lf

    xor rax, rax
    mi \
    mov rax, 100 : <conout str$(rax)," this fails",lf,lf>

    waitkey

    invoke ExitProcess,0

    ret

Output is as follows.

400 showit proc
400 inline macro
0 this fails


It also fails on every variation of angle brackets.

Here is a further variation that still fails, it appears that ML64 always expands embedded macros before their parent macro.

    mtst MACRO arg:VARARG
    % echo arg
    ENDM
.............
    mtst mov rax, 100 : <conout str$(rax)>
    mtst <mov rax, 100 : conout str$(rax)>
    mtst mov rax, 100 : conout <str$(rax)>

All fail due to prior expansion.


jj2007

#18
I've made some ugly and complicated tests with your mi macro, and it seems the FORC is really the problem. Mine works fine with <conout str$(rax)>, apparently because <args> gets expanded only step by step, i.e. in @SubStr ::)

mcs MACRO args:VARARG   ; part of the MasmBasic package
Local isL, isR, isQ
  isR=0
  While 1
isL=isR+1
isQ INSTR isL, <args>, <"> ; xx : Print "Hello: a problem" : xx
isR INSTR isL, <args>, <:>
if isQ and isQ lt isR
isQ INSTR isQ+1, <args>, <">
isR INSTR isQ, <args>, <:>
endif
if isR
@SubStr(<args>, isL, isR-isL)
else
@SubStr(<args>, isL)
EXITM
endif
  ENDM
ENDM


Of course, you are welcome to use my code in your include files. Over time, I have taken so many ideas from your includes that it is a pleasure to give something back :icon14:

qWord

An option is to delay the expansion by using wrappers for the function like macros. The result of an function like macro is no further expanded.

include \masm32\include\masm32rt.inc


mi TEXTEQU <__delay()>

__delay_expansion = 0

__delay macro
__delay_expansion = 1
EXITM <_mi>
endm

_mi macro args:VARARG

_mi_cmd TEXTEQU <args>
_mi_size SIZESTR <args>
_mi_pos = 1
_mi_colon_pos INSTR <args>,<:>
WHILE _mi_colon_pos NE 0
_mi_cmd SUBSTR <args>,_mi_pos, _mi_colon_pos - _mi_pos

__delay_expansion = 0
% &_mi_cmd
__delay_expansion = 1

_mi_pos = _mi_colon_pos + 1
_mi_colon_pos INSTR _mi_pos,<args>,<:>
ENDM
IF _mi_pos LE _mi_size
_mi_cmd SUBSTR <args>,_mi_pos
__delay_expansion = 0
% &_mi_cmd
__delay_expansion = 1
ENDIF

__delay_expansion = 0
endm

myStr$ macro arg
IFE __delay_expansion
EXITM str$(arg)
ELSE
EXITM <str$(arg)>
ENDIF
endm

myCmd$ macro arg
IFE __delay_expansion
EXITM cmd$(arg)
ELSE
EXITM <cmd$(arg)>
ENDIF
endm


.code
main proc

mi mov eax,123 : fn MessageBox,0,myStr$(eax),0,0 : mov eax, myCmd$(0)

exit
main endp
end main


pro: no further syntax elements needed to control expansion
con: all function likes macros must be changed
MREAL macros - when you need floating point arithmetic while assembling!

hutch--

Thanks guys, you both have some interesting ideas, the problem for what my target was is notational simplicity, it was originally targeted at purely mnemonics where you could bundle a sequence of uninteresting mnemonics onto one line to reduce clutter something like this.

NOSTACKFRAME
.szLen proc
  mi mov rax, rcx : sub rax, 1
  lbl:
  mi add rax, 1 : movzx r10, BYTE PTR [rax] : test r10, r10 : jnz lbl
  lbl1:
  mi sub rax, rcx : ret
.szLen endp
STACKFRAME


Here is a variation.

  mi NOSTACKFRAME : .szLen proc : mov rax, rcx : sub rax, 1
  lbl:
  mi add rax, 1 : movzx r10, BYTE PTR [rax] : test r10, r10 : jnz lbl
  lbl1:
  mi sub rax, rcx : ret : .szLen endp : STACKFRAME



It will also handle function/API calls and simple non dependent macro statements but will not handle result dependent embedded macros.

Calling a procedure directly works fine but the general approach is clunky and fails in terms of simplicity.

xor rax, rax
mi mov rax, 100 : fn .str$,rax : conout rax," this works as a proc",lf


hutch--

I slightly tweaked the original macro to take different delimiters then made a set of wrappers with multiple variations.

    .mi MACRO args:VARARG           ;; multiple instructions
      .multiple_instruction :,args
    ENDM

    .ma MACRO args:VARARG           ;; multiple arguments
      .multiple_instruction #,args
    ENDM

    .mn MACRO args:VARARG           ;; alternate multiple arguments
      .multiple_instruction ~,args
    ENDM

    .m MACRO args:VARARG            ;; alternate multiple arguments
      .multiple_instruction ^,args
    ENDM


Using any of the alternatives allows you to use labels so you can produce nightmares like this.

; NOSTACKFRAME
;
; szLen proc
;
;   ; rcx = address of string
;
;     mov rax, rcx
;     sub rax, 1
;   lbl:
;   REPEAT 3
;     add rax, 1
;     movzx r10, BYTE PTR [rax]
;     test r10, r10
;     jz lbl1
;   ENDM
;
;     add rax, 1
;     movzx r10, BYTE PTR [rax]
;     test r10, r10
;     jnz lbl
;
;   lbl1:
;     sub rax, rcx
;
;     ret
;
; szLen endp
;
; STACKFRAME

  .ma NOSTACKFRAME # .szLen proc # mov rax,rcx # sub rax,1 # lbl:
  REPEAT 3
  .ma add rax,1 # movzx r10, BYTE PTR [rax] # test r10,r10 # jz lbl1
  ENDM
  .ma add rax,1 # movzx r10, BYTE PTR [rax] # test r10,r10
  .ma jnz lbl # lbl1: # sub rax,rcx # ret # .szLen endp # STACKFRAME


jj2007

Quote from: hutch-- on October 11, 2016, 11:18:03 AMUsing any of the alternatives allows you to use labels so you can produce nightmares like this.

Different, but on par with slickhuh.asm :badgrin: