[list -] %if 0 NASM macro collection Public Domain by C. Masloch, 2008-2012 Intended for 86 Mode programs. %endif %ifndef __lMACROS1_MAC__ %assign __lMACROS1_MAC__ 1 %if !!2 - 1 %error Unary operator ! returns values > 1. Adjust all sources. %endif %idefine by byte %idefine wo word %idefine dwo dword %idefine b byte %idefine w word %idefine d dword %idefine s short %idefine sho short %idefine n near %idefine ne near %idefine f far ; Bind major and minor version byte to version word or ; version word with exchanged bytes. %idefine ver(major,minor) ( ((major)&0FFh)<<8|((minor)&0FFh) ) %idefine verx(major,minor) ( ((minor)&0FFh)<<8|((major)&0FFh) ) ; Macros for table creation %define count(start,stop) stop-start+1 %define range(start,stop) (start),count(start,stop) ; generates two comma-separated values ; Work with 4-byte dates %define date4b(y,m,d) (((y)&0FFFFh)<<16|((m)&0FFh)<<8|((d)&0FFh)) ; create 4-byte date %define year4bd(d) (((d)>>16)&0FFFFh) ; get year from 4-byte date %define month4bd(d) (((d)>>8)&0FFh) ; get month from 4-byte date %define day4bd(d) ((d)&0FFh) ; get day from 4-byte date ; The 4-byte date stores the year 0-65535 in the high word, ; followed by the month 0-255 in the high byte (of the low ; word) and the day 0-255 in the low byte. ; Write numbers as decimal digits into a string %define _1digits_nocheck(d) (((d)% 10)+'0') %xdefine _2digits_nocheck(d) _1digits_nocheck((d)/10),_1digits_nocheck(d) %xdefine _3digits_nocheck(d) _1digits_nocheck((d)/100),_2digits_nocheck(d) %xdefine _4digits_nocheck(d) _1digits_nocheck((d)/1000),_3digits_nocheck(d) %xdefine _5digits_nocheck(d) _1digits_nocheck((d)/10000),_4digits_nocheck(d) %xdefine _1digits(d) (!!(d/10)*(1<<32)+ _1digits_nocheck(d)) %xdefine _2digits(d) _1digits((d)/ 10),_1digits_nocheck(d) %xdefine _3digits(d) _1digits((d)/ 100),_2digits_nocheck(d) %xdefine _4digits(d) _1digits((d)/ 1000),_3digits_nocheck(d) %xdefine _5digits(d) _1digits((d)/10000),_4digits_nocheck(d) ; Write number as decimal digits into a string, ; and automagically determine how many digits to use. ; The input number is a critical expression to NASM. ; ; %1 = number to write ; %2 = minimal number of digits, 0..5. defaults to 1 ; (setting it to 0 with a number of 0 writes nothing) %macro _autodigits 1-2.nolist 1 %if %2 > 5 %error Minimal number of digits 6 or more: %2 %endif %if (%1) >= 100000 %error Number has to use 6 or more digits: %1 %elif (%1) >= 10000 || %2 == 5 db _5digits(%1) %elif (%1) >= 1000 || %2 == 4 db _4digits(%1) %elif (%1) >= 100 || %2 == 3 db _3digits(%1) %elif (%1) >= 10 || %2 == 2 db _2digits(%1) %elif (%1) >= 1 || %2 == 1 db _1digits(%1) %endif %endmacro ; %1 = name of single-line macro to set. will be prefixed by underscore ; %2 = number to write ; %3 = minimal number of digits, 0..5. defaults to 1 ; (setting it to 0 with a number of 0 defines macro to "") %macro _autodigitsdef 2-3.nolist 1 %if %3 > 5 %error Minimal number of digits 6 or more: %3 %endif %if (%2) >= 100000 %error Number has to use 6 or more digits: %2 %elif (%2) >= 10000 || %3 == 5 %define _%1 %[_5digits(%2)] %elif (%2) >= 1000 || %3 == 4 %define _%1 %[_4digits(%2)] %elif (%2) >= 100 || %3 == 3 %define _%1 %[_3digits(%2)] %elif (%2) >= 10 || %3 == 2 %define _%1 %[_2digits(%2)] %elif (%2) >= 1 || %3 == 1 %define _%1 %[_1digits(%2)] %elif %define _%1 "" %endif %endmacro ; Write numbers as hexadecimal digits into a string %define _1digitshex_nocheck(h) ( ((h)& 0Fh) + '0' + ('A'-'9'-1)*(((h)& 0Fh)/10) ) %xdefine _2digitshex_nocheck(h) _1digitshex_nocheck((h)>> 4),_1digitshex_nocheck(h) %xdefine _3digitshex_nocheck(h) _1digitshex_nocheck((h)>> 8),_2digitshex_nocheck(h) %xdefine _4digitshex_nocheck(h) _1digitshex_nocheck((h)>>12),_3digitshex_nocheck(h) %xdefine _5digitshex_nocheck(h) _1digitshex_nocheck((h)>>16),_4digitshex_nocheck(h) %xdefine _6digitshex_nocheck(h) _1digitshex_nocheck((h)>>20),_5digitshex_nocheck(h) %xdefine _7digitshex_nocheck(h) _1digitshex_nocheck((h)>>24),_6digitshex_nocheck(h) %xdefine _8digitshex_nocheck(h) _1digitshex_nocheck((h)>>28),_7digitshex_nocheck(h) %xdefine _1digitshex(h) (!!(h&~0Fh)*(1<<32)+ _1digitshex_nocheck(h)) %xdefine _2digitshex(h) _1digitshex((h)>> 4),_1digitshex_nocheck(h) %xdefine _3digitshex(h) _1digitshex((h)>> 8),_2digitshex_nocheck(h) %xdefine _4digitshex(h) _1digitshex((h)>>12),_3digitshex_nocheck(h) %xdefine _5digitshex(h) _1digitshex((h)>>16),_4digitshex_nocheck(h) %xdefine _6digitshex(h) _1digitshex((h)>>20),_5digitshex_nocheck(h) %xdefine _7digitshex(h) _1digitshex((h)>>24),_6digitshex_nocheck(h) %xdefine _8digitshex(h) _1digitshex((h)>>28),_7digitshex_nocheck(h) %xdefine _5digitssephex(h) _1digitshex((h)>>16),"_",_4digitshex_nocheck(h) %xdefine _6digitssephex(h) _2digitshex((h)>>16),"_",_4digitshex_nocheck(h) %xdefine _7digitssephex(h) _3digitshex((h)>>16),"_",_4digitshex_nocheck(h) %xdefine _8digitssephex(h) _4digitshex((h)>>16),"_",_4digitshex_nocheck(h) %macro _appenddigitstrdef 2.nolist %substr %%ii "0123456789ABCDEF" (%2) + 1 %strcat _%1 _%1,%%ii %endmacro ; %1 = name of single-line macro to set. will be prefixed by underscore ; %2 = number to write ; %3 = minimal number of digits, 0..5. defaults to 1 ; (setting it to 0 with a number of 0 defines macro to "") %macro _autodigitsstrdef 2-3.nolist 1 %if %3 > 5 %error Minimal number of digits 6 or more: %3 %endif %define _%1 "" %if (%2) >= 100000 %error Number has to use 6 or more digits: %2 %endif %if (%2) >= 10000 || %3 >= 5 _appenddigitstrdef %1, %2 / 10000 % 10 %endif %if (%2) >= 1000 || %3 >= 4 _appenddigitstrdef %1, %2 / 1000 % 10 %endif %if (%2) >= 100 || %3 >= 3 _appenddigitstrdef %1, %2 / 100 % 10 %endif %if (%2) >= 10 || %3 >= 2 _appenddigitstrdef %1, %2 / 10 % 10 %endif %if (%2) >= 1 || %3 >= 1 _appenddigitstrdef %1, %2 / 1 % 10 %endif %endmacro ; %1 = name of single-line macro to set. will be prefixed by underscore ; %2 = number to write ; %3 = minimal number of hexits, 0..8. defaults to 1 ; (setting it to 0 with a number of 0 defines macro to "") %macro _autohexitsstrdef 2-3.nolist 1 %if %3 > 8 %error Minimal number of hexits 9 or more: %3 %endif %define _%1 "" %if (%2) >= 1_0000_0000h %error Number has to use 9 or more hexits: %2 %endif %if (%2) >= 1000_0000h || %3 >= 8 _appenddigitstrdef %1, (%2 >> (7 * 4)) & 0Fh %endif %if (%2) >= 100_0000h || %3 >= 7 _appenddigitstrdef %1, (%2 >> (6 * 4)) & 0Fh %endif %if (%2) >= 10_0000h || %3 >= 6 _appenddigitstrdef %1, (%2 >> (5 * 4)) & 0Fh %endif %if (%2) >= 1_0000h || %3 >= 5 _appenddigitstrdef %1, (%2 >> (4 * 4)) & 0Fh %endif %if (%2) >= 1000h || %3 >= 4 _appenddigitstrdef %1, (%2 >> (3 * 4)) & 0Fh %endif %if (%2) >= 100h || %3 >= 3 _appenddigitstrdef %1, (%2 >> (2 * 4)) & 0Fh %endif %if (%2) >= 10h || %3 >= 2 _appenddigitstrdef %1, (%2 >> (1 * 4)) & 0Fh %endif %if (%2) >= 1h || %3 >= 1 _appenddigitstrdef %1, (%2 >> (0 * 4)) & 0Fh %endif %endmacro ; Compute required words/dwords/qwords/paragraphs/pages/KiB of known byte size (rounds up if necessary) %idefine bytes(b) (b) %idefine words(b) ((b)+1>>1) %idefine dwords(b) ((b)+3>>2) %idefine qwords(b) ((b)+7>>3) %idefine paragraphs(b) ((b)+15>>4) %idefine paras(b) ((b)+15>>4) %idefine pages(b) ((b)+511>>9) %idefine kib(b) ((b)+1023>>10) ; Compute required bytes of known word/dword/qword/paragraph/page/KiB size %idefine frombytes(b) (b) %idefine fromwords(w) ((w)<<1) %idefine fromdwords(d) ((d)<<2) %idefine fromqwords(q) ((q)<<3) %idefine fromparagraphs(p) ((p)<<4) %idefine fromparas(p) ((p)<<4) %idefine frompages(p) ((p)<<9) %idefine fromkib(k) ((k)<<10) ; Compute relative address ; ; address: which address is referenced ; fixup: how many bytes are added to get the absolute address %define __REL__(address,fixup) ((address)-(fixup)) %xdefine __REL8__(address,fixup) __REL__(address,fixup) %xdefine __REL8__(address) __REL__(address,$+1) ; for __JMP_REL8 (db) %xdefine __REL16__(address,fixup) __REL__(address,fixup) %xdefine __REL16__(address) __REL__(address,$+2); for __JMP_REL16 (dw) ; Values of some opcodes %define __JMP_REL8 0EBh %define __JMP_REL16 0E9h %define __JMP_FAR 0EAh %define __CALL_REL16 0E8h %define __CALL_FAR 9Ah %define __NOP 90h %define __TEST_IMM8 0A8h ; changes flags, NC %define __TEST_IMM16 0A9h ; changes flags, NC ; Longer NOPs require two bytes, like a short jump does. ; However they execute faster than unconditional jumps. ; This one reads random data in the stack segment. ; (Search for better ones.) %define __TEST_OFS16_IMM8 0F6h,86h ; changes flags, NC ; Remove the following macros if already found in other files %unimacro asciz 0-1+.nolist %unimacro ascii 0-1+.nolist %unmacro _ascii_prefix_suffix 2-3+.nolist %undef ascii %undef asciz %undef ascic %undef asciiline %undef ascizline %undef ascicline %unimacro verdef 4.nolist %unimacro numdef 1-3.nolist %unimacro strdef 3.nolist %unimacro incdef 2-4.nolist %unimacro _incdef 2-4.nolist %unimacro incdef 2-*.nolist %unimacro excdef 2-*.nolist %unimacro comment 0-*.nolist %unimacro commentif 2.nolist %unimacro commentifn 2.nolist %unimacro fixme 0-1+.nolist %unimacro noparam 0-1+.nolist %unimacro noparamnamed 1-2+.nolist %unimacro assignif 3-4.nolist %unimacro assignifn 3-4.nolist %unimacro cpu 1+.nolist ; removes NASM's standard macro %unimacro selcpu 0.nolist %unimacro cpufailmsg 0-2+.nolist %unimacro check186 0-1.nolist %unimacro check286 0-1.nolist %unimacro check386 0-1.nolist %unimacro check186 0-3.nolist %unimacro check286 0-3.nolist %unimacro check386 0-3.nolist %unimacro checkcpu 1.nolist %unimacro _d 2.nolist %unimacro d0out 0-3.nolist %unimacro endarea 1-2.nolist %unimacro struc 1.nolist ; removes NASM's standard macro %unimacro struc 1-2.nolist %unimacro endstruc 0.nolist ; removes NASM's standard macro %unimacro endstruc 0-1.nolist %unimacro istruc 1.nolist ; removes NASM's standard macro %unimacro at 1-2+.nolist ; removes NASM's standard macro %unimacro iend 0.nolist ; removes NASM's standard macro %unimacro fill 2-3+.nolist %unimacro _fill 3.nolist %unimacro pad 2-3+.nolist %unimacro _pad 3.nolist %unimacro alignd 2-3+.nolist %if __NASM_VERSION_ID__ >= (2 * 100_0000h + 15 * 1_0000h) %pragma preproc sane_empty_expansion true %endif ; String prefixed and/or suffixed with sequence(s) ; ; %1 = prefix ; %2 = suffix ; %3+ = Optional string %macro _ascii_prefix_suffix 2-3+.nolist db %1 %ifnempty %3 db %3 %endif db %2 %endmacro %idefine ascii _ascii_prefix_suffix "","", %idefine asciz _ascii_prefix_suffix "",0, %idefine ascic _ascii_prefix_suffix "",36, %idefine asciiline _ascii_prefix_suffix "",{13,10}, %idefine ascizline _ascii_prefix_suffix "",{13,10,0}, %idefine ascicline _ascii_prefix_suffix "",{13,10,36}, ; Counted string ; ; %1 = data type of count ; %2+ = optional string %imacro _counted 2+.nolist %push %ifnempty %2 _strlen %2 %1 %$len db %2 %else %1 0 %endif %pop %endmacro %idefine counted _counted db, %idefine countedb _counted db, %idefine countedw _counted dw, %imacro _strlen 1-*.nolist %assign %$len 0 %rep %0 %ifstr %1 %strlen %$addlen %1 %assign %$len %$len+%$addlen %elifnempty %1 %assign %$len %$len+1 %endif %rotate 1 %endrep %endmacro ; Define version information. ; ; %1 = Base name of single-line macros ; %2 = Additional letter for base name of single-line macros ; %3 = Major version ; %4 = Minor version ; ; Created single-line macros: ; %1_VER%2 = version word, high byte = major version ; %1_VER%2_X = version word (bytes exchanged), high byte = minor version ; %1_VER%2_MAJ = Major version number byte ; %1_VER%2_MIN = Minor version number byte ; %1_VER%2_STR = Up to 7 character version string (maximal "255.255") %imacro verdef 4.nolist %assign %1_VER%2_MAJ %3 %assign %1_VER%2_MIN %4 %assign %1_VER%2 ver(%3,%4) %assign %1_VER%2_X verx(%3,%4) %push %defstr %$maj %3 %defstr %$min %4 %strlen %$length %$min %if %$length < 2 %strcat %$min '0', %$min ; one digit low numbers padded to two digits %endif %strcat %1_VER%2_STR %$maj, '.', %$min %pop %endmacro ; Define a numeric definition for conditional assembly ; ; Instead of: ; %ifdef DEFINE ; use: ; %if _DEFINE ; ; %1 = Definition name. Will get an additional underscore prefix ; %2 = Value to assign if not defined (defaults to 0) ; %3 = Value to assign if defined, but empty (defaults to 1) %imacro numdef 1-3.nolist 0,1 %ifnum __DEFAULTED_%1 %if __lMACROS1_MAC__DEFAULTING && __DEFAULTED_%1 %undef _%1 ; cause defaulting to take effect again %if __DEFAULTED_%1 == 2 ; defaulted to third parameter? %define _%1 ; cause alternative defaulting to take effect again %endif %endif %endif %ifdef _%1 %ifempty _%1 %if __lMACROS1_MAC__SHOW_EXPLICIT__INITIALISED %if __lMACROS1_MAC__SHOW_EXPLICIT_DEFAULT || __lMACROS1_MAC__SHOW_EXPLICIT %defstr WARNINGMESSAGE %1 %strcat WARNINGMESSAGE "define _",WARNINGMESSAGE," set empty explicitly" %warning WARNINGMESSAGE %endif %endif %assign _%1 %3 ; i.e. "-d_DEFINE" option on NASM's command line %if __lMACROS1_MAC__DEFAULTING %assign __DEFAULTED_%1 2 ; note alternative defaulting occurred %endif %else %if __lMACROS1_MAC__SHOW_EXPLICIT__INITIALISED %if __lMACROS1_MAC__SHOW_EXPLICIT_VALUE || __lMACROS1_MAC__SHOW_EXPLICIT %defstr WARNINGMESSAGE %1 %strcat WARNINGMESSAGE "define _",WARNINGMESSAGE," set explicitly" %warning WARNINGMESSAGE %endif %endif %endif ; (if option was "-d_DEFINE=0", it's left as zero) %if __lMACROS1_MAC__DEFAULTING %ifnnum __DEFAULTED_%1 %assign __DEFAULTED_%1 0 ; note no defaulting occurred %endif %endif %else ; If not defined (no option on NASM's command line) %assign _%1 %2 ; then assign to zero %if __lMACROS1_MAC__DEFAULTING %assign __DEFAULTED_%1 1 ; note defaulting occurred %endif %endif %endmacro ; Define a generic definition for conditional assembly ; ; %1 = Definition name. Will get an additional underscore prefix ; %2 = What to define as if not defined (defaults to empty) ; %3 = What to define as if defined, but empty (defaults to empty) %imacro gendef 1-3.nolist %ifnum __DEFAULTED_%1 %if __lMACROS1_MAC__DEFAULTING && __DEFAULTED_%1 %undef _%1 ; cause defaulting to take effect again %if __DEFAULTED_%1 == 2 ; defaulted to third parameter? %define _%1 ; cause alternative defaulting to take effect again %endif %endif %endif %ifdef _%1 %ifempty _%1 %if __lMACROS1_MAC__SHOW_EXPLICIT__INITIALISED %if __lMACROS1_MAC__SHOW_EXPLICIT_DEFAULT || __lMACROS1_MAC__SHOW_EXPLICIT %defstr WARNINGMESSAGE %1 %strcat WARNINGMESSAGE "define _",WARNINGMESSAGE," set empty explicitly" %warning WARNINGMESSAGE %endif %endif %define _%1 %3 ; i.e. "-d_DEFINE" option on NASM's command line %if __lMACROS1_MAC__DEFAULTING %assign __DEFAULTED_%1 2 ; note alternative defaulting occurred %endif %else %if __lMACROS1_MAC__SHOW_EXPLICIT__INITIALISED %if __lMACROS1_MAC__SHOW_EXPLICIT_VALUE || __lMACROS1_MAC__SHOW_EXPLICIT %defstr WARNINGMESSAGE %1 %strcat WARNINGMESSAGE "define _",WARNINGMESSAGE," set explicitly" %warning WARNINGMESSAGE %endif %endif %endif ; (if option was "-d_DEFINE=0", it's left as zero) %if __lMACROS1_MAC__DEFAULTING %ifnnum __DEFAULTED_%1 %assign __DEFAULTED_%1 0 ; note no defaulting occurred %endif %endif %else ; If not defined (no option on NASM's command line) %define _%1 %2 ; then define to default %if __lMACROS1_MAC__DEFAULTING %assign __DEFAULTED_%1 1 ; note defaulting occurred %endif %endif %endmacro ;; %macro __help_strdef_stringify 3+.nolist ;;%xdefine _%1 %2%3%2 ;; %endmacro ; Define a string definition ; ; %1 = Definition name. Will get an additional underscore prefix ; %2 = Value to assign if not defined ; %3 = Value to assign if defined, but no string (if not given, %2) ; ; Using braces { and } is useful for %2 and %3 %imacro strdef 2-3.nolist %ifnum __DEFAULTED_%1 %if __lMACROS1_MAC__DEFAULTING && __DEFAULTED_%1 %undef _%1 ; cause defaulting to take effect again %if __DEFAULTED_%1 == 2 ; defaulted to third parameter? %define _%1 ; cause alternative defaulting to take effect again %endif %endif %endif %ifdef _%1 %ifempty _%1 %if __lMACROS1_MAC__SHOW_EXPLICIT__INITIALISED %if __lMACROS1_MAC__SHOW_EXPLICIT_DEFAULT || __lMACROS1_MAC__SHOW_EXPLICIT %defstr WARNINGMESSAGE %1 %strcat WARNINGMESSAGE "define _",WARNINGMESSAGE," set empty explicitly" %warning WARNINGMESSAGE %endif %endif %if %0 >= 3 %define _%1 %3 ; i.e. "-d_DEFINE" option on NASM's command line %if __lMACROS1_MAC__DEFAULTING %assign __DEFAULTED_%1 2 ; note alternative defaulting occurred %endif %else %define _%1 %2 %if __lMACROS1_MAC__DEFAULTING %assign __DEFAULTED_%1 1 ; note defaulting occurred %endif %endif %else %if __lMACROS1_MAC__SHOW_EXPLICIT__INITIALISED %if __lMACROS1_MAC__SHOW_EXPLICIT_VALUE || __lMACROS1_MAC__SHOW_EXPLICIT %defstr WARNINGMESSAGE %1 %strcat WARNINGMESSAGE "define _",WARNINGMESSAGE," set explicitly" %warning WARNINGMESSAGE %endif %endif %ifnstr _%1 ; refer to http://stackoverflow.com/questions/15650276/nasm-convert-integer-to-string-using-preprocessor %defstr _%1 _%1 ; %define %%quot " ; %define %%stringify(x) %%quot %+ x %+ %%quot ; %xdefine _%1 %%stringify(_%1) ;; __help_strdef_stringify %1, ", _%1 ; issue: leading and trailing blanks are deleted out of the string. ; workaround: quote the content so that it's a string already. ; ie this may need nasm source.asm -D_"' content '" %endif %endif ; (if option was "-d_DEFINE='FOO'", it's left as 'FOO') %else ; If not defined (no option on NASM's command line) %define _%1 %2 ; %if __lMACROS1_MAC__DEFAULTING %assign __DEFAULTED_%1 1 ; note defaulting occurred %endif %endif %endmacro ; Include definitions if a condition is met ; ; %1 = condition ; %2-* = included definition name, without underscore prefix %imacro incdef 2-*.nolist %if %1 ; if true %rep (%0 - 1) %rotate 1 %assign _%1 1 ; set any included to true %endrep %endif %endmacro ; Exclude definitions if a condition is met ; ; %1 = condition ; %2-* = excluded definition name, without underscore prefix %imacro excdef 2-*.nolist %if %1 ; if true %rep (%0 - 1) %rotate 1 %assign _%1 0 ; set any excluded to false %endrep %endif %endmacro %imacro overridedef 1-2.nolist 0 %push OVERRIDE %ifdef _%1 %assign %$defined 1 %else %assign %$defined 0 %endif %xdefine %$prior _%1 %xdefine %$defaulted __DEFAULTED_%1 %define %$name %1 %assign _%1 %2 %assign __DEFAULTED_%1 0 %endmacro %imacro resetdef 0-1.nolist %ifnctx OVERRIDE %error Wrong context %endif %if %0 %ifnidn %1, %$name %error Wrong usage of resetdef %endif %endif %xdefine _%[%$name] %$prior %xdefine __DEFAULTED_%[%$name] %$defaulted %ifn %$defined %undef _%[%$name] %endif %pop %endmacro %assign __lMACROS1_MAC__DEFAULTING 0 %imacro defaulting 0-1.nolist 1 %ifidni %1,toggle %assign __lMACROS1_MAC__DEFAULTING !__lMACROS1_MAC__DEFAULTING %else %assign __lMACROS1_MAC__DEFAULTING !!(%1) %endif %endmacro %assign __lMACROS1_MAC__SHOW_EXPLICIT__INITIALISED 0 numdef _lMACROS1_MAC__SHOW_EXPLICIT numdef _lMACROS1_MAC__SHOW_EXPLICIT_DEFAULT numdef _lMACROS1_MAC__SHOW_EXPLICIT_VALUE %assign __lMACROS1_MAC__SHOW_EXPLICIT__INITIALISED 1 numdef _lMACROS1_MAC__CPU_DEFAULTS %if __lMACROS1_MAC__CPU_DEFAULTS numdef 186 ; 186 opcodes allowed numdef 386 ; 386 opcodes allowed incdef _386,186 ; excluding 186 opcodes on 386+ seems not useful %endif numdef _lMACROS1_MAC__DEBUG_DEFAULTS %if __lMACROS1_MAC__DEBUG_DEFAULTS numdef DEBUG ; all of the debugging levels incdef _DEBUG,DEBUG0,DEBUG1,DEBUG2,DEBUG3,DEBUG4 numdef DEBUG0 ; 1st level debug numdef DEBUG1 ; 2nd level debug numdef DEBUG2 ; 3rd level debug numdef DEBUG3 ; 4th level debug numdef DEBUG4 ; 5th level debug ; Note: All specific debug levels are independent. ; What each debug level does is program-specific. %endif ; Automatically select CPU depending on definitions ; ; Use "hardwired" cpu directives instead in source files that ; don't include code for multiple CPU generations. %imacro selcpu 0.nolist %if _386 cpu 386 %elif _186 cpu 186 %else cpu 8086 %endif %endmacro ; Write a message stating what CPU type is required. ; ; %1 = Part of message before numerical CPU identifier (Defaults to "") ; %2 = Part of message after numerical CPU identifier (Defaults to ASCIZ " CPU required") ; ; Using braces { and } is useful ; ; No data is generated if assembling for 8086 %imacro cpufailmsg 0-2+.nolist "",{" CPU required",0} %if _386 db %1, "386", %2 %elif _186 db %1, "186", %2 %endif %endmacro ; Check if required CPU is available ; ; The code uses cx, ax and comparison flags. ; ; %1 = Code to execute if CPU requirement not meet %imacro checkcpu 1.nolist %if _386 %%checkcpu: check386 .fail: %1 .pass: %elif _186 %%checkcpu: check186 .fail: %1 .pass: %endif %endmacro ; Check for 186+ CPU ; ; %1 = true if .fail follows (jump to .pass), ; false if .pass follows (jump to .fail) ; Default true ; %2 = label to jump to if 186+ CPU ; Default .pass ; %3 = label to jump to if no 186+ CPU ; Default .fail %imacro check186 0-3 1 .pass .fail mov cx, 1_0010_0001b ; Shift count's bit 0 and 5 are set; shift data is 1 shl ch, cl ; 186+ limits shift count to five bits (0-4): resulting ; ch is 10b on 186+; 0 on 8086 (actual result overflows) %if %1 jnz %2 ; 186+ --> %else jz %3 ; 8086 --> %endif %endmacro ; Check for 286+ CPU ; ; %1 = true if .fail follows (jump to .pass), ; false if .pass follows (jump to .fail) ; Default true ; %2 = label to jump to if 286+ CPU ; Default .pass ; %3 = label to jump to if no 286+ CPU ; Default .fail %imacro check286 0-3 1 .pass .fail push sp pop ax cmp ax, sp ; 286+ pushes original sp value %if %1 je %2 ; 286+ --> %else jne %3 ; 8086 or 186 --> %endif %endmacro ; Check for 386+ CPU ; ; %1 = true if .fail follows (jump to .pass), ; false if .pass follows (jump to .fail) ; Default true ; %2 = label to jump to if 386+ CPU ; Default .pass ; %3 = label to jump to if no 386+ CPU ; Default .fail %imacro check386 0-3 1 .pass .fail check286 0, {%2}, {%3} ; branches to .fail if 8086 or 186 --> [cpu 286] pushf push wo 0111_0000_0000_0000b popf ; set bits 12-14 of FLAGS ("push imm16" instruction 186+) pushf pop ax ; get what was in FLAGS popf ; restore original flags test ax, 0111_0000_0000_0000b ; 386+ leaves what we pushed; 286 clears bits 12-14 in ; Real Mode (Bits 12-14 are used in Protected Mode only) __CPU__ %if %1 jnz %2 ; 386+ --> %else jz %3 ; 286 --> %endif %endmacro ; Instruction if specified level debug definition true ; ; %1 = Level of requested debug definition ; %2 = Instruction %imacro _d 2+.nolist %if _DEBUG%1 %2 %endif %endmacro ; Instruction if xth level debug definition true ; ; %1+ = Instruction %idefine d0 _d 0, %idefine d1 _d 1, %idefine d2 _d 2, %idefine d3 _d 3, %idefine d4 _d 4, ; Breakpoint if xth level debug definition true %idefine dbp _d, __DEBUG_BP__ %idefine d0bp d0 __DEBUG0_BP__ %idefine d1bp d1 __DEBUG1_BP__ %idefine d2bp d2 __DEBUG2_BP__ %idefine d3bp d3 __DEBUG3_BP__ %idefine d4bp d4 __DEBUG4_BP__ %idefine dd0bp _d,{d0bp} %idefine dd1bp _d,{d1bp} %idefine dd2bp _d,{d2bp} %idefine dd3bp _d,{d3bp} %idefine dd4bp _d,{d4bp} ; Breakpoint instructions for all debug levels ; ; "call ...", "int1", "int3" are useful single-instruction breakpoints %define __DEBUG_BP__ int3 %define __DEBUG0_BP__ int3 %define __DEBUG1_BP__ int3 %define __DEBUG2_BP__ int3 %define __DEBUG3_BP__ int3 %define __DEBUG4_BP__ int3 ; Print number if 1st level debug definition true ; ; %1 = Number to display (in range 0-255) ; %2 = Override call for "call __DEBUG0_DISPALDEC__" ; %3 = Override call for "call __DEBUG0_DISPAL__" ; ; The calls have to preserve all registers except ax. %macro d0out 0-3.nolist -,call __DEBUG0_DISPALDEC__,call __DEBUG0_DISPAL__ %if _DEBUG0 push ax %ifnidn %1,- mov al, %1 %2 %endif mov al, '.' %3 pop ax %endif %endmacro ; cpu macro replacement ; ; __CPU__ = like __SECT__ (that is, use [cpu ...] and switch back with __CPU__) %imacro cpu 1+.nolist %define __CPU__ [cpu %1] __CPU__ %endmacro ; Define size of area ; ; %1 = Starting label of area ; %2 = if true, use already defined label %1_size (for ; use with NASM's struc and endstruc); default is false ; ; Provided labels: %1_size (byte), %1_size_b (same), ; %1_size_w (words), %1_size_d (dwords), %1_size_q ; (qwords), %1_size_p (paragraphs), %1_size_pg (pages), ; %1_size_ki (KiB). %imacro endarea 1-2.nolist 0 %ifn %2 %1_size equ $ - %1 %endif %1_size_b equ %1_size %1_size_w equ %1_size+1 >>1 %1_size_d equ %1_size+3 >>2 %1_size_q equ %1_size+7 >>3 %1_size_p equ %1_size+15 >>4 %1_size_pg equ %1_size+511 >>9 %1_size_ki equ %1_size+1023 >>10 %endmacro ; The following macros replace NASM's standard struc macros. ; They add support for non-zero starting offsets of structures ; (optional %2 of struc is starting offset) and creating endarea's ; various size definitions with endstruc (if optional %1 of endstruc ; is true). %imacro struc 1-2.nolist 0 %push %define %$strucname %1 [absolute %2] %$strucname: %endmacro %imacro endstruc 0-1.nolist 0 %if %1 endarea %$strucname %else %{$strucname}_size equ ($-%$strucname) %endif %pop __SECT__ %endmacro %imacro istruc 1.nolist %push %define %$strucname %1 %$strucstart: %endmacro %imacro at 1-2+.nolist times (%1-%$strucname)-($-%$strucstart) db 0 %2 %endmacro %imacro iend 0.nolist times %{$strucname}_size-($-%$strucstart) db 0 %pop %endmacro %imacro checkeven 1.nolist %ifnnum %1 %error Number expected %endif %if %1 & 1 %error Even value expected %endif %endmacro ; Fill data to specified byte size (specify data) ; ; %1 = Byte size to fill ; %2 = Fill byte (eg 0, 90h, 32) ; %3 = Actual instruction to place before filling (optional) %imacro fill 2-3+.nolist %%data: %3 ; Actual data if given, else expands to nothing _fill %1,%2,%%data %endmacro ; Fill data to specified byte size (specify start label of data) ; ; %1 = Byte size to fill ; %2 = Fill byte (eg 0, 90h, 32) ; %3 = Start label of data %imacro _fill 3.nolist %if %1 < 0 ; hu? %error _fill: size is negative %elif $-(%3) < 0 ; should generate a phase error anyway. however %error _fill: start label must not be behind filling %elif (%1)-$+(%3) < 0 ; times would also throw an error then ("TIMES value x is negative") %error _fill: data too large, exceeds size ; but showing a more specific message won't hurt %endif times (%1)-$+(%3) db %2 ; Fill with fill byte to given size %endmacro ; Pad data to specified byte boundary (specify data) ; ; Does _not_ pad relative to the section start but relative to the start of the data. Use ; NASM's align (or alignd below) to pad (cough, cough... align) if you don't want that. ; ; %1 = Byte boundary ; %2 = Pad byte (eg 0, 90h, 32) ; %3 = Actual instruction to place before padding (optional, else macro creates nothing) %imacro pad 2-3+.nolist %%data: %3 ; Actual data if given, else expands to nothing _pad %1,%2,%%data %endmacro ; Pad data to specified byte boundary (specify start label of data) ; ; Does _not_ pad relative to the section start but relative to the start of the data. Use ; NASM's align (or alignd below) to pad (cough, cough... align) if don't you want that. ; ; %1 = Byte boundary ; %2 = Pad byte (eg 0, 90h, 32) ; %3 = Start label of data (if it equals $, no padding is created) %imacro _pad 3.nolist %if %1 < 0 ; hu? %error _pad: boundary is negative %elifn %1 %error _pad: boundary is zero %elif $-%3 < 0 ; should generate a phase error anyway. however %error _pad: start label must not be behind padding %endif %push %assign %$size $-%3 ; size of data %assign %$count 0 %rep %1 ; must be at the boundary then %ifn (%$count + %$size) % %1 ; if ( !((paddedcount + size) modulo boundary) ) %exitrep ; then { we're done } %endif %assign %$count %$count+1 ; else keep counting %endrep %if %$count >= %1 ; if loop end caused by %endrep %error pad: an unknown error occured %endif times (%$count-1) db %2 ; put this data for the assembler. but in _one_ line %pop %endmacro ; Similar to pad but using align: ; ; %1 = Byte boundary (relative from the section start) ; %2 = Align byte (eg 0, 90h, 32) ; %3 = Actual instruction to place before aligning (optional, else macro still aligns!) %imacro alignd 2-3+.nolist %3 %if %1 < 0 ; hu? %error alignd: boundary is negative %elifn %1 %error alignd: boundary is zero %else align %1,db %2 %endif %endmacro ; Comment a line out ; ; Purpose: In front of instructions that are only to be created if ; an expressions results in true, place a valid single-line ; macro name. Then use the commentif (or commentifn) macro ; below to create a single-line macro from an expression. ; ; This will use the macro "comment" so the instructions won't ; assemble if the expression doesn't result in true. (Or ; if the expression doesn't result in false, in case of ; commentifn.) Else the single-line macro gets defined to ; nothing causing the instructions to assemble. This "comment" ; macro is required because you can't define a single-line ; macro to the value of semicolon (";") with the preprocessor ; for obvious reasons. (It will think a real comment starts ; with the semicolon ;) %imacro comment 0-*.nolist %endmacro ; Specify a single-line macro for above comment macro purpose ; ; %1 = expression (gets placed after %if) ; %2 = single-line macro (gets underscore prefix) %imacro commentif 2.nolist %if %1 %define _%2 comment ; define it to invoke comment %else %define _%2 ; define it to a zero string %endif %endmacro ; Like commentif, but expression gets placed after %ifn %imacro commentifn 2.nolist %ifn %1 %define _%2 comment ; define it to invoke comment %else %define _%2 ; define it to a zero string %endif %endmacro ; Aborts assembling if found anywhere within source %imacro fixme 0-1+.nolist %error %1 %endmacro ; Comment a line out but make sure there are no parameters %imacro noparam 0-1+.nolist %if %0 %error Must not have parameters. %endif %endmacro ; Like noparam, except %1 is a message to display %imacro noparamnamed 1-2+.nolist %ifnempty %2 %error %1: Must not have parameters. %endif %endmacro ; Assign a single-line macro to an value if a condition matches, else zero ; ; %1 = expression (gets placed after %if) ; %2 = single-line macro (gets underscore prefix) ; %3 = value if condition matches ; %4 = value if condition doesn't match (default: 0) %imacro assignif 3-4.nolist 0 %if %1 %assign _%2 %3 %else %assign _%2 %4 %endif %endmacro ; Like assignif, but expression gets placed after %ifn %imacro assignifn 3-4.nolist 0 %ifn %1 %assign _%2 %3 %else %assign _%2 %4 %endif %endmacro %endif [list +]