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 it's needed, to allocate local stack variables with macrosLocalVar, ClearLocalVar
and to save used registers with macroUses
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).
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
,
LocalVar
will assign %LvSize_MyFn %SETA 8+16
.
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),
stdcal64 HEAD
; 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
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
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.
%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.' %ENDIF %Uses %SET2 %%Uses_%^PROC reg %FOR %* %IF REGTYPE# %reg = 'Q' PUSHQ %reg %Uses %SET %reg,%Uses %ELSE %ERROR ID=5927,'Macro "Uses" does not support operand "%reg".' %ENDIF %ENDFOR reg %%Uses_%^PROC %SETX %Uses %ENDMACRO Uses
SUB RSP,%Size
to reserve room on the machine stack.
%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
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.
RSP
, cleared size is specified with "global" variable
%LvSize_CurrentProcName
.ClearLocalVar %MACRO %GlbLvSize %SET2 %%LvSize_%^PROC %IF %GlbLvSize MOV RDI,RSP MOV RCX,(%GlbLvSize) / 8 XOR RAX,RAX REP STOSQ %ENDIF %ENDMACRO ClearLocalVar
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 instructionRET
to return from the block defined withProcedure .. EndProcedure
. If premature return is required, jump to the label ofEndProcedure
statement instead.
Procedure
statement.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] %ENDWHILE %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 %ENDIF POP RBP RET 8 * (%ArgC) ENDP %ProcName, NESTINGCHECK=OFF %ENDMACRO EndProcedure
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