1107 lines
32 KiB
Plaintext
1107 lines
32 KiB
Plaintext
[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 +]
|