This file can be included to 16bit programs written in EuroAssembler.
The library contains OS-independent macroinstructions which extend generic pseudoinstruction
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.
Macroinstructions in this library comunicate with one another
at assembly time using the preprocessing %variables
%LocalFrameSize
and %NrOfArg_ProcedureName
.
doscall HEAD
Implementation of 16bit standard call convention in €ASM uses formal %names for accessing Procedure parameters, local stack-memory variables and all eight general-purpose registers saved on stack.
Following example shows the stack frame created by invoking Procedure with the name
Function which has three parameters and uses two local stack variables
with sizes 2 and 4. Prologue of Function Procedure Par1, Par2, Par3
will assign "global" %variables
%NrOfArg_Function %SETA 3
and %LocalFrameSize %SETA 2+4
, they will be used by epilogue in EndProcedure Function
during
Invoke Function, Param1, Param2, Param3
.
; Formal %names assignment: %Par4 %SET BP+24 %Par3 %SET BP+22 %Par2 %SET BP+20 %Par1 %SET BP+18 %ReturnAX %SET BP+14 %ReturnCX %SET BP+12 %ReturnDX %SET BP+10 %ReturnBX %SET BP+08 %ReturnSP %SET BP+06 %ReturnBP %SET BP+04 %ReturnSI %SET BP+02 %ReturnDI %SET BP+00
PUSHAW
and
MOV BP,SP
as the procedure prolog.EndProcedure Move
.
%LocalFrameSize %SETA 0 ; This %variable propagates to macro LocalVar, if used in Move Procedure
.
%Source %SET BP+18 ; These are formal %variables of all operands.
%Destination %SET BP+20
%Size %SET BP+22Procedure %MACRO Operands ; Prologue of standard calling convention procedure. LblCheck %IF "%:" == "" %ERROR ID=5921, 'Macro "Procedure" requires a label.' %EXITMACRO Procedure %ENDIF LblCheck %%NrOfArg_%: %SETX %# %LocalFrameSize %SETA 0 ArgNr %FOR 1..%#, STEP=+1 %%%*{%ArgNr} %SETX BP+(16+%ArgNr<<1) ; >> %ENDFOR ArgNr %::: PROC %=*, NESTINGCHECK=OFF ; Open the namespace. PUSHAW MOV BP,SP %ENDMACRO Procedure
BP-2
Block LocalVar Size=1K ; %Block is now BP-1026
.
ClearLocalVar ; Fill Block and BlockSize with 0.
MOV [%BlockSize],1K, DATA=WORD
LEA DI,[%Block] ; DI is now offset of Block in segment SS.
...
EndProcedure ProcNameSUB SP,%Size
to reserve room on the machine stack.
%LocalFrameSize
which was initialized in macro Procedure
.LocalVar %MACRO Size=2 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. %LocalVarSize %SETA (%Size + 1) & ~1 ; Round up to WORD. %LocalFrameSize %SETA %LocalFrameSize + %LocalVarSize SUB SP, %LocalVarSize %%%: %SETX BP-%LocalFrameSize ; 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.
SP
, cleared size is specified with "global" variable
%LocalFrameSize
.ClearLocalVar %MACRO %IF %LocalFrameSize MOV DI,SP MOV CX,%LocalFrameSize/2 PUSH ES,SS POP ES XOR AX,AX CLD REP STOSW POP ES %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 SP,BP
, restore all GP registers using
POPAW
and then return to the parent code
which the Procedure was Invoked from.
Operands pushed on stack in the Invoke statement
will be discarded here by this EndProcedure macro, using
RET 2 * NrOfArg
.
All registers are preserved throughout the procedure invocation
unless the procedure changed their stored value
on the stack frame (writing to [%ReturnEAX]
for instance).
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 %ProcNameStrip %SET %ProcName %WHILE "%ProcNameStrip[%&]" == ":" ; Get rid of trailing colons. %ProcNameStrip %SET %ProcNameStrip[1..%&-1] %ENDWHILE %NrOfArg %SET2 %%NrOfArg_%ProcNameStrip NestChck %IF "%NrOfArg" == "" %ERROR ID=5925, '"%ProcName Procedure" statement missing.' %EXITMACRO EndProcedure %ENDIF NestChck MOV SP,BP POPAW RET 2 * %NrOfArg ENDP %ProcName, NESTINGCHECK=OFF ; Terminate the namespace. %ENDMACRO EndProcedure
Invoke %MACRO StdProcedure, Arguments ArgCheck %IF "%StdProcedure" == "" %ERROR ID=5926, 'Macro "Invoke" requires the name of called Procedure.' %EXITMACRO Invoke %ENDIF ArgCheck ArgNr %FOR %#..2, STEP= -1 PUSHW %*{%ArgNr} %ENDFOR ArgNr CALL %StdProcedure %ENDMACRO Invoke
ENDHEAD doscall