summaryrefslogtreecommitdiff
path: root/proc32.ash
diff options
context:
space:
mode:
Diffstat (limited to 'proc32.ash')
-rw-r--r--proc32.ash441
1 files changed, 441 insertions, 0 deletions
diff --git a/proc32.ash b/proc32.ash
new file mode 100644
index 0000000..f513b73
--- /dev/null
+++ b/proc32.ash
@@ -0,0 +1,441 @@
+;--------=========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__