nasm/misc/proc32.ash
2002-04-30 21:08:11 +00:00

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__