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
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.
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.
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:
mul ax using two nop instructions.The output is as follows:
; 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:
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.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
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