| ;--------=========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__ |