;************************************************************************
;
; __RetICInt  Return the address of the intervention code control block
;
; function  __RetICInt : _IntCtrlPtr;
;
; Description This function returns the address of the intervention
;	      code control block.  The intervention filters are also
;	      defined in this file.
;
;	      The intervention control block controls the intervention
;	      filters that give control to the installed intervention
;	      procedure.  The block is located in the code segment so it
;	      can be accessed by each of the filters.  Its address is
;	      exported through the unit interface (_IntCtrlAddr) and is
;	      initialized by calling __RetICInt (that happens in the
;	      unit initialization code).
;
;	      It is usually not necessary to access the control block,
;	      but it is useful for debugging and reinstalling the previous
;	      vectors that the intervention code filters.
;
; Version     4.00 (C)Copyright Blaise Computing Inc.  1987
;________________________________________________________________________


; Structure defining the interrupt vectors used by the filters

INTVECT    struc
  timerptr  dd	   ?		       ; Timer tick vector
  keybdptr  dd	   ?		       ; Keystroke
  com2ptr   dd	   ?		       ; COM2 interrupt service
  com1ptr   dd	   ?		       ; COM1 interrupt service
  diskptr   dd	   ?		       ; BIOS disk services
  keyioptr  dd	   ?		       ; BIOS keyboard I/O service
  breakptr  dd	   ?		       ; Ctrl/Break handler
  dosptr    dd	   ?		       ; DOS functions
  idleptr   dd	   ?		       ; DOS idle vector
INTVECT    ends


; Macro definitions.  popff simulates a pop flags operations (thereby
; avoiding a 286 bug), and beginfil defines the beginning of a filter.

popff	   macro		       ;; Simulate POPF instruction
	   local   do_call,do_iret
	   jmp	   short do_call
do_iret:
	   iret 		       ;; Pop IP, CS and flags
do_call:
	   push    cs
	   call    do_iret	       ;; CS and IP now pushed
	   endm

beginfil   macro   filter_proc	       ;; Leave room for pointer to
	   local   begin_label	       ;; intervention code control
	   public  filter_proc	       ;; block at the start of each
filter_proc proc   far		       ;; filter
	   jmp	   short begin_label
	   dw	   offset begin_block
begin_label:
	   endm

; Code begins here ......................................................

code	   segment
	   assume  cs:code,ds:nothing

; Define the intervention code control block.  Its definition must be the
; same as exported through the ISR unit interface.

begin_block equ     $

signature   db	    3,'BCI'	       ; Intervention code signature
ident	    db	    10 dup (0)	       ; Intervention code identification
psp	    dw	    0		       ; PSP of the intervention code
prev_vect   INTVECT <>		       ; Interrupt vectors altered
installed   db	    0		       ; Intervention code installed
dosneed     db	    0		       ; DOS services required
dkeyneed    db	    0		       ; DOS function 1-12 required
keysize     db	    0		       ; Size of hot key table
keytable    dd	    0		       ; Address of hot key table
keyfound    dw	    0		       ; Hot key found if any
keyaction   dw	    0		       ;  and associated hot key action.
waiting     db	    0		       ; In 21 or 28 filter wait loop
readykey    db	    0		       ; INT 16 ReadyKey function number
getkey	    db	    0		       ; INT 16 GetKey function number
request     db	    0		       ; Intervention code request made
sleep	    db	    0		       ; Intervention code is asleep
timerbusy   db	    0		       ; Timer filter is busy
keybdbusy   db	    0		       ; Keystroke filter is busy
com2busy    db	    0		       ; COM2 ISR is in use
com1busy    db	    0		       ; COM1 ISR is in use
diskbusy    db	    0		       ; BIOS disk service in use
keyiobusy   db	    0		       ; BIOS keyboard I/O filter busy
idlebusy    db	    0		       ; DOS idle filter is busy
inidle	    db	    0		       ; In a safe section of DOS idle
ctrlbreak   db	    0		       ; Ctrl/Break or C pressed
schedptr    dd	    0		       ; Address of task schedulre
indosptr    dd	    0		       ; Address of DOS in use flag
incritptr   dd	    0		       ; Address critical error flag
currentid   dw	    0		       ; Current process identification

; The following internal variables are used to store the address
; of the caller's return address upon entry to the intervention
; filters.

timer_caller_ofs dw ?
timer_caller_seg dw ?

keybd_caller_ofs dw ?
keybd_caller_seg dw ?

com2_caller_ofs  dw ?
com2_caller_seg  dw ?

com1_caller_ofs dw  ?
com1_caller_seg dw  ?

disk_caller_ofs  dw ?
disk_caller_seg  dw ?

keyio_caller_ofs dw ?
keyio_caller_seg dw ?
keyio_save_flags dw ?
keyio_save_bx	 dw ?

dos_caller_ofs	 dw ?
dos_caller_seg	 dw ?
dos_save_flags	 dw ?

idle_caller_ofs  dw ?
idle_caller_seg  dw ?


;========================================================================
;
; __RetICInt  Return the address of the intervention control block
;
; Notice that __RetICIsr is a near procedure because it is not exported
; through the unit interface.
;
;________________________________________________________________________

	   public  __RetICInt
__RetICInt proc    near

	   mov	   dx,cs		  ; Move address of the block into
	   mov	   ax,offset begin_block  ; DX:AX for function return
	   ret
__RetICInt endp


;========================================================================
;
; TimerFilter  Filter for the timer tick interrupt ($08)
;
; If the timer busy flag is not set, then set the busy flag TRUE, call
; the previous handler, and upon return call the task scheduler.  When
; the task scheduler returns, pass control back to the original caller
; by pushing the address on the stack and performing a far return.  If
; the timer busy flag is set (that is, the filter has already gained
; control), just jump to the original timer handler.
;
;________________________________________________________________________

	   beginfil TimerFilter

	   pushf
	   cli
	   cmp	   com1busy,1	       ; Exit immediately if communica-
	   je	   timer_jmp_prev      ; tions interrupts are being
	   cmp	   com2busy,1	       ; processed.
	   je	   timer_jmp_prev
	   cmp	   timerbusy,0	       ; If the filter is already active
	   je	   timer_enter	       ; jump to the previous timer

timer_jmp_prev:
	   popff
	   jmp	   prev_vect.timerptr  ; interrupt handler

timer_enter:
	   mov	   timerbusy,1	       ; Set the busy flag
	   popff
	   pop	   timer_caller_ofs    ; Save caller's address
	   pop	   timer_caller_seg

; Call the previous handler.  This will pop the flags from the top of the
; stack, send an EOI to the interrupt controller (8259), and reenable
; other interrupts.

	   call    prev_vect.timerptr

	   pushf		       ; Call the scheduler.  It is like
	   call    schedptr	       ; an ISR so the flags are pushed.

	   push    timer_caller_seg    ; Return to the caller; save the
	   push    timer_caller_ofs    ; address on the stack, release the
	   mov	   timerbusy,0	       ; busy flag, and transfer control
	   ret			       ; via a far return.

TimerFilter endp


;========================================================================
;
; KeyBdFilter  Filter for the keystroke interrupt ($09)
;
; If the keybd busy flag is not set, then set the busy flag TRUE and call
; the previous handler.  If a keystroke is ready, it is checked to see if
; it is a hot key (that is, it is in the key signal table).  If a hot key
; is found, it and its action are recorded and control is returned.
;
;________________________________________________________________________

	   beginfil KeyBdFilter

	   pushf
	   cli			       ; Disable interrupts while setting
	   cmp	   keybdbusy,0	       ; setting the busy flag.
	   je	   keybd_enter	       ; If the filter is already active
	   popff		       ; jump to the previous handler.
	   jmp	   prev_vect.keybdptr

keybd_enter:
	   mov	   keybdbusy,1	       ; Set the flag busy to prevent
	   popff		       ; nesting.

; Pop off the return address of the process that was interrupted (note
; that the flags are still on the stack), and make a far call to the
; previous keystroke ISR (it will remove the flags).  The previous ISR
; will also send an EOI to the interrupt controller, enabling further
; hardware interrupts.

	   pop	   keybd_caller_ofs    ; Save caller's address
	   pop	   keybd_caller_seg
	   call    prev_vect.keybdptr

; In order to check for a hot key, we must save all the registers because
; the BIOS is called as well as a search.

	   pushf
	   push    ax
	   push    bx
	   push    cx
	   push    dx
	   push    bp
	   push    si
	   push    di
	   push    ds
	   push    es

; If a key request is already pending (keyaction is not zero), no check-
; ing is required, in which case, just return.	Otherwise, if there are
; key signals to look for (the key table size, keysize, is not zero),
; and there is a keystroke in the keyboard buffer (the BIOS "key ready"
; function tell this), search the table to see if a signal if found.

	   cmp	   keyaction,0	       ; Quit if a signal has been already
	   jne	   keybd_exit	       ; detected and service is pending.

	   xor	   cx,cx
	   mov	   cl,keysize	       ; Now scan the table...
	   jcxz    keybd_exit	       ;  if it has some entries
	   mov	   ah,readykey	       ; See if a key sequence is in the
	   int	   16h		       ; keyboard buffer (it is in AX),
	   jz	   keybd_exit	       ;  and quit if none is found.

	   les	   di,keytable
	   cld

keybd_loop:
	   scasw		       ; Check one hot key listing
	   je	   keybd_found
	   inc	   di		       ; Step over the action code and
	   inc	   di		       ; the reserved byte.
	   loop    keybd_loop

	   mov	   keyfound,0	       ; The keystroke is not a hot key
	   mov	   keyaction,0
	   jmp	   keybd_exit

; Save the key action associated with the signal (hot key) found.  If the
; task is awake (sleep is zero), then go on to record the request.
; If, on the other hand, the task is asleep, just return unless the
; request is a wakeup signal (wake action).  If the hot key is recognized
; and recorded, it is removed from the keyboard buffer by calling the
; BIOS "get a key" function.

keybd_found:			       ; Found a hot key
	   xor	   bx,bx
	   mov	   bl,byte ptr es:[di] ; BX is the key action for the key
	   cmp	   sleep,1
	   jne	   keybd_awake
	   cmp	   bx,3 	       ; It is a wake up call?
	   jnz	   keybd_exit	       ; Leave if it is not

keybd_awake:
	   mov	   keyfound,ax	       ; Record the hot key and action
	   mov	   keyaction,bx        ; for access by the scheduler
	   mov	   ah,getkey	       ; Remove the hot key from the
	   int	   16h		       ; BIOS keyboard buffer

; Now prepare to exit.	Restore all the registers, leaving the flags
; on the stack; they will be removed by the IRET instruction.  The
; value of the flags are those returned by the previous 9 handler.

keybd_exit:
	   pop	   es
	   pop	   ds
	   pop	   di
	   pop	   si
	   pop	   bp
	   pop	   dx
	   pop	   cx
	   pop	   bx
	   pop	   ax

	   push    keybd_caller_seg    ; Prepare for IRET
	   push    keybd_caller_ofs
	   mov	   keybdbusy,0

	   iret

KeyBdFilter endp


;========================================================================
;
; Com2Filter  Filter for the COM2 interrupt
;
; This interrupt is filtered to ensure that the task scheduler does not
; gain control if a communications interrupt is being processed.  The
; problem occurs if the COM interrupt service routine is processing input
; data, and a timer interrupt occurs.  If control is not returned to the
; COM ISR immediately, data characters can be lost.
;
;________________________________________________________________________

	   beginfil Com2Filter

	   pushf
	   cli			       ; Disable interrupts while setting
	   cmp	   com2busy,0	       ; setting the busy flag.
	   je	   com2_enter	       ; If the filter is already active
	   popff		       ; jump to the previous handler.
	   jmp	   prev_vect.com2ptr

com2_enter:
	   mov	   com2busy,1	       ; Set the flag busy to prevent
	   popff		       ; nesting.
	   pop	   com2_caller_ofs     ; Save caller's address
	   pop	   com2_caller_seg
	   call    prev_vect.com2ptr   ; Call the previous handler

	   push    com2_caller_seg     ; Prepare to return control to
	   push    com2_caller_ofs     ; the original process
	   mov	   com2busy,0	       ;  and clear the busy flag
	   ret

Com2Filter endp


;========================================================================
;
; com1filter  Filter for the COM1 interrupt
;
; This interrupt is filtered for the same reasons that the interrupt for
; COM2 is filtered.
;
;________________________________________________________________________

	   beginfil Com1Filter

	   pushf
	   cli			       ; Disable interrupts while setting
	   cmp	   com1busy,0	       ; setting the busy flag.
	   je	   com1_enter	       ; If the filter is already active
	   popff		       ; jump to the previous handler.
	   jmp	   prev_vect.com1ptr

com1_enter:
	   mov	   com1busy,1	       ; Set the flag busy to prevent
	   popff		       ; nesting.
	   pop	   com1_caller_ofs     ; Save caller's address
	   pop	   com1_caller_seg
	   call    prev_vect.com1ptr   ; Call the previous handler

	   push    com1_caller_seg     ; Prepare to return control to
	   push    com1_caller_ofs     ; the original process
	   mov	   com1busy,0	       ;  and clear the busy flag
	   ret

Com1Filter endp


;========================================================================
;
; DiskFilter  Filter for the BIOS disk services ($13)
;
; This interrupt is filtered to ensure that no intervention procedure
; requiring disk services is given control during BIOS disk calls.
; Normally this not a concern, because the DOS filter catches this situ-
; ation, but some processes may call the BIOS services directly.
;
;________________________________________________________________________

	   beginfil DiskFilter

	   pushf
	   cli			       ; Disable interrupts while setting
	   cmp	   diskbusy,0	       ; setting the busy flag.
	   je	   disk_enter	       ; If the filter is already active
	   popff		       ; jump to the previous handler.
	   jmp	   prev_vect.diskptr

disk_enter:
	   mov	   diskbusy,1	       ; Set the flag busy to prevent
	   popff		       ; nesting.
	   pop	   disk_caller_ofs     ; Save caller's address
	   pop	   disk_caller_seg
	   call    prev_vect.diskptr   ; Call the previous handler

	   push    disk_caller_seg     ; Prepare to return control to
	   push    disk_caller_ofs     ; the original process
	   mov	   diskbusy,0	       ;  and clear the busy flag
	   ret

diskfilter endp


;========================================================================
;
; KeyIOFilter  Filter for BIOS keyboard I/O services ($16)
;
; An intervention procedure requiring DOS services can use DOS
; services for I/O from within the procedure.  When a Ctrl/C is
; encountered, DOS prepares for an INT 23h call, and upon return either
; terminates or calls the interrupted DOS function again.  The former case
; can be handled by temporarily replaced the 23 handler; however, in the
; latter case, DOS is left in an unstable state when control returns to the
; foreground process.
;
; The purpose of this filter is to prevent Ctrl/C to be passed to any
; DOS call.  The filter is only installed by the task scheduler if DOS
; services are required, and the previous vector is reestablished before
; control is retunred from the scheduler.
;
; The filter checks for BIOS keyboard I/O calls 0 (GetKey) and 1
; (ReadyKey).  DOS does not use the extended keyboard functions to access
; the keyboard.  If a GetKey call is made, the filter checks for a Ctrl/C
; and if one is found, discards it and calls GetKey again until a key
; sequence different from Ctrl/C is returned. If a ReadyKey call is made,
; the filter checks if a Ctrl/C is in the buffer.  If it is, it removes
; it and keeps checking until the buffer is either empty or another key
; is found.  The status is then returned to the calling program.
;
; If a Ctrl/C is found, even though it is discarded, the ctrlbreak flag
; in the intervention control block is set, so the intervention procedure
; can detect if a Ctrl/C has been pressed.
;________________________________________________________________________

	   beginfil KeyIOFilter

	   pushf
	   cli
	   cmp	   ah,2 	       ; Need only check for GetKey (0)
	   jae	   keyio_jmp_prev      ; and ReadyKey (1).
	   cmp	   keyiobusy,0	       ; If the filter is already active
	   je	   keyio_enter	       ; jump to the previous handler
keyio_jmp_prev:
	   popff
	   jmp	   prev_vect.keyioptr

keyio_enter:
	   mov	   keyiobusy,1	       ; Set the busy flag to prevent
	   popff		       ; nesting.
	   pop	   keyio_caller_ofs    ; Return the caller's address and
	   pop	   keyio_caller_seg    ; the flags as passed to the ISR.
	   pop	   keyio_save_flags
	   push    ax		       ; Save the function number, and
	   push    keyio_save_flags    ; put the flags back on the stack
	   call    prev_vect.keyioptr  ; as the original handler will pop
				       ; them off.
	   mov	   keyio_save_bx,bx    ; Save BX and restore at exit
	   pop	   bx		       ; Recover the function number, and
	   pushf		       ; save the current flags (ReadyKey
	   cmp	   bh,0 	       ; alters them).
	   jne	   keyio_ready

	   cmp	   al,3 	       ; GetKey returns the key in AX
	   jne	   keyio_exit	       ; Check for Ctrl/C - exit if not

keyio_discard:
	   mov	   ctrlbreak,1	       ; Ctrl/C found.	Before calling
	   popff		       ; services again, restore the
	   mov	   ah,getkey	       ; flags as returned before.
	   int	   16h
	   pushf		       ; Save the flags, and check
	   cmp	   al,3 	       ; for another Ctrl/C.  If found,
	   je	   keyio_discard       ; discard it and look again.
	   jmp	   keyio_exit	       ; If not found, can exit now.

keyio_ready:
	   popff		       ; Check the flags to see if
	   pushf		       ; the zero flag is set; if it is
	   jz	   keyio_exit	       ; no character is ready.
keyio_lose:
	   cmp	   al,3 	       ; If it is a Ctrl/C, set the
	   jne	   keyio_exit	       ; ctrlbreak flag, and then call
	   mov	   ctrlbreak,1	       ; GetKey to throw the character
	   mov	   ah,getkey	       ; away.
	   int	   16h
	   pop	   bx		       ; Remove the saved flags, since
	   mov	   ah,readykey	       ; the flags from the next ReadyKey
	   int	   16h		       ; call must be returned.
	   pushf		       ; Save the returned flags, and
	   jnz	   keyio_lose	       ; check if a character is still
				       ; in the buffer.
keyio_exit:
	   mov	   bx,keyio_save_bx    ; Restore BX, and return the
	   popff		       ; flags.  Prepare to return
	   push    keyio_caller_seg    ; control to the original caller.
	   push    keyio_caller_ofs
	   mov	   keyiobusy,0
	   ret

KeyIOFilter endp


;========================================================================
;
; BreakIsr  Ctrl/Break interrupt service routine ($1B)
;
; Like the keyiofilter, the interrupt service routine is designed to
; prevent DOS from generating an INT 23h instruction from within an
; intervention procedure.
;
; DOS normally sets the 1B vector to point to an ISR which sets a flag
; that is inspected by DOS whenever functions 1-12 are called (and
; for almost every other call if BREAK is on).	The purpose of this ISR
; is to replace DOS's ISR with one that does not set the DOS Ctrl/Break
; flag.  The ISR is installed by the task scheduler if DOS services are
; required by the intervention procedure, but the original ISR is
; reestablished before control is returned.
;
; Breakisr does set the ctrlbreak flag is a Ctrl/Break is encountered so
; that the intervention procedure can detect the event.
;
;________________________________________________________________________


	   public  BreakIsr
BreakIsr   proc    far

	   mov	   ctrlbreak,1
	   iret

BreakIsr   endp


;========================================================================
;
; DOSFilter Filter DOS function calls ($21)
;
; The purpose of this filter is to delay the return from DOS calls if the
; intervention procedure needs DOS and is waiting to gain control.  After
; invoking DOS, this filter may enter a wait loop allowing the scheduler
; to gain control at the next timer tick.  The task scheduler clears the
; wait flag as soon as it gains control.
;
;________________________________________________________________________

	   beginfil DOSFilter

; Under some circumstances it is either not necessary or desirable to
; filter a DOS call.  If the intervention procedure does not require
; DOS services (dosneed is 0), or the task scheduler already has control
; (timerbusy is 1), or a DOS function that does return is invoked, con-
; trol is returned to DOS immediately.

	   push    bx		       ; Save the flags in AX and place
	   xchg    ax,bx	       ; the DOS function code in BH.
	   lahf 		       ; Save flags in AH

	   cmp	   dosneed,0	       ; DOS services not required
	   je	   dos_jmp_prev
	   cmp	   timerbusy,0	       ; The scheduler already has
	   jne	   dos_jmp_prev        ; control.

; The following DOS functions must be called directly because they
; either do not return (so called "one-way" functions), or they
; examine the return address of the caller on the stack.

	   cmp	   bh,26h	       ; Function 26h - Make PSP
	   je	   dos_jmp_prev        ; It examines caller's address
	   cmp	   bh,4Bh	       ; Function 4Bh - EXEC
	   je	   dos_jmp_prev        ; Destroys registers and cannot
				       ; be filtered in a child under 2.10
	   cmp	   bh,00h	       ; Function 00h - Terminate
	   je	   dos_jmp_prev        ; Does not return
	   cmp	   bh,31h	       ; Function 31h - Keep process
	   je	   dos_jmp_prev        ; Does not return
	   cmp	   bh,4ch	       ; Function 4Ch - Terminate
	   jne	   dos_enter	       ; Does not return

dos_jmp_prev:
	   sahf 		       ; Restore the flags and
	   xchg    ax,bx	       ; the registers, and jump to the
	   pop	   bx		       ; DOS handler.  It returns
	   jmp	   prev_vect.dosptr    ; directly to the original caller.

dos_enter:
	   sahf 		       ; Restore the flags and
	   xchg    ax,bx	       ; the registers.
	   pop	   bx

	   cli			       ; Rearrange the top items on the
	   pop	   dos_caller_ofs      ; stack to bring the flags to the
	   pop	   dos_caller_seg      ; top of the stack.
	   pop	   dos_save_flags
	   push    dos_caller_seg
	   push    dos_caller_ofs
	   push    dos_save_flags
				       ; Call DOS; upon return the flags
	   call    prev_vect.dosptr    ; are popped from the stack.

; If no task request is pending, then just return.  Also if the task
; does not require DOS services, control can be returned immediately.
; If, on the other hand, the intervention code does require DOS services
; and DOS is not in a critical section, then go into a wait loop.  The
; task scheduler will unblock the wait.

	   pushf		       ; Save the flags, and turn off
	   cli			       ; interrupts (DOS may have enabled)
	   cmp	   request,0	       ; Skip the wait loop if no request
	   je	   dos_exit	       ; is pending.

	   push    es		       ; Now determine if DOS is in a
	   push    di		       ; critical state.  If it is, there
	   les	   di,indosptr	       ; is no point in waiting.
	   cmp	   es:byte ptr [di],0
	   jne	   dos_busy
	   les	   di,incritptr        ; Is a critical error occurring?
	   cmp	   es:byte ptr [di],0
	   je	   dos_not_busy
dos_busy:
	   pop	   di		       ; DOS is busy (in one way or
	   pop	   es		       ; another), so skip the wait loop
	   jmp	   dos_exit	       ; so DOS can continue
dos_not_busy:
	   pop	   di
	   pop	   es
	   mov	   waiting,1	       ; Now go into the wait loop
	   sti			       ; until the scheduler clears the
dos_wait:			       ; wait flag
	   cmp	   waiting,0
	   jne	   dos_wait

dos_exit:
	   popff		       ; These are the flags as returned
	   ret			       ; by the DOS call

DOSFilter  endp


;========================================================================
;
; IdleFilter  Filter for the DOS idle interrupt ($28)
;
; The purpose of this filter is similar to that of the DOS filter: to
; delay return from the 28 handler if the intervention procedure is
; waiting for service. After invoking the previous handler, this filter
; enters a wait loop if the intervention procedure needs DOS services
; but does not require DOS keyboard (functions 1 - 12) services.
;
;________________________________________________________________________

	   beginfil IdleFilter

	   pushf
	   cli			       ; Disable interrupts while setting
	   cmp	   idlebusy,1	       ; the busy flag.
	   je	   idle_jmp_prev       ; If the filter is already active
				       ; jump to the previous handler.
	   cmp	   dosneed,0	       ; If DOS services are not needed,
	   je	   idle_jmp_prev       ; leave immediately, and
	   cmp	   timerbusy,0	       ; only continue if the scheduler
	   je	   idle_enter	       ; does not already have control.

idle_jmp_prev:
	   popff
	   jmp	   prev_vect.idleptr

idle_enter:
	   mov	   idlebusy,1	       ; Prevent further nesting
	   popff
	   pop	   idle_caller_ofs     ; Save the caller's address
	   pop	   idle_caller_seg
	   call    prev_vect.idleptr   ; Call the previous handler.  The
				       ; original flags are popped.
	   mov	   inidle,1	       ; From here on it is safe for the
				       ; scheduler to pass control on.
	   pushf		       ; Prepare for entry to the wait loop
	   cli
	   cmp	   request,0	       ; Skip the wait loop if no request
	   je	   idle_exit	       ; is pending at this time
	   cmp	   dosneed,0	       ; Skip the wait loop if DOS services
	   je	   idle_exit	       ; are not required.
	   cmp	   dkeyneed,0	       ; Only wait if DOS keyboard services
	   jne	   idle_exit	       ; are not required

	   mov	   waiting,1	       ; Now go into the wait loop
	   sti			       ; until the scheduler clears the
idle_wait:			       ; wait flag
	   cmp	   waiting,0
	   jne	   idle_wait

idle_exit:			       ; Top of the stack contains the
	   push    idle_caller_seg     ; flags returned by the previous
	   push    idle_caller_ofs     ; handler.
	   mov	   inidle,0
	   mov	   idlebusy,0

	   iret

IdleFilter endp

code	   ends
	   end
