mirror of https://github.com/FDOS/kernel.git
1264 lines
34 KiB
Plaintext
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 +]
|