📄 tskdos.asm
字号:
uf_nobsychk:
les bx,in_error
cmp es:byte ptr [bx],0
jnz no_relc_uf ; don't release if in error
;
les bx,tsk_glob_rec.current_task
cli
mov es:t_indos[bx],0
mov word ptr es:sched_ent_func[bx],0
mov word ptr es:sched_ent_func+2[bx],0
sti
;
callp release_resource,<<ds,#upper_dos>>
;
; All done, restore registers and return.
;
no_relc_uf:
cli
mov ax,entry_flags[bp]
mov caller_flags[bp],ax
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
;
;--------------------------------------------------------------------------
;
; 'dosbusy' checks the DOS busy flags.
;
; Entry: -
; Exit: DL is nonzero if DOS is busy.
;
@dosbusy proc near
;
push bx
push es
les bx,in_error
mov dl,es:byte ptr [bx]
les bx,tsk_glob_rec.dos_in_use
or dl,es:byte ptr [bx]
pop es
pop bx
ret
;
@dosbusy endp
;
;--------------------------------------------------------------------------
;
; Wait_dos_free uses busy waiting (calling _yield) in case
; some other background program has entered DOS without going
; through our INT 21 interface.
;
@wait_dos_free proc near
push es
push bx
;
in_use_loop:
les bx,in_error
cmp byte ptr es:[bx],0
jne is_in_use ; wait if in_error set
;
cmp idle_active,0 ; idle interrupt active?
je ck_inuse ; check for flag if no
pop bx
pop es
ret ; else return immediately
;
ck_inuse:
les bx,tsk_glob_rec.dos_in_use
cmp byte ptr es:[bx],0
jne is_in_use
pop bx
pop es
ret
;
is_in_use:
call yield
jmp in_use_loop
;
@wait_dos_free endp
;
;----------------------------------------------------------------------------
;
; 'relres' releases the DOS-resources of a task.
;
; Entry:
; ES:BX = TCB pointer
;
@relres proc near
;
test es:t_indos[bx],OWN_UPPER
jz rel_lower
xor es:t_indos[bx],OWN_UPPER
push bx
push es
callp release_resource,<<ds,#upper_dos>>
pop es
pop bx
;
rel_lower:
test es:t_indos[bx],OWN_LOWER
jz rel_exit
xor es:t_indos[bx],OWN_LOWER
push bx
push es
callp release_resource,<<ds,#lower_dos>>
pop es
pop bx
;
rel_exit:
ret
;
@relres endp
;
;----------------------------------------------------------------------------
;
; The 'schedent' function is called by the scheduler if the
; scheduler is entered and the current task owns one of the
; DOS resources.
; This routine checks whether the DOS busy flags are clear now,
; which would mean that the task left DOS without passing through
; the return address. If that's the case, we release the resources.
;
; The only catch is that there is a race condition if we just check
; the busy flags. There is a time slot where the function is set in
; the TCB, but the busy flag is clear because DOS has not yet been
; entered. Since we have no control over what will happen, and how
; long it will take to reach the point where the in_use flag is
; incremented, we can't simply disable preemption. So what we have
; to do is to wait for some other indication that DOS has begun
; executing the request, and mark this in the TCB. In all versions
; starting at 3.1, DOS will call the 'critical section' INT 2A
; with AX=8200 on functions > 0C after incrementing the in_use flag.
; We also have some indication that DOS is active if the 'idle'
; INT 28 is called, or if the in_use flag is set when we get here.
;
; Entry:
; DS CTask data segment
; BX Global data block offset
; ES:DI Task control block
;
@schedent proc far
;
call @dosbusy ; DOS still busy ?
jz schedent_relres ; release resources if not busy
or es:t_indos[di],DOS_ENTERED
ret
;
schedent_relres:
test es:t_indos[di],DOS_ENTERED
jz schedent_ret ; don't release if DOS not yet entered
;
mov bx,di
call @relres
;
schedent_ret:
ret
;
@schedent endp
;
;----------------------------------------------------------------------------
;
; INT 28: DOS Idle Interrupt
;
@idleentry proc far
;
call tsk_switch_stack
;
; check the in_use flag. If it's set, set the DOS_ENTERED flag
; in the current TCB.
;
les bx,tsk_glob_rec.dos_in_use
cmp byte ptr es:[bx],0
je no_entermark
;
les bx,tsk_glob_rec.current_task
or es:t_indos[bx],DOS_ENTERED
;
; Check if someone is waiting for upper_dos. If not, we can return
; immediately.
;
no_entermark:
les bx,upper_dos.rwaiting.q_first
test es:q_kind[bx],Q_HEAD
jnz idle_exit
;
; Also make sure this is not a second invocation of INT 28, and
; that the in_error flag is not set.
; Normally, this should never happen, but better safe than sorry.
;
cmp idle_active,0
jne idle_exit
les bx,in_error
cmp byte ptr es:[bx],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
;
callp release_resource,<<ds,#upper_dos>>
callp request_resource,<<ds,#upper_dos>,0,0>
;
; 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
;
; 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
;
; We only handle function number 82 in this version,
; it is used to mark that DOS has been entered. All functions
; are passed on to the next in chain.
;
@critsectint proc far
;
cmp ah,82h
jne cs_exit
push ds
push es
push bx
mov ds,cs:tsk_dgroup
;
; check the in_use flag. If it's set, set the DOS_ENTERED flag
; in the current TCB.
;
les bx,tsk_glob_rec.dos_in_use
cmp byte ptr es:[bx],0
je cs_no_enter
;
les bx,tsk_glob_rec.current_task
or es:t_indos[bx],DOS_ENTERED
;
cs_no_enter:
pop bx
pop es
pop ds
;
cs_exit:
jmp cs:savcsect
;
@critsectint endp
;
;---------------------------------------------------------------------------
;---------------------------------------------------------------------------
;
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,'$'
;
;---------------------------------------------------------------------------
;
; 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 ax,@CTASK_DATA
mov ds,ax
;
les bx,tsk_glob_rec.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
;
callp tsk_remove_group,<<es,bx>,0>
;
mov dx,offset group_term_msg
cmp ax,0
je emergency_end
jb pg_fatal
;
les bx,tsk_glob_rec.current_task[bx]
les bx,es:tgroup[bx]
callp tsk_kill_group,<<es,bx>>
call tsk_remove_tasker
;
mov dx,offset term_err_msg
;
emergency_end:
;
mov ax,cs
mov ds,ax
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.
; SI must point to the '$'-terminated error message.
;
; tsk_fatal does the same, but assumes C calling sequence and
; a zero-terminated string.
;
Globalfunc tsk_fatal,<strp: far ptr>
;
cld
lds si,strp
jmp short dis_err
;
tsk_fatal endp
;
fatal_error:
mov ax,cs
mov ds,ax
;
dis_err:
IF IBM
xor ax,ax
int 10h ; re-init display
fatal_loop:
lodsb
cmp al,'$'
je fatal_end
or al,al
jz fatal_end
mov bx,7
mov ah,14
int 10h
jmp fatal_loop
ELSE
mov dx,si
mov ah,9
int 21h
ENDIF
fatal_end:
sti
jmp fatal_end
;
;--------------------------------------------------------------------------
;
; void CGlobalfunc tsk_fatal_pmd (byteptr string, ...)
;
; tsk_fatal_pmd is only present in "checking" mode.
; It does a minimal "post mortem dump", and displays the
; error message passed as parameter.
; The messages are output to both the primary and secondary
; monitor via the "regen" printf routines in tskprf.
; The system is then halted, this routine never returns.
;
IF CHECKING
;
;
CGlobalfunc tsk_fatal_pmd,<txt: far ptr,argp:far ptr>
;
; int 3 ; uncomment to break to debugger
push sp
push ss
push es
push ds
push di
push si
push dx
push cx
push bx
push ax
;
mov ds,tsk_dgroup
callp preempt_off
callp tsk_set_currdis
;
callp tsk_rputc,<0ch>
les di,txt
lea bx,argp
call @fatal_dump
;
callp tsk_set_dualdis
or ax,ax
jz no_second
;
callp tsk_rputc,<0ch>
les di,txt
lea bx,argp
call @fatal_dump
;
no_second:
jmp fatal_end
;
tsk_fatal_pmd endp
;
;
pmd1 db 0ah
db 'AX = %04X BX = %04X CX = %04X DX = %04X',0ah
db 'SI = %04X DI = %04X DS = %04X ES = %04X',0ah
db 'SS = %04X SP = %04X BP = %04X',0ah
db 'IP = %04X CS = %04X',0ah,0
pmd2 db 'Current Task = %FP'
IF TSK_NAMED
db ' (%s)'
ENDIF
db 0ah,0
pmde db 'System Halted',0
;
;
@fatal_dump proc near
;
callp tsk_vrprintf,<<es,di>,<ss,bx>>
mov bx,sp
add bx,2
callp tsk_vrprintf,<<cs,#pmd1>,<ss,bx>>
;
IF SINGLE_DATA
mov bx,offset tsk_glob_rec
push ds
pop es
ELSE
les bx,tsk_global
ENDIF
les bx,es:current_task[bx]
IF TSK_NAMED
lea dx,tname.nname[bx]
callp tsk_rprintf,<<cs,#pmd2>,<es,bx>,<es,dx>>
ELSE
callp tsk_rprintf,<<cs,#pmd2>,<es,bx>>
ENDIF
callp tsk_rprintf,<<cs,#pmde>>
ret
;
@fatal_dump endp
;
ENDIF
;
;---------------------------------------------------------------
;
@sp_schedule proc near
call schedule
ret
@sp_schedule endp
;
@sp_yield proc near
call yield
ret
@sp_yield endp
;
;
spfunctab label word
dw @sp_schedule
dw @sp_yield
;
@special_function proc near
;
mov bx,save_cx[bp]
xor bh,bh
add bx,bx
jmp spfunctab[bx]
;
@special_function endp
;
;---------------------------------------------------------------
;
IF DEBUG AND DEB_DOSTRBUF
;
dpmi_int proc far
;
inc cs:i31count
pushf
call savdpmi
pushf
dec cs:i31count
popf
ret 2
;
dpmi_int endp
;
ENDIF
IF DEBUG AND DEB_DOSTRBUF
;
multiplex_int proc far
;
inc cs:i2fcount
pushf
call savmux
pushf
dec cs:i2fcount
popf
ret 2
;
multiplex_int endp
;
dump_dostrace proc far
;
mov bx,tr_ptr
mov cx,TR_ENTRIES
lea ax,tr_head
cmp tr_count,cx
jae dtr_cok
xor bx,bx
mov cx,tr_count
or cx,cx
jnz dtr_cok
lea ax,tr_none
;
dtr_cok:
push bx
push cx
callp tsk_cprintf,<<ds,#tr_string>>
pop cx
pop bx
jcxz dtr_end
;
dtr_loop:
push bx
push cx
;
callp tsk_vcprintf,<<ds,#tr_string>,<ds,/tr_buffer[bx]>>
;
pop cx
pop bx
add bx,TYPE trrec
cmp bx,TR_ENTRIES * TYPE trrec
jb dtr_nowrap
xor bx,bx
dtr_nowrap:
loop dtr_loop
xor bx,bx
mov tr_ptr,cx
mov tr_count,cx
dtr_end:
ret
;
dump_dostrace endp
;
ENDIF
;
.tsk_ecode
;
ENDIF
;
end
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -