User Tools

Site Tools


blog:pushbx:2026:0113_some_more_test_cases_as_of_recently

Some more test cases as of recently

Trace Interrupt + Other Interrupts tests

My first attempt

test.asm: Hook interrupt 8 (IRQ #0) and interrupt 1 (Trace) and try to check when the IRQ is delivered, if the Trace Flag and Interrupt Flag are set by the same iret instruction. This test case was the basis for my first reply to this RC.SE question. It wasn't much of a success because regardless the parameters and modifications, I only ever received an "Iret points to next inst traced" response line. Only the three timer variables differed some.

%if 0

IRQ trace flag test
 2026 by E. C. Masloch

Usage of the works is permitted provided that this
instrument is retained with the works, so that any entity
that uses the works is notified of this instrument.

DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY.

%endif

	cpu 8086
	org 256
start:
	mov si, 81h
.skip:
	lodsb
	cmp al, 32
	je .skip
	cmp al, 9
	je .skip
	cmp al, '0'
	jb error
	cmp al, '9'
	ja error
	xor di, di
	jmp .digit
.loop:
	lodsb
.digit:
	sub al, '0'
	jb .end
	cmp al, 10
	jae .end
	cbw
	add di, di	; times 2
	mov bx, di
	add di, di	; times 4
	add di, di	; times 8
	add di, bx	; times 10
	add di, ax
	jmp .loop

.end:

hook:
	mov ax, 3508h
	int 21h
	mov word [i08.next], bx
	mov word [i08.next + 2], es
	mov dx, i08
	mov ax, 2508h
	int 21h
	mov ax, 3501h
	int 21h
	mov word [i01.next], bx
	mov word [i01.next + 2], es
	mov dx, i01
	mov ax, 2501h
	int 21h

test:
	mov dx, msg.header
	mov ah, 09h
	int 21h
	mov si, 4
.si:
	mov cx, di
	cli
.outer:
	push cx
	mov cx, -1
.inner:
	mov ax, 16661
	mul ax
	add ax, ax
	loop .inner
	pop cx
	loop .outer
	xchg ax, cx
	mov word [timer.1], ax
	mov word [timer.2], ax
	mov word [timer.3], ax
	mov ax, 100h | 200h	; set TF and IF
	push ax
	push cs
	call iret_inst
addr1: equ $
	mov ax, [timer.1]
addr2: equ $
	mov bx, [timer.2]
addr3: equ $
	mov cx, [timer.3]
	sti
	call disp_ax_hex
	mov dx, msg.sep
	mov ah, 09h
	int 21h
	xchg ax, bx
	call disp_ax_hex
	mov dx, msg.sep
	mov ah, 09h
	int 21h
	xchg ax, cx
	call disp_ax_hex
	mov bx, [match]
	mov dx, [bx - 2]
	mov ah, 09h
	int 21h
	dec si
	jnz .si

	lds dx, [cs:i08.next]
	mov ax, 2508h
	int 21h
	lds dx, [cs:i01.next]
	mov ax, 2501h
	int 21h
	mov ax, 4C00h
	int 21h

error:
	mov dx, msg.error
	mov ah, 09h
	int 21h
	mov ax, 4C01h
	int 21h


iret_inst:
	iret


i08:
addr4 equ $
	jmp strict short .enter
.next:
	dd 0
	dw "KB"
	db 0
	jmp strict short iret_inst
	times 7 db 0
.enter:
addr5 equ $
	nop
	inc word [cs:timer.1]
	inc word [cs:timer.2]
	inc word [cs:timer.3]
	jmp far [cs:.next]

i01:
	jmp strict short .enter
.next:
	dd 0
	dw "KB"
	db 0
	jmp strict short iret_inst
	times 7 db 0
.enter:
	push bp
	mov bp, sp
	push ds
	push si
	push ax
	mov ax, cs
	mov ds, ax
	cld
	mov si, table
	mov word [match], si
	cmp word [bp + 4], ax
	jne .none
.loop:
	lodsw
	test ax, ax
	jz .none
	cmp word [bp + 2], ax
	lodsw
	jne .loop
	mov word [match], si
	jmp .done
.none:
	stc
.done:
	pop ax
	pop si
	pop ds
	jc .chain
	and byte [bp + 7], ~1
	pop bp
	iret

.chain:
	pop bp
	jmp far [cs:.next]

disp_ax_hex:
	xchg al, ah
	call disp_al_hex
	xchg al, ah
disp_al_hex:
	push cx
	mov cl, 4
	rol al, cl
	call disp_al_nybble_hex
	rol al, cl
	pop cx
disp_al_nybble_hex:
	push ax
	push dx
	and al, 15
	add al, '0'
	cmp al, '9'
	jbe .got
	add al, 7
.got:
	xchg dx, ax
	mov ah, 02h
	int 21h
	pop dx
	pop ax
	retn


	align 2
timer:
.1:		dw 0
.2:		dw 0
.3:		dw 0
match:		dw 0
pre:	dw 0, msg.pre	; pre+4 = table
table:
	dw addr1, msg.addr1
	dw addr2, msg.addr2
	dw addr3, msg.addr3
	dw addr4, msg.addr4
	dw addr5, msg.addr5
	dw 0
msg:
.error:		db "Error!",13,10,36
.header:	db "Traced Next   Double",13,10,36
.sep:		db "h, ",36
.addr1:		db "h, Iret points to inst traced",13,10,36
.addr2:		db "h, Iret points to next inst traced",13,10,36
.addr3:		db "h, Iret points to double next inst traced",13,10,36
.addr4:		db "h, Iret points to i08",13,10,36
.addr5:		db "h, Iret points to i08.enter",13,10,36
.pre:		db "h, Iret points to unknown",13,10,36

testp.com: Less expensive variation, for HP 95LX

Only given as a binary and the patch by which it was created:

lcdebugu.com test.com
e 174 90 90
e 16F as words 800
k testp.com
w
q

This reduces the inner loop counter from 0FFFFh to 0800h iterations, and replaces the mul ax instruction by two nop instructions.

testpt.com

Again only given as a binary and patch:

lcdebugu.com testp.com
e 24D 90 90
k testpt.com
w
q

This disables the fallback to the original int 1 handler, instead always returning to the interrupted code with TF=0 and thus likely improving the chance to see an unknown interrupt return address.

My second attempt: Wrapping Michael Karcher's code

test2.asm: This contains a code sequence originally suggested by RC.SE user Michael Karcher. It wraps it in some handling so that the Trace Interrupt return addresses are stored and can be listed at the end, including (by way of NASM macro magic) a copy of the assembly language source text line corresponding to a traced instruction. This is the code used in my second answer to the TF IRQ question.

Unlike the first test, this one allows to specify more parameters:

  • A number specifying the number of busy loops in the int 1 handler (outer loop). Minimum is zero for no busy looping.
  • Optional: Exclamation mark, patch out mul ax using two nop instructions.
  • Optional: Question mark followed by number, specify the number of busy loops in the int 1 handler (inner loop). 1 is the minimum because 0 is treated like 65_536. Default is 65_535.
  • Optional: One or two plus signs.

The output is as follows:

  • No plus sign: Dump of the addresses stored in the Trace Interrupt Return Address buffer. Lists CS, IP, and either a message indicating "Not in our CS", "Unknown line in our CS", or the offset behind this instruction and the instruction source text line for identification.
  • One plus sign: Listing of all known instruction lines, with a leading asterisk if a Trace Interrupt pointing to this instruction was detected.
  • Two plus signs: Listing of known instructions first, then the dump of addresses.
; Contains code licensed under CC-BY-SA by Michael Karcher
;  from https://retrocomputing.stackexchange.com/questions/12693/how-does-single-stepping-on-the-8086-interact-with-internal-and-external-interru/12694#12694
;  2026 by E. C. Masloch

	cpu 8086
	org 256
start:
	cmp sp, stack.top
	jb error
	mov si, 81h
	call getnumber
	mov word [counter], di
	dec si
scan:
.:	lodsb
	cmp al, 32
	je .
	cmp al, 9
	je .
	cmp al, 13
	je .end
	cmp al, '!'
	jne .notex
	mov word [..@multiply], 9090h
	jmp .

.notex:
	cmp al, '?'
	jne .notq
	call getnumber
	mov word [..@counter], di
	dec si
	jmp .

.notq:
	cmp al, '+'
	jne .notplus
	inc byte [..@list]
	jmp .

.notplus:
	jmp error

.end:

hook:
	mov ax, 3501h
	int 21h
	mov word [i01.next], bx
	mov word [i01.next + 2], es
	mov dx, i01
	mov ax, 2501h
	int 21h

	mov ax, 35A1h
	int 21h
	mov word [iA1.next], bx
	mov word [iA1.next + 2], es
	mov dx, iA1
	mov ax, 25A1h
	int 21h

%define STRINGS db ""
%define TABLE dw ""

%imacro i 1+.nolist
 %assign %%entry ($ - start + 256)
 %defstr %%string %1
 %xdefine STRINGS STRINGS, %%name:, {db %%string, 36}
 %xdefine TABLE TABLE, dw %%entry, dw %%length, dw %%name
	%1
 %assign %%length ($ - start + 256) - %%entry
%endmacro

	align 64, nop
test:
; Following from https://retrocomputing.stackexchange.com/questions/12693/how-does-single-stepping-on-the-8086-interact-with-internal-and-external-interru/12694#12694
i pushf
i mov  ax, 300h      ; 100h = TF; 200h = IF
i push ax
i popf               ; This instruction sets the trace flag
i mov  ax, 1234h
i mov  bl, 1    
i inc  ax       
i mov  dx, ds   
i mov  es, ax        ; On 8086/8088: No interrupts before NOP is executed
i nop           
i mov  dx, ss   
i mov  ss, dx        ; On any x86 processor: No interrupts before NOP is executed
i nop           
i int  0A1h     
i dec  cx       
i popf               ; This instruction clears the trace flag
i dec  ax

unhook:
	lds dx, [cs:i01.next]
	mov ax, 2501h
	int 21h

	lds dx, [cs:iA1.next]
	mov ax, 25A1h
	int 21h

dump:
	 push cs
	 pop ds
	 push cs
	 pop es
	mov si, buffer
	mov di, [next]
	mov ax, di
	sub ax, si
	shr ax, 1
	shr ax, 1
	call disp_ax_hex

	mov dx, msg.linebreak
	call disp_msg

	mov bx, cs
	xchg cx, ax
	int3
	cmp byte [..@list], 1
	jb dumponly
list:
	push si
	push cx
	mov si, table
.loop:
	lodsw

	mov dx, msg.asterisk
	mov di, buffer
	push cx
.next:
	scasw
	je .found
	scasw		; di += 2
	loop .next
.notfound:
	mov dx, msg.blank
.found:
	call disp_msg
	pop cx
	call disp_ax_hex
	mov dx, msg.plus
	call disp_msg
	lodsw
	call disp_al_dec
	mov dx, msg.sep
	call disp_msg
	lodsw
	xchg dx, ax
	call disp_msg
	mov dx, msg.linebreak
	call disp_msg

	cmp si, table.end
	jb .loop
.end:
	pop cx
	pop si
	cmp byte [..@list], 2
	jb exit
dumponly:
	jcxz .done
.loop:
	lodsw
	xchg ax, dx
	lodsw
	call disp_ax_hex
	call disp_colon
	xchg ax, dx
	call disp_ax_hex
	cmp dx, bx
	je .ourcs
	mov dx, msg.not_our_cs
	call disp_msg
	jmp .next

.ourcs:
	mov di, table
.innerloop:
	scasw
	jne .innernext
	add ax, word [di + 0]
	mov dx, msg.sep
	call disp_msg
	call disp_ax_hex
	mov dx, msg.sep
	call disp_msg
	mov dx, word [di + 2]
	call disp_msg
	mov dx, msg.linebreak
	call disp_msg
	jmp .next

.innernext:
	scasw			; di += 2
	scasw			; di += 2
	cmp di, table.end
	jb .innerloop
	mov dx, msg.unknownline
	call disp_msg
.next:
	loop .loop
.done:

exit:
	mov ax, 4C00h
	int 21h

error:
    mov dx, msg.error
    call disp_msg
    mov ax, 4C01h
    int 21h

disp_msg:
	push ax
	mov ah, 09h
	int 21h
	pop ax
	retn

disp_colon:
	push ax
	push dx
	mov dl, ':'
	mov ah, 02h
	int 21h
	pop dx
	pop ax
	retn

retf_inst:
	retf

	align 2
iA1:
i	jmp strict short .enter
.next:	dd 0
	dw "KB"
	db 0
	jmp strict short retf_inst
	times 7 db 0
.enter:
i	inc dx
i	dec dx
i	iret

	align 2
i01:
	jmp strict short .enter
.next:	dd 0
	dw "KB"
	db 0
	jmp strict short retf_inst
	times 7 db 0
.enter:
	push bp
	mov bp, sp
	push es
	push di
	push ax
	push cx
	push dx
	cld
	 push cs
	 pop es
	mov di, [es:next]
	cmp di, buffer.end
	jb .space
	inc word [es:toomany]
	jmp .done

.space:
	mov ax, word [bp + 2]	; ip
	stosw
	mov ax, word [bp + 4]	; cs
	stosw
	mov word [es:next], di
.done:

    mov cx, word [es:counter]
    jcxz .skip
    cli
.outer:
    push cx
    mov cx, -1
..@counter: equ $ - 2
.inner:
    mov ax, 16661
..@multiply:
    mul ax
    add ax, ax
    loop .inner
    pop cx
    loop .outer
.skip:
	pop dx
	pop cx
	pop ax
	pop di
	pop es
	pop bp
	iret

disp_ax_hex:
    xchg al, ah
    call disp_al_hex
    xchg al, ah
disp_al_hex:
    push cx
    mov cl, 4
    rol al, cl
    call disp_al_nybble_hex
    rol al, cl
    pop cx
disp_al_nybble_hex:
    push ax
    push dx
    and al, 15
    add al, '0'
    cmp al, '9'
    jbe .got
    add al, 7
.got:
    xchg dx, ax
    mov ah, 02h
    int 21h
    pop dx
    pop ax
    retn


disp_al_dec:
	push ax
	push cx
	push dx
	mov cx, 100 << 8; ch = 100, cl = 0
.loop:
	mov ah, 0	; ax = dividend
	div ch		; ah = remainder, al = quotient
	 push ax
	test al, al	; leading zero ?
	jnz .do		; no -->
	test cx, 01FFh	; (ch & 1) == 0 (not true for 100, 10)
			;  AND cl == 0
	jz .dont	; then skip leading zero -->
.do:
	inc cx		; remember had a digit
	add al, '0'
	xchg dx, ax
	mov ah, 02h
	int 21h
.dont:
	mov al, ch
	aam		; divide by 10
	mov ch, ah
	 pop ax
	mov al, ah
	cmp ch, 1
	jae .loop
	pop dx
	pop cx
	pop ax
	retn


		; INP:	si -> text
		; OUT:	si - 1 -> end (nondigit)
		;	di = number
getnumber:
.skip:
    lodsb
    cmp al, 32
    je .skip
    cmp al, 9
    je .skip
    cmp al, '0'
    jb error
    cmp al, '9'
    ja error
    xor di, di
    jmp .digit
.loop:
    lodsb
.digit:
	cmp al, '_'
	je .loop
    sub al, '0'
    jb .end
    cmp al, 10
    jae .end
    cbw
    add di, di  ; times 2
    mov bx, di
    add di, di  ; times 4
    add di, di  ; times 8
    add di, bx  ; times 10
    add di, ax
    jmp .loop

.end:
	retn

%imacro dumptables 1-*.nolist
 %rep %0
	%1
  %rotate 1
 %endrep
%endmacro

	align 2, db 0
table:
dumptables TABLE
.end:

next:	dw buffer
toomany:dw 0
..@list:db 0

msg:
.sep:		db "  ",36
.linebreak:	db 13,10,36
.not_our_cs:	db "  Not in our CS",13,10,36
.unknownline:	db "  Unknown line in our CS",13,10,36
.error:		db "Error!",13,10,36
.asterisk:	db "*",36
.blank:		db " ",36
.plus:		db "+",36
dumptables STRINGS

	absolute $

	alignb 2
counter:resw 1

	alignb 4
buffer:
	resd 512
.end:

	alignb 16
stack:
	resb 512
.top:

A20 switch utilities

a20off

a20off.asm: Simple program which calls either the XMM "Global Disable A20" function or int 15h function 2400h in order to attempt turning off the A20 line.

; Public Domain

	cpu 8086
start:
	mov ax, 4300h
	int 2Fh
	cmp al, 80h
	jne noxms
	mov ax, 4310h
	int 2Fh
	push es
	push bx
	mov bp, sp
	mov ah, 04h
	call far [bp]
	jmp end

noxms:
	mov ax, 2400h
	int 15h
end:
	mov ax, 4C00h
	int 21h

a20on

a20on.asm: The inverse, using either "Global Enable A20" or int 15h function 2401h.

; Public Domain

	cpu 8086
start:
	mov ax, 4300h
	int 2Fh
	cmp al, 80h
	jne noxms
	mov ax, 4310h
	int 2Fh
	push es
	push bx
	mov bp, sp
	mov ah, 03h
	call far [bp]
	jmp end

noxms:
	mov ax, 2401h
	int 15h
end:
	mov ax, 4C00h
	int 21h

emu2 keyboard line input test

test.asm: Check that running int 21h function 0B00h then function 0Ah will actually prompt for input rather than returning immediately. This test was written for the first bug report that I sent to the original emu2 repo.

	cpu 8086
	org 256
start:
	mov ax, 0B00h
	int 21h

	mov ah, 0Ah
	mov dx, buffer
	int 21h
	mov ax, 4C00h
	int 21h

buffer:
	db 255
	db 0
	times 255 db 13
You could leave a comment if you were logged in.
blog/pushbx/2026/0113_some_more_test_cases_as_of_recently.txt · Last modified: 2026-01-13 20:23:56 +0100 Jan Tue by ecm