EuroAssembler Index Manual Download Source Macros

Sitemap Links Forum Tests Projects


This file can be included to 64bit programs written in EuroAssembler. The library contains OS-independent macroinstructions Procedure, EndProcedure, Invoke which extend generic pseudoinstructions PROC, ENDPROC, CALL.

Macroinstructions Procedure and EndProcedure hide the prologue and epilogue of standard call calling convention , where the arguments are pushed backwards and they are removed by the called procedure.

Although fastcall or System V convention are used almost exclusively in the realm of 64bit programming, more effective macros from this library can be preferred for writing private procedures, which are not supposed to be invoked from 3rd party programs or as OS callbacks. StdCall does not bother with stack alignment or shadow space. It is the job of invoked private procedure to align the stack at the moment whenever is's needed, to allocate local stack variables with macros LocalVar, ClearLocalVar and to save used registers with macro Uses on its own account.

Macroinstructions in this library comunicate at assembly time through the preprocessing %variables %LvSize_ProcName (total size of all local stack-variables), %ArgC_ProcName (number of procedure parameters) and %Uses_ProcName (the list of used calee-saved registers).
Individualization of global %variable names allows to nest procedure definitions in one another.

Implementation of standard call convention in €ASM uses formal %names for accessing Procedure parameters and local stack-memory variables.
Unlike 32bit and 16bit version, returned value of registes is not available as stack variables, because GPR are not automatically stored (there is no PUSHAQ in 64bit mode).

The following example shows the stack frame created by invoking Procedure with the name MyFn which has three parameters, uses only 3 registers RCX,RSI,RDI in its body, and allocates two local stack variables named LocV1 and LocV2 with sizes 8 and 16.
Prologue of MyFn Procedure Par1, Par2, Par2 will assign "global" %variable %ArgC_MyFn %SETA 3,
Uses RCX,RSI,RDI will assign %Uses_MyFn %SET RDI,RSI,RCX,
both macros LocalVar will assign %LvSize_MyFn %SETA 8+16.
MyFn Procedure Par1, Par2, Par3 Uses RCX,RSI,RDI ; Enumerate registers which should be preserved in MyProc invokation. LocV1 LocalVar ; Reserve local stack variable with default size 8. LocV2 LocalVar Size=16 ; Reserve another variable. ; MyProc body can use formal names Par1, Par2, Par3, LocV1, LocV2 ; to access its parameters and local stack variables by prefixing ; the formal name with %, for instance MOV RAX,[%Par1] MOV [%LocV1],RAX ; Macro EndProcedure discards local variables, restores used GPR ; and returns below Invoke, removing the pushed arguments. EndProcedure MyFn

The following diagram shows the stack frame created by invoking MyProc defined in the example above.

Macros of standardcall convention will create and update the following "global" %variables at asm-time:
%ArgC_MyProc %SETA 3 (number of arguments),
%Uses_MyProc %SET RCX,RSI,RDI (list of used callee-saved registers),
%LvSize_MyProc %SETA 8+16 (total size of local stack-variables),
%LocV1 %SET RBP-32 (1st local stack variable),
%LocV2 %SET RBP-48 (2nd local stack variable),
%Par1 %SET RBP+16 (1st parameter pushed on stack),
%Par2 %SET RBP+24 (2nd parameter pushed on stack),
%Par3 %SET RBP+32 (3rd parameter pushed on stack),
RSP after the step. <─8 bytes─> ┌─ ┌─────────┐<17. ─┐ │ 1. PUSH Par3 │ %Par3 │ │ │ 1.>├─────────┤ │ │ 2. PUSH Par2 │ %Par2 │ │ Invoke ─┤ 2.>├─────────┤ │ │ 3. PUSH Par1 │ %Par1 │ │ │ 3.>├─────────┤ │ └─4. CALL MyProc │ return │ 17. RET 3*8 │ ┌─ 4.>├─────────┤<16. │ Procedure ─┤ 5.PUSH RBP;MOV RBP,RSP│ RBP │ 16. POP RBP │ └─ 5.>├─────────┤<15. │ ┌─6. PUSH RCX │ RCX │ 15. POP RDI ├─EndProcedure │ 6.>├─────────┤<14. │ Uses ─┤ 7. PUSH RSI │ RSI │ 14. POP RSI │ │ 7.>├─────────┤<13. │ └─8. PUSH RDI │ RDI │ 13. POP RDI │ ┌─ 8.>├─────────┤<12. │ │ 9. SUB RSP,8 │ LocV1 │ 12. ADD RSP,8+16 │ │ 9.>├─────────┤ │ LocalVar ─┤ │ │ │ │ │ LocV2 │ │ │10. SUB RSP,16 │ │ │ └─ 10.>└─────────┘<11. ─┘ 11. MyProc body

stdcal64 HEAD
↑ %StdCall64
Argument values pushed on stack during Invoke are available inside the Procedure body as formal names or as generic names defined here:
; Formal %names assignment:
%Par8  %SET RBP+72
%Par7  %SET RBP+64
%Par6  %SET RBP+56
%Par5  %SET RBP+48
%Par4  %SET RBP+40
%Par3  %SET RBP+32
%Par2  %SET RBP+24
%Par1  %SET RBP+16
↑ Procedure Operand1, Operand2,,,
This macro declares 64bit StandardCall procedure prolog which, unlike ordinary pseudoinstruction PROC, accepts ordinal operands.
Macro operands will be assigned to preprocessing %variables using the operand formal name prefixed with a percent sign.
Label of Procedure statement is mandatory, it gives the procedure an invokable name.
Operand* is the list of formal names for arguments passed to the procedure. Anything pushable as QWORD can be used as Procedure operand. Number of operands is not limited.
Macroinstruction will define global symbol, assign formal operands to %variables and establish operand stack frame based on RBP.
Move Procedure Source,Destination,Size Uses RCX,RSI,RDI MOV RSI,[%Source] MOV RDI,[%Destination] MOV RCX,[%Size] REP MOVSB EndProcedure Move
Macro Procedure in the previous example will assign
%ArgC_Move %SETA 3 ; This %variable propagates to macro EndProcedure and tells how many parameters should be discarded. %Uses_Move %SET ; This %variable propagates to macro Uses and in EndProcedure it tells which registers should be restored. %LvSize_Move %SETA 0 ; This %variable propagates to macros LocalVar and ClearLocalVar, if they're used in Move Procedure. %Source %SET RBP+16 ; These are formal %variables of all operands. %Destination %SET RBP+24 %Size %SET RBP+32
Procedure %MACRO Operands ; Prologue of standard calling convention procedure.
LblCheck    %IF "%:" === ""
              %ERROR ID=5921, 'Macro "Procedure" requires a label.'
              %EXITMACRO Procedure
            %ENDIF LblCheck
%%ArgC_%:   %SETX %#
%%Uses_%:   %SETX
%%LvSize_%: %SETX 0
ArgNr       %FOR 1..%#, STEP=+1
%%%*{%ArgNr}  %SETX RBP+(8+%ArgNr*8)
            %ENDFOR ArgNr
%:::        PROC %=*, NESTINGCHECK=OFF
            PUSH RBP
            MOV RBP,RSP
          %ENDMACRO Procedure
↑ Uses Register1, Register2,,,,

Macro Uses specifies which callee-save registers does the Procedure use, so they are saved on stack here (and they will be restored in EndProcedure epilogue).
In other words, all registers which we don't wan't to get clobbered by procedure invokation should be enumerated as arguments of macro Uses.

Macro Uses should be expanded in 64bit mode only, right after the statement Procedure and before local stack variables are defined with LocalVar , see the examples above.

Registers RBP, RSP should never be enumerated here, because they are saved and restored by Procedure and EndProcedure automatically. Also register(s) which return the procedure result should not appear on Uses list of arguments.

Well behaved Procedure changes only those registers which return its result. However, this rule may be not observed from performance reason. Fastcall convention, which is also commonly used in 64bit programs, specifies that RCX,EDX,RSI,RDI,R8,R9,R10,R11 are scratch registers which may be clobbered by procedure invokation.
Register* is the list of 64bit general purpose registers. If the procedure uses registers of other types, it should save/restore them by its own.
Macro will push all enumerated registers on stack and assign their list to a "global" %variable %Uses_ProcName in reversed order. This %variable will be used by EndProcedure for restoration of callee-save registers.
Uses        %MACRO Register1,Register2,,,
              %IF "%^PROC" === ""
                %ERROR ID=5926,'Macro "Uses" can only be used within Procedure..EndProcedure block.'
%Uses         %SET2 %%Uses_%^PROC
reg           %FOR %*
                %IF REGTYPE# %reg = 'Q'
                  PUSHQ %reg
%Uses             %SET %reg,%Uses
                  %ERROR ID=5927,'Macro "Uses" does not support operand "%reg".'
              %ENDFOR reg
%%Uses_%^PROC %SETX %Uses
            %ENDMACRO Uses
↑ LocalVar Size=8
reserves and declares local memory variable with the given Size allocated on machine stack.
Plain identifier must be defined as a label of LocalVar statement. It does not need to be unique in the program because it does not declare assembly symbol. The name will be prefixed with % and used as preprocessing %variable for addressing parameters withing the procedure body.
Size=8 specifies how many bytes should be reserved for the local variable. It is rounded up to QWORD internally.
Macro will define a preprocessing %variable with the name which was defined as the label but it is now prefixed with percent sign. Then it will emit machine instruction SUB RSP,%Size to reserve room on the machine stack.
Macro LocalVar also maintains the "global" variable %LvSize_ProcName which was initialized in macro Procedure.
LocalVar %MACRO Size=8
LblCheck        %IF "%:" === ""
                  %ERROR ID=5922, 'Macro "LocalVar" requires a label.'
                  %EXITMACRO LocalVar
                %ENDIF LblCheck
SizeCheck       %IF %#
                  %ERROR ID=5923, 'Macro "LocalVar" does not expect ordinal parameters.'
                  %EXITMACRO LocalVar
                %ENDIF SizeCheck
%:              %COMMENT                          ; This makes the label of macro invocation void,
                %ENDCOMMENT %:                    ;  so it does not declare a symbol.
%ThisLvSize     %SETA (%Size + 7) & ~7            ; Round up the required size to QWORD.
%GlbLvSize      %SET2 %%LvSize_%^PROC+%ThisLvSize ; Accumulate total size of all LocalVar.
%%LvSize_%^PROC %SETX %GlbLvSize                  ; Update the "global" variable.
                SUB RSP, %ThisLvSize              ; Allocate room on stack.
%ThisUses       %SET2 %%Uses_%^PROC               ; List of registers enumerated by Uses.
%ThisLen        %SETL %ThisUses                   ; Number of registers pushed by Uses.
%%%:            %SETX RBP-8*%ThisLen-(%GlbLvSize) ; Assign formal %name to the id specified with LocalVar label.
            %ENDMACRO LocalVar
↑ ClearLocalVar
This macro zeroes all variables on stack previously declared with LocalVar. ClearLocalVar should be expanded right after LocalVar declarations, before any stack operations are made.
We could as well decide to initialize each local variable individually, in this case the macro ClearLocalVar will not be used in the Procedure body at all.
Macro does not use explicit parameters. Pointer to the cleared memory is specified with RSP, cleared size is specified with "global" variable %LvSize_CurrentProcName.
RDI= RSP + %LvSize_CurrentProcName
ClearLocalVar %MACRO
%GlbLvSize %SET2 %%LvSize_%^PROC
           %IF %GlbLvSize
             MOV RDI,RSP
             MOV RCX,(%GlbLvSize) / 8
             XOR RAX,RAX
             REP STOSQ
         %ENDMACRO ClearLocalVar
↑ EndProcedure ProcName

Macro EndProcedure terminates context of the previously opened Procedure . This epilogue of StdCall convention will discard local variables defined with LocalVar (using machine instruction MOV RSP,RBP, restore used GP registers declared with macro Uses and then return to the parent code which the Procedure was Invoked from (using RET 8*%ArgC_ProcName).
Operands pushed on stack in the Invoke statement will be discarded here by this EndProcedure macro.
Registers not saved by Uses and CPU flags are not preserved, EndProcedure returns with the same flag values which were set at the EndProcedure entry.

Programmer should never use explicit machine instruction RET to return from the block defined with Procedure .. EndProcedure . If premature return is required, jump to the label of EndProcedure statement instead.
ProcName This macroinstruction requires exactly one operand which is identical with the label of previous corresponding Procedure statement.
Stack frame is released, current process returns below Invoke statement.
EndProcedure %MACRO ProcName
OpCheck    %IF "%ProcName" === ""
             %ERROR ID=5924, 'Macro "EndProcedure" requires one operand.'
             %EXITMACRO EndProcedure
           %ENDIF OpCheck
%NameStrip %SET %ProcName
           %WHILE "%NameStrip[%&]" == ":" ; Get rid of trailing colon(s).
             %NameStrip %SET %NameStrip[1..%&-1]
%ArgC      %SET2 %%ArgC_%NameStrip
NestCheck  %IF "%ArgC" === ""
             %ERROR ID=5925, '"%ProcName Procedure" statement missing.'
             %EXITMACRO EndProcedure
           %ENDIF NestCheck
%GlbLvSize %SET2 %%LvSize_%NameStrip
           ADD RSP,%GlbLvSize
%Uses      %SET2 %%Uses_%NameStrip
           %IF "%Uses" !=== ""
             POP %Uses
           POP RBP
           RET 8 * (%ArgC)
           ENDP %ProcName, NESTINGCHECK=OFF
         %ENDMACRO EndProcedure
↑ Invoke ProcName, Arg1, Arg2, ...
Invoke is a replacement of standard CALL instruction which can pass parameters to the Procedure. Arguments are pushed on stack as QWORDs, starting from the last. The procedure is then called and it is responsible for removing pushed arguments from stack.
It is necessary to Invoke a procedure with exactly the same number of arguments which were declared by the Procedure macro.
If any argument represents a 64bit pointer or SIMD register, it should be loaded to a temporary register and this register used as Invoke's argument, for instance
LEA RDX,[SomePointer] MOVQ RCX,XMM1 Invoke MyStdcallProc, RDX, RCX
ProcName The first operand is the name of invoked procedure.
Arg* is a list of Procedure arguments. It can be anything pushable as QWORD.
The returned registers and flags depend on the invoked Procedure.
Invoke %MACRO ProcName, Arguments
ArgCheck %IF "%ProcName" === ""
           %ERROR ID=5926, 'Macro "Invoke" requires the name of called Procedure.'
           %EXITMACRO Invoke
         %ENDIF ArgCheck
ArgNr    %FOR %#..2, STEP= -1
           PUSHQ %*{%ArgNr}
         %ENDFOR ArgNr
         CALL %ProcName
       %ENDMACRO Invoke
  ENDHEAD stdcal64

▲Back to the top▲