kernel/test/lmacros/lmacros2.mac

1264 lines
34 KiB
Plaintext

[list -]
%if 0
Extensions to NASM macro collection
Public Domain by C. Masloch, 2011-2012
Intended for 86 Mode programs.
%endif
%ifndef __lMACROS2_MAC__
%assign __lMACROS2_MAC__ 1
%include "lmacros1.mac"
[list -]
%ifdef _186
%ifnum _186 ; if appears like it is available:
numdef _lframe__use_186_def, 1 ; then default to use it
%endif
%endif
numdef _lframe__use_186_def ; use _186 to determine whether 186 available
numdef _lframe__force_186 ; regardless the above: assume 186 available
%assign __lframe__override_force_8086 0; regardless the above: temporarily stay 8086-compatible
; Initiate stack frame definition.
; %1 Type of frame for parameter processing.
; What this really means is only how large the return
; address and other things between bp and the last
; parameter are.
; Default: none
; Accepted: none, near (or 2), far (or 4), int (or 6),
; positive even numbers
; Notice: lret's auto %1 observes near/far/int here.
; %2 Nesting type of frame.
; Default: normal
; Accepted: normal, nested, inner
;
; A nested frame pushes and sets bp anew. That means that
; the labels of the outside frame are not usable.
; An inner frame modifies sp but not bp. The outer frame's
; variables may be accessed from within the inner frame.
; A normal frame is neither a nested nor an inner frame.
%imacro lframe 0-2.nolist none,normal
%ifdef lsizeparameters
%undef lsizeparameters
%endif
%ifdef __lframe__autoret
%undef __lframe__autoret
%endif
%ifidni %2, normal
%elifidni %2, nested
%elifidni %2, inner
%else
%fatal Invalid frame nesting type specified: %2
%endif
%ifctx LFRAME
%fatal Already inside lframe, must end previous with lenter
%elifctx LENTER
%ifidni %2, normal
%fatal Still inside lenter, must end previous with lleave
%endif
%elifctx LENTEREARLY
%ifidni %2, normal
%fatal Still inside lenter, must end previous with lleave
%endif
%endif
%ifnctx LENTER
%ifnidni %2, normal
%fatal Not inside lenter, cannot use nested or inner frame
%endif
%endif
%push LFRAME
%assign %$bits __BITS__
%if %$bits == 16
%assign %$base_size 2
%assign %$near_offset 2
%assign %$far_offset 4
%assign %$int_offset 6
%elif %$bits == 32
%assign %$base_size 4
%assign %$near_offset 4
%assign %$far_offset 8
%assign %$int_offset 12
%elif %$bits == 64
%assign %$base_size 8
%assign %$near_offset 8
%assign %$far_offset 16
%assign %$int_offset 24
%else
%fatal Unknown BITS specified: %$bits
%endif
%assign %$inner 0
%ifidni %2, inner
%assign %$inner 1
%endif
%assign %$nested 0
%ifidni %2, nested
%assign %$nested 1
%endif
%define %$autoret
%ifidni %1,none
%elifidni %1,-
%elifidni %1,near
%define %$autoret near
%assign %$parofs %$near_offset
%elifidni %1,far
%define %$autoret far
%assign %$parofs %$far_offset
%elifidni %1,int
%define %$autoret int
%assign %$parofs %$int_offset
%elifnum %1
%if %1 < 0
%fatal Invalid frame type specified (negative): %1
%endif
%if %1 & 1
%fatal Invalid frame type specified (odd): %1
%endif
%assign %$parofs %1
%else
%fatal Invalid frame type specified: %1
%endif
%ifdef %$parofs
%assign %$alignofs %$parofs + %$base_size
%endif
%assign %$pars_size 0
%assign %$pars_return_size 0
%define %$parlist
%define %$labellist empty,empty,empty
%assign %$ofs 0
%assign %$enter 0
%assign %$req 0
%assign %$usereq 0
%assign %$emit 1
%assign %$restore_frame_ofs 0
%if %$inner
%assign %$ofs %$$ofs
%assign %$restore_frame_ofs %$$ofs
%endif
%assign %$pars_inner_size 0
%endmacro
; Indicate that the stack frame set-up should not
; actually write any code (is not required) if no
; parameters or variables are registered.
; Note that if no variables or parameters are
; registered before lenter, and this is used, then it
; is an error to register variables after lenter.
; As a workaround, do not use this then, or additionally
; use lframe_needotherwise (effectively overriding this).
;
; This must be placed before lenter.
%imacro lframe_needonlyregistered 0.nolist
%ifnctx LFRAME
%fatal Must use lframe first
%endif
%assign %$usereq 1
%endmacro
; Indicate that the stack frame set-up is necessary,
; even if no parameters or variables are registered.
;
; This must be placed before lenter.
%imacro lframe_needotherwise 0.nolist
%ifnctx LFRAME
%fatal Must use lframe first
%endif
%assign %$req 1
%endmacro
%macro __lvar_check_duplicate_label 3-*.nolist
%if %0 % 3
%fatal Expected number of parameters that is a multiple of three
%endif
%rep (%0 / 3) - 1
%ifidn %1, %$newlabel
%fatal Duplicate label: %1
%endif
%rotate 3
%endrep
%endmacro
%macro __lvar_list_label 1.nolist
%define %$newlabel %1
__lvar_check_duplicate_label %$labellist
%ifdef ?%1
%xdefine %$labellist %1,defined,?%1,%$labellist
%else
%xdefine %$labellist %1,undefined,,%$labellist
%endif
%endmacro
%macro __lvar_define_var 2.nolist
__lvar_list_label %1
%assign ?%1 %2
._%$lframe_?%1: equ %2
%endmacro
; Define a stack frame variable.
; %1 Type or size of variable.
; Accepted: byte (1), word (2), dword (4), numeric
; %2 Name of variable. A question mark is prepended.
;
; Variables registered before lenter are accounted for
; in the code lenter generates, ie uninitialised space
; is automatically allocated for them on the stack.
; Variables registered after lenter are not automatically
; allocated, you must manually either push data onto the
; stack (possibly with desired initial values) or adjust
; sp to allocate space for them on the stack.
; Defining variables after "lleave code" is untested and
; potentially has unexpected results.
%imacro lvar 2.nolist
%ifctx LENTER
%ifn %$enter
%fatal lenter did not set up stack frame
%endif
%elifctx LENTEREARLY
%elifnctx LFRAME
%fatal Must use lframe first
%endif
%ifidni %1,byte
%assign %$varsize 1
%elifidni %1,word
%assign %$varsize 2
%elifidni %1,dword
%assign %$varsize 4
%elifidni %1,qword
%assign %$varsize 8
%else
; Previously we checked with %elifnum here that the parameter
; is numeric. That turned out not to be what we want in
; https://hg.pushbx.org/ecm/inicomp/file/dc376b09985e/exodecr.asm#l153
; In that case we used lequ as a workaround but it really
; should be allowed directly as lvar size specification.
; Therefore, we give the parameter to %assign and let it fail
; in case the expression doesn't result in a scalar number.
%assign %$varsize %1
%endif
%assign %$ofs %$ofs-%$varsize
__lvar_define_var %2, %$ofs
%assign %$req 1
%assign %$hadvar 1
%endmacro
; Define a stack frame parameter.
; %1 Type or size of parameter.
; Accepted: byte (1), word (2), dword (4), numeric
; %2 Name of parameter. A question mark is prepended.
;
; Parameters must be registered before lenter, as lenter
; contains the mechanism to automatically calculate the
; final offsets. This is necessary to allow registering
; parameters in the same order they have to be pushed
; onto the stack by the caller.
%imacro lpar 2.nolist
%ifnctx LFRAME
%fatal Must use lframe first
%endif
%ifndef %$parofs
%fatal Specified no frame type with lframe
%endif
%ifdef %$hadvar
%fatal lpar must occur before any lvar
; This is for lframe inner-type frames
; which modify %$ofs in lpar.
%endif
%ifidni %1,byte
%assign %$varsize 1
%elifidni %1,word
%assign %$varsize 2
%elifidni %1,dword
%assign %$varsize 4
%elifidni %1,qword
%assign %$varsize 8
%elifnum %1
%assign %$varsize %1
%else
%fatal Invalid type specified: %1
%endif
__lvar_list_label %2
%assign %$pars_size %$pars_size+%$varsize
%ifempty %$parlist
%xdefine %$parlist %$varsize,%2
%else
%xdefine %$parlist %$parlist,%$varsize,%2
%endif
%assign %$req 1
%if %$inner
%assign %$pars_inner_size %$pars_inner_size + %$varsize
%assign %$ofs %$ofs - %$varsize
%endif
%endmacro
; Note to return all prior defined stack frame parameters.
; This simply subtracts the current length of parameters
; from the length that is to be popped off the stack by lret.
;
; This is sort of a hack. May incorrectly handle cases of
; byte (or otherwise odd-sized) parameters.
%imacro lpar_return 0.nolist
%ifnctx LFRAME
%fatal Must use lframe first
%endif
%ifndef %$parofs
%fatal Specified no frame type with lframe
%endif
%ifempty %$parlist
%fatal Specified no parameters with lpar
%endif
%ifn %$pars_size
%fatal Specified no parameters with lpar
%endif
%assign %$pars_return_size %$pars_size
%assign %$req 1
%endmacro
; Define a stack frame equ.
; %1 Numeric value to assign to this equ.
; %2 Name of equ. A question mark is prepended.
;
; The equ's lifetime is the same as the frame's, just like
; for lvar and lpar labels. (All are internally defines.)
%imacro lequ 2.nolist
%ifctx LENTER
%elifctx LENTEREARLY
%elifnctx LFRAME
%fatal Must use lframe first
%endif
__lvar_define_var %2, %1
%endmacro
; Define an alignment variable.
; %1 What boundary to align to. Default is 16.
; %2 Space to add to align. Default is 0.
;
; It is assumed that the call instruction that
; called the current function had the stack
; aligned to at least the same boundary.
; The second parameter specifies the size of
; any parameters that we want to push before
; doing a call next. The alignment is chosen
; so that the call instruction will get the
; aligned stack.
; Sets %$alignment to the size of the variable
; defined for aligning, 0 if none. If not 0,
; the variable ?align_X_Y is defined where X
; is a decimal number indicating the size of
; all prior variables plus r/e/bp on stack
; plus the size set for the frame type, and
; Y is the decimal number of the %2 parameter.
; This can only be used if the lframe had a
; frame type other than "none".
; If a variable is defined then lreserve or
; another space reservation method must be
; used to actually allocate space for the
; created variable and thus align the stack.
%imacro lalign 0-2.nolist 16, 0
%ifctx LENTER
%elifctx LENTEREARLY
%elifnctx LFRAME
%fatal Must use lframe first
%endif
%ifndef %$parofs
%fatal Specified no frame type with lframe
%endif
%assign %$alignbase %$alignofs - %$ofs
%assign %$alignadjust %2
%assign %$alignment (%1 - ((%$alignbase + %$alignadjust) % %1)) % %1
%if %$alignment
lvar %$alignment, align_ %+ %$alignbase %+ _ %+ %$alignadjust
%endif
%endmacro
; Create a defined stack frame.
; This sets up bp and reserves stack space for all defined
; variables. It also defines the labels for parameters.
; If no variables or parameters were registered until now,
; and lframe_needonlyregistered has been used (without
; overriding it by using lframe_needotherwise), then it is
; assumed that no more variables will be registered after
; this and that the stack frame is unnecessary. It will
; thus not be created. lenter and lleave will consequently
; not create any code.
; Defines %$lsizeparameters to be the even number of bytes
; all parameters take up, and %$lsizevariables to be the
; even number of bytes all variables take up.
; The code created by this does not change any flags, as it
; uses either (one or two) "push ax" or "lea sp, [bp - x]"
; or "enter x, 0" to reserve the stack space.
;
; "lenter early" can be used to write only the initial
; "push bp" \ "mov bp, sp" sequence. Then variables are
; defined with lvar as usual. However, these variables
; can *optionally* be initialised by pushing into them
; (similarly to lvar variables defined after lenter).
; Finally, a "lenter" acts as the default lenter does,
; and reserves stack space for all yet defined lvar
; variables. This is always done using "lea sp, [bp - x]"
; so it doesn't matter how many of the variables were
; already initialised by pushing into them.
%imacro lenter 0-1.nolist
%ifctx LENTEREARLY
%elifnctx LFRAME
%fatal Must use lframe first
%endif
%ifidni %1,early
%ifctx LENTEREARLY
%fatal Cannot use lenter early twice
%endif
%repl LENTEREARLY
%assign %$req 1
%assign %$early 1
%assign %$wasearly 0
%elifempty %1
%assign %$wasearly 0
%ifctx LENTEREARLY
%assign %$wasearly 1
%endif
%assign %$early 0
%repl LENTER
%else
%fatal Invalid parameter: %1
%endif
%assign %$lsizevariables (-%$ofs+1 - (- %$restore_frame_ofs) - %$pars_inner_size )&~1
%ifn %$usereq
%assign %$req 1
%endif
%assign %$enter_emitted 0
%if %$req
%assign %$enter 1
%assign %$186 0
%if %$early || %$wasearly
%elif __lframe__override_force_8086
%elif __lframe__force_186
%assign %$186 1
%elif __lframe__use_186_def
%assign %$186 _186
%endif
%if %$186 && %$ofs && (! %$inner) && %$emit
enter %$lsizevariables, 0
%assign %$enter_emitted 1
%endif
%ifnidn %$bits,__BITS__
%fatal BITS of lframe (%$bits) differs from current: __BITS__
%endif
%if %$bits == 16
%ifn %$inner
%ifn %$wasearly
__lvar_define_var frame_bp, 0
%ifidni %$autoret, near
__lvar_define_var frame_ip, 2
%elifidni %$autoret, far
__lvar_define_var frame_ip, 2
__lvar_define_var frame_cs, 4
%elifidni %$autoret, int
__lvar_define_var frame_ip, 2
__lvar_define_var frame_cs, 4
__lvar_define_var frame_fl, 6
%endif
%endif
%endif
%if %$emit
%ifn %$enter_emitted
%ifn %$inner
%ifn %$wasearly
push bp
mov bp, sp
%endif
%endif
%ifn %$early
%if %$ofs
%if %$wasearly == 0 && (%$lsizevariables) == 0
%elif %$wasearly == 0 && (%$lsizevariables) == 2
push ax
%elif %$wasearly == 0 && (%$lsizevariables) == 4
push ax
push ax
%else
lea sp, [bp + %$restore_frame_ofs - (%$lsizevariables + %$pars_inner_size)]
%endif
%endif
%endif
%endif
%endif
%elif %$bits == 32
%ifn %$inner
%ifn %$wasearly
__lvar_define_var frame_ebp, 0
%ifidni %$autoret, near
__lvar_define_var frame_eip, 4
%elifidni %$autoret, far
__lvar_define_var frame_eip, 4
__lvar_define_var frame_cs, 8
%elifidni %$autoret, int
__lvar_define_var frame_eip, 4
__lvar_define_var frame_cs, 8
__lvar_define_var frame_efl, 12
%endif
%endif
%endif
%if %$emit
%ifn %$enter_emitted
%ifn %$inner
%ifn %$wasearly
push ebp
mov ebp, esp
%endif
%endif
%ifn %$early
%if %$ofs
%if %$wasearly == 0 && (%$lsizevariables) == 0
%elif %$wasearly == 0 && (%$lsizevariables) == 2
push ax
%elif %$wasearly == 0 && (%$lsizevariables) == 4
push eax
%elif %$wasearly == 0 && (%$lsizevariables) == 8
push eax
push eax
%else
lea esp, [ebp + %$restore_frame_ofs - (%$lsizevariables + %$pars_inner_size)]
%endif
%endif
%endif
%endif
%endif
%elif %$bits == 64
%ifn %$inner
%ifn %$wasearly
__lvar_define_var frame_rbp, 0
%ifidni %$autoret, near
__lvar_define_var frame_rip, 8
%elifidni %$autoret, far
__lvar_define_var frame_rip, 8
__lvar_define_var frame_cs, 16
%elifidni %$autoret, int
__lvar_define_var frame_rip, 8
__lvar_define_var frame_cs, 16
__lvar_define_var frame_rfl, 24
%endif
%endif
%endif
%if %$emit
%ifn %$enter_emitted
%ifn %$inner
%ifn %$wasearly
push rbp
mov rbp, rsp
%endif
%endif
%ifn %$early
%if %$ofs
%if %$wasearly == 0 && (%$lsizevariables) == 0
%elif %$wasearly == 0 && (%$lsizevariables) == 2
push ax
%elif %$wasearly == 0 && (%$lsizevariables) == 8
push rax
%else
lea rsp, [rbp + %$restore_frame_ofs - (%$lsizevariables + %$pars_inner_size)]
%endif
%endif
%endif
%endif
%endif
%else
%fatal Unknown BITS specified: %$bits
%endif
%ifn %$wasearly
._%$lenter:
%endif
%endif
%ifn %$wasearly
%assign %$lsizeparameters (%$pars_size+1)&~1
__lenter_parse_parlist %$parlist
%endif
%endmacro
%macro __lenter_parse_parlist 0-*.nolist
%if %0 & 1
%fatal Expected even number of macro parameters!
%endif
%rep %0 >> 1
%assign %$pars_size %$pars_size - %1
%if %$inner
%assign ?%2 (%$pars_size + %$parofs + %$restore_frame_ofs - %$pars_inner_size)
._%$lframe_?%2: equ (%$pars_size + %$parofs + %$restore_frame_ofs - %$pars_inner_size)
%else
%assign ?%2 (%$pars_size + %$parofs + %$base_size)
._%$lframe_?%2: equ (%$pars_size + %$parofs + %$base_size)
%endif
%rotate 2
%endrep
%endmacro
; Reserve stack space for prior lvar variables.
;
; This is used after lenter and reserves the stack space
; for all yet defined variables, including those defined
; after the lenter usage (which aren't reserved space for
; by lenter itself).
;
; "lenter early \ lvar a \ lenter \ lvar b" is equivalent
; to "lenter \ lvar a \ lreserve \ lvar b".
;
; lreserve can be used multiple times. It always uses
; "lea sp, [bp - x]" to reserve space, so the space will
; always be equal to the already defined variables.
;
; Warning: Using lreserve after having pushed additional
; data, beyond the defined variables, will discard that
; data from the stack as sp is reset.
%imacro lreserve 0.nolist
%ifnctx LENTER
%fatal Must use lenter first
%endif
%ifn %$enter
%fatal lenter did not set up stack frame
%endif
%assign %$lsizevariables (-%$ofs+1)&~1
%ifnidn %$bits,__BITS__
%fatal BITS of lframe (%$bits) differs from current: __BITS__
%endif
%if %$emit
%if %$bits == 16
%if %$ofs
lea sp, [bp - %$lsizevariables]
%endif
%elif %$bits == 32
%if %$ofs
lea esp, [ebp - %$lsizevariables]
%endif
%elif %$bits == 64
%if %$ofs
lea rsp, [rbp - %$lsizevariables]
%endif
%else
%fatal Unknown BITS specified: %$bits
%endif
%endif
%endmacro
%imacro ldup 0.nolist
%ifnctx LENTER
%fatal Must use lenter first
%endif
%ifn %$nested
%fatal ldup only valid in nested frame in normal frame
%endif
%if %$$inner
%fatal ldup only valid in nested frame in normal frame
%endif
%if %$enter != %$$enter
%fatal Failed ldup (enter)
%endif
%ifnempty %$autoret
%ifnidn %$autoret, %$$autoret
%fatal Failed ldup (autoret)
%endif
%else
%ifnempty %$$autoret
%fatal Failed ldup (autoret one empty)
%endif
%endif
%if %$parofs != %$$parofs
%fatal Failed ldup (parofs)
%endif
%if %$bits != %$$bits
%fatal Failed ldup (bits)
%endif
%if %$pars_return_size != %$$pars_return_size
%fatal Failed ldup (pars_return_size)
%endif
__ldup_count newparlist, %$parlist
__ldup_count oldparlist, %$$parlist
%if %$count_newparlist != %$count_oldparlist
%fatal Failed ldup (count_*parlist)
%endif
__ldup_check_parlist %$$parlist
__ldup_count newlabellist, %$labellist
__ldup_count oldlabellist, %$$labellist
%if %$count_newlabellist != %$count_oldlabellist
%fatal Failed ldup (count_*labellist)
%endif
__ldup_check_labellist %$$labellist
%endmacro
%macro __ldup_count 1-*.nolist
%assign %$count_%[%1] %0 - 1
%endmacro
%macro __ldup_check_parlist 0-*.nolist
%assign %$ii 0
%rep %0
%define %$check %1
__ldup_check_single %$ii + 1,%$parlist
%assign %$ii %$ii + 1
%rotate 1
%endrep
%endmacro
%macro __ldup_check_labellist 3-*.nolist
%assign %$ii 0
%rep (%0 / 3) - 1
%define %$check %1
__ldup_check_single %$ii + 1,%$labellist
%xdefine %$check ?%1
__ldup_check_single %$ii + 1 + 2,%$labellist
%assign %$ii %$ii + 3
%rotate 3
%endrep
%endmacro
%macro __ldup_check_single 1-*.nolist
%rotate %1
%ifnidn %1, %$check
; The following is a workaround for older versions of NASM.
; Refer to https://bugzilla.nasm.us/show_bug.cgi?id=3392628
%ifnnum %1
%fatal Failed ldup (check single: 1 1=%1 check=%$check)
%elifnnum %$check
%fatal Failed ldup (check single: 2 1=%1 check=%$check)
%elifn %1 == %$check
%fatal Failed ldup (check single: 3 1=%1 check=%$check)
%endif
%endif
%endmacro
%imacro lemit 0-1.nolist on
%ifctx LENTER
%elifctx LENTEREARLY
%elifnctx LFRAME
%fatal Must use lframe first
%endif
%ifidni %1,on
%assign %$emit 1
%elifidni %1,off
%assign %$emit 0
%else
%assign %$emit !! %1
%endif
%endmacro
%imacro lifemit 1+.nolist
%ifctx LENTER
%elifctx LENTEREARLY
%elifnctx LFRAME
%fatal Must use lframe first
%endif
%if %$emit
%1
%endif
%endmacro
; Remove a created stack frame.
; This restores bp if it was altered to access variables or
; parameters. It also restores sp from bp if sp was altered
; to allocate space to variables.
; The code created by this does not change any flags, as it
; uses only "mov sp, bp" and "pop bp", or "leave".
; This undefines all labels that were defined for variables
; and parameters in this stack frame, to prevent accidental
; usage of these labels later.
; lsizeparameters is defined as the (even) number of bytes
; that all parameters use on the stack. (This is zero if no
; parameters have been registered.)
; No return instruction is written by this macro; you may use
; lret or place the appropriate instruction(s) manually.
; If none of lframe_needonlyregistered,needotherwise have
; been used, and no variables or parameters at all have been
; registered (regardless whether before or after lenter),
; then this will stop with an error.
; %1 Specifies whether to write the stack frame removal
; code or to remove the macros' context. This allows
; writing the code multiple times.
; Default: code+ctx
; Accepted: code+ctx (or ), code, ctx
; %2 Specifies whether to always restore sp.
; Default: optimiserestoresp
; Accepted: optimiserestoresp (or ), forcerestoresp
%imacro lleave 0-2.nolist
%ifnctx LENTER
%fatal Must use lenter first
%endif
%ifidni %1,code
%elifidni %1,ctx
%elifidni %1,code+ctx
%elifempty %1
%else
%fatal Invalid parameter specified: %1
%endif
%ifidni %2,forcerestoresp
%elifidni %2,optimizerestoresp
%elifidni %2,optimiserestoresp
%elifempty %2
%else
%fatal Invalid second parameter specified: %2
%endif
%ifnidni %1,ctx
%ifn %$inner
%if %$enter
%ifn %$req
%fatal lframe apparently unnecessary
; Note that to fix this, you either have to remove
; the lframe usage altogether, or employ one of those:
; * lframe_needonlyregistered
; * lframe_needotherwise
; See the descriptions of those.
%endif
%assign %$186 0
%if __lframe__override_force_8086
%elif __lframe__force_186
%assign %$186 1
%elif __lframe__use_186_def
%assign %$186 _186
%endif
%ifn %$ofs
%ifnidni %2,forcerestoresp
%assign %$186 0
%endif
%endif
%if %$emit
%if %$186
leave
%else
%ifnidn %$bits,__BITS__
%fatal BITS of lframe (%$bits) differs from current: __BITS__
%endif
%if %$bits == 16
%if %$ofs
mov sp, bp
%elifidni %2,forcerestoresp
mov sp, bp
%endif
pop bp
%elif %$bits == 32
%if %$ofs
mov esp, ebp
%elifidni %2,forcerestoresp
mov esp, ebp
%endif
pop ebp
%elif %$bits == 64
%if %$ofs
mov rsp, rbp
%elifidni %2,forcerestoresp
mov rsp, rbp
%endif
pop rbp
%else
%fatal Unknown BITS specified: %$bits
%endif
%endif
%endif
%else
%if %$ofs
%fatal No stack frame was created
%elifidni %2,forcerestoresp
%fatal No stack frame was created
%endif
%endif
%endif
%if %$inner
%ifnidn %$bits,__BITS__
%fatal BITS of lframe (%$bits) differs from current: __BITS__
%endif
%if %$emit
%if %$bits == 16
lea sp, [bp + %$restore_frame_ofs - %$pars_return_size]
%elif %$bits == 32
lea esp, [ebp + %$restore_frame_ofs - %$pars_return_size]
%elif %$bits == 64
lea rsp, [rbp + %$restore_frame_ofs - %$pars_return_size]
%else
%fatal Unknown BITS specified: %$bits
%endif
%endif
%endif
%else
%ifnempty %2
%fatal Invalid to specify second parameter
%endif
%endif
%ifdef %$parofs
%assign lsizeparameters %$lsizeparameters - %$pars_return_size
%endif
%xdefine __lframe__autoret %$autoret
%ifnidni %1,code
._%$lleave:
__lleave_undefine_labels %$labellist
%pop
%endif
%endmacro
%macro __lleave_undefine_labels 3-*.nolist
%if %0 % 3
%fatal Expected number of parameters that is a multiple of three
%endif
%rep (%0 / 3) - 1
%ifidn %2, undefined
%undef ?%1
%elifidn %2, defined
%define ?%1 %3
%else
%fatal Expected parameter to be 'defined' or 'undefined' but is %2
%endif
%rotate 3
%endrep
%endmacro
; Place matching return instruction.
; This uses lsizeparameters, if it has been set by
; lleave, as the size of all parameters on the
; caller's stack frame.
; This generally must be used after using lleave.
; This may be used any number of times.
; %1 Specifies type of return.
; Default: auto
; Accepted: auto, near, far, int
; Notice: auto will use lframe's %1 only if it was
; one of near, far, int. Otherwise, auto
; (including the default) is an error!
; %2 Specifies an adjustment to remove either more
; or less than all the registered parameters
; from the caller's stack frame.
; Default: 0
; Accepted: even numbers, including negatives.
; Must be at least -lsizeparameters!
; Notice: The sum of lsizeparameters and %2
; must be 0 for type int (ie iret).
;
; Note: As this is usually used after the lframe's
; context is already popped, the lemit
; setting does not affect this macro.
%imacro lret 0-2.nolist auto,0
%ifctx LFRAME
%fatal Must use lenter first
%elifctx LENTEREARLY
%fatal Must use lenter first
%endif
; Do not check specifically for ctx LENTER here!
; Usually, lleave already popped the ctx.
%push
%define %$type %1
%ifidni %$type,-
%define %$type auto
%endif
%ifempty %$type
%define %$type auto
%endif
%ifidni %$type,auto
%ifdef __lframe__autoret
%ifnempty __lframe__autoret
%define %$type __lframe__autoret
%endif
%endif
%endif
%ifidni %$type,near
%define %$inst retn
%elifidni %$type,far
%define %$inst retf
%elifidni %$type,int
%define %$inst iret
%else
%fatal Invalid type specified: %1
%endif
%if %2 & 1
%fatal Invalid adjustment %2, is odd
%endif
%assign %$remov %2
%ifnum lsizeparameters
%assign %$remov %$remov+lsizeparameters
%endif
%if %$remov < 0
%fatal Invalid adjustment %2, sum is negative
%endif
%ifn %$remov
%define %$remov
%endif
%$inst %$remov
%pop
%endmacro
%unimacro @@ 1+.nolist
%unimacro @@ 0-1+.nolist
; Core macro implementing anonymous local labels that behave
; similarly to (but not exactly the same way as) MS MASM's.
; It shifts @FFFF..@BBBB by one and then invents a new label
; name for @FFFF. The invented names contain incrementing
; numbers to insure they are unique, and all start with ..@
; to avoid interfering with NASM's named local labels.
;
; Provided (cap-insensitive) smacros:
; @FFFF (or @F4) Forward reference to fourth @@
; @FFF (or @F3) Forward reference to third @@
; @FF (or @F2) Forward reference to second @@
; @F (or @F1) Forward reference to next @@
; @B (or @B1) Backward reference to previous @@
; @BB (or @B2) Backward reference to second @@
; @BBB (or @B3) Backward reference to third @@
; @BBBB (or @B4) Backward reference to fourth @@
; (It is recommended to seldom use anything but @F and @B.)
; Internally used (cap-sensitive) smacro:
; __@@_seq Incremented by @S, initialised to 0
; by including this file. This gives
; the first number of invented names.
; __@@_num Incremented by @@, initialised to 4
; by including this file as well as
; by @S. This gives the second number
; of invented names. (Note that the
; current value is always what @@
; will invent for the new @FFFF; thus
; eg the current @F refers to a name
; that was invented with the current
; value less four. Consequently the
; first @@ defines a label whose name
; contains a 0 as the second number.)
; Invented names, full form:
; ..@.@@.%[__@@_seq].%[__@@_num]
; eg ..@.@@.0.0, ..@.@@.0.1, etc; ..@.@@.1.0, etc
;
; Note that as NASM has no procedure concept, the @@ labels
; of logically distinct code sequences aren't automatically
; reset, that is an @F near the end of one code sequence
; whose intended target @@ erroneously wasn't placed will
; jump to the next placed @@ after the code sequence.
; To work around that, manually place @S (start sequence for
; @@ labels) between logically distinct code sequences. For
; more strict checks, put @I (invalidate @@ references)
; whereever a logically distinct code sequence ends, and
; @S whereever a logically distinct code sequence begins.
; (This disallows any @@ references outside such marked
; code sequences.)
; This
; will cause all erroneous @@ forward references in front
; of it and all erroneous @@ backward references behind it
; to refer to undefined labels.
; The former will have a (regular) positive second number in
; the referenced label name, while the latter will have a
; (special) negative second number -4..-1 in the referenced
; label name. The line on which the error occurs is the
; line with the erroneous reference.
%macro __@@_next 0-1.nolist 1
%if _@@_check && ! __@@_in_seq
%if _@@_check == 1
%warning "not in a sequence"
%else
%error "not in a sequence"
%endif
%endif
%rep %1
%ixdefine @BBBB @BBB
%ixdefine @BBB @BB
%ixdefine @BB @B
%ixdefine @B @F
%ixdefine @F @FF
%ixdefine @FF @FFF
%ixdefine @FFF @FFFF
%idefine @FFFF ..@.@@.%[__@@_seq].%[__@@_num]
%ixdefine @B4 @BBBB
%ixdefine @B3 @BBB
%ixdefine @B2 @BB
%ixdefine @B1 @B
%ixdefine @F1 @F
%ixdefine @F2 @FF
%ixdefine @F3 @FFF
%ixdefine @F4 @FFFF
%assign __@@_num __@@_num+1
%endrep
%endmacro
; This macro defines an anonymous local label (optionally
; writing any kind of code afterwards)
; then it calls the above macro to shift the pre-invented
; names and invent a new one for @FFFF.
; Note that smacros inside following code if any are
; expanded by NASM's preprocessor before processing mmacros
; hence "@@: jmp @F" is equivalent to "@@: jmp $".
; (If correct label missing colon warning behaviour wasn't
; wanted, then the implementation could be simpler. Hmm.)
%macro @@ 0.nolist
@F
__@@_next
%endmacro
; "@@", label alone on line missing colon warning.
%macro @@ 1+.nolist
%ifidn %1,:
@F %+ :
__@@_next
%else
%push
%defstr %$str %1
%substr %$colon %$str 1
%ifidn %$colon,':'
%substr %$str %$str 2,-1
%deftok %$str %$str
@F %+ :
__@@_next
%$str
%else
@F times 0 nop
__@@_next
%1
%endif
%pop
%endif
%endmacro
; "@@ :", (trivial) no label-missing-colon warnings.
; "@@ nop", label missing colon warning; the "times 0 nop"
; part is necessary so that this warning isn't 'promoted'
; to the label alone on line missing colon warning.
; "@@ : nop", no label-missing-colon warnings.
; Not handled like actual label: following "instruction" is
; allowed to be a directive, another label, or even an
; mmacro usage like @@ itself. Hmmm... allows eg "@@: @@:"
; to define two @@ labels at once. May be useful.
numdef @@_check, 0 ; 0 = none, 1 = warn, else = error
numdef @@_start, 1 ; 0 = omit @S, else = do @S
; Only invalidate outstanding @@ forward references.
%imacro @FI 0.nolist
%assign __@@_num_inv __@@_num_inv+1
%idefine @FFFF ..@.@@.%[__@@_seq].inv.%[__@@_num_inv]
%assign __@@_num_inv __@@_num_inv+1
%idefine @FFF ..@.@@.%[__@@_seq].inv.%[__@@_num_inv]
%assign __@@_num_inv __@@_num_inv+1
%idefine @FF ..@.@@.%[__@@_seq].inv.%[__@@_num_inv]
%assign __@@_num_inv __@@_num_inv+1
%idefine @F ..@.@@.%[__@@_seq].inv.%[__@@_num_inv]
%ixdefine @F4 @FFFF
%ixdefine @F3 @FFF
%ixdefine @F2 @FF
%ixdefine @F1 @F
%endmacro
; Only invalidate @@ backward references.
%imacro @BI 0.nolist
%assign __@@_num_inv __@@_num_inv+1
%idefine @B ..@.@@.%[__@@_seq].inv.%[__@@_num_inv]
%assign __@@_num_inv __@@_num_inv+1
%idefine @BB ..@.@@.%[__@@_seq].inv.%[__@@_num_inv]
%assign __@@_num_inv __@@_num_inv+1
%idefine @BBB ..@.@@.%[__@@_seq].inv.%[__@@_num_inv]
%assign __@@_num_inv __@@_num_inv+1
%idefine @BBBB ..@.@@.%[__@@_seq].inv.%[__@@_num_inv]
%ixdefine @B4 @BBBB
%ixdefine @B3 @BBB
%ixdefine @B2 @BB
%ixdefine @B1 @B
%endmacro
; Invalidate all references.
; The sequence flag is reset, unless non-zero %1 is given.
%imacro @I 0-1.nolist 0
%if __@@_in_seq && %1
%assign __@@_in_seq 1
%else
%assign __@@_in_seq 0
%endif
@FI
@BI
%endmacro
; Invalidate outstanding @@ forward references (by causing
; them to ultimately refer to undefined labels) then invent
; four new names for @BBBB..@B that are invalid (ie would
; ultimately refer to undefined labels) then invent four
; new names for @F..@FFFF that can be defined by @@.
; Note that __@@_next successfully passes along whatever
; @F..@FFFF are initialised to when this macro is used;
; even if they aren't initialised at all (in that case
; their names are passed along!). No error conditions are
; caused there, and all of these passed names are discarded
; by @BI.
; As __@@_num is initialised to -4, at the end of this
; macro's processing the @@ backward references @BBBB..@B
; refer to names ending in -4..-1 (which are to be left
; undefined) and the @@ forward references @F..@FFFF refer
; to ..@.@@.%[__@@_seq].0..3 (which can be defined by @@).
%imacro @S 0-1.nolist _@@_check
%if %1 && __@@_in_seq
%if %1 == 1
%warning "already in a sequence"
%else
%error "already in a sequence"
%endif
%endif
%assign __@@_seq __@@_seq+1
%assign __@@_num 0
%assign __@@_num_inv 0
%assign __@@_in_seq 1
__@@_next 4
@BI
%endmacro
%ifnnum __@@_in_seq
%assign __@@_in_seq 0
%endif
%ifnnum __@@_seq
%assign __@@_seq 0
%assign __@@_num 0
%assign __@@_num_inv 0
%endif
%ifnnum __@@_num
%assign __@@_num 0
%endif
%ifnnum __@@_num_inv
%assign __@@_num_inv 0
%endif
%if _@@_start
@S
%endif
%imacro checklframeend 0
%ifctx LFRAME
%fatal Still inside lframe!
%elifctx LENTER
%fatal Still inside lenter!
%elifctx LENTEREARLY
%fatal Still inside lenter!
%endif
%endmacro
%imacro _struc_at 2-3+.nolist
%if %1
%if (%2 - ($ - %$strucname)) != 0
%if %1 == 1
%error Not at that offset!
%else
%warning Not at that offset!
%endif
%endif
%endif
resb %2 - ($ - %$strucname)
%3
%endmacro
%idefine struc_at _struc_at 0,
%idefine exact_struc_at _struc_at 1,
%idefine warn_struc_at _struc_at 2,
%endif
[list +]