;
;       TCtask - DOS access module.
;
;       V1.1    T.Wagner
;       V1.2    TECON Ltd.
;
;
; The DOS interrupt (and the related direct disk I/O interrupts) are
; not reentrant. Access to DOS thus has to be channelled such that no
; two tasks use DOS services simultaneously. However, there is one
; exception to this rule. Whenever DOS is waiting for the keyboard, it
; issues a special interrupt, INT 28h, to signal that background
; processing for functions > 0Ch may be performed. This is used in the
; DOS interface for TCtask in the following manner:
;
;   A task issuing a DOS interrupt will request one of two resources:
;   "lower_dos" for functions <= 0C, "upper_dos" for functions > 0C.
;   If a task gets access to "lower_dos", it will also request the
;   "upper_dos" resource to lock out other tasks from interrupting.
;   This "upper_dos" resource is shortly released on each INT 28, and
;   then immediately reclaimed, with task priority temporarily raised
;   to the maximum value. The first task waiting to execute a
;   function > 0C will thus be scheduled to execute the request, but
;   the resource will be reassigned to the INT 28 handler as soon as
;   this request terminates, so the waiting task will not be delayed too
;   long.
;
; There are two additional safety measures which have to be taken to
; avoid getting into conflicts with other resident background programs,
; especially the DOS PRINT background spooler:
;
;   Before requesting any resource, the status of the DOS critical section
;   flag is checked. If this flag is set, the task is made waiting for
;   the flag to be cleared.
;   Only the task that set the flag will be allowed access to DOS.
;
;   Before actually executing the request, the status of the DOS in-use
;   flag is checked. If this flag is set, the task enters a busy waiting
;   loop, calling the scheduler so that the processor is not tied up.
;
;
        name    tcdos
;
;        .model  large
;
        public  tsk_install_dos_
        public  tsk_remove_dos_
;
;
        include tsk.mac
;
CSECT           =       2ah     ; Critical Section Interrupt
get_in_use_flag =       34h     ; DOS-function to get in_use_flag address

termoff=20h*4
termseg=20h*4+2

idosoff=21h*4
idosseg=21h*4+2

absreadoff=25h*4
absreadseg=25h*4+2

abswriteoff=26h*4
abswriteseg=26h*4+2

keepoff=27h*4
keepseg=27h*4+2

idleoff=28h*4
idleseg=28h*4+2

csectoff=CSECT*4
csectseg=CSECT*4+2

;
;
;intseg  segment at 0
;                org     20h*4
;termoff         dw      ?       ; program terminate vector
;termseg         dw      ?
;                org     21h*4
;idosoff         dw      ?       ; dos interrupt
;idosseg         dw      ?
;                org     25h*4
;absreadoff      dw      ?       ; absolute disk read
;absreadseg      dw      ?
;                org     26h*4
;abswriteoff     dw      ?       ; absolute disk write
;abswriteseg     dw      ?
;                org     27h*4
;keepoff         dw      ?       ; Terminate but stay resident
;keepseg         dw      ?
;                org     28h*4
;idleoff         dw      ?       ; dos idle interrupt
;idleseg         dw      ?
;                org     CSECT*4
;csectoff        dw      ?       ; dos critical section
;csectseg        dw      ?
;
;intseg  ends
;
;----------------------------------------------------------------------------
;
;       Variables
;
        .data
;
        extrn   _tsk_current: dword
;
term_err_msg    db      0dh,0ah,"Program terminated - TCtask uninstalled"
                db      0dh,0ah,'$'
;
idle_active     db      0               ; Idle-Interrupt active
dos310          db      0               ; DOS version >= 3.10
;
        IF      TSK_NAMEPAR
ldos_name       db      "DOSUPPER",0
udos_name       db      "DOSLOWER",0
cdos_name       db      "DOSCRIT",0
        ENDIF
;
        .data?
;
in_use          dd      ?               ; Adress of DOS in-use-flag
in_error        dd      ?               ; Adress of DOS error-mode-flag
;
lower_dos       resource <>
upper_dos       resource <>
critical        flag <>
;
idle_ss         dw      ?               ; Stack save
idle_sp         dw      ?
;
                dw      256 dup(?)      ; Stack for IDLE-Interrupt
local_stack     label   word
;
        .code
;
;       Original Interrupt-Entries
;
savtermoff      dw      ?               ; Terminate vector save
savtermseg      dw      ?
;
savdos          label   dword           ; original DOS-Entry
savdosoff       dw      ?
savdosseg       dw      ?
;
savidle         label   dword           ; original IDLE-Entry
savidleoff      dw      ?
savidleseg      dw      ?
;
savcsect        label   dword           ; Critical Section save
savcsectoff     dw      ?
savcsectseg     dw      ?
;
savabsread      label   dword           ; Absolute Disk Read save
savabsreadoff   dw      ?
savabsreadseg   dw      ?
;
savabswrite     label   dword           ; Absolute Disk Write save
savabswriteoff  dw      ?
savabswriteseg  dw      ?
;
savkeepoff      dw      ?               ; Terminate resident vector save
savkeepseg      dw      ?
;
critsect_active db      0               ; DOS Critical Section active
ctask_active    db      0               ; CTask DOS call active
crit_task       dd      ?               ; Task requesting critical section
;
;
dos     macro
        pushf
        cli
        call    cs:savdos
        endm
;
;---------------------------------------------------------------------------
;
        .code
;
        extrn   _create_resource   :near
        extrn   _delete_resource   :near
        extrn   _request_resource  :near
        extrn   _release_resource  :near
        extrn   _create_flag       :near
        extrn   _delete_flag       :near
        extrn   _set_flag          :near
        extrn   _clear_flag        :near
        extrn   _wait_flag_clear   :near
        extrn   _remove_tasker     :near
        extrn   scheduler          :near
;
;
;       void tsk_install_dos (void)
;
;               Install DOS handler
;
tsk_install_dos_        proc
;
;       create needed resources & flags
;
        IF      TSK_NAMEPAR
        mov     ax,offset udos_name
        push    ds
        push    ax
        ENDIF
        mov     ax,offset upper_dos
        push    ds
        push    ax
        call    _create_resource
        IF      TSK_NAMEPAR
        add     sp,8
        ELSE
        add     sp,4
        ENDIF
;
        IF      TSK_NAMEPAR
        mov     ax,offset ldos_name
        push    ds
        push    ax
        ENDIF
        mov     ax,offset lower_dos
        push    ds
        push    ax
        call    _create_resource
        IF      TSK_NAMEPAR
        add     sp,8
        ELSE
        add     sp,4
        ENDIF
;
        IF      TSK_NAMEPAR
        mov     ax,offset cdos_name
        push    ds
        push    ax
        ENDIF
        mov     ax,offset critical
        push    ds
        push    ax
        call    _create_flag
        IF      TSK_NAMEPAR
        add     sp,8
        ELSE
        add     sp,4
        ENDIF
;
;       Get the address of DOS's in_use-flag. This flag indicates that
;       DOS is already active. This might happen if there are other
;       background tasks, like popups or print spoolers, active in
;       parallel to CTask.
;       This is also the address of the critical error flag in DOS. Beginning
;       with DOS 3.10 the flag is located one byte before the in_use_flag.
;       With older DOS versions, we would have to search through DOS for the
;       address. This is omitted here, but you could include the code
;       for pre 3.1 versions from pages 378-379 of the MS-DOS Encyclopedia.
;
        mov     ah,get_in_use_flag
        int     21h
        mov     word ptr in_use,bx
        mov     word ptr in_use+2,es
        mov     word ptr in_error+2,es
;
        mov     ah,30h
        int     21h
        cmp     ah,3
        jb      not_dos3
        cmp     ah,0ah
        je      not_dos3        ; OS/2 compatibility box
        cmp     al,10
        jb      not_dos3
        inc     dos310
        dec     bx
        mov     word ptr in_error,bx
        jmp     short save_ints
;
not_dos3:

;
;       Save old interrupt vectors
;
save_ints:
        push    es
        xor     ax,ax
        mov     es,ax
;
;        assume  es:intseg
;
        mov     ax,es:[termoff]            ; DOS
        mov     savtermoff,ax
        mov     ax,es:[termseg]
        mov     savtermseg,ax
;
        mov     ax,es:[idosoff]            ; DOS
        mov     savdosoff,ax
        mov     ax,es:[idosseg]
        mov     savdosseg,ax
;
        mov     ax,es:[idleoff]            ; IDLE
        mov     savidleoff,ax
        mov     ax,es:[idleseg]
        mov     savidleseg,ax
;
        mov     ax,es:[csectoff]           ; Critical Section
        mov     savcsectoff,ax
        mov     ax,es:[csectseg]
        mov     savcsectseg,ax
;
        mov     ax,es:[absreadoff]         ; Absolute Disk read
        mov     savabsreadoff,ax
        mov     ax,es:[absreadseg]
        mov     savabsreadseg,ax
;
        mov     ax,es:[abswriteoff]        ; Absolute Disk write
        mov     savabswriteoff,ax
        mov     ax,es:[abswriteseg]
        mov     savabswriteseg,ax
;
        mov     ax,es:[keepoff]           ; Terminate Resident
        mov     savkeepoff,ax
        mov     ax,es:[keepseg]
        mov     savkeepseg,ax
;
;       Enter new Interrupt-Entries
;
        cli
        mov     es:[idosoff],offset dosentry         ; DOS-Entry
        mov     es:[idosseg],cs
        mov     es:[idleoff],offset idleentry        ; Idle-Entry
        mov     es:[idleseg],cs
        mov     es:[termoff],offset terminate_int    ; Terminate Process Entry
        mov     es:[termseg],cs
        mov     es:[csectoff],offset critsectint     ; Critical Section Entry
        mov     es:[csectseg],cs
        mov     es:[keepoff],offset keep_int         ; Keep Process Entry
        mov     es:[keepseg],cs
        mov     es:[absreadoff],offset absread_int   ; Absolute Disk Read Entry
        mov     es:[absreadseg],cs
        mov     es:[abswriteoff],offset abswrite_int ; Absolute Disk Write Entry
        mov     es:[abswriteseg],cs
        sti
        pop     es
;
        ret
;
;        assume  es:nothing
;
tsk_install_dos_        endp
;
;
;       void tsk_remove_dos (void)
;
;               Un-install DOS handler
;
tsk_remove_dos_ proc
;
;       Delete resources & flags
;
        mov     ax,offset upper_dos
        push    ds
        push    ax
        call    _delete_resource
        add     sp,4
;
        mov     ax,offset lower_dos
        push    ds
        push    ax
        call    _delete_resource
        add     sp,4
;
        mov     ax,offset critical
        push    ds
        push    ax
        call    _delete_flag
        add     sp,4
;
        push    es
        xor     ax,ax
        mov     es,ax
;
;        assume  es:intseg
;
;       Restore interrupt entries
;
        cli
        mov     ax,savtermoff
        mov     es:[termoff],ax
        mov     ax,savtermseg
        mov     es:[termseg],ax
;
        mov     ax,savdosoff
        mov     es:[idosoff],ax
        mov     ax,savdosseg
        mov     es:[idosseg],ax
;
        mov     ax,savidleoff
        mov     es:[idleoff],ax
        mov     ax,savidleseg
        mov     es:[idleseg],ax
;
        mov     ax,savcsectoff
        mov     es:[csectoff],ax
        mov     ax,savcsectseg
        mov     es:[csectseg],ax
;
        mov     ax,savabsreadoff
        mov     es:[absreadoff],ax
        mov     ax,savabsreadseg
        mov     es:[absreadseg],ax
;
        mov     ax,savabswriteoff
        mov     es:[abswriteoff],ax
        mov     ax,savabswriteseg
        mov     es:[abswriteseg],ax
;
        mov     ax,savkeepoff
        mov     es:[keepoff],ax
        mov     ax,savkeepseg
        mov     es:[keepseg],ax
;
        sti
;
        pop     es
        ret
;
;        assume  es:nothing
;
tsk_remove_dos_ endp
;
;
;---------------------------------------------------------------------------
;
;       Stack-Offsets relative to BP
;
caller_cs       =       4               ; Caller's CS
caller_flags    =       6               ; Caller's Flags
;
;
;---------------------------------------------------------------------------
;
;       INT 25: Absolute Disk Read
;
;       This interrupt is translated into INT 21, function C0.
;       This function is re-translated later after the necessary resources
;       have been requested.
;
absread_int:
        mov     ah,0c0h
        jmp     short absrw_int
;
;
;---------------------------------------------------------------------------
;
;       INT 26: Absolute Disk Write
;
;       This interrupt is translated into INT 21, function C1.
;       This function is re-translated later after the necessary resources
;       have been requested.
;
abswrite_int:
        mov     ah,0c1h
;
;       Interrupts 25 und 26 leave the flag-word on the stack.
;       Since flags are removed in normal processing, the flag-word
;       has to be duplicated on the stack here.
;
absrw_int:
        push    bp                      ; reserve space
        push    bp                      ; save BP
        mov     bp,sp
        sti
        push    ax
        mov     ax,4[bp]                ; Move return offset, segment down
        mov     2[bp],ax
        mov     ax,6[bp]
        mov     4[bp],ax
        mov     ax,8[bp]                ; duplicate flags
        mov     6[bp],ax
        pop     ax
        push    caller_flags[bp]
        jmp     short dosentry_2
;
;---------------------------------------------------------------------------
;
;       INT 27: Terminate But Stay Resident Interrupt
;
;       This interrupt is translated to INT 21, function 31.
;
keep_int:
        push    bp
        mov     bp,sp
        push    caller_flags[bp]        ; flags (bp-2)
        sti                             ; allow interrupts
;
        add     dx,0fh                  ; last addr + 0f to round
        sub     dx,caller_cs[bp]        ; minus CS (= PSP)
        mov     cl,4
        shr     dx,cl                   ; div 16 = paragraphs

        mov     ax,3100h                ; Keep process
        jmp     short dosentry_2
;
;---------------------------------------------------------------------------
;
;       INT 20: Terminate Program interrupt
;
;       This interrupt is translated to INT 21, function 0.
;
terminate_int:
        mov     ah,0
;
;       fall through
;
;---------------------------------------------------------------------------
;
;       INT 21: DOS-Interrupt
;
dosentry        proc
;
;       Save registers
;
        push    bp
        mov     bp,sp
        push    caller_flags[bp]        ; flags (bp-2)
;
dosentry_2:
        push    es
        push    ds
        push    dx
        push    cx
        push    bx
        push    ax
        push    si
        push    di
;
;       Here we check if the DOS critical region is active.
;       If yes, this means that some outside background process has
;       started DOS (most likely DOS PRINT).
;       To avoid busy waiting, we wait for the "critical" flag to be
;       cleared, if this is *not* the task that set the flag.
;       The task that set the critical task is *not* made waiting.
;
wait_crit_loop:
        cli
        cmp     cs:critsect_active,0
        je      no_crit
        mov     ax,word ptr cs:crit_task
        cmp     word ptr _tsk_current,ax
        jne     wait_crit
        mov     ax,word ptr cs:crit_task+2
        cmp     word ptr _tsk_current+2,ax
        je      no_crit
;
wait_crit:
        xor     ax,ax
        push    ax
        push    ax
        mov     ax,offset critical
        push    ds
        push    ax
        call    _wait_flag_clear
        add     sp,8
;
no_crit:
        mov     cs:ctask_active,1       ; mark that we are active
        sti                             ; Interrupts allowed now
;
        cld
        mov     bx,cs                 ; SEG dgroup   ;"++"
        mov     ds,bx
        mov     es,bx
;
        cmp     ah,4ch                  ; terminate?
        jne     dosent_x
        mov     ah,0                    ; translate to fn 0
dosent_x:
        cmp     ah,0ch
        jbe     lower_funcs
        jmp     upper_funcs
;
;       Functions 00-0C
;
lower_funcs:
;
;       first, request the "lower_dos" resource
;
        mov     bx,offset lower_dos
        xor     cx,cx
        push    cx              ; no timeout
        push    cx
        push    ds              ; resource address
        push    bx
        call    _request_resource
        add     sp,8
;
;       we have it, now let's get the upper_dos resource, too
;
        mov     bx,offset upper_dos
        xor     cx,cx
        push    cx              ; no timeout
        push    cx
        push    ds              ; resource address
        push    bx
        call    _request_resource
        add     sp,8
;
;       both resources gained, now we may execute the function if dos is free
;
        pop     di
        pop     si
        pop     ax
        pop     bx
        pop     cx
        pop     dx
;
        call    wait_dos_free
;
        or      ah,ah                   ; terminate ?
        je      fg_terminate            ; special treatment required
        cmp     ah,4ch
        je      fg_terminate            ; special treatment required
        pop     ds
        pop     es
        popf
        pop     bp
;
        dos                             ; execute function
;
;       Now we have to release the resources.
;
        pushf                           ; save registers again
        sti
        push    es
        push    ds
        push    dx
        push    cx
        push    bx
        push    ax
        push    si
        push    di
;
        mov     bx,cs               ;SEG dgroup   ;"++"
        mov     ds,bx
        mov     es,bx
;
        mov     bx,offset upper_dos
        push    ds              ; resource address
        push    bx
        call    _release_resource
        add     sp,4
;
        mov     bx,offset lower_dos
        push    ds              ; resource address
        push    bx
        call    _release_resource
        add     sp,4
;
;       If both resources are free now, clear the ctask_active flag to
;       allow other background processes to gain access to DOS.
;
        cli
        mov     ax,upper_dos.rstate
        or      ax,ax
        jz      no_relc
        cmp     ax,lower_dos.rstate
        jne     no_relc
        mov     cs:ctask_active,0
no_relc:
;
;       All done, restore registers and return.
;
        pop     di
        pop     si
        pop     ax
        pop     bx
        pop     cx
        pop     dx
        pop     ds
        pop     es
        popf
;
        ret     2                       ; don't restore flags
;
;
;       The terminate request requires special treatment.
;       Since terminating the program without first un-installing
;       would crash the system, we do this here, not without telling
;       the user something went very wrong.
;
fg_terminate:
        cli
        call    _remove_tasker
        mov     dx,offset term_err_msg
        mov     ah,9
        int     21h
        mov     ax,4cffh
        int     21h
;
;--------------------------------------------------------------------------
;
;       Functions 0D and above
;
upper_funcs:
        mov     bx,offset upper_dos
        xor     cx,cx
        push    cx              ; no timeout
        push    cx
        push    ds              ; resource address
        push    bx
        call    _request_resource
        add     sp,8
;
;       resource gained, now we may execute the function if dos is free
;
        pop     di
        pop     si
        pop     ax
        pop     bx
        pop     cx
        pop     dx
;
        call    wait_dos_free
;
        pop     ds
        pop     es
;
;
;       Filter pseudo-functions C0/C1 (Absolute Read/Write)
;
        cmp     ah,0c0h
        jne     uf_exec1
        popf
        pop     bp
        call    exec_absread
        jmp     short uf_complete
uf_exec1:
        cmp     ah,0c1h
        jne     uf_exec2
        popf
        pop     bp
        call    exec_abswrite
        jmp     short uf_complete
;
uf_exec2:
        popf
        pop     bp
        dos                             ; execute function
;
;       Now we have to release the resources.
;
uf_complete:
        pushf                           ; save registers again
        sti
        push    es
        push    ds
        push    dx
        push    cx
        push    bx
        push    ax
        push    si
        push    di
;
        mov     bx,cs                    ;SEG dgroup ;"++"
        mov     ds,bx
        mov     es,bx
;
        mov     bx,offset upper_dos
        push    ds              ; resource address
        push    bx
        call    _release_resource
        add     sp,4
;
;       If both resources are free now, clear the ctask_active flag to
;       allow other background processes to gain access to DOS.
;
        cli
        mov     ax,upper_dos.rstate
        or      ax,ax
        jz      no_relc1
        cmp     ax,lower_dos.rstate
        jne     no_relc1
        mov     cs:ctask_active,0
no_relc1:
;
;       All done, restore registers and return.
;
        pop     di
        pop     si
        pop     ax
        pop     bx
        pop     cx
        pop     dx
        pop     ds
        pop     es
        popf
;
        ret     2                       ; don't restore flags
;
;
dosentry        endp
;
;--------------------------------------------------------------------------
;
;       Absolute Read/Absolute Write.
;
exec_absread    proc    near

        pushf
        call    cs:savabsread
        inc     sp                      ; Remove flags (not using ADD,
        inc     sp                      ; this would clobber Carry)
        ret

exec_absread    endp
;
exec_abswrite   proc    near

        pushf
        call    cs:savabswrite
        inc     sp                      ; Remove flags (not using ADD,
        inc     sp                      ; this would clobber Carry)
        ret

exec_abswrite   endp
;
;----------------------------------------------------------------------------
;
wait_dos_free   proc    near
;
        push    es
        push    bx
in_use_loop:
        cmp     idle_active,0           ; idle interrupt active?
        jne     dos_free                ; then don't check for flag
;
        les     bx,in_use
        cmp     byte ptr es:[bx],0
        jne     is_in_use
        cmp     dos310,0
        je      dos_free
        les     bx,in_error
        cmp     byte ptr es:[bx],0
        je      dos_free
is_in_use:
        pushf
        call    scheduler
        jmp     in_use_loop
;
dos_free:
        pop     bx
        pop     es
        ret
;
wait_dos_free   endp
;
;----------------------------------------------------------------------------
;
;       INT 28: DOS Idle Interrupt
;
idleentry       proc
;
        push    ds
        push    es
        push    ax
;
        mov     ax,cs                    ;SEG dgroup  ;"++"
        mov     ds,ax
        mov     es,ax
;
;       Check if someone is waiting for upper_dos. If not, we can return
;       immediately.
;
        mov     ax,word ptr upper_dos.rwaiting
        or      ax,word ptr upper_dos.rwaiting+2
        jz      idle_exit
;
;       Also make sure this is not a second invocation of INT 28.
;       Normally, this should never happen, but better safe than sorry.
;
        cmp     idle_active,0
        jne     idle_exit
        inc     idle_active
;
;       someone is waiting, let's please him by releasing the resource
;
        mov     idle_ss,ss              ; Switch to local stack
        mov     idle_sp,sp
        mov     ax,ds
        mov     ss,ax
        mov     sp,offset local_stack
;
        sti                             ; Interrupts allowed now
;
        push    bx                      ; save remaining regs
        push    cx
        push    dx
;
;       temporarily increase priority
;
        les     bx,_tsk_current
        push    es:prior[bx]
        mov     es:prior[bx],0ffffh
        push    bx
        push    es
;
;       release resource & request it again
;
        mov     ax,ds
        mov     es,ax
        mov     bx,offset upper_dos
        push    ds
        push    bx
        call    _release_resource
        add     sp,4
;
        mov     bx,offset upper_dos
        xor     cx,cx
        push    cx
        push    cx
        push    ds
        push    bx
        call    _request_resource
        add     sp,8
;
;       ready, restore priority, stack & regs
;
        cli
        pop     es
        pop     bx
        pop     es:prior[bx]
;
        pop     dx
        pop     cx
        pop     bx
;
        mov     ss,idle_ss              ; restore stack
        mov     sp,idle_sp
;
        mov     idle_active,0
;
idle_exit:
        pop     ax
        pop     es
        pop     ds
;
        jmp     cs:savidle              ; chain to original interrupt
;
idleentry       endp
;
;---------------------------------------------------------------------------
;
;       INT 2A: DOS Critical Section Interrupt.
;
;       Not documented.
;       Is used by DOS PRINT to mark Critical Regions.
;       Usage by PRINT:
;               AX = 8700 - Begin Critical Region
;                               Returns:
;                               Carry set if already active.
;               AX = 8701 - End Critical Region
;
;       Both these functions are handled here.
;
;       Other usage in DOS, function unknown:
;               AH = 82 (AL undefined)
;                       seems to be called on DOS-Functions > 0C
;               AH = 84 (AL undefined)
;                       seems to be called when DOS is idle
;
;       These functions are currently ignored.
;
critsectint     proc

        cmp     ax,8700h        ; Enter critical region
        jne     csi1
        cmp     cs:critsect_active,0
        stc
        jnz     critsect_end
        cmp     cs:ctask_active,0
        stc
        jnz     critsect_end
;
        inc     cs:critsect_active
        push    ax
        push    bx
        push    cx
        push    dx
        push    ds
        push    es
        mov     ax,cs                       ;seg dgroup   "++"
        mov     ds,ax
        mov     es,ax
        mov     ax,word ptr _tsk_current
        mov     word ptr cs:crit_task,ax
        mov     ax,word ptr _tsk_current+2
        mov     word ptr cs:crit_task+2,ax
        mov     ax,offset critical
        push    ds
        push    ax
        call    _set_flag
        add     sp,4
        pop     es
        pop     ds
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        clc
critsect_end:
        ret     2
;
csi1:
        cmp     ax,8701h        ; Leave critical region
        jne     csi2
        mov     cs:critsect_active,0
        push    ax
        push    bx
        push    cx
        push    dx
        push    ds
        push    es
        mov     ax,cs                ;seg dgroup  ;"++"
        mov     ds,ax
        mov     es,ax
        mov     ax,offset critical
        push    ds
        push    ax
        call    _clear_flag
        add     sp,4
        pop     es
        pop     ds
        pop     dx
        pop     cx
        pop     bx
        pop     ax
csi2:
        iretd
;
critsectint     endp
;
;
;---------------------------------------------------------------------------
;
        end