;
; Dustin Puryear
;
; Shows how to put the x86 into single-step (debug) mode
;

        IDEAL
        DOSSEG
        MODEL   small
        STACK   256

        DATASEG

string1         db      'I am in a loop! What a hoot!', 13, 10, '$'
string2         db      'I am in an ISR! What a bore!', 13, 10, '$'

int1_flag       dw      0       ; single-step active flag
int1_off        dw      ?       ; interrupt 1's original vector
int1_seg        dw      ?       ; interrupt 1's original vector

        CODESEG

start:
        mov     ax, @data       ; setup data segment registers
        mov     ds, ax
        mov     es, ax

        ; setup single-step ISR and put us in single-step mode

                ; store old interrupt 1's vector

        mov     ax, 3501h       ; get int 1 vector, function 35
        int     21h             ; es:bx <- int 1 vector
        mov     [int1_off], es  ; store int 1's old segment
        mov     [int1_seg], bx  ; store int 1's old offset

                ; set interrupt 1 to new vector

        push    ds

        mov     ax, 2501h       ; set int 1 vector, function 25
        push    cs
        pop     ds              ; ds <- ISR's segment
        mov     dx, OFFSET trap ; dx <- ISR's offset (original name!)
        int     21h             ; int 1 vector <- ds:dx

        pop     ds              ; restore ds and es
        push    ds
        pop     es

                ; tell ISR to bring us into single-step mode

        mov     [int1_flag], 1  ; set flag to active
        int     1               ; tell ISR to turn on single-step (wake up!)

        ; okay, we are in single-step mode.. a few sample runs

        mov     cx, 5d          ; display loop msg 5 times (below)

@@top1:
        mov     ah, 09h         ; DOS write ASCII$ string
        mov     dx, OFFSET string1
        int     21h

        loop    @@top1          ; dec cx and loop if cx != 0

        ; okay, now GET OUT of single-step and reset vector

        mov     [int1_flag], 0  ; this is all we need to do to turn it off

        push    ds
        mov     ax, 2501h       ; set int 1 vector, function 25
        mov     ds, [int1_seg]  ; ds <- old int 1 segment
        mov     dx, [int1_off]  ; dx <- old int 1 offset
        int     21h             ; int 1 <- ds:dx
        pop     ds

        ; don't believe me?

        mov     cx, 5h          ; display loop msg 5 times (below)

@@top2:
        mov     ah, 09h         ; DOS write ASCII$ string
        mov     dx, OFFSET string1
        int     21h

        loop    @@top2

exit:
        mov     ah, 04ch        ; DOS terminate
        mov     al, 0           ; exit code
        int     21h

;
; trap
;
; Input:
;       [int1_flag] = 0, single-step off
;       [int1_flag] = 1, single-step on (issue an int 1 to wake up ISR)
; Output:
;       none
;

PROC    trap

        sti                     ; interrupts on (ever hear of a system clock?)

        push    bp
        mov     bp, sp
        push    ax
        push    dx
        push    ds

        mov     ax, @data       ; setup segment registers
        mov     ds, ax

        ; check if we are told to do something

@@turn_on:

        cmp     [int1_flag], 1  ; turn single-step on?
        jne     @@turn_off      ; guess not..
        or      [WORD bp + 6], 0100h    ; this is an ISR, flags are on stack
        mov     [int1_flag], 3
        jmp     @@quit

@@turn_off:

        cmp     [int1_flag], 0  ; turn single-step off?
        jne     @@routine       ; guess not..
        and     [WORD bp + 6], 0feffh   ; this is an ISR, flags are on stack
        jmp     @@quit

@@routine:

        mov     ah, 09h         ; DOS write ASCII$
        mov     dx, OFFSET string2
        int     21h

@@quit:

        pop     ds
        pop     dx
        pop     ax
        pop     bp

        iret

ENDP    trap

        END     start
