Files
Digital-Research-Source-Code/CPM OPERATING SYSTEMS/CPM 86/CONCURRENT/CCPM-86 2.0 SOURCE/kern/dsptch.rtm
Sepp J Morris 31738079c4 Upload
Digital Research
2020-11-06 18:50:37 +01:00

605 lines
16 KiB
Plaintext

;*****************************************************
;*
;* Dispatch Routines
;*
;*****************************************************
;=====
fdisp:
;=====
; This entry point used by interrupt routines in XIOS
; Note: if the XIOS is performing memory protection interrupt
; handlers must enable O.S. memory before calling the O.S.
cli
push ds ! mov ds,sysdat
cmp indisp,true ! je nodisp ;if indisp=true then we are
mov ax_sav,ax ;in the dispatcher and
mov al,true ! mov indisp,al ;this code is skipped
mov ax,es ! mov es_sav,ax
mov ax,rlr ! xchg ax,bx ! mov bx_sav,ax
mov es,p_uda[bx]
mov al,true ! mov u_in_int,al
pop ax ! mov u_ds_sav,ax
mov al,p_stat[bx]
mov u_stat_sav,al
mov p_stat[bx],ps_run
mov ax,es_sav
mov u_es_sav,ax
; AX in AX_SAV
; BX in BX_SAV
; ES in U_ES_SAV
; DS in U_DS_SAV
; p_stat in U_STAT_SAV
jmp intdisp
;dispatcher will jump to here if
; u_in_int = true.
int_disp_exit: ;interrupts are off
; AX in AX
; BX in BX
; ES in U_ES_SAV
; DS in U_DS_SAV
; p_stat in U_STAT_SAV
mov ax_sav,ax
mov ax,bx ! mov bx_sav,ax
;check for
cmp drl,0 ! jnz intdisp ;interrupt occurence
;on dispatcher exit
mov al,false ! mov u_in_int,al
mov ax,rlr ! mov bx,ax
mov al,u_stat_sav
mov p_stat[bx],al
mov ax,bx_sav ! mov bx,ax
mov ax,ax_sav
mov indisp,false
mov ds,u_ds_sav
mov es,u_es_sav
iret
nodisp: pop ds
iret
;========
farpdisp:
;========
; Intermodule pdisp (non-interrupt)
call pdisp ! retf
;=====
pdisp:
;=====
; Call dispatcher with no special action
push bx ! mov bx,rlr
mov p_stat[bx],ps_run ! pop bx
;jmp dsptch
;======
dsptch:
;======
; The dispatch function looks like a noop to the
; caller. All flags and registers are maintained.
; No levels of user stack is used.
; (jmp dispatch = ret)
; Interrupt routines enter through fdisp.
;
; Dispatch has two (2) arguments:
; 1. the p_stat field of the process descriptor
; determines the type of action to perform
; for this process.
; 2. the dparam field of the uda is an argument
; to the action.
; The main part of the dispatch routine takes the
; currently running process off the Ready list
; and jmps to a routine which will put it on some
; other list depending on the p_stat argument.
; The subsequent routine will then jump to the
; scheduler which will do polling of devices and
; move processes off the dispatch ready list onto
; the Ready list. The Ready List is maintained
; in priority order with round-robin scheduling
; of processes with equivalent priorities. The
; first process on the ready list will then be
; switched in.
; set indisp flag
pushf ! cli
cmp indisp,true ! jne dispin
popf ! ret
dispin: mov indisp,true
pop u_flag_sav
; assumming bx=RLR:
; if PLR=0 and DRL=0 then
; if p_stat[bx]=PS_RUN then
; if p_link[bx]=0 or
; p_prior[p_link[bx]]<>p_prior[bx] then
; don't do dispatch
mov ax_sav,ax
mov ax,bx ! mov bx_sav,bx
intdisp:
cmp plr,0 ! jne dcont ;if Poll list = 0 and
cmp drl,0 ! jne dcont ;Dsptch Ready list = 0 and
mov ax,rlr ! mov bx,ax ;(RLR can never be 0 here)
cmp p_stat[bx],ps_run ! jne dcont ;our status is run and
cmp p_link[bx],0 ! je no_disp2 ;other PD to ready to run
mov al,p_prior[bx] ;with an equal priority
mov bx,p_link[bx] ;THEN skip the dispatch
cmp al,p_prior[bx] ! je dcont
no_disp2:
mov ax,bx_sav ! mov bx,ax
mov ax,ax_sav
jmp dext
dcont:
mov u_ss,ss ! mov u_sp,sp
mov ss,sysdat ! mov sp,offset dsptchtos
mov ax,bx_sav ! mov bx,ax
mov ax,ax_sav
sti
cld
; save registers
; NOTE: We will use DS instead of ES
; No segment overrides...
push es ! pop ds
mov ds:u_ax,ax
mov ax,bx ! mov ds:u_bx,ax
mov ax,cx ! mov ds:u_cx,ax
mov ax,dx ! mov ds:u_dx,ax
mov ax,di ! mov ds:u_di,ax
mov ax,si ! mov ds:u_si,ax
mov ax,bp ! mov ds:u_bp,ax
; Save interrupt vectors 0,1,3,4 not INT 2 which is NMI
; MP/M-86, CCP/M-86 1.0 on the IBM PC saved NMI
; Block move first 2
xor bx,bx ! mov ds,bx
mov si,bx ! mov di,offset u_ivectors
mov dx,4
mov cx,dx ! rep movsw
mov cx,dx ! add si,dx ;get next 2
add di,dx ;skip INT 2 location documented
rep movsw ;now as reserved word in UDA
; block move osint,debugint
mov si,offset i_os_ip ! mov di,offset u_os_ip
mov cx,dx ! rep movsw
; take current process off RLR.
mov ds,sysdat
; ;disable memory for the process
; ;we are taking out of context
;turn off interrupts ?
; mov bx,rlr !; lea si,p_mem-ms_link[bx]
;dsp_dnxt:
; mov si,ms_link[si]
; test si,si !; jz dsp_disabled
; mov cx,ms_start[si] !; mov dx,ms_length[si]
; push si
; mov ax,io_disable !; call xiosif
; pop si
; jmps dsp_dnxt
;
;dsp_disabled:
nop
mov ax,rlr ! mov si,ax
mov ax,p_link[si] ! mov rlr,ax
mov p_link[si],0
; We are now in NO-MAN's land
; From now until the end of the
; switch routine, There is no
; process in context.
; SI -> PD just taken out of context.
; jump to routine for given status
xor bh,bh ! mov bl,p_stat[si] ! shl bx,1
jmp cs:dsp_table[bx]
org ((offset $)+1) AND 0fffeh
dsp_table dw disp_act ;00 - run
dw disp_act ;01 - (nop)-poll device
dw delay_act ;02 - delay
dw disp_act ;03 - (nop)-swap
dw term_act ;04 - terminate
dw sleep_act ;05 - sleep
dw disp_act ;06 - (nop)-dq
dw disp_act ;07 - (nop)-nq
dw flag_act ;08 - flag wait
dw disp_act ;09 - (nop)-ciowait
dw disp_act ;10 - (nop)-sync
sleep_act:
;---------
; insert running process into list specified by
; u_dparam and set p_stat from p_scratch
; Note: we cannot sleep on the DLR since interrupts are on
; here, and flag_set can change the DLR
mov ax,u_dparam
mov bx,ax
push si ! call insert_process
pop si ! or p_flag[si],pf_resource
mov al,p_scratch[si] ! mov p_stat[si],al
jmp schedule
delay_act:
;---------
; Put the running process on the Delay List. The
; delay list is built such that any process's
; remaining delay time is the additive of the delay
; times of all processes ahead of it plus the # of
; ticks in it's own p_wait field. At each clock tick
; the p_wait field of the top process in the list
; is decremented. If it reaches zero (0), all
; processes with a zero in the p_wait field are
; placed on the dispatch ready list.
; input: SI=pd address
cli ;keep flag set from changing
if mpm ;TICK, and changing DLR
push si ! mov al,io_strtclk
call xiosif ! pop si
endif
if ccpm
mov tick,true
endif
mov bx,(offset dlr)-p_link
mov cx,u_dparam ! inc cx
cmp cx,0 ! jne del_lp
dec cx
del_lp: mov di,p_link[bx]
cmp di,0 ! je del_o
mov ax,p_wait[di]
cmp ax,cx ! ja del_o
sub cx,ax ! mov bx,di ! jmps del_lp
del_o: mov p_link[si],di ! mov p_link[bx],si
mov p_wait[si],cx
cmp di,0 ! je del_e
sub p_wait[di],cx
del_e: jmp schedule
flag_act:
;--------
; place running process in flag table to wait
; for next flag. Note, flag may have been set...
; input: SI=pd address
; U_DPARAM=address of Flag entry
mov ax,u_dparam ! mov bx,ax
cli ;protect from flag set
cmp flg_pd[bx],flag_on ! je gflagon
mov flg_pd[bx],si ! mov p_link[si],0
jmp schedule
gflagon: ; Flag set since wait check
mov flg_pd[bx],flag_off
sti
jmps disp_act
term_act:
;--------
; Terminate the running process, free memory, free pd, free sync
; structures. Can only be called by TERMINATE_ENTRY.
; input: SI=pd address
; MEM_SYNC owned by calling process
; place PD on rlr for now.
mov ax,rlr
mov p_link[si],ax
mov rlr,si
; clean up consoles
mov cx,f_cioterm ! call osif
; clean up memory
; (we own the MXmemory queue)
free_nxt:
mov ax,rlr ! mov si,ax
mov si,p_mem[si]
test si,si ! jz end_free
push ds ! xor cx,cx ! push cx
push ms_start[si]
mov ax,ss ! mov ds,ax ! mov dx,sp
mov cx,f_memfree ! call osif
pop bx ! pop cx ! pop ds
jmps free_nxt
end_free:
;release any sync structures
;after releasing memory
mov mem_cnt,0 ;and MEM_CNT=1
;on entry, also own THRD_SYNC
;so it is safe to call
;FREEPD below,
;ASSIGN_SYNC cannot be called
;with the THRD_SPB
;SI=terminating process
mov bx,offset slr - sy_link ;release any sync structures
t_nsync: ;owned by terminating PD
mov bx,sy_link[bx]
test bx,bx ;end of syncs?
jz t_sync_done
push bx ;PD cannot be allowed to
call unsync_entry ;abort if in SY.NEXT
pop bx
jmps t_nsync
t_sync_done:
; take off RLR
mov si,rlr
mov ax,p_link[si]
mov rlr,ax
mov p_link[si],0
; cmp si,owner_8087 ;release 8087 if we owned it
; jne t_end ! mov owner_8087,0
;t_end:
; free up PD
call freepd ! jmp schedule
disp_act:
;--------
; place process on RLR
; input: SI=pd address
mov p_stat[si],ps_run
mov bx,(offset rlr)-p_link
call insert_process ! jmp schedule
;==============
insert_process:
;==============
;
; put PD# in list ordered by priority
;
; entry: BX = list root
; SI = pd number
; exit: SI is preserved
; interrupt state as on entry
mov cx,pflag[si] ! and cx,pf_resource
;if a process was waiting
ins_npd: mov di,p_link[bx] ;on a resource, insert
test di,di ! jz ins_out ;it ahead of equal priority
mov al,p_prior[di] ;process
cmp al,p_prior[si]
ja ins_out ;lowest priority first
jb ins_nxt ;higher - keep going down list
jcxz ins_nxt ;equal and not resource
jmps ins_out ;equal & resource
ins_nxt: mov bx,di ! jmp ins_npd
ins_out: jcxz ins_exit
and p_flag[si],not pf_resource
ins_exit:
mov p_link[si],di ! mov p_link[bx],si ! ret
;========
schedule:
;========
; poll all required devices and place any ready
; processes on the Ready List
;we can enable interrupts now.
;there MUST be a process on the RLR
;at this point, ie. IDLE...
sti
;go through the Poll List
mov di,(offset plr)-p_link
;get the next PD on list.
;DI is the last one which
;has already been checked.
polld_another:
mov si,p_link[di]
;SI is the next PD to check
test si,si ! jz drltorlr
;SI is valid PD, poll it.
;If top PD on the PLR has a worse
;priority compared to top PD on the RLR,
;there is no reason to call the XIOS
;and poll the device, this time through
;the dispatcher. We must poll on equal
;priority to keep a compute bound
;process and the CLOCK from locking
;out a polling process.
;Note, we stop polling after
;the first process that has polled
;successfully, or we get to the end of
;the PLR. The process is placed on
;the RLR.
mov bx,rlr ! test bx,bx ;if RLR=0: poll
jz poll_it
mov al,p_prior[si] ;priority of 1st poll PD
cmp al,p_prior[bx] ! jbe poll_it ;poll if equal or better
jmps drltorlr ;priority than head of RLR
poll_it:
push di
if mpm
mov cx,p_wait[si]
endif
if ccpm
mov dx,p_wait[si]
endif
mov al,io_polldev ! call xiosif
pop di ! mov si,p_link[di]
;if AL=0, device not ready.
cmp al,0 ! je polld_next
;device ready,
;move SI from PLR to RLR
mov ax,p_link[si] ! mov p_link[di],ax
mov bx,(offset rlr)-p_link
mov p_stat[si],ps_run
call insert_process ;got one ready to run:
jmps drltorlr ;stop polling
;p_link[SI]=next PD to check
polld_next: ;SI has been checked
mov di,si ! jmps polld_another
drltorlr:
;--------
; Pull all processes on the dispatch ready list and
; place them on the Ready List.
;We must disable interrupts while
;playing with DRL since interrupts
;doing a Flag_set may also.
;We must competely drain the DRL since
;it is in no particular order.
cli ! mov si,drl ;protect DRL from flag set
test si,si ! jz switch
mov ax,p_link[si] ! mov drl,ax
; test ax,ax ;is this the last PD on DRL?
; jnz drl_noi ;yes - don't turn on interrupts
sti ;interrupts off guarentees
;drl_noi: ;the last DRL PD with the
mov p_stat[si],ps_run ;best priority will run
mov bx,(offset rlr)-p_link ;next and at least until
call insert_process ;it turns on interrupts
jmps drltorlr
switch:
;------
; switch to the first process on the Ready List
sti
mov bx,rlr
; if no next process, go back ;
; to schedule. Gives more immediate ;
; response to polled and interrupt ;
; driven devices ;
test bx,bx ! jnz switch1 ;
jmp schedule ;
switch1: ;
; ;enable memory for this process
; ;turn on interrupts ?
; mov bx,rlr !; lea si,p_mem-ms_link[bx]
;dsp_enxt:
; mov si,ms_link[si]
; test si,si !; jz dsp_enabled
; mov cx,ms_start[si] !; mov dx,ms_length[si]
; push si
; mov ax,io_enable !; call xiosif
; pop si
; jmps dsp_enxt
;
;dsp_enabled:
;
; ;Save and restore the 8087 environment if process to run
; ;uses the 8087 and is not the owner. Interrupts
; ;must be on. Code from Intel Ap. Note. 113 page 29
;
; ;This code shouldn't be added unless interrupt windows in
; ;switch are allowed or the 8087 restore is separated
; ;from the 8086/8088 restore. The switch code without this
; ;commented out 8087 code,
; ;creates an interrupt window approx. 100 to 200 micro
; ;secs on 5 to 4 meg CPU.
; ;Allowing interrupt windows in switch
; ;means we must check on leaving the dispatcher
; ;for an interrupt awakened process (DRL again <> 0)
; ;and call the
; ;dispatcher again to prevent a 16 milli second wakeup time
; ;for a PD doing a flagwait after the interrupt service routine.
; ;Calling the dispatcher at the end of the dispatch
; ;(see commented out code at end of dispatcher and at
; ;INT_DISP_EXIT:)
; ;creates contention problems between PDs waiting for a resource
; ;and PDs waking up from interrupts. It cannot be guarenteed
; ;who will run next. An interrupt awakened process can
; ;get a just freed resource is should have waited for.
; ;The RTM ASSIGN_SYNC_ENTRY is an untested solution
; ;for allowing interrupts in the dispatcher switch code.
; ;BX=PD to run next
; test p_flag[bx],pf_8087 ;does this process use the NPX ?
; jz done8087
; cmp bx,owner_8087 ;do we already own it
; je done8087
; xchg bx,owner_8087 ;new owner, also set by terminate
; test bx,bx ! jz get8087 ;no one owns it if BX=0
; mov dx,ds ;save sysdat in DX
; mov ds,p_uda[bx] ;old owner's UDA
; fnstcw ds:u_8087 ;save IEM bit status
; nop ;delay while 8087 busy saves control reg
; fndisi ;disable 8087 busy signal
; mov ax,ds:u_8087 ;get original control word
;
; fsave ds:u_8087 ;save NPX context
; fwait ;IEM=1.wait for save to finish
; mov ds:u_8087,ax ;save original control word
;get8087:
; mov ds,dx ;DS=sysdat
; mov bx,rlr
; mov ds,p_uda[bx] ;PD to run next
; frstor ds:u_8087 ;get its 8087 environment
; push ds ;DS=UDA
; jmps restore ;UDA on stack
;
;done8087: ;BX=PD to run next
mov dx,p_uda[bx] ! mov ds,dx ;DS=UDA
push dx ;UDA on stack
;Restore interrupt vectors
restore:
xor ax,ax ! mov es,ax ! mov di,ax
mov si,offset u_ivectors
mov dx,4
mov cx,dx ! rep movsw ;restore interrupt vectors 0,1
mov cx,dx ! add di,dx ;don't touch NMI
add si,dx ;skip what was NMI
rep movsw ;restore interupt vectors 3,4
mov si,offset u_os_ip ;DS=UDA
mov di,offset i_os_ip
mov cx,dx ! rep movsw
; restore registers
mov ax,ds:u_bx ! mov bx,ax
mov ax,ds:u_cx ! mov cx,ax
mov ax,ds:u_dx ! mov dx,ax
mov ax,ds:u_si ! mov si,ax
mov ax,ds:u_di ! mov di,ax
mov ax,ds:u_bp ! mov bp,ax
mov ax,ds:u_ax
; restore DS and ES and stack
pop es ;ES=UDA
cli ;turn interrupts off for rest
mov ss,u_ss ;of exit
mov sp,u_sp
mov ds,sysdat
dext:
cmp u_in_int,true ! jne dret
jmp int_disp_exit
dret:
push u_flag_sav
mov indisp,false
cmp drl,0 ! je dd_ret
popf ; someone is on DRL from interrupt during
jmp pdisp ; switch, dispatch now, no 16ms wait
dd_ret:
popf
ret