home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Frostbyte's 1980s DOS Shareware Collection
/
floppyshareware.zip
/
floppyshareware
/
DOOG
/
CTASK.ZIP
/
TSKDOS.ASM
< prev
next >
Wrap
Assembly Source File
|
1989-12-22
|
32KB
|
1,576 lines
;
; --- Version 2.0 89-12-13 17:57 ---
;
; CTask - DOS access module.
;
; Public Domain Software written by
; Thomas Wagner
; Patschkauer Weg 31
; D-1000 Berlin 33
; West Germany
;
;
; 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 CTask 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.
;
; NOTE: The method for checking the status of DOS is described in-depth
; in the book "The MS-DOS Encyclopedia" from Microsoft Press, in
; the chapter on TSR programming. The logic in this module was
; developed without the help of this book, so if you compare
; the code here with the routines in the Encyclopedia, you may
; notice that not all re-entry conditions are checked here.
; According to my experience with debugging TSR's and CTask, the
; logic should be sufficient for all but the most obscure TSR's.
; If you want to be completely on the safe side, you might consider
; adding the more thorough checks listed in the Encyclopedia.
;
;
name tskdos
;
.model large,c
;
public tsk_install_dos
public tsk_remove_dos
public tsk_resident
public tsk_emergency_exit
public tsk_free_mem
;
include tsk.mac
;
extrn create_resource: far
extrn delete_resource: far
extrn request_resource: far
extrn release_resource: far
extrn create_flag: far
extrn delete_flag: far
extrn set_flag: far
extrn clear_flag: far
extrn wait_flag_clear: far
extrn tsk_scheduler: far
;
extrn tsk_dgroup: word
;
;
CSECT = 2ah ; Critical Section Interrupt
get_in_use_flag = 34h ; DOS-function to get in_use_flag address
get_invars = 5d06h ; DOS-function to get DOS-variables area
;
psp_offset = 10h ; Offset of current PSP in DOS save area
;
; Structure of the start of a PSP, including undocumented fields
;
psp_record struc
;
dw ? ; INT 20
dw ? ; alloc block end
db ? ; reserved
db 5 dup(?) ; system call
psp_exit_addr dd ? ; exit routine address
psp_ctlc_addr dd ? ; control C routine address
psp_cerr_addr dd ? ; critical error routine address
parent_psp dw ? ; PSP of parent process
db 20 dup(?) ; files table
psp_envseg dw ? ; environment segment
psp_ustack dd ? ; ss/sp of caller
dw ? ; file table length
dd ? ; file table pointer
dd ? ; pointer to nested PSP (?)
;
psp_record ends
;
;
; Structure of a DOS MCB (Memory Control Block)
;
mcbstruc struc
;
mcb_id db ?
mcb_owner dw ?
mcb_len dw ?
;
mcbstruc ends
;
;
intseg segment at 0
org 10h*4
vidoff dw ? ; video interrupt
vidseg dw ?
org 13h*4
diskoff dw ? ; disk i/o interrupt
diskseg dw ?
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
;
.tsk_data
;
extrn tsk_glob_rec: word ; glob_rec
extrn tsk_global: dword
extrn tsk_instflags: word
extrn tsk_version: byte
;
term_err_msg db 0dh,0ah,"Program terminated - CTask uninstalled"
db 0dh,0ah,'$'
;
group_term_msg db 0dh,0ah,"Program terminated - Task Group removed"
db 0dh,0ah,'$'
;
gcb_mixup_err db 0dh,0ah,"Group chain damaged - System halted"
db 07h,'$'
;
idle_active db 0 ; Idle-Interrupt active
dos310 db 0 ; DOS version >= 3.10
;
IF TSK_NAMEPAR
udos_name db "DOSUPPER",0
ldos_name db "DOSLOWER",0
cdos_name db "DOSCRIT",0
hdio_name db "HARDDISK",0
fdio_name db "FLEXDISK",0
vid_name db "VIDEO",0
ENDIF
;
;
in_use dd ? ; Adress of DOS in-use-flag
in_error dd ? ; Adress of DOS error-mode-flag
;
lower_dos resource <>
upper_dos resource <>
hdisk_io resource <>
fdisk_io resource <>
video resource <>
critical flag <>
;
;
.tsk_edata
.tsk_code
;
extrn tsk_switch_stack: near
extrn tsk_old_stack: near
extrn tsk_remove_group: near
;
;---------------------------------------------------------------------------
;
; Original Interrupt-Entries
;
savvid label dword ; original Video entry
savvidoff dw ?
savvidseg dw ?
;
savdisk label dword ; original Disk I/O entry
savdiskoff dw ?
savdiskseg dw ?
;
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_version dw ? ; DOS version
;
temp_1 dw ?
temp_2 dw ?
temp_3 dw ?
;
xsp_sp dw ?
xsp_ss dw ?
xsp_ip dw ?
xsp_cs dw ?
;
;
calldos macro
pushf
cli
call cs:savdos
endm
;
;---------------------------------------------------------------------------
;
tsk_resident proc near uses si di ds
;
mov ds,cs:tsk_dgroup
;
mov dx,offset tsk_version
mov bx,1234h
mov ax,3000h
int 21h
mov di,bx
mov es,cx
mov si,offset tsk_version
mov cx,8
repe cmpsb
jcxz tr_is_res
xor bx,bx
mov es,bx
tr_is_res:
mov ax,bx
mov dx,es
ret
;
tsk_resident endp
;
;---------------------------------------------------------------------------
;
; void tsk_install_dos (void)
;
; Install DOS handler
;
tsk_install_dos proc near uses ds
;
; create needed resources & flags
;
mov ds,cs:tsk_dgroup
;
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
;
test tsk_instflags,IFL_DISK
jz inst_nodsk1
IF TSK_NAMEPAR
mov ax,offset hdio_name
push ds
push ax
ENDIF
mov ax,offset hdisk_io
push ds
push ax
call create_resource
IF TSK_NAMEPAR
add sp,8
ELSE
add sp,4
ENDIF
;
IF TSK_NAMEPAR
mov ax,offset fdio_name
push ds
push ax
ENDIF
mov ax,offset fdisk_io
push ds
push ax
call create_resource
IF TSK_NAMEPAR
add sp,8
ELSE
add sp,4
ENDIF
;
inst_nodsk1:
test tsk_instflags,IFL_VIDEO
jz inst_novid1
IF TSK_NAMEPAR
mov ax,offset vid_name
push ds
push ax
ENDIF
mov ax,offset video
push ds
push ax
call create_resource
IF TSK_NAMEPAR
add sp,8
ELSE
add sp,4
ENDIF
;
inst_novid1:
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 address and length of the DOS internal variable area.
; This call is not documented, and not available in versions
; prior to 3.1. It returns the address of the area in DS:SI,
; and the length of the area that must be swapped between calls
; from different groups in DX.
; Additionally, the total length of the area, including
; the DOS-stacks, minus the length in DX, is returned in CX.
;
mov tsk_glob_rec.l_swap,0
test tsk_instflags,IFL_NODOSVARS
jnz no_invars
;
push ds
mov ax,get_invars
int 21h
mov ax,ds
pop ds
mov word ptr tsk_glob_rec.dos_vars,si
mov word ptr tsk_glob_rec.dos_vars+2,ax
mov tsk_glob_rec.l_swap,dx
;
; 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.
;
no_invars:
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
;
push bx
mov ah,30h
int 21h
pop bx
mov cs:dos_version,ax
cmp al,3
jb not_dos3
cmp al,0ah
je not_dos3 ; OS/2 compatibility box
cmp al,3
jne is_dos3 ; DOS 4.x
cmp ah,10
jb not_dos3
is_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
;
test tsk_instflags,IFL_VIDEO
jz inst_novid2
mov ax,vidoff ; video
mov savvidoff,ax
mov ax,vidseg
mov savvidseg,ax
;
inst_novid2:
test tsk_instflags,IFL_DISK
jz inst_nodsk2
mov ax,diskoff ; Disk I/O
mov savdiskoff,ax
mov ax,diskseg
mov savdiskseg,ax
;
inst_nodsk2:
mov ax,termoff ; DOS
mov savtermoff,ax
mov ax,termseg
mov savtermseg,ax
;
mov ax,idosoff ; DOS
mov savdosoff,ax
mov ax,idosseg
mov savdosseg,ax
;
mov ax,idleoff ; IDLE
mov savidleoff,ax
mov ax,idleseg
mov savidleseg,ax
;
mov ax,csectoff ; Critical Section
mov savcsectoff,ax
mov ax,csectseg
mov savcsectseg,ax
;
mov ax,absreadoff ; Absolute Disk read
mov savabsreadoff,ax
mov ax,absreadseg
mov savabsreadseg,ax
;
mov ax,abswriteoff ; Absolute Disk write
mov savabswriteoff,ax
mov ax,abswriteseg
mov savabswriteseg,ax
;
mov ax,keepoff ; Terminate Resident
mov savkeepoff,ax
mov ax,keepseg
mov savkeepseg,ax
;
; Enter new Interrupt-Entries
;
cli
test tsk_instflags,IFL_VIDEO
jz inst_novid3
mov vidoff,offset videntry ; Video Entry
mov vidseg,cs
;
inst_novid3:
test tsk_instflags,IFL_DISK
jz inst_nodsk3
mov diskoff,offset diskentry ; Disk I/O Entry
mov diskseg,cs
;
inst_nodsk3:
mov idosoff,offset dosentry ; DOS-Entry
mov idosseg,cs
mov idleoff,offset idleentry ; Idle-Entry
mov idleseg,cs
mov termoff,offset terminate_int ; Terminate Process Entry
mov termseg,cs
mov csectoff,offset critsectint ; Critical Section Entry
mov csectseg,cs
mov keepoff,offset keep_int ; Keep Process Entry
mov keepseg,cs
mov absreadoff,offset absread_int ; Absolute Disk Read Entry
mov absreadseg,cs
mov abswriteoff,offset abswrite_int ; Absolute Disk Write Entry
mov abswriteseg,cs
sti
;
assume es:nothing
;
pop es
ret
;
;
tsk_install_dos endp
;
;
; void tsk_remove_dos (void)
;
; Un-install DOS handler
;
tsk_remove_dos proc near uses ds
;
mov ds,cs:tsk_dgroup
;
; 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
;
test tsk_instflags,IFL_DISK
jz rem_nodsk1
mov ax,offset hdisk_io
push ds
push ax
call delete_resource
add sp,4
;
mov ax,offset fdisk_io
push ds
push ax
call delete_resource
add sp,4
;
rem_nodsk1:
test tsk_instflags,IFL_VIDEO
jz rem_novid1
mov ax,offset video
push ds
push ax
call delete_resource
add sp,4
;
rem_novid1:
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
test tsk_instflags,IFL_VIDEO
jz rem_novid2
mov ax,savvidoff
mov vidoff,ax
mov ax,savvidseg
mov vidseg,ax
;
rem_novid2:
test tsk_instflags,IFL_DISK
jz rem_nodsk2
mov ax,savdiskoff
mov diskoff,ax
mov ax,savdiskseg
mov diskseg,ax
;
rem_nodsk2:
mov ax,savtermoff
mov termoff,ax
mov ax,savtermseg
mov termseg,ax
;
mov ax,savdosoff
mov idosoff,ax
mov ax,savdosseg
mov idosseg,ax
;
mov ax,savidleoff
mov idleoff,ax
mov ax,savidleseg
mov idleseg,ax
;
mov ax,savcsectoff
mov csectoff,ax
mov ax,savcsectseg
mov csectseg,ax
;
mov ax,savabsreadoff
mov absreadoff,ax
mov ax,savabsreadseg
mov absreadseg,ax
;
mov ax,savabswriteoff
mov abswriteoff,ax
mov ax,savabswriteseg
mov abswriteseg,ax
;
mov ax,savkeepoff
mov keepoff,ax
mov ax,savkeepseg
mov keepseg,ax
;
sti
;
pop es
ret
;
assume es:nothing
;
tsk_remove_dos endp
;
;
;---------------------------------------------------------------------------
;---------------------------------------------------------------------------
;
;
; INT 10: Video BIOS interrupt
;
videntry proc far
;
call tsk_switch_stack
xor ax,ax
push ax
push ax
push ds
mov ax,offset video
push ax
call request_resource
add sp,8
push caller_flags[bp]
popf
call tsk_old_stack
pushf
cli
call savvid
call tsk_switch_stack
mov ax,entry_flags[bp]
mov caller_flags[bp],ax
push ds
mov ax,offset video
push ax
call release_resource
add sp,4
iret
;
videntry endp
;
;---------------------------------------------------------------------------
;
;
; INT 13: Disk I/O BIOS interrupt
;
diskentry proc far
;
call tsk_switch_stack
xor ax,ax
push ax
push ax
push ds
test dl,80h ; fixed disk ?
jz disk_floppy ; jump if not
mov ax,offset hdisk_io
push ax
call request_resource
add sp,8
push caller_flags[bp]
popf
call tsk_old_stack
pushf
cli
call savdisk
call tsk_switch_stack
mov ax,entry_flags[bp]
mov caller_flags[bp],ax
push ds
mov ax,offset hdisk_io
push ax
call release_resource
add sp,4
iret
;
disk_floppy:
mov ax,offset fdisk_io
push ax
call request_resource
add sp,8
push caller_flags[bp]
popf
call tsk_old_stack
pushf
cli
call savdisk
call tsk_switch_stack
mov ax,entry_flags[bp]
mov caller_flags[bp],ax
push ds
mov ax,offset fdisk_io
push ax
call release_resource
add sp,4
iret
;
diskentry endp
;
;---------------------------------------------------------------------------
;
; Stack-Offsets relative to BP
;
d_func = -2
;
;---------------------------------------------------------------------------
;
; INT 25: Absolute Disk Read
;
; This interrupt is channeled through the normal DOS-function
; processing, with function = 0x25 and special flag set.
; It is re-translated later after the necessary resources
; have been requested.
; 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.
;
absread_int:
sti
push bp ; reserve space
push bp ; save BP
mov bp,sp
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
pop bp
call tsk_switch_stack
mov ax,2501h
mov cx,ax
jmp short dosentry_2
;
;
;---------------------------------------------------------------------------
;
; INT 26: Absolute Disk Write
;
; This interrupt is channeled through the normal DOS-function
; processing, with function = 0x26 and special flag set.
; It is re-translated later after the necessary resources
; have been requested.
; 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.
;
abswrite_int:
sti
push bp ; reserve space
push bp ; save BP
mov bp,sp
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
pop bp
call tsk_switch_stack
mov ax,2601h
mov cx,ax
jmp short dosentry_2
;
;---------------------------------------------------------------------------
;
; INT 27: Terminate But Stay Resident Interrupt
;
; This interrupt is translated to INT 21, function 31.
;
keep_int:
call tsk_switch_stack
;
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
mov save_ax[bp],ax
mov cx,ax
jmp short dosentry_2
;
;---------------------------------------------------------------------------
;
; INT 20: Terminate Program interrupt
;
; This interrupt is translated to INT 21, function 4c.
;
terminate_int:
call tsk_switch_stack
mov ax,4c00h
mov save_ax[bp],ax
mov cx,ax
jmp short dosentry_2
;
;---------------------------------------------------------------------------
;
; INT 21: DOS-Interrupt
;
dosentry proc far
;
call tsk_switch_stack
xor cl,cl
mov ch,ah
;
dosentry_2:
push cx ; BP-2: func in CH, special flag in CL
;
; Check if this is a special 'get dos version' call.
;
or cl,cl
jnz dosent_x ; no function check if special
cmp ax,3000h
jne no_spdos
cmp bx,1234h
jne no_spdos
push ds
mov si,dx
mov ds,save_ds[bp]
mov di,offset tsk_glob_rec
mov cx,8
repe cmpsb
pop ds
jcxz is_spdos
jmp short no_spdos
;
; Special version call returns global variable block address
;
is_spdos:
add sp,2
call tsk_old_stack
mov ax,cs:dos_version
mov bx,offset tsk_glob_rec
mov cx,cs:tsk_dgroup
iret
;
; 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 flag is *not* made waiting.
;
no_spdos:
mov ax,d_func[bp]
or ah,ah ; terminate?
jne dosent_x
mov save_ax[bp],4c00h ; translate to fn 4c, retcode 0
mov d_func[bp],4c00h
dosent_x:
;
cmp cs:critsect_active,0
je no_crit
mov ax,word ptr cs:crit_task
cmp word ptr tsk_glob_rec.current_task,ax
jne wait_crit
mov ax,word ptr cs:crit_task+2
cmp word ptr tsk_glob_rec.current_task+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
;
;
; Now let's check if the current task owns one of the dos-resources.
; This may happen on
; - terminate (TSR or normal)
; - spawn process
; - critical error/break
; - redirection of INT 28 to out-of-CTask routine
; If the task owns one, and the dos-flags are clear,
; the resource is released.
; If the dos-flags are nonzero, the resource is not
; released, but the task is allowed access to DOS regardless of
; resource and flag states.
; DL is used as an "emergency" marker. If nonzero, no resources
; are to be requested, and the in-dos flag is not checked.
;
xor dl,dl
les bx,tsk_glob_rec.current_task
cli
cmp es:t_indos[bx],0
je no_reenter
;
les bx,in_error
mov dl,es:byte ptr [bx]
les bx,in_use
or dl,es:byte ptr [bx]
jnz no_reenter
;
les bx,tsk_glob_rec.current_task
and es:t_indos[bx],NOT SPAWNING
jz no_reenter
;
test es:t_indos[bx],OWN_UPPER
jz rel_lower
xor es:t_indos[bx],OWN_UPPER
mov bx,offset upper_dos
push ds ; resource address
push bx
call release_resource
add sp,4
xor dl,dl
les bx,tsk_glob_rec.current_task
rel_lower:
test es:t_indos[bx],OWN_LOWER
jz no_reenter
xor es:t_indos[bx],OWN_LOWER
mov bx,offset lower_dos
push ds ; resource address
push bx
call release_resource
add sp,4
xor dl,dl
;
no_reenter:
sti
mov ax,d_func[bp]
cmp ah,0ch
jbe lower_funcs
jmp upper_funcs
;
;
; Functions 00-0C
;
lower_funcs:
;
; first, request the "lower_dos" resource
;
or dl,dl
jnz lower_emergency
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
;
les bx,tsk_glob_rec.current_task
mov es:t_indos[bx],OWN_UPPER or OWN_LOWER
;
; both resources gained, now we may execute the function if dos is free
;
call wait_dos_free
;
lower_emergency:
add sp,2
push caller_flags[bp]
popf
call tsk_old_stack
;
calldos ; execute function
;
; Now we have to release the resources.
;
call tsk_switch_stack
;
les bx,in_error
cmp es:byte ptr [bx],0
jne no_relc
;
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
;
les bx,tsk_glob_rec.current_task
mov es:t_indos[bx],0
;
; If both resources are free now, clear the ctask_active flag to
; allow other background processes to gain access to DOS.
;
cli
cmp upper_dos.rcount,0
jne no_relc
cmp lower_dos.rcount,0
jne no_relc
mov cs:ctask_active,0
no_relc:
;
; All done, restore registers and return.
;
mov ax,entry_flags[bp]
mov caller_flags[bp],ax
iret
;
;--------------------------------------------------------------------------
;
; Functions 0D and above
;
upper_funcs:
or dl,dl
jnz upper_emergency
;
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
;
les bx,tsk_glob_rec.current_task
mov es:t_indos[bx],OWN_UPPER
;
; resource gained, now we may execute the function if dos is free
;
call wait_dos_free
;
upper_emergency:
cmp byte ptr d_func[bp],0
jne no_term
mov ax,save_ax[bp]
cmp ah,31h ; terminate resident?
jne ckfunc1
jmp term_resident
ckfunc1:
cmp ax,4b00h ; spawn new process?
jne ckfunc2
jmp terminate
ckfunc2:
cmp ah,4ch ; terminate program?
jne no_term
jmp terminate
;
no_term:
;
; Filter special-functions 25/26 (Absolute Read/Write)
;
pop ax
cmp ax,2501h
jne uf_exec1
push caller_flags[bp]
popf
call tsk_old_stack
pushf
cli
call cs:savabsread
pop cs:temp_1 ; remove flags
jmp short uf_complete
;
uf_exec1:
cmp ax,2601h
jne uf_exec2
push caller_flags[bp]
popf
call tsk_old_stack
pushf
cli
call cs:savabswrite
pop cs:temp_1 ; remove flags
jmp short uf_complete
;
uf_exec2:
push caller_flags[bp]
popf
call tsk_old_stack
;
calldos ; execute function
;
; Now we have to release the resources.
;
uf_complete:
call tsk_switch_stack
mov ax,entry_flags[bp]
mov caller_flags[bp],ax
;
; if in_error or in_use is set, we do not release the resources.
;
uf_relres:
les bx,in_error
cmp es:byte ptr [bx],0
jne no_relc1
les bx,in_use
cmp es:byte ptr [bx],0
jne no_relc1
;
mov bx,offset upper_dos
push ds ; resource address
push bx
call release_resource
add sp,4
;
les bx,tsk_glob_rec.current_task
mov es:t_indos[bx],0
;
; If both resources are free now, clear the ctask_active flag to
; allow other background processes to gain access to DOS.
;
cli
cmp upper_dos.rcount,0
jne no_relc1
cmp lower_dos.rcount,0
jne no_relc1
mov cs:ctask_active,0
;
; All done, restore registers and return.
;
no_relc1:
iret
;
;--------------------------------------------------------------------------
;
; Terminate, TSR and Spawn calls go directly to DOS.
; TSR has to reset the exit address if it's the PSP of the
; current group, so the group is not deleted.
;
term_resident:
les bx,tsk_glob_rec.dos_vars
mov ax,es:[bx+psp_offset] ; current PSP
les bx,tsk_glob_rec.current_task
les bx,es:tgroup[bx]
cmp es:gcreate_psp[bx],ax
jne terminate
push ds
mov ds,ax
mov ax,word ptr es:grp_exit_addr[bx]
mov word ptr ds:psp_exit_addr,ax
mov ax,word ptr es:grp_exit_addr+2[bx]
mov word ptr ds:psp_exit_addr+2,ax
pop ds
;
terminate:
add sp,2
push caller_flags[bp]
popf
call tsk_old_stack
cli
jmp cs:savdos
;
dosentry endp
;
;--------------------------------------------------------------------------
;
wait_dos_free proc near
;
in_use_loop:
cmp dos310,0 ; dos version < 3.10?
je no_in_error ; then we don't check for in_error
les bx,in_error
cmp byte ptr es:[bx],0
jne is_in_use
no_in_error:
cmp idle_active,0 ; idle interrupt active?
je ck_inuse ; check for flag if no
ret ; else return immediately
;
ck_inuse:
les bx,in_use
cmp byte ptr es:[bx],0
jne is_in_use
ret
;
is_in_use:
pushf
call tsk_scheduler
jmp in_use_loop
;
wait_dos_free endp
;
;----------------------------------------------------------------------------
;
; INT 28: DOS Idle Interrupt
;
idleentry proc far
;
call tsk_switch_stack
;
; Check if someone is waiting for upper_dos. If not, we can return
; immediately.
;
les bx,upper_dos.rwaiting.q_first
cmp es:q_kind[bx],0
je 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
;
; temporarily increase priority
;
les bx,tsk_glob_rec.current_task
push es:cqueue.q_el.q_prior[bx]
push es:cqueue.q_el.q_ini_prior[bx]
mov es:cqueue.q_el.q_prior[bx],0ffffh
mov es:cqueue.q_el.q_ini_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
;
cli
pop es
pop bx
pop es:cqueue.q_el.q_ini_prior[bx]
pop es:cqueue.q_el.q_prior[bx]
;
mov idle_active,0
;
idle_exit:
push caller_flags[bp]
popf
call tsk_old_stack
cli
;
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 far
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 ds,cs:tsk_dgroup
les bx,tsk_glob_rec.current_task
mov word ptr cs:crit_task,bx
mov word ptr cs:crit_task+2,es
; or es:flags[bx],F_CRIT ;??
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 ds,cs:tsk_dgroup
les bx,tsk_glob_rec.current_task
; and es:flags[bx],NOT F_CRIT
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:
iret
;
critsectint endp
;
;---------------------------------------------------------------------------
;---------------------------------------------------------------------------
;
; tsk_emergency_exit is entered by DOS when a task group exits
; without releasing the current group.
; Registers are set up, remove_group is called, and the program
; is terminated by jumping to the terminate_address.
;
tsk_emergency_exit proc far
;
pushf
sub sp,4 ; make room for return addr
push bp
mov bp,sp
push ax
push bx
push cx
push dx
push si
push di
push ds
push es
mov ds,cs:tsk_dgroup
;
les bx,tsk_global
les bx,es:current_task[bx]
les bx,es:tgroup[bx]
mov ax,word ptr es:grp_exit_addr[bx]
mov 2[bp],ax
mov ax,word ptr es:grp_exit_addr+2[bx]
mov 4[bp],ax
;
xor ax,ax
push ax
push es
push bx
call tsk_remove_group
add sp,6
;
mov dx,offset group_term_msg
cmp ax,0
je emergency_end
jb pg_fatal
mov dx,offset term_err_msg
;
emergency_end:
;
mov ah,9
int 21h
;
pop es
pop ds
pop di
pop si
pop dx
pop cx
pop bx
pop ax
pop bp
iret
;
pg_fatal:
mov si,offset gcb_mixup_err
jmp short fatal_error
;
tsk_emergency_exit endp
;
;--------------------------------------------------------------------------
;
; fatal_error can be called if the system can't continue for
; some reason. It issues an error message and halts the system.
; DS:SI must point to the '$'-terminated error message.
;
fatal_error:
IF IBM
xor ax,ax
int 10h ; re-init display
fatal_loop:
lodsb
cmp al,'$'
je fatal_end
mov bx,7
mov ah,14
int 10h
jmp fatal_loop
ELSE
mov dx,si
mov ah,9
calldos
ENDIF
fatal_end:
sti
jmp fatal_end
;
;---------------------------------------------------------------
;
tsk_free_mem proc near owner: word
;
mov ah,51h
int 21h
push bx ; save current PSP
mov bx,owner
mov ah,50h
int 21h ; set new PSP
;
free_beg:
mov ah,52h
int 21h ; get DOS invars
mov dx,es:[bx-2] ; start of MCB chain
mov bx,owner
;
free_loop:
mov es,dx
cmp es:[mcb_owner],bx
jne free_next
;
; If we have released a block, we restart at the beginning of the
; MCB chain, since DOS might merge blocks, thereby invalidating
; our pointers. In the current versions of DOS, this seems to be
; an unneccessary precaution, but you never know...
;
inc dx
mov es,dx
mov ah,49h ; free memory block
int 21h
jmp free_beg
;
free_next:
cmp es:[mcb_id],'Z' ; end of chain ?
je free_end ; then exit
add dx,es:[mcb_len]
inc dx ; point to next
jmp free_loop
;
free_end:
pop bx
mov ah,50h
int 21h ; restore PSP
ret
;
tsk_free_mem endp
;
.tsk_ecode
end