; *********** EVENTCHN.ASM *******************************************

        .model medium, basic
        .data

IntNumber       dw      -1      ; the requested INT vector

        .code

PreviousVector  LABEL DWORD     ; placed in the code segment to...
PVOffset        dw      -1      ; ...facilitate chaining
PVSegment       dw      77      ; a signature to avoid multi-installs

EXTRN   SetUEvent:FAR           ; this is in the BC runtime
EXTRN   B_OnExit:FAR            ;          ditto

InstallHandler  PROC intnum     ; intnum passed BYVAL

; This takes care of wrapping our metaphorical tentacles around the
; PC.  We take over the interrupt vector, saving the previous contents
; so that we can put things back the way they were when we exit.

        cmp     PVSegment, 77   ; insure this is first time
        jne     iherr
        mov     ax, intnum      ; get the requested vector
        cmp     ax, 255
        ja      iherr           ; vector too high...
        mov     dx, offset RemoveInt
        push    cs              ; push far address of RemoveInt
        push    dx              ; to register the exit routine
        call    B_OnExit        ; so that we don't hang machine
        or      ax, ax          ; registered OK?
        jz      iherr           ; error: too many registered routines
        mov     ax, intnum      ;  _onexit corrupted this
        mov     IntNumber, ax   ; save for removal routine
        mov     ah, 35H         ; use DOS to get current...
        INT     21H             ; ...holder of the vector
        mov     PVSegment, es   ; save for chaining, removal
        mov     PVOffset, bx
        push    ds              ; hold DS a moment
        mov     bx, cs          ; transfer CS to DS
        mov     ds, bx
        mov     dx, offset IntHandler; get address of our MASM handler
        mov     ah, 25H         ; tell DOS we're taking this vector
        INT     21H             ; grab the vector, Victor
        pop     ds              ; restore DS
        xor     ax, ax          ; return zero: all OK
@@:     ret

iherr:  mov     ax, -1          ; couldn't install: return TRUE
        jmp     @b

InstallHandler  ENDP

RemoveInt       PROC uses ds

; RemoveInt will undo the InstallHandler by restoring the PC to the 
; state it was in before we came along.  If we don't, the interrupt
; vector will point into volatile space when the program terminates.
;
; Because we "register" this routine with B_OnExit, restoring the 
; machine state becomes a part of BASIC's normal shutdown process...
; we don't need to call this routine explicitly.

        mov     ax, IntNumber           ; vector we're using
        mov     ah, 25H                 ; SET VECTOR call
        lds     dx, PreviousVector      ; saved original vector
        INT     21H                     ; restore it
        mov     PVSegment, 77           ; only needed for .QLB purposes
        ret

RemoveInt       ENDP

IntHandler      PROC FAR  ;note that we don't just say "PROC" here

; This routine notifies the BASIC runtime that an event has occurred.
; You MUST save and restore any registers you use.  In this example
; we're overly pessimistic, assuming that BC will destroy everything.

        assume  cs:@code, ds:nothing, es:nothing, ss:nothing

        push    ds              ; only save the registers you really need to
        push    di
        push    es
        push    si
        push    dx
        push    cx
        push    bx
        push    ax
        call    SetUEvent       ; notify BC/QB that the event occurred
        pop     ax
        pop     bx
        pop     cx
        pop     dx
        pop     si
        pop     es
        pop     di
        pop     ds
        jmp     [PreviousVector]  

IntHandler      ENDP

        END

