The MASM Forum

64 bit assembler => 64 bit assembler. Conceptual Issues => Topic started by: hutch-- on October 09, 2016, 12:06:37 PM

Title: Multiple instructions on one line macro.
Post by: hutch-- on October 09, 2016, 12:06:37 PM
I am not sure its useful but it does work OK.

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

    include \masm32\include64\masm64rt.inc

    mi MACRO a1,a2,a3,a4,a5,a6,a7,a8,a9,a0
      a1
      a2
      a3
      a4
      a5
      a6
      a7
      a8
      a9
      a0
    ENDM

    close MACRO exitcode:=<0>
      mov rcx, exitcode
      call ExitProcess
    ENDM

    .code

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

entry_point proc

    mi <mov rax, 1>,<add rax, 2>,<sub rax, 3>,waitkey,close,ret

entry_point endp

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

    end
Title: Re: Multiple instructions on one line macro.
Post by: jj2007 on October 09, 2016, 12:50:41 PM
Basic style:

include \Masm32\MasmBasic\Res\JBasic.inc      ; download (http://masm32.com/board/index.php?topic=94.0)

mijj MACRO args:VARARG
Local isL, isR, isFlag, argsLen
  isR=0
  isFlag=1
  While isFlag
      isL=isR+1
      isR INSTR isL, <args>, <:>
      ife isR
            isR=@SizeStr(<args>)+1
            isFlag=0
      endif
      @SubStr(<args>, isL, isR-isL)
  ENDM
ENDM
Init
  PrintLine Chr$("This code was assembled with ", @AsmUsed$(1), " in ", jbit$, "-bit format")
  mijj mov rax, 100:add rax, 30:sub rax, 7: <Inkey Str$("rax=%i", rax)>
EndOfCode


Output:
This code was assembled with ml64 in 64-bit format
rax=123


Problem with both versions is the premature expansion of macros - therefore <brackets> around the macros.
Title: Re: Multiple instructions on one line macro.
Post by: Mikl__ on October 09, 2016, 01:09:59 PM
Hi, hutch--!
This is macro from the article "Как писать на MASM в строчку (http://arhive.xaker.name/threads/10430/?q=)" (How to write on MASM in single line) of Svet(R)off​ 2001 from site ASSEMBLER.RU
Google translating article into English (https://translate.google.ru/translate?sl=ru&tl=en&js=y&prev=_t&hl=ru&ie=UTF-8&u=http%3A%2F%2Farhive.xaker.name%2Fthreads%2F10430%2F%3Fq%3D&edit-text=)
(http://www.cyberforum.ru/images/smilies/smile3.gif)
Title: Re: Multiple instructions on one line macro.
Post by: hutch-- on October 09, 2016, 03:56:56 PM
With a little more effort, this one works OK.
[[t]
; *************************************************************************
; -------------------------------------------------------------------------

    multiple_instruction MACRO args:VARARG
      LOCAL arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg0,cntr,flag

      cntr = 1                      ;; counter determined which buffer is being written to
      flag = 0                      ;; flag starts as clear

      arg1 equ <>
      arg2 equ <>
      arg3 equ <>
      arg4 equ <>
      arg5 equ <>
      arg6 equ <>
      arg7 equ <>
      arg8 equ <>
      arg9 equ <>
      arg0 equ <>

    ;; ---------------------------------
      FORC char,<args>
        IFIDN <char>,<:>            ;; test if char is the ":" delimiter
          IF flag eq 0
            cntr = cntr + 1         ;; only increment the counter if the flag is clear
          ENDIF
          flag = 1                  ;; set the flag so you only get 1 increment of cntr
          goto over
        ENDIF
        flag = 0                    ;; clear the flag when the next char is not a space

        IF cntr eq 1
          arg1 CATSTR arg1,<char>   ;;
        ELSEIF cntr eq 2
          arg2 CATSTR arg2,<char>   ;;
        ELSEIF cntr eq 3
          arg3 CATSTR arg3,<char>   ;;
        ELSEIF cntr eq 4
          arg4 CATSTR arg4,<char>   ;;
        ELSEIF cntr eq 5
          arg5 CATSTR arg5,<char>   ;;
        ELSEIF cntr eq 6
          arg6 CATSTR arg6,<char>   ;;
        ELSEIF cntr eq 7
          arg7 CATSTR arg7,<char>   ;;
        ELSEIF cntr eq 8
          arg8 CATSTR arg8,<char>   ;;
        ELSEIF cntr eq 9
          arg9 CATSTR arg9,<char>   ;;
        ELSEIF cntr eq 10
          arg0 CATSTR arg0,<char>   ;;
        ELSEIF cntr eq 11
          % echo *********************************************
          % echo ERROR : too many arguments, 10 argument limit
          % echo *********************************************
          .err <too many arguments>
          goto over
        ENDIF
      :over
      ENDM
    ;; ---------------------------------

      arg1
      arg2
      arg3
      arg4
      arg5
      arg6
      arg7
      arg8
      arg9
      arg0

    ENDM

; --------------------------------

    mi MACRO args:VARARG
      multiple_instruction args
    ENDM

; -------------------------------------------------------------------------
; *************************************************************************
[/tt]

Looks like this in use.

    mi \
    mov rax,1 : add rax,1 : sub rax,3 : xor rax,rax
    mi waitkey : fn ExitProcess,0 : ret
Title: Re: Multiple instructions on one line macro.
Post by: jj2007 on October 09, 2016, 07:46:20 PM
Quote from: Mikl__ on October 09, 2016, 01:09:59 PMGoogle translating article into English (https://translate.google.ru/translate?sl=ru&tl=en&js=y&prev=_t&hl=ru&ie=UTF-8&u=http%3A%2F%2Farhive.xaker.name%2Fthreads%2F10430%2F%3Fq%3D&edit-text=)

QuoteAssembler - the greatest language, the king of languages, virtually devoid of shortcomings worthy of people's love and worship

Absolutely :t

  mi   mov rax, 100:add rax, 30:sub rax, 7: <Print Str$("version Hutch...\t rax=%i\n", rax)>
  mijj mov rax, 100:add rax, 30:sub rax, 7: <Print Str$("version JJ...   \t rax=%i\n", rax)>


Output:
version Hutch...         rax=1
version JJ...            rax=123

(expected: 123; assembled with ml64 in 64-bit format)

Can't see, though, why yours shouldn't work - it looks ok ::)

A look in the *.lst file:

FORC char,<mov rax,100:add rax,30:sub rax,7: Print offset s$buffer>

??0104 INSTR ??0100, <mov rax,100:add rax,30:sub rax,7: Print Str$("version JJ:    \t rax=%i\n", rax)>, <"> ;

So the FORC version gets expanded (offset...), the other one is still Str$(...).

Attached a new version, baptised mulComs, builds with ML/ML64, HJWasm and AsmC.

This works now:
  mi mov rax, 100:add rax, 30:sub rax, 7: <Print Str$("version Hutch...\t rax=%i\n", rax)>
  mulComs mov rax, 100:add rax, 30:sub rax, 7: <Print Str$("version JJ:     \t rax=%i\n", rax)>


P.S.: Try version Hutch: instead of version Hutch...
Title: Re: Multiple instructions on one line macro.
Post by: hutch-- on October 09, 2016, 08:55:11 PM
I have tracked down at this end what is happening and I have seen it before, the "conout" macro that I test with is being filled out before the "mi" macro is being executed so it misses the result of previous instructions.
Title: Re: Multiple instructions on one line macro.
Post by: jj2007 on October 09, 2016, 09:00:19 PM
Indeed:
Quote from: jj2007 on October 09, 2016, 12:50:41 PM
Problem with both versions is the premature expansion of macros - therefore <brackets> around the macros.

Question is why your mi doesn't work with my Print Str$(), despite the brackets, while mine does. My guess is that the FORC is the problem.
Title: Re: Multiple instructions on one line macro.
Post by: hutch-- on October 09, 2016, 09:33:07 PM
This shows the problem.

Arg count = 4
mov r15,100
add r15,30
sub r15,7
add r15,r15

Arg count = 3
conout "Result = ",??0025,??0001
waitkey
fn __imp_ExitProcess,0


From being in the FORC loop, macros and other operators are being expanded before the parent macro is run.
Title: Re: Multiple instructions on one line macro.
Post by: jj2007 on October 09, 2016, 10:13:43 PM
It seems so. My version avoids CATSTR, and therefore expansion:

mulComs MACRO args:VARARG
Local isL, isR, isFlag, argsLen, isQ
  isR=0
  isFlag=1
  While isFlag
isL=isR+1
isQ INSTR isL, <args>, <"> ; xxx : Print "Hello: a problem" : xxx
isR INSTR isL, <args>, <:>
if isQ and isQ lt isR
isQ INSTR isQ+1, <args>, <">
isR INSTR isQ, <args>, <:>
endif
ife isR
isR=@SizeStr(<args>)+1
isFlag=0
endif

@SubStr(<args>, isL, isR-isL)
  ENDM
ENDM

Title: Re: Multiple instructions on one line macro.
Post by: hutch-- on October 09, 2016, 11:17:42 PM
I put this line at the front of the main macro before either the FORC loop and any instance of CATSTR and still got this.

      % echo Args = args

and get this output,

Args = conout "Result = ",??0025,??0001 : waitkey : fn __imp_ExitProcess,0

It does not appear to be a problem with macros that have static parameter or an API call but it prevents an embedded macro from getting results in the same calling macro before it.
Title: Re: Multiple instructions on one line macro.
Post by: hutch-- on October 10, 2016, 01:39:34 AM
After wasting some hours, I cannot find a format of passing a macro inside another macro that does not expand the embedded macro unless it is contained in a string so I have put it aside at the moment. I can get this to work correctly as it isolates macro expansion by placing such a macro on its own line. This restricts the use to non accumulated arguments.

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

    include \masm32\include64\masm64rt.inc

    .code

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

entry_point proc

    mi mov rax, 100 : sub rax, 75 : shl rax, 2
    conout "Result = ",str$(rax),lf
    mi waitkey : invoke ExitProcess,0 : ret

entry_point endp

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

    end

May have a play with it later but have more important things to do.
Title: Re: Multiple instructions on one line macro.
Post by: Mikl__ on October 10, 2016, 01:47:44 PM
file which is mentioned in the article of Svet(R)off "How to write on MASM in single line"
QuoteWishes to enjoy a new kind of source code and learn some style ideas can click here
Wow! It is 400-th message on the forum, it is necessary to celebrate it - I will go to pour himself some vodka from a samovar, feed the bear and play the balalaika (http://www.kolobok.us/smiles/personal/russian.gif)
Title: Re: Multiple instructions on one line macro.
Post by: hutch-- on October 10, 2016, 06:43:50 PM
Mikl__,  :t
Title: Re: Multiple instructions on one line macro.
Post by: jj2007 on October 10, 2016, 07:35:03 PM
Quote from: Mikl__ on October 10, 2016, 01:47:44 PMfile which is mentioned in the article of Svet(R)off "How to write on MASM in single line"

Wow, he has a multiple instructions macro called "@", and puts the comments in front:
List_DX_Modes PROC
@<;Clear variables>,<mov modes_buffer,0>,<mov modes_number,0>
@<;Create list modes buffer>,<invoke GlobalAlloc,GPTR,MODES_BUFFER_SIZE>,<or eax,eax>,<jz bad>,<mov modes_buffer,eax>
@<;Create a DirectDraw object>,<invoke DirectDrawCreate,NULL,offset(lpDD),NULL>,<cmp eax,DD_OK>,<jne bad>


Reminds me of the old, old times when I had to keep a certain fat BASIC source below 16384 lines, for technical reasons :icon_mrgreen:

In the meantime, I added my version as mcs (=multiple commands) to the MB package, for use with (32-bit) MasmBasic and the dual 32-/64-bit JBasic variant; tested with all "our" assemblers here. More here (http://masm32.com/board/index.php?topic=94.msg60949#msg60949).
Title: Re: Multiple instructions on one line macro.
Post by: hutch-- on October 10, 2016, 08:41:01 PM
The problem is for the design I was after that I wanted to keep it simple, just have a " : " between each component and for static content it works fine, what beats embedded dependent content is the characteristic of the macro engine to expand nested macros before the parent macro is called which defeats sequential dependent components. The form I have got is usable and simple but dependent procedure calls or macros must be put on a separate line to get the previous data.

mi \
mov rax, 100 : add rax, 300 : shl rax, 1

This works fine and is simple notation, as soon as it gets cluttered with notation, it loses any advantage it has.
Title: Re: Multiple instructions on one line macro.
Post by: hutch-- on October 10, 2016, 08:55:06 PM
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
Title: Re: Multiple instructions on one line macro.
Post by: jj2007 on October 10, 2016, 08:57:34 PM
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>
Title: Re: Multiple instructions on one line macro.
Post by: hutch-- on October 10, 2016, 10:23:43 PM
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.

Title: Re: Multiple instructions on one line macro.
Post by: jj2007 on October 10, 2016, 11:42:47 PM
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:
Title: Re: Multiple instructions on one line macro.
Post by: qWord on October 11, 2016, 04:03:36 AM
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
Title: Re: Multiple instructions on one line macro.
Post by: hutch-- on October 11, 2016, 08:46:36 AM
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

Title: Re: Multiple instructions on one line macro.
Post by: hutch-- on October 11, 2016, 11:18:03 AM
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

Title: Re: Multiple instructions on one line macro.
Post by: jj2007 on October 11, 2016, 05:12:14 PM
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: