mirror of
https://github.com/netwide-assembler/nasm.git
synced 2024-12-27 09:29:06 +08:00
442 lines
12 KiB
Plaintext
442 lines
12 KiB
Plaintext
;--------=========xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=========--------
|
|
;
|
|
; Copyright (C) 1999 by Andrew Zabolotny
|
|
; Miscelaneous NASM macros that makes use of new preprocessor features
|
|
;
|
|
; This library is free software; you can redistribute it and/or
|
|
; modify it under the terms of the GNU Library General Public
|
|
; License as published by the Free Software Foundation; either
|
|
; version 2 of the License, or (at your option) any later version.
|
|
;
|
|
; This library is distributed in the hope that it will be useful,
|
|
; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
; Library General Public License for more details.
|
|
;
|
|
; You should have received a copy of the GNU Library General Public
|
|
; License along with this library; if not, write to the Free
|
|
; Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
;
|
|
;--------=========xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=========--------
|
|
|
|
; The macros in this file provides support for writing 32-bit C-callable
|
|
; NASM routines. For a short description of every macros see the
|
|
; corresponding comment before every one. Simple usage example:
|
|
;
|
|
; proc sin,1
|
|
; targ %$angle
|
|
; fld %$angle
|
|
; fsin
|
|
; endproc sin
|
|
|
|
%ifndef __PROC32_ASH__
|
|
%define __PROC32_ASH__
|
|
|
|
[WARNING -macro-selfref]
|
|
|
|
;-----======xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx======-----
|
|
; Summary:
|
|
; Mangle a name to be compatible with the C compiler
|
|
; Arguments:
|
|
; The name
|
|
; Example:
|
|
; cname (my_func)
|
|
;-----======xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx======-----
|
|
%ifdef EXTERNC_UNDERSCORE
|
|
%define cname(x) _ %+ x
|
|
%else
|
|
%define cname(x) x
|
|
%endif
|
|
|
|
;-----======xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx======-----
|
|
; Summary:
|
|
; Import an external C procedure definition
|
|
; Arguments:
|
|
; The name of external C procedure
|
|
; Example:
|
|
; cextern printf
|
|
;-----======xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx======-----
|
|
%macro cextern 1
|
|
%xdefine %1 cname(%1)
|
|
%ifidni __OUTPUT_FORMAT__,obj
|
|
extern %1:wrt FLAT
|
|
%else
|
|
extern %1
|
|
%endif
|
|
%endmacro
|
|
|
|
;-----======xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx======-----
|
|
; Summary:
|
|
; Export an C procedure definition
|
|
; Arguments:
|
|
; The name of C procedure
|
|
; Example:
|
|
; cglobal my_printf
|
|
;-----======xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx======-----
|
|
%macro cglobal 1
|
|
%xdefine %1 cname(%1)
|
|
global %1
|
|
%endmacro
|
|
|
|
;-----======xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx======-----
|
|
; Summary:
|
|
; Misc macros to deal with PIC shared libraries
|
|
; Comment:
|
|
; Note that we have a different syntax for working with and without
|
|
; PIC shared libraries. In a PIC environment we should load first
|
|
; the address of the variable into a register and then work through
|
|
; that address, i.e: mov eax,myvar; mov [eax],1
|
|
; In a non-PIC environment we should directly write: mov myvar,1
|
|
; Example:
|
|
; extvar myvar
|
|
; GetGOT
|
|
; %ifdef PIC
|
|
; mov ebx,myvar ; get offset of myvar into ebx
|
|
; %else
|
|
; lea ebx,myvar
|
|
; %endif
|
|
;-----======xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx======-----
|
|
%ifdef PIC
|
|
cextern _GLOBAL_OFFSET_TABLE_
|
|
%macro GetGOT 0
|
|
%ifdef .$proc.stkofs
|
|
%assign .$proc.stkofs .$proc.stkofs+4
|
|
%endif
|
|
call %$Get_GOT
|
|
%$Get_GOT:
|
|
pop ebx
|
|
add ebx,_GLOBAL_OFFSET_TABLE_ + $$ - %$Get_GOT wrt ..gotpc
|
|
%endmacro
|
|
%macro extvar 1
|
|
cextern %1
|
|
%xdefine %1 [ebx+%1 wrt ..got]
|
|
%endmacro
|
|
%else
|
|
%define GetGOT
|
|
%macro extvar 1
|
|
cextern %1
|
|
%endmacro
|
|
%endif
|
|
|
|
;-----======xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx======-----
|
|
; Summary:
|
|
; Begin a procedure definition
|
|
; For performance reasons we don't use stack frame pointer EBP,
|
|
; instead we're using the [esp+xx] addressing. Because of this
|
|
; you should be careful when you work with stack pointer.
|
|
; The push/pop instructions are macros that are defined to
|
|
; deal correctly with these issues.
|
|
; Arguments:
|
|
; First argument - the procedure name
|
|
; Second optional argument - the number of bytes for local variables
|
|
; The following arguments could specify the registers that should be
|
|
; pushed at beginning of procedure and popped before exiting
|
|
; Example:
|
|
; proc MyTestProc
|
|
; proc MyTestProc,4,ebx,esi,edi
|
|
;-----======xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx======-----
|
|
%macro proc 1-3+ 0
|
|
cglobal %1
|
|
%push %1
|
|
align 16
|
|
%1:
|
|
%xdefine %$proc.name %1
|
|
; total size of local arguments
|
|
%assign %$proc.locsize (%2+3) & 0xFFFC
|
|
; offset from esp to argument
|
|
%assign %$proc.argofs 4+%$proc.locsize
|
|
; additional offset to args (tracks push/pops)
|
|
%assign .$proc.stkofs 0
|
|
; offset from esp to local arguments
|
|
%assign %$proc.locofs 0
|
|
; Now push the registers that we should save
|
|
%define %$proc.save %3
|
|
%if %$proc.locsize != 0
|
|
sub esp,%$proc.locsize
|
|
%endif
|
|
push %$proc.save
|
|
%endmacro
|
|
|
|
;-----======xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx======-----
|
|
; Summary:
|
|
; Declare an argument passed on stack
|
|
; This macro defines two additional macros:
|
|
; first (with the name given by first argument) - [esp+xx]
|
|
; second (with a underscore appended to first argument) - esp+xx
|
|
; Arguments:
|
|
; First argument defines the procedure argument name
|
|
; Second optional parameter defines the size of the argument
|
|
; Default value is 4 (a double word)
|
|
; Example:
|
|
; arg .my_float
|
|
; arg .my_double,8
|
|
;-----======xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx======-----
|
|
%macro arg 1-2 4
|
|
%ifndef %$proc.argofs
|
|
%error "`arg' not in a proc context"
|
|
%else
|
|
; Trick: temporary undefine .$proc.stkofs so that it won't be expanded
|
|
%assign %%. .$proc.stkofs
|
|
%undef .$proc.stkofs
|
|
%xdefine %{1}_ esp+%$proc.argofs+.$proc.stkofs
|
|
%xdefine %1 [esp+%$proc.argofs+.$proc.stkofs]
|
|
%assign .$proc.stkofs %%.
|
|
%assign %$proc.argofs %2+%$proc.argofs
|
|
%endif
|
|
%endmacro
|
|
|
|
;-----======xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx======-----
|
|
; Summary:
|
|
; Declare an local variable
|
|
; first (with the name given by first argument) - [esp+xx]
|
|
; second (with a slash prefixing the first argument) - esp+xx
|
|
; Arguments:
|
|
; First argument defines the procedure argument name
|
|
; Second optional parameter defines the size of the argument
|
|
; Default value is 4 (a double word)
|
|
; Example:
|
|
; loc .int_value
|
|
; loc .double_value,8
|
|
;-----======xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx======-----
|
|
%macro loc 1-2 4
|
|
%ifndef %$proc.locofs
|
|
%error "`loc' not in a proc context"
|
|
%elif %$proc.locofs + %2 > %$proc.locsize
|
|
%error "local stack space exceeded"
|
|
%else
|
|
%assign %%. .$proc.stkofs
|
|
%undef .$proc.stkofs
|
|
%xdefine %{1}_ esp+%$proc.locofs+.$proc.stkofs
|
|
%xdefine %1 [esp+%$proc.locofs+.$proc.stkofs]
|
|
%assign .$proc.stkofs %%.
|
|
%assign %$proc.locofs %$proc.locofs+%2
|
|
%endif
|
|
%endmacro
|
|
|
|
;-----======xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx======-----
|
|
; Summary:
|
|
; Get the type of given size into context-local variable %$type
|
|
; Arguments:
|
|
; Size of type we want (1,2,4,8 or 10)
|
|
; Example:
|
|
; type 4 ; gives "dword"
|
|
; type 10 ; gives "tword"
|
|
;-----======xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx======-----
|
|
%macro type 1
|
|
%if %1 = 1
|
|
%define %$type byte
|
|
%elif %1 = 2
|
|
%define %$type word
|
|
%elif %1 = 4
|
|
%define %$type dword
|
|
%elif %1 = 8
|
|
%define %$type qword
|
|
%elif %1 = 10
|
|
%define %$type tword
|
|
%else
|
|
%define %$. %1
|
|
%error "unknown type for argument size %$."
|
|
%endif
|
|
%endmacro
|
|
|
|
;-----======xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx======-----
|
|
; Summary:
|
|
; Same as `arg' but prepends "word", "dword" etc (typed arg)
|
|
; first (with the name given by first argument) - dword [esp+xx]
|
|
; second (with a slash prefixing the first argument) - esp+xx
|
|
; Arguments:
|
|
; Same as for `arg'
|
|
; Example:
|
|
; targ .my_float ; .my_float is now "dword [esp+xxx]"
|
|
; targ .my_double,8 ; .my_double is now "qword [esp+xxx]"
|
|
;-----======xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx======-----
|
|
%macro targ 1-2 4
|
|
%ifndef %$proc.argofs
|
|
%error "`targ' not in a proc context"
|
|
%else
|
|
arg %1,%2
|
|
type %2
|
|
%assign %%. .$proc.stkofs
|
|
%undef .$proc.stkofs
|
|
%xdefine %1 %$type %1
|
|
%assign .$proc.stkofs %%.
|
|
%endif
|
|
%endmacro
|
|
|
|
;-----======xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx======-----
|
|
; Summary:
|
|
; Same as `loc' but prepends "word", "dword" etc (typed loc)
|
|
; first (with the name given by first argument) - dword [esp+xx]
|
|
; second (with a slash prefixing the first argument) - esp+xx
|
|
; Arguments:
|
|
; Same as for `loc'
|
|
; Example:
|
|
; tloc int_value
|
|
; tloc double_value,8
|
|
;-----======xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx======-----
|
|
%macro tloc 1-2 4
|
|
%ifndef %$proc.locofs
|
|
%error "`tloc' not in a proc context"
|
|
%else
|
|
loc %1,%2
|
|
type %2
|
|
%assign %%. .$proc.stkofs
|
|
%undef .$proc.stkofs
|
|
%xdefine %1 %$type %1
|
|
%assign .$proc.stkofs %%.
|
|
%endif
|
|
%endmacro
|
|
|
|
;-----======xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx======-----
|
|
; Summary:
|
|
; Finish a procedure
|
|
; Gives an error if proc/endproc pairs mismatch
|
|
; Defines an label called __end_(procedure name)
|
|
; which is useful for calculating function size
|
|
; Arguments:
|
|
; (optional) The name of procedure
|
|
; Example:
|
|
; endproc MyTestProc
|
|
;-----======xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx======-----
|
|
%push tmp ; trick: define a dummy context to avoid error in next line
|
|
%macro endproc 0-1 %$proc.name
|
|
%ifndef %$proc.argofs
|
|
%error "`endproc' not in a proc context"
|
|
%elifnidn %$proc.name,%1
|
|
%define %$. %1
|
|
%error "endproc names mismatch: expected `%$proc.name'"
|
|
%error "but got `%$.' instead"
|
|
%elif %$proc.locofs < %$proc.locsize
|
|
%error "unused local space declared (used %$proc.locofs, requested %$proc.locsize)"
|
|
%else
|
|
%$exit:
|
|
; Now pop the registers that we should restore on exit
|
|
pop %$proc.save
|
|
%if %$proc.locsize != 0
|
|
add esp,%$proc.locsize
|
|
%endif
|
|
ret
|
|
__end_%1:
|
|
%pop
|
|
%endif
|
|
%endmacro
|
|
%pop
|
|
|
|
;-----======xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx======-----
|
|
; Summary:
|
|
; A replacement for "push" for use within procedures
|
|
; Arguments:
|
|
; any number of registers which will be push'ed successively
|
|
; Example:
|
|
; push eax,ebx,ecx,edx
|
|
;-----======xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx======-----
|
|
%macro push 0-*
|
|
; dummy comment to avoid problems with "push" on the same line with a label
|
|
%rep %0
|
|
push %1
|
|
%rotate 1
|
|
%assign .$proc.stkofs .$proc.stkofs+4
|
|
%endrep
|
|
%endmacro
|
|
|
|
;-----======xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx======-----
|
|
; Summary:
|
|
; A replacement for "pop" for use within procedures
|
|
; Arguments:
|
|
; any number of registers which will be pop'ed in reverse order
|
|
; Example:
|
|
; pop eax,ebx,ecx,edx
|
|
;-----======xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx======-----
|
|
%macro pop 0-*
|
|
; dummy comment to avoid problems with "pop" on the same line with a label
|
|
%rep %0
|
|
%rotate -1
|
|
pop %1
|
|
%assign .$proc.stkofs .$proc.stkofs-4
|
|
%endrep
|
|
%endmacro
|
|
|
|
;-----======xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx======-----
|
|
; Summary:
|
|
; Replacements for "pushfd" and "popfd" that takes care of esp
|
|
; Example:
|
|
; pushfd
|
|
; popfd
|
|
;-----======xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx======-----
|
|
%macro pushfd 0
|
|
pushfd
|
|
%assign .$proc.stkofs .$proc.stkofs+4
|
|
%endmacro
|
|
%macro popfd 0
|
|
popfd
|
|
%assign .$proc.stkofs .$proc.stkofs-4
|
|
%endmacro
|
|
|
|
;-----======xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx======-----
|
|
; Summary:
|
|
; Exit from current procedure (optionally on given condition)
|
|
; Arguments:
|
|
; Either none or a condition code
|
|
; Example:
|
|
; exit
|
|
; exit nz
|
|
;-----======xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx======-----
|
|
%macro exit 0-1 mp
|
|
j%1 near %$exit
|
|
%endmacro
|
|
|
|
;-----======xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx======-----
|
|
; Summary:
|
|
; start an conditional branch
|
|
; Arguments:
|
|
; A condition code
|
|
; second (optional) argument - "short" (by default - "near")
|
|
; Example:
|
|
; if nz
|
|
;-----======xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx======-----
|
|
%macro if 1-2 near
|
|
; dummy comment to avoid problems with "if" on the same line with a label
|
|
%push if
|
|
j%-1 %2 %$elseif
|
|
%endmacro
|
|
|
|
;-----======xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx======-----
|
|
; Summary:
|
|
; define the "else" branch of a conditional statement
|
|
; Arguments:
|
|
; optionaly: "short" if jmp to endif is less than 128 bytes away
|
|
; Example:
|
|
; else
|
|
;-----======xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx======-----
|
|
%macro else 0-1
|
|
%ifnctx if
|
|
%error "`else' without matching `if'"
|
|
%else
|
|
jmp %1 %$endif
|
|
%$elseif:
|
|
%define %$elseif_defined
|
|
%endif
|
|
%endmacro
|
|
|
|
;-----======xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx======-----
|
|
; Summary:
|
|
; Finish am conditional statement
|
|
; Arguments:
|
|
; none
|
|
; Example:
|
|
; endif
|
|
;-----======xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx======-----
|
|
%macro endif 0
|
|
%ifnctx if
|
|
%error "`endif' without matching `if'"
|
|
%else
|
|
%ifndef %$elseif_defined
|
|
%$elseif:
|
|
%endif
|
|
%$endif:
|
|
%pop
|
|
%endif
|
|
%endmacro
|
|
|
|
%endif ; __PROC32_ASH__
|