.TITLE JOBCTL job control & scheduling .IDENT /04.04/ ; ; This file implements a job controlling facility for GRUMPF. ; Macros provided: ; .JOBCTL defines symbols for accessing a job structure ; .DONE sets all jobs waiting for a given event running by ; clearing their wait status and connecting them to ; RUN$Q. Any process that has a matching asynchronous ; event registered and that is also synchronously waiting ; for a (different) event will stay running as if both ; events were done. ; .WAIT sets the current job waiting for the named event and ; reschedules ; .EVENT registers a handler routine for the given event, ; a event block is allocated from SYSJOB pool ; .J$ALLO creates a job, allocating a buffer to hold the job ; structure from the pool. The job is set waiting for ; for the CREATE event, which must be .DONE to start ; execution of the new job. ; .LDPCTX loads the process context from CURJOB, assuming that it ; is operating on the system stack. ; .SVPCTX saves the process context to CURJOB, assuming that it ; is operating on the process stack. ; .TERM terminates CURJOB, will delete any event blocks that ; the job may still have, will leave the jobs allocated ; memory partitions intact ; .EXIT terminates CURJOB, will delete any event blocks that ; the job may still have as well as all of the jobs ; allocated memory partitions ; .CRASH terminates CURJOB, will not modify the job other than ; deleting all its event blocks and setting it waiting ; for CRASH ; ; other routines: ; J$INIT initializes job control and the system job entry. ; J$CALL registers job control system calls ; J$INFO prints out the job queues ; EVKILL deletes all event blocks of a job ; ; system calls: ; $SCHED saves process context and reschedules ; $DONE wakes up any matching ASYN oder WAIT ; $WAIT sets the process waiting ; $EVENT registers a handler for a asynchronous event ; $CURJOB returns the current value of CURJOB ; $TERM terminates the job, frees its ASYNs ; $EXIT terminates the job, frees its memory ; $TICK increment clock tick count of current job ; $BUSHND install a bus error handler ; $CRASH terminates the job, not modifying it ; ; JOB structure: ; J$NEXT pointer to next job in queue, or 0 at end ; J$NAME two RAD50 words containing the job name ; J$WAIT two RAD50 words containing the name of a wait event ; J$TIME two words containing the number of clock ticks used ; by this job ; J$IOCH list of I/O channels used by this job ; J$PART start address of primary memory partition of job ; J$R5 job call frame pointer ; J$SP job stack pointer ; J$PC job program counter ; J$PSW job processor status ; J$BUS bus error handler ; J$POOL pointer to pool partition of job ; J$STCK pointer to stack partition of job, also top of stack ; ; EVENT structure: ; E$NEXT pointer to next event in list, or 0 at end ; E$NAME two RAD50 words containing the event name ; E$JOB pointer to the job that created this event block ; E$ARG argument that will be given to the event handler ; E$PC event handler address ; ; other variables: ; SYSJOB system job structure ; CURJOB current job, not associated with any queue ; JOBTBL job table ; RUN$Q queue of jobs waiting for the processor (runnable jobs) ; WAITBL hash table of jobs waiting for some event ; EVNTBL hash table of event blocks ; STACK system stack pointer ; ; tunable constants: ; NJOB number of jobs (size of job table) ; TBLSIZ size of hash tables (WAITBL at the moment) ; ; 11-AUG-2005 H. Rosenfeld added support for stack partitions ; 17-JUL-2005 H. Rosenfeld removed all ASYN related stuff, replaced it ; with a EVENT facility for asynchronous events ; 08-JUL-2005 H. Rosenfeld reworked nearly all parts of the job control ; facility: a job table containing all jobs and a ; hash table containing waiting jobs has been ; added, TERM$Q and WAIT$Q have been removed, ; RUN$Q has been changed to be a queue instead of ; simple linked list, J$PREV has been removed ; from the job structure, all ASYN related stuff ; has been deactivated since it was neither ; tested nor used and wouldn't have worked with ; these changes anyway ; 04-JUN-2005 H. Rosenfeld changed WAIT and SCHED to set the PSW for SVPCTX ; directly to 340, not using MFPS, this allows ; Grumpf to run on processors using all 16 bits of ; the PSW ; 03-JUN-2005 H. Rosenfeld set initial system stack pointer to 600 instead ; of 500 ; 24-MAY-2005 H. Rosenfeld added some missing CLCs, added $CRASH ; 22-MAY-2005 H. Rosenfeld added TERM$Q, changed $EXIT, $TERM and J$INFO ; accordingly, added $BUSHND call ; 20-MAY-2005 H. Rosenfeld added $TICK system call ; 17-MAY-2005 H. Rosenfeld reworked ASYN activation in ASYN$J ; 03-MAY-2005 H. Rosenfeld use system call priorities ; 28-APR-2005 H. Rosenfeld fixed bug in J$EXIT ; 22-APR-2005 H. Rosenfeld added J$POOL to job structure. Since the POOL ; calls now no longer take the POOL address as ; first argument and instead use J$POOL of CURJOB, ; POOL allocations (ASYNs etc.) will be made from ; the jobs POOL by default. This implies that a ; job switch is necessary in some cases. ; 14-APR-2005 H. Rosenfeld added J$TERM, J$EXIT ; 13-APR-2005 H. Rosenfeld added J$BUS to job structure ; 11-APR-2005 H. Rosenfeld added $CURJOB system call ; 09-APR-2005 H. Rosenfeld adapt to change in POOL system ; 06-MAR-2005 H. Rosenfeld added missing SEC to indicate errors in J$ALLO ; 25-JAN-2005 H. Rosenfeld changed routines and system calls to use the ; new calling convention, this implies changes in ; the way ASYNs work. The general registers were ; removed from the job structure and the context ; switching code was reworked. ; 11-DEC-2004 H. Rosenfeld fixed SCHED bug, processor priority was set ; wrong in idle loop ; 06-DEC-2004 H. Rosenfeld changed syscall macros to reflect changes in ; $SYS macro ; 01-NOV-2004 H. Rosenfeld ; .LIST ME .NLIST CND .ENABL REG .LIBRARY /CALL.SML/ .LIBRARY /MEMORY.SML/ .LIBRARY /SYSCLL.SML/ .LIBRARY /UTIL.SML/ .MCALL .CALL,.ENTRY,.RETRN .MCALL .SYSPUT,.OCTAL,.RADDEC,.R50HSH .MCALL .P$ALLO,.P$FREE,.P$ERR,.M$KILL,.M$FREE .MCALL $SYS,$REGSYS .GLOBL $RETRN .MACRO .JOBCTL J$NEXT=0 J$NAME=2 J$WAIT=6 J$TIME=12 J$IOCH=16 J$PART=20 J$POOL=22 J$STCK=24 J$R5=26 J$SP=30 J$PC=32 J$PSW=34 J$BUS=36 SIZE$J=40 .ENDM .MACRO .EVNTCTL E$NEXT=0 E$NAME=2 E$JOB=6 E$ARG=10 E$PC=12 SIZE$E=14 .ENDM .JOBCTL .EVNTCTL NJOB=40 ; 40(8) jobs, should be more than enough for now TBLSIZ=17 ; hash table size .CSECT ; LDPCTX ; load process context, to be followed by an RTI .MACRO .LDPCTX .GLOBL STACK MOV SP,STACK ;; save system stack pointer MOV CURJOB,R4 ;; get current job MOV J$SP(R4),SP ;; switch to job stack MOV J$PSW(R4),-(SP) ;; restore job PSW for RTI MOV J$PC(R4),-(SP) ;; restore job PC for RTI MOV J$R5(R4),R5 ;; restore job frame pointer .ENDM ; SVPCTX ; save process context .MACRO .SVPCTX .GLOBL STACK MOV CURJOB,R0 ;; get current job MOV R5,J$R5(R0) ;; save job frame pointer MOV (SP)+,J$PC(R0) ;; save job PC MOV (SP)+,J$PSW(R0) ;; save job PSW MOV SP,J$SP(R0) ;; save job stack pointer MOV STACK,SP ;; switch to system stack .ENDM ; J$INIT, initialize job control data structures .MACRO .J$INIT .GLOBL J$INIT .CALL J$INIT .ENDM J$INIT::.ENTRY CLR RUN$Q ; we have no runnable jobs CLR RUN$Q+2 MOV #JOBTBL,R4 ; get job table MOV #NJOB,R3 ; get size of job table 1$: CLR (R4)+ ; initialize job table SOB R3,1$ MOV #WAITBL,R4 ; get wait hash table MOV #EVNTBL,R3 ; get event hash table MOV #TBLSIZ,R2 ; get size of hash table 2$: CLR (R4)+ ; initialize wait hash table CLR (R3)+ ; initialize event hash table SOB R2,2$ MOV #SYSJOB,R4 ; get SYSJOB MOV R4,CURJOB ; which will be our current job CLR J$NEXT(R4) ; clear job queue links MOV #^RGRU,J$NAME(R4) ; the SYSJOB is named GRUMPF MOV #^RMPF,J$NAME+2(R4) CLR J$WAIT(R4) ; it is not waiting for anything CLR J$WAIT+2(R4) CLR J$TIME(R4) ; and has not yet used any CPU time CLR J$TIME+2(R4) CLR J$IOCH(R4) ; neither has it any I/O channels CLR J$PART(R4) ; and its memory partition is at loc 0 CLR J$BUS(R4) ; nor does it expect a bus error CLR J$POOL(R4) ; it has no buffer pool CLR J$STCK(R4) ; stack partition at loc 0 MOV R4,JOBTBL ; SYSJOB is first job in table MOV #600,STACK ; set the system stack area well below the ; SYSJOB stack, this leaves just 200 words ; before the interrupt vector table .RETRN ; J$CALL, register job control system calls .MACRO .J$CALL .GLOBL J$CALL .CALL J$CALL .ENDM J$CALL::.ENTRY $REGSYS #^RSCH,#^RED,#SCHED,#340 $REGSYS #^RDON,#^RE,#DONE,#340 $REGSYS #^RWAI,#^RT,#WAIT,#340 $REGSYS #^RCUR,#^RJOB,#J$CURR,#0 $REGSYS #^RTER,#^RM,#J$TERM,#340 $REGSYS #^REXI,#^RT,#J$EXIT,#340 $REGSYS #^RTIC,#^RK,#J$TICK,#340 $REGSYS #^RBUS,#^RHND,#J$BHND,#0 $REGSYS #^RCRA,#^RSH,#J$CRSH,#340 $REGSYS #^REVE,#^RNT,#J$EVNT,#340 .RETRN ; J$INFO, print information on all jobs .MACRO .J$INFO .GLOBL J$INFO .CALL J$INFO .ENDM J$INFO::.ENTRY .P$ALLO #120 ; allocate a output buffer BCC 1$ ; continue if no error .P$ERR .RETRN 1$: MOV R0,-(SP) ; save address of buffer .SYSPUT #HDLINE ; print a headline .SYSPUT #UDLINE ; and underline it MOV #JOBTBL,R3 ; get job table MOV #NJOB,R2 ; get job table size 2$: MOV (R3)+,R4 ; get job BEQ 3$ ; skip printing if no job JSR PC,JOB$PR ; print job 3$: SOB R2,2$ .SYSPUT #CRLF MOV (SP)+,R3 ; get output buffer pointer .P$FREE R3,#120 ; free the output buffer .RETRN ; this is a helper routine for J$INFO ; it assumes that R4 contains the address of the job structure to print and ; the address of a buffer that can be used for output on the stack JOB$PR: MOV 2(SP),R0 ; get output buffer .RADDEC J$NAME(R4),R0 ; print job name .RADDEC J$NAME+2(R4),R0 MOVB #11,(R0)+ .RADDEC J$WAIT(R4),R0 ; and what the job is waiting for .RADDEC J$WAIT+2(R4),R0 MOVB #11,(R0)+ .OCTAL J$TIME+2(R4),R0 ; as well as the time it spent executing MOVB #11,(R0)+ .OCTAL J$TIME(R4),R0 MOVB #11,(R0)+ .OCTAL J$PART(R4),R0 ; and the address of its memory partition MOVB #11,(R0)+ .OCTAL J$IOCH(R4),R0 ; as well as the address of its I/O channels MOVB #11,(R0)+ .OCTAL J$POOL(R4),R0 ; and the address of its POOL partition MOVB #11,(R0)+ .OCTAL J$STCK(R4),R0 ; and the address of its stack partition MOV 2(SP),R0 ; get output buffer .SYSPUT R0 .SYSPUT #CRLF RTS PC ; J$ALLO, allocate job structure ; input: name (two RAD50 words) ; output: skeleton job structure, waiting for CREATE ; return: R0 pointer to job structure, carry set on error, clear otherwise .MACRO .J$ALLO NAM1 NAM2 .GLOBL J$ALLO .CALL J$ALLO,NAM1,NAM2 .ENDM J$ALLO::.ENTRY MOV #JOBTBL,R4 ; get job table MOV #NJOB,R3 ; get job table size 4$: TST (R4)+ ; test for free table entry BEQ 5$ ; found, use it SOB R3,4$ SEC ; no free table entries, indicate error .RETRN 5$: TST -(R4) ; rewind R4 to point to free entry .P$ALLO #SIZE$J ; allocate buffer for the job structure BCC 1$ ; continue if no error .P$ERR SEC ; indicate error .RETRN 1$: MOV R0,(R4) ; store pointer to job in table MOV 6(R5),J$NAME(R0) ; first argument: first part of name MOV 10(R5),J$NAME+2(R0) ; second argument: second part of name MOV #^RCRE,J$WAIT(R0) ; wait for creation MOV #^RATE,J$WAIT+2(R0) CLR J$TIME(R0) ; no CPU time used yet CLR J$TIME+2(R0) CLR J$IOCH(R0) ; no I/O channels yet CLR J$PART(R0) ; no memory partition yet CLR J$R5(R0) ; no frame pointer CLR J$SP(R0) ; no stack pointer CLR J$PC(R0) ; no program counter CLR J$PSW(R0) ; and no CPU status CLR J$BUS(R0) ; no bus error expected CLR J$POOL(R0) ; no buffer pool yet CLR J$STCK(R0) ; no stack partition MOV R0,R4 ; save job pointer .R50HSH #^RCRE,#^RATE,#TBLSIZ ; calculate hash for CREATE ASL R0 ; convert to table index ADD #WAITBL,R0 ; calclulate table entry MOV (R0),J$NEXT(R4) ; prepend job to list MOV R4,(R0) MOV R4,R0 ; return pointer to job structure CLC ; no error .RETRN ; CURJOB ; input: none ; output: none ; return: R0 current job .MACRO $CURJOB $SYS #^RCUR,#^RJOB .ENDM J$CURR::.ENTRY MOV CURJOB,R0 ; return current job .RETRN ; TICK, increment job tick count ; input: none ; output: incremented tick count in CURJOB or SYSJOB ; return: carry set if it is useful to reschedule, clear otherwise .MACRO $TICK $SYS #^RTIC,#^RK .ENDM J$TICK::.ENTRY MOV CURJOB,R4 ; get current job BNE 1$ MOV #SYSJOB,R4 ; if no current job, use SYSJOB 1$: INC J$TIME(R4) ; increment tick count ADC J$TIME+2(R4) TST CURJOB ; is there a current job? BEQ 2$ ; if not, indicate no runnable jobs TST RUN$Q ; check for runnable jobs BEQ 2$ SEC ; indicate that there are runnable jobs .RETRN 2$: CLC ; no runnable jobs .RETRN ; BUSHND, register bus error handler ; input: bus error handler address ; output: bus error handler set in job structure ; return: none .MACRO $BUSHND HND $SYS #^RBUS,#^RHND,HND .ENDM J$BHND::.ENTRY MOV CURJOB,R4 MOV 6(R5),J$BUS(R4) .RETRN ; EVENT, register handler for asynchronous event ; input: event name (two RAD50 words), handler address, handler argument ; output: if handler address is zero, the event block is deleted ; otherwise the event handler will be registered, either overwriting ; any previously registered handler for the same event or by allocating ; a new event block from SYSJOB pool ; return: carry set on internal error, clear on success .MACRO .EVENT NAM1,NAM2,ADDR,ARG .GLOBL J$EVNT .CALL J$EVNT,NAM1,NAM2,ADDR,ARG .ENDM .MACRO $EVENT NAM1,NAM2,ADDR,ARG $SYS #^REVE,#^RNT,NAM1,NAM2,ADDR,ARG .ENDM J$EVNT::.ENTRY MOV 6(R5),R3 ; first part of name MOV 10(R5),R4 ; second part of name .R50HSH R3,R4,#TBLSIZ ; calculate name hash ASL R0 ; convert to table index ADD #EVNTBL,R0 ; calculate table entry MOV R0,R2 ; save for reuse 1$: MOV R0,R1 ; save for delete MOV (R0),R0 ; get entry BEQ 2$ ; end of list? CMP R3,E$NAME(R0) ; first part of name matching? BNE 1$ ; nay, get next CMP R3,E$NAME+2(R0) ; second part of name matching? BNE 1$ ; nay, get next CMP CURJOB,E$JOB(R0); job matching? BNE 1$ ; nay, get next MOV 14(R5),E$ARG(R0); overwrite argument MOV 12(R5),E$PC(R0) ; overwrite handler address BEQ 3$ ; delete event if no handler CLC ; no error .RETRN 3$: MOV E$NEXT(R0),(R1) ; remove from list MOV CURJOB,-(SP) ; switch to SYSJOB MOV #SYSJOB,CURJOB .P$FREE R0,#SIZE$E ; free event block MOV (SP)+,CURJOB ; switch back to CURJOB CLC ; no error .RETRN 2$: TST 12(R5) ; non-zero handler address? BNE 4$ ; yea, continue CLC ; no error .RETRN 4$: MOV CURJOB,-(SP) ; switch to SYSJOB MOV #SYSJOB,CURJOB .P$ALLO #SIZE$E ; allocate event block MOV (SP)+,CURJOB ; switch back to CURJOB BCC 5$ ; continue if allocation successful .P$ERR ; print error message SEC ; indicate error .RETRN 5$: MOV R3,E$NAME(R0) ; first part of name MOV R4,E$NAME+2(R0) ; second part of name MOV CURJOB,E$JOB(R0); event job MOV 14(R5),E$ARG(R0); event handler argument MOV 12(R5),E$PC(R0) ; event handler address MOV (R2),E$NEXT(R0) ; prepend to list MOV R0,(R2) CLC ; no error .RETRN ; EVKILL is a helper routine for TERM, EXIT and CRASH ; input: pointer to the job ; output: all event blocks belonging to this job will be deleted ; return: none EVKILL: .ENTRY MOV #EVNTBL,R4 ; get event table MOV #TBLSIZ,R3 ; get event table size MOV 6(R5),R2 ; get job argument 1$: MOV R4,R0 ; get next table entry 2$: MOV R0,R1 ; save last event for reuse MOV (R0),R0 ; get next event BEQ 3$ ; stop at end of list CMP R2,E$JOB(R0) ; this job? BNE 2$ ; nay, get next event MOV (R0),(R1) ; disconnect from list MOV CURJOB,-(SP) ; switch to SYSJOB MOV #SYSJOB,CURJOB .P$FREE R0,#SIZE$E ; free event block MOV (SP)+,CURJOB ; switch back to CURJOB MOV R1,R0 ; rewind back one block BR 2$ 3$: TST (R4)+ ; switch to next table entry SOB R3,1$ .RETRN ; DONE, wake up jobs waiting for event ; input: name of event (two RAD50 words) ; output: jobs waiting for event will be set runnable, event blocks will be ; deleted ; return: none .MACRO $DONE NAM1,NAM2 $SYS #^RDON,#^RE,NAM1,NAM2 .ENDM .MACRO .DONE NAM1 NAM2 .GLOBL DONE .CALL DONE,NAM1,NAM2 .ENDM DONE:: .ENTRY MOV 10(R5),R3 ; second part of event name MOV 6(R5),R2 ; first part of event name .R50HSH R2,R3,#TBLSIZ ; calculate hash ASL R0 ; convert to table index ADD #WAITBL,R0 ; calculate table entry 1$: MOV R0,R4 ; save for reuse MOV (R0),R0 ; get next job in list BEQ 3$ ; stop at end of list CMP R2,J$WAIT(R0) ; first part matching? BNE 1$ ; skip if not CMP R3,J$WAIT+2(R0) ; second part matching? BNE 1$ ; skip if not MOV J$NEXT(R0),J$NEXT(R4) ; unlink job from list CLR J$WAIT(R0) ; not waiting CLR J$WAIT+2(R0) CLR J$NEXT(R0) ; mark end of queue MOV RUN$Q+2,R1 ; get last entry in RUN$Q BNE 2$ ; append to queue if queue not empty MOV R0,RUN$Q ; make it first job in queue MOV R0,RUN$Q+2 ; as well as last job in queue MOV R4,R0 ; rewind back one job BR 1$ ; continue 2$: MOV R0,J$NEXT(R1) ; connect to end of list MOV R0,RUN$Q+2 ; make it last job in queue MOV R4,R0 ; rewind back one job BR 1$ ; continue 3$: .R50HSH R2,R3,#TBLSIZ ; calculate hash, again ASL R0 ; convert to table index ADD #EVNTBL,R0 ; calculate event table entry 4$: MOV R0,R4 ; save for reuse MOV (R0),R0 ; get next event block BEQ 5$ ; stop at end of list CMP R2,E$NAME(R0) ; first part matching? BNE 4$ ; nay, get next CMP R3,E$NAME+2(R0) ; second part matching? BNE 4$ ; nay, get next MOV E$JOB(R0),R1 ; get job MOV R4,-(SP) ; we need R4 MOV J$SP(R1),R4 ; get job stack MOV J$PSW(R1),-(R4) ; save PSW for RTI MOV J$PC(R1),-(R4) ; save PC for RTI MOV J$R5(R1),-(R4) ; save old frame pointer MOV E$ARG(R0),-(R4) ; event handler argument MOV #MARK!1,-(R4) ; argument count is 1 MOV R4,J$R5(R1) ; set new frame pointer MOV #J$ERET,-(R4) ; return to J$ERET MOV E$PC(R0),J$PC(R1) ; set new PC to event handler, PSW will not be ; modified MOV R4,J$SP(R1) ; set new job stack pointer MOV (SP)+,R4 ; restore R4 MOV E$NEXT(R0),(R4) ; disconnect from list MOV CURJOB,-(SP) ; switch to SYSJOB MOV #SYSJOB,CURJOB .P$FREE R0,#SIZE$E ; return event block MOV (SP)+,CURJOB ; switch back to CURJOB MOV R4,R0 ; prepare for next run TST J$WAIT(R1) ; is this job waiting? BNE 6$ ; yea, set it running TST J$WAIT+2(R1) ; is this job waiting? BEQ 4$ ; nay, get next event 6$: MOV R1,-(SP) ; save for reuse .R50HSH J$WAIT(R1),J$WAIT+2(R1),#TBLSIZ ; calculate wait hash ASL R0 ; convert to table index ADD #WAITBL,R0 ; calculate table entry MOV (SP)+,R1 7$: CMP (R0),R1 ; is this job? BEQ 8$ ; yea MOV (R0),R0 ; get next in list BNE 7$ ; continue until end of list MOV R4,R0 ; prepare for next run BR 4$ 8$: MOV (R1),(R0) ; remove from list CLR J$WAIT(R1) ; not waiting CLR J$WAIT+2(R1) CLR J$NEXT(R1) ; mark end of queue MOV RUN$Q+2,R0 ; get last job in RUN$Q BNE 9$ ; branch if queue is not empty MOV R1,RUN$Q ; make this job the first job MOV R1,RUN$Q+2 ; as well as last job MOV R4,R0 ; prepare for next run BR 4$ 9$: MOV R1,J$NEXT(R0) ; append to list MOV R1,RUN$Q+2 ; make this job the last job MOV R4,R0 ; prepare for next run BR 4$ 5$: .RETRN ; J$ERET is a dummy for return from asynchronous events J$ERET: RTI ; WAIT, wait for event ; input: name of event (two RAD50 words) ; output: job is set waiting for event ; return: none ; note that this call will "return" after the event ; note that it is not guaranteed that the event really occured before return .MACRO $WAIT NAM1,NAM2 $SYS #^RWAI,#^RT,NAM1,NAM2 .ENDM .MACRO .WAIT NAM1 NAM2 .GLOBL WAIT .CALL WAIT,NAM1,NAM2 .ENDM WAIT:: .ENTRY MOV #340,-(SP) ; set PSW for SVPCTX MOV #$RETRN,-(SP) ; return to the register restore routine .SVPCTX ; save process context, switch to system stack MOV CURJOB,R4 ; get current job MOV 10(R5),R3 ; second part of event name MOV 6(R5),R2 ; first part of event name MOV R3,J$WAIT+2(R4) ; set job waiting MOV R2,J$WAIT(R4) .R50HSH R2,R3,#TBLSIZ ; calculate hash ASL R0 ; convert to table index ADD #WAITBL,R0 ; calculate table entry MOV (R0),J$NEXT(R4) ; prepend to list MOV R4,(R0) CLR CURJOB ; this job is now no longer "current" BR IDLE ; go to idle loop ; SCHED, reschedule ; input: none ; output: CURJOB appended to RUN$Q ; return: none ; note that this routine will "return" after the job is rescheduled .MACRO $SCHED $SYS #^RSCH,#^RED .ENDM .MACRO .SCHED .GLOBL SCHED .CALL SCHED .ENDM SCHED:: .ENTRY MOV CURJOB,R0 ; get current job BEQ IDLE ; nay, just idle MOV #340,-(SP) ; set PSW for SVPCTX MOV #$RETRN,-(SP) ; return to register restore routine .SVPCTX ; save process context MOV RUN$Q+2,R1 ; get last queue entry BEQ 1$ ; skip appending if empty MOV R0,J$NEXT(R1) ; append to queue CLR J$NEXT(R0) ; mark end of queue MOV R0,RUN$Q+2 CLR CURJOB ; no current job BR IDLE ; branch to idle loop 1$: CLR J$NEXT(R0) ; mark end of queue MOV R0,RUN$Q ; first runnable job MOV R0,RUN$Q ; as well as last runnable job CLR CURJOB ; no current job ; fall through to IDLE ; IDLE, system idle loop ; check for runnable job, switch if it exists ; wait for interrupt if not, then retry IDLE: MTPS #340 ; we do not want to be interrupted TST RUN$Q ; any runnable jobs? BNE SWITCH ; yea, switch MTPS #0 ; nay, lower priority... WAIT ; and wait for interrupts BR IDLE ; try again ; SWITCH, switch into next runnable job ; unlink first job in RUN$Q, switch into it SWITCH: MOV RUN$Q,R4 ; get first runnable job MOV R4,CURJOB ; make it the current job MOV J$NEXT(R4),RUN$Q; unlink it from the queue BNE 1$ CLR RUN$Q+2 ; clear last entry if queue empty 1$: CLR J$NEXT(R4) ; clear its forward link .LDPCTX ; load process context RTI ; return to job ; TERM, terminate job ; input: none ; output: jobs stack partition freed, job set "waiting" for TERM ; return: none ; this routine never returns as the job will not be rescheduled .MACRO .TERM .GLOBL J$TERM .CALL J$TERM .ENDM .MACRO $TERM $SYS #^RTER,#^RM .ENDM J$TERM::MOV STACK,SP ; switch to system stack MOV CURJOB,R4 ; get current job .CALL EVKILL,R4 ; free this jobs event blocks TST J$STCK(R4) ; does this job have a stack partition? BEQ 1$ ; nay, so don't free it .M$FREE J$STCK(R4) ; free stack partition 1$: MOV #^RTER,J$WAIT(R4) ; this job is terminated MOV #^RM,J$WAIT+2(R4) CLR CURJOB ; no current job JMP IDLE ; go to idle loop ; EXIT, exit job ; input: none ; output: job partitions returned to system, set "waiting" for EXIT ; return: none ; this routine never returns as the job will not be rescheduled .MACRO .EXIT .GLOBL J$EXIT .CALL J$EXIT .ENDM .MACRO $EXIT $SYS #^REXI,#^RT .ENDM J$EXIT::MOV STACK,SP ; switch to system stack MOV CURJOB,R4 ; get current job .CALL EVKILL,R4 ; kill its event blocks .M$KILL R4 ; kill its memory partitons, this will also ; take care of I/O channels and the pool, which ; is were any still active ASYNs were allocated ; from CLR J$PART(R4) ; no partition CLR J$POOL(R4) ; no pool CLR J$IOCH(R4) ; no I/O channels MOV #^REXI,J$WAIT(R4) ; this job has exited MOV #^RT,J$WAIT+2(R4) CLR CURJOB ; no current job JMP IDLE ; go to idle loop ; CRASH, crash the job ; input: none ; output: job set "waiting" for CRASH ; return: none ; this routine will never return as the job will not be rescheduled .MACRO .CRASH .GLOBL J$CRSH .CALL J$CRSH .ENDM .MACRO $CRASH $SYS #^RCRA,#^RSH .ENDM J$CRSH::MOV STACK,SP ; switch to system stack MOV CURJOB,R4 ; get current job BEQ 2$ ; no current job, system crash .CALL EVKILL,R4 ; kill this jobs event blocks MOV #^RCRA,J$WAIT(R4) ; wait for CRASH MOV #^RSH,J$WAIT+2(R4) CLR CURJOB ; no current job JMP IDLE ; go to idle loop 2$: .SYSPUT #CRASH ; print message HALT ; halt system .PSECT DATA CURJOB::.BLKW 1 ; pointer to current job RUN$Q:: .BLKW 1 ; pointer to first runnable job .BLKW 1 ; pointer to last runnable job SYSJOB::.BLKW SIZE$J ; job structure for SYSJOB STACK:: .BLKW 1 ; storage for system stack pointer JOBTBL::.BLKW NJOB ; job table WAITBL::.BLKW TBLSIZ ; hash table for waiting jobs EVNTBL::.BLKW TBLSIZ ; hash table for events .PSECT CONST UDLINE: .ASCII /--------------------------------------------------------------/ CRLF: .ASCIZ <15><12> HDLINE: .ASCIZ ZNAME WAIT TIME-H TIME-L MEMORY I/O POOL STACKZ<15><12> CRASH: .ASCIZ /SYSTEM CRASHED/<7> .END