;       SCCSID = @(#)fapisig.asm	6.1 91/02/15
;       SCCSID = @(#)fapisig.asm	6.4 90/01/22
; ****************************************************************************
; *									     *
; *			  IBM/Microsoft Confidential			     *
; *									     *
; *		    Copyright (c) IBM Corporation  1987, 1990		     *
; *		    Copyright (c) Microsoft Corp.  1987, 1990		     *
; *			      All Rights Reserved			     *
; *									     *
; ****************************************************************************
; *** fapisig.asm - module to handle signals in family api
;
;       Member of API library
;       David Silverstein
;       Copyright MicroSoft Corporation, 1986
;       23-Sep-86
;
;       library routine for API simulator
;
;       Called from dos 2.x,3.x compatible dos 5.x applications
;
;       Special Information
;
;               This file will get linked whenever the following FAPI calls
;               are used: DOSBEEP, DOSDEVIOCTL, DOSERROR, DOSHOLDSIGNAL,
;               DOSSETSIGHANDLER, DOSSETVEC
;
;       Modification History
;

include dos2equ.inc
include api.inc

CRIT_ABORT      equ     2               ; abort code returned from critical
                                        ; error handler.
CTRLCINT        equ     23h             ; control C interrupt
CRITERRINT      equ     24h             ; critical error handler interrupt

; flags for interrupt installation and control

INT23           equ     1               ; control C
INT24           equ     2               ; critical error
EXIT            equ     4               ; program exit
GIVECTRL        equ     8               ; give control (jump to)

; The purpose of this common segment is to define an exit cleanup routine
; when this file is loaded.  The segment contains the far address of the
; cleanup routine

_SIGS   segment byte common 'CODE'

        dw      offset _TEXT:exitlist   ; offset of cleanup routine
        dw      seg _TEXT               ; segment of cleanup routine

_SIGS   ends


_TEXT   segment byte public 'CODE'
        assume  CS:_TEXT, DS:nothing

        public  FapiAddSigCatch
        public  FapiSubSigCatch

SigHandTable    HOOKSTRUC       <, INT23+GIVECTRL>      ; DosHoldSignal
                HOOKSTRUC       <, INT23>               ; DosBeep
                HOOKSTRUC       <, INT23+GIVECTRL>      ; DosSetSigHandler
                HOOKSTRUC       <, INT23+INT24+EXIT>    ; DosDevIoctl category 5
                HOOKSTRUC       <, INT23+INT24+EXIT>    ; DosSetVec
I_Versio        HOOKSTRUC       <, INT23+INT24+EXIT>    ; I_Versio
CtrlCVect       HOOKSTRUC       <, INT23+GIVECTRL>      ; original ctrlc vector
CritErrExit     HOOKSTRUC       <, INT24+GIVECTRL>
DosErrorVect    HOOKSTRUC       <, INT24>               ; DosError

HOOKTABLESIZE   equ             ($ - SigHandTable)

CritErrVect     dd      ?               ; critical error handler vector
TempVec         dd      ?               ; temporary vector used for far jump
HooksInstalled  db      0               ; flag of interrupt vec hooks installed
ExitStatus      db      0               ; flag which signal caused exit


; This procedure is used for enabling a signal catching routine from a fapi
; library call.  On input, the following parameters are passed:
;       ds:dx - address of routine
;       bx    - fapi library call
;
; NOTE: If a call to this routine results in hooking CtrlC or the critical
;       error handler, they will remain hooked for the duration of the process.

FapiAddSigCatch proc    far
        push    ds
        push    es
        push    di
        push    bx

        lea     di, _TEXT:[SigHandTable]
        mov     _TEXT:[di][bx], dx                      ; load new address
        mov     _TEXT:[di][bx + 2], ds
        test    byte ptr _TEXT:[di][bx].HookState, INT23; CtrlC hook needed?
        jz      facs1                                   ; no, skip it
        test    _TEXT:[HooksInstalled], INT23           ; CtrlC hook installed?
        jnz     facs1                                   ; no, skip it

        ; Save current control C vector and install a new one.

        push    bx
        mov     ax, D2_GetVector SHL 8 + CTRLCINT       ; get ctrl C vector
        int     21h
        mov     word ptr _TEXT:[CtrlCVect].HookVect, bx ; save it
        mov     word ptr _TEXT:[CtrlCVect].HookVect + 2, es
        mov     bx, SEG _TEXT                           ; seg of new CtrlC trap
        mov     ds, bx
        mov     dx, offset _TEXT:ctrlchook              ; offset
        mov     ax, D2_SetVector SHL 8 + CTRLCINT       ; set ctrl C vector
        int     21h
        or      _TEXT:[HooksInstalled], INT23   ; flag CtrlC hook installed
        pop     bx

facs1:
        test    byte ptr _TEXT:[di][bx].HookState, INT24; Crit Err hook needed?
        jz      facs2                                   ; no, skip it
        test    _TEXT:[HooksInstalled], INT24           ; hook installed?
        jnz     facs2                                   ; no, skip it

        ; install critical error exit routine

        mov     word ptr _TEXT:[CritErrExit].HookVect, offset _TEXT:errExit
        mov     ax, SEG _TEXT
        mov     word ptr _TEXT:[CritErrExit].HookVect+2, ax

        ; Save current critical error vector and install a new one.

        mov     ax, D2_GetVector SHL 8 + CRITERRINT     ; get crit error vector
        int     21h
        mov     word ptr _TEXT:[CritErrVect].HookVect, bx; save it
        mov     word ptr _TEXT:[CritErrVect].HookVect+2, es
        mov     bx, SEG _TEXT                           ; seg of new vector
        mov     ds, bx
        mov     dx, offset _TEXT:criterrhook            ; offset
        mov     ax, D2_SetVector SHL 8 + CRITERRINT     ; set crit error vector
        int     21h
        or      _TEXT:[HooksInstalled], INT24   ; flag crit err hook installed

facs2:
        pop     bx
        pop     di
        pop     es
        pop     ds
        ret
FapiAddSigCatch endp


; This procedure is used for disabling a signal catching routine from a fapi
; library call.  On input, the following parameter is passed:
;       bx    - fapi library call

FapiSubSigCatch proc    far
        push    di

        lea     di, _TEXT:[SigHandTable]        ; addrs of signal handling table
        mov     word ptr _TEXT:[di][bx].HookVect, 0     ; zero far call address
        mov     word ptr _TEXT:[di][bx].HookVect + 2, 0

        pop     di
        ret
FapiSubSigCatch endp


; This procedure calls any clean up routines installed.
; It does this by scanning all of the signal handling table for anything
; flagged with EXIT.  If one is found and the cleanup routine address
; is not zero, that routine is called.

exitlist        proc    far

        lea     si, _TEXT:[SigHandTable]
        xor     bx, bx

exit2:
        test    byte ptr _TEXT:[si][bx].HookState, EXIT ; flagged with EXIT?
        jz      exit4                                   ; keep checking
        mov     ax, word ptr _TEXT:[si][bx].HookVect    ; is the addrs zero?
        or      ax, word ptr _TEXT:[si][bx].HookVect + 2
        jz      exit4                                   ; keep checking
exit3:
        call    dword ptr _TEXT:[si][bx].HookVect       ; call cleanup routine
exit4:
        add     bx, SIZE HOOKSTRUC                      ; inc to next entry
        cmp     bx, HOOKTABLESIZE                       ; end of table?
        jb      exit2                                   ; continue
        ret

exitlist        endp



; Control C handler entry point

ctrlchook:
        mov     _TEXT:[ExitStatus], INT23       ; flag what caused signal

; common code for handling control C and critical error cleanup routines

sigexit1:
        push    ax
        push    bx

        lea     bx, _TEXT:[SigHandTable]        ; addrs of cleanup routine table

sigexit2:
        mov     al, _TEXT:[bx].HookState        ; is this the right routine?
        test    al, _TEXT:[ExitStatus]
        jz      sigexit4                        ; go try next one
        mov     ax, word ptr _TEXT:[bx].HookVect        ; is routine address
        or      ax, word ptr _TEXT:[bx].HookVect+2      ; zero?
        jz      sigexit4                                ; yes, go try next one
        test    byte ptr _TEXT:[bx].HookState, GIVECTRL ; call or jump?
        jz      sigexit3                                ; go call
        mov     ax, word ptr _TEXT:[bx].HookVect        ; set up jump vector
        mov     word ptr _TEXT:[TempVec], ax
        mov     ax, word ptr _TEXT:[bx].HookVect+2
        mov     word ptr _TEXT:[TempVec + 2], ax
        pop     bx                                      ; realign stack
        pop     ax
        jmp     dword ptr _TEXT:[TempVec]               ; give routine control

sigexit3:
        push    bx                                      ; save table index
        call    dword ptr _TEXT:[bx].HookVect           ; call cleanup routine
        pop     bx                                      ; restore table index
sigexit4:
        add     bx, SIZE HOOKSTRUC                      ; next entry
        jmp     short sigexit2


; entry point for the critical error handler

criterrhook:
        cmp     word ptr _TEXT:[DosErrorVect].HookVect, 0   ; DOSERROR enabled?
        jne     criterrhook1
        cmp     word ptr _TEXT:[DosErrorVect].HookVect + 2, 0
        je      criterrhook2
criterrhook1:
        jmp     dword ptr _TEXT:[DosErrorVect].HookVect ; yes, go to its vector

criterrhook2:
        pushf
        call    dword ptr _TEXT:[CritErrVect].HookVect  ; call previous crit
        mov     _TEXT:[ExitStatus], INT24       ; error handler
        cmp     al, CRIT_ABORT                  ; did the user choose abort?
        je      sigexit1                        ; yes, go execute int24 handlers
        iret                                    ; no, return to int21 call


; Tell Dos to abort

errExit:
        mov     ax, CRIT_ABORT
        iret


_TEXT   ends

        end
