INSERT[MesaPDefs];
:TITLE[MesaP];
%
Ed Fiala 11 May 1982: Fix bug that PSB not refetched after ExitM2
call by MXWait.
Ed Fiala 2 February 1982: rearranged RSSaveStack to avoid
PStore4 after tasking and RSLoadAndFreeState; fix noninterlocked
overwrite of PFetch4 at RSSaveProcess.
Ed Fiala 22 September 1981: P4Ret edits; Idle loop change; minimal stack error check bugfix; bum 3 mi.
Ed Fiala 5 May 1981: Bum 1 mi; move prTime and prTicks out
of RM 0-77 by expending 6 mi.
Ed Fiala 21 April 1981 Bum 14b mi.
Ev Neely March 26, 1981 4:31 PM Add test of condition.abortable to MXW.
Ev Neely March 24, 1981 10:36 AM Added tasking.
Ed Fiala 23 March 1981 Change xfBrkByte value.
Jim Frandeen February 21, 1981 4:37 PM Add Calls and replace some Tasks with calls to fix All Memory Task Gotcha (number 17).
Ev Neely February 19, 1981 12:42 PM Moved definitions to MesaPDefs.mc because MesaP.mc has gotten to large for bravo editing.
Ev Neely February 13, 1981 7:18 AM Handle nil source for Requeue Op.
Ev Neely January 30, 1981 12:21 PM Big Stack.
Ev Neely January 28, 1981 8:56 AM Fix AR 6958 & co.
Ev Neely January 14, 1981 10:02 AM Fault Notification .
Ev Neely December 18, 1980 4:57 PM Change Reschedule to use StateAllocTable. Set PSB.waiting in Wait and clear it in WakeHead and TimeoutScan.
Jim Frandeen October 20, 1980 11:06 AM Fix timeout. Check for minimal stack. Fix trap bug caused by not saving state after every requeue.
Johnsson October 10, 1980 6:01 PM fixup trap code
Johnsson September 29, 1980 10:53 AM fixup trap code
Jim Frandeen September 22, 1980 4:32 PM Remove Task before PStore4 at RequeueExit just in case another WorstGotcha appears.
Jim Frandeen September 17, 1980 10:09 AM
Jim Frandeen July 16, 1980 2:11 PM Created from Jim Sandman’s original MesaP to change the PSB format.
%
@ME:
*Monitor Entry. Stack contains long or short pointer to Monitor.
prFlags ← MEFlags, GoTo[LongOrShortMonitor], OpCode[1];
@MRE:
*Monitor Re-Entry. Stack contains long or short pointer to Condition and long or short pointer to Monitor.
prFlags ← MREFlags, GoTo[CheckLongOrShort2], OpCode[2];
P4PopToT: T ← Stack&-1, Return;
@MXW:
*Monitor Exit and Wait. Stack contains time, long or short pointer to Condition, and long or short pointer to Monitor.
prFlags ← MXWFlags, Call[P4PopToT], OpCode[3];
prTimeout ← T, GoTo[CheckLongOrShort2];
@MXD:
*Monitor Exit and Depart. Stack contains long or short pointer to Monitor.
prFlags ← MXDFlags, GoTo[LongOrShortMonitor], OpCode[4];
@NOTIFY:
*Notify. Stack contains long or short pointer to Condition.
prFlags ← NOTIFYFlags, GoTo[LongOrShortCondition], OpCode[5];
@BCAST:
*Broadcast. Stack contains long or short pointer to Condition.
prFlags ← BCASTFlags, OpCode[6];
LongOrShortCondition:
T ← 375C, Call[FixConditionQueue];*Check for stack pointer = 2.
prConditionQ ← T, Call[GetCondition];
PStore1[prConditionQ,prCondition,0];*Trigger write protect fault if protected
ProcessOps:
T ← 377C;
LU ← (NStkP) XOR T;*Check for minimal stack.
GoTo[StackEmpty,ALU=0];*IF stack is empty
LoadPageExternal[FaultPage];
GoToExternal[StackErrorLoc];*Stack error KFCB trap
StackEmpty:
LoadPage[prPage], T ← prCurrentPsb;*Prepare to fetch CurrentPsb.
Dispatch[prFlags,5,4], GoTo[ProcessDispatch];
P4Ret:Return;
@REQUEUE:
*Requeue. Stack contains a Psb index, a long or short pointer to a source queue, and a long or short pointer to a destination queue. Put the source queue in MonitorQueue, destination queue in ConditionQueue.
prFlags ← REQFlags, Call[P4PopToT], OpCode[7];
prPsbIndex ← T;
CheckLongOrShort2:
T ← 373C, Call[FixConditionQueue];*Check for stack pointer = 4.
prConditionQ ← T, Call[GetCondition];
PStore1[prConditionQ,prCondition,0];*Trigger write protect fault if protected.
LongOrShortMonitor:
T ← 375C;*Check for stack pointer = 2.
LU ← (NStkP) XOR T, Call[FixMonitor];*Check for short or long pointer.
LU ← (prMonitorQhi) OR T;*Check for nil long pointer.
prMonitorQ ← T, GoTo[MonitorFetch,ALU#0];
*Come here when MonitorQ is nil. If RequeueOp, treat as nil source and don’t fetch Monitor; otherwise allow address fault.
LU ← (prFlags) XOR (REQFlags);*Check for RequeueOp.
prFlags ← (prFlags) OR (SourceQNil),
GoTo[ProcessOps,ALU=0];*Don’t fetch if RequeueOp
Nop;*Needed because of multiple conditonal gotos. No need for speed as will fault anyway.
MonitorFetch:
*If the Monitor is write protected the following instruction pair will cause a write protect fault before changes to the stack or memory make it unrecoverable.
PFetch1[prMonitorQ,prMonitor,0], Call[P4Ret];
PStore1[prMonitorQ,prMonitor,0], GoTo[ProcessOps];*OK fetch it for real. Watch out for Gotcha#1.
FixConditionQueue:
LU ← (NStkP) XOR T;*Check for short or long pointer.
LU ← LdF[Stack,0,12], Skip[ALU=0];*Check for map out of bounds.
T ← MDShi, GoTo[FixShortConditionQueue];
*All we use ConditionQ for is to fetch and store one word, so we don’t need to create a perfect base register.
T ← LSh[Stack&-1,10], Skip[ALU=0];*Long pointer
T ← (Zero) - 1;*Cause map out of bounds.
FixShortConditionQueue:
prConditionQhi ← T, GoTo[P4PopToT];
GetCondition:
PFetch1[prConditionQ,prCondition,0], GoTo[P4Ret];
FixMonitor:
LU ← LDF[Stack,0,12], Skip[ALU=0];*Check for map out of bounds.
T ← MDShi, GoTo[FixShortMonitorQueue];
*All we use MonitorQ for is to fetch and store one word, so we don’t need to create a perfect base register.
T ← LSH[Stack&-1,10], Skip[ALU=0];*Long pointer
T ← (Zero) - 1;*Cause map out of bounds.
FixShortMonitorQueue:
prMonitorQhi ← T, GoTo[P4PopToT];
OnPage[prPage];
ProcessDispatch:
PFetch4[prPda,prPsbLink], Disp[MEnter];*Fetch CurrentPsb
PrTail:
LoadPage[opPage0], GoTo[PrExit];
PrTailx:
LoadPage[opPage0], GoTo[PrExit];
PrExit:
GoTo[P4Tail];
MEnter:
%Monitor Entry is executed by an entry procedure of a Mesa monitor. It returns TRUE on the stack if the monitor was not locked; otherwise the running process is put on the monitor queue, and the stack is left empty.
Input:
MonitorQbase register pair points to Monitor
Monitorcontains MonitorQ↑
PsbLinkfour-word buffer contains CurrentPsb
Exits:
PrTailif enter successful
EntryFailedif enter not successful
%
GoTo[EntryFailed,R Odd],*Monitor.locked THEN GoTo EntryFailed
prMonitor ← (prMonitor) OR (monitorLock),*Monitor.lock ← locked
AT[ProcessDisp,MEloc];
PushTrue:
Stack&+1 ← 1C;*Push[TRUE].
LockMonitor&Exit:
PStore1[prMonitorQ,prMonitor,0],*Store Monitor
GoTo[PrTail];*No need to Call ReSchedule because no requeue has occurred.
EntryFailed:
%Come here if monitor is already locked. Set EnterFailed. Move the current process from the ready queue to the monitor lock queue. PsbLink will be updated by Requeue when the Psb is stored at exit.
Exit:
RequeueOp
%
prPsbLink ← (prPsbLink) OR (EnterFailed);*CurrentPsb.link.enterFailed ← TRUE.
EntryFailedx:
T ← prCurrentPsb;*Prepare to requeue CurrentPsb.
prPsbIndex ← T, GoTo[Requeue&ReSchedule];*Dequeue the CurrentPsb from the ReadyQ.
MREnter:
%Monitor Re-Entry is used to re-enter a monitor which a process has exited in order to wait on a condition variable queue. If the monitor is locked, the process is placed on the monitor lock queue as in the ME instruction.
Input:
MonitorQbase register pair points to Monitor
Monitorcontains MonitorQ↑
ConditionQbase register pair points to Condition
Conditioncontains ConditionQ↑
PsbLinkfour-word buffer contains CurrentPsb
Subroutines called:
CleanUpQueueif enter successful
Exits:
PrTailif enter successful
EntryFailedif enter not successful
PRTrapif abortPending and abortable
%
GoTo[ReEntryFailed,R Odd],*THEN GoTo ReEntryFailed
prMonitor ← (prMonitor) OR (monitorLock),*Monitor.lock ← locked
*IF monitor.locked
AT[ProcessDisp,MREloc];
NOP;*Placement constraint
UseCTask, Call[CleanUpQueue];* Clean up the condition queue.
prPsbFlags ← (prPsbFlags) AND NOT T,*Return with PsbIndexMask in T.
Call[StoreCurrentPsb];*Zero Flags.CleanupLink.
LU ← (prPsbFlags) AND (AbortPending);*If ~flags.abortPending
LU ← (prCondition) AND (abortable),
Skip[ALU#0];
Stack&+1 ← 1C, GoTo[LockMonitor&Exit];*THEN {Push[TRUE];exit}. No need to Call ReSchedule because no requeue has occurred.
T ← (sProcessTrap),*If condition.abortable
GoTo[PRTrap,ALU#0];*THEN trap.
Stack&+1 ← 1C,*Push[TRUE];
GoTo[LockMonitor&Exit];*and exit
PRTrap:
RTemp ← T, LoadPage[opPage0];
T ← SStkp, GoToP[BackSPPCandTrap];
ReEntryFailed:
prPsbLink ← (prPsbLink) OR (EnterFailed),
GoTo[EntryFailedx];
MXDepart:
%Monitor Exit and Depart is executed at the end of an entry procedure of a Mesa monitor. It unlocks the monitor and if the monitor queue is non-empty its first (i.e., highest priority) entry is moved to the ready queue.
Input:
MonitorQbase register pair points to Monitor
Monitorcontains MonitorQ↑
PsbLinkfour-word buffer contains CurrentPsb
Subroutines called:
ExitMon
Exits:
ReSchedule
%
UseCTask, Call[ExitMon], AT[ProcessDisp, MXDloc];
LU ← (prFlags) AND (RequeueOccurred),
GoTo[ReScheduleIfRequeueOccurred];
ExitMon:
%ExitMonitor subroutine.
Input:
MonitorQbase register pair points to Monitor
Monitorcontains MonitorQ↑
PsbLinkfour-word buffer contains CurrentPsb
Output:
PsbIndexPsbIndex pointed to by Monitor.tail
PsbLinkfour-word buffer contains Psb to be requeued from MonitorQ to ReadyQ.
Subroutines called:
RequeueSub
Exits:
Return to caller
%
T ← APC&APCTask, Call[SaveReturnInReturnLink];
prMonitor ← (prMonitor) AND NOT (monitorLock);*Monitor.lock ← unlocked.
PStore1[prMonitorQ,prMonitor,0],
Call[TGetsPsbIndexMask];
T ← (prMonitor) AND T;*T ← monitor.tail.
PFetch1[prPda,prPsbIndex], *IF monitor.tail # nil THEN fetch monitor.tail↑.
GoTo[RequeueMonitorQtoReadyQ, ALU#0];
APC&APCTask ← prReturnLink,
GoTo[PrRet];*IF monitor.tail = nil THEN exit.
MXWait:
%Monitor Exit and Wait is executed within a monitor when it is desirable to wait on a condition variable. It unlocks the monitor, and if the monitor queue is non-empty, its first entry is moved to the ready queue. If there is not a wakeup waiting or an abort pending, then the process executing the MXW is moved to the condition variable queue; otherwise it remains on the ready list. Timeout contains timeout.
Input:
MonitorQbase register pair points to Monitor
Monitorcontains MonitorQ↑
ConditionQbase register pair points to Condition
Conditioncontains ConditionQ↑
Timeoutcontains time out value. This is the same register as WW.
PsbLinkfour-word buffer contains CurrentPsb
Output:
PsbIndexCurrentPsb for Requeue
PsbLinkfour-word buffer contains CurrentPsb
Subroutines called:
CleanUpQueue
ExitMon
Exits:
ReSchedule
%
UseCTask, Call[CleanUpQueue], AT[ProcessDisp, MXWloc];
UseCTask, Call[ExitMon];
*Refetch the CurrentPsb since ExitMon may have called Requeue and wiped out the previous contents.
T ← prCurrentPsb;
PFetch4[prPda,prPsbLink], Call[PrRet];
LU ← (prPsbFlags) AND (AbortPending);
LU ← (prCondition) AND (abortable),
GoTo[MXWait1,ALU = 0];*If ~abortPending
GoTo[MXWAbort,ALU#0];*Goto MXWAbort when PsbFlags.abortPending and Condition.abortable.
NOP;*Placement Constraint
MXWait1:
prCondition ← (prCondition) AND NOT (wakeup),*Condition.wakeup ← FALSE.
GoTo[MonitorOntoCV, R Even];*IF prior ~condition.wakeup, GoTo MonitorOntoCV ELSE
PStore1[prConditionQ, prCondition, 0],*Store condition.
GoTo[MXWExit];
MonitorOntoCV:
prPsbFlags ← (prPsbFlags) OR (waiting);*Psb.waiting ← TRUE.
prPsbIndex ← T;*Set up PsbIndex for Requeue.
T ← prTimeout;*Put timeout in Psb.
prPsbTimeout ← T, GoTo[ZeroTimeout,ALU=0];*IF Timeout = 0
prSaveStkP ← IP[prTicks]C, Call[prStkPSave];*Use StkP to address prTicks (outside RM 0-77)
T ← Stack, Call[prStkPSwap];*Restore StkP
prPsbTimeout ← (prPsbTimeout) + T;*Timeout ← MAX[1, Ticks + Timeout]
prFlags ← RHMask[prFlags],*Save Requeue bit
GoTo[RequeueToConditionQ,ALU#0];
prPsbTimeout ← 1C, GoTo[RequeueToConditionQ];
prStkPSave:
T ← (SStkP&NStkP) xor (377C);
prStkPSwap:
prSaveStkP ← T, StkP ← prSaveStkP, NoRegILockOK, Return;
ZeroTimeout:
prFlags ← RHMask[prFlags];
*PsbTimeout will be updated when the entire Psb is stored at exit from Requeue. Set up prFlags so we will requeue from the ReadyQ to the ConditionQ. Initially, we were set up to move from the MonitorQ to the ReadyQ.
RequeueToConditionQ:
prFlags ← (prFlags) OR (RequeueReadyQtoConditionQ);
Requeue&ReSchedule:
UseCTask, Call[RequeueSubPsbFetched];
T ← prCurrentPsb, GoTo[ReSchedule];
MXWExit:
MXWAbort:
LU ← (prFlags) AND (RequeueOccurred),
GoTo[ReScheduleIfRequeueOccurred];
NotifyBroadcast:
%Notify moves the first entry of a condition variable queue to the ready queue. Broadcast moves all entries of a condition variable queue to the ready queue.
Input:
ConditionQbase register pair points to Condition
Conditioncontains ConditionQ↑
PsbLinkfour-word buffer contains CurrentPsb
Exits:
ReSchedule
%
UseCTask, Call[CleanUpQueue], AT[ProcessDisp, NotifyBroadcastLoc];
T ← prCondition ← (prCondition) AND T,
GoTo[NotifyTestCondition];
NotifyLoop:
PFetch1[prConditionQ,prCondition,0],
Call[TGetsPsbIndexMask];
T ← prCondition ← (prCondition) AND T;
NotifyTestCondition:
GoTo[NotifyExit,ALU=0];*IF condition.tail = nil Then Exit
UseCTask, Call[WakeHead];
LU ← (prFlags) AND (RequeueOccurred),
DblGoTo[NotifyLoop,ReScheduleIfRequeueOccurred,R<0];*Loop IF broadcast
NotifyExit:
LU ← (prFlags) AND (RequeueOccurred),
GoTo[ReScheduleIfRequeueOccurred];
WakeHead:
%Move the first Psb on ConditionQueue to the ready queue.
WakeHead is called:
1. by Broadcast or Notify.
2. by Interrupt processing when an I/O controller has set a bit in NWW that corresponds to a condition in Pda.Interrupt.
Input:
ConditionQbase register pair points to Condition
Conditioncontains ConditionQ↑ not nil
Output:
PsbIndexCondition.tail↑ for Requeue
PsbLinkfour-word buffer contains Psb pointed to by PsbIndex
Subroutines called:
RequeueSub
Exits:
Return to caller from RequeueSub
%
T ← APC&APCTask,
Call[SaveReturnInReturnLink];*Returns with PsbIndexMask in T.
T ← (prCondition) AND T;
PFetch1[prPda,prPsbIndex], *Fetch condition.tail↑ into PsbIndex for Requeue.
Call[TGetsPsbIndexMask];
T ← prPsbIndex ← (prPsbIndex) AND T,
Call[FetchPsb];*Fetch PsbIndex↑ into 4-word Psb buffer.
prPsbFlags ← (prPsbFlags) AND NOT (waiting);*Psb.waiting ← FALSE.
prPsbTimeout ← 0C,*Zero timeout
GoTo[RequeuePsbFetched];*Timeout will be updated when Psb is stored at exit from Requeue.
RequeueOp:
%Requeue, given a PsbIndex two queue pointers, moves the process from the source queue and inserts it according to priority into the destination queue.
Input:
ConditionQbase register pair points to Dest queue
MonitorQbase register pair points to Source queue
PsbIndexpoints to Psb to be requeued
Exits:
ReSchedule
%
UseCTask, Call[RequeueSub], AT[ProcessDisp, REQloc];
T ← prCurrentPsb, GoTo[ReSchedule];
%Requeue is called:
1. by EnterFail to move a Psb from the ready queue to a monitor queue.
2. by ExitMon: if the monitor queue is not empty, to move a Psb from the monitor queue to the ready queue.
3. by Exit&Wait to move a Psb from the ready queue to a condition queue.
4. by WakeHead to move a Psb from a condition queue to the ready queue. WakeHead may have been called by Interrupt.
5. by TimeOut to move a Psb from an unknown condition queue (the source queue points to the first condition in the ConditionVector of the PDA) to the ready queue.
6. by RequeueOp to move a Psb from the source queue (we use MonitorQ) to the dest queue (we use ConditionQ).
Input:
prFlagsindicates source and dest queues
PsbIndexPsbIndex of Psb to be requeued
Output:
prFlagsset to indicate requeue occurred
ReadyContains ReadyQueue↑
Temps:
PsbLink4-word buffer contains PsbIndex↑
PsbLink.nextNext link of PsbLink
PrevPsbIndexPoints to previous Psb
PrevPsbLinkPrevPsbIndex↑.
PsbPriorityPriority of Psb to be requeued
QTempBase register pair used to reference source and dest queues
QueueContains QTemp↑
NextPsbUsed by Enqueue to chain Psb according to priority
Exits:
Return to caller
%
RequeueMonitorQtoReadyQ:
T ← prPsbIndexMask, GoTo[Requeue];*Come here from ExitMon.
RequeueSub:
T ← APC&APCTask, Call[SaveReturnInReturnLink];*Returns with PsbIndexMask in T
Requeue:
prPsbIndex ← T ← (prPsbIndex) AND T,
Call[FetchPsb];*Fetch PsbIndex↑ into PsbLink 4-word buffer.
GoTo[RequeuePsbFetched];
RequeueSubPsbFetched:
T ← APC&APCTask, Call[SaveReturnInReturnLink];*Returns with PsbIndexMask in T
RequeuePsbFetched:
Dispatch[prFlags,SourceQbit,2],
Call[SetQTemp];*Set QTemp to SourceQ. Returns with PsbIndexMask in T.
*First, traverse the source queue looking for the Psb immediately before the Psb pointed to by PsbIndex so that this Psb can be bypassed in the linked queue structure. This starts with the last Psb of a queue since the process instructions always remove the first Psb and the last entry always points to the first.
T ← LDF[prPsbLink,1,3];*Load PsbLink.priority
prPsbPriority ← T,*Save priority of Psb to be requeued.
Call[TGetsPsbIndexMask];
*See if Psb to be requeued points to itself. If so, we won’t need to unchain it from the previous Psb.
T ← (prPsbLink) AND T;*T ← PsbLink.next
prPsbLink.next ← T;
LU ← (prPsbIndex) - T;*IF prPsbIndex = PsbLink.next
LU ← (prFlags) AND (SourceQNil), Skip[ALU#0];
prPrevPsbIndex ← Zero, GoTo[SetCleanupLink];*THEN prev ← nil. We won’t need to unchain Psb.
*Continue if the Psb does not point to itself. We must remove it from the source queue. Search for the preceding Psb in the queue. When we are through, PrevPsbIndex will point to the Psb preceding the Psb we wish to dequeue, and PrevPsbLink will contain the Link word of the preceding Psb. Start our search from the tail of the source queue if we know what queue we are on; otherwise search from the Psb being dequeued.
prPrevPsbIndex ← T,*IF source queue nil THEN
GoTo[FetchPrev, ALU # 0];*PrevPsbIndex ← Link.next
PFetch1[prQTemp,prQueue,0];*Fetch SourceQ↑ into Queue.
T ← prPsbIndexMask;*ELSE PrevLink ← queue.tail
T ← (prQueue) AND T;
FindPrev:
prPrevPsbIndex ← T;
FetchPrev:
PFetch1[prPda,prPrevPsbLink],*Fetch PrevPsbIndex↑ into PrevPsbLink.
Call[TGetsPsbIndexMask];
T ← (prPrevPsbLink) AND T;
LU ← (prPsbIndex) - T;*IF PrevPsbLink.next # PsbIndex
GoTo[FindPrev, ALU#0];*THEN prPrevPsbIndex ← PrevPsbLink.next
*Continue when we have found the preceding Psb. Move the next pointer of the Psb being dequeued to the next pointer of the previous Psb.
prPrevPsbLink ← (prPrevPsbLink) AND NOT T;*PrevPsbLink.next ← 0
T ← prPsbLink.next,*T ← PsbLink.next
Call[PrevLinkOrT];*PrevPsbLink.next ← PsbLink.next
T ← prPrevPsbIndex, TASK;
PStore1[prPda,prPrevPsbLink];*Store PrevPsbLink.
SetCleanupLink:
*Now we have removed the Psb pointed to by PsbIndex from the source queue. If we don’t know what queue we are on, set the cleanup link.
PRFlags, GoTo[SourceNotNil, R Even];
*Continue if source is nil. We don’t know what queue we are on, so we must set the cleanup link. Take PsbLink.next (of the psb being dequeued) and put it in the cleanup link.
T ← prPsbIndexMask;
prPsbFlags ← (prPsbFlags) AND NOT T;*Flags.cleanup ← 0
T ← prPsbLink.next;*Flags.cleanup ← PsbLink.next
prPsbFlags ← (prPsbFlags) OR T, GoTo[Enqueue];
SourceNotNil:
*Come here if the source queue is not nil. We know what queue we are on. If SourceQueue points to the Psb being dequeued, change it to point to the previous Psb.
PFetch1[prQTemp,prQueue,0];*Fetch SourceQ↑ into Queue.
T ← prPsbIndexMask;
T ← (prQueue) AND T;*T ← SourceQ.tail
LU ← (prPsbIndex) - T;*IF SourceQ.tail # PsbIndex
prQueue ← (prQueue) AND NOT T,
GoTo[Enqueue,ALU#0];*THEN GoTo Enqueue; queue.tail ← 0
T ← prPrevPsbIndex;
prQueue ← (prQueue) OR T;*Queue.tail ← PrevPsbIndex
PStore1[prQTemp,prQueue,0];*Store Queue.tail
Enqueue:
*Now we are ready to insert the Psb into the dest queue.
Dispatch[prFlags,DestQbit,2],
Call[SetQTemp];*Set QTemp to DestQ
PFetch1[prQTemp,prQueue,0],
Call[TGetsPsbIndexMask];
T ← (prQueue) AND T;*T ← DestQueue.tail
prPrevPsbIndex ← T,
GoTo[DestQueueNotNil,ALU#0];*IF DestQueue.tail # nil THEN GoTo RQDestQueueNotNil
*Continue if dest queue is nil. Set the Psb being enqueued to point to itself. Then set DestQ to point to the Psb being enqueued.
T ← prPsbIndexMask;
prPsbLink ← (prPsbLink) AND NOT T;*PsbLink.next ← 0
T ← prPsbIndex;
prPsbLink ← (prPsbLink) OR T;*PsbLink.next ← PsbIndex
T ← prPsbIndexMask, Call[MakeDestQueuePointToPsb];
PFetch1[prPda,prReady,pdaReady!];*Fetch ReadyQ↑ into Ready.
RequeueExit:
prFlags ← (prFlags) OR (RequeueOccurred);
T ← prPsbIndex;
Pstore4[prPda, prPsbLink],*Update Psb
GoTo[ReturnLinkRet];
DestQueueNotNil:
*Dest queue is not nil. PrevPsbIndex and T contain DestQueue.tail. We must insert the Psb into the DestQueue according to priority. First see if the priority of the Psb is less than or equal to the priority of the last Psb in the DestQueue. If so, we can insert the Psb at the end of the DestQueue.
Pfetch1[prPda,prPrevPsbLink], Call[PrRet];*Fetch DestQueue.tail↑ into PrevPsbLink.
T ← LDF[prPrevPsbLink,1,3];*T = PrevPsbLink.priority
LU ← (prPsbPriority) - T - 1;*IF PrevPsbLink.priority >= Psb.priority
GoTo[ChainToEndOfQueue,ALU < 0],*THEN GoTo ChainToEndOfQueue
T ← prPsbIndexMask;
*Continue if priority of Psb being enqueued is greater than or equal to priority of the Psb at the end of the queue. This means we cannot add it to the end of the queue. We will need to search through the queue and find a place to insert it.
T ← (prPrevPsbLink) AND T;*T ← PrevPsbLink.next
InsertByPriority:
PFetch1[prPda,prNextPsbLink],Call[PrRet];*Fetch PrevPsbLink.next↑ into NextPsbLink
T ← LDF[prNextPsbLink,1,3];*T ← NextPsbLink.priority
LU ← (prPsbPriority) - T - 1;*IF Psb.priority > NextPsbLink.priority
T ← prPsbIndexMask,
GoTo[InsertPsb, ALU>=0];*THEN GoTo InsertPsb
T ← (prPrevPsbLink) AND T;*T ← PrevPsbLink.next
prPrevPsbIndex ← T;
T ← prNextPsbLink;
prPrevPsbLink ← T;
T ← (prPsbIndexMask) AND T, GoTo[InsertByPriority];
ChainToEndOfQueue:
*Come here the priority of the Psb to be enqueued is less than or equal to the priority of the last item in the queue. Change the queue head to point to the Psb being enqueued. We chain the new Psb onto the end of the queue.
prQueue ← (prQueue) AND NOT T,*DestQueue.tail ← 0
Call[MakeDestQueuePointToPsbx];
InsertPsb:
*Come here when the priority of the Psb to be inserted is greater than the priority of the Psb pointed to by PrevPsbLink.next. Move PrevLink.next to the next field of the Psb being inserted. Change PrevLink.next to point to the Psb being inserted.
prPsbLink ← (prPsbLink) AND NOT T;*PsbLink.next ← 0
T ← (prPrevPsbLink) AND T;*T ← PrevPsbLink.next
prPsbLink ← (prPsbLink) OR T;*PsbLink.next ← PrevPsbLink.next
PFetch1[prPda,prReady,pdaReady!];*Fetch ReadyQ↑ into Ready in preparation for exit.
*Now set PrevLink.next to point to Psb being enqueued.
T ← prPsbIndexMask,
Call[PrevLinkAndNotT];*PrevPsbLink.next ← 0
T ← prPsbIndex,
Call[PrevLinkOrT];*PrevPsbLink.next ← PsbIndex
T ← prPrevPsbIndex;
PStore1[prPda,prPrevPsbLink],*Store PrevPsbLink
GoTo[RequeueExit];
MakeDestQueuePointToPsb:
*Queue contains DestQueue↑, T contains PsbIndexMask. Change DestQueue.tail to point to Psb.
prQueue ← (prQueue) AND NOT T;*DestQueue.tail ← 0
MakeDestQueuePointToPsbx:
T ← prPsbIndex;
prQueue ← (prQueue) OR T;
PStore1[prQTemp,prQueue,0], GoTo[TGetsPsbIndexMask];
SetQTemp:
prQTemp ← 0C, Disp[.+1];
prQTempHi ← 400C, GoTo[TGetsPsbIndexMask], AT[QueueDisp,ReadyQbits];
T ← prMonitorQ, AT[QueueDisp,MonitorQbits];
prQTemp ← T;
T ← prMonitorQhi, GoTo[SetQTempHi];
T ← prConditionQ, AT[QueueDisp,ConditionQbits];
prQTemp ← T;
T ← prConditionQhi;
SetQTempHi:
prQTempHi ← T, GoTo[TGetsPsbIndexMask];
CleanUpQueue:
%This procedure must be invoked before accessing a condition variable queue since the queue pointer may not be correct. This occurs when the last Psb of a CV queue has been moved to another queue and Requeue did not know which CV queue the Psb belonged to. (This happens when a process times out on a CV queue or is aborted.) When Requeue detects this situation, it causes the Psb’s cleanup link to have the value of the Psb’s old link field -- which should point to the front of the queue. The goal of this subroutine is to find the correct head of the CV queue, and therefore the tail -- to which the CV should point to. We follow the cleanup links til there are no more, declare the last Psb as the head, and then follow the usual links until the tail is found.
Input:
ConditionQbase register pair points to Condition
Conditioncontains ConditionQ↑
Output:
Conditioncontains updated ConditionQ↑
Tcontains PsbIndexMask
Temps:Temps are shared with Requeue Temps
CleanupLinkused to fetch and store CleanupLink
ConditionTailcontains Condition.tail
QueueHeadcontains new head of condition queue
QueueTailcontains new tail of condition queue
Exits:
Return to caller
%
T ← APC&APCTask,
Call[SaveReturnInReturnLink];*Returns with PsbIndexMask in T.
T ← (prCondition) AND T;*T ← condition.tail
prConditionTail ← T,*Save condition.tail
GoTo[ConditionQueueEmpty, ALU=0];
T ← (prConditionTail) + (psbFlags),
Call[FetchCleanupLink];*Returns with PsbIndexMask in T.
T ← (prCleanupLink) AND T;*IF flags.cleanup = NIL THEN
GoTo[CleanupLinkEmpty, ALU=0],*RETURN
LU ← (prConditionTail) - T;*IF Condition.tail = flags.cleanup
GoTo[RemoveSolePsb, ALU=0],*THEN GoTo RemoveSolePsb
prQueueHead ← T;*QueueHead ← flags.cleanup
FindCleanupHead:
*Search through the chain until we find a cleanup link that is nil. We then declare this to be the head of the queue. If we find a cleanup link that points to the tail of the queue, then the queue is now empty.
T ← (prQueueHead) + (psbFlags),
Call[FetchCleanupLink];*Fetch flags word of the next entry in the cleanup link chain.
*T = PsbIndexMask
T ← (prCleanupLink) AND T;*IF flags.cleanup = NIL THEN
GoTo[FoundHead, ALU=0],*THEN GoTo FoundHead
LU ← (prQueueHead) - T;*IF flags.cleanup = QueueHead
GoTo[RemoveAllPsbs, ALU = 0];*THEN GoTo RemoveAllPsbs
prQueueHead ← T, GoTo[FindCleanupHead];
FoundHead:
*Come here when QueueHead points to the head of the Psb chain.
T ← MNBR ← prQueueHead;*MNBR ← head of queue
FindQueueTail:
prQueueTail ← T, Call[FetchCleanupLink];*Fetch next link
prCondition ← (prCondition) AND NOT T;*condition.tail ← nil
T ← (prCleanupLink) AND T;*T ← Link.next
LU ← (MNBR) - T;*IF Link.next # head
GoTo[FindQueueTail, ALU#0];
prCondition ← (prCondition) OR T,*Set condition.tail
GoTo[UpdateConditionQueue];
RemoveSolePsb:
*Come here if the cleanup link in the Psb points to itself. There was only one Psb in the queue, and we must remove it. We make the queue empty.
prCondition ← (prCondition) AND (abortable),*condition.wakeup ← FALSE. condition.tail ← nil
GoTo[UpdateConditionQueue];
RemoveAllPsbs:
*Come here if the cleanup link in the Psb points to the last Psb in the Queue. We make the queue empty.
prCondition ← (prCondition) AND (abortable);*condition.wakeup ← FALSE. condition.tail ← nil
UpdateConditionQueue:
PStore1[prConditionQ,prCondition,0];
ReturnLinkRet:
ConditionQueueEmpty:
APC&APCTask ← prReturnLink, GoTo[TGetsPsbIndexMask];
CleanupLinkEmpty:
APC&APCTask ← prReturnLink, GoTo[TGetsPsbIndexMask];
FetchCleanupLink:
PFetch1[prPda,prCleanupLink],
GoTo[TGetsPsbIndexMask];*Return with PsbIndexMask in T.
%ReSchedule takes the first (i.e., highest priority) Psb of the ready queue and configures the machine to run that process. The scheduler is always invoked if Requeue has moved some process from or to the ready queue. A process can run until it fails to enter a monitor, waits on a conditon variable with no wakeups waiting, aborts, or an I/O interrupt causes a higher priority process to move to the front of the ready queue.
Input:
Readycontains ReadyQueue↑ if RequeueSub did a requeue.
LUprFlags AND RequeueOccurred is pending
LOCALpoints to local frame
Output:
CurrentPsbupdated to point to head of ready queue
xfMXdest link for Xfer
xfMYset to zero for Xfer
xfBrkByteset to 40400b for Xfer
MemStatset to NoPushSD for Xfer
Temps:The following are shared with RequeueSub temps.
PsbLink
StateBase register pair used to reference Pda.state
StackPointerused to save stack pointer in SV
Frameused to save frame in SV
Subroutines called:
SavPCInFrame
Exits:
PrTailif no reschedule necessary
Xferif reschedule occurred
Kfcrif wakeup error
%
ReScheduleIfRequeueOccurred:
GoTo[PrTailx, ALU=0],*Enter here to reschedule only if requeue occurred.
T ← prCurrentPsb;
ReSchedule:
*Always come here with T just set to prCurrentPsb.
prStateHi ← 400C,*Set up StateHi.
GoTo[RSFindRunnable,ALU=0];*prCurrentPsb = 0 means not running. If not running, don’t save process.
RSSaveProcess:
*Note that it is always necessary to save a running process, because its priority may have been changed.
PFetch4[prPda,prPsbLink];*Fetch the running Psb into prPsbLink, prPsbFlags, prPsbContext, prPsbTimeout
T ← LOCAL, Task;
prFrame ← T;*Prepare to save LOCAL in state vector.
LU ← (prFlags) AND (SaveStack);*See if reschedule is due to an interrupt or fault.
prPsbContext ← (Zero) or T,*Prepare to save LOCAL in Psb; interlock PFetch4.
GoTo[RSSavePsb,ALU=0];*Need only save the Psb when not preempted.
RSAllocState:
prPsbLink ← (prPsbLink) OR (vector);*Link.vector ← TRUE
T ← (pdaState), Call[SetState];*Uses the current priority to return with: (1) T ← prCurrentSAT ← ptr to the StateAllocTable entry. (2) prPtrToSV ← ptr to the first stateVector.
T ← prPtrToSV;
prState ← T;*prPsbContext ← ptr to StateVector.
prPsbContext ← T,*prPsbContext ← ptr to StateVector.
GoTo[RSSVAvailable,ALU#0];*If there is a StateVector

*If the ptr to StateVector is 0, then no StateVector is available which is a fatal error.
RSSVError:
LoadPageExternal[0];
T ← NoStateVectorError, GoToExternal[CrashLoc];*MPC = 0726.
RSSVAvailable:
PFetch1[prPda,prPtrToSV];*Fetch ptr to next StateVector for this priority.
T ← prCurrentSAT,
Call[StorePtrToSV];*Store ptr to next StateVector in StateAllocTable.
RSSaveStack:
*Even if the stack pointer is zero, we must save at least two words of the stack, so we do one PStore4. We could do subsequent PStores only if the stack pointer > 3, but time spent testing reduces the average gain and we are tight on space.
PStore2[prState,Stack12,svStack12!], OddOK;*Store Stack12..13
T ← 377C;
T ← (NStkP) xor T;*T ← stack pointer
prStackPointer ← T;
PStore4[prState,Stack0,svStack!], NonQuadOK, Task;
*Save Stack0..3 in StateVector.
T ← svData;
PStore2[prState,prData];*Save Data in SV. Only meaningful for faults.
PStore4[prState,Stack4,svStack4!], NonQuadOK;*Store Stack4..7
PStore4[prState,Stack8,svStack8!], NonQuadOK,*Store Stack8..11
Call[PrRet];*Allow write of T.
PStore2[prState,prStackPointer,svWord!];*Save stack pointer and Local frame pointer in SV.
RSSavePsb:
LoadPage[OpPage3];
CallP[SavPCInFrame];*Save PC in local frame. NOTE: This must be done after RSSaveStack because it clobers prData. Also clobers RTemp1
Stkp ← RZero;*Stack pointer ← 0.
Call[StoreCurrentPsb];*Store prPsbLink, prPsbFlags, prPsbContext, prPsbTimeout into Current Psb.
RSFindRunnable:
PFetch1[prPda,prPsbLink,pdaReady!];*Fetch ReadyQTailPtr
T ← (prPsbIndexMask),
Call[FetchNextPsb];*Fetch Tail Psb.
T ← 0C;*To Test For Empty ReadyQ
RSFRLoop:
LU ← (prReady) XOR T;*End of ReadyQ?
GoTo[NoneReady,ALU=0];*Yes, and no runnable process found.
T ← prPsbIndexMask, Call[FetchNextPsb];*Also sets prCurrentPsb.
T ← (pdaState), Call[SetState];*Uses the current priority to return with: (1) T ← prCurrentSAT ← ptr to the StateAllocTable entry. (2) prPtrToSV ← ptr to the first stateVector.
prPsbLink ← (prPsbLink) AND NOT (vector),*Link.vector ← FALSE
GoTo[RSLoadAndFreeState, R ODD];*IF Link.vector WAS TRUE THEN load state and run this process.
*StateVector must be available at current priority to run the process.
LU ← (prPtrToSV);*StateVector available?
T ← (prCurrentPsb),
GoTo[RSFRLoop,ALU=0];*No, get next process.
*This process is runnable and doesn’t need to LoadStack.
prPsbLink ← (prPsbLink) AND NOT (EnterFailed),*Link.enterFailed ← FALSE
GoTo[RSXfer,R>=0];*If not enter failed, goto RSXfer
Stack&+1 ← 0C, *Push[FALSE]
GoTo[RSXfer];
RSLoadAndFreeState:
*Come here with T = prCurrentSAT.
***Could move LoadPage down 2 mi, saving 1 mi.
LoadPage[OpPage1];*Offload crowded prPage
PStore1[prPda,prPsbContext];*Store ptr to saved StateVector in StateAllocTable.
OnPage[OpPage1];*Offload crowded prPage
T ← prPsbContext;
prState ← T;
PFetch4[prPda,Stack0], NonQuadOK,*Load Stack0..3
Call[P5Ret];*Alow write of prState
PFetch2[prState,prStackPointer,svWord!];*Fetch stack pointer and Frame pointer.
PFetch1[prState,prPsbContext,svFrame!], Call[P5Ret];*Fetch Frame ptr again.
PFetch4[prState,Stack4,svStack4!], NonQuadOK, Call[P5Ret];*Load Stack4..7
LU ← StkP ← prStackPointer;*Load stack pointer.
PStore1[prPda,prPtrToSV], Call[P5Ret];*Store prev. content of SAT in first word of freed stateVector to add this stateVector to the chain.
PFetch4[prState,Stack8,svStack8!], NonQuadOK;*Load Stack8..11
LoadPage[prPage];
PFetch2[prState,Stack12,svStack12!], OddOK;*Load Stack12..Stack13
OnPage[prPage];
RSXfer:
LoadPage[OpPage2],T ← prPsbContext;*Frame pointer of CurrentPsb
xfMX ← T;
OnPage[OpPage2];*Offload crowded prPage
LOCAL ← T;*LOCAL ← destination link.
T ← prCurrentPsb;
PStore4[prPda,prPsbLink], Task;
MemStat ← NoPushSD;
xfBrkByte ← 40400C;
T ← xfMY ← 0C;*Source ← nil
LoadPage[xfPage1];
PCB ← 1C, GoToP[Xfer];*Set PCB to illegal value for Xfer.
OnPage[prPage];
SetState:
*Enters with T set to the Pda relative offset of the beginning of the StateAllocTable. Establishes SAT entry using the current priority from prPsbLink.
*Returns with: (1) T ← prCurrentSAT ← ptr to the StateAllocTable entry. (2) prPtrToSV ← ptr to the first stateVector or 0 if no StateVector available.
T ← (LDF[prPsbLink,priBitPos,priFldSize]) + T;*T ← ptr to the StateAllocTable entry for the current priority.
prCurrentSAT ← T;*Save it in prCurrentSAT.
PFetch1[prPda,prPtrToSV];*Fetch ptr to first StateVector this priority.
PrRet:
RETURN;
NoneReady:
LU ← (xfWDC) - 1, LoadPage[OpPage0];*Test Wakeup Disable Count
T ← prCurrentPsb ← 0C, GoToP[.+3,ALU<0];*set to not running.
OnPage[OpPage0];
LoadPage[opPage3];
T ← sWakeupError, GoToP[kfcr];*IF wdc # 0 THEN WakeError
RTemp ← T, Call[.+1];*Set up for MIPend
*Idle loop
T ← RZero, GoTo[MIPendy,IntPending];
Return;
OnPage[PrPage];
TGetsFlagsAndT:
T ← (prPsbFlags) AND T, RETURN;
TGetsPsbIndexMask:
T ← prPsbIndexMask, RETURN;
FlagsOrT:
prPsbFlags ← (prPsbFlags) OR T, RETURN;
SaveReturnInReturnLink:
prReturnLink ← T, GoTo[TGetsPsbIndexMask];*Return with PsbIndexMask in T.
FetchNextPsb:
*Enters with T set to prPsbIndexMask.
T ← (prPsbLink) AND T;
prCurrentPsb ← T, Goto[FetchPsb];
FetchCurrentPsb:
T ← prCurrentPsb;
FetchPsb:
PFetch4[prPda,prPsbLink], Goto[PrRet];
StoreCurrentPsb:
T ← prCurrentPsb;
StorePsb:
PStore4[prPda,prPsbLink], GoTo[PrRet];
StorePtrToSV:
PStore1[prPda,prPtrToSV], Goto[PrRet];
PrevLinkAndNotT:
prPrevPsbLink ← (prPrevPsbLink) AND NOT T, RETURN;
PrevLinkOrT:
prPrevPsbLink ← (prPrevPsbLink) OR T, RETURN;
OnPage[OpPage0];
prFault:
%
Come here on PageFault, WriteProtectFault or FrameFault.
Input:
TFaultOffset
prDataFault Parameter
Output:
prData(1)For PageFault and WriteProtectFault convert prData to LongPtr to faulted page (page portion a 16 bit -1 if MapOutOfBounds)
Temps:
prConditionQ(hi)Used as base-reg-ptr to Fault[fi].queue and Fault[fi].condition.
prFlagsSrcQ and DestQ spec. for RequeueSub and NotifyWakeUp.
prPsbIndexTell RequeueSub which Psb to requeue.
Subroutines called:
RequeueSub
NotifyWakeUp
Exit:
ReSchedule
%
prFlags ← FaultFlags, AT[prFaultLoc];*Indicate SrcQ and DestQ for RequeueSub.
*Set prConditionQ to Fault[fi].queue (pda + offset).
T ← (prPda) + T, Task;*Add Pda to fault offset.
prConditionQ ← T;
prConditionQhi ← 400C;*ConditionQhi is PdaHi.
T ← prCurrentPsb, LoadPage[prPage];
prPsbIndex ← T, UseCTask,
Call[RequeueSub];*Will requeue faulted process from ReadyQ to "ConditionQ" because of prFlags.
prFaultNW:
*Call NotifyWakeUp with prConditionQ pointing to Fault[fi].condition and prFlags set for requeue from ConditionQ to ReadyQ.
PFetch1[prPda,prReady,pdaReady!];*This will be set to ReadyQueue↑ if RequeueSub puts someone on the ReadyQueue.
prFlags ← InterruptFlags;*Set to requeue from ConditionQ to ReadyQ.
prConditionQ ← (prConditionQ) + 1, UseCTask,
Call[NotifyWakeUp];
LU ← (prConditionQ) XOR (qFrameFaultCOS);*Does prConditionQ = Fault[frameFault].cond.
T ← LDF[prData,0,10],
GoTo[prFaultRS, ALU=0];*Frame Fault doesn’t convert prData.
*Convert page number to long pointer in prData(1) for PageFault or WriteProtectFault.
prData1 ← T;
prData ← LSH[prData,10];
prFaultRS:
prFlags ← (prFlags) OR (SaveStack);
LoadPage[prPage];
T ← prCurrentPsb,
GoToP[ReSchedule];*Note: ReSchedule stores prData(1) in State.data.
Interrupt:
%Come here from MesaX. MesaX has determined that NWW # 0 (there are wakeups waiting), and xfWDC = 0 (wakeups are not disabled). NWW has been set to zero, and WW has been set to the previous contents of NWW. Now we must translate bits in WW into naked notifies. This results in a process being moved from a condition variable queue to the ready queue or else the wakeupWaiting bit of the condition variable is set.
Input:
WWprevious contents of NWW
Output:
ConditionQupdated to point to a Condition in the Pda
ConditionContents of ConditionQ↑
Timeupdated if timer interrupt
Ticksupdated if timer interrupt
Linkset to Condition.tail↑ for Requeue
Temps:
PsbLink4-word Psb buffer
Temp1temporary (same register as prPsbFlags).
WWwhen all levels have been checked, we use this for a temporary.
Subroutines called:
RequeueSub
Exits:
ReSchedule
%
PFetch1[prPda,prReady,pdaReady!];*This will be set to ReadyQueue↑ if RequeueSub puts someone on the ReadyQueue.
prFlags ← InterruptFlags;*Set to requeue from ConditionQ to ReadyQ.
prConditionQ ← pdaLastInterrupt;*Start with the last interrupt level.
CheckForInterrupt:
prConditionQhi ← 400C,*ConditionQhi is the same as PdaHi.
Call[CheckLevel];
CheckLevel:
WW ← RSH[WW,1], GoTo[IntThisCV,R Odd];*IF wakeup[intLevel]
GoTo[IntExit,ALU=0];*Exit if all levels checked
prConditionQ ← (prConditionQ) - (2C), Return;*Next level
IntThisCV:
*Come here if this level has an interrupt.
LU ← (prConditionQ) - (pdaInterrupt);*IF level = 0 THEN
prSaveStkP ← IP[prTime]C, GoTo[CheckForTimeouts,ALU=0];*Check for timeouts ELSE
UseCTask, Call[NotifyWakeUp];
NextLevel:
prConditionQ ← (prConditionQ) - (2C),*Next level
GoTo[CheckForInterrupt];
NotifyWakeUp:
T ← APCTask&APC;
PFetch1[prConditionQ,prCondition,0];*Fetch condition.
LoadPage[prPage];*Cleanup interrupt[level].condition
prRtnLink2 ← T, UseCTask, Call[CleanUpQueue];*Returns with PsbIndexMask in T.
LU ← (prCondition) AND T;*IF condition.tail = nil THEN
GoTo[NWUQueueNil,ALU=0];*GoTo QueueNil
LoadPage[prPage];
UseCTask, Call[WakeHead];
NWUExit:
APCTask&APC ← prRtnLink2;
RETURN;
NWUQueueNil:
prCondition ← (prCondition) OR (wakeup);*condition.wakeup ← TRUE
PStore1[prConditionQ,prCondition,0],*Store condition
GoTo[NWUExit];
%prRtnLink2 ← T, LoadPage[prPage];
PFetch1[prConditionQ,prCondition,0];*Fetch Condition.
OnPage[prPage];
UseCTask, Call[CleanUpQueue];*Cleanup interrupt[level].conditon;returns with PsbIndexMask in T.
LU ← (prCondition) AND T;*IF condition.tail # nil THEN
APCTask&APC ← prRtnLink2, GoTo[WakeHead,ALU#0];
prCondition ← (prCondition) OR (wakeup);*condition.wakeup ← TRUE
APCTask&APC ← prRtnLink2;
PStore1[prConditionQ,prCondition,0],*Store condition
Return;
OnPage[opPage0];%
CheckForTimeouts:
T ← (SStkP&NStkP) xor (377C);
prSaveStkP ← T, StkP ← prSaveStkP, NoRegILockOK;
Stack ← (Stack) - 1;*prTime ← (prTime) - 1
PFetch1[prPda,WW,pdaCount!], Skip[ALU=0];*Fetch Psb count into WW
StkP ← prSaveStkP, GoTo[IntExit];
Stack ← 3C;*prTime ← tickSize
Stack&+1, Task;
Stack ← (Stack) + 1;
*prTicks ← (prTicks) + 1
*[prTicks is at prTime+1]
*BEGIN TimeoutScan.
T ← prPsbIndex ← pdaBlock;*Index of first Psb.
CheckForTimeoutLoop:
PFetch4[prPda,prPsbLink], Call[P4Ret];*Fetch Psb into 4-word buffer
T ← prPsbTimeout;*IF timeout = 0
LU ← (Stack) - T, GoTo[CheckEnd,ALU=0];*THEN GoTo CheckEnd
prPsbTimeout ← 0C, GoTo[Timeout,ALU=0];*IF timeout
WW ← (WW) - 1, GoTo[CheckEndx];*Decrement count
Timeout:
*Prepare to requeue the Psb pointed to by Link to the Ready queue. The timeout will be updated when the Psb is stored at the exit from Requeue.
prFlags ← (prFlags) OR (SourceQNil);
prPsbFlags ← (prPsbFlags) AND NOT (waiting);*Psb.waiting ← FALSE.
LoadPage[prPage];
UseCTask, Call[RequeueSubPsbFetched];
WW ← (WW) - 1, GoTo[CheckEndx];
CheckEnd:
WW ← (WW) - 1;*Decrement count
CheckEndx:
T ← prPsbIndex ← (prPsbIndex) + (PsbSize),
GoTo[CheckForTimeoutLoop,ALU#0];
StkP ← prSaveStkP;
IntExit:
LU ← prReady, LoadPage[prPage];*Fetch ready queue into Temp
prFlags ← (prFlags) OR (SaveStack), SkipP[ALU=0];
OnPage[prPage];
T ← prCurrentPsb, GoTo[ReSchedule];
GoTo[NoneReady];
*END TimeoutScan.
:END[MesaP];