From fb689c1372ac54d74a98d99cafd5e0d46ead0619 Mon Sep 17 00:00:00 2001 From: "C. Masloch" Date: Thu, 26 May 2022 21:30:34 +0200 Subject: [PATCH] import lmacros from snapshot of hg 9fa0e64034cd From https://hg.pushbx.org/ecm/lmacros/file/9fa0e64034cd --- test/lmacros/jn_warn.mac | 304 ++++++++ test/lmacros/lmacros1.mac | 1101 +++++++++++++++++++++++++++++ test/lmacros/lmacros2.mac | 1218 ++++++++++++++++++++++++++++++++ test/lmacros/lmacros3.mac | 137 ++++ test/lmacros/tests/000.asm | 68 ++ test/lmacros/tests/001.asm | 32 + test/lmacros/tests/002.asm | 37 + test/lmacros/tests/003.asm | 40 ++ test/lmacros/tests/004.asm | 34 + test/lmacros/tests/005.asm | 34 + test/lmacros/tests/006.asm | 43 ++ test/lmacros/tests/007.asm | 44 ++ test/lmacros/tests/008.asm | 51 ++ test/lmacros/tests/009.asm | 52 ++ test/lmacros/tests/010.asm | 49 ++ test/lmacros/tests/011.asm | 50 ++ test/lmacros/tests/012.asm | 48 ++ test/lmacros/tests/013.asm | 49 ++ test/lmacros/tests/014.asm | 56 ++ test/lmacros/tests/015.asm | 70 ++ test/lmacros/tests/016.asm | 64 ++ test/lmacros/tests/017.asm | 60 ++ test/lmacros/tests/018.asm | 36 + test/lmacros/tests/019.asm | 26 + test/lmacros/tests/test.sh | 8 + test/lmacros/tests/test2.asm | 51 ++ test/lmacros/tests/testmap.sh | 7 + test/lmacros/tests/testpar.asm | 58 ++ test/lmacros/tests/testsat.asm | 18 + 29 files changed, 3845 insertions(+) create mode 100644 test/lmacros/jn_warn.mac create mode 100644 test/lmacros/lmacros1.mac create mode 100644 test/lmacros/lmacros2.mac create mode 100644 test/lmacros/lmacros3.mac create mode 100644 test/lmacros/tests/000.asm create mode 100644 test/lmacros/tests/001.asm create mode 100644 test/lmacros/tests/002.asm create mode 100644 test/lmacros/tests/003.asm create mode 100644 test/lmacros/tests/004.asm create mode 100644 test/lmacros/tests/005.asm create mode 100644 test/lmacros/tests/006.asm create mode 100644 test/lmacros/tests/007.asm create mode 100644 test/lmacros/tests/008.asm create mode 100644 test/lmacros/tests/009.asm create mode 100644 test/lmacros/tests/010.asm create mode 100644 test/lmacros/tests/011.asm create mode 100644 test/lmacros/tests/012.asm create mode 100644 test/lmacros/tests/013.asm create mode 100644 test/lmacros/tests/014.asm create mode 100644 test/lmacros/tests/015.asm create mode 100644 test/lmacros/tests/016.asm create mode 100644 test/lmacros/tests/017.asm create mode 100644 test/lmacros/tests/018.asm create mode 100644 test/lmacros/tests/019.asm create mode 100755 test/lmacros/tests/test.sh create mode 100644 test/lmacros/tests/test2.asm create mode 100755 test/lmacros/tests/testmap.sh create mode 100644 test/lmacros/tests/testpar.asm create mode 100644 test/lmacros/tests/testsat.asm diff --git a/test/lmacros/jn_warn.mac b/test/lmacros/jn_warn.mac new file mode 100644 index 0000000..4deb70f --- /dev/null +++ b/test/lmacros/jn_warn.mac @@ -0,0 +1,304 @@ +[list -] +%if 0 + +Warn for near jumps +Public Domain by C. Masloch, 2018 + +%endif + +%ifndef __JN_WARN_MAC__ +%assign __JN_WARN_MAC__ 1 + + %imacro jz 1.nolist +%%start: + %? %1 +%%end: +%assign %%length %%end - %%start +%if %%length > 2 + %warning __FILE__ __LINE__ %? %1 %%length +%endif + %endmacro + %imacro jnz 1.nolist +%%start: + %? %1 +%%end: +%assign %%length %%end - %%start +%if %%length > 2 + %warning __FILE__ __LINE__ %? %1 %%length +%endif + %endmacro + %imacro je 1.nolist +%%start: + %? %1 +%%end: +%assign %%length %%end - %%start +%if %%length > 2 + %warning __FILE__ __LINE__ %? %1 %%length +%endif + %endmacro + %imacro jne 1.nolist +%%start: + %? %1 +%%end: +%assign %%length %%end - %%start +%if %%length > 2 + %warning __FILE__ __LINE__ %? %1 %%length +%endif + %endmacro + %imacro jc 1.nolist +%%start: + %? %1 +%%end: +%assign %%length %%end - %%start +%if %%length > 2 + %warning __FILE__ __LINE__ %? %1 %%length +%endif + %endmacro + %imacro jnc 1.nolist +%%start: + %? %1 +%%end: +%assign %%length %%end - %%start +%if %%length > 2 + %warning __FILE__ __LINE__ %? %1 %%length +%endif + %endmacro + %imacro jb 1.nolist +%%start: + %? %1 +%%end: +%assign %%length %%end - %%start +%if %%length > 2 + %warning __FILE__ __LINE__ %? %1 %%length +%endif + %endmacro + %imacro jnb 1.nolist +%%start: + %? %1 +%%end: +%assign %%length %%end - %%start +%if %%length > 2 + %warning __FILE__ __LINE__ %? %1 %%length +%endif + %endmacro + %imacro ja 1.nolist +%%start: + %? %1 +%%end: +%assign %%length %%end - %%start +%if %%length > 2 + %warning __FILE__ __LINE__ %? %1 %%length +%endif + %endmacro + %imacro jna 1.nolist +%%start: + %? %1 +%%end: +%assign %%length %%end - %%start +%if %%length > 2 + %warning __FILE__ __LINE__ %? %1 %%length +%endif + %endmacro + %imacro jae 1.nolist +%%start: + %? %1 +%%end: +%assign %%length %%end - %%start +%if %%length > 2 + %warning __FILE__ __LINE__ %? %1 %%length +%endif + %endmacro + %imacro jnae 1.nolist +%%start: + %? %1 +%%end: +%assign %%length %%end - %%start +%if %%length > 2 + %warning __FILE__ __LINE__ %? %1 %%length +%endif + %endmacro + %imacro jbe 1.nolist +%%start: + %? %1 +%%end: +%assign %%length %%end - %%start +%if %%length > 2 + %warning __FILE__ __LINE__ %? %1 %%length +%endif + %endmacro + %imacro jnbe 1.nolist +%%start: + %? %1 +%%end: +%assign %%length %%end - %%start +%if %%length > 2 + %warning __FILE__ __LINE__ %? %1 %%length +%endif + %endmacro + + + %imacro js 1.nolist +%%start: + %? %1 +%%end: +%assign %%length %%end - %%start +%if %%length > 2 + %warning __FILE__ __LINE__ %? %1 %%length +%endif + %endmacro + %imacro jns 1.nolist +%%start: + %? %1 +%%end: +%assign %%length %%end - %%start +%if %%length > 2 + %warning __FILE__ __LINE__ %? %1 %%length +%endif + %endmacro + + %imacro jo 1.nolist +%%start: + %? %1 +%%end: +%assign %%length %%end - %%start +%if %%length > 2 + %warning __FILE__ __LINE__ %? %1 %%length +%endif + %endmacro + %imacro jno 1.nolist +%%start: + %? %1 +%%end: +%assign %%length %%end - %%start +%if %%length > 2 + %warning __FILE__ __LINE__ %? %1 %%length +%endif + %endmacro + + %imacro jp 1.nolist +%%start: + %? %1 +%%end: +%assign %%length %%end - %%start +%if %%length > 2 + %warning __FILE__ __LINE__ %? %1 %%length +%endif + %endmacro + %imacro jnp 1.nolist +%%start: + %? %1 +%%end: +%assign %%length %%end - %%start +%if %%length > 2 + %warning __FILE__ __LINE__ %? %1 %%length +%endif + %endmacro + + + %imacro jl 1.nolist +%%start: + %? %1 +%%end: +%assign %%length %%end - %%start +%if %%length > 2 + %warning __FILE__ __LINE__ %? %1 %%length +%endif + %endmacro + %imacro jnl 1.nolist +%%start: + %? %1 +%%end: +%assign %%length %%end - %%start +%if %%length > 2 + %warning __FILE__ __LINE__ %? %1 %%length +%endif + %endmacro + %imacro jg 1.nolist +%%start: + %? %1 +%%end: +%assign %%length %%end - %%start +%if %%length > 2 + %warning __FILE__ __LINE__ %? %1 %%length +%endif + %endmacro + %imacro jng 1.nolist +%%start: + %? %1 +%%end: +%assign %%length %%end - %%start +%if %%length > 2 + %warning __FILE__ __LINE__ %? %1 %%length +%endif + %endmacro + %imacro jge 1.nolist +%%start: + %? %1 +%%end: +%assign %%length %%end - %%start +%if %%length > 2 + %warning __FILE__ __LINE__ %? %1 %%length +%endif + %endmacro + %imacro jnge 1.nolist +%%start: + %? %1 +%%end: +%assign %%length %%end - %%start +%if %%length > 2 + %warning __FILE__ __LINE__ %? %1 %%length +%endif + %endmacro + %imacro jle 1.nolist +%%start: + %? %1 +%%end: +%assign %%length %%end - %%start +%if %%length > 2 + %warning __FILE__ __LINE__ %? %1 %%length +%endif + %endmacro + %imacro jnle 1.nolist +%%start: + %? %1 +%%end: +%assign %%length %%end - %%start +%if %%length > 2 + %warning __FILE__ __LINE__ %? %1 %%length +%endif + %endmacro + + + %imacro jecxz 1.nolist +%%start: + %? %1 +%%end: +%assign %%length %%end - %%start +%if %%length > 2 + %warning __FILE__ __LINE__ %? %1 %%length +%endif + %endmacro + + %imacro jcxz 1.nolist +%%start: + %? %1 +%%end: +%assign %%length %%end - %%start +%if %%length > 2 + %warning __FILE__ __LINE__ %? %1 %%length +%endif + %endmacro + + + %imacro jmp 1.nolist +%%start: + %? %1 +%%end: +%assign %%length %%end - %%start +%if %%length > 2 && %%length < 5 + %warning __FILE__ __LINE__ %? %1 %%length +%endif + %endmacro + +%endif +[list +] diff --git a/test/lmacros/lmacros1.mac b/test/lmacros/lmacros1.mac new file mode 100644 index 0000000..39d307c --- /dev/null +++ b/test/lmacros/lmacros1.mac @@ -0,0 +1,1101 @@ +[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) + + +%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 +] diff --git a/test/lmacros/lmacros2.mac b/test/lmacros/lmacros2.mac new file mode 100644 index 0000000..717b227 --- /dev/null +++ b/test/lmacros/lmacros2.mac @@ -0,0 +1,1218 @@ +[list -] +%if 0 + +Extensions to NASM macro collection +Public Domain by C. Masloch, 2011-2012 +Intended for 86 Mode programs. + +%endif + +%ifndef __lMACROS2_MAC__ +%assign __lMACROS2_MAC__ 1 + +%include "lmacros1.mac" +[list -] + +%ifdef _186 + %ifnum _186 ; if appears like it is available: + numdef _lframe__use_186_def, 1 ; then default to use it + %endif +%endif + numdef _lframe__use_186_def ; use _186 to determine whether 186 available + numdef _lframe__force_186 ; regardless the above: assume 186 available + %assign __lframe__override_force_8086 0; regardless the above: temporarily stay 8086-compatible + + ; Initiate stack frame definition. + ; %1 Type of frame for parameter processing. + ; What this really means is only how large the return + ; address and other things between bp and the last + ; parameter are. + ; Default: none + ; Accepted: none, near (or 2), far (or 4), int (or 6), + ; positive even numbers + ; Notice: lret's auto %1 observes near/far/int here. + ; %2 Nesting type of frame. + ; Default: normal + ; Accepted: normal, nested, inner + ; + ; A nested frame pushes and sets bp anew. That means that + ; the labels of the outside frame are not usable. + ; An inner frame modifies sp but not bp. The outer frame's + ; variables may be accessed from within the inner frame. + ; A normal frame is neither a nested nor an inner frame. +%imacro lframe 0-2.nolist none,normal +%ifdef lsizeparameters + %undef lsizeparameters +%endif +%ifdef __lframe__autoret + %undef __lframe__autoret +%endif + +%ifidni %2, normal +%elifidni %2, nested +%elifidni %2, inner +%else + %fatal Invalid frame nesting type specified: %2 +%endif + +%ifctx LFRAME + %fatal Already inside lframe, must end previous with lenter +%elifctx LENTER + %ifidni %2, normal + %fatal Still inside lenter, must end previous with lleave + %endif +%elifctx LENTEREARLY + %ifidni %2, normal + %fatal Still inside lenter, must end previous with lleave + %endif +%endif +%ifnctx LENTER + %ifnidni %2, normal + %fatal Not inside lenter, cannot use nested or inner frame + %endif +%endif +%push LFRAME + +%assign %$bits __BITS__ +%if %$bits == 16 + %assign %$base_size 2 + %assign %$near_offset 2 + %assign %$far_offset 4 + %assign %$int_offset 6 +%elif %$bits == 32 + %assign %$base_size 4 + %assign %$near_offset 4 + %assign %$far_offset 8 + %assign %$int_offset 12 +%elif %$bits == 64 + %assign %$base_size 8 + %assign %$near_offset 8 + %assign %$far_offset 16 + %assign %$int_offset 24 +%else + %fatal Unknown BITS specified: %$bits +%endif + +%assign %$inner 0 +%ifidni %2, inner + %assign %$inner 1 +%endif +%assign %$nested 0 +%ifidni %2, nested + %assign %$nested 1 +%endif + +%define %$autoret +%ifidni %1,none +%elifidni %1,- +%elifidni %1,near + %define %$autoret near + %assign %$parofs %$near_offset +%elifidni %1,far + %define %$autoret far + %assign %$parofs %$far_offset +%elifidni %1,int + %define %$autoret int + %assign %$parofs %$int_offset +%elifnum %1 + %if %1 < 0 + %fatal Invalid frame type specified (negative): %1 + %endif + %if %1 & 1 + %fatal Invalid frame type specified (odd): %1 + %endif + %assign %$parofs %1 +%else + %fatal Invalid frame type specified: %1 +%endif +%assign %$pars_size 0 +%assign %$pars_return_size 0 +%define %$parlist +%define %$labellist empty,empty,empty +%assign %$ofs 0 +%assign %$enter 0 +%assign %$req 0 +%assign %$usereq 0 +%assign %$emit 1 + +%assign %$restore_frame_ofs 0 +%if %$inner + %assign %$ofs %$$ofs + %assign %$restore_frame_ofs %$$ofs +%endif +%assign %$pars_inner_size 0 +%endmacro + + ; Indicate that the stack frame set-up should not + ; actually write any code (is not required) if no + ; parameters or variables are registered. + ; Note that if no variables or parameters are + ; registered before lenter, and this is used, then it + ; is an error to register variables after lenter. + ; As a workaround, do not use this then, or additionally + ; use lframe_needotherwise (effectively overriding this). + ; + ; This must be placed before lenter. +%imacro lframe_needonlyregistered 0.nolist +%ifnctx LFRAME + %fatal Must use lframe first +%endif +%assign %$usereq 1 +%endmacro + + ; Indicate that the stack frame set-up is necessary, + ; even if no parameters or variables are registered. + ; + ; This must be placed before lenter. +%imacro lframe_needotherwise 0.nolist +%ifnctx LFRAME + %fatal Must use lframe first +%endif +%assign %$req 1 +%endmacro + +%macro __lvar_check_duplicate_label 3-*.nolist +%if %0 % 3 + %fatal Expected number of parameters that is a multiple of three +%endif +%rep (%0 / 3) - 1 + %ifidn %1, %$newlabel + %fatal Duplicate label: %1 + %endif + %rotate 3 +%endrep +%endmacro + +%macro __lvar_list_label 1.nolist +%define %$newlabel %1 +__lvar_check_duplicate_label %$labellist +%ifdef ?%1 + %xdefine %$labellist %1,defined,?%1,%$labellist +%else + %xdefine %$labellist %1,undefined,,%$labellist +%endif +%endmacro + +%macro __lvar_define_var 2.nolist +__lvar_list_label %1 +%assign ?%1 %2 +._%$lframe_?%1: equ %2 +%endmacro + + ; Define a stack frame variable. + ; %1 Type or size of variable. + ; Accepted: byte (1), word (2), dword (4), numeric + ; %2 Name of variable. A question mark is prepended. + ; + ; Variables registered before lenter are accounted for + ; in the code lenter generates, ie uninitialised space + ; is automatically allocated for them on the stack. + ; Variables registered after lenter are not automatically + ; allocated, you must manually either push data onto the + ; stack (possibly with desired initial values) or adjust + ; sp to allocate space for them on the stack. + ; Defining variables after "lleave code" is untested and + ; potentially has unexpected results. +%imacro lvar 2.nolist +%ifctx LENTER + %ifn %$enter + %fatal lenter did not set up stack frame + %endif +%elifctx LENTEREARLY +%elifnctx LFRAME + %fatal Must use lframe first +%endif +%ifidni %1,byte + %assign %$varsize 1 +%elifidni %1,word + %assign %$varsize 2 +%elifidni %1,dword + %assign %$varsize 4 +%elifidni %1,qword + %assign %$varsize 8 +%else + ; Previously we checked with %elifnum here that the parameter + ; is numeric. That turned out not to be what we want in + ; https://hg.pushbx.org/ecm/inicomp/file/dc376b09985e/exodecr.asm#l153 + ; In that case we used lequ as a workaround but it really + ; should be allowed directly as lvar size specification. + ; Therefore, we give the parameter to %assign and let it fail + ; in case the expression doesn't result in a scalar number. + %assign %$varsize %1 +%endif +%assign %$ofs %$ofs-%$varsize +__lvar_define_var %2, %$ofs +%assign %$req 1 +%assign %$hadvar 1 +%endmacro + + ; Define a stack frame parameter. + ; %1 Type or size of parameter. + ; Accepted: byte (1), word (2), dword (4), numeric + ; %2 Name of parameter. A question mark is prepended. + ; + ; Parameters must be registered before lenter, as lenter + ; contains the mechanism to automatically calculate the + ; final offsets. This is necessary to allow registering + ; parameters in the same order they have to be pushed + ; onto the stack by the caller. +%imacro lpar 2.nolist +%ifnctx LFRAME + %fatal Must use lframe first +%endif +%ifndef %$parofs + %fatal Specified no frame type with lframe +%endif +%ifdef %$hadvar + %fatal lpar must occur before any lvar + ; This is for lframe inner-type frames + ; which modify %$ofs in lpar. +%endif +%ifidni %1,byte + %assign %$varsize 1 +%elifidni %1,word + %assign %$varsize 2 +%elifidni %1,dword + %assign %$varsize 4 +%elifidni %1,qword + %assign %$varsize 8 +%elifnum %1 + %assign %$varsize %1 +%else + %fatal Invalid type specified: %1 +%endif +__lvar_list_label %2 +%assign %$pars_size %$pars_size+%$varsize +%ifempty %$parlist + %xdefine %$parlist %$varsize,%2 +%else + %xdefine %$parlist %$parlist,%$varsize,%2 +%endif +%assign %$req 1 +%if %$inner + %assign %$pars_inner_size %$pars_inner_size + %$varsize + %assign %$ofs %$ofs - %$varsize +%endif +%endmacro + + ; Note to return all prior defined stack frame parameters. + ; This simply subtracts the current length of parameters + ; from the length that is to be popped off the stack by lret. + ; + ; This is sort of a hack. May incorrectly handle cases of + ; byte (or otherwise odd-sized) parameters. +%imacro lpar_return 0.nolist +%ifnctx LFRAME + %fatal Must use lframe first +%endif +%ifndef %$parofs + %fatal Specified no frame type with lframe +%endif +%ifempty %$parlist + %fatal Specified no parameters with lpar +%endif +%ifn %$pars_size + %fatal Specified no parameters with lpar +%endif +%assign %$pars_return_size %$pars_size +%assign %$req 1 +%endmacro + + ; Define a stack frame equ. + ; %1 Numeric value to assign to this equ. + ; %2 Name of equ. A question mark is prepended. + ; + ; The equ's lifetime is the same as the frame's, just like + ; for lvar and lpar labels. (All are internally defines.) +%imacro lequ 2.nolist +%ifctx LENTER +%elifctx LENTEREARLY +%elifnctx LFRAME + %fatal Must use lframe first +%endif +__lvar_define_var %2, %1 +%endmacro + + ; Create a defined stack frame. + ; This sets up bp and reserves stack space for all defined + ; variables. It also defines the labels for parameters. + ; If no variables or parameters were registered until now, + ; and lframe_needonlyregistered has been used (without + ; overriding it by using lframe_needotherwise), then it is + ; assumed that no more variables will be registered after + ; this and that the stack frame is unnecessary. It will + ; thus not be created. lenter and lleave will consequently + ; not create any code. + ; Defines %$lsizeparameters to be the even number of bytes + ; all parameters take up, and %$lsizevariables to be the + ; even number of bytes all variables take up. + ; The code created by this does not change any flags, as it + ; uses either (one or two) "push ax" or "lea sp, [bp - x]" + ; or "enter x, 0" to reserve the stack space. + ; + ; "lenter early" can be used to write only the initial + ; "push bp" \ "mov bp, sp" sequence. Then variables are + ; defined with lvar as usual. However, these variables + ; can *optionally* be initialised by pushing into them + ; (similarly to lvar variables defined after lenter). + ; Finally, a "lenter" acts as the default lenter does, + ; and reserves stack space for all yet defined lvar + ; variables. This is always done using "lea sp, [bp - x]" + ; so it doesn't matter how many of the variables were + ; already initialised by pushing into them. +%imacro lenter 0-1.nolist +%ifctx LENTEREARLY +%elifnctx LFRAME + %fatal Must use lframe first +%endif +%ifidni %1,early + %ifctx LENTEREARLY + %fatal Cannot use lenter early twice + %endif + %repl LENTEREARLY + %assign %$req 1 + %assign %$early 1 + %assign %$wasearly 0 +%elifempty %1 + %assign %$wasearly 0 + %ifctx LENTEREARLY + %assign %$wasearly 1 + %endif + %assign %$early 0 + %repl LENTER +%else + %fatal Invalid parameter: %1 +%endif +%assign %$lsizevariables (-%$ofs+1 - (- %$restore_frame_ofs) - %$pars_inner_size )&~1 +%ifn %$usereq + %assign %$req 1 +%endif +%assign %$enter_emitted 0 +%if %$req + %assign %$enter 1 + %assign %$186 0 + %if %$early || %$wasearly + %elif __lframe__override_force_8086 + %elif __lframe__force_186 + %assign %$186 1 + %elif __lframe__use_186_def + %assign %$186 _186 + %endif + %if %$186 && %$ofs && (! %$inner) && %$emit + enter %$lsizevariables, 0 + %assign %$enter_emitted 1 + %endif +%ifnidn %$bits,__BITS__ + %fatal BITS of lframe (%$bits) differs from current: __BITS__ +%endif +%if %$bits == 16 +%ifn %$inner +%ifn %$wasearly + __lvar_define_var frame_bp, 0 + %ifidni %$autoret, near + __lvar_define_var frame_ip, 2 + %elifidni %$autoret, far + __lvar_define_var frame_ip, 2 + __lvar_define_var frame_cs, 4 + %elifidni %$autoret, int + __lvar_define_var frame_ip, 2 + __lvar_define_var frame_cs, 4 + __lvar_define_var frame_fl, 6 + %endif +%endif +%endif + %if %$emit + %ifn %$enter_emitted + %ifn %$inner + %ifn %$wasearly + push bp + mov bp, sp + %endif + %endif + %ifn %$early + %if %$ofs + %if %$wasearly == 0 && (%$lsizevariables) == 0 + %elif %$wasearly == 0 && (%$lsizevariables) == 2 + push ax + %elif %$wasearly == 0 && (%$lsizevariables) == 4 + push ax + push ax + %else + lea sp, [bp + %$restore_frame_ofs - (%$lsizevariables + %$pars_inner_size)] + %endif + %endif + %endif + %endif + %endif +%elif %$bits == 32 +%ifn %$inner +%ifn %$wasearly + __lvar_define_var frame_ebp, 0 + %ifidni %$autoret, near + __lvar_define_var frame_eip, 4 + %elifidni %$autoret, far + __lvar_define_var frame_eip, 4 + __lvar_define_var frame_cs, 8 + %elifidni %$autoret, int + __lvar_define_var frame_eip, 4 + __lvar_define_var frame_cs, 8 + __lvar_define_var frame_efl, 12 + %endif +%endif +%endif + %if %$emit + %ifn %$enter_emitted + %ifn %$inner + %ifn %$wasearly + push ebp + mov ebp, esp + %endif + %endif + %ifn %$early + %if %$ofs + %if %$wasearly == 0 && (%$lsizevariables) == 0 + %elif %$wasearly == 0 && (%$lsizevariables) == 2 + push ax + %elif %$wasearly == 0 && (%$lsizevariables) == 4 + push eax + %elif %$wasearly == 0 && (%$lsizevariables) == 8 + push eax + push eax + %else + lea esp, [ebp + %$restore_frame_ofs - (%$lsizevariables + %$pars_inner_size)] + %endif + %endif + %endif + %endif + %endif +%elif %$bits == 64 +%ifn %$inner +%ifn %$wasearly + __lvar_define_var frame_rbp, 0 + %ifidni %$autoret, near + __lvar_define_var frame_rip, 8 + %elifidni %$autoret, far + __lvar_define_var frame_rip, 8 + __lvar_define_var frame_cs, 16 + %elifidni %$autoret, int + __lvar_define_var frame_rip, 8 + __lvar_define_var frame_cs, 16 + __lvar_define_var frame_rfl, 24 + %endif +%endif +%endif + %if %$emit + %ifn %$enter_emitted + %ifn %$inner + %ifn %$wasearly + push rbp + mov rbp, rsp + %endif + %endif + %ifn %$early + %if %$ofs + %if %$wasearly == 0 && (%$lsizevariables) == 0 + %elif %$wasearly == 0 && (%$lsizevariables) == 2 + push ax + %elif %$wasearly == 0 && (%$lsizevariables) == 8 + push rax + %else + lea rsp, [rbp + %$restore_frame_ofs - (%$lsizevariables + %$pars_inner_size)] + %endif + %endif + %endif + %endif + %endif +%else + %fatal Unknown BITS specified: %$bits +%endif +%ifn %$wasearly + ._%$lenter: +%endif +%endif +%ifn %$wasearly + %assign %$lsizeparameters (%$pars_size+1)&~1 + __lenter_parse_parlist %$parlist +%endif +%endmacro + +%macro __lenter_parse_parlist 0-*.nolist +%if %0 & 1 + %fatal Expected even number of macro parameters! +%endif +%rep %0 >> 1 +%assign %$pars_size %$pars_size - %1 +%if %$inner +%assign ?%2 (%$pars_size + %$parofs + %$restore_frame_ofs - %$pars_inner_size) +._%$lframe_?%2: equ (%$pars_size + %$parofs + %$restore_frame_ofs - %$pars_inner_size) +%else +%assign ?%2 (%$pars_size + %$parofs + %$base_size) +._%$lframe_?%2: equ (%$pars_size + %$parofs + %$base_size) +%endif +%rotate 2 +%endrep +%endmacro + + ; Reserve stack space for prior lvar variables. + ; + ; This is used after lenter and reserves the stack space + ; for all yet defined variables, including those defined + ; after the lenter usage (which aren't reserved space for + ; by lenter itself). + ; + ; "lenter early \ lvar a \ lenter \ lvar b" is equivalent + ; to "lenter \ lvar a \ lreserve \ lvar b". + ; + ; lreserve can be used multiple times. It always uses + ; "lea sp, [bp - x]" to reserve space, so the space will + ; always be equal to the already defined variables. + ; + ; Warning: Using lreserve after having pushed additional + ; data, beyond the defined variables, will discard that + ; data from the stack as sp is reset. +%imacro lreserve 0.nolist +%ifnctx LENTER + %fatal Must use lenter first +%endif +%ifn %$enter + %fatal lenter did not set up stack frame +%endif +%assign %$lsizevariables (-%$ofs+1)&~1 +%ifnidn %$bits,__BITS__ + %fatal BITS of lframe (%$bits) differs from current: __BITS__ +%endif +%if %$emit +%if %$bits == 16 + %if %$ofs + lea sp, [bp - %$lsizevariables] + %endif +%elif %$bits == 32 + %if %$ofs + lea esp, [ebp - %$lsizevariables] + %endif +%elif %$bits == 64 + %if %$ofs + lea rsp, [rbp - %$lsizevariables] + %endif +%else + %fatal Unknown BITS specified: %$bits +%endif +%endif +%endmacro + + +%imacro ldup 0.nolist +%ifnctx LENTER + %fatal Must use lenter first +%endif +%ifn %$nested + %fatal ldup only valid in nested frame in normal frame +%endif +%if %$$inner + %fatal ldup only valid in nested frame in normal frame +%endif +%if %$enter != %$$enter + %fatal Failed ldup (enter) +%endif +%ifnempty %$autoret + %ifnidn %$autoret, %$$autoret + %fatal Failed ldup (autoret) + %endif +%else + %ifnempty %$$autoret + %fatal Failed ldup (autoret one empty) + %endif +%endif +%if %$parofs != %$$parofs + %fatal Failed ldup (parofs) +%endif +%if %$bits != %$$bits + %fatal Failed ldup (bits) +%endif +%if %$pars_return_size != %$$pars_return_size + %fatal Failed ldup (pars_return_size) +%endif +__ldup_count newparlist, %$parlist +__ldup_count oldparlist, %$$parlist +%if %$count_newparlist != %$count_oldparlist + %fatal Failed ldup (count_*parlist) +%endif +__ldup_check_parlist %$$parlist +__ldup_count newlabellist, %$labellist +__ldup_count oldlabellist, %$$labellist +%if %$count_newlabellist != %$count_oldlabellist + %fatal Failed ldup (count_*labellist) +%endif +__ldup_check_labellist %$$labellist +%endmacro + +%macro __ldup_count 1-*.nolist +%assign %$count_%[%1] %0 - 1 +%endmacro + +%macro __ldup_check_parlist 0-*.nolist +%assign %$ii 0 +%rep %0 + %define %$check %1 + __ldup_check_single %$ii + 1,%$parlist + %assign %$ii %$ii + 1 + %rotate 1 +%endrep +%endmacro + +%macro __ldup_check_labellist 3-*.nolist +%assign %$ii 0 +%rep (%0 / 3) - 1 + %define %$check %1 + __ldup_check_single %$ii + 1,%$labellist + %xdefine %$check ?%1 + __ldup_check_single %$ii + 1 + 2,%$labellist + %assign %$ii %$ii + 3 + %rotate 3 +%endrep +%endmacro + +%macro __ldup_check_single 1-*.nolist +%rotate %1 +%ifnidn %1, %$check + ; The following is a workaround for older versions of NASM. + ; Refer to https://bugzilla.nasm.us/show_bug.cgi?id=3392628 + %ifnnum %1 + %fatal Failed ldup (check single: 1 1=%1 check=%$check) + %elifnnum %$check + %fatal Failed ldup (check single: 2 1=%1 check=%$check) + %elifn %1 == %$check + %fatal Failed ldup (check single: 3 1=%1 check=%$check) + %endif +%endif +%endmacro + + +%imacro lemit 0-1.nolist on +%ifctx LENTER +%elifctx LENTEREARLY +%elifnctx LFRAME + %fatal Must use lframe first +%endif +%ifidni %1,on + %assign %$emit 1 +%elifidni %1,off + %assign %$emit 0 +%else + %assign %$emit !! %1 +%endif +%endmacro + + +%imacro lifemit 1+.nolist +%ifctx LENTER +%elifctx LENTEREARLY +%elifnctx LFRAME + %fatal Must use lframe first +%endif +%if %$emit + %1 +%endif +%endmacro + + + ; Remove a created stack frame. + ; This restores bp if it was altered to access variables or + ; parameters. It also restores sp from bp if sp was altered + ; to allocate space to variables. + ; The code created by this does not change any flags, as it + ; uses only "mov sp, bp" and "pop bp", or "leave". + ; This undefines all labels that were defined for variables + ; and parameters in this stack frame, to prevent accidental + ; usage of these labels later. + ; lsizeparameters is defined as the (even) number of bytes + ; that all parameters use on the stack. (This is zero if no + ; parameters have been registered.) + ; No return instruction is written by this macro; you may use + ; lret or place the appropriate instruction(s) manually. + ; If none of lframe_needonlyregistered,needotherwise have + ; been used, and no variables or parameters at all have been + ; registered (regardless whether before or after lenter), + ; then this will stop with an error. + ; %1 Specifies whether to write the stack frame removal + ; code or to remove the macros' context. This allows + ; writing the code multiple times. + ; Default: code+ctx + ; Accepted: code+ctx (or ), code, ctx + ; %2 Specifies whether to always restore sp. + ; Default: optimiserestoresp + ; Accepted: optimiserestoresp (or ), forcerestoresp +%imacro lleave 0-2.nolist +%ifnctx LENTER + %fatal Must use lenter first +%endif +%ifidni %1,code +%elifidni %1,ctx +%elifidni %1,code+ctx +%elifempty %1 +%else + %fatal Invalid parameter specified: %1 +%endif +%ifidni %2,forcerestoresp +%elifidni %2,optimizerestoresp +%elifidni %2,optimiserestoresp +%elifempty %2 +%else + %fatal Invalid second parameter specified: %2 +%endif + %ifnidni %1,ctx +%ifn %$inner +%if %$enter + %ifn %$req + %fatal lframe apparently unnecessary + ; Note that to fix this, you either have to remove + ; the lframe usage altogether, or employ one of those: + ; * lframe_needonlyregistered + ; * lframe_needotherwise + ; See the descriptions of those. + %endif + %assign %$186 0 + %if __lframe__override_force_8086 + %elif __lframe__force_186 + %assign %$186 1 + %elif __lframe__use_186_def + %assign %$186 _186 + %endif + %ifn %$ofs + %ifnidni %2,forcerestoresp + %assign %$186 0 + %endif + %endif +%if %$emit + %if %$186 + leave + %else +%ifnidn %$bits,__BITS__ + %fatal BITS of lframe (%$bits) differs from current: __BITS__ +%endif +%if %$bits == 16 + %if %$ofs + mov sp, bp + %elifidni %2,forcerestoresp + mov sp, bp + %endif + pop bp +%elif %$bits == 32 + %if %$ofs + mov esp, ebp + %elifidni %2,forcerestoresp + mov esp, ebp + %endif + pop ebp +%elif %$bits == 64 + %if %$ofs + mov rsp, rbp + %elifidni %2,forcerestoresp + mov rsp, rbp + %endif + pop rbp +%else + %fatal Unknown BITS specified: %$bits +%endif + %endif +%endif +%else + %if %$ofs + %fatal No stack frame was created + %elifidni %2,forcerestoresp + %fatal No stack frame was created + %endif +%endif +%endif +%if %$inner +%ifnidn %$bits,__BITS__ + %fatal BITS of lframe (%$bits) differs from current: __BITS__ +%endif +%if %$emit +%if %$bits == 16 + lea sp, [bp + %$restore_frame_ofs - %$pars_return_size] +%elif %$bits == 32 + lea esp, [ebp + %$restore_frame_ofs - %$pars_return_size] +%elif %$bits == 64 + lea rsp, [rbp + %$restore_frame_ofs - %$pars_return_size] +%else + %fatal Unknown BITS specified: %$bits +%endif +%endif +%endif + %else +%ifnempty %2 + %fatal Invalid to specify second parameter +%endif + %endif +%ifdef %$parofs + %assign lsizeparameters %$lsizeparameters - %$pars_return_size +%endif +%xdefine __lframe__autoret %$autoret + %ifnidni %1,code +._%$lleave: +__lleave_undefine_labels %$labellist +%pop + %endif +%endmacro + +%macro __lleave_undefine_labels 3-*.nolist +%if %0 % 3 + %fatal Expected number of parameters that is a multiple of three +%endif +%rep (%0 / 3) - 1 + %ifidn %2, undefined + %undef ?%1 + %elifidn %2, defined + %define ?%1 %3 + %else + %fatal Expected parameter to be 'defined' or 'undefined' but is %2 + %endif + %rotate 3 +%endrep +%endmacro + + ; Place matching return instruction. + ; This uses lsizeparameters, if it has been set by + ; lleave, as the size of all parameters on the + ; caller's stack frame. + ; This generally must be used after using lleave. + ; This may be used any number of times. + ; %1 Specifies type of return. + ; Default: auto + ; Accepted: auto, near, far, int + ; Notice: auto will use lframe's %1 only if it was + ; one of near, far, int. Otherwise, auto + ; (including the default) is an error! + ; %2 Specifies an adjustment to remove either more + ; or less than all the registered parameters + ; from the caller's stack frame. + ; Default: 0 + ; Accepted: even numbers, including negatives. + ; Must be at least -lsizeparameters! + ; Notice: The sum of lsizeparameters and %2 + ; must be 0 for type int (ie iret). + ; + ; Note: As this is usually used after the lframe's + ; context is already popped, the lemit + ; setting does not affect this macro. +%imacro lret 0-2.nolist auto,0 +%ifctx LFRAME + %fatal Must use lenter first +%elifctx LENTEREARLY + %fatal Must use lenter first +%endif +; Do not check specifically for ctx LENTER here! +; Usually, lleave already popped the ctx. +%push + %define %$type %1 + %ifidni %$type,- + %define %$type auto + %endif + %ifempty %$type + %define %$type auto + %endif + %ifidni %$type,auto + %ifdef __lframe__autoret + %ifnempty __lframe__autoret + %define %$type __lframe__autoret + %endif + %endif + %endif + %ifidni %$type,near + %define %$inst retn + %elifidni %$type,far + %define %$inst retf + %elifidni %$type,int + %define %$inst iret + %else + %fatal Invalid type specified: %1 + %endif + %if %2 & 1 + %fatal Invalid adjustment %2, is odd + %endif + %assign %$remov %2 + %ifnum lsizeparameters + %assign %$remov %$remov+lsizeparameters + %endif + %if %$remov < 0 + %fatal Invalid adjustment %2, sum is negative + %endif + %ifn %$remov + %define %$remov + %endif + %$inst %$remov +%pop +%endmacro + + + +%unimacro @@ 1+.nolist +%unimacro @@ 0-1+.nolist + + ; Core macro implementing anonymous local labels that behave + ; similarly to (but not exactly the same way as) MS MASM's. + ; It shifts @FFFF..@BBBB by one and then invents a new label + ; name for @FFFF. The invented names contain incrementing + ; numbers to insure they are unique, and all start with ..@ + ; to avoid interfering with NASM's named local labels. + ; + ; Provided (cap-insensitive) smacros: + ; @FFFF (or @F4) Forward reference to fourth @@ + ; @FFF (or @F3) Forward reference to third @@ + ; @FF (or @F2) Forward reference to second @@ + ; @F (or @F1) Forward reference to next @@ + ; @B (or @B1) Backward reference to previous @@ + ; @BB (or @B2) Backward reference to second @@ + ; @BBB (or @B3) Backward reference to third @@ + ; @BBBB (or @B4) Backward reference to fourth @@ + ; (It is recommended to seldom use anything but @F and @B.) + ; Internally used (cap-sensitive) smacro: + ; __@@_seq Incremented by @S, initialised to 0 + ; by including this file. This gives + ; the first number of invented names. + ; __@@_num Incremented by @@, initialised to 4 + ; by including this file as well as + ; by @S. This gives the second number + ; of invented names. (Note that the + ; current value is always what @@ + ; will invent for the new @FFFF; thus + ; eg the current @F refers to a name + ; that was invented with the current + ; value less four. Consequently the + ; first @@ defines a label whose name + ; contains a 0 as the second number.) + ; Invented names, full form: + ; ..@.@@.%[__@@_seq].%[__@@_num] + ; eg ..@.@@.0.0, ..@.@@.0.1, etc; ..@.@@.1.0, etc + ; + ; Note that as NASM has no procedure concept, the @@ labels + ; of logically distinct code sequences aren't automatically + ; reset, that is an @F near the end of one code sequence + ; whose intended target @@ erroneously wasn't placed will + ; jump to the next placed @@ after the code sequence. + ; To work around that, manually place @S (start sequence for + ; @@ labels) between logically distinct code sequences. For + ; more strict checks, put @I (invalidate @@ references) + ; whereever a logically distinct code sequence ends, and + ; @S whereever a logically distinct code sequence begins. + ; (This disallows any @@ references outside such marked + ; code sequences.) + + ; This + ; will cause all erroneous @@ forward references in front + ; of it and all erroneous @@ backward references behind it + ; to refer to undefined labels. + ; The former will have a (regular) positive second number in + ; the referenced label name, while the latter will have a + ; (special) negative second number -4..-1 in the referenced + ; label name. The line on which the error occurs is the + ; line with the erroneous reference. + %macro __@@_next 0-1.nolist 1 +%if _@@_check && ! __@@_in_seq + %if _@@_check == 1 + %warning "not in a sequence" + %else + %error "not in a sequence" + %endif +%endif + %rep %1 +%ixdefine @BBBB @BBB +%ixdefine @BBB @BB +%ixdefine @BB @B +%ixdefine @B @F +%ixdefine @F @FF +%ixdefine @FF @FFF +%ixdefine @FFF @FFFF +%idefine @FFFF ..@.@@.%[__@@_seq].%[__@@_num] +%ixdefine @B4 @BBBB +%ixdefine @B3 @BBB +%ixdefine @B2 @BB +%ixdefine @B1 @B +%ixdefine @F1 @F +%ixdefine @F2 @FF +%ixdefine @F3 @FFF +%ixdefine @F4 @FFFF + +%assign __@@_num __@@_num+1 + %endrep + %endmacro + + ; This macro defines an anonymous local label (optionally + ; writing any kind of code afterwards) + ; then it calls the above macro to shift the pre-invented + ; names and invent a new one for @FFFF. + ; Note that smacros inside following code if any are + ; expanded by NASM's preprocessor before processing mmacros + ; hence "@@: jmp @F" is equivalent to "@@: jmp $". + ; (If correct label missing colon warning behaviour wasn't + ; wanted, then the implementation could be simpler. Hmm.) + %macro @@ 0.nolist +@F +__@@_next + %endmacro + ; "@@", label alone on line missing colon warning. + %macro @@ 1+.nolist +%ifidn %1,: +@F %+ : +__@@_next +%else + %push + %defstr %$str %1 + %substr %$colon %$str 1 + %ifidn %$colon,':' + %substr %$str %$str 2,-1 + %deftok %$str %$str +@F %+ : +__@@_next +%$str + %else +@F times 0 nop +__@@_next +%1 + %endif + %pop +%endif + %endmacro + ; "@@ :", (trivial) no label-missing-colon warnings. + ; "@@ nop", label missing colon warning; the "times 0 nop" + ; part is necessary so that this warning isn't 'promoted' + ; to the label alone on line missing colon warning. + ; "@@ : nop", no label-missing-colon warnings. + ; Not handled like actual label: following "instruction" is + ; allowed to be a directive, another label, or even an + ; mmacro usage like @@ itself. Hmmm... allows eg "@@: @@:" + ; to define two @@ labels at once. May be useful. + + numdef @@_check, 0 ; 0 = none, 1 = warn, else = error + numdef @@_start, 1 ; 0 = omit @S, else = do @S + + ; Only invalidate outstanding @@ forward references. + %imacro @FI 0.nolist +%assign __@@_num_inv __@@_num_inv+1 +%idefine @FFFF ..@.@@.%[__@@_seq].inv.%[__@@_num_inv] +%assign __@@_num_inv __@@_num_inv+1 +%idefine @FFF ..@.@@.%[__@@_seq].inv.%[__@@_num_inv] +%assign __@@_num_inv __@@_num_inv+1 +%idefine @FF ..@.@@.%[__@@_seq].inv.%[__@@_num_inv] +%assign __@@_num_inv __@@_num_inv+1 +%idefine @F ..@.@@.%[__@@_seq].inv.%[__@@_num_inv] +%ixdefine @F4 @FFFF +%ixdefine @F3 @FFF +%ixdefine @F2 @FF +%ixdefine @F1 @F + %endmacro + + ; Only invalidate @@ backward references. + %imacro @BI 0.nolist +%assign __@@_num_inv __@@_num_inv+1 +%idefine @B ..@.@@.%[__@@_seq].inv.%[__@@_num_inv] +%assign __@@_num_inv __@@_num_inv+1 +%idefine @BB ..@.@@.%[__@@_seq].inv.%[__@@_num_inv] +%assign __@@_num_inv __@@_num_inv+1 +%idefine @BBB ..@.@@.%[__@@_seq].inv.%[__@@_num_inv] +%assign __@@_num_inv __@@_num_inv+1 +%idefine @BBBB ..@.@@.%[__@@_seq].inv.%[__@@_num_inv] +%ixdefine @B4 @BBBB +%ixdefine @B3 @BBB +%ixdefine @B2 @BB +%ixdefine @B1 @B + %endmacro + + ; Invalidate all references. + ; The sequence flag is reset, unless non-zero %1 is given. + %imacro @I 0-1.nolist 0 +%if __@@_in_seq && %1 + %assign __@@_in_seq 1 +%else + %assign __@@_in_seq 0 +%endif +@FI +@BI + %endmacro + + ; Invalidate outstanding @@ forward references (by causing + ; them to ultimately refer to undefined labels) then invent + ; four new names for @BBBB..@B that are invalid (ie would + ; ultimately refer to undefined labels) then invent four + ; new names for @F..@FFFF that can be defined by @@. + ; Note that __@@_next successfully passes along whatever + ; @F..@FFFF are initialised to when this macro is used; + ; even if they aren't initialised at all (in that case + ; their names are passed along!). No error conditions are + ; caused there, and all of these passed names are discarded + ; by @BI. + ; As __@@_num is initialised to -4, at the end of this + ; macro's processing the @@ backward references @BBBB..@B + ; refer to names ending in -4..-1 (which are to be left + ; undefined) and the @@ forward references @F..@FFFF refer + ; to ..@.@@.%[__@@_seq].0..3 (which can be defined by @@). + %imacro @S 0-1.nolist _@@_check +%if %1 && __@@_in_seq + %if %1 == 1 + %warning "already in a sequence" + %else + %error "already in a sequence" + %endif +%endif + +%assign __@@_seq __@@_seq+1 +%assign __@@_num 0 +%assign __@@_num_inv 0 +%assign __@@_in_seq 1 + +__@@_next 4 +@BI + %endmacro + +%ifnnum __@@_in_seq + %assign __@@_in_seq 0 +%endif +%ifnnum __@@_seq + %assign __@@_seq 0 + %assign __@@_num 0 + %assign __@@_num_inv 0 +%endif +%ifnnum __@@_num + %assign __@@_num 0 +%endif +%ifnnum __@@_num_inv + %assign __@@_num_inv 0 +%endif +%if _@@_start + @S +%endif + + + %imacro checklframeend 0 +%ifctx LFRAME + %fatal Still inside lframe! +%elifctx LENTER + %fatal Still inside lenter! +%elifctx LENTEREARLY + %fatal Still inside lenter! +%endif + %endmacro + + + %imacro _struc_at 2-3+.nolist +%if %1 + %if (%2 - ($ - %$strucname)) != 0 + %if %1 == 1 + %error Not at that offset! + %else + %warning Not at that offset! + %endif + %endif +%endif + resb %2 - ($ - %$strucname) +%3 + %endmacro + +%idefine struc_at _struc_at 0, +%idefine exact_struc_at _struc_at 1, +%idefine warn_struc_at _struc_at 2, + + +%endif +[list +] diff --git a/test/lmacros/lmacros3.mac b/test/lmacros/lmacros3.mac new file mode 100644 index 0000000..d9420a7 --- /dev/null +++ b/test/lmacros/lmacros3.mac @@ -0,0 +1,137 @@ +[list -] +%if 0 + +Extensions to NASM macro collection +Public Domain by C. Masloch, 2019 +Intended for 86 Mode programs. + +%endif + +%ifndef __lMACROS3_MAC__ +%assign __lMACROS3_MAC__ 1 + +%include "lmacros2.mac" +[list -] + + + ; This macro is used to generate byte or word access to specific + ; bits of a dword variable. + ; %1 = token: if "~", bit value will be negated after checking + ; %2 = instruction + ; %3 = variable, in the form "[address]" without size specification + ; %4 = bit value(s) to access + ; %5 = bool: disable word access warning, defaults to 0 + ; If the value in %4 has bits set in different bytes so that + ; a single 8- or 16-bit instruction cannot access all the bits, + ; an error is displayed. This insures that the macro only has + ; to generate one instruction, as a 32-bit access on 16-bit + ; CPUs requires multiple instructions. This workaround code + ; needs to be written specifically then, or the flags have to + ; be re-ordered to allow the access. +%macro _opt 4-5.nolist 0 +%push +%defstr %$adr %3 +%strlen %$len %$adr +%substr %$tf %$adr 1 +%substr %$tb %$adr %$len +%substr %$adr %$adr 2,-2 +%deftok %$adr %$adr +%%num: equ %4 +%assign %$num %%num +%ifnidn %$tf,"[" + %error Invalid memory access syntax +%elifnidn %$tb,"]" + %error Invalid memory access syntax +%elifn %$num + %error Bit value is zero! Check code. +%elifn (%$num) & ~0FFh + %2 byte [%$adr], %1(%$num) +%elifn (%$num) & ~0FF00h + %2 byte [%$adr+1], %1((%$num)>>8) +%elifn (%$num) & ~0FF0000h + %2 byte [%$adr+2], %1((%$num)>>16) +%elifn (%$num) & ~0FF000000h + %2 byte [%$adr+3], %1((%$num)>>24) +%elifn (%$num) & ~0FFFFh + %ifn %5 + %warning Macro generated word access + %endif + %2 word [%$adr], %1(%$num) +%elifn (%$num) & ~0FFFF00h + %ifn %5 + %warning Macro generated word access + %endif + %2 word [%$adr+1], %1((%$num)>>8) +%elifn (%$num) & ~0FFFF0000h + %ifn %5 + %warning Macro generated word access + %endif + %2 word [%$adr+2], %1((%$num)>>16) +%else + %error Unsupported macro usage, requires dword: + %ifempty %1 + %error %2 dword [%$adr], %$num + %else + %error %2 dword [%$adr], %1(%$num) + %endif +%endif +%pop +%endmacro + + ; User forms for above macro. + ; %1 = variable, in the form "[address]" without size specification + ; %2 = bit value(s) to access + ; %3 = bool: disable word access warning, defaults to 0 + ; testopt tests the specified bits (using a "test" instruction) and + ; leaves a meaningful result in ZF, as well as NC. + ; clropt clears the specified bits (using an "and" instruction with + ; the negated value) and leaves NC, but a random ZF. + ; setopt sets the specified bits (using an "or" instruction) and + ; leaves NC, NZ. + ; xoropt toggles the specified bits (using a "xor" instruction) and + ; leaves NC, but a random ZF. +%idefine testopt _opt ,test, +%idefine clropt _opt ~,and, +%idefine setopt _opt ,or, +%idefine xoropt _opt ,xor, + + + %imacro addsection 1-2.nolist +%ifdef _SECTION_ADDED_%1 + %error Section %1 already added +%endif + section %1 %2 +%define _SECTION_ADDED_%1 1 + usesection %1 + %endmacro + + %imacro usesection 1-2.nolist 0 +%ifndef _SECTION_ADDED_%1 + %error Section %1 not yet added +%endif +%if %2 + [section %1] +%else + section %1 +%endif + %endmacro + + + %macro subcpu 1.nolist +%push SUBCPU +%xdefine %$CPU_PREV __CPU__ + cpu %1 + %endmacro + + %macro subcpureset 0.nolist +%ifnctx SUBCPU + %error Wrong context +%endif +%xdefine __CPU__ %$CPU_PREV +%pop +__CPU__ + %endmacro + + +%endif +[list +] diff --git a/test/lmacros/tests/000.asm b/test/lmacros/tests/000.asm new file mode 100644 index 0000000..9b74e6a --- /dev/null +++ b/test/lmacros/tests/000.asm @@ -0,0 +1,68 @@ + +%assign _@@_check 2 +%assign _@@_start 0 +%include "lmacros2.mac" + +%ifdef _MAP +[map symbols brief _MAP] +%endif + + cpu 8086 + org 256 + sectalign off + section lCode start=256 align=1 + +start: @S + jmp @F + nop + nop +@@: + +;%fatal 1>@F1< 2>@F2< 3>@F3< 4>@F4< + jz @F + nop + nop +@@: + + jnz @F + nop + nop +@@: + + call @F + nop +@@: + +exit: + mov ax, 4C00h + int 21h + align 16 + +second: @S 0 + jmp @F + nop + nop +@@: + +;%fatal 1>@F1< 2>@F2< 3>@F3< 4>@F4< + jz @F + nop + nop +@@: + + jnz @F + nop + nop +@@: + + call @F + nop +@@: + jmp exit + align 16 + + jmp @B + jmp @BB + jmp @B3 + jmp @B4 + diff --git a/test/lmacros/tests/001.asm b/test/lmacros/tests/001.asm new file mode 100644 index 0000000..147b302 --- /dev/null +++ b/test/lmacros/tests/001.asm @@ -0,0 +1,32 @@ + +%include "lmacros2.mac" + + numdef STANDALONE, 1 + +%if _STANDALONE +%ifdef _MAP +[map symbols brief _MAP] +%endif + + cpu 8086 + org 256 + sectalign off + section lCode start=256 align=1 + +start: + call fun1 + + mov ax, 4C00h + int 21h +%endif + +fun1: + lframe near + lvar word, foo + lvar word, bar + lenter + lvar word, quux + push ax + + lleave + lret diff --git a/test/lmacros/tests/002.asm b/test/lmacros/tests/002.asm new file mode 100644 index 0000000..68a3039 --- /dev/null +++ b/test/lmacros/tests/002.asm @@ -0,0 +1,37 @@ + +%include "lmacros2.mac" + + numdef STANDALONE, 1 + +%if _STANDALONE +%ifdef _MAP +[map symbols brief _MAP] +%endif + + cpu 8086 + org 256 + sectalign off + section lCode start=256 align=1 + +start: + push ax + call fun2 + + mov ax, 4C00h + int 21h +%endif + +fun2: + lframe near + lpar word, alpha + lvar word, foo + lvar word, bar + lenter + lvar word, quux + push ax + + mov word [bp + ?foo], 0F00h + mov dx, word [bp + ?alpha] + + lleave + lret diff --git a/test/lmacros/tests/003.asm b/test/lmacros/tests/003.asm new file mode 100644 index 0000000..2ecc7ad --- /dev/null +++ b/test/lmacros/tests/003.asm @@ -0,0 +1,40 @@ + +%include "lmacros2.mac" + + numdef STANDALONE, 1 + +%if _STANDALONE +%ifdef _MAP +[map symbols brief _MAP] +%endif + + cpu 8086 + org 256 + sectalign off + section lCode start=256 align=1 + +start: + push bx + push cx + call fun3 + pop bx + + mov ax, 4C00h + int 21h +%endif + +fun3: + lframe near + lpar word, beta + lpar_return + lpar word, alpha + lvar word, foo + lvar word, bar + lenter + lvar word, quux + push ax + + mov word [bp + ?beta], 0BE7Ah + + lleave + lret diff --git a/test/lmacros/tests/004.asm b/test/lmacros/tests/004.asm new file mode 100644 index 0000000..b7fbf8d --- /dev/null +++ b/test/lmacros/tests/004.asm @@ -0,0 +1,34 @@ + +%include "lmacros2.mac" + + numdef STANDALONE, 1 + +%if _STANDALONE +%ifdef _MAP +[map symbols brief _MAP] +%endif + + cpu 8086 + org 256 + sectalign off + section lCode start=256 align=1 + +start: + call fun4 + + mov ax, 4C00h + int 21h +%endif + +fun4: + lframe near + lenter early + lvar word, foo + push bx + lvar word, bar + lenter + lvar word, quux + push ax + + lleave + lret diff --git a/test/lmacros/tests/005.asm b/test/lmacros/tests/005.asm new file mode 100644 index 0000000..1888f0a --- /dev/null +++ b/test/lmacros/tests/005.asm @@ -0,0 +1,34 @@ + +%include "lmacros2.mac" + + numdef STANDALONE, 1 + +%if _STANDALONE +%ifdef _MAP +[map symbols brief _MAP] +%endif + + cpu 8086 + org 256 + sectalign off + section lCode start=256 align=1 + +start: + call fun5 + + mov ax, 4C00h + int 21h +%endif + +fun5: + lframe near + lenter + lvar word, foo + push bx + lvar word, bar + lreserve + lvar word, quux + push ax + + lleave + lret diff --git a/test/lmacros/tests/006.asm b/test/lmacros/tests/006.asm new file mode 100644 index 0000000..c3fc562 --- /dev/null +++ b/test/lmacros/tests/006.asm @@ -0,0 +1,43 @@ + +%include "lmacros2.mac" + + numdef STANDALONE, 1 + +%if _STANDALONE +%ifdef _MAP +[map symbols brief _MAP] +%endif + + cpu 8086 + org 256 + sectalign off + section lCode start=256 align=1 + +start: + call fun6 + + mov ax, 4C00h + int 21h +%endif + +fun6: + lframe near + lvar word, foo + lvar word, bar + lenter + + mov word [bp + ?foo], ax + + lframe 0, nested + lvar word, qux + lvar word, foo + lenter + + mov word [bp + ?foo], bx + + lleave + + mov ax, word [bp + ?foo] + + lleave + lret diff --git a/test/lmacros/tests/007.asm b/test/lmacros/tests/007.asm new file mode 100644 index 0000000..63b2327 --- /dev/null +++ b/test/lmacros/tests/007.asm @@ -0,0 +1,44 @@ + +%include "lmacros2.mac" + + numdef STANDALONE, 1 + +%if _STANDALONE +%ifdef _MAP +[map symbols brief _MAP] +%endif + + cpu 8086 + org 256 + sectalign off + section lCode start=256 align=1 + +start: + call fun7 + + mov ax, 4C00h + int 21h +%endif + +fun7: + lframe near + lvar word, foo + lvar word, bar + lenter + + mov word [bp + ?foo], ax + + lframe 0, inner + lvar word, qux + lvar word, foo + lenter + + mov word [bp + ?foo], bx + mov ax, word [bp + ?bar] + + lleave + + mov ax, word [bp + ?foo] + + lleave + lret diff --git a/test/lmacros/tests/008.asm b/test/lmacros/tests/008.asm new file mode 100644 index 0000000..a0c181f --- /dev/null +++ b/test/lmacros/tests/008.asm @@ -0,0 +1,51 @@ + +%include "lmacros2.mac" + + numdef STANDALONE, 1 + +%if _STANDALONE +%ifdef _MAP +[map symbols brief _MAP] +%endif + + cpu 8086 + org 256 + sectalign off + section lCode start=256 align=1 + +start: + call fun8 + + mov ax, 4C00h + int 21h +%endif + +fun8: + lframe near + lvar word, foo + lvar word, bar + lenter + + mov word [bp + ?foo], ax + + push cs + push si + + lframe 0, inner + lpar word, segment + lpar word, offset + lvar word, qux + lvar word, foo + lenter + + mov word [bp + ?foo], bx + mov ax, word [bp + ?bar] + mov bx, word [bp + ?segment] + mov dx, word [bp + ?offset] + + lleave + + mov ax, word [bp + ?foo] + + lleave + lret diff --git a/test/lmacros/tests/009.asm b/test/lmacros/tests/009.asm new file mode 100644 index 0000000..1c6fc85 --- /dev/null +++ b/test/lmacros/tests/009.asm @@ -0,0 +1,52 @@ + +%include "lmacros2.mac" + + numdef STANDALONE, 1 + +%if _STANDALONE +%ifdef _MAP +[map symbols brief _MAP] +%endif + + cpu 8086 + org 256 + sectalign off + section lCode start=256 align=1 + +start: + call fun9 + + mov ax, 4C00h + int 21h +%endif + +fun9: + lframe near + lvar word, foo + lvar word, bar + lenter + + mov word [bp + ?foo], ax + + push cs + push si + + lframe 0, inner + lpar word, segment + lpar word, offset + lvar word, qux + lvar word, foo + lvar word, xyzzy + lenter + + mov word [bp + ?foo], bx + mov ax, word [bp + ?bar] + mov bx, word [bp + ?segment] + mov dx, word [bp + ?offset] + + lleave + + mov ax, word [bp + ?foo] + + lleave + lret diff --git a/test/lmacros/tests/010.asm b/test/lmacros/tests/010.asm new file mode 100644 index 0000000..be873e1 --- /dev/null +++ b/test/lmacros/tests/010.asm @@ -0,0 +1,49 @@ + +%include "lmacros2.mac" + + numdef STANDALONE, 1 + +%if _STANDALONE +%ifdef _MAP +[map symbols brief _MAP] +%endif + + cpu 8086 + org 256 + sectalign off + section lCode start=256 align=1 + +start: + call fun10 + + mov ax, 4C00h + int 21h +%endif + +fun10: + lframe near + lvar word, foo + lvar word, bar + lenter + + mov word [bp + ?foo], ax + + push cs + push si + + lframe 0, inner + lpar word, segment + lpar word, offset + lenter + + mov word [bp + ?foo], bx + mov ax, word [bp + ?bar] + mov bx, word [bp + ?segment] + mov dx, word [bp + ?offset] + + lleave + + mov ax, word [bp + ?foo] + + lleave + lret diff --git a/test/lmacros/tests/011.asm b/test/lmacros/tests/011.asm new file mode 100644 index 0000000..90fa57e --- /dev/null +++ b/test/lmacros/tests/011.asm @@ -0,0 +1,50 @@ + +%include "lmacros2.mac" + + numdef STANDALONE, 1 + +%if _STANDALONE +%ifdef _MAP +[map symbols brief _MAP] +%endif + + cpu 8086 + org 256 + sectalign off + section lCode start=256 align=1 + +start: + call fun11 + + mov ax, 4C00h + int 21h +%endif + +fun11: + lframe near + lvar word, foo + lvar word, bar + lenter + + mov word [bp + ?foo], ax + + push cs + push si + + lframe 0, inner + lpar word, segment + lpar_return + lpar word, offset + lenter + + mov word [bp + ?foo], bx + mov ax, word [bp + ?bar] + mov bx, word [bp + ?segment] + mov dx, word [bp + ?offset] + + lleave + + mov ax, word [bp + ?foo] + + lleave + lret diff --git a/test/lmacros/tests/012.asm b/test/lmacros/tests/012.asm new file mode 100644 index 0000000..165712d --- /dev/null +++ b/test/lmacros/tests/012.asm @@ -0,0 +1,48 @@ + +%include "lmacros2.mac" + + numdef STANDALONE, 1 + +%if _STANDALONE +%ifdef _MAP +[map symbols brief _MAP] +%endif + + cpu 8086 + org 256 + sectalign off + section lCode start=256 align=1 + +start: + call fun12 + + mov ax, 4C00h + int 21h +%endif + +fun12: + lframe near + lvar word, foo + lvar word, bar + lenter + lvar word, baz + push bx + + mov word [bp + ?foo], ax + + lframe 0, inner + lvar word, quux + lvar word, foo + lenter + + mov word [bp + ?foo], bx + mov ax, word [bp + ?bar] + mov bx, word [bp + ?quux] + mov dx, word [bp + ?baz] + + lleave + + mov ax, word [bp + ?foo] + + lleave + lret diff --git a/test/lmacros/tests/013.asm b/test/lmacros/tests/013.asm new file mode 100644 index 0000000..fa7a584 --- /dev/null +++ b/test/lmacros/tests/013.asm @@ -0,0 +1,49 @@ + +%include "lmacros2.mac" + + numdef STANDALONE, 1 + +%if _STANDALONE +%ifdef _MAP +[map symbols brief _MAP] +%endif + + cpu 8086 + org 256 + sectalign off + section lCode start=256 align=1 + +start: + call fun13 + + mov ax, 4C00h + int 21h +%endif + +fun13: + lframe near + lvar word, foo + lvar word, bar + lenter + lvar word, baz + push bx + + mov word [bp + ?foo], ax + + lframe 0, inner + lenter + lvar word, quux + lvar word, foo + lreserve + + mov word [bp + ?foo], bx + mov ax, word [bp + ?bar] + mov bx, word [bp + ?quux] + mov dx, word [bp + ?baz] + + lleave + + mov ax, word [bp + ?foo] + + lleave + lret diff --git a/test/lmacros/tests/014.asm b/test/lmacros/tests/014.asm new file mode 100644 index 0000000..f7d32e6 --- /dev/null +++ b/test/lmacros/tests/014.asm @@ -0,0 +1,56 @@ + +%include "lmacros2.mac" + + numdef STANDALONE, 1 + +%if _STANDALONE +%ifdef _MAP +[map symbols brief _MAP] +%endif + + cpu 8086 + org 256 + sectalign off + section lCode start=256 align=1 + +start: + call fun14 + + mov ax, 4C00h + int 21h +%endif + +fun14: + lframe near + lvar word, foo + lvar word, bar + lenter + lvar word, baz + push bx + + mov word [bp + ?foo], ax + + push cs + push si + lframe 0, inner + lpar word, alpha + lpar word, beta + lpar_return + lenter + lvar word, quux + lvar word, foo + lreserve + + mov word [bp + ?foo], bx + mov ax, word [bp + ?bar] + mov bx, word [bp + ?quux] + mov dx, word [bp + ?baz] + mov si, word [bp + ?beta] + inc word [bp + ?alpha] + + lleave + + mov ax, word [bp + ?foo] + + lleave + lret diff --git a/test/lmacros/tests/015.asm b/test/lmacros/tests/015.asm new file mode 100644 index 0000000..2b2ba14 --- /dev/null +++ b/test/lmacros/tests/015.asm @@ -0,0 +1,70 @@ + +%include "lmacros2.mac" + + numdef STANDALONE, 1 + +%if _STANDALONE +%ifdef _MAP +[map symbols brief _MAP] +%endif + + cpu 8086 + org 256 + sectalign off + section lCode start=256 align=1 + +start: + push ax + call fun15 + + mov ax, 4C00h + int 21h +%endif + +fun15: +.1: + lframe near + lpar word, alpha + lvar word, foo + lvar word, bar + lenter + lvar word, baz + push bx + jmp .common + +.2: +%ifdef FAIL3 +bits 32 +%endif +%ifdef FAIL4 + lframe far, nested +%else + lframe near, nested +%endif + lpar word, alpha +%ifdef FAIL2 + lpar_return +%endif + lvar word, foo +%ifdef FAIL5 + lvar word, foobar +%else + lvar word, bar +%endif +%ifdef FAIL1 + lvar word, quux +%endif + lenter + lvar word, baz + push bx + + ldup + + lleave ctx + +.common: + mov dx, word [bp + ?alpha] + mov word [bp + ?foo], ax + + lleave + lret diff --git a/test/lmacros/tests/016.asm b/test/lmacros/tests/016.asm new file mode 100644 index 0000000..bdcb6ff --- /dev/null +++ b/test/lmacros/tests/016.asm @@ -0,0 +1,64 @@ + +%include "lmacros2.mac" + + numdef STANDALONE, 1 + +%if _STANDALONE +%ifdef _MAP +[map symbols brief _MAP] +%endif + + cpu 8086 + org 256 + sectalign off + section lCode start=256 align=1 + +start: + xor ax, ax + push ax + call fun16 + mov ax, 1 + push bx + call fun16 + + mov ax, 4C00h + int 21h +%endif + +fun16: + mov bx, 0B3B3h + mov dx, 0D3D3h + lframe near + lpar word, alpha + lvar word, foo + lvar word, bar + lenter + test ax, ax + jnz .handler2 + +.handler1: + lvar word, quux + push cx + mov dx, word [bp + ?alpha] + mov word [bp + ?foo], ax + add ax, word [bp + ?quux] + + lleave + lret + +.handler2: + lframe near + lemit off + lpar word, alpha + lvar word, foo + lvar word, bar + lenter + lemit + lvar word, baz + push dx + + mov bx, word [bp + ?alpha] + mov word [bp + ?foo], ax + + lleave + lret diff --git a/test/lmacros/tests/017.asm b/test/lmacros/tests/017.asm new file mode 100644 index 0000000..e83081e --- /dev/null +++ b/test/lmacros/tests/017.asm @@ -0,0 +1,60 @@ + +%include "lmacros2.mac" + + numdef STANDALONE, 1 + +%if _STANDALONE +%ifdef _MAP +[map symbols brief _MAP] +%endif + + cpu 8086 + org 256 + sectalign off + section lCode start=256 align=1 + +start: + mov cx, 1234h + xor ax, ax + push ax + call fun17 + + mov ax, 4C00h + int 21h +%endif + +fun17: + lframe near + lpar word, alpha + lvar word, foo + lvar word, bar + lenter ; push bp \ mov bp, sp \ push ax \ push ax + + lvar word, quux + push cx ; push cx + lvar word, baz + lvar dword, xyzzy + lreserve ; lea sp, [bp - 0Ch] + mov dx, word [bp + ?alpha] + ; [bp + 4] + mov word [bp + ?foo], ax + ; [bp - 2] + add ax, word [bp + ?quux] + ; [bp - 6] + mov word [bp + ?xyzzy], dx + ; [bp - 0Ch] + + lframe 0, inner + lvar word, e + lvar word, f + lvar word, g + lenter ; lea sp, [bp - 12h] + lvar word, h + push ax ; push ax + lvar dword, i + lreserve ; lea sp, [bp - 18h] + + lleave ; lea sp, [bp - 0Ch] + + lleave ; mov sp, bp \ pop bp + lret ; retn 2 diff --git a/test/lmacros/tests/018.asm b/test/lmacros/tests/018.asm new file mode 100644 index 0000000..df281c7 --- /dev/null +++ b/test/lmacros/tests/018.asm @@ -0,0 +1,36 @@ + +%include "lmacros2.mac" + + numdef STANDALONE, 1 + +%if _STANDALONE +%ifdef _MAP +[map symbols brief _MAP] +%endif + + cpu 8086 + org 256 + sectalign off + section lCode start=256 align=1 + +start: + call fun18 + + mov ax, 4C00h + int 21h +%endif + +fun18: + lframe near + lequ 16 * 3, 16tablesize + lvar ?16tablesize, firsttable + lvar fromparas(paras(26)), secondtable + lenter ; push bp \ mov bp, sp \ lea sp, [bp - 50h] + + lea si, [bp + ?firsttable] + ; lea si, [bp - 30h] + lea di, [bp + ?secondtable] + ; lea di, [bp - 50h] + + lleave ; mov sp, bp \ pop bp + lret ; retn diff --git a/test/lmacros/tests/019.asm b/test/lmacros/tests/019.asm new file mode 100644 index 0000000..536ac0e --- /dev/null +++ b/test/lmacros/tests/019.asm @@ -0,0 +1,26 @@ +%define _@@_check +%assign _@@_start 0 +%include "lmacros2.mac" + +@S + nop +@@: + nop + jmp @F + nop + nop + jmp @B + +@@: + nop + nop +@I + + nop + nop +@S +@@: + nop + nop + jmp @B +@I diff --git a/test/lmacros/tests/test.sh b/test/lmacros/tests/test.sh new file mode 100755 index 0000000..55418ca --- /dev/null +++ b/test/lmacros/tests/test.sh @@ -0,0 +1,8 @@ +#! /bin/bash + +echo -ne "" > "$1.lst" +[ -z "$NASM" ] && NASM=nasm +"$NASM" "$1.asm" -D_MAP="$1.map" -l "$1.lst" -f bin -o "$@" -I ../ +cat "$1.lst" +ndisasm "$1" + diff --git a/test/lmacros/tests/test2.asm b/test/lmacros/tests/test2.asm new file mode 100644 index 0000000..06be0e9 --- /dev/null +++ b/test/lmacros/tests/test2.asm @@ -0,0 +1,51 @@ + +%include "lmacros2.mac" + +%ifdef _MAP +[map symbols brief _MAP] +%endif + + cpu 8086 + org 256 + sectalign off + section lCode start=256 align=1 + +start: + call fun1 + push ax + call fun2 + push bx + push cx + call fun3 + pop bx + call fun4 + call fun5 + call fun6 + call fun7 + call fun8 + call fun9 + call fun10 + call fun11 + call fun12 + call fun13 + call fun14 + + mov ax, 4C00h + int 21h + + overridedef STANDALONE, 0 +%include "001.asm" +%include "002.asm" +%include "003.asm" +%include "004.asm" +%include "005.asm" +%include "006.asm" +%include "007.asm" +%include "008.asm" +%include "009.asm" +%include "010.asm" +%include "011.asm" +%include "012.asm" +%include "013.asm" +%include "014.asm" + resetdef diff --git a/test/lmacros/tests/testmap.sh b/test/lmacros/tests/testmap.sh new file mode 100755 index 0000000..487abe5 --- /dev/null +++ b/test/lmacros/tests/testmap.sh @@ -0,0 +1,7 @@ +#! /bin/bash + +echo -ne "" > "$1.lst" +[ -z "$NASM" ] && NASM=nasm +"$NASM" "$1.asm" -D_MAP="$1.map" -l "$1.lst" -f bin -o "$@" -I ../ +cat "$1.lst" +cat "$1.map" diff --git a/test/lmacros/tests/testpar.asm b/test/lmacros/tests/testpar.asm new file mode 100644 index 0000000..08c3ae7 --- /dev/null +++ b/test/lmacros/tests/testpar.asm @@ -0,0 +1,58 @@ + +%include "lmacros2.mac" + + cpu 8086 + org 100h +start: + mov ax, 1 + mov bx, 2 + push ax + push bx + call fun + + mov cx, 3 + mov dx, 4 + push cx + push dx + call fun2 + pop ax + + mov word [fifth], ax + + mov ax, 4C00h + int 21h + +fun: + lframe near + lpar word, alpha + lpar word, beta + lenter + mov ax, [bp + ?alpha] + mov word [first], ax + mov ax, [bp + ?beta] + mov word [second], ax + lleave + lret + + +fun2: + lframe near + lpar word, gamma + lpar_return + lpar word, delta + lenter + mov ax, [bp + ?gamma] + mov word [third], ax + mov ax, [bp + ?delta] + mov word [fourth], ax + mov word [bp + ?gamma], 5 + lleave + lret + + align 256 +first: dw 0 +second: dw 0 +third: dw 0 +fourth: dw 0 +fifth: dw 0 + diff --git a/test/lmacros/tests/testsat.asm b/test/lmacros/tests/testsat.asm new file mode 100644 index 0000000..d11bf73 --- /dev/null +++ b/test/lmacros/tests/testsat.asm @@ -0,0 +1,18 @@ +%include "lmacros2.mac" + + [map all testsat.map] + cpu 8086 + org 0 + +struc TESTSTRUC + resb 1 +struc_at 1, at_1: resb 3 +struc_at 4, at_4: + resw 1 +exact_struc_at 6 +at_6: resb 1 +struc_at 32 +at_32: resb 10 +warn_struc_at 64 +at_64: resb 1 +endstruc