diff options
Diffstat (limited to 'proc32.ash')
-rw-r--r-- | proc32.ash | 441 |
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__ |