User Tools

Site Tools


blog:pushbx:2026:0216_recent_warplink_tests

Recent WarpLink tests

These tests were written to test several of the wlcalc and wllist features during their development. They're all stored in https://pushbx.org/ecm/test/20260204/

The scriptlets

The basic assemble and link scriptlet looks like this (as of the most recent WarpLink revision - some of the switches require a newer revision than others). It assembles the test object file, then links it using the local warplink.exe (a link to the development repo's executable). Then it runs the linker with the /MX, /WS0, /XL, /XS, /XC, /TO=, /TL=, /I, and /XLD switches:

(nasm test$NUM.asm -f obj && ~/proj/ldos/warplink.sh /mx /ws0 /xl /xs /xc /to=wllist.obj /tl=wllist.bin /i /xld test$NUM.obj,test$NUM.exe,test$NUM.map\;)

To dump the executable image of the output file, the following scriptlet can be used:

tail --bytes +513 test$NUM.exe | podhex

To run omfdump on the temporary wllist object file:

~/proj/omfdump/omfdump wllist.obj

To hex dump the temporary wllist list file:

podhex wllist.bin

The dox.bat script is to be run from a DOS that maps the test directory to a DOS directory. It runs the debugger with its /T, /P, and /C switches to load WarpLink and enable serial I/O. The WarpLink command it runs specifies the /TL=, /TO=, /XL, /XC, and /XS switches, along with the older /MX, /WSN, and /WS0. The first parameter, %1, specifies the test number to link.

ldebug /t /p /c=install,serial warplink /tl=wllist.bin /to=wllist.obj /xl /xc /xs /mx /wsn /ws0 test%1.obj,test%1.exe,test%1.map;

The tests

test.asm

This creates a code segment with a zeroed word.

section code
	dw 0

test1.asm

This uses two wlcalc post-link calculations, to imitate the usual "find number of paragraphs neeeded" for a program where this is less than 64 KiB.

section code
wlcalc_word_add_15:
wlcalc_word_div_16:
global wlcalc_word_add_15
global wlcalc_word_div_16
	dw label
	times 26 nop
label:

test2.asm

Like the prior test, but without a label and the order of the global directives is switched. NASM still emits the "add" operation in the PUBDEF record before the "div".

section code
wlcalc_word_add_15:
wlcalc_word_div_16:
global wlcalc_word_div_16
global wlcalc_word_add_15
	dw 0

test3.asm

Like the prior test but the order of the operations is changed again.

section code
wlcalc_word_div_16:
wlcalc_word_add_15:
global wlcalc_word_div_16
global wlcalc_word_add_15
	dw 0

test4.asm

Again a different order of the global directives. NASM still follows the order of the labels, ignoring the differing order of the global directives.

section code
wlcalc_word_div_16:
wlcalc_word_add_15:
global wlcalc_word_add_15
global wlcalc_word_div_16
	dw 0

test5.asm

Place a label as an equate with a dollar sign calculation. NASM emits them in the order the labels are defined in the source (add first, then div) despite the label defined later pointing at an address that's earlier.

section code
	times 4 dw 1234h
wlcalc_word_add_15:
wlcalc_word_div_16: equ $ - 2
global wlcalc_word_add_15
global wlcalc_word_div_16
	dw 100h

test6.asm

A jmp far with a destination label in the same segment.

section DOSSECTION
label:
	times 4 dw 0
wlcalc_word_add_15:
wlcalc_word_div_16: equ $ - 2
global wlcalc_word_add_15
global wlcalc_word_div_16
	dw 0
	jmp far label

test7.asm

An extern label written to a word in the section.

section DOSSECTION
	extern label
	dw label

test8.asm

Attempt to cause a common variable overflow.

section DOSSECTION
	common variable 4999999999:1024

test9.asm

A wlcalc label as an equate, pointing to the same word as a non-equate wlcalc label.

section code
	times 4 dw 1234h
wlcalc_word_add_15:
	dw 2642h
wlcalc_word_div_16: equ $ - 2
global wlcalc_word_add_15
global wlcalc_word_div_16
	dw 100h

test10.asm

Multiple wlcalc requests with differing sizes and trailing identifiers.

section code
wlcalc_word_shr_4?bar:
global wlcalc_word_shr_4?bar:
	dw 123h
	nop

wlcalc_byte_shr_4?foo:
global wlcalc_byte_shr_4?foo:
	dw 123h
	nop

wlcalc_3byte_shr_4?foo:
global wlcalc_3byte_shr_4?foo:
	dd 0FF123456h

wlcalc_dword_shr_4?foo:
global wlcalc_dword_shr_4?foo:
	dd 12345678h
	int3

test11.asm

A wlcalc request with the divisor as zero.

section code
 global wlcalc_word_div_0
wlcalc_word_div_0:
	dw 1

test12.asm

A wlcalc request with a "subr" operation, to negate a label.

section code

wlcalc_word_subr_0:
global wlcalc_word_subr_0:
	dd label

	times 16 nop

label:

test13.asm

A wlcalc request that will encounter a short read at the executable's EOF.

section code

wlcalc_word_subr_0:
global wlcalc_word_subr_0:

test14.asm

A wlemitalign test case.

	section code
	times 4 db 0

	section WLEMITALIGNdata align=16

test15.asm

Multiple new wlcalc requests: OR, MUL, XOR, and AND.

section code
wlcalc_word_or_4?bar:
global wlcalc_word_or_4?bar
	dw 1000h
	nop

wlcalc_word_mul_256?foo:
global wlcalc_word_mul_256?foo
	dw 26h
	nop

wlcalc_word_xor_4096?bar:
global wlcalc_word_xor_4096?bar
	dw 1000h
	nop

wlcalc_word_and_4096?bar:
global wlcalc_word_and_4096?bar
	dw 1010h
	nop

test16.asm

A dword MUL wlcalc request that overflows the variable.

section code

wlcalc_dword_mul_256?foo:
global wlcalc_dword_mul_256?foo
	dd 11223344h
	nop

test17.asm

A CLR wlcalc request.

section code

wlcalc_dword_clr_256?foo:
global wlcalc_dword_clr_256?foo
	dd -1
	nop

test18.asm

A wlcalc request with a literal >= 64 KiB, and as a hexadecimal number.

section code

wlcalc_dword_clr_0x10000?foo:
global wlcalc_dword_clr_0x10000?foo
	dd -1
	nop

test19.asm

Multiple wlcalc requests: Hexadecimal, decimal, binary, all with underscore separators within the numbers.

section code

wlcalc_dword_clr_0xFF_FF?foo:
global wlcalc_dword_clr_0xFF_FF?foo
	dd -1
	nop

wlcalc_dword_clr_65_535?foo:
global wlcalc_dword_clr_65_535?foo
	dd -1
	nop

wlcalc_dword_clr_1111_0000B?foo:
global wlcalc_dword_clr_1111_0000B?foo
	dd -1
	nop

test20.asm

A wlcalc request with a constant that overflows the 4 Gi - 1 numeric range.

section code

wlcalc_dword_clr_1_0000_0000h?foo:
global wlcalc_dword_clr_1_0000_0000h?foo
	dd -1
	nop

test21.asm

The wlcalc helper mmacro for NASM. Several tests of 32-bit constants.

%imacro wlcalc 1-2.nolist
 global wlcalc_%1
wlcalc_%1: %2
%endmacro

	section code
wlcalc dword_mul_01020304h
	dd 8

wlcalc 3byte_xor_11223344h
	dd 0F0F0F0Fh

wlcalc dword_div_1230000h
	dd 1230_0000h

test22.asm

Repetition of a wlcalc request, and using the wlcalc mmacro with an equate.

%imacro wlcalc 1-2.nolist
 global wlcalc_%1
wlcalc_%1: %2
%endmacro

	section code
start:
wlcalc dword_mul_01020304h
	dd 8

wlcalc 3byte_xor_11223344h
	dd 0F0F0F0Fh

wlcalc dword_div_1230000h
	dd 1230_0000h

%assign AMOUNT ($-$$) / 4
wlcalc dword_xor_FF00FF00h_repeat_%[AMOUNT], equ start

test23.asm

Like prior example but with additional wlcalc requests in pass 1 and pass 2, to test that the passes are honoured.

%imacro wlcalc 1-2.nolist
 global wlcalc_%1
wlcalc_%1: %2
%endmacro

	section code
start:
wlcalc dword_mul_01020304h
	dd 8

wlcalc 3byte_xor_11223344h
wlcalc dword_and_0_pass_1
wlcalc word_or_2642h_repeat_2_pass_2
	dd 0F0F0F0Fh

wlcalc dword_div_1230000h
	dd 1230_0000h

%assign AMOUNT ($-$$) / 4
wlcalc dword_xor_FF00FF00h_repeat_%[AMOUNT], equ start

test24.asm

The wlcalc "seg" operation.

%imacro wlcalc 1-2.nolist
 global wlcalc_%1
wlcalc_%1: %2
%endmacro

	section code
start:
wlcalc word_seg_data?foo
	dw 26h
wlcalc word_seg_data?bar
	dw 0
wlcalc word_seg_stack?foo
	dw 0
	dw 0
	times 16 nop

	section data
	align 16
	times 16 nop

	section stack stack
	alignb 16
	resb 512

test25.asm

A wlcalc "seg" operation with an unknown segment name.

%imacro wlcalc 1-2.nolist
 global wlcalc_%1
wlcalc_%1: %2
%endmacro

	section code
start:
wlcalc word_seg_data?foo
	dw 26h
wlcalc word_seg_unknown?foo
	dw 0
	times 16 nop

	section data
	align 16

test26.asm

Another "seg" operation. Unclear why this.

%imacro wlcalc 1-2.nolist
 global wlcalc_%1
wlcalc_%1: %2
%endmacro

	section code
start:
wlcalc word_seg_data?foo
	dw 26h
	dw 0
	times 16 nop

	section data
	align 16

test27.asm

A wlcalc "seg" operation with a dword variable. This is rejected by the linker.

%imacro wlcalc 1-2.nolist
 global wlcalc_%1
wlcalc_%1: %2
%endmacro

	section code
start:
wlcalc word_seg_data?foo
	dw 26h
wlcalc word_seg_data
	dw 26h
wlcalc dword_seg_data?foo
	dw 0
	times 16 nop

	section data
	align 16

test28.asm

A wlcalc "seg" request at offset 0FFFFh, which is rejected as an overflow.

%imacro wlcalc 1-2.nolist
 global wlcalc_%1
wlcalc_%1: %2
%endmacro

	section code
start:
wlcalc word_seg_data?foo
	dw 26h
wlcalc word_seg_data?bar
	dw 0
wlcalc word_seg_stack?foo
	dw 0
	dw 0
	times 16 nop

	times (0xffff) - ($-$$) db 0
wlcalc word_seg_data?quux

	section data
	align 16
	times 16 nop

	section stack stack
	alignb 16
	resb 512

test29.asm

A wlcalc sequence involving first shifting right, then in another pass applying a segment relocation ("seg").

%imacro wlcalc 1-2.nolist
 global wlcalc_%1
wlcalc_%1: %2
%endmacro

	section code
start:

wlcalc word_shr_4
wlcalc word_pass_1_seg_stack
	dw stack_top + 15
wlcalc word_seg_data?foo
	dw 26h
wlcalc word_seg_data?bar
	dw 0
wlcalc word_seg_stack?foo
	dw 0
	dw 0
	times 16 nop

	section data
	align 16
	times 16 nop

	section stack stack
	alignb 16
	resb 512
stack_top:

test30.asm

Testing the "segrel" and "minussegrel" wlcalc operations.

%imacro wlcalc 1-2.nolist
 global wlcalc_%1
wlcalc_%1: %2
%endmacro

	section code
start:

wlcalc word_shr_4
wlcalc word_pass_1_segrel_stack
	dw stack_top + 15
wlcalc word_segrel_data?foo
	dw 26h
wlcalc word_segrel_data?bar
	dw 0
wlcalc word_segrel_stack?foo
	dw 0
wlcalc word_minussegrel_data?bar
	dw 100h
wlcalc word_minussegrel_stack?foo
	dw 100h
	dw 0
	times 16 nop

	section data
	align 16
	times 16 nop

	section stack stack
	alignb 16
	resb 512
stack_top:

test31.asm

Reference to a label in another section, and making the label global.

	section code
	dw 1234h + label

	section data
	times 260h nop
 global label
label:

test32.asm

The first wllist mmacro, and naive draft of two wllist list items. These are invalid to the eventual wllist implementation because the lists aren't declared and the items are missing the "L" key letter to indicate the list name.

%imacro wllist 1.nolist
 global ..@wllist_%1?%[%%l]
..@wllist_%1?%[%%l]:
%endmacro

	section code
	nop
	nop
 global wllist_init_code_386
wllist_init_code_386:
	dd "Quux"
wllist init_code_386
	db 26h

	section init align=1

test33.asm

Multiple wllist list declarations, in the "init" and "exit" segments. A wllist declaration of a group, "initgroup", that contains the "init" segment.

%imacro wllist 1.nolist
 global ..@wllist_%1?%[%%l]
..@wllist_%1?%[%%l]:
%endmacro

	section code
wllist n_sinit_linit_code_386
wllist n_sexit_lexit_code_386
wllist n_sexit_lexit_code_no386
wllist n_sinit_linit_code_no386
wllist g_ginitgroup_sinit
	nop
	nop
 global wllist_linit_code_386
wllist_linit_code_386:
	dd "Quux"
wllist linit_code_386
	db 26h

group initgroup init

	section init align=1

test34.asm

Similar to prior, but put the group declaration before the new list declarations. (This still works because of how the declarations are handled.)

%imacro wllist 1.nolist
 global ..@wllist_%1?%[%%l]
..@wllist_%1?%[%%l]:
%endmacro

	section code
wllist g_ginitgroup_sinit
wllist n_sinit_linit_code_386
wllist n_sexit_lexit_code_386
wllist n_sexit_lexit_code_no386
wllist n_sinit_linit_code_no386
	nop
	nop
 global wllist_linit_code_386
wllist_linit_code_386:
	dd "Quux"
wllist linit_code_386
	db 26h

group initgroup init

	section init align=1

test35.asm

A wllist group declaration that includes an unknown section name, "omit".

%imacro wllist 1.nolist
 global ..@wllist_%1?%[%%l]
..@wllist_%1?%[%%l]:
%endmacro

	section code
wllist g_ginitgroup_somit
wllist n_sinit_linit_code_386
wllist n_sexit_lexit_code_386
wllist n_sexit_lexit_code_no386
wllist n_sinit_linit_code_no386
	nop
	nop
 global wllist_linit_code_386
wllist_linit_code_386:
	dd "Quux"
wllist linit_code_386
	db 26h

group initgroup init

	section init align=1

test36.asm

Multiple group declarations, one of them containing multiple segments.

%imacro wllist 1.nolist
 global ..@wllist_%1?%[%%l]
..@wllist_%1?%[%%l]:
%endmacro

	section code
wllist g_gexitgroup_sexit
wllist g_ginitgroup_sinit_stest
wllist n_sinit_linit_code_386
wllist n_sexit_lexit_code_386
wllist n_sexit_lexit_code_no386
wllist n_sinit_linit_code_no386
wllist n_stest_ltest
	nop
	nop
 global wllist_linit_code_386
wllist_linit_code_386:
	dd "Quux"
wllist linit_code_386
	db 26h

group initgroup init

	section init align=1

test37.asm

Entering a large number of entries into the "init_code_no386" list.

%imacro wllist 1.nolist
 global ..@wllist_%1?%[%%l]
..@wllist_%1?%[%%l]:
%endmacro

	section code
wllist g_gexitgroup_sexit
wllist g_ginitgroup_sinit_stest
wllist n_sinit_linit_code_386
wllist n_sexit_lexit_code_386
wllist n_sexit_lexit_code_no386
wllist n_sinit_linit_code_no386
wllist n_stest_ltest
	nop
	nop
 global wllist_linit_code_386
wllist_linit_code_386:
	dd "Quux"
wllist linit_code_386
	db 26h

%rep 100
wllist linit_code_no386
	nop
%endrep

group initgroup init

	section init align=1

test38.asm

This test creates a small test program that patches part of its code. It uses the symbols generated as PUBDEF records in the wllist object file, and also checks that the list amount and list end symbols match one another. The wllist mmacro accepts a second parameter to use it as an equate.

%imacro wllist 1-2.nolist
 global ..@wllist_%1?%[%%l]
..@wllist_%1?%[%%l]: %2
%endmacro

	section code
wllist g_ginitgroup_sinit
wllist n_sinit_linit_code_386
wllist n_sinit_linit_code_no386

	mov ax, init
	mov ds, ax

 extern init_code_386
 extern init_code_386.end
 extern init_code_386.amount
 extern init_code_no386
 extern init_code_no386.end
 extern init_code_no386.amount
	mov si, init_code_386
	mov cx, init_code_386.amount
l:
	lodsw
	loop l
	cmp si, init_code_386.end
	jne error

	mov si, init_code_no386
	mov cx, init_code_no386.amount
q:
	lodsw
	xchg di, ax
	mov byte [cs:di], 90h
	loop q
	cmp si, init_code_no386.end
	jne error

	align 64
	nop
	nop
wllist linit_code_386
	mov ax, 1234h
	nop
wllist linit_code_386
	inc ax

wllist linit_code_no386
	o32
	push ax
wllist linit_code_no386
	o32
	push bx
wllist linit_code_no386
	o32
	push cx
wllist linit_code_no386
	o32
	push dx

	mov al, '^'
wllist linit_code_no386, equ $ - 1

	mov ax, 4C00h
	int 21h
error:
	mov ax, 4CFFh
	int 21h

group initgroup init

	section init align=1

test39.asm

Based on prior. Additionally, test the wllist repetition and repetition step width features. Also test that zero repetition and different bases for the repetition count work.

%imacro wllist 1-2.nolist
 global ..@wllist_%1?%[%%l]
..@wllist_%1?%[%%l]: %2
%endmacro

	section code
wllist g_ginitgroup_sinit
wllist n_sinit_linit_code_386
wllist n_sinit_linit_code_no386
wllist n_sinit_lemptylist

	mov ax, init
	mov ds, ax

 extern init_code_386
 extern init_code_386.end
 extern init_code_386.amount
 extern init_code_no386
 extern init_code_no386.end
 extern init_code_no386.amount
	mov si, init_code_386
	mov cx, init_code_386.amount
l:
	lodsw
	loop l
	cmp si, init_code_386.end
	jne error

	mov si, init_code_no386
	mov cx, init_code_no386.amount
q:
	lodsw
	xchg di, ax
	mov byte [cs:di], 90h
	loop q
	cmp si, init_code_no386.end
	jne error

	align 64
	nop
	nop
wllist linit_code_386
	mov ax, 1234h
	nop
wllist linit_code_386
	inc ax

wllist linit_code_no386
	o32
	push ax
wllist linit_code_no386
	o32
	push bx
wllist linit_code_no386
	o32
	push cx
wllist linit_code_no386
	o32
	push dx

wllist r4_w2_linit_code_no386
	pop edx
	pop ecx
	pop ebx
	pop eax

	xor bx, bx
wllist r3_linit_code_no386
	mov bx, 2642h
wllist r0_linit_code_no386
wllist r0x0_linit_code_no386
wllist r0h_linit_code_no386
wllist r0_0_0b_linit_code_no386

	mov al, '^'
wllist linit_code_no386, equ $ - 1

wllist r0_w26h_lemptylist
	mov ax, 4C00h
	int 21h
error:
	mov ax, 4CFFh
	int 21h

group initgroup init

	section init align=1
You could leave a comment if you were logged in.
blog/pushbx/2026/0216_recent_warplink_tests.txt · Last modified: 2026-02-16 14:03:36 +0100 Feb Mon by ecm