



		    meter_response_time.alm         11/11/89  1106.3r w 11/11/89  0807.4       96993



" ***********************************************************
" *                                                         *
" * Copyright, (C) Honeywell Bull Inc., 1987                *
" *                                                         *
" * Copyright, (C) Honeywell Information Systems Inc., 1982 *
" *                                                         *
" * Copyright (c) 1972 by Massachusetts Institute of        *
" * Technology and Honeywell Information Systems, Inc.      *
" *                                                         *
" ***********************************************************
	name	meter_response_time

" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	subroutine to monitor response times of interactive processes
"
"	The state of a process is defined by a finite-state automaton
"	representing that process.  This finite-state automaton is
"	viewed as follows:
"
"
"                                +------------------+    
"                                |                  |
"        		             |  Processing      |
"     		             |	            |
"     		             +------------------+
"                  call ring-0 tty   |          ^
"          for next interaction      |          |  return from ring-0 tty with 
"                                    V          |     interaction
"         award eligibility      +------------------+
"              +-------------->  |                  |
"              |                 |     Other        |
"     +------------------+       |                  |   
"     |                  |       +------------------+
"     |     Queued       |         |        ^   
"     |                  |         |block   |
"     +------------------+         |        | non-tty wakeup
"              ^                   V        |
"              |          +------------------+
"              |          |                  |
"              +----------|     Blocked      |
"          tty wakeup     |                  |
"     		      +------------------+
"     
"	This subroutine implements the finite-state automaton described
"	for each process.  Transitions between states are defined by calls
"	to the subroutine.  There are two calling sequences, as follows:
"
"	External to bound_traffic_control_priv:
"
"	     call meter_response_time (processid, transition)
"
"
"	Internal to bound_traffic_control_priv:
"
"		tsx7	meter_response_time$tc
"
"		     pr2 -> apte for process
"		     pr3 -> base of tc_data$
"		     qreg = transition number
"
"	Written April 1981 by John J. Bongiovanni
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"


" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	Table to drive finite-state automaton
"
"	transition_table implements the following finite-state
"	automaton (ref. MTB-489):
"
"          
"                                   Transitions
"          
"          State                1   2   3   4   5   6   
"                             +---+---+---+---+---+---+
"          Initial (I)        | I | I | P | I | I | Q |
"                             +---+---+---+---+---+---+
"          Blocked (B)        | I | I | I | I | O | Q |
"                             +---+---+---+---+---+---+
"          Queued (Q)         | O | I | I | I | Q | I |
"                             +---+---+---+---+---+---+
"          Other (O)          | O | O | P | B | O | O |
"                             +---+---+---+---+---+---+
"          Processing (P)     | P | O | I | B | P | I |
"                             +---+---+---+---+---+---+
"          
"          
"          
"                    Transitions:
"          
"          	   1 - Award eligibility
"          
"          	   2 - Call ring-0 tty for next interaction
"          
"          	   3 - Return from ring-0 tty with next interaction
"          
"          	   4 - Block
"          
"          	   5 - Non-tty wakeup
"          
"          	   6 - Tty wakeup (input)
"          
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

"

"	Definition of States

	equ	initial,0
	equ	blocked,1
	equ	queued,2
	equ	other,3
	equ	processing,4
	equ	max_response_state,4
	
"

"	Table definition macros

	equ	state_element_words,2
	equ	state_elements_per_word,4
	equ	state_elements_per_entry,state_element_words*state_elements_per_word
	equ	state_entry_shift,1
	equ	state_element_shift,2
	
	set	current_state_element,0

	macro	state_element
	maclist	off save
	use	trans
	org	current_state_element
	dup	state_element_words
	dec	0
	dupend
	maclist	object
	org	current_state_element
&R&(&=&x,1&[          vfd       &;,&]o9/&i&)
	set	current_state_element,current_state_element+state_element_words
	maclist	restore
	use	.text.	
	&end
	
"
	use	trans
	even
transition_table:
	use	.text.

"	Initial (State 0)
	state_element	initial,initial,initial,processing,initial,initial,queued
"	Blocked (State 1)
	state_element	initial,initial,initial,initial,initial,other,queued
"	Queued (State 2)
	state_element	initial,other,initial,initial,initial,queued,initial
"	Other (State 3)
	state_element	initial,other,other,processing,blocked,other,other
"	Processing (State 4)
	state_element	initial,processing,other,initial,blocked,processing,initial

"
	join	/text/trans

	
trans_shift_table:
	dec	27,18,9,0
trans_mask_table:
	oct	777000000000
	oct	000777000000
	oct	000000777000
	oct	000000000777

"	macro to prepare for the day ...
	macro	read_clock
	rccl	sys_info$clock_,*
	&end
	

"

	entry	meter_response_time
	segdef	tc

"
	
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	Entry from outside of bound_traffic_control_priv
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

meter_response_time:

	push
	epbp3	tc_data$
	lda	pr0|2,*			processid
	epp2	pr3|0,au			pr2 -> apte
	cmpa	pr2|apte.processid		can this possibly be right?
	tnz	return			no way
	ldq	pr0|4,*			transition number
	tsx7	tc
return:	return

"

" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	Entry from inside of bound_traffic_control_priv
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

	inhibit	on	<+><+><+><+><+><+><+><+><+><+><+><+><+><+><+><+>

tc:	stq	pre_temp			transition number
	read_clock
	staq	pre_time

"	Validate current state and transition number.  Punt if either is
"	invalid by setting state to 0 (initial) and metering.
"	Find array element in transition table corresponding to
"	(current state, transition number)
	
	lda	pr2|apte.current_response_state
	tmi	invalid
	cmpa	max_response_state,dl
	tpnz	invalid
	eax1	0,al			x1=current state number
	als	state_entry_shift		al=offset into trans table this state
	sta	pre_temp+1
	lda	pre_temp			transition number
	tmoz	invalid
	cmpa	MAX_TRANSITION,dl		
	tpnz	invalid
	eax6	0,al			x6=transition number
	ldq	0,dl
	lrl	state_element_shift		al=word offset into array from state
	qrl	18-state_element_shift	qu=char offset into word
	ada	pre_temp+1		al=word offset into array
	lda	transition_table,al		array word
	ana	trans_mask_table,qu		mask out proper character
	lxl0	trans_shift_table,qu	x0=shift count
	arl	0,x0			areg=new state
	cmpa	pr2|apte.current_response_state has state changed
	tze	meter_and_exit		no
	als	0			check for invalid transition
	tze	invalid			invalid
	ldx0	pr2|apte.wct_index
	epp0	pr3|0,x0			pr0 -> wcte this process
	eax0	0,al			x0=new state
"
"	Do special things based on previous state

	ldaq	pre_time
	sbaq	pr2|apte.last_response_state_time  aq=delta time in state
	tra	prev_state_actions,x1*
prev_state_actions:
	arg	check_next		initial
	arg	update_think_time		blocked
	arg	update_queue_time		queued
	arg	check_next		other
	arg	update_response_time	processing

update_think_time:
	cmpx6	TTY_WAKEUP,du		was the blocked time think time
	tnz	check_next		no
	aos	pr0|wcte.number_thinks	count number times thinking
	adaq	pr0|wcte.total_think_time	and update total time
	staq	pr0|wcte.total_think_time
	tra	check_next

update_queue_time:
	aos	pr0|wcte.number_queues	update count of queues
	adaq	pr0|wcte.total_queue_time	and total time queued
	staq	pr0|wcte.total_queue_time
	tra	check_next


update_response_time:
	cmpx6	CALL_RING_0_TTY,du		was this the end of an interaction
	tnz	check_next		no
	staq	pre_temp+2		save response time
	ldaq	pre_time			clock value on entry
	sbaq	pds$cpu_time		total cpu time
	sbaq	pds$virtual_delta		virtual cpu time
	sbaq	pr2|apte.begin_interaction_vcpu	qreg=vcpu this interaction

"	Find proper bucket for this interaction.  Bucket boundaries (in terms
"	of vcpu time) are in the array tc_data$vcpu_response_bounds

	lda	pr3|vcpu_response_bounds_size	al=highest array offset
find_vcpu_bucket:
	cmpq	pr3|vcpu_response_bounds-1,al	in this bucket
	tpl	found_vcpu_bucket		yes
	sba	1,dl
	tpnz	find_vcpu_bucket		fall through if lowest bucket

"	Update statistics in APTE and WCTE

found_vcpu_bucket: 
	aos	pr0|wcte.number_processing,al	count interactions
	aos	pr2|apte.number_processing
	als	1			x2 (offset for double-word array)
	eax1	0,al			x0 = offset into double-word array
	lda	0,dl			aq = vcpu this interaction
	adaq	pr0|wcte.total_vcpu_time,x1
	staq	pr0|wcte.total_vcpu_time,x1
	ldaq	pre_temp+2		aq = response time this interaction
	adaq	pr0|wcte.total_processing_time,x1
	staq	pr0|wcte.total_processing_time,x1
	ldaq	pre_temp+2		aq = response time this interaction
	adaq	pr2|apte.total_processing_time
	staq	pr2|apte.total_processing_time
	

"
"	Do special things based on new state

check_next:
	tra	next_state_actions,x0*
next_state_actions:
	arg	state_update		initial
	arg	state_update		blocked
	arg	state_update		queued
	arg	state_update		other
	arg	mark_processing		processing

"	Note virtual cpu time at begin of interaction
"	We depend on running in the address space of the process
"	specified, which will be true since the transition is
"	a return from within that process
	
mark_processing:
	ldaq	pre_time
	sbaq	pds$cpu_time		real cpu time
	sbaq	pds$virtual_delta		virtual cpu time
	staq	pr2|apte.begin_interaction_vcpu

"
"	Update state, meter, and return

state_update:
	sxl0	pr2|apte.current_response_state
	ldaq	pre_time			clock at entry
	staq	pr2|apte.last_response_state_time
meter_and_exit: 
	read_clock
	sbaq	pre_time			metering overhead
	adaq	pr3|meter_response_time_overhead
	staq	pr3|meter_response_time_overhead
	aos	pr3|meter_response_time_calls
	tra	0,x7

invalid:	
	ldx0	initial,du		reset state
	aos	pr3|meter_response_time_invalid	count these
	tra	state_update
	
	inhibit	off	<-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><->
"	
	include	apte

          include   pxss_page_stack

	include	response_transitions

	include	tc_meters

	include	wcte
		
	end
	



		    pxss.alm                        11/11/89  1106.3rew 11/11/89  0806.8     1322064



" ***********************************************************
" *                                                         *
" * Copyright, (C) Honeywell Bull Inc., 1987                *
" *                                                         *
" * Copyright, (C) Honeywell Information Systems Inc., 1983 *
" *                                                         *
" * Copyright (c) 1972 by Massachusetts Institute of        *
" * Technology and Honeywell Information Systems, Inc.      *
" *                                                         *
" ***********************************************************

" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	pxss -- The Multics Traffic Controller (Scheduler)
"
"	Last Modified: (Date and Reason)
"
"	February 1985 by Keith Loepere to fix initialization disk polling.
"	January 1985 by Keith Loepere to fix window in ips sending and oob in
"		set_work_class.
"	December 1984 by Keith Loepere for page_pause and pause functions.
"	84-11-21 by E. Swenson for IPC event channel validation.
"	October 1984 by M. Pandolf for getwork suspension
"	84-07-23 by E. Swenson to clean up ittes of dead processes
"		correctly.  
"	07/16/84 by R. Michael Tague:  Changed the $ips_wakeup and
"		  $ips_wakeup_int entries to expect an ips bit mask
"		  as an indicator of the signal to be sent instead
"		  of a name.
"	84-02 BIM for sst$ references instead of sst.
"	October 1983 by Keith Loepere for disk polling during initialization.
"	June 1983 by E. A. Ranzenbach for operator console polling.
"	June 1983 by Chris Jones to call the new IOI entries.
"	May 1983 by BIM for call-side pre-empt and stop.
"	April 1983 by E. N. Kittlitz to DRL instead of 0,ic looping.
"	February 1983 by E. N. Kittlitz for hex floating point.
"	December 1982 by C. Hornig to punt NCP
"	October 1982 by C. Hornig for no ITT message on fast channels.
"	August 1982 by J. Bongiovanni for realtime_io priority, 
"		relinquish_priority
"	April 1982 by J. Bongiovanni to enhance governing
"	February 1982 by J. Bongiovanni to fix masking bug
"	September 1981 by J. Bongiovanni for procs_required, moving
"		code to tc_util
"	June 1981 by J. Bongiovanni for governed work classes,
"		-tcpu, +pre_empt_sample_time
"	May 1981 by J. Bongiovanni for response time metering
"	03/01/81 by W. Olin Sibert, for Phase One of ADP conversion
"	March 1981 by J. Bongiovanni for page pinning, saved stack_0's,
"	          argument copying protocol, initialization NTO
"	February 1981 by J. Bongiovanni for fast connect
"	March 1981 by E. Donner for new ipc - include file for itt entry
"	and to change check for fast channel
"	February 1981 by J. Bongiovanni to fix set_proc_required
"	January 1981 by J. Bongiovanni to fix ITT overflow, credit
"	          clipping
"	Spring 1979 by B. Greenberg for shared stack_0's.
"	Fall 1978 RE Mullen for +ptl_NTO, +disable int_q, -XED's
"	Winter 1977 RE Mullen for lockless (lockfull?) scheduler:
"		concurrent read_lock, ptlocking state, apte.lock,
"		unique_wakeup entry, tcpu_scheduling
"	Spring 1976 by RE Mullen for deadline scheduler
"	02/17/76 by S. Webber for new reconfiguration
"	3/10/76 by B Greenberg for page table locking event
"	Spring 1975 RE Mullen to implement priority scheduler and
"		delete loop_wait code.  Also fixed plm/lost_notify bug.
"	Last modified on 02/11/75 at 19:49:10 by R F Mabee.  Fixed arg-copying & other bugs.
"	12/10/74 by RE Mullen to add tforce, ocore, steh, tfmax & atws
"	     disciplines to insure response in spite of long quanta, and
"	     fix bugs in get_processor, set_newt, and loop_wait unthreading.
"	12/6/74 by D. H. Hunt to add access isolation mechanism checks
"	4/8/74 by S.H.Webber to merge privileged and unprivileged code.
"		and to add quit priority and fix lost notify bug
"	5/1/74 by B. Greenberg to add cache code
"	8/8/72 by R.B.Snyder for follow-on
"	2/2/72 by R. J. Feiertag to a simulated alarm clock
"	9/16/71 by Richard H. Gumpertz to add entry rws_notify
"	7/**/69 by Steve H. Webber
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

" HISTORY COMMENTS:
"  1) change(86-05-13,GJohnson), approve(86-05-13,MCR7387),
"     audit(86-05-13,Martinson), install(86-05-14,MR12.0-1056):
"     Correct error message documentation.
"  2) change(86-08-09,Kissel), approve(86-08-12,MCR7479),
"     audit(86-10-08,Fawcett), install(86-11-03,MR12.0-1206):
"     Changed to check the flags in an event channel name when sending a
"     wakeup.  If the flags indicate that this is an asynchronous event
"     channel, then send an IPS wkp_ signal in addition to the normal IPC
"     wakeup.  This code uses an algorithm copied from ipc_validate_ to decode
"     the event channel flags, hence, any changes must be reflected in both
"     places.
"  3) change(88-09-16,Fawcett), approve(88-10-06,MCR8004),
"     audit(88-10-06,Farley), install(88-10-10,MR12.2-1156):
"     Removed check in compute_virtual_clocks which was testing for a zero
"     second word in prds$last_recorded_time to determine if it should be
"     initialized.  The initialization is already done in init_processor.  All
"     this check was doing was opening a timing hole for errors to occur (i.e.
"     the last 36 bits of the clock roll over ~ every 19 hours).
"  4) change(88-10-05,Fawcett), approve(88-10-05,MCR7999),
"     audit(88-10-06,Farley), install(88-10-10,MR12.2-1156):
"     Changed to always reset the number a pages in use  used before giving
"     the processor the process. This enables a more acturate measure of
"     memory units.
"                                                      END HISTORY COMMENTS

	name	pxss


	iftarget	adp
	  warn	(This has not been converted yet for the ADP. Beware.)
	ifend


	link	prds_link,prds$+0

	even
channel_mask_set:
	oct	17,17

null:	its	-1,1		null pointer

null_epaq:
	vfd	3/0,15/-1,18/0,18/1,18/0

ring0_timer_event:
	aci	"dlay"
	even
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	Table of contents
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "


	entry	addevent
	entry	block
	entry	delevent
	entry	dvctl_retry_ptlwait
	entry	empty_t
	entry	fast_ipc_block
	entry	fast_ipc_get_event 
	entry	force_stop
	entry	free_itt
	entry	get_entry
	entry	get_event
	entry	guaranteed_eligibility_off
	entry	guaranteed_eligibility_on
	entry	io_wakeup
	entry	ips_wakeup
	entry	ips_wakeup_int
	entry	lock_apt
	entry	lock_apte
	entry	notify
	entry	page_notify
	entry	page_pause
	entry	page_wait
	entry	pause
	entry	pre_empt
	entry	pre_empt_poll
	entry	ptl_notify
	entry	ptl_wait
	entry	relinquish_priority
	entry	ring_0_wakeup
	entry	set_cpu_timer
	entry	set_procs_required
	entry	set_timer
	entry	set_work_class
	entry	start
	entry	stop
	entry	stop_wakeup
	entry	suspend_getwork
	entry	thread_in_idle
	entry	unique_ring_0_wakeup
	entry	unlock_apt
	entry	unlock_apte
	entry	unthread_apte
	entry	usage_values
	entry	wait
	entry	waitp
	entry	wakeup
	entry	wakeup_int

"
	include	apte
"
	include	aste
"
	include	drl_macros
"
	include	event_channel_name
"
	include	ips_mask_data
"
	include	itt_entry
"
	include	mc
"
	include	mode_reg
"
	include	ptw
"
	include	pxss_page_stack
"
	include	response_transitions
"
	include	scs
"
	include	stack_0_data
"
	include	stack_frame
"
	include	stack_header
"
	include	state_equs
"
	include	tc_meters
"
	include	wcte
"

	macro	read_clock
	rccl	sys_info$clock_,*
	&end

"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	BLOCK -- entry to block a process.
"
"	Call is
"		call pxss$block;
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

block:	eppbp	short_ret
	sprpbp	pds$ipc_block_return

	equ	ipcv.retsw,1

fast_ipc_block:
	tsplb	setup_mask	mask, switch_stack
	aos	bb|te_block
	aos	bb|blocks
	lda	bp|apte.flags	check for wakeup waiting
	cana	apte.wakeup_waiting,du
	tnz	block_ez		able to avoid heavy lock!
	tsx7	update_te		update te in own APT entry
	tsx6	WRITE_LOCK	block locks
	tsx6	LOCK_bp		block locks
	lda	bp|apte.flags	check for wakeup waiting
	cana	apte.wakeup_waiting,du
	tnz	block_not		there is one, return
	ldq	BLOCK_PROCESS,dl
	tsx7	meter_response_time$tc	this is a response transition
	tsx7	unthread		thread out of ready list
	ldx0	blocked,du	set state to blocked
	tsx7	update_execution_state
	tsx7	revoke_elig	block
	tsx7	reschedule	block
	tsx7	purge_UNLOCK	block
	tsx7	getwork
	ldaq	bp|apte.virtual_cpu_time note vtime at gain elig
	staq	pds$virtual_time_at_eligibility
	tsx6	LOCK_bp
block_returns:
	lcx0	apte.wakeup_waiting+1,du turn off wakeup waiting
	ansx0	bp|apte.flags
return_event_messages:
	lda	bp|apte.flags	check for interrupts pending
	ana	apte.stop_pending,du look for stop connect
	ora	bp|apte.ips_message	or ips signals
	tze	*+2		if interrupt pending leave 1
	lda	1,dl		..
	ldq	bp|apte.flags2	get special interrupts
	anq	apte.special_chans,dl ..
	ersq	bp|apte.flags2	turn off special chans
	qls	apte.chans_offset	..
	lls	17		put info together
	eax3	0,al		remember info
	eax4	0		use to zero event thread
	ldx2	bp|apte.ipc_pointers return event thread
	stx4	bp|apte.ipc_pointers zero event thread in APT entry
	tsx6	UNLOCK_bp
	tsx7	switch_back_ret_pds
	stz	pds$itt_head
	stx2	pds$itt_head
	eaq	0,3		get info
	lda	0,dl		zero a
	lls	1		split info
	orsq	pds$events_pending 	store special chans
	orsa	pds$ipc_vars+ipcv.retsw store return sw
	lprpbp	pds$ipc_block_return
	epbpsb	pds$stack_0_ptr,*	Load this up for fast_hc_ipc,
				"who can't do it himself.
	tra	bp|0

"
"here if find wakeup_waiting after full lock, to back off big lock
"
block_not:
	tsx6	UNLOCK		only need self locked
	tra	block_returns

"
"here if notice wakeup_waiting before locking anything
"
block_ez:	tsx6	LOCK_bp		only need to lock self
	tra	block_returns

" 
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " 
"
"	GET_EVENT -- procedure to return a process' event thread
"
"	Call is
"		call pxss$get_event(event)
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

get_event:
	eppbp	short_ret
	sprpbp	pds$ipc_block_return
fast_ipc_get_event:
	tsplb	setup_mask
	tsx6	LOCK_bp
	tra	return_event_messages
" 
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	WAKEUP
"
"	call pxss$wakeup(processid,event_channel,event_message,state)
"
"	entry to wake up a process given event channel and event message
"
"	The following entries have the same calling sequence:
"
"	   wakeup - send wakeup, give interactive credit if blocked
"		  more than priority_sched_inc
"
"	   ring_0_wakeup - send wakeup, give interactive credit if blocked
"
"	   unique_ring_0_wakeup - send wakeup only if this wakeup is unique
"	             in ITT for this process, give interactive credit if
"		   blocked.
"
"	   io_wakeup - send wakeup, give interactive credit if blocked,
"	             give realtime credit if blocked and tuning parameter
"		   set.
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

ring_0_wakeup:
wakeup_int:
	tsx6	setup_
	lca	1,dl		set flag saying ring_0_wakeup entry
	sta	pds$wakeup_flag	set flag saying ring_0_wakeup entry
	tra	wjoin
unique_ring_0_wakeup:
	tsx6	setup_
	stz	pds$wakeup_flag	set flag for unique_ring_0_wakeup
	tra	wjoin
io_wakeup:
	tsx6	setup_
	lca	2,dl		set flag for io_wakeup entry
	sta	pds$wakeup_flag	set flag for io_wakeup entry
	tra	wjoin
	
wakeup:
	tsx6	setup_
	stc1	pds$wakeup_flag	set flag saying wakeup entry
wjoin:
	lda	ap|2,*		get processid
	sta	pds$arg_1
	ldaq	ap|4,*		save event channel in pds$arg_2
	staq	pds$arg_2
	ldaq	ap|6,*		save event message in pds$arg_3
	staq	pds$arg_3
	tsplb	setup_check	switch stacks and lock
	arg	0,6

	szn	pds$wakeup_flag	see if ring_0_wakeup entry
	tmoz	w_rz		it is. do later code
	lda	pds$validation_level it isn't. 
	sta	tmp_ring		get ready to make ITT message
	stz	dev_signal
	tra	copy_evs		skip code for ring_0_wakeup entry
w_rz:	stz	tmp_ring
	lda	1,dl
	sta	dev_signal	must be a device signal
copy_evs:
	ldaq	pds$arg_2		finish setup for make ITT message
	staq	tmp_ev_channel
	ldaq	pds$arg_3
	staq	tmp_ev_message

	ldx2	0,du		pre-set execution state to zero
	tsx6	WRITE_LOCK	Wakeup may need to thread in
	tsx7	hash_LOCK		wakeup finds and locks
	arg	wakeup_returns_nul	indirect if error
	lda	bp|apte.flags	see if this is idle process
	cana	apte.idle,du
	tnz	wakeup_returns	can't wakeup an idle process
	aos	bb|wakeups
	lxl0	bp|apte.state	check for stopped process
	cmpx0	stopped,du
	tze	wakeup_returns

	ldq	NON_TTY_WAKEUP,dl
	tsx7	meter_response_time$tc	response transition

	ldq	pds$wakeup_flag	should we give priority ?
	tpnz	non_ring_0	no,normal wakeup
	lxl0	bp|apte.state	recover process state
	cmpx0	blocked,du	no interaction credit unless blocked
	tnz	no_int_credit	..at time of wakeup.
	lda	apte.interaction,du	give priority ... turn on interaction sw.
	orsa	bp|apte.flags	in APT entry of process getting wakeup
	cmpq	=-2		io_wakeup?
	tnz	no_int_credit	no
	szn	bb|realtime_io_priority_switch are we giving realtime priority?
	tze	no_int_credit	no
	lda	apte.realtime_burst,du
	orsa	bp|apte.flags	yes
no_int_credit:
	tsx7	make_itt_message	wakeup adds itt_msg to process' queue

"				See if we have to send an IPS wkp_ signal
"				This algorithm is copied from ipc_validate_

	lda	pds$arg_2			Get event_channel_name.encoded_idx
	arl	18			 in al
	sba	bp|apte.ipc_r_offset	Subtract the r_offset to get the decoded_index
	arl	decoded_index.flags_shift-18	Get the flag bits (we already shifted 18 above)
	ana	decoded_index.flags_mask,dl	For now there are only 2 possibilities
	tze	wakeup_wake	We have the normal flags
"				We have to send an IPS wkp_ signal

	lda	sys_info$wkp_mask	We want to send a wkp_ signal
	tsx6	send_ips_wakeup	Do the real work, destroys pds$arg_3
"				Next instruction may be skipped on return
wakeup_wake:
	tsx7	wake		Go to common wakeup code
	lxl2	bp|apte.state	return arg in x2
	szn	errcode
	tze	*+2
	lxl2	errcode
wakeup_returns:
	tsx6	UNLOCK_bp		Wakeup unlocks target apte
wakeup_returns_nul:
	tsx6	UNLOCK		Wakeup unlocks
	tsx7	switch_back_ret	Exit from wakeup if pid invalid and no apte locked
	stz	ap|8,*		return execution state
	sxl2	ap|8,*
	short_return

non_ring_0:
"Here are the checks for the
"Access Isolation Mechanism

	ldaq	bp|apte.access_authorization if target has ipc privilege
	orq	pds$access_authorization+1 or if sender has ipc privilege,
	canq	apte.no_ipc_check,dl
	tnz	no_int_credit	then it is OK to send the wakeup.	
	ldx0	bp|apte.access_authorization+1 get level of target process
	cmpx0	pds$access_authorization+1 if it's less than sender's level,
	tmi	send_down		do not allow wakeup to be sent.
	ana	pds$access_authorization if the category set of the sender
	cmpa	pds$access_authorization is contained in (or equal to)
	tze	no_int_credit	the category set of the target,
"then it is OK to send the wakeup.
send_down:
	ldx2	100,du		this error code indicates
	tra	wakeup_returns	an IPC send-down attempt.

" 
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	WAKE -- internal subroutine used to wake up a process
"	and award it a processor if it is warrented.
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

wake:
	tsx6	subroutine_save
	szn	bp|apte.lock	ASSUME bp locked
	drlnz	(pxss: APTE not locked) ASSUME bp locked
	lda	apte.wakeup_waiting,du turn on wakeup waiting flag
	orsa	bp|apte.flags
	lxl0	bp|apte.state	make sure process is blocked
	cmpx0	blocked,du
	tnz	subroutine_unsave
	read_clock		" check for long time block
	sbaq	bp|apte.state_change_time subtract out last state chage time
	cmpq	bb|priority_sched_inc see if process has been asleep too long
	tmi	short_time	just went blocked, no priority
	tsx0	setup_p_int	boost priority
short_time:
	ldx0	ready,du		change state to ready
	tsx7	update_execution_state
	tsx7	sort_in		schedule and thread in
	eax2	bp|0		fp = entry waking up
	tsx7	get_processor	does he get a processor?
	tra	subroutine_unsave
" 
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	FREE_ITT - entry to put a list of ITT entries into the list of free ITT 
"		entries. It is always called after a process returns from block, 
"		to release its event message queue.
"
"		Nothing is locked when this code is entered.
"		Apte's are locked to validate that the processes exist.
"		If the pid has changed we know empty_t zeroed the counter.
"		Never looplock an APTE while holding the itt_free_list.
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

free_itt:
	tsx6	setup_
	lda	ap|2,*		pick up pointer to list to be freed
	sta	pds$arg_1
	tsplb	setup_check	switch stacks and lock
	arg	0,6

	ldx0	pds$arg_1		get head of process's itt queue
	tsx7	free_itt_		free them
	tra	switch_back	free_itt exits
"

free_itt_:
"Come here to free X0->ITTE's
	eax5	0,0		Remember newly freed head in X5
	tze	free_itt_exit	there is nothing to free
	epbpbb	tc_data$		get pointer to base of tc_data
	ldx4	-1,du		put all-seven into RX4
	eaa	0		count num freed
follow_itt_list:
	sxl4	bb|itt_entry.next_itt_relp,0  tag the discarded entry for debugging
	sba	1,dl		maintain counter
	ldx3	bb|itt_entry.origin,0 see if dev_signal
	tnz	fi_skip_sender	nz means dev_signal
	ldx3	bb|itt_entry.sender,0 was dev_signal
	tsx6	LOCK_x3		free_itt locks sender
	ldq	bb|apte.processid,3 if process still exists
	cmpq	bb|itt_entry.sender,0
	tnz	fi_sender_gone	processid has changed!
	lcq	1,dl		ok to decrement
	asq	bb|apte.ittes_sent,3
fi_sender_gone:
	tsx6	UNLOCK_x3		free_itt unlocks sender
fi_skip_sender:
"				get following entry's forward pointer
	ldx2	bb|itt_entry.next_itt_relp,0
	tze	thread_in_itt_queue
	eax0	0,2		put index 2 into XR0
	tra	follow_itt_list
"
"x5->first_freed, x0 ->last_freed, A has count
thread_in_itt_queue:
	ldqc	bb|itt_free_list	free_itt LOCKS free list
	tnz	*+2
	tra	thread_in_itt_queue	If zero somebody else has it

	eax1	0,qu
"				make tail of new -> old head
	stx1	bb|itt_entry.next_itt_relp,0
	ldx3	bb|itt_entry.target_id,0 prepare to decrement target counter
	ldq	bb|itt_entry.target_id,0 get rcvr pid
	asa	bb|used_itt	use A-reg to decrement
	stx5	bb|itt_free_list	free_itt UNLOCKS free_list
	tsx6	LOCK_x3		free_itt locks target
	cmpq	bb|apte.processid,3	compare processid
	tnz	rcvr_gone		processid has changed!
	asa	bb|apte.ittes_got,3	use A-reg to decrement
rcvr_gone:
	tsx6	UNLOCK_x3		free_itt unlocks target
free_itt_exit:
	tra	0,7		free_itt_  exits

" 
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	PRE_EMPT
"	TIMER_RUNOUT
"
"	This procedure is called at timer runout time and
"	when a process gets pre-empted by a higher priority
"	process. Pre-empt merely gives the processor away.
"	Timer-runout gives up eligibility as well.
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

pre_empt_poll:
	tsplb	setup_mask
	tra	pre_empt.common

pre_empt:
	stz	pds$pre_empt_poll_return

	tsx6	init_pxss_save_stack init x7 save stack
pre_empt.common:
	tsx7	update_te
	aos	bb|te_pre_empt
	lda	bp|apte.flags
	cana	apte.idle,du	Idle suffers pre-empt not timer-runout
	tnz	pmt_idle
	lda	bp|apte.temax	See if time left in quantum
	cmpa	bp|apte.te
	tmi	tro_		None left give up elig

pmt_:	tsx6	LOCK_bp		pre-empt changes state
	lcx0	apte.pre_empt_pending+1,du turn OFF flag
	ansx0	bp|apte.flags	..
	ldx0	ready,du		set state to ready
	tsx7	update_execution_state
	tsx6	UNLOCK_bp		pre-empt unlocks before getwk
	tsx7	getwork
pre_empt.return:
	szn	pds$pre_empt_poll_return	zero => this process entered pre-empt through connect fault
	tze	wired_fim$pre_empt_return 	Return from connect

" Switch back by hand, since the circumstances are peculiar.
" Code adapted from switch_back.

	ldaq	prds$+stack_header.stack_begin_ptr restore stack end ptr
	staq	prds$+stack_header.stack_end_ptr ..

	ldaq	pds$tc_mask	restore previous mask
	oraq	channel_mask_set
	anaq	scs$open_level
	lxl1	prds$processor_tag
	lprpab	scs$mask_ptr,1	get set for masking
	xec	scs$set_mask,1

	eppsp	pds$pre_empt_poll_return,*	Adopt outer ring stack frame
	stz	pds$pre_empt_poll_return    	Au resevoir
	epbpsb	pds$stack_0_ptr,*		So we can get to operators
	short_return			and return to caller of gate
	


pmt_idle:	szn	bb|wait_enable	If idle make sure not shutting down
	tnz	pmt_		Idle pmt ok if multiprog
	szn	bb|system_shutdown
	tze	pmt_		Idle pmt ok if not shutdown
				     " Idles never get ring alarms
	tra	wired_fim$pre_empt_return Must not do getwork!
				

tro_:	tsx6	WRITE_LOCK	tro_ unthreads
	tsx6	LOCK_bp		tro_ locks apte
	lcx0	apte.pre_empt_pending+1,du
	ansx0	bp|apte.flags
	tsx7	unthread		tro_
	ldx0	ready,du		tro_
	tsx7	update_execution_state tro_
	tsx7	revoke_elig	tro_
	tsx7	reschedule	tro_
	tsx7	sort_in		tro_
	tsx7	purge_UNLOCK	tro_
	tsx7	getwork		tro_
	ldaq	bp|apte.virtual_cpu_time note vtime at gain elig
	staq	pds$virtual_time_at_eligibility
	tra	pre_empt.return

" 
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	stop	 - called by hcs_$stop_proc to stop a process
"		stop is unusual in that it is a call side
"		operation which must unthread and sort_in
"		a ready non-eligible process
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

stop_wakeup:
stop:
	tsx6	setup_
	lda	ap|2,*		get processid
	sta	pds$arg_1
	tsplb	setup_check	switch stacks and lock
	arg	0,6
	tsx6	WRITE_LOCK	stop may need rethread

	lda	pds$validation_level get caller's ring number
	sta	tmp_ring

	ldx2	0,du		pre-set execution state to zero
	tsx7	hash_LOCK		stop finds and locks target
	arg	stop_returns_nul	indirect if error

	lda	bb|default_procs_required	set  process to default
	ana	apte.procs_required_mask,du	just in case
	drlze	(pxss: APTE disdains all processors) should never happen
	era	bp|apte.procs_required
	ersa	bp|apte.procs_required
	ldx2	apte.default_procs_required,du set default flag
	orsx2	bp|apte.flags

	lxl2	bp|apte.state	target's state to x2
	cmpx2	stopped,du	Compare to the stopped state
	tze	stop_returns	If equal, target already stopped
	stz	dev_signal	count it as dev_signal
	aos	dev_signal	count it as dev_signal
	lda	=aquit		put recognizable pattern in message
	ldq	=astop		... namely "quitstop"
	staq	tmp_ev_channel
	staq	tmp_ev_message	pattern into channel & message
	stc1	pds$wakeup_flag	not require unique message
	tsx7	make_itt_message	now go make "quitstop" message

	tsx0	setup_p_int	boost priority
	lxl0	bp|apte.state
	cmpx0	blocked,du	Blocked?
	tze	st_wake		Yes, wake him.

	ldx0	bp|apte.flags	Eligible?
	canx0	apte.eligible,du
	tnz	st_wake		Yes, don't unthread
	tsx7	unthread		No, move up in queue
	tsx7	sort_in_before

st_wake:	tsx7	wake		Awaken blocked target
	lda	apte.stop_pending,du turn on stop pending
	orsa	bp|apte.flags
	tsx7	send_connect	send connect if processor running
	lxl2	bp|apte.state	return state to caller
stop_returns:
	tsx6	UNLOCK_bp
stop_returns_nul:
	tsx6	UNLOCK		stop is done
	tsx7	switch_back_ret
	stz	ap|4,*		return execution state
	sxl2	ap|4,*
	short_return
" 
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	start	 - called by hcs_$start_proc to start a process
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

start:
	tsx6	setup_
	lda	ap|2,*		get processid
	sta	pds$arg_1
	tsplb	setup_check	switch stacks and lock
	arg	0,6

	tsx6	WRITE_LOCK	start must protect pid, rethreads
	ldx2	0,du		pre-set state to zero
	tsx7	hash_LOCK		Get pointer to apt entry
	arg	stop_returns_nul	start: indirect if error
	lcx0	apte.stop_pending+1,du turn off stop pending
	ansx0	bp|apte.flags
"	lxl0	bp|state		pick up target's state
"	cmpx0	stopped,du	Compare it to stopped state
"	tnz	stop_returns	If not equal, return
	ldx0	blocked,du	Otherwise redefine target state
	tsx7	update_execution_state
	tsx7	wake		And awaken target
	lxl2	bp|apte.state	return target's state
	tra	stop_returns	return
" 

" " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
" FORCE_STOP -- called to make the current process stop itself.
"		sometimes known as I_stop.
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

force_stop:
	getlp
	tsplb	setup_mask
	aos	bb|te_i_stop
	tsx7	update_te
	tsx6	WRITE_LOCK	I_stop unthreads
	tsx6	LOCK_bp
	lda	bp|apte.flags
	cana	apte.stop_pending,du check for stop pending
	tze	force_stop_not	no, return
	szn	bp|apte.term_processid  Is there a buzzard for this process?
	drlze	(pxss: No term_processid) NO - CRASH
	ldaq	bp|apte.alarm_time	check for alarm pending
	tze	force_stop_getwork	no alarm pending

	eax0	bp|0		thread out of alarm list
	cmpx0	bb|alarm_timer_list	see if first on list
	tnz	is_scan

	ldx2	bp|apte.alarm_time	if so thread out
	stx2	bb|alarm_timer_list
	tra	force_stop_getwork

is_scan:	ldx2	bb|alarm_timer_list	search list for entry
is_loop:	cmpx0	bb|apte.alarm_time,2
	tze	is_done
	ldx2	bb|apte.alarm_time,2
	tra	is_loop
is_done:	ldx0	bp|apte.alarm_time	thread out of list
	stx0	bb|apte.alarm_time,2

force_stop_getwork:
	tsx7	unthread		istop
	ldx0	stopped,du	set state to stopped
	tsx7	update_execution_state
	tsx7	revoke_elig	istop
	tsx7	reschedule	istop
	tsx7	purge_UNLOCK	istop
	eax4	0
	ldx0	bp|apte.ipc_pointers
	stx4	bp|apte.ipc_pointers
	tsx7	free_itt_
	tsx7	getwork
	drltra	(pxss: Stop returned from getwork) should never get here, nohow


stop_return:
	short_return

force_stop_not:
UNLOCK2_switch_back:
	tsx6	UNLOCK_bp		force_stop done
	tsx6	UNLOCK		force_stop done
	tra	switch_back
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	set_procs_required - entry to set and reset the
"		group of CPUs on which this process can run.
"	
"	call pxss$set_procs_required (cpu_set, code)
"		cpu_set = bit (8) aligned CPU mask
"			("0"b => set to system default)
"		code    = non-standard error code
"			0 => group set and now running in group
"			^0 => no member of group online
"
"
"	system default is a CPU mask stored in tc_data$default_procs_required
"	It is used for processes which have not requested explicitly
"	CPUs required, and for those which have reset to the default.
"	To avoid various races (with reconfiguration, default changing, etc.),
"	this cell should not be used or set without the global APT lock held
"
"	THIS ENTRY MUST NOT BE CALLED ON THE PRDS
"
"	It may, however, be called from a wired environment
"	(the reason for non-standard error codes)
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

set_procs_required:
	ldx0	lp|prds_link	check whether we're on the prds
	cmpx0	sb|stack_header.stack_begin_ptr      already
	drlze	(pxss: sprq already on prds) invalid call--can't do much else
	
	tsx6	setup_
	ldq	ap|2,*		save argument in pds
	stq	pds$arg_1
	stz	ap|4,*		clear error code
	tsplb	setup_check	switch stacks and lock
	arg	0,6
	tsx6	WRITE_LOCK	sprq locks out getwork and reset_proc_req

	eax2	0		assume set_procs (to non-default)
	ldq	pds$arg_1		refetch the argument
	anq	apte.procs_required_mask,du	strip out garbage
	tnz	pr_set		set
	ldq	bb|default_procs_required	system default
	anq	apte.procs_required_mask,du	shouldn't be garbage, but ...
	drlze	(pxss: APTE disdains all processors) we do this, nobody's running anyway
	eax2	1		reset (to default)
pr_set:
	canq	scs$processor	is any CPU in the set online
	tze	UNLOCK_sprq_error	yes--don't set, return error code
	eaa	0,qu		save group mask for check	
	tsx6	LOCK_me_bp	sprq
	erq	bp|apte.procs_required	set into APTE
	anq	apte.procs_required_mask,du
	ersq	bp|apte.procs_required

	eax2	0,2		set to default
	tnz	set_default_flag	yes
	lcx0	apte.default_procs_required+1,du
	ansx0	bp|apte.flags
	tra	set_reset_done
set_default_flag:
	ldx0	apte.default_procs_required,du
	orsx0	bp|apte.flags
set_reset_done:

"
"	Now check to see if we're on a CPU in the group. If not
"	go thru getwork (which won't run us unless we're on such a CPU).
"
	cana	prds$processor_pattern
	tnz	UNLOCK2_switch_back	already on one, unlock everything and return

	call	page$cam		clear all caches if wrong cpu
	tsx7	update_te		get set for getwork
	ldx0	ready,du
	tsx7	update_execution_state
	tsx6	UNLOCK_bp		sprq unlock for getwk
	tsx6	UNLOCK		sprq unlocks before getwork
	tsx7	getwork
	eax7	0		short_return after switch back
	tra	switch_back_ret_pds

UNLOCK_sprq_error:
	tsx6	UNLOCK
	tsx7	switch_back_ret_pds
	stc1	ap|4,*		error code
	short_return

" 
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	EMPTY_T  --  procedure to thread an APT entry into the APT
"	free list.
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

empty_t:
	tsx6	setup_
	ldaq	ap|2,*		save input apt pointer
	staq	pds$arg_1
	tsplb	setup_check	switch stacks and lock
	arg	0,6
	tsx6	WRITE_LOCK	emtpty_t rethreads
	eppbp	pds$arg_1,*	get pointer to desired APT entry
	tsx6	LOCK_bp		empty_t changes state
	lxl0	bp|apte.state	Check state first
	cmpx0	empty,du		Already empty OK
	tze	et_1
	cmpx0	stopped,du	Also stopped OK
	drlnz	(pxss: empty_t APTE not stopped or empty)
	ldx0	empty,du
	tsx7	update_execution_state Changed stopped to empty.
et_1:
	eax7	0
	ldx5	bp|apte.ipc_pointers claim ITTE's while locked, free later
	stx7	bp|apte.ipc_pointers
	stz	bp|apte.ittes_sent		safe to zero these since piud=0
	stz	bp|apte.ittes_got

"	Return any stack_0 owned by the defunct process

	lda	bp|apte.flags
	cana	apte.shared_stack_0,du	Is there as stack_0 to return?
	tze	check_stack_0_none		No

	tsx6	lock_stack_queue
	eaa	bp|0		au = apte offset
	arl	18		al = apte offset
	ldq	-1,du		comparison mask
	lxl0	ab|sdt.num_stacks	number of stack_0's
	eax4	0		index into sdt
check_stack_0_loop:
	cmk	ab|sdt.aptep,4	this stack_0 belong to deadproc
	tnz	bump_next_stack_0	no
	tsx6	free_stack_0	yes--give it up
	ldx0	1,du
	asx0	bb|max_max_eligible number available stack_0's
	lca	1,dl
	asa	bb|stopped_stack_0	count of suspended stack_0's
	tra	check_stack_0_done
bump_next_stack_0:
	eax4	sdte_size,4	bump sdt index
	eax0	-1,0		one less sdte
	tpnz	check_stack_0_loop  transfer if more to go
check_stack_0_done:
	tsx6	unlock_stack_queue
	
check_stack_0_none:
	tsx6	UNLOCK_bp
	ldx4	bb|empty_q	thread into free list
	stx4	bp|apte.thread	singly threaded
	eax4	bp|0		get pointer to this entry
	stx4	bb|empty_q	to update into empty_q
	tsx6	UNLOCK		empty_t uses  lock to protect empty_q
	eax4	0
	stx4	bp|apte.ipc_pointers  zero itt thread in apte
	eax0	0,5		restore old itt thread
	tsx7	free_itt_
	tra	switch_back
" 
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	UPDATE_EXECUTION_STATE -- subroutine to store the execution
"	state passed in x0 into the APTE pointed to by bp. The
"	appropriate counters in tc_data are also updated.
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

update_execution_state:
	szn	bp|apte.lock	ASSUME bp locked
	drlnz	(pxss: APTE not locked) ASSUME bp locked
	lda	bp|apte.flags	dont change meters for idle
	cana	apte.idle,du
	tnz	update_exec_ret
	lxl4	bp|apte.state	get previous (old) state
	lca	1,dl
	asa	bb|statistics,4
	aos	bb|statistics,0
"old_assume_state_change_ok
update_exec_ret:
	sxl0	bp|apte.state
	read_clock		" read the clock
	staq	bp|apte.state_change_time for state change time
	tra	0,7



" 
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	UNTHREAD -- procedure to thread the APT entry pointed to by 
"	bp out of the list it is in.
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

unthread:
	szn	tc_data$apt_lock	ASSUME write locked
	drlmi	(pxss: APT not locked) ASSUME write locked
	szn	bp|apte.thread	check if not in a list
	tze	0,7		return if not in a list
	lxl4	bp|apte.thread	x4 -> previous entry in list
	drlze	(pxss: unthread null back ptr) ASSUME cur.bp nonzero
	eax0	bp|0		ASSUME prev.fp -> cur
	cmpx0	bb|apte.thread,4	ASSUME prev.fp -> cur
	drlnz	(pxss: unthread prev.fp ^= cur) ASSUME prev.fp -> cur
	ldx0	bp|apte.thread	x0 -> next entry in list
	drlze	(pxss: unthread null cur.fp) ASSUME cur.fp nonzero
	stx0	bb|apte.thread,4	store old forward in previous
	sxl4	bb|apte.thread,0	store old back in next
	stz	bp|apte.thread	zero thread pointers
	tra	0,7
" 

" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	GET_ENTRY  -  returns pointer to empty entry
"
"	On return, apte is unthreaded, unlocked, and procs_required
"	is set to the system default. Other fields are cleared.
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

get_entry:
	tsplb	setup_mask
	tsx6	WRITE_LOCK	get_entry unthreads
	ldx0	bb|empty_q	x0 points to first entry on list
	tnz	available_entries	ok if non-zero
	eppbp	null,*		None available
	tra	get_entry_returns	So return
available_entries:
	lca	1,dl		Decrement count of free APTE's
	asa	bb|statistics+empty	Caller must AOS new state
	ldx1	bb|apte.thread,0	thread entry out of free list
	stx1	bb|empty_q
	eppbp	bb|0,0
	tsx6	LOCK_bp		get_entry locks before zeroing
	mlr	(),(pr),fill(0)	Zero out APTE
	desc9a	0,0
	desc9a	bp|0,size_of_apt_entry*4
	ldx0	apte.default_procs_required,du  system default
	orsx0	bp|apte.flags
	lda	bb|default_procs_required
	ana	apte.procs_required_mask,du
	drlze	(pxss: APTE disdains all processors) never happen
	sta	bp|apte.procs_required
	tsx6	UNLOCK_bp		get_entry makes apte lockable
get_entry_returns:
	tsx6	UNLOCK		get_entry done
	tsx7	switch_back_ret
	spribp	ap|2,*		return pointer to new APT entry
	short_return


"

" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	SET_WORK_CLASS -- entry to move a process from one work_class to
"	another.  Call is:
"		call pxss$set_work_class (processid, new_wc, old_wc, code)
"
"	processid	-bit (36) aligned specifies process (INPUT)
"	new_wc	-fixed bin specifies new work class (INPUT)
"	old_wc	-fixed bin previous value of work_class (OUTPUT)
"	code	-fixed bin. 0=>OK, 1=>bad_processid, 2=>bad_work_class (OUTPUT)
"
"	The steps are:
"	1. Find apte given processid.
"	2. Compute old_wc from apte.wct_index.
"	3. Compute new wct_index from new_wc.
"	4. If ready & not eligible move to new queue.
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

set_work_class:
	tsx6	setup_
	lda	ap|2,*		get processid
	sta	pds$arg_1
	lda	ap|4,*		get new_wc
	sta	pds$arg_2		into wired storage.

	tsplb	setup_check	switch stacks and lock
	arg	0,6
	tsx6	WRITE_LOCK	set_work_class rethreads, protects pid
	stz	pds$arg_3		Clear old_wc now.
	lda	1,dl		Assume processid bad,
	sta	pds$arg_4		set code to 1.
	tsx7	hash_LOCK
	arg	swc_ret_nul	indirect if error
	lda	bp|apte.flags	If process is idle
	cana	apte.idle,du	skip craziness.
	tnz	swc_ret		Return code = 1.

	aos	pds$arg_4		Preset code to 2, bad new_wc
	ldq	bp|apte.wct_index	Pick up old index into WCT.
	eax1	0,qu		oldwc in X1
	sbq	bb|min_wct_index	Convert it to a number.
	div	size_of_wct_entry,du The first wc_num is zero.
	stq	pds$arg_3		Reurn old_wc.

	ldq	pds$arg_2		Pick up new wc number.
	tmi	swc_ret		Return code = 2, bad new_wc.
	mpy	size_of_wct_entry,du Convert number to index.
	adlq	bb|min_wct_index
	tmi	swc_ret		user gave us very big wc
	cmpq	bb|max_wct_index	Make sure not oob on WCT
	tpnz	swc_ret		Return code = 2, bad new_wc.
	lda	bb|wcte.flags,qu	Make sure this wc is defined.
	cana	wcte.defined,du
	tze	swc_ret		Return code = 2, bad new_wc.
	stbq	bp|apte.wct_index,60 OK- set new value.
	stz	pds$arg_4		Clear code now.

	szn	bp|apte.thread	Threaded in some queue?
	tze	swc_ret		No. All done.
	lda	bp|apte.flags	Yes.
	cana	apte.eligible,du	In the eligible queue?
	tnz	swc_elig		Yes. Don't mess around.
	tsx7	unthread		Not eligible, remove from curr rdyq.
	lda	bp|apte.ts	If ts is non-zero then sort in before
	sta	before		but if ts is zero, sort in after.
	lda	bp|apte.ti	Prepare to jump into sort subr
	tsx7	sort_in_again	which requires ti in the A.
swc_ret:
	tsx6	UNLOCK_bp		set_wc unlocks APTE
swc_ret_nul:
	tsx6	UNLOCK		set_wc unlocks
	lxl2	pds$arg_4		Get pseudo errcode
	tsx7	switch_back_ret
	lda	pds$arg_3		Return output args to caller.
	sta	ap|6,*
	xec	swc_code_table,2	Load A with real errcode
	sta	ap|8,*

	short_return

swc_elig:
	lca	1,dl
	asa	bb|wcte.nel,1	Reduce former wc
	aos	bb|wcte.nel,qu	Increm new
	tra	swc_ret
"
"Table to map err = 0|1|2 to real (but unwired) error_code
swc_code_table:
	eaa	0		No error
	lda	error_table_$bad_processid
	lda	error_table_$bad_work_class



"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	GUARANTEED_ELIGIBILITY_ON - this primitive guarantees that this
"	process has at least a certain minimum amount of time left in its
"	eligibility quantum. The primitive does not return until the process will
"	retain eligibility for the specified minimum time. Also if the process
"	loses its eligibility it is given a higher priority scheduling quaranteeing
"	the process some fixed percentage of an eligibility slot. The high priority
"	scheduling is in effect until turned off.
"
"	GUARANTEED_ELIGIBILITY_OFF - this primitive turns off the high
"	priority scheduling granted by guarranteed_eligibility_on.
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

guaranteed_eligibility_on:
	eppbp	pds$apt_ptr,*	set bp-> own apte
	lda	apte.prior_sched,dl	Turn on high priority mode
	orsa	bp|apte.flags2
	lda	4,du		Boost time slice
	asa	bp|apte.temax
	short_return

guaranteed_eligibility_off:
	eppbp	pds$apt_ptr,*	set bp-> own apte
	lca	apte.prior_sched+1,dl turn off priority scheduling
	ansa	bp|apte.flags2	..
	lda	bp|apte.temax	Reduce time slice if need be
	tmi	ge_off_ret
	sba	4,du
	tpl	*+2		Leave positive, tho ..
	lda	1000,dl
	sta	bp|apte.temax
ge_off_ret:
	short_return
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	RELINQUISH_PRIORITY
"
"	This primitive lowers the traffic control priority of the
"	invoking process by moving it to the tail of the eligible queue.
"	This is intended for long-running ring-0 functions which operate
"	as background (e.g., the scavenger). A long-running ring-0
"	function migrates to the head of the eligible queue and thereby
"	gains a high priority.
"	
"	call pxss$relinquish_priority
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

relinquish_priority:
	tsplb	setup_mask		" Switch stacks and mask
	tsx6	WRITE_LOCK		" We rethread
	tsx6	LOCK_bp			" And change state

	tsx7	unthread			" Remove from eligible queue
	ldx0	ready,du
	tsx7	update_execution_state	" Change state from running
	eax1	bb|eligible_q_tail		" Thread to end of eligible queue
	tsx7	thread_him_in
	
	tsx6	UNLOCK_bp
	tsx6	UNLOCK
	aos	bb|relinquishes		" Meter
	tsx7	getwork
	tra	switch_back_pds

"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	SET_TIMER  -- entry to set the per-process cpu timer.
"	Call is:
"		call pxss$set_timer(delta_t, ev_channel)
"
"	dcl delta_t fixed bin (35),	/* time to be added to current time.
"	    ev_channel fixed bin (71)
"
"	If ev_channel is zero an IPS signal will be sent rather than
"	a wakeup.
"	Nothing need be locked for setting timer, provided the
"	time_out cell is cleared while operating.  This is so getwork
"	will ignore the timer channel if we happen through getwork.
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

set_timer:
	eppbp	pds$apt_ptr,*	Get ptr to own apte
	fld	0,dl		Put zero in AQ
	staq	pds$timer_time_out	Cancel any timer
	ldaq	ap|4,*		Now safe to set channel
	staq	pds$timer_channel

	fld	0,dl		Want two words but given one
	ldq	ap|2,*		Pick up delta_t
	tmoz	zero_chan		if delta_t <= 0 then reset
	tra	rel_time		else set relative timer

set_cpu_timer:
	eppbp	pds$apt_ptr,*	Get ptr to own apte
	fld	0,dl		Cancel any timer
	staq	pds$timer_time_out	Now if we getwork we will not have timer go off
	ldaq	ap|6,*		safe to set channel now
	staq	pds$timer_channel

	ldaq	ap|2,*		Put time arg in AQ

	lxl0	ap|4,*		pick up timesw arg
	cmpx0	2,du		Absolute timer?
	tze	abs_time		If so go do it
	cmpx0	1,du		Relative timer?
	tze	rel_time		If so then relative

zero_chan:
	fld	0,dl		resetting--timeout already zero
	staq	pds$timer_channel
	short_return

rel_time:	adaq	bp|apte.virtual_cpu_time
abs_time:	staq	pds$timer_time_out	Now safe to set timeout
	short_return
" 
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	PXSS$LOCK_APT		PXSS$UNLOCK_APT
"
"	Externally available entries to manipulate
"	apt lock.  Caller must be wired and masked.
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

lock_apt:	push
	tsx6	WRITE_LOCK	Give caller protection.
	return


unlock_apt:
	push
	tsx6	UNLOCK
	return

"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " 
"
"	PXSS$LOCK_APTE
"
"	Externally available routine to lock an APTE
"
"	call pxss$lock_apte (processid, aptep, code)
"
"	processid  is Input
"	aptep is set on return (null if APTE) not locked
"	code = 0 => APTE locked
"	     ^=0 => processid invalid
"
"	Caller must be wired and masked and real careful
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " 

lock_apte:
	push
	eppbb	tc_data$
	lda	ap|2,*		processid
	sta	pds$arg_1		for hash_LOCK
	stc1	ap|6,*		non-zero return code
	tsx7	hash_LOCK		Try to lock APTE
	arg	lock_apte_null	Fail
	stz	ap|6,*		Succeed - zero return code
lock_apte_null:
	spribp	ap|4,*		aptep
	return

" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " 
"
"	PXSS$UNLOCK_APTE
"
"	Externally available entry to unlock an APTE
"
"	call pxss$unlock_apte (aptep)
"
"	aptep is a pointer to the APTE to unlock
"	caller should still be masked and wired
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " 

unlock_apte:
	push
	eppbp	ap|2,*		ptr to aptep
	eppbp	bp|0,*		aptep
	epbpbb	bp|0		tc_data
	tsx6	UNLOCK_bp		Unlock APTE
	return
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " 
"
"	Subroutines to lock and unlock the APT
"	Meaning of lock values is
"	+N	APT is locked for single writer with caller||cpu_tag
"	0	lock is busy/hidden/out_to_lunch
"	-1	APT is unlocked
"	-(N+1)	APT is locked for N readers
"
"	Note that the lock value is claimed by a
"	primitive LDAC and restored by a primitive STA.
"	Note that lock_loops take special care not to
"	keep lock in busy state (~anti-hog hardware)
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " 

READ_LOCK:
	read_clock		" setup for metering looplocks
	staq	temp
	eax5	0		but assume no need to meter looplocks
read_:
	ldac	tc_data$apt_lock
	tpnz	r_put		was write locked
	tze	read_		was busy try again
	sba	1,dl		was unlocked or read locked
	sta	tc_data$apt_lock	one more reader
rw_ret:	eax5	0,5		See if looplock metering needed
	tze	0,6
	read_clock	
	sbaq	temp
	asq	tc_data$loop_lock_time
	aos	tc_data$loop_locks
	tra	0,6

r_put:	sta	tc_data$apt_lock	restor locks state
r_wait:	szn	tc_data$apt_lock	loop till unlocked or readlocked
	tmi	read_		was unlocked or read locked
	eax5	1		force looplock metering
	tra	r_wait		was busy or write locked


WRITE_LOCK:
	read_clock	
	staq	temp
	eax5	0
write_:
	ldac	tc_data$apt_lock
	cmpa	unlocked_APT
	tnz	w_fail		busy or lockd for read or write
	eaa	0,6		lock with caller||cpunum
	ada	prds$processor_tag
	sta	tc_data$apt_lock
	tra	rw_ret		join looplock meter code
w_fail:	cmpa	0,dl		if was not busy
	tze	w_wait
	sta	tc_data$apt_lock	then restor lock value
w_wait:	lda	unlocked_APT	loop till unlocked
	cmpa	tc_data$apt_lock
	tze	write_
	eax5	1		force looplock metering/
	tra	w_wait


UNLOCK:	ldac	tc_data$apt_lock
	tmi	unread
	tze	UNLOCK		busy so retry
	lda	unlocked_APT	was write locked so now unlock
	tra	unlock_
unread:	ada	1,dl		one less reader
	drlze	(pxss: unlock apt read lock bad count) DEBUG somebody lostcount
unlock_:	sta	tc_data$apt_lock
	tra	0,6


WRITE_TO_READ:
	ldac	tc_data$apt_lock
	tpnz	w_to_r		got lock
	tze	WRITE_TO_READ
	drltra	(pxss: write_to_read bad lock count) somebody lost count
w_to_r:
	cmpa	tc_data$apt_lock	DEBUG
	drlze	(pxss: write_to_read ldac failed) DEBUG ldac must have failed to clear
	lca	2,dl		set lock value to one reader
	sta	tc_data$apt_lock
	tra	0,6

unlocked_APT:
	dec	-1

"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " 
"
"	APTE LOCKING PROCEDURES
"	apte is locked if apte.lock = 0
"	apte is unlocked if apte.lock ^= 0
"	Note that address of caller of unlock is saved in apte.lock
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " 

LOCK_me_bp:
	eppbp	pds$apt_ptr,*	get ptr to own apte
	epbpbb	bp|0		get ptr to base of tcdata
LOCK_bp:
	sznc	bp|apte.lock
	tnz	0,6
	tra	LOCK_bp

UNLOCK_bp:
	szn	bp|apte.lock	DEBUG
	drlnz	(pxss: UNLOCK_bp not locked) DEBUG
	stx6	bp|apte.lock	Remember last unlocker
	tra	0,6

LOCK_x2:
	sznc	bb|apte.lock,2
	tnz	0,6
	tra	LOCK_x2

UNLOCK_x2:
	szn	bb|apte.lock,2	DEBUG
	drlnz	(pxss: UNLOCK_X2 not locked) DEBUG
	stx6	bb|apte.lock,2	Remember last unlocker
	tra	0,6

LOCK_x3:
	sznc	bb|apte.lock,3
	tnz	0,6
	tra	LOCK_x3

UNLOCK_x3:
	szn	bb|apte.lock,3	DEBUG
	drlnz	(pxss: UNLOCK_x3 not locked) DEBUG
	stx6	bb|apte.lock,3	Remember last unlocker
	tra	0,6

"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	stack switching subroutines
"
"	A routine which holds the global traffic control lock or an
"	APTE lock cannot be interrupted in a way which would cause
"	reinvocation of pxss.  For this reason, pxss must run on the
"	PRDS.  However, most of its entries may be called from an
"	unwired environment.  To accomplish this, arguments to pxss
"	are copied into wired storage in the PDS prior to any stack
"	switching.  This copying may result in loss of control of
"	a CPU and reinvocation of pxss (e.g., because of a page
"	fault), and overwriting of the PDS cells used for argument
"	storage.  To avoid this interference, all entries of pxss
"	which can be called from an unwired environment adhere to
"	following protocol:
"
"	tsx6	setup_
"
"        	  (Copy arguments to PDS and set any flags used as temporaries
"	   in the PDS - x6 and pr0 must be preserved in this code)
"
"	tsplb	setup_check
"	arg	0,6
"	  (Next Instruction)
"
"	On return to (Next Instruction), we are running on the PRDS
"	with interrupts masked, and the values of all temporaries in
"	the PDS are guaranteed to be those set between the call to
"	setup_ and the call to setup_check.  These values are
"	guaranteed to be safe until either getwork is called or one of
"	the routines to switch stacks back is called.
"
"	Routines which can be called only from a wired environment
"	must set the cell pds$pxss_args_invalid to non-zero if they
"	use ANY temporaries in the PDS.
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	setup_
"
"	subroutine to mark begin of argument copy to PDS
"
"	tsx6	setup_
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

setup_:	stz	pds$pxss_args_invalid	clear interference flag
	tra	0,6			return to next instruction

" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	setup_check
"
"	subroutine to check for successful copy of temporaries
"
"	tsplb	setup_check
"	arg	<return if args must be re-copied>
"	<return if args copied successfully, wired and masked>
"
"	On successful return, bp -> apte for this process
"	                      bb -> base of tc_data
"		            ap has been saved in pds$tc_argp
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

	inhibit	on		<+><+><+><+><+><+><+><+><+><+><+><+><+><+><+><+><+>	
setup_check:
	sznc	pds$pxss_args_invalid Did we get overwritten?
	tnz	lb|0,*		Cause err exit, get args copied again.
	epplb	lb|1		Fix return address.

"	Fall through to setup_mask

" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	setup_mask
"
"	mask to sys_level, save current mask in pds$tc_mask (unless on PRDS) and
"	switch stacks to PRDS (by invoking setup)
"
"	tsplb	setup_mask
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

setup_mask: 
	spriap	pds$tc_argp
	eppab	sp|0
	ldx0	lp|prds_link	are we on the PRDS
	cmpx0	sb|stack_header.stack_begin_ptr
	tze	easy		yes - already masked - fall thru
	lxl1	prds$processor_tag
	lprpab	scs$mask_ptr,1	get set for masking
	xec	scs$read_mask,1
	staq	pds$tc_mask	THIS MUST BE WIRED!
	ldaq	scs$sys_level
	xec	scs$set_mask,1

"	Fall through to setup 
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	setup
"
"	switch stacks to PRDS.  If stacks are indeed
"	switched (not running on PRDS already), the current stack
"	pointer is saved in pds$last_sp
"
"	setup is intended to be called directly only by page control
"	"side doors", where it is known that we are running on
"	the PRDS.  These "side doors" must not call any switch_back_xxx
"	routines, as this would re-load an interrupt mask saved elsewhere.
"
"	tsplb	setup
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
	
setup:
	eppab	sp|0		save stack pointer
	ldx0	lp|prds_link	are we on prds ?
	cmpx0	sb|stack_header.stack_begin_ptr
	tze	easy		yes, easy save

	sprisp	pds$last_sp	save pointer to previous stack frame
	eppsp	prds$+stack_header.stack_begin_ptr,* get ptr to new frame
	epbpsb	sp|0		get stack base ptr
easy:	push
	spriab	sp|stack_frame.prev_sp save prev sp
	eppbp	pds$apt_ptr,*	get own apt pointer
	epbpbb	bp|0		set bb to point at base of tc_data
	tsx6	init_pxss_save_stack
	tra	lb|0		return

"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	routines to switch back to calling environment
"
"	These routines all restore the interrupt mask to the value
"	saved in pds$tc_mask and the value of ap to the value
"	saved in pds$tc_argp
"
"	tsx7	switch_back_xxx
"	(Next Instruction)
"
"	switch_back_pds - return to caller of pxss on unwired stack
"
"	switch_back - pop a stack frame, switching stacks if necessary,
"		return to caller of pxss
"
"	switch_back_ret - pop a stack frame, switching stacks if necessary,
"		return to (Next Instruction)
"
"	switch_back_ret_pds - switch to unwired stack, return to
"		(Next Instruction)
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
	
switch_back_pds:			"return uncond. to pds history
	eax7	0
	tra	restore_sp

switch_back:
	eax7	0		flag to distinguish from switch_back_ret
switch_back_ret:
	ldx0	lp|prds_link	do we have to switch stacks
	cmpx0	sp|stack_frame.prev_sp ..
	tnz	restore_sp	yes, skip simple switch
	eppsp	sp|stack_frame.prev_sp,* go back one frame
	tra	no_change_mask	and keep masked
restore_sp:
switch_back_ret_pds:
	eppsp	pds$last_sp,*	switch to other stack
	ldaq	prds$+stack_header.stack_begin_ptr restore stack end ptr
	staq	prds$+stack_header.stack_end_ptr ..

	epbpsb	sp|0
	ldaq	pds$tc_mask	restore previous mask
	oraq	channel_mask_set
	anaq	scs$open_level
	lxl1	prds$processor_tag
	lprpab	scs$mask_ptr,1	get set for masking
	xec	scs$set_mask,1
no_change_mask:
	stc1	pds$pxss_args_invalid Signal possible mutual interference.
	eax7	0,7		check who made call
	tze	short_ret
	eppap	pds$tc_argp,*	Get pxss arg list
	tra	0,7

	inhibit	off		<-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><->

init_pxss_save_stack:
	eaa	pxss_save_stack	get address of save stack base
	ora	pxss_stack_size*64,dl set up a tally word for storing into pxss_save_stack
	sta	pxss_stackp	stash this away
	tra	0,6		return to caller


subroutine_save:
	stx7	pxss_stackp,id	store x7 in save stack using tally word
	ttf	0,6		return to the caller if tally not runout
	drltra	(pxss: subroutine_save stack overflow) die - we have run off the save stack

subroutine_unsave:
	ldx7	pxss_stackp,di	pop value of x7 (also updating tally word properly)
	tra	0,7

send_connect:
	ldq	bp|apte.flags	see if processor to be stopped is running
	canq	apte.dbr_loaded,du	by checking the dbr-loaded flag
	tze	0,7		not running, return

	eax0	bp|0		APTE offset in X0
	cmpx0	pds$apt_ptr+1	is this process target of connect?
	tze	delay_connect	if so, just set ring alarm for now

	ldq	bp|apte.flags2	is running- must send cpu stop connect
	anq	apte.pr_tag_mask,dl	leave only processor tag
	cioc	scs$cow_ptrs,ql*	zap the processor
	tra	0,7		return to caller

delay_connect:
	lda	1,dl		set ring alarm register
	sta	pds$alarm_ring	..
	lra	pds$alarm_ring	..
	tra	0,7		and return to caller

" 
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	MAKE_ITT_MESSAGE
"	
"	subroutine to make a wakeup-associated message,
"	allocate an entry for it in the ITT, and add this entry
"	to the tail of this process' event queue in the APT.
"
"	Caller must set pds$wakeup_flag to control whether non-unique
"	messages will be sent.
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

make_itt_message:
" bp points to process' APT entry and must not be changed

	bool	event_channel_type_mask,040000

	szn	bp|apte.lock	ASSUME bp locked
	drlnz	(pxss: APTE not locked) ASSUME bp locked
	stz	errcode		we have run into no problem yet

	lda	tmp_ev_channel+1	check for special channel
	ana	event_channel_type_mask,du mask everything but type
	tnz	mimj2		if not, continue

	lxl0	tmp_ev_channel+1	get special channel index
	lda	=o400000,du	get wakeup bit
	arl	apte.chans_offset-1,0 move to correct cell
	ana	apte.special_chans,dl change only special channels
	orsa	bp|apte.flags2	put in APT
	tra	0,x7		" return

mimj2:
	ldac	bb|itt_free_list	Grab head
	tmi	itt_overflows	there are no more entries available
	tze	*-2

	eax1	0,au		move head to X1
"				XR0 contains forward thread
	ldx0	bb|itt_entry.next_itt_relp,1
	stx0	bb|itt_free_list	restor free-list-head

	aos	bb|used_itt	count ITT entry usage
	lda	tmp_ring		get caller's validation ring
	sta	bb|itt_entry.origin,1 put ring number in ITT message
	lda	pds$processid	get this process (sender's) ID
	sta	bb|itt_entry.sender,1 put sender ID in ITT message
	eax3	0,au		X3 -> sender
	lxl0	dev_signal	get origin flag(1=dev-signal)
	tnz	*+2		if not dev_signal
	aos	bb|apte.ittes_sent,3 charge sender
	stx0	bb|itt_entry.origin,1 store in origin LHE
	aos	bp|apte.ittes_got	charge rcvr
	lda	pds$arg_1		get processid of target
	sta	bb|itt_entry.target_id,1 store target process' ID in ITT message
	ldaq	tmp_ev_message	get event message
	staq	bb|itt_entry.message,1 put event message in ITT entry
	ldaq	tmp_ev_channel	get target event channel's name
	staq	bb|itt_entry.channel_id,1 put channel name in ITT message, and AQ-reg
"				zero thread this entry will be the last
	stz	bb|itt_entry.next_itt_relp,1

"
"Check situation and counts to see if we allow usage of itte.
	stz	count		if count is zero there is no per channel limit
	eax5	0
	lda	bb|used_itt	if U < C/4 then no checks
	cmpa	bb|cid4
	tmi	mim_check_done
	lda	bb|initializer_id	load A-reg for mi_iz_xxx
	ldx0	bb|itt_entry.origin,1 dev_signals are different
	tnz	mim_check_ds	nz means dev_signal
"Checks for non-dev_signal
	ldq	bb|cid2
	tsx6	mm_iz_either	D = C/2 or 0
	adq	bb|cid3
	cmpq	bb|apte.ittes_sent,3
	tmi	unmake_message	if sent > D + C/3 then REJECT
	cmpq	bp|apte.ittes_got
	tmi	unmake_message	if got > D + C/3 then REJECT

	sbq	bb|cid3
	adq	bb|cid2
	sbq	bb|apte.ittes_sent,3
	sbq	bp|apte.ittes_got
	tmi	unmake_message	if sent+got > D + C/2 then REJECT

	ldq	bb|itt_size
	sbq	bb|used_itt
	sbq	bb|apt_size
	tmi	unmake_message	if U > C then REJECT
	tra	mim_check_done

"Checks for dev_signals
mim_check_ds:
	ldq	bb|cid4
	cmpq	bp|apte.ittes_got	if got <  C/4 then OK
	tpl	mim_check_done
	qls	18		else chan_limit =  C/4
	stq	count
mim_check_done:
" append this message to the tail of the process' event queue
	ldx0	bp|apte.ipc_pointers get head of process' event message queue
	tnz	find_queue_end	go follow queue to end
	eax0	0,1		load XR0 from XRR1
	stx0	bp|apte.ipc_pointers this entry is first in q
	tra	0,7		end of event message production
find_queue_end:
	ldaq	tmp_ev_channel	event channel for compare
	szn	pds$wakeup_flag	should we insure uniqueness?
	tze	mumloop		yes - go to that loop
	szn	count		must we count wkups on chan?
	tnz	mumloop		yes - go to that loop

mimloop:
"				get forward thread from next entry
	ldx4	bb|itt_entry.next_itt_relp,0
	tze	append_itt_message	it is the end of the queue
	eax0	0,4		XR0 points to next entry
	tra	mimloop

mumloop:	cmpaq	bb|itt_entry.channel_id,0 this chan = new?
	tze	mum_ck
mum_ok:	ldx4	bb|itt_entry.next_itt_relp,0
	tze	maybe_append_itt_message x0 -> last itte
	eax0	0,4
	tra	mumloop

mum_ck:
	eax5	1,5		Another itte for this channel
	szn	pds$wakeup_flag
	tnz	mum_ok		not unique entry, just counting
	ldaq	tmp_ev_message	is msg = new?
	cmpaq	bb|itt_entry.message,0
	tnz	mum_ck_ret	not same - go fix AQ then loop more
	ldaq	bb|itt_entry.origin,1 test origin,ring,target
	cmpaq	bb|itt_entry.origin,0
	tze	unmake_message	everthing same=> dont need itte
mum_ck_ret:
	ldaq	tmp_ev_channel	restor chan to AQ for compare
	tra	mum_ok


maybe_append_itt_message:
	szn	count
	tze	append_itt_message
	cmpx5	count
	tpnz	unmake_message	too many for this chan
append_itt_message:
"				thread new entry to end of queue
	stx1	bb|itt_entry.next_itt_relp,0
	tra	0,7

unmake_message:
	lca	1,dl		meter ITTE non-usage
	ldx4	bb|itt_entry.origin,1
	tnz	um_is_ds
	asa	bb|apte.ittes_sent,3
um_is_ds:	asa	bp|apte.ittes_got
	asa	bb|used_itt
	lda	200,dl
	sta	errcode		200 means wakeup not sent (itt_ovfl)

	ldac	bb|itt_free_list	give back itt_entry
	tnz	*+2		ok -free list locked
	tra	*-2		loop on itt_free_list lock
	eax0	0,au		x0 -> former top free itte
"				put that relp in returned itte
	stx0	bb|itt_entry.next_itt_relp,1
	stx1	bb|itt_free_list
	tra	0,7
itt_overflows:
	drltra	(pxss: ITT overflows) error condition
" at this point something must be done to decongest the ITT
	tra	0,7
"
"Subr called via x6 to set Q to zero if not Iz
"Iz pid must be in A, Iz delta in Q, X3 must -> sender
mm_iz_either:
	cmpa	bb|apte.processid,3
	tze	0,6
mm_iz_rcvr:
	cmpa	bp|apte.processid
	tze	0,6
	eaq	0
	tra	0,6
" 
"come here for various functions during initialization
pw1:
	eppap	pds$apt_ptr,*
	ldac	ap|apte.wait_event	get wait event
	sta	tc_data$init_event	save the event

	inhibit	on		<+><+><+><+><+><+><+><+><+><+><+>
pi_wait:
	lxl1	prds$processor_tag	get processor tag for masking
	lprpab	scs$mask_ptr,1
	xec	scs$read_mask,1	find current mask
	staq	pds$tc_mask	and save for return
	ldaq	scs$open_level	open up mask to await interrupt
	xec	scs$set_mask,1

	sprisp	pds$last_sp	save sp because we will change it to non-PRDS
	eppsp	null,*		

	read_clock
	staq	tc_data$init_wait_time	save time of wait
	
check_pi_event:
	lca	1,dl		for display
	ldq	tc_data$init_event
	tze	pi_wait_ret	notify has occured
	
	ldt	=o200,du		about 1/8 second
	inhibit	off		<-><-><-><-><-><-><-><-><->
	dis	0
	inhibit	on		<+><+><+><+><+><+><+><+><+>

	szn	tc_data$init_event	0 => event has occurred
	tze	pi_wait_ret
	read_clock		" check for notify-time-out
	sbaq	tc_data$init_wait_time " aq = time waiting
	cmpaq	tc_data$init_wait_timeout  " more than allowed
	tmi	check_pi_event	" no -wait some more

pi_wait_ret:
	eppsp	pds$last_sp,*	restore stack pointer
	epbpsb	sp|0		..
	ldaq	pds$tc_mask	must be pwait or page_wait
	xec	scs$set_mask,1

	szn	tc_data$init_event	did we NTO
	tze	pi_wait_ret_con	no--event occurred

" check for possible disk polling

	szn	pds$pc_call
	tmi	pi_wait_no_poll	normal wait

	ldaq	scs$sys_level	mask down for poll sake
	xec	scs$set_mask,1

	lxl3	disk_poll_entry	get address of routine to call
	call	lp|0,3*(null_arglist) make call to disk poller

	ldaq	pds$tc_mask
	xec	scs$set_mask,1

	szn	tc_data$init_event	did we NTO
	tze	pi_wait_ret_con	no--event occurred

pi_wait_no_poll:
	szn	tc_data$init_timeout_severity	do we want to print NTO message
	tmi	pi_nto_no_message	no
	szn	tc_data$init_timeout_recurse	are we recursing
	tze	pi_call_syserr	no
pi_nto_no_message: 
	stz	tc_data$init_event	just notify, don't blow the whistle
	tra	pi_wait_ret_con


"	call 	syserr (tc_data$init_timeout_severity, "pxss:  notify time out:  event=^w. During init/shutdown.")

pi_call_syserr:
	aos	tc_data$init_timeout_recurse
	push
	epplb	pds$last_sp,*
	sprilb	pre_temp
	lda	pds$pc_call
	sta	pre_temp+2
	epplb	tc_data$init_timeout_severity
	sprilb	arg+2
	epplb	pi_timeout_mess
	sprilb	arg+4
	epplb	tc_data$init_event
	sprilb	arg+6
	epplb	fixed_desc
	sprilb	arg+8
	sprilb	arg+12
	epplb	pi_timeout_desc
	sprilb	arg+10
	ldaq	=v18/6,18/4,18/6,18/0
	staq	arg
	call	syserr$syserr(arg)
	epplb	pre_temp,*
	sprilb	pds$last_sp
	lda	pre_temp+2
	sta	pds$pc_call
	stz	tc_data$init_event
	lca	1,dl
	asa	tc_data$init_timeout_recurse
	tra	pi_wait_ret_con

pi_timeout_mess:
	aci	"pxss:  notify time out:  event=^w. During init/shutdown."
	equ	pi_timeout_mess_words,*-pi_timeout_mess
	equ	pi_timeout_mess_char,4*pi_timeout_mess_words
pi_timeout_desc:
	vfd	1/1,6/21,5/0,24/pi_timeout_mess_char
"

pi_wait_ret_con: 
	eppsp	pds$last_sp,*	restore machine state
	szn	pds$pc_call	check if pc wait or not
	tze	*+2		don't reset ap for page_fault
	eppap	sp|stack_frame.operator_ptr,*
	tmi	nwait_ret		normal wait
	szn	pds$pc_call	is the a pc call?
	tze	page_fault$wait_return no
	tra	device_control$pwait_return

nwait_ret:
	rtcd	sp|stack_frame.return_ptr
	inhibit	off		<-><-><-><-><-><-><-><-><-><->
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	ADDEVENT -- entry to set up event into APT entry prior
"	to a call to wait.
"	There is no need to lock anything.
"	Call is
"		call pxss$addevent(event)
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

addevent:
	ldq	ap|2,*		pick up input event
	tnz	*+2		event must be non-zero
	ldq	=o707070,dl	so force it so
	szn	tc_data$wait_enable	during initialization ?
	tze	pi_add		yes, special code
	eppbp	pds$apt_ptr,*
	stq	bp|apte.wait_event	store event in APT entry
short_ret:
	short_return

pi_add:
	stq	tc_data$init_event
	tra	short_ret

fixed_desc:
	oct	404000000005


"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	DELEVENT -- entry to remove interest in event.
"	Call is
"		call pxss$delevent (event)
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

delevent:
	szn	tc_data$wait_enable
	tze	pi_notify
	eaa	0
	ldq	ap|2,*		pick up event
	eppbp	pds$apt_ptr,*	get ptr to own apte
	stacq	bp|apte.wait_event
	short_return
" 
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	PAGE_WAIT -- special purpose entry to wait for a page.
"
"	This entry is a combination of pxss$addevent and pxss$wait.
"	The argument (wait event) is passed in pds$arg_1.
"	Called by page_fault, returns to page_fault$wait_return to
"	restart a page fault.
"
" " " " " " " " " " " " " " " " " " " " " "" " " " " " " " " "

page_wait:
	stc1	pds$pxss_args_invalid Signal overwriting of temporaries.
	stz	pds$pc_call	set flag for page_wait, vs. waitp.
	szn	tc_data$wait_enable	see if during initialization
	tze	pw1		yes, special code
	tsx6	init_pxss_save_stack init x7 save stack
pwait:				"COME FROM WAITP
	tsx7	update_te		update times

	tsx6	LOCK_bp		-- Wait --
	szn	bp|apte.wait_event	wait?
	tze	UNLOCK_bp_unpwait	no -event may have occured
	ldx0	waiting,du	set state to waiting
	tsx7	update_execution_state
	tsx6	UNLOCK_bp		page_wait to call getwk
	lda	apte.page_wait_flag,du set flag indicating page wait
	orsa	bp|apte.flags	set flag ON in APT entry
	aos	bb|waits
	ldx0	prds$depth	get depth in queue
	aos	bb|pfdepth,0	for metering
	tsx7	getwork
	read_clock		" meter time ready after notify
	sbaq	bp|apte.state_change_time
	ldx0	prds$depth	get depth in queue
	adx0	prds$depth	multiply depth by 2
	aos	bb|readytime,0
	asq	bb|readytime+1,0
unpwait:
	szn	pds$pc_call	see if page_wait or waitp
	tze	page_fault$wait_return page_wait
waitp_return:
	eppsp	pds$last_sp,*	restore pds stack history
	epbpsb	sp|0		makes dvctl happy
	ldaq	prds$+stack_header.stack_begin_ptr reset prds too
	staq	prds$+stack_header.stack_end_ptr ..
	tra	device_control$pwait_return

UNLOCK_bp_unpwait:
	tsx6	UNLOCK_bp		if not waiting and APTE locked ..
	tra	unpwait
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	PAGE_PAUSE
"
"	This primitive is a combination of pause, which
"	causes the process to delay in ring 0 and page_wait.
"	It is used by page control when it must delay a page operation,
"	usually for defeating a covert channel.
"	This routine returns to page_fault$wait_return when done.
"
"	The time to delay until is in pds$arg_1.
"	
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

page_pause:
	stc1	pds$pxss_args_invalid	" overwriting temps
	stz	pds$pc_call		" indicates page_wait
	szn	tc_data$wait_enable
	tze	page_fault$wait_return	" pxss not setup to delay
	tsx6	init_pxss_save_stack	" init x7 save stack
	eppbp	pds$apt_ptr,*		" our apte
	epbpbb	bp|0			" tc_data

	tsx6	WRITE_LOCK		" changing next_ring0_timer
	lda	ring0_timer_event
	sta	bp|apte.wait_event		" we are waiting for timer
	ldaq	pds$arg_1			" delay time
	cmpaq	bb|next_ring0_timer
	tpl	*+2			" timer will expire before our time
	staq	bb|next_ring0_timer		" our time comes first
	cmpaq	bb|next_alarm_time
	tpl	*+2
	staq	bb|next_alarm_time		" also update next system alarm
	tsx6	UNLOCK			" done updating
	aos	bb|pauses			" meter
	tra	pwait
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	PAUSE
"
"	This primitive causes the current process to pause in ring 0.
"	It is used when it is necessary to delay an operation,
"	usually for defeating a covert channel.
"
"	call pxss$pause (delay_time);
"
"	The delay_time is the desired wakeup time.  Note, though, that
"	this function may return before this time, so it is necessary
"	for the caller to repeat the call, if necessary.
"	
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

pause:	szn	tc_data$wait_enable
	tnz	pause.setup
	short_return			" pxss not setup to delay

pause.setup:
	tsx6	setup_
	ldaq	ap|2,*			" desired delay til time
	staq	pds$arg_1			" copy into wired place

	tsplb	setup_check		" switch stacks & mask
	arg	0,6

	tsx6	WRITE_LOCK		" changing next_ring0_timer
	tsx6	LOCK_me_bp		" rescheduling
	lda	ring0_timer_event
	sta	bp|apte.wait_event		" we are waiting for timer
	ldaq	pds$arg_1			" delay time
	cmpaq	bb|next_ring0_timer
	tpl	*+2			" timer will expire before our time
	staq	bb|next_ring0_timer		" our time comes first
	cmpaq	bb|next_alarm_time
	tpl	*+2
	staq	bb|next_alarm_time		" also update next system alarm
	aos	bb|pauses			" meter

	ldx0	waiting,du
	tsx7	update_execution_state
	tsx6	UNLOCK_bp
	tsx7	update_te
	tsx6	UNLOCK
	tsx7	getwork			" pause!
	tra	switch_back_pds
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	WAITP -- General purpose entry to wait for a page control event.
"
"	Called by device_control$pwait, we gain control 
"	masked		at sys level, wired stack, and event in pds$arg_1
"
"	Control is returned to caller of device_control$pwait.
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

waitp:
	stc1	pds$pxss_args_invalid Signal overwriting of temporaries.
	lda	1,dl
	sta	pds$pc_call	set switch for pds return
	szn	tc_data$wait_enable	see if in initialization
	tze	pw1		yes, do special code
	tsplb	setup		switch stacks
	tra	pwait


"


" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	WAIT -- entry called to wait for a predictably short time.
"	Call is
"		call pxss$wait
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

wait:
	szn	tc_data$wait_enable	in initialization ?
	tze	w1		yes, special code
	tsplb	setup_mask
	tsx6	LOCK_me_bp
	szn	bp|apte.wait_event	have we been notified yet?
	tze	wait_not		yes so return
	ldx0	waiting,du	set state to waiting
	tsx7	update_execution_state
	tsx6	UNLOCK_bp
	aos	bb|te_wait
	tsx7	update_te		update times
	tsx7	getwork
wait_returns:
	tra	switch_back_pds	return to pds stack history

wait_not:
	tsx6	UNLOCK_bp
	tra	switch_back

w1:	lca	1,dl		get negative flag
	sta	pds$pc_call
	tra	pi_wait
" 
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	PAGE_NOTIFY -- special porpose entry for notifies on pages
"
"	This entry is like notify except that an extra argument is passed.
"	The extra argument is the device ID of the device whose
"	page transfer has just completed. This is used for latency
"	metering.
"
"	The		event being notified is passed in pds$arg_1.
"	The		device ID is passed in pds$arg_2
"
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

page_notify:
	stc1	dev_signal	flag says special notify
pn2:	stc1	pds$pxss_args_invalid Signal overwriting of temporaries.
	szn	tc_data$wait_enable	during initialization?
	tze	pn1		yes, special code

	eppbb	tc_data$		set ptr to base of tcdata
	tsx6	init_pxss_save_stack init x7 save stack

	aos	bb|page_notifies
	tsx6	notify_		go notify
"""""	lxl5	x5		restore x5
	tra	notify_return
pn1:
	ldq	pds$arg_1		see if we are waiting for the event being notified
	cmpq	tc_data$init_event	is it what we're waiting for?
	tnz	notify_return	no, return
	stz	tc_data$init_event	yes, reset event
notify_return:
	tra	page$notify_return	
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	NOTIFY -- entry to notify that an event has happened.
"	Call is
"		call pxss$notify(event)
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

notify:
	szn	tc_data$wait_enable	in initialization ?
	tze	pi_notify		yes, special code
	tsx6	setup_
	ldq	ap|2,*		pick up event to notify
	tnz	*+2		can't allow zero, so use 707070
	ldq	=o707070,dl
	stq	pds$arg_1		save in PDS
	tsplb	setup_check	switch stacks and lock
	arg	0,6
	stz	dev_signal	set flag for normal return
	aos	bb|notifies
"
"Come here to notify event which is in pds$arg_1
notify_:
	stx6	x5		save index 6 (used by get_processor )
	tsx6	READ_LOCK		notify freezes threads
	eax2	bb|eligible_q_head	X2 -> eligible_q_head
	eax5	0		USELESS NOTIFY?
	ldq	pds$arg_1		Put event in Q-reg and stack
	stq	tmp_event
nfy_loop:
	ldx2	bb|apte.thread,2	go to next entry
	szn	bb|apte.flags,2	stop at sentinel
	tmi	nfy_ret
	cmpq	bb|apte.wait_event,2 check current APT for same event
	tnz	nfy_loop		not equal, skip
"
"	Fall thru here if must notify this process
	tsx6	LOCK_x2
	lcx0	apte.page_wait_flag+1,du turn off page wait flag
	ansx0	bb|apte.flags,2
	stz	bb|apte.wait_event,2
	eax5	1,5		USELESS NOTIFY?
	lxl0	bb|apte.state,2	make sure dont change running state
	cmpx0	waiting,du
	tnz	nfy_not_waiting	not waiting, dont change state
	ldx0	ready,du		set state to ready
	eppbp	bb|0,2		bp must be set for update..
	tsx7	update_execution_state
	tsx6	UNLOCK_x2
	eax7	nfy_restor	set ret addr for get_(idle_)processor
	szn	bb|gp_at_notify	see which flavor gp
	tze	get_idle_processor
	tra	get_processor
nfy_restor:
	ldq	tmp_event		Restore event to Q
	tra	nfy_loop		continue scan
nfy_not_waiting:
	tsx6	UNLOCK_x2
	tra	nfy_restor	restor back
nfy_ret:
	eax5	0,5		USELESS NOTIFY?
	tnz	*+3		USELESS NOTIFY?
	stq	bb|notify_nobody_event	USELESS NOTIFY?
	aos	bb|notify_nobody_count	USELESS NOTIFY?
	tsx6	UNLOCK		notify is done
	ldx6	x5		restore x6
	szn	dev_signal	check return flag
	tnz	0,6		return to caller
	tra	switch_back

pi_notify:
	ldq	ap|2,*		get event
	tnz	*+2		if non-zero, OK
	ldq	=o707070,dl	otherwise use special coded event
	cmpq	tc_data$init_event
	tnz	short_ret		not the right one
	stz	tc_data$init_event
	short_return
" 
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	PTL_WAIT -- wait for  page table lock
"
"	special cased to avoid cluttering up page_wait even further.
"
"	Note that the per APTE lock protects processes interest in
"	ptl_wait_ct.  ptl_wait_ct is >= num of (unlocked) processes in the
"	ptlocking state.  It is greater only temporarily, while
"	some process is making sure a notify is not lost.
"
" " " " " " " " " " " "  " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

ptl_wait:
	stc1	pds$pxss_args_invalid
	stz	pds$pc_call	set return switch
	eppbb	tc_data$
	tsx6	init_pxss_save_stack

ptlw_:	szn	sst$ptl	quick check on ptl
	tze	ptlw_ez		not locked - meter and retn
	tsx6	LOCK_me_bp
	ldx0	ptlocking,du	go ptlocking
	tsx7	update_execution_state now I cannot miss notify
	aos	sst$ptl_wait_ct guar notify next time ptl unlocked
	szn	sst$ptl	see if still locked
	tze	ptlw_not		no - dont wait
	tsx6	UNLOCK_bp
	tsx7	update_te
	aos	bb|ptl_waits	meter these
	tsx7	getwork
ptlw_ret:	szn	pds$pc_call	test return switch
	tze	page_fault$ptl_wait_return return to locking code
	tra	waitp_return	take fancy return to dvctl trylock

ptlw_not:	lca	1,dl
	asa	sst$ptl_wait_ct dont notify on my account
	ldx0	running,du	like delevent ..
	tsx7	update_execution_state
	tsx6	UNLOCK_bp
ptlw_ez:	aos	bb|ptl_not_waits	meter this window
	tra	ptlw_ret

dvctl_retry_ptlwait:
	lda	1,dl
	sta	pds$pc_call	set return switch
	tsplb	setup
	tra	ptlw_		join common ptlwait code

"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	PTL_NOTIFY -- notify one process that ptl is unlocked
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

ptl_notify:
	stc1	pds$pxss_args_invalid
	eppbb	tc_data$
	tsx6	init_pxss_save_stack
	tsx6	READ_LOCK		ptl_notify freezes threads

	lda	bb|eligible_q_head
ptln_loop:
	ldaq	bb|apte.thread,au
	tra	ptln_tv,ql*

	arg	ptln_ret		sentinel
ptln_tv:	arg	DRL_empty_apte	empty
	arg	ptln_loop		running
	arg	ptln_loop		ready
	arg	ptln_loop		waiting
	arg	DRL_blocked_apte	blocked
	arg	DRL_stopped_apte	stopped
	arg	ptln_found_ptlocking

" This is the place where jump indirects through tables come when they feel
" a need to punt. We lose a few hreg entries, but not so many as a arg *,*!

DRL_empty_apte:
	drltra	(pxss: untenable empty APTE)
DRL_blocked_apte:
	drltra	(pxss: untenable blocked APTE)
DRL_stopped_apte:
	drltra	(pxss: untenable stopped APTE)

ptln_found_ptlocking:
	szn	sst$ptl	is ptl actually unlocked now?
	tnz	ptln_ret		no - abort ptln

	lxl2	bb|apte.thread,au	make X2 -> ptlocking process
	tsx6	LOCK_x2		ptln locks to test and set state
	lxl0	bb|apte.state,2
	cmpx0	ptlocking,du
	tnz	ptln_not_ptlocking

	lca	1,dl
	asa	sst$ptl_wait_ct one fewer waiting for ptl
	eppbp	bb|0,2		set bp for ues
	ldx0	ready,du
	tsx7	update_execution_state
	tsx6	UNLOCK_x2

	eax7	ptln_ret		set ret addr for get_(idle_)processor
	szn	bb|gp_at_ptlnotify	see which flavor gp
	tze	get_idle_processor
	tra	get_processor

ptln_ret:	tsx6	UNLOCK
"""""	lxl5	x5		??????
	tra	core_queue_man$ptl_notify_return

ptln_not_ptlocking:
	tsx6	UNLOCK_x2
	tra	ptln_loop

"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	UPDATE_TE -- procedure to update the virtual cpu time used by
"	the running process into "te".
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

update_te:
	eppbp	pds$apt_ptr,*	get own apt pointer
	epbpbb	bp|0		set bb to point at base of tc_data
	szn	pds$vtime_count
	tpl	te_vtime_1
	read_clock	
	sbaq	pds$cpu_time
	tra	te_vtime_2
te_vtime_1:
	ldaq	pds$time_v_temp
te_vtime_2:
	sbaq	pds$virtual_delta
	sbaq	pds$virtual_time_at_eligibility
	stq	bp|apte.te
	tra	0,7



" 
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	USAGE_VALUES  -- procedure to return the total page faults
"	for this process as well as the total cpu time this process
"	has been charged with.
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

usage_values:
	szn	tc_data$wait_enable	see if during initialization
	tze	ret_zero		if so return zeros
	inhibit	on		<+><+><+><+><+><+><+><+><+><+><+><+><+><+><+><+><+>
	read_clock
	sbaq	pds$cpu_time	compute virtual time
	sbaq	pds$virtual_delta	convert to virtual cpu time
	staq	ap|4,*		return value
	ldq	pds$page_waits	also return page waits
	stq	ap|2,*		return value
	short_return
	inhibit	off		<-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><->

ret_zero:
	fld	0,dl		zero a-q
	staq	ap|4,*		return time of zero
	stz	ap|2,*		return zero page_waits
	short_return
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	SORT_IN -- procedure to sort a process into the ready list at 
"	the appropriate spot depending on his updated ti value.
"	bp must point to the APT entry for the process to be sorted 
"	in.
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

sort_in_before:
	stc1	before		here to sort in before those of same ti
	tra	*+2
sort_in:
	stz	before
	aos	bb|schedulings
	ldx0	apte.interaction,du	check interaction switch
	canx0	bp|apte.flags
	tze	sort_no_int	no interaction
	tsx0	setup_int		boost priority
	lcx0	apte.interaction+1,du
	ansx0	bp|apte.flags	turn off interaction bit

sort_no_int:
	lda	bp|apte.ti
	cmpa	bp|apte.timax	must always be less than timax
	tmi	*+2		ok, use new value
	lda	bp|apte.timax	must use max value
	sta	bp|apte.ti

	ldx0	bp|apte.flags
	canx0	apte.realtime_burst,du realtime boost
	tze	sort_in_again	no
	tsx0	setup_io_realtime	yes
	tra	realtime_sort
sort_in_again:
"old_assume_bp_not_eligible
"old_assume_bp_wct_index
	ldx1	bp|apte.wct_index	here to put in right rdy_q
	szn	bb|wcte.realtime,1	if realtime
	tnz	realtime_sort	sort into realtime_q
	szn	bb|deadline_mode	else if not percenting
	tnz	virtual_sort	then put in interactive_q
	lda	bb|wcte.interactive_q_word,1	See if interactive queue is
	cana	wcte.interactive_q,du	  enabled for this WC
	tze	ti_loop		Not enabled
"
"	here to sort into workclass or interactive queue by ti
ti_sort:	ldx4	bp|apte.ti	put ti in X4 for sort
	tnz	ti_loop		sort into X1 -> wc_q
	szn	bp|apte.ts	also if ts not zero
	tnz	ti_loop		sort into X1 -> wc_q
	szn	bb|int_q_enabled	also if int_q turned off
	tze	ti_loop		sort into X1 -> wc_q
	eax1	bb|interactive_q	else direct to tail of int_q
	tra	thread_him_in
ti_loop:
	ldx1	bb|apte.thread,1	chase to next
	szn	bb|apte.sentinel,1	put before sentinel
	tmi	thread_him_in
	cmpx4	bb|apte.ti,1	compare to this ti
	tpnz	ti_loop		if greater, go deeper
	tmi	thread_him_in	if less, put before
	szn	before		if equal and told to put before
	tnz	thread_him_in	then put before
	tra	ti_loop		else go deeper
"
"	various deadline sorts follow
realtime_sort:
	ldx0	apte.realtime_burst,du  mark as realtime
	orsx0	bp|apte.flags
	eax1	bb|realtime_q	here to put in realtime_q
	tra	deadline_sort
virtual_sort:
	eax1	bb|interactive_q	int_q has vir deadlines
deadline_sort:
	ldaq	bp|apte.deadline	here for general deadline sort
ds_loop:
	ldx1	bb|apte.thread,1	chase to next
	szn	bb|apte.sentinel,1	put before sentinel
	tmi	thread_him_in
	cmpaq	bb|apte.deadline,1	compare to this deadline
	tpl	ds_loop		sooner => fall thru to thread before
"
"Subroutine to thread unthreaded APTE at bp|0 before that at bb|0,1.
"
thread_him_in:
	szn	tc_data$apt_lock	ASSUME write_locked
	drlmi	(pxss: APT not locked) ASSUME write_locked
	szn	bp|apte.thread	ASSUME bp_unthreaded
	drlnz	(pxss: thread_him_in already threaded) ASSUME bp_unthreaded
	eax1	0,1		ASSUME x1_nonzero
	drlze	(pxss: thread_him_in x1 zero) ASSUME x1_nonzero
	lxl4	bb|apte.thread,1	thread new entry in here
	drlze	(pxss: thread_him_in x4 zero) ASSUME x4_nonzero
	cmpx1	bb|apte.thread,4	ASSUME x4->apte.fp = x1
	drlnz	(pxss: thread_him_in x4->apte.fp ^= x1) ASSUME x4->apte.fp = x1
	lxl0	bp|apte.state
	cmpx0	ready,du		ASSUME apte.state = "ready"
	drlnz	(pxss: apte.state ^= ready)
	eax0	bp|0
	drlze	(pxss: thread_him_in x0 zero) ASSUME x0_nonzero
	sxl0	bb|apte.thread,1
	stx0	bb|apte.thread,4
	sxl4	bp|apte.thread
	stx1	bp|apte.thread
	tra	0,7
" 
" " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	THREAD_IN_IDLE -- entry to thread an idle process into the
"	ready list.  Called during initialization and reconfiguration.
"
"	call pxss$thread_in_idle(apt_ptr)
"
"	Where:
"
"		apt_ptr	is an its pointer to an idle process apt entry
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " "

thread_in_idle:
	tsx6	setup_
	ldaq	ap|2,*		save apt_ptr in PDS
	staq	pds$arg_1
	tsplb	setup_check	switch stacks and lock
	arg	0,6
	tsx6	WRITE_LOCK	thread_in_idle rethreads
	eppbp	pds$arg_1,*	get pointer to APT entry
	tsx7	unthread		thread entry out of any list it's in
	ldx1	bb|eligible_q_tail
	tsx7	thread_him_in	thread into list
	tsx6	UNLOCK		thread_in_idle is done
	tra	switch_back
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	UNTHREAD_APTE -- entry to unthread an APTE from whatever
"	queue it's in.  Called by stop_cpu during reconfiguration.
"
"	call pxss$unthread_apte (apt_ptr)
"
"	Where:
"		apt_ptr	is a pointer to the APTE to be unthreaded.
"
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

unthread_apte:
	tsx6	setup_
	ldaq	ap|2,*
	staq	pds$arg_1
	tsplb	setup_check
	arg	0,6
	tsx6	WRITE_LOCK	pxss$unthread_apte

	eppbp	pds$arg_1,*
	tsx7	unthread
	tsx6	UNLOCK		pxss$unthread_apte
	tra	switch_back
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	GET_PROCESSOR -- procedure to award a processor (via
"	pre_emption of a lower priority process).
"	First check idle processes, then check eligible processes starting
"	with lowest priority eligible process).
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	GET_IDLE_PROCESSOR -- procedure to award a processor via
"	pre-emption of an idle process.  This procedure should be
"	called instead of get_processor when the process for whom we
"	are pre-empting may not even be eligible yet.
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

get_idle_processor:
	eax0	1		just check idle processes
	ldx3	bb|apte.wct_index,2	fall thru to full gp if realtime
	szn	bb|wcte.realtime,3
	tze	gp_init		go check idle queue
get_processor:
	eax0	2		check both idle and elig queues

gp_init:	lda	bb|apte.thread,2	pre-empting on behalf of X2
	sta	temp		save (unique) threads for compares
	lda	bb|idle_tail	start with idle processes
	aos	bb|gp_start_count	For getwork/get_processor window
"
"All set up.  Now for the main loop.  It will be executed over the
"Idle processes first.  If no Idle process is running then if the
"XR0 is > 1 (ie: came in at get_processor entry,
"not get_idle_processor) then the loop will be executed over the
"elig processes.
"
gp_loop:	ldaq	bb|apte.thread,al	go higher in queue
	cmpa	temp		is this process which was notified?
	tnz	gp_tv,ql*		no - branch on state
	tra	gp_return		yes - give up and return

	arg	check_eligible	-1 => sentinel
gp_tv:	arg	gp_loop		gp found empty (ok-may be idle)
	arg	gp_found_running
	arg	gp_loop		gp found ready
	arg	gp_loop		gp found waiting
	arg	DRL_blocked_apte	gp found blocked
	arg	DRL_stopped_apte	gp found stopped
	arg	gp_loop		gp found ptlocking

gp_found_running:
	lxl3	bb|apte.thread,au	extract addr from next's backptr
	tsx6	LOCK_x3		gp locks
	ldq	bb|apte.flags,3	grab fresh copy of flags
	canq	apte.pre_empted,du	have we pre-empted it before?
	tnz	gp_skip		yes, skip it
	lda	apte.pre_empted+apte.pre_empt_pending,du turn on pre_empted bit
	orsa	bb|apte.flags,3
	ldq	bb|apte.flags2,3	get processor tag
	anq	apte.pr_tag_mask,dl
	cmpq	prds$processor_tag	this cpu?
	tze	gp_this_cpu	dont cioc if so
	cioc	scs$cow_ptrs,ql*
	tsx6	UNLOCK_x3		gp unlocks
	tra	gp_return

gp_this_cpu:
	tsx6	UNLOCK_x3		gp unlocks
	lda	1,dl		set alarm to r1
	sta	pds$alarm_ring
	lra	pds$alarm_ring
	tra	gp_return

gp_skip:	tsx6	UNLOCK_x3
	tra	gp_loop		have not clobbered AL ( -> next)

check_eligible:
	eax0	-1,0		need check the eligibles?
	tze	gp_return		no - return to caller

	lda	bb|eligible_q_tail	now check eligibles
	tra	gp_loop
gp_return:
	aos	bb|gp_done_count	For getwork/get_processor window
	tra	0,7		gp ret to caller
" 
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	FIND_NEXT_ELIGIBLE -- the policy implementing part of the
"	scheduler.  This code could be in-line in getwork but is
"	separated for clarity. It is entered at find_next_eligible
"	from getwork if no eligible process can be run.  It is entered
"	with the traffic controller locked for multiple readers but
"	must run with the exclusive lock allowing rethreading and awarding
"	eligibility.  It will return to getwork with x2 -> newly elig
"	or will return to find idle. In either case before returning
"	the lock will be changed to allow concurrency.
"	This code will also be entered at AWARD with the write lock set,
"	from the part of getwork that decides to award eligibility
"	to process with realtime deadlines which are past.

"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
find_next_eligible:
	tsx6	UNLOCK		fne must unlock read before write
	tsx6	WRITE_LOCK	fne rethreads, awards elig
"
"If there are any processes which have just interacted,
"we must run them immediately.  So check the interactive_queue.
"If we are in deadline mode the interactive queue contains most processes
"and also some special conditions must be fulfilled.
"

	stz	depth
	eax2	bb|interactive_q
	stz	temp2		no work class restrictions so far
lr_loop:
	ldx2	bb|apte.thread,2	step to next process.
	szn	bb|apte.sentinel,2	If none then scan wc queues.
	tmi	lr_wcq		Look in workclass queues
	szn	bb|deadline_mode	If deadline mode make checks.
	tze	test_award	Else return this X2 to getwork.

	ldx1	bb|apte.wct_index,2	Check if limit on n elig
	ldq	bb|wcte.maxel,1
	tze	lr_e_ok		OK if no limit.
	cmpq	bb|wcte.nel,1	Else compare to limit.
	tpnz	lr_e_ok		Below limit.
	stc1	temp2		Flag work class restriction
	tra	lr_loop		And go deeped
lr_e_ok:
	lda	bb|max_batch_elig	Limit on batch?
	tze	test_award	No.
	lxl1	bb|apte.flags2,2	Yes.
	canx1	apte.batch,du	Is this process batch?
	tze	test_award	No, limit doesn't matter.
	cmpa	bb|num_batch_elig	Yes, check limit.
	tpnz	test_award	OK, within limit.
	stc1	temp2		Flag work class restriction
	tra	lr_loop		Go deeper.

lr_wcq:
"
"Nobody found in interactive/virtual_deadline queue.  We must
"update the credits in each workclass before scanning the workclass queues.
"
	szn	bb|deadline_mode
	tze	sc_loop		not dmode => scatter credits
	stz	bb|credit_bank	clear cell prvents ovfl
	tra	sc_done

sc_loop:
	lda	bb|credit_bank	Are there credits to scatter?
	tmi	sc_done		No.
	stz	bb|credits_scattered None scattered yet.
	ldq	bb|telast		Credits always < 4*telast
	qls	2
	ldx1	bb|min_wct_index	Start at first work class.

sc_wc_loop:
	szn	bb|wcte.realtime,1	Is this wc getting some percent?
	tnz	sc_check		No, go to next wc. after clipping
	lda	bb|wcte.minf,1	Yes, give credits.
	asa	bb|wcte.credits,1
	asa	bb|credits_scattered Note we scattered some.
sc_check: 
	szn	bb|wcte.credits,1	don't let credits go negative
	tpl	*+2
	stz	bb|wcte.credits,1
	cmpq	bb|wcte.credits,1	Make sure credits < 4*telast
	tpl	sc_next		OK.
	stq	bb|wcte.credits,1	CREDIT SINK
sc_next:
	eax1	size_of_wct_entry,1	Move to next work class.
	cmpx1	bb|max_wct_index	Done all?
	tmoz	sc_wc_loop	more wc to do.

	lca	bb|credits_scattered Did we scatter any?
	tze	sc_done		No. Don't loop back!
	asa	bb|credit_bank	Yes. Bookkeep and
	tra	sc_loop		loop back to scatter more.
sc_done:

"
"	Now scan the workclass queues to find the most worthy workclass
"	which has a non-empty queue.  In this process, governing credits
"	are distributed to governed work classes.  Any governed work
"	class whose governing credits are negative will not be
"	considered for eligibility.
"

	ldx2	-1,du		Preset to none-found.
	ldx1	bb|min_wct_index	Start at first work class.
	lda	=o400000,du	Anybody can beat this.
	sta	bb|best_credit_value

	eax0	-1		preset to do-not-pass-out-gv-credits
	szn	bb|governing_credit_bank
	tmi	fne_loop		none to pass out
	lda	bb|governing_credit_bank	pass out, decrementing bank
	sba	bb|credits_per_scatter
	ars	1			allow to grow to 2*credits_per_scatter
	cmpa	bb|credits_per_scatter	limit growth in the bank
	tmoz	*+2
	lda	bb|credits_per_scatter	(must be S&L)
	als	1
	sta	bb|governing_credit_bank
	eax0	0		pass-out-gv-credits
	
fne_loop:
	lda	bb|wcte.governed_word,1	is this W.C. governed
	cana	wcte.governed,du
	tze	fne_not_governed	No
	eax0	0,0		governing credits to pass out
	tmi	fne_no_gv_credits	no
	lda	bb|wcte.maxf,1	credits for this W.C. per scatter
	asa	bb|wcte.governing_credits,1
fne_no_gv_credits:
	lda	bb|gv_integration	limit of abs value of gv credits
	cmpa	bb|wcte.governing_credits,1
	tpl	*+2
	sta	bb|wcte.governing_credits,1
	szn	bb|wcte.governing_credits,1	is this W.C. in the hole?
	tpl	fne_not_governed	no - can consider for eligibility
	neg	0		areg = -limit ov abs value of gv credits
	cmpa	bb|wcte.governing_credits,1	there's a limit on the hole
	tmi	*+2		negative but not bankrupt 
	sta	bb|wcte.governing_credits,1	minimum value
	cmpx1	bb|wcte.thread,1	Th to self => none ready
	tze	fne_try_next
	stc1	temp2		flag work class restriction
	tra	fne_try_next

fne_not_governed: 
	cmpx1	bb|wcte.thread,1	Th to self => none rdy.
	tze	fne_try_next
	szn	bb|wcte.credits,1	Credits never left negative
	tpl	*+2
	stz	bb|wcte.credits,1	CREDIT SOURCE

	ldx3	bb|wcte.thread,1
	lca	bb|telast		maximize credits - min (ti, 2*telast)
	als	1
	cmg	bb|apte.ti,3	deal with abs values
	tmi	*+2
	lca	bb|apte.ti,3

	ada	bb|wcte.credits,1
	cmpa	bb|best_credit_value See if this is best sofar.
	tmi	fne_try_next	Wasn't, move to next.

	sta	bb|best_credit_value Was, remember value.
	eax2	0,3		remember the champ

fne_try_next:
	eax1	size_of_wct_entry,1	Move to next work class.
	cmpx1	bb|max_wct_index	If any.
	tmoz	fne_loop
	eax2	0,2		Neg=> nobody
	tpl	test_award	See if process fits in core.
	tra	recheck_real	Continue looking for candidate.

"
"Come here if no processes found in int/vird queue or in
"the workclass queues.  We determine whether any process is present in the
"realtime queue.  If so such a process will be awarded eligibility subject
"to the usual constraints even though its deadline has not
"arrived yet.
"
recheck_real:
	ldx2	bb|realtime_q	REALTIME AWARD?
	szn	bb|apte.sentinel,2
	tmi	fne_fail
"
"Arrive here with x2 pointing at a candidate for eligibility.
"A few more checks are made to determine if eligibility will
"actually be awarded.
"
test_award:
	lda	bb|apte.flags,2
	cana	apte.dbr_loaded,du
	tze	*+3		dbr not loaded ok to award elig
	cmpx2	pds$apt_ptr+1
	tnz	fne_fail		dbr loaded ok only if this cpu

	lxl0	bb|n_eligible	get number of eligible processes
	cmpx0	bb|min_eligible	Below min eligible?
	tmi	award		Yes, make eligible.
	cmpx0	bb|max_eligible	At max eligible?
	tpl	fne_fail		Yes, go idle.
	cmpx0	bb|max_max_eligible
	tpl	fne_fail
	ldq	bb|ws_sum		See if it fits.
	adq	bb|apte.ws_size,2
	cmpq	sst$nused
	tpl	fne_fail		no, go idle.
"
"Here to award eligibility to x2-> apte.  Arrive here
"either by falling through above code or directly from getwork
"if a process 's realtime deadline has arrived.
"
award:
	aos	bb|n_eligible	increment count of eligible s
	lxl1	bb|apte.flags2,2	DIGS
	canx1	apte.batch,du
	tze	*+2
	aos	bb|num_batch_elig
	ldx1	bb|apte.wct_index,2
	aos	bb|wcte.nel,1	and per wc count
	ldx0	apte.eligible,du
	ersx0	bb|apte.flags,2
	ldq	bb|apte.ws_size,2	ws_sum = ws_sum + ws_size,2
	anq	-1,dl		Leave only ws estimate
	asq	bb|ws_sum
"
"put the newly elig process in the proper place in elig queue
"note bp is fudged for sort subr's, then reset
"
	eppbp	bb|0,2		Set bp to process of interest
	tsx7	unthread		Remove from ready queue
	eax1	bb|eligible_q_tail	Assume put at eltail.

	ldx0	bb|apte.flags,2
	canx0	apte.realtime_burst,du  Normal process?
	tze	put_in_el_q	Yes, put at tail of el queue.
	lcx0	apte.realtime_burst+1,du Reset flag
	ansx0	bb|apte.flags,2

	ldx1	bb|eligible_q_head	Could sort by deadline here
	stz	depth		Reset depth.
put_in_el_q:
	tsx7	thread_him_in
	ldq	AWARD_ELIGIBILITY,dl
	tsx7	meter_response_time$tc	response transition
	eppbp	pds$apt_ptr,*	Restore bp -> old_user
	tsx6	LOCK_x2		Claim this apte now
	tsx6	WRITE_TO_READ	shift lock to read before return

	ldx1	bb|apte.wct_index,2	Determine work class.
	lda	bb|apte.temax,2	Decrement wc credits in advance
	tnz	*+2
	lda	bb|process_initial_quantum	Must be first time thru
	sta	bb|apte.saved_temax,2	Save to compensate later
	neg
	asa	bb|wcte.credits,1
	ldq	bb|wcte.governed_word,1	Is wc governed
	canq	wcte.governed,du
	tze	*+2		No
	asa	bb|wcte.governing_credits,1
	aos	bb|wcte.eligibilities,1 Meter elig awarded.

	ldq	bb|apte.ts,2	see if first time since wakeup
	adq	bb|apte.ti,2	continue with check ...
	tnz	no_response	non-zero means not first time
	read_clock		" meter response time
	sbaq	bb|apte.state_change_time,2 get time this process was ready
	adaq	bb|response_time	add in to total meter
	staq	bb|response_time
	aos	bb|response_count	count number of times we added in
no_response:
	tra	gw_ck_suspend	Success return from fne

fne_fail:
	tsx6	WRITE_TO_READ
	tra	find_idle		Failure return from fne


"
" " " " " " " " " " " " " " " " " " " "  " " " " " " " " " " " " " " "
"
"	GETWORK -- procedure which is invoked when the running 
"	process is ready to give the processor to another process.
"	When it is invoked bb->bp must point into tc_data.
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

getwork:
	stc1	pds$pxss_args_invalid Signal overwriting of temporaries.
	stz	pds$post_purged	reset flag indicating purge is done
	read_clock		" meter getwork time
	staq	getwork_temp	save for user being switched to
	sxl7	bp|apte.savex7	save x7 in APT entry
	aos	bb|depth_count
	lcx0	apte.pre_empted+apte.pre_empt_pending+1,du turn off pre-empted flags
	ansx0	bp|apte.flags

gw_retry:
"
"	The following code, checked each time through the traffic controller, checks to see
"	if the running processor is to be deleted. If so, it branches to the
"	appropriate code which ultimately DISes.
"	1. Force idle process to run on this cpu.
"	2. Rather than returning from getwork, transfer to DIS-ing code.
"
	lxl1	prds$processor_tag	get our own processor tag
	lda	scs$processor_data,1 get our control word from SCS
	cana	processor_data.delete_cpu,du
	tnz	find_idle_for_delete
"
"	End of reconfiguration tests
"
	read_clock		" get time in AQ for several tests

	cmpaq	bb|next_alarm_time	go boost priority if time
	tpl	alarm_timer	..

	cmpaq	bb|nto_check_time	go check notify_timeouts if time
	tpl	nto_check

	ldx2	bb|realtime_q	maybe realtime award?
	szn	bb|apte.sentinel,2
	tmi	gw_lock_init_loop	nobody
	cmpaq	bb|apte.deadline,2
	tmi	gw_lock_init_loop	Not yet

	tsx6	WRITE_LOCK	probable realtime award
	lxl0	bb|n_eligible
	cmpx0	bb|max_max_eligible
	tpl	no_real
	ldx2	bb|realtime_q	REALTIME AWARD?
	szn	bb|apte.sentinel,2
	tmi	no_real
	read_clock	
	cmpaq	bb|apte.deadline,2
	tmi	no_real		Delay him for now.

	lda	bb|apte.flags,2
	cana	apte.dbr_loaded,du
	tze	award		dbr not loaded ok to award elig
	cmpx2	pds$apt_ptr+1
	tze	award		dbr loaded ok only if this cpu
no_real:
	tsx6	WRITE_TO_READ	cant make realtime elig so shift to readlock
	tra	gw_init_loop

gw_lock_init_loop:
	tsx6	READ_LOCK
gw_init_loop:
	lda	bb|gp_done_count	If gp occur we must not idle
	sta	temp1		So we remember the count
	lda	bb|eligible_q_head	ELIGIBLE to RUN?
	ldx1	-1,du		keep depth in X1

gw_loop:
	ldaq	bb|apte.thread,au	go to next entry via AU
	eax1	1,1		increase depth
	tra	gw_tv,ql*		dispatch on state in QL

	arg	find_next_eligible	state was -1 ie sentinel
gw_tv:	arg	DRL_empty_apte	found empty
	arg	gw_loop		state was running so go deeper
	arg	gw_found_ready
	arg	gw_loop		state was waiting so go deeper
	arg	DRL_blocked_apte	found blocked
	arg	DRL_stopped_apte	found stopped
	arg	gw_loop		found ptlocking



gw_found_ready:
	ldx2	bb|apte.thread,al	extract ready's addr from prev's fp
	tsx6	LOCK_x2		gw must make sure x2 is ready etc
	sxl1	depth		remember depth
gw_ck_suspend:
	lda	bb|tc_suspend_lock	see if someone wants exclusive use of the system
	tze	gw_ck_ready	no one does, so bypass the following checks
	cmpa	bb|apte.processid,2	is it I that wants the system?
	tze	gw_ck_ready	if so, then I get to run.
	lda	bb|apte.flags,2	otherwise,
	cana	apte.idle,du	is this an idle process?
	tze	gw_cant_run	well, if it's not, then it can't be run
gw_ck_ready:
	lda	bb|apte.flags,2	pick up flags&state from locked apte
	eax0	0,al		put state in X0
	cmpx0	ready,du		really ready?
	tnz	gw_cant_run	not really ready
	cana	apte.dbr_loaded,du	Is dbr_loaded?
	tze	gw_dbr_ok		no - ok to run
	cmpx2	pds$apt_ptr+1	yes - Is this self?
	tnz	gw_cant_run_dbrl	not self - must not touch
gw_dbr_ok:
	cana	apte.loaded,du	is this process loaded?
	tze	load_him		no, load the process

	lda	bb|apte.procs_required,2	see whether this process
	ana	apte.procs_required_mask,du	can run on this CPU
	ana	prds$processor_pattern
	tnz	gw_can_run		OK to run

gw_cant_run:
	tsx6	UNLOCK_x2		unlock the apte
	lda	bb|apte.thread,2	repair AU
	tra	gw_loop		go loop to next
gw_cant_run_dbrl:
	lca	1,dl		force gw retry if we idle
	asa	temp1		by spoiling gw_gp_window test
	tra	gw_cant_run

gw_can_run:
"
"We have decided to run this process, but will retry getwork if this
"process is idle and if there has been a get_processor in progress while
"we were chasing down the eligible queue. Strictly speaking, we should retry
"even if the found process is not idle -- but who cares.  Strictly speaking
"we need not retry (cannot lose a get_processor) unless BOTH a process has
"been readied AND a get_processor has been in progress DURING this getwork.
"
	eppbp	bb|0,2		processor addevent!
	ldx0	running,du	mark as running
	tsx7	update_execution_state

	lda	bb|gp_start_count
	ssa	temp1		temp1 is nonzero if done(t0) ^= start(t1)

gw_can_really_run:
	tsx7	compute_virtual_clocks
	tsx7	set_newt		calculate the time to run
	lxl0	depth		set depth meter
	cmpx0	max_depth-1,du	depth can't be bigger than max
	tmi	*+2
	ldx0	max_depth-1,du
	stx0	prds$depth
	eppap	pds$apt_ptr,*	get pointer to own APT entry
	stz	pds$number_of_pages_in_use
	spriap	apt_ptr		save pointer to last user to run
	eppbp	bb|0,2		make bp -> our APTE

" " " " " " " " " " " " " " " " " " " " "
"			          "
"   P R O C E S S   S W I T C H I N G   "
"				"
"        D O N E    H E R E		"
"				"
" " " " " " " " " " " " " " " " " " " " "
	ldx0	lp|prds_link	pick up segno of prds
	adlx0	lp|prds_link	multiply by two (add it in again)
	sbar	pds$base_addr_reg	save base address reg for process
	ldt	prds$last_timer_setting and load with new value
	inhibit	on		<+><+><+><+><+><+><+><+><+><+><+><+>
	ldaq	dseg$+0,0		pick up prds SDW address
	ldbr	bp|apte.dbr	load the dbr
	staq	dseg$+0,0		store prds SDW in new DSEG

	spribp	prds$apt_ptr	mark process running this cpu
	lda	prds$processor_tag	set proc tag for
	era	bp|apte.flags2
	ana	apte.pr_tag_mask,dl
	tze	*+2
	cams	4		clear cache if different cpu
	ersa	bp|apte.flags2
	eppab	prds$mode_reg_enabled
	lca	mr.enable_hfp+1,dl	is hex fp enabled for this process?
	ana	prds$mode_reg
	szn	pds$hfp_exponent_enabled
	tze	*+2
	ora	mr.enable_hfp,dl
	sta	prds$mode_reg
	ora	mr.enable_mr+mr.enable_hist,dl  enable mode reg and enable hist regs
	sta	ab|0
	lcpr	ab|0,04
	inhibit	off		<-><-><-><-><-><-><-><-><-><-><-><->

	lda	ap|apte.flags	OLD USER TO BE UNLOADED?
	cana	apte.eligible+apte.always_loaded,du Loadedness protected?
	tnz	dont_unload	yes - skip unload
	cana	apte.loaded,du	Currently loaded?
	tze	dont_unload	no - can't unload

	lcx0	apte.loaded+1,du	turn off loaded bit
	ansx0	ap|apte.flags

	lda	ap|apte.asteps	pick up both asteps
	lcq	ptw.wired+1,dl	set bit for turnoff
	ansq	sst$+aste_size,au	unwire one
	ansq	sst$+aste_size,al	unwire other
	lcq	2,dl		unwired total of two pages
	asq	sst$wired	keep pc counter up to date
dont_unload:
"
"At this point we can pass the apte.dbr_loaded bit to the new user.
"This simple bit prevented anything interesting from happening
"to old user from the time he left the running state until now.
"Old user cannot have regained elig, except on this cpu, if it was lost.
"Old user cannot be running on any other cpu.
"Old user not subject to load/unload race: gw not go to load_him if dbr_loaded
"If old lost elig then old not in elig_q then old not being loaded.
"There is a window in the opposite direction, analogous to the gp/gw race:
"Old user is HIDDEN from getwork on other cpu's.  It is possible for
"another cpu to go idle because this cpu has hidden old_user.  This possibility
"is prevented by forcing a gw_gp_window retry of getwork if
"a processor is about to go idle but has seen and skipped
"a ready process because that process had it dbrloaded
"on another processor.
"
	lcx0	apte.dbr_loaded+1,du turn off dbr_loaded bit for old user
	ansx0	ap|apte.flags
	ldx0	apte.dbr_loaded,du	turn on dbr_loaded bit for new user
	orsx0	bp|apte.flags

	tsx6	UNLOCK_bp		gw unlocks new user
	tsx6	UNLOCK

stop_check:
	eppbp	apt_ptr,*		OLD USER STOPPED?
	lxl0	bp|apte.state	check for stopped
	cmpx0	stopped,du
	tze	stop_check_	jump out_of_line
end_stop_check:

cpu_monitor_check:
	szn	bp|apte.cpu_monitor	OLD USER MONITOR TIME?
	tnz	cpu_monitor_	maybe time now
end_cpu_monitor:

cput_check:
	eppbp	pds$apt_ptr,*	NEW USER CPUT?
	lda	bp|apte.flags	see if idle
	cana	apte.idle,du	yes, skip timer check
	tnz	end_cput_check	..
	szn	pds$timer_time_out+1 see if non-zero time exists
	tze	end_cput_check	no, continue with getwork
	ldaq	bp|apte.virtual_cpu_time see if process is over time limit
	cmpaq	pds$timer_time_out
	tmi	end_cput_check	no, continue with getwork
	fld	0,dl		zero the time out value
	staq	pds$timer_time_out
	ldaq	pds$timer_channel	time up, check if ev chan is zero
	tnz	cput_wakeup	it's non-zero, need full wakeup
	lda	sys_info$cput_mask
	tsx6	add_ips_message	use stacq
	tra	end_cput_check
cput_wakeup:
	stz	tmp_ring		set up for call to 'make_itt_message'
	stz	dev_signal	count it as dev_signal
	aos	dev_signal	count it as dev_signal
	lda	=acput		ev message is 'cputimer'
	ldq	=aimer
	staq	tmp_ev_message	save in stack
	ldaq	pds$timer_channel	copy ev channel into stack
	staq	tmp_ev_channel
	lda	bp|apte.processid	copy processid for make_itt_message
	sta	pds$arg_1
	stc1	pds$wakeup_flag	not require unique message
	tsx6	LOCK_bp		cput_wakeup protects event thread
	tsx7	make_itt_message
	lda	apte.wakeup_waiting,du Cheap wakeup of self
	orsa	bp|apte.flags
	tsx6	UNLOCK_bp		cput_wakeup is done
end_cput_check:

	lda	bp|apte.flags	check stop or ips pending
	ana	apte.stop_pending,du
	ora	bp|apte.ips_message	see if an ips signal must be sent
	tze	no_more_pending	no, skip over SMIC
	lda	1,dl		set ring alarm for exit from ring 0
	sta	pds$alarm_ring
no_more_pending:

	lbar	pds$base_addr_reg	get base address reg for new process
	lra	pds$alarm_ring	get his ralr setting too

	ldaq	prds$last_recorded_time calc this process's CPU time
	sbaq	bp|apte.time_used_clock fudge it for easy calc later
	staq	pds$cpu_time	save in the PDS for the process

	read_clock		" meter rest of getwork time
	sbaq	getwork_temp	set on entry to getwork by 'old_user'
	adaq	bb|getwork_time	keep running total
	staq	bb|getwork_time
	aos	bb|getwork_time+2	keep count of getworks
	eppbp	pds$apt_ptr,*	set bp to apt entry 
"
"	Still masked and wired, check for need to allocate a stack_0
"	and if needed do so.

	szn	pds$stack_0_sdwp,*
	tnz	already_got_stack_0 Doin' fine, no problem.

	tsx6	lock_stack_queue
	ldx0	ab|sdt.freep
	drlze	(pxss: no available stack_0) A fine time to run out of stacks.
	ldx4	ab|sdte.nextp,0	Thread out.
	stx4	ab|sdt.freep
	eax4	0
	stx4	ab|sdte.nextp,0	Clean thread
	eax2	bp|0
	sxl2	ab|sdte.aptep,0	For debugging.
	ldaq	ab|sdte.sdw,0
	staq	pds$stack_0_sdwp,*	Store the SDW in DSEG.
	cams	4		Clear cache. Think about it.
			"cams 0 not needed, did ldbr since last stack_0.
	tsx6	unlock_stack_queue
	lda	apte.shared_stack_0,du	Flag stack to be returned
	orsa	bp|apte.flags
already_got_stack_0:

	lda	bp|apte.flags	Load flags once again
	cana	apte.firstsw+apte.idle,du First time & ^idle ?
	tze	stproc		is first time. do special return
"
	cana	apte.idle,du	idle process?
	tze	no_idle_no_del	if not, don't do next check
	lxl1	prds$processor_tag	get CPU tag
	lda	scs$processor_data,1 look at data for this CPU
	cana	processor_data.delete_cpu,du to be deleted?
	tnz	delete_me		if so, stop it now
	szn	temp1		before going idle, see if there was interference
	tze	no_idle_no_del	was none, ok to idle
	lda	1,dl
	sta	pds$alarm_ring	set ring_alarm to remind idle to come back
	lda	apte.pre_empt_pending,du
	orsa	bp|apte.flags
	aos	bb|gw_gp_window_count meter these
no_idle_no_del:

	lxl7	bp|apte.savex7	restore x7 for return
	tra	0,7

stproc:	lda	apte.firstsw,du	turn ON flag saying we're initialized
	orsa	bp|apte.flags
	lda	bb|process_initial_quantum	Give special quantum first time
	sta	bp|apte.temax	put initial quantum in APTE
"
"Unlock,switch stacks, restore mask, and perform special first time call.
"
	eppsp	pds$last_sp,*
	ldaq	sb|stack_header.stack_begin_ptr Truncate PRDS.
	staq	sb|stack_header.stack_end_ptr
	epbpsb	sp|0
	lda	bp|apte.processid
	cmpa	tc_data$initializer_id Is this the initializer?
	tze	dont_reset_stack_0
	ldaq	sb|stack_header.stack_begin_ptr Clean out any old crud
	staq	sb|stack_header.stack_end_ptr
dont_reset_stack_0:
	inhibit	on		<+><+><+><+><+><+><+><+><+><+><+><+>
	ldaq	scs$open_level
	lxl1	prds$processor_tag
	lprpab	scs$mask_ptr,1
	xec	scs$set_mask,1
	inhibit	off		<-><-><-><-><-><-><-><-><-><-><-><->
	eppap	=its(-1,1),*
	eppbp	pds$initial_procedure
	rtcd	bp|0




" 
"
"	NTO_CHECK -- This code checks for lost notifies.
"		Salient features include:
"		1. It is executed every nto_delta microsec.
"		2. It is entered with no locks set.
"		3. It looks for processes unchanged for > nto_delta.
"		4. If one is found it is notified, metered, and johndeaned.
"		5. If none, the time of the next check is set.
"		6. It returns to gw_retry with no locks set.
nto_check:
	tsx6	READ_LOCK
	eax2	bb|eligible_q_head
nto_loop:	ldx2	bb|apte.thread,2	step to next process
	szn	bb|apte.sentinel,2
	tmi	nto_reset_time	have done them all, all done
	tsx6	LOCK_x2		no need for speed here
	lxl0	bb|apte.state,2	only (ptl)waiters get nto'd
	cmpx0	waiting,du	waiter?
	tze	nto_wait		this one is waiting now
	cmpx0	ptlocking,du	waiter on PTL?
	tnz	nto_not		no
	lda	=aptlw		yes, fake the event
	sta	bb|apte.wait_event,2
	tra	nto_wait		now go notify

nto_not:	tsx6	UNLOCK_x2		not waiting
	tra	nto_loop		so go to next

nto_wait:	ldaq	bb|apte.state_change_time,2
	adl	bb|nto_delta
	cmpaq	getwork_temp	State change more than nto_delta ago?
	tpl	nto_not		No, go to next

	aos	bb|nto_count	meter notify timeouts
	ldac	bb|apte.wait_event,2 Yes, reset state
	sta	tmp_event
	sta	bb|nto_event
	lda	bb|apte.processid,2
	sta	temp1
	lcx0	apte.page_wait_flag+1,du
	ansx0	bb|apte.flags,2
	ldx0	ready,du
	eppbp	bb|0,2
	tsx7	update_execution_state
	tsx6	UNLOCK_x2
	tsx7	get_processor
	szn	bb|time_out_severity negative means dont print
	tmi	nto_loop		can continue looping
	tsx6	UNLOCK		don't call out with lock set
"
"	call syserr (tc_data|time_out_severity,"pxss: notify time out: event=^w, pid=^w",
"	     apte.wait_event, apte.processid)
"
	epplb	bb|time_out_severity
	sprilb	arg+2
	epplb	nto_message
	sprilb	arg+4
	epplb	tmp_event
	sprilb	arg+6
	epplb	temp1
	sprilb	arg+8
	epplb	fixed_desc
	sprilb	arg+10
	sprilb	arg+14
	sprilb	arg+16
	epplb	nto_message_desc
	sprilb	arg+12
	ldaq	=v18/8,18/4,18/8,18/0
	staq	arg
	call	syserr$syserr(arg)
	tra	gw_retry		nto_check returns with no lock set
"
"Come here when scan of eligible queue has found no nto's.
nto_reset_time:
	read_clock	
	adl	bb|nto_delta
	staq	bb|nto_check_time
	tsx6	UNLOCK		nto_check returns with no lock set
	tra	gw_retry
nto_message_desc:
	oct	524000000047
nto_message:
	aci	"pxss: notify time out: event=^w, pid=^w"

"
load_him:	cana	apte.being_loaded,du see if we must load him
	tnz	gw_cant_run	sombody already loading this process

	lda	apte.being_loaded,du turn on being loaded flag
	orsa	bb|apte.flags,2
	tsx6	UNLOCK_x2
	tsx6	UNLOCK
	eppap	apt_ptr
	spriap	arg+2
	eppap	bb|0,2
	spriap	apt_ptr
	fld	=1b24,dl
	staq	arg
	call	wired_plm$load(arg)	load the process
	tsx6	LOCK_x2
	eppbp	bb|0,2		set bp to this entry
	lda	bp|apte.wait_event	 should we wait for the loading to complete ?
	tze	loaded_test	no, loading may be complete
	lda	apte.page_wait_flag,du
	orsa	bp|apte.flags
	ldx0	waiting,du	-- Wait --
	tsx7	update_execution_state
	tra	load_him_done	start over
" must verify loading with APTE locked
loaded_test:
	lda	bp|apte.asteps	Get both asteps in Areg
	ldq	ptw.wired+ptw.valid,dl	Get mask in Qreg
	anq	sst$+aste_size,au	Require this page
	anq	sst$+aste_size,al	AND that page
	cmpq	ptw.wired+ptw.valid,dl	have required bits on.
	tnz	load_him_done	Didn't, must retry getwork.
	lda	apte.loaded,du	turn loaded flag on
	orsa	bb|apte.flags,2
	aos	bb|loadings
load_him_done:
	lcx0	apte.being_loaded+1,du Turn off being loading bit
	ansx0	bp|apte.flags
	tsx6	UNLOCK_x2
	tra	gw_retry		load_him retries getwork from the top

find_idle_for_delete:
	tsx6	READ_LOCK
find_idle:
	ldx2	prds$idle_ptr+1
	tsx6	LOCK_x2
	stz	bb|apte.te,2	zero idle's te
	tra	gw_can_run
"
"	This section of code is invoked when a simulated alarm
"	clock interrupt is detected. Metering is kept of the lag of
"	the simulated timer from when it should really gone off.

alarm_timer:
	tsx6	WRITE_LOCK	alarm timer
at_retry:
	read_clock	
	sbaq	bb|next_alarm_time	compute lag in simulated timer
	tpnz	at_now
	tsx6	UNLOCK		false alarm
	tra	gw_retry

at_now:
	aos	bb|clock_simulations count number of clock simulations
	cmpq	bb|max_clock_lag	keep maximum lag
	tmi	*+2		..
	stq	bb|max_clock_lag	..
	adaq	bb|total_clock_lag	add to total
	staq	bb|total_clock_lag	..
	read_clock		" remember clock time
	staq	bb|next_alarm_time	..

"	The following code checks to see if a process timer should have
"	gone off.

next_timer:
	ldx2	bb|alarm_timer_list	get next process on timer list
	tze	priority_scheduling	no one is on list
	eppbp	bb|0,2		set APT pointer
	ldaq	bp|apte.alarm_time	when should process timer go off
	ana	=o777777,dl	mask off thread
	cmpaq	bb|next_alarm_time	has time passed
	tpl	priority_scheduling	if not then we are done here
	ldx3	bp|apte.alarm_time	unthread from list
	stx3	bb|alarm_timer_list
	fld	0,dl		zero out time
	staq	bp|apte.alarm_time	..
	lxl3	bp|apte.state	make sure process is alive
	cmpx3	stopped,du	..
	tze	next_timer	if process dead, forget it
	tsx6	LOCK_bp		wakup alrm
	ldaq	bp|apte.alarm_event	get event channel
	tnz	wakeup_alarm	if channel then go wakeup
	lda	sys_info$alrm_mask
	tsx6	add_ips_message
	tsx7	send_connect	send connect if running
	tra	at_wake
wakeup_alarm:
	stz	tmp_ring		send wakeup
	stz	dev_signal	count it as dev_signal
	aos	dev_signal	count it as dev_signal
	lda	=aseta		event message is setalarm
	ldq	=alarm		..
	staq	tmp_ev_message	save in stack
	ldaq	bp|apte.alarm_event	get event channel
	staq	tmp_ev_channel	save in stack
	lda	bp|apte.processid	copy process id for ITT message
	sta	pds$arg_1		..
	stc1	pds$wakeup_flag	not require unique message
	tsx7	make_itt_message	create wakeup message
at_wake:	tsx7	wake		wake up process
	tsx6	UNLOCK_bp		wakup alrm
	tra	next_timer	see if more timers to go off

"	Now we check to see if there are any priority scheduling processes
"	that must have their priority boosted.

priority_scheduling:
	ldaq	bb|next_alarm_time	see if any processes need boosting
	cmpaq	bb|priority_sched_time ..
	tmi	check_ring0_timer	no processes need boosting
	ldaq	bb|end_of_time_loc	set time to high value
	staq	bb|priority_sched_time
	ldx2	bb|min_wct_index	begin search of ready lists

ps_get_rq_head:
	ldx2	bb|wcte.thread,2	get index of top guy in ready queue
ps1:	eppbp	bb|0,2		get pointer to next entry in ready list
	szn	bp|apte.sentinel	stop if end of ready list
	tmi	ps_next_ready_queue	Now at sentinel, x2-> wcte again
	ldx2	bp|apte.thread	save index to next before rethreading!
	lda	bp|apte.flags2	is process in priority sched mode
	cana	apte.prior_sched,dl	..
	tze	ps1		if not then go to next entry
	ldaq	bp|apte.state_change_time get time lost eligibility
	adl	bb|priority_sched_inc add in increment for rescheduling
	cmpaq	bb|next_alarm_time	..
	tmi	ps_boost		..
	cmpaq	bb|priority_sched_time see if this is next process to be boosted
	tpl	ps1		if not then go to next entry
	staq	bb|priority_sched_time if so then remember time
	tra	ps1		go to next entry
ps_boost:
	szn	bp|apte.ti	already interactive?
	tze	ps1		don't boost again, and avoid tail chasing
	tsx0	setup_p_int	boost priority
	tsx7	unthread		thread out of ready list
	tsx7	sort_in		sort into ready list in new place
	aos	bb|boost_priority	count number of boosts
	tra	ps1		go to next entry
ps_next_ready_queue:
	eax2	size_of_wct_entry,2	Move to next wcte
	cmpx2	bb|max_wct_index	If If there is one.
	tmoz	ps_get_rq_head
"
check_ring0_timer:
	ldaq	bb|next_ring0_timer
	cmpaq	bb|next_alarm_time
	tpl	check_polling

	ldaq	bb|end_of_time_loc	don't allow this again
	staq	bb|next_ring0_timer

" wake up any ring 0 process waiting for a short time

	tsx6	UNLOCK		unlock for notify
	ldq	ring0_timer_event
	stq	pds$arg_1
	stc1	dev_signal	flag says special notify
	tsx6	notify_		notify waiters
	tsx6	WRITE_LOCK	return to polling checks

"	Now we check to see if the disk DIM or tty DIM need
"	to be polled.

check_polling:
	eax4	n_polling_table-4	initialize index for table search
polling_loop:
	xec	polling_table+2,4	execute before test
test_poll:
	ldaq	bb|next_alarm_time	get time for this alarm
	ldx3	polling_table,4	get address of link to time
	cmpaq	lp|0,3*		has time matured?
	tmi	skip_polling	if not, skip this call

	adl	polling_table+1,4	compute time of next poll
	staq	lp|0,3*		and set new time

	tsx6	UNLOCK		call out with APT unlocked
	lxl3	polling_table,4	get address of routine to call
	call	lp|0,3*(null_arglist) make call
	tsx6	WRITE_LOCK	relock the APT now
	tra	next_poll		on to the next

skip_polling:
	xec	polling_table+3,4	execute after test
next_poll:
	eax4	-4,4		step to next table entry
	tpl	polling_loop	and loop

"	Now we compute the next simulated alarm clock time.

compute_next_alarm:
	ldx2	bb|alarm_timer_list	get next process timer
	tze	*+5		no process timer
	ldaq	bb|apte.alarm_time,2 ..
	ana	=o777777,dl	mask off thread
	cmpaq	bb|priority_sched_time is it before priority sched time
	tmi	*+2
	ldaq	bb|priority_sched_time if sched time first use it
	cmpaq	bb|next_ring0_timer	how about ring0 timers?
	tmi	*+2
	ldaq	bb|next_ring0_timer

	eax4	n_polling_table-4	initialize index for table search
next_alarm_loop:
	ldx3	polling_table,4	get pointer to time
	cmpaq	lp|0,3*		test for earliest time
	tmi	*+2		..
	ldaq	lp|0,3*		..

	eax4	-4,4		try next table entry
	tpl	next_alarm_loop	..

	staq	bb|next_alarm_time	set time for next alarm
	tra	at_retry		go check time again

	even
null_arglist:
	oct	4,0		arglist with no args

polling_table:
disk_poll_entry:
	link	disk_polling_time,tc_data$disk_polling_time
	link	disk_poll,page$time_out
	zero	disk_polling_time,disk_poll
	dec	15b15
	nop
	nop

	link	ioi_polling_time,tc_data$tape_polling_time
	link	ioi_poll,ioi_masked$timer
	zero	ioi_polling_time,ioi_poll
	dec	16b15
	nop
	nop

	link	mos_polling_time,tc_data$mos_polling_time
	link	mos_poll,mos_memory_check$poll
	zero	mos_polling_time,mos_poll
	dec	600b15
	nop
	nop
	
	link	opc_polling_time,tc_data$opc_polling_time
	link	opc_poll,ocdcm_$poll_for_timeout
	zero	opc_polling_time,opc_poll
	dec	30b15
	nop
	nop

	link	volmap_poll_time,tc_data$volmap_polling_time
	link	volmap_poll,page$poll_volmap_io
	zero	volmap_poll_time,volmap_poll
	dec	30b15
	nop
	nop

	equ	n_polling_table,*-polling_table
" 
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "" " "
"	
"	COMPUTE_VIRTUAL_CLOCKS -- procedure to figure out what type of
"	idle time we have, and also to update the time used in the
"	APT entry for the process just run.
"	A meter lock protects per-system doubleword variables
"
"	Idle time is categorized as follows:
"
"	zero idle       - all processes blocked except idle processes
"	nmp idle        - all processes which could be eligible are eligible
"	loading idle    - not all processes which could be eligible are
"		        eligible, and not all eligible processes are loaded
"	work class idle - not all processes which could be eligible are
"		        eligible, at least one work class was
"		        skipped because of work class limits
"		        (governing, max eligible for work class),
"		        and system maxe has not been reached
"	mp idle         - not all processes which could be eligible are
"		        eligible, and criteria for work class idle
"		        not met
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

compute_virtual_clocks:
	eppbp	pds$apt_ptr,*	set bp to this process
	lda	pds$page_waits	copy page fault count into APT entry
	sta	bp|apte.page_faults
	ldaq	bp|apte.virtual_cpu_time Remember bp's vcpu
	staq	temp		Use temp for delta virtual time.

	read_clock
	staq	bb|last_time	save last time anyone run
	sbaq	prds$last_recorded_time delta t in microseconds
	staq	delta_t
	asq	bb|governing_credit_bank
	
	adaq	bp|apte.time_used_clock update time used in APT
	staq	bp|apte.time_used_clock
	sbaq	pds$virtual_delta	update the virtual CPU time
	staq	bp|apte.virtual_cpu_time

	sbaq	temp		Subtract old vcpu from new
	staq	temp		Remember for idle meters.

	ldaq	delta_t		meter last recorded time
	adaq	prds$last_recorded_time
	staq	prds$last_recorded_time

mlock_loop:
	ldac	bb|metering_lock	LOCK LOCK LOCK LOCK LOCK LOCK LOCK
	tnz	mlocked		OK- meters are locked
	tra	mlock_loop	retry locking meters
mlocked:
	ldaq	delta_t		meter total CPU time for system
	adaq	bb|processor_time
	staq	bb|processor_time

	ldaq	prds$last_recorded_time compute ave queue length
	anq	=o3777777		sample every sec (2**20 usec)
	adq	delta_t+1
	qrl	20-18		convert to sec
agl:	eaq	-1,qu		count seconds
	tmi	age
	lda	bb|statistics+running get queue length
	ada	bb|statistics+ready
	ada	bb|statistics+waiting running+ready+waiting
	als	18		give answer 6 octal points
	sba	bb|avequeue	new ave=oldave+(cur-oldave)/64
	ars	6
	asa	bb|avequeue
	lda	bb|n_eligible	now get average eligible
	als	18
	sba	bb|ave_eligible
	ars	6
	asa	bb|ave_eligible
	tra	agl

age:

	lda	bp|apte.flags	check for idle process
	cana	apte.idle,du
	tnz	cvidle		is

	ldx0	prds$depth	count runs at depth
	aos	bb|depths,0
	adx0	prds$depth	double index
	ldaq	delta_t		bump time at level
	adaq	bb|tdepth,0
	staq	bb|tdepth,0

	ldaq	temp		Update vcpu used by nonidle
	adaq	bb|system_virtual_time
	staq	bb|system_virtual_time

	ldx0	bp|apte.wct_index	Update work_class data.
	ldaq	delta_t		Workclasses get % of tcpu - not vcpu
	szn	bb|wcte.realtime,0	Realtime not add to bank.
	tnz	*+2
	asq	bb|credit_bank	But others do.

	adaq	bb|wcte.cpu_sum,0	Add to time gotten
	staq	bb|wcte.cpu_sum,0
	lcq	delta_t+1		Decrement credits.
	asq	bb|wcte.credits,0
	lda	bb|wcte.governed_word,0  	Is W.C. governed
	cana	wcte.governed,du
	tze	m_unlock		no
	asq	bb|wcte.governing_credits,0
	tra	m_unlock		Now go unlock metering data.

cvidle:	ldaq	temp		up total idle vcpu
	adaq	bb|idle_time
	staq	bb|idle_time

	ldaq	delta_t		Update idle  real cpu time.
	adaq	bb|gross_idle_time
	staq	bb|gross_idle_time

	ldaq	temp		up idle vcpu by type
	ldx0	bp|apte.term_processid recall our idle type
	tze	m_unlock		ignore first time
	adaq	bb|0,0
	staq	bb|0,0

m_unlock:	stc1	bb|metering_lock	UNLOCK UNLOCK UNLOCK UNLOCK UNLOCK
cvidt:	lda	bb|apte.flags,2	Is new guy idle?
	cana	apte.idle,du
	tze	0,7		no, not idle
	eax0	zero_idle		yes, figure type
	lda	bb|statistics+running
	ada	bb|statistics+ready
	ada	bb|statistics+waiting
	ada	bb|statistics+ptlocking
	tze	cvst		Will be zero_idle
	eax0	nmp_idle
	cmpa	bb|n_eligible
	tze	cvst		Will be NMP idle
	eax0	loading_idle
	lxl1	bb|eligible_q_tail
	ldx1	bb|apte.flags,1
	canx1	apte.loaded,du
	tze	cvst		Will be Loading idle
	eax0	mp_idle
	szn	temp2		Work class limit reached?
	tze	cvst		No, will be MP idle
	lxl1	bb|n_eligible	Are we at system max eligible?
	cmpx1	bb|max_eligible
	tpl	cvst		Yes, will be MP idle
	eax0	work_class_idle	No, will be work class idle
cvst:	stx0	bb|apte.term_processid,2 Remember idle type.
	tra	0,7
" 
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	SET_NEWT  -- procedure to figure out how long the new process
"	should run.
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

set_newt:
	lda	bb|apte.flags,2	if idle, give him much time
	cana	apte.idle,du
	tze	sn1		if not idle then compute new timer

	read_clock		" compute time to next alarm
	sbaq	bb|next_alarm_time	..
	tpl	setsmall		make sure time has not already passed
	negl	0		make time positive
	cmpaq	idle_runout	is it very large
	tmi	sn2		no, go set it
	ldaq	idle_runout	yes, reduce it
	tra	sn2		then set it
sn1:

	eaa	0
	ldq	bb|apte.temax,2	Pick up correct quantum
	sbq	bb|apte.te,2
	cmpq	4000,dl
	tpl	sn_ck_big
	ldq	4000,dl
	tra	sn2
sn_ck_big:
	cmpaq	bb|max_timer_register
	tmi	sn2
	ldaq	bb|max_timer_register

sn2:
	qls	3+1		binary point at 3, round
	div	apte.timer_factor,dl convert to clock ticks
	adq	1,dl		round
	qls	12-1		timer_shift, round bit
	tpl	*+2		must have positive time value
setsmall:	ldq	=o010000,dl	small timer value
	stq	prds$last_timer_setting
	tra	0,7		success return

	even
idle_runout:
	dec	0,250000		timer runout for idle process
" 
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	hash_LOCK -- called with alleged processid in pds$arg_1
"
"		if processid is valid:
"			apt_ptr, BP -> APTE
"			APTE is locked
"			return to 1,7
"
"		if processid is invalid:
"			apt_ptr, BP = null
"			nothing new is locked
"			return is indirect through 0,7
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

hash_LOCK:
	ldq	pds$arg_1		get processid
	tmi	not_found		Neg pid may cause overflow fault
	sbq	bb|apt_offset	subtract apt offset
	div	size_of_apt_entry,du
	cmpq	bb|apt_size	check against bounds of array
	trc	not_found		was invalid offset

	mpy	size_of_apt_entry,dl Apply array to index
	eppbp	tc_data$apt,ql
	spribp	apt_ptr		return pointer to apt entry
	tsx6	LOCK_bp		hash_lock must lock to check pid
	ldq	pds$arg_1
	cmpq	bp|apte.processid	make sure it's the same one
	tze	1,7		success return from hash_lock
	tsx6	UNLOCK_bp		hash_lock found wrong pid
not_found:
	eppbp	null,*		get null pointer, return
	spribp	apt_ptr		null this too
	tra	0,7*		hash_lock failure indirect return

	even
" 
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	IPS_WAKEUP -- entry to wake up a process, turn on the ips_pending
"	flag and resort the process with high priority.  
"	Call is
"		call pxss$ips_wakeup(processid,message)
"	wher processid is the process id of the process to be awakened, and
"	message specifies which IPS message is being sent.
"	Currently 'message' is declared char (4), eventually it will be fixed
"
" " " " " " " " " " " " " " " " " " " " " " "" " " "  " " " " " " " " " " " " "


ips_wakeup_int:
	tsx6	setup_
	stz	pds$wakeup_flag	save info about which entry
	tra	ijoin
ips_wakeup:
	tsx6	setup_
	stc1	pds$wakeup_flag
ijoin:
	lda	ap|2,*		get processid
	sta	pds$arg_1
	lda	ap|4,*		pick up IPS bit string
	sta	pds$arg_2		save in PDS
	tsplb	setup_check	switch stacks and lock
	arg	0,6
	tsx6	WRITE_LOCK	ips_wakeup protect pid, rethreads
	tsx7	hash_LOCK		hash search for the APTE
	arg	ips_wakeup_returns_nul no, don't continue
	lxl0	bp|apte.state	make sure not stopped
	cmpx0	stopped,du
	tze	ips_wakeup_returns
	lda	pds$arg_2		get IPS bit string
	tsx6	send_ips_wakeup	Do the real work, destroys pds$arg_3
"				Next instruction may be skipped on return
	nop	0		The subroutine already did the right thing
ips_wakeup_returns:
	tsx6	UNLOCK_bp		We're all done now
ips_wakeup_returns_nul:
	tsx6	UNLOCK
	tra	switch_back	Return to caller
"
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"
"	SEND_IPS_WAKEUP:  called via tsx6.  It saves the value
"	of x6 in pds$arg_3 for use when returning.  This is OK
"	since the current callers are WAKEUP and IPS_WAKEUP
"	who know what they are doing.  It expects the IPS bit
"	string to be in the a register when it is called.  It
"	also expects pds$wakeup_flag to be set to 0 if good
"	priority is desired and to > 0 if not.
"
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""

send_ips_wakeup:
	stx6	pds$arg_3		save x6 for later return
	tsx6	add_ips_message

	szn	pds$wakeup_flag	should we give good priority ?
	tpnz	send_ips_ij2	no, skip code which gives priority

	lxl0	bp|apte.state	get state of process being awakened
	cmpx0	ptlocking,du	bugfix:dont OOB quit buckets
	tpl	*+2		bugfix:dont OOB quit buckets
	aos	bb|quit_counts,0	and count this ips priority wakeup

	ldq	bp|apte.te	first update ti
	adq	bp|apte.ts
	adq	bp|apte.ti
	mpy	bb|quit_priority	get new priority
	lls	36-18		(binary point at 18)
	eax3	0,au		Save ti in X3
	tsx0	setup_p_int	boost priority
	stx3	bp|apte.ti	Store ti from X3.
send_ips_ij2:
	ldx0	bp|apte.flags	don't move if eligible
	canx0	apte.eligible,du
	tze	send_ips_non_elig
	canx0	apte.dbr_loaded,du	however, running may never notice
	tnz	send_ips_connect
	tra	send_ips_wakeup_returns otherwise will notice at next run time
send_ips_non_elig:
	lxl0	bp|apte.state
	cmpx0	blocked,du	if not blocked, move in q
	tze	send_ips_connect
	tsx7	unthread
	tsx7	sort_in
send_ips_connect:
	tsx7	send_connect	send connect if process running
	tsx7	wake		wake up process
	ldx6	pds$arg_3		restore x6
	tra	1,x6		return to wake done place
send_ips_wakeup_returns:
	ldx6	pds$arg_3		restore x6
	tra	0,x6		return to no wake done place
"
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"
"	SETUP_INT: called via tsx0, sets apte variables to give
"	high priority and initial quantum following interaction.
"	SETUP_P_INT: gives high priority etc w/o interaction.
"

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
setup_p_int:
	aos	bb|p_interactions	Meter priority interactions.
	lcx1	apte.interaction+1,du
	ansx1	bp|apte.flags	Turn off interaction flag.

setup_int:
	ldx1	bp|apte.wct_index
	stz	bp|apte.ti	High priority.
	stz	bp|apte.ts
	stz	bp|apte.te
	read_clock		" Calc new deadline.
	adaq	bb|wcte.resp1,1	By adding to curr time.
	staq	bp|apte.deadline

	ldq	bb|wcte.quantum1,1	Pick up new quantum.
	lda	bb|deadline_mode	If deadline mode
	ada	bb|wcte.realtime,1	or realtime process
	tpnz	*+2		use per-workclass quantum.
	ldq	bb|tefirst	use default.
	stq	bp|apte.temax

	tra	0,0		Return from setup_int.
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	SETUP_IO_REALTIME: called via tsx0, sets apte variables
"	for a realtime burst of eligibility
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

setup_io_realtime:
	read_clock
	adl	bb|realtime_io_deadline
	staq	bp|apte.deadline
	ldq	bb|realtime_io_quantum	" And quantum
	stq	bp|apte.temax
	aos	bb|realtime_priorities	" Meter
	tra	0,0

"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	REVOKE_ELIG: called with write_lock, turns off elig, decrements counters
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

revoke_elig:
	lcx1	apte.eligible+1,du	take away eligibilty
	ansx1	bp|apte.flags
	lca	1,dl		Put -1 in Areg for subtracts ..
	asa	bb|n_eligible
	lxl1	bp|apte.flags2	If was batch
	canx1	apte.batch,du	then decrement batch counter
	tze	*+2		else dont
	asa	bb|num_batch_elig	use -1 in the A

	ldx1	bp|apte.wct_index	Decrement workclass counter.
	asa	bb|wcte.nel,1
	lda	bp|apte.saved_temax	Make up for init decrement
	asa	bb|wcte.credits,1	of wc credits.
	ldq	bb|wcte.governed_word,1	Is wc governed
	canq	wcte.governed,du
	tze	*+2		No
	asa	bb|wcte.governing_credits,1
	lca	bp|apte.ws_size	Sub ws from sum of elig ws
	asa	bb|ws_sum
	stz	bp|apte.wait_event	he won't get notified

"	Give up the stack_0 if it has a shared one

	lda	bp|apte.flags
	cana	apte.shared_stack_0,du
	tze	0,7

"	Don't give up the stack for a stopped process, unless the
"	limit of such suspended stacks has been reached

	lxl0	bp|apte.state
	cmpx0	stopped,du	is he stopped
	tnz	give_up_stack	no--OK to release stack
	lda	bb|stopped_stack_0	check limit on suspended
	cmpa	bb|max_stopped_stack_0    stacks
	tpl	give_up_stack	too bad--flush the stack
	aos	bb|stopped_stack_0	add to count of suspended stacks
	ldx0	-1,du		and decrement count of
	asx0	bb|max_max_eligible   available stacks
	tra	0,7
give_up_stack:
	
"	Check the validity of the stack

	eppab	sst$
	ldaq	pds$stack_0_sdwp,*
	arl	12
	sbla	sst$ptwbase
	eppab	ab|0,al		ptr to ptw for page 0

	lda	ab|0		get the ptw
	cana	ptw.wired,dl	check page 0 wired
	drlnz	(pxss: stack_0 page 0 wired) SHOULD NOT BE!!!!

	tsx6	lock_stack_queue
	ldaq	pds$stack_0_sdwp,*	Get stack SDW
	drlze	(pxss: no stack_0 sdw) LOSE! snb/0
	lxl0	ab|sdt.num_stacks	This is how many slots
	eax4	0		Entry index
free_stack_0_loop:
	cmpaq	ab|sdt.sdw,4
	tze	free_stack_0_got
	eax4	sdte_size,4
	eax0	-1,0
	tpnz	free_stack_0_loop
	drltra	(pxss: freeing unknown stack_0) STACK NOT FOUND
free_stack_0_got:
	tsx6	free_stack_0	Give it up
	fld	0,dl
	staq	pds$stack_0_sdwp,*	Clear out SDW for getwork check.

	tsx6	unlock_stack_queue

	lcx0	apte.shared_stack_0+1,du
	ansx0	bp|apte.flags	reset flag
	tra	0,7		return from revoke_elig

"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	RESCHEDULE: called with write_lock set
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "


reschedule:
	lda	bp|apte.te	update te into ts
	tmi	*+2		avoid neg ts
	asa	bp|apte.ts

	ldx1	bp|apte.wct_index
	read_clock		" Calc new deadline.
	adaq	bb|wcte.resp2,1	By adding to cutime.
	staq	bp|apte.deadline

	ldq	bb|wcte.quantum2,1	Pick up new quantum.
	lda	bb|deadline_mode	If in deadline_mode
	ada	bb|wcte.realtime,1	or realtime process
	tpnz	*+2		then use per workclass quantum.
	ldq	bb|telast
	stq	bp|apte.temax

	lda	bb|tefirst	if ts>min(tefirst+ti,timax)
	ada	bp|apte.ti
	cmpa	bp|apte.timax
	tmi	*+2
	lda	bp|apte.timax
	cmpa	bp|apte.ts
	tpl	nupti
	lda	bp|apte.ts	update ts into ti
	asa	bp|apte.ti
	stz	bp|apte.ts
nupti:	stz	bp|apte.te

	lda	bp|apte.flags2	is this a guaranteed eligibility process
	cana	apte.prior_sched,dl	..
	tze	rs_ps_done	if not then OK
	aos	bb|lost_priority_eligibility record loss of eligibility
	ldaq	bp|apte.state_change_time compute when to boost priority
	adl	bb|priority_sched_inc
	cmpaq	bb|priority_sched_time remember when to boost next
	tpl	rs_ps_done	..
	staq	bb|priority_sched_time
	cmpaq	bb|next_alarm_time	update time of next alarm if necessary
	tpl	rs_ps_done	..
	staq	bb|next_alarm_time
rs_ps_done:
	tra	0,7		reschedule returns

"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	purge_UNLOCK: called with write_lock and apte_lock, it unlocks both
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "


purge_UNLOCK:
	stz	bp|apte.ws_size

	tsx6	UNLOCK_bp		UNLOCK own apte before call out
	tsx6	UNLOCK

	szn	bb|post_purge_switch If at all.
	tze	pU_ret
	ldx1	bp|apte.wct_index
	szn	bb|wcte.purging,1	Purge on per wc basis
	tze	pU_ret
	call	page$post_purge	clean up last process's pages
pU_ret:	tra	0,7		Return from purge_UNLOCK


" 
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	stop_check_ -- call if old user probably is stopped
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "


stop_check_:
	tsx6	WRITE_LOCK	prevent empty_t interference
	lxl0	bp|apte.state	check for really stopped
	cmpx0	stopped,du
	tnz	stop_ul		not stopped, dont notify
	lda	=astop		message will be "stopstop"
	sta	tmp_ev_message	..
	sta	tmp_ev_message+1
	tsplb	wake_term_pid	send stopstop to term pid
	eppbp	apt_ptr,*		repair BP for next test
stop_ul:	tsx6	UNLOCK
	tra	end_stop_check	stop_check_ ret to getwork



" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	cpu_monitor_ -- called if probably need to send cpulimit wakeup
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

cpu_monitor_:
	ldaq	bp|apte.virtual_cpu_time
	lrs	10		convert to 1/1024 secs
	cmpq	bp|apte.cpu_monitor
	tmi	end_cpu_monitor
	tsx6	WRITE_LOCK
	tsx6	LOCK_bp
	szn	bp|apte.cpu_monitor
	tze	cm_not
	ldaq	bp|apte.virtual_cpu_time
	lrs	10		convert to 1/1024 secs
	cmpq	bp|apte.cpu_monitor
	tmi	cm_not
	stz	bp|apte.cpu_monitor	clear cell
	tsx6	UNLOCK_bp
	ldaq	cpulimit_msg
	staq	tmp_ev_message
	tsplb	wake_term_pid	send cpulimit to term_pid
cm_done:	tsx6	UNLOCK
	tra	end_cpu_monitor	return to gw

cm_not:	tsx6	UNLOCK_bp
	tra	cm_done

	even
cpulimit_msg:
	aci	"cpulimit"
"

" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	WAKE_TERM_PID -- send tmp_ev_message to term_pid over term_chan
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

wake_term_pid:
	stz	dev_signal	count it as dev_signal
	aos	dev_signal	count it as dev_signal
	ldaq	bp|apte.term_channel
	staq	tmp_ev_channel
	stz	tmp_ring
	lda	bp|apte.term_processid
	sta	pds$arg_1
	stc1	pds$wakeup_flag	not require unique message
	eppbp	bb|0,au		make bp -> target_apte
	tsx6	LOCK_bp		wake_term_pid locks target of wakeup
	tsx7	make_itt_message
	tsx7	wake
	tsx6	UNLOCK_bp		wake_term_pid unlocks target apte
	tra	lb|0		wake_term_pid returns

"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	DELETE_ME
"
"	This is the legendary CPU Graveyard, where CPUs go to die
"	when taken off-line.  When a CPU is to be deleted, getwork
"	will run only an idle process on it.  When this happens,
"	the process will transfer to this routine.  This routine
"	does the following:
"
"	1. Under the Global APT lock, checks whether the system default
"	   CPU set would contain no online CPUs. 
"
"	2. If the system default CPU set would contain no online
"	   CPUs, it changes the system default CPU set to all
"	   CPUs, unlocks, and prints a message on the console.
"	   Under the Global APT lock, checks the system default CPU
"	   set again.  This second check is necessary to avoid races,
"	   since the call to syserr must be made with the Global APT
"	   lock unlocked. If the second check fails, the first is
"	   repeated.  This should happen very rarely.
"
"	3. Changes the state of the idle process for this CPU to
"	   empty, unthreading it, but not returning it to the empty
"	   list.
"
"	4. Under the connect lock, turn off the bit for this CPU
"	   in scs$processor (this must be done under the connect
"	   lock to prevent other CPUs doing connects from spinning
"	   forever waiting for this one to respond).
"
"	5. Walk the APTE array, looking for processes whose set of
"	   required CPUs includes no online CPUs. Any such are changed
"	   to require the current system default. This must be done
"	   under the Global APT lock.
"
"	6. Unlock the Global APT lock and die (inhibited DIS).
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

delete_me:
	tsx6	WRITE_LOCK	delete_me locks out sprq

	lda	prds$processor_pattern	bit for this CPU
	era	scs$processor		Areg = bits for remaining CPUs
	ana	bb|default_procs_required	Any left in default?
	tnz	procs_required_ok		Yes
retry_delete_me:				" Also come here if lost race
	lda	all_procs_required,du	Set default to all CPUs
	sta	bb|default_procs_required

	tsx6	UNLOCK		Unlock before call-out

"	call	syserr (3, "pxss: System default procs required reset to ABCDEFGH")

	epplb	reset_system_procs_severity
	sprilb	arg+2
	epplb	reset_system_procs_mess
	sprilb	arg+4
	epplb	fixed_desc
	sprilb	arg+6
	epplb	reset_system_procs_desc
	sprilb	arg+8
	ldaq	=v18/4,18/4,18/4,18/0
	staq	arg
	call	syserr$syserr(arg)

	tsx6	WRITE_LOCK	relock before checking again

	lda	prds$processor_pattern	Bit for this CPU
	era	scs$processor		Areg = bits for remaining CPUs
	ana	bb|default_procs_required	Any left online
	tze	retry_delete_me		No - lost race

procs_required_ok:

"	Walk APTE array, looking for processes which can't run

	ldq	scs$processor	Bits for CPUs still running
	anq	apte.procs_required_mask,du	Strip out garbage
	erq	prds$processor_pattern	And bit for this CPU
	lca	1,dl
	era	apte.procs_required_mask,du	To reset procs required
	ldx3	bb|default_procs_required	To set to default
	anx3	apte.procs_required_mask,du	Strip out garbage
	ldx2	apte.default_procs_required,du To set bit saying it's default
	eppap	tc_data$apt	Begin of APTE array
	lxl1	bb|apt_size	Number APTEs
check_proc_loop:
	ldx0	ap|apte.flags
	canx0	apte.idle,du	Skip idle processes
	tnz	check_proc_next
	canq	ap|apte.procs_required Any left for this process?
	tnz	check_proc_next	Yes
	ansa	ap|apte.procs_required Clear procs required
	orsx3	ap|apte.procs_required Set to default
	orsx2	ap|apte.flags	And remember it's the default
check_proc_next:
	eppap	ap|size_of_apt_entry Next APTE
	eax1	-1,1		One less to go
	tpnz	check_proc_loop	But still some left

	inhibit	on		<+><+><+><+><+><+><+><+><+><+><+><+>

	lxl1	prds$processor_tag
	lcx0	processor_data.online+1,du turn this bit OFF
	ansx0	scs$processor_data,1 ..

	eppbp	prds$idle_ptr,*	bp -> APTE for idle process
	tsx6	LOCK_bp
	ldx0	empty,du		set empty state
	tsx7	update_execution_state ..
	tsx6	UNLOCK_bp

	inhibit	off		<-><-><-><-><-><-><-><-><-><-><-><->
	lda	pds$processid	lock the connect lock
	stac	scs$connect_lock	before diddling scs$processor
	nop
	nop
	tnz	-3,ic
	lca	1,dl		one's in A
	era	prds$processor_pattern make a mask
	ansa	scs$processor	turn off bit for this processor
	lda	0,dl		clear the A
	ansa	scs$connect_lock	can undo lock now

	inhibit	on		<+><+><+><+><+><+><+><+><+><+><+><+>
	lxl1	prds$processor_tag
	lcx0	processor_data.delete_cpu+1,du turn this bit OFF
	ansx0	scs$processor_data,1
	ldx0	processor_data.offline+processor_data.halted_cpu,du turn these bits ON
	orsx0	scs$processor_data,1

	tsx6	UNLOCK		undo the lock before dying

	dis	=o777,dl
	tra	*-1

	inhibit	off		<-><-><-><-><-><-><-><-><-><-><->

reset_system_procs_severity:
	dec	3		" severity of syserr message
reset_system_procs_mess:
	aci	"pxss: System default procs required reset to ABCDEFGH"
	equ	reset_system_procs_words,*-reset_system_procs_mess
	equ	reset_system_procs_chars,4*reset_system_procs_words
reset_system_procs_desc:
	vfd	1/1,6/21,5/0,24/reset_system_procs_chars
"
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " 
"
"	Hardcore stack queue locking/unlocking
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

lock_stack_queue:
	lda	pds$processid
	eppab	stack_0_data$
	cmpa	ab|sdt.lock
	drlze	(pxss: mylock on stack queue) mylock err
	stac	ab|sdt.lock
	nop
	nop
	tnz	*-3
	nop
	cmpa	ab|sdt.lock	Check for stac loss.
	drlnz	(pxss: lock_stack_queue stac failed)
	tra	0,6		That's why locks are expensive in Multics.

unlock_stack_queue:
	ldq	pds$processid
	cmpq	ab|sdt.lock	Check for other random lossage
	drlnz	(pxss: unlock_stack_queue not my lock)
	eaa	0
	stacq	ab|sdt.lock
	drlnz	(pxss: unlock_stack_queue stacq failed) stacq claims failure
	nop
	cmpq	ab|sdt.lock	Stacq really win?
	drlze	(pxss: unlock_stack_queue stacq failed)
	tra	0,6
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	subroutine to return a stack_0 to the free list
"
"	tsx6	free_stack_0
"
"	On entry -
"	     sdt lock must be owned
"	     ab -> stack_0_data$
"	     x4 = index of sdte for this stack
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
	
free_stack_0:
	eax0	0
	sxl0	ab|sdt.aptep,4	No aptep
	eax0	ab|sdt.stacks,4	Thread in
	ldx4	ab|sdt.freep
	stx4	ab|sdte.nextp,0
	stx0	ab|sdt.freep
	tra	0,6
	
add_ips_message:
	sta	tmp_event		"Remember the event
add_ips.retry:
	ldq	bp|apte.ips_message
	lda	bp|apte.ips_message
	ora	tmp_event
	stacq	bp|apte.ips_message	
	tnz	add_ips.retry
	tra	0,x6
		
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
"
"	SUSPEND_GETWORK -- entry to loop until getwork all suspended
"
"	Call is
"		call pxss$suspend_getwork ()
"
"
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " "

suspend_getwork:
	lda	pds$processid		see if i have the suspension lock
	cmpa	tc_data$tc_suspend_lock
	tnz	sg_returns		return if we don't hold lock

	tsplb	setup_mask	going to have the APT locked
	tsx6	WRITE_LOCK	while we pre-empt other processes

	lda	bb|eligible_q_head
sg_pe_loop:
	ldaq	bb|apte.thread,au
	tra	sg_pe_tv,ql*

	arg	sg_pe_done	-1 => sentinel
sg_pe_tv:	arg	DRL_empty_apte	empty - in eligible list??
	arg	sg_pre_empt_it	running - get the process out of there
	arg	sg_pe_loop	ready - skip it
	arg	sg_pe_loop	waiting - skip it
	arg	DRL_blocked_apte	blocked - doesn't belong here
	arg	DRL_stopped_apte	stopped - doesn't belong here
	arg	sg_pe_loop	ptlwait - skip it

sg_pre_empt_it:
	lxl3	bb|apte.thread,au	get this apte addr from next apte
	tsx6	LOCK_x3
	ldq	bb|apte.flags,3	get a copy of the flags
	canq	apte.pre_empted,du	is it already pre-empted?
	tnz	sg_pe_skip	yes, so skip it
	lda	apte.pre_empted+apte.pre_empt_pending,du
	orsa	bb|apte.flags,3	turn on flags to indicate pre-emption
	ldq	bb|apte.flags2,3	get processor tag for the connect
	anq	apte.pr_tag_mask,dl
	cmpq	prds$processor_tag	is it this processor?
	tze	sg_pe_skip	yes, don't connect myself
	cioc	scs$cow_ptrs,ql*

sg_pe_skip:
	tsx6	UNLOCK_x3		release the apte
	ldaq	bb|apte.thread,3	clobbered by sg_pre_empt_it code
	tra	sg_pe_loop

sg_pe_done:
	tsx6	UNLOCK		give up the lock for a few microseconds

	nop	0		delay between lockings
	nop	0

	tsx6	READ_LOCK		for seeing if all are off the cpus

	lda	bb|eligible_q_head
sg_wt_loop:
	ldaq	bb|apte.thread,au
	tra	sg_wt_tv,ql*

	arg	sg_wt_done	-1 => sentinel
sg_wt_tv:	arg	DRL_empty_apte	empty - in eligible list??
	arg	sg_check_it	running - see who it is
	arg	sg_wt_loop	ready - skip it
	arg	sg_wt_loop	waiting - skip it
	arg	DRL_blocked_apte	blocked - doesn't belong here
	arg	DRL_stopped_apte	stopped - doesn't belong here
	arg	sg_wt_loop	ptlwait - skip it

sg_check_it:
	lxl3	bb|apte.thread,au	get this apte addr from next apte
	tsx6	LOCK_x3
	ldq	bb|apte.flags,3	get a copy of the flags
	canq	apte.idle,du	is it an idle?
	tnz	sg_wt_skip	yes - this is ok.
	ldq	bb|apte.flags2,3	get processor tag for this process
	anq	apte.pr_tag_mask,dl
	cmpq	prds$processor_tag	is it me??
	tze	sg_wt_skip	yes - this is also ok.

	tsx6	UNLOCK_x3		no - unlock and check all over again
	tra	sg_pe_done

sg_wt_skip:
	tsx6	UNLOCK_x3		release the apte
	ldaq	bb|apte.thread,3	clobbered by sg_check_it code
	tra	sg_wt_loop

sg_wt_done:
	tsx6	UNLOCK
	tsx7	switch_back_ret_pds

sg_returns:
	short_return


"
" BEGIN MESSAGE DOCUMENTATION

" Message:
" pxss: notify time out: event=ZZZZZZZZZZZZ, pid=XXXXXXXXXXXX

" S:	$info

" T:	$run

" M:	A hardcore event has not occurred within a reasonable time.
" This may be due to hardware problems
" or to a programming error.
" The system attempts to continue operation.

" A:	If this message persists, contact system programmers.


" Message:
" pxss: notify time out:  event=ZZZZZZZZZZZZ. During init/shutdown.

" S:	$info

" T:	$init

" M:	A hardcore event has not occurred within a reasonable time
" during system initialization or shutdown.  This may be due to hardware 
" problems or to a programming error.  The system attempts to continue
" initialization or shutdown.

" A:	If this message persists, contact system programmers.


" Message:
" pxss: System default procs required reset to ABCDEFGH

" S:	$beep

" T:	During CPU deconfiguration.

" M:	Due to the deletion of a CPU, there were no online CPUs
" remaining in the default set of CPUs.  These CPUs are the only CPUs
" on which processes can run which have not requested to be run on
" specific CPUs. The default set of CPUs has been changed to all
" online CPUs.

" A:	$inform_sa

" Message:
" pxss: APTE not locked
"
" S:	$crash
"
" T:	$run
"
" M:	$err
"
" A:	$recover

" Message:
" pxss: APTE disdains all processors
"
" S:	$crash
"
" T:	$run
"
" M:	$err
"	There are no processors-required set for this APTE.
"
" A:	$recover

" Message:
" pxss: No term_processid
"
" S:	$crash
"
" T:	$run
"
" M:	$err
"	As a process was about to indicate its demise to the master process,
"	it discovered, to its chagrin, that it had forgotten who that was.
"
" A:	$recover

" Message:
" pxss: Stop returned from getwork
"
" S:	$crash
"
" T:	$run
"
" M:	$err
"
" A:	$recover

" Message:
" pxss: sprq already on prds
"
" S:	$crash
"
" T:	$run
"
" M:	$err
"
" A:	$recover

" Message:
" pxss: empty_t APTE not stopped or empty
"
" S:	$crash
" 
" T:	$run
"
" M:	$err
"	an attempt was made to clear an APTE that of a process that was neither
"	stopped nor empty.
"
" A:	$recover

" Message:
" pxss: APT not locked
"
" S:	$crash
" 
" T:	$run
"
" M:	$err
"
" A:	$recover

" Message:
" pxss: unthread null back ptr
"
" S:	$crash
" 
" T:	$run
"
" M:	$err
"
" A:	$recover

" Message:
" pxss: unthread prev.fp ^= cur
"
" S:	$crash
" 
" T:	$run
"
" M:	$err
"
" A:	$recover

" Message:
" pxss: unthread null cur.fp
"
" S:	$crash
" 
" T:	$run
"
" M:	$err
"
" A:	$recover

" Message:
" pxss: unlock apt read lock bad count
"
" S:	$crash
" 
" T:	$run
"
" M:	$err
"
" A:	$recover

" Message:
" pxss: write_to_read bad lock count
"
" S:	$crash
" 
" T:	$run
"
" M:	$err
"
" A:	$recover

" Message:
" pxss: write_to_read ldac failed
"
" S:	$crash
" 
" T:	$run
"
" M:	$err
"	This indicates a hardware error.
"
" A:	$recover

" Message:
" pxss: UNLOCK_bp not locked
"
" S:	$crash
" 
" T:	$run
"
" M:	$err
"
" A:	$recover

" Message:
" pxss: UNLOCK_X2 not locked
"
" S:	$crash
" 
" T:	$run
"
" M:	$err
"
" A:	$recover

" Message:
" pxss: UNLOCK_x3 not locked
"
" S:	$crash
" 
" T:	$run
"
" M:	$err
"
" A:	$recover

" Message:
" pxss: subroutine_save stack overflow
"
" S:	$crash
" 
" T:	$run
"
" M:	$err
"
" A:	$recover

" Message:
" pxss: ITT overflows
"
" S:	$crash
" 
" T:	$run
"
" M:	$err
"
" A:	$recover

" Message:
" pxss: untenable empty APTE
"
" S:	$crash
" 
" T:	$run
"
" M:	$err
"
" A:	$recover

" Message:
" pxss: untenable blocked APTE
"
" S:	$crash
" 
" T:	$run
"
" M:	$err
"
" A:	$recover

" Message:
" pxss: untenable stopped APTE
"
" S:	$crash
" 
" T:	$run
"
" M:	$err
"
" A:	$recover

" Message:
" pxss: thread_him_in already threaded
"
" S:	$crash
" 
" T:	$run
"
" M:	$err
"
" A:	$recover

" Message:
" pxss: thread_him_in x1 zero
"
" S:	$crash
" 
" T:	$run
"
" M:	$err
"
" A:	$recover

" Message:
" pxss: thread_him_in x4 zero
"
" S:	$crash
" 
" T:	$run
"
" M:	$err
"
" A:	$recover

" Message:
" pxss: thread_him_in x4->apte.fp ^= x1
"
" S:	$crash
" 
" T:	$run
"
" M:	$err
"
" A:	$recover

" Message:
" pxss: apte.state ^= ready
"
" S:	$crash
" 
" T:	$run
"
" M:	$err
"
" A:	$recover

" Message:
" pxss: thread_him_in x0 zero
"
" S:	$crash
" 
" T:	$run
"
" M:	$err
"
" A:	$recover

" Message:
" pxss: no available stack_0
"
" S:	$crash
" 
" T:	$run
"
" M:	$err
"
" A:	$recover

" Message:
" pxss: stack_0 page 0 wired
"
" S:	$crash
" 
" T:	$run
"
" M:	$err
"	process loading was about to wire the first page of the ring 0 stack when
"	it discovered that it had been beaten to the punch.
"
" A:	$recover

" Message:
" pxss: no stack_0 sdw
"
" S:	$crash
" 
" T:	$run
"
" M:	$err
"
" A:	$recover

" Message:
" pxss: freeing unknown stack_0
"
" S:	$crash
" 
" T:	$run
"
" M:	$err
"	the stack_0 being returned could not be found in the list of stack_0s.
"
" A:	$recover

" Message:
" pxss: mylock on stack queue
"
" S:	$crash
" 
" T:	$run
"
" M:	$err
"
" A:	$recover

" Message:
" pxss: lock_stack_queue stac failed
"
" S:	$crash
" 
" T:	$run
"
" M:	$err
"	This indicates a hardware error.
"
" A:	$recover

" Message:
" pxss: unlock_stack_queue not my lock
"
" S:	$crash
" 
" T:	$run
"
" M:	$err
"
" A:	$recover

" Message:
" pxss: unlock_stack_queue stacq failed
"
" S:	$crash
" 
" T:	$run
"
" M:	$err
"	This indicates a hardware error.
"
" A:	$recover

" END MESSAGE DOCUMENTATION

	end



		    bull_copyright_notice.txt       08/30/05  1008.4r   08/30/05  1007.3    00020025

                                          -----------------------------------------------------------


Historical Background

This edition of the Multics software materials and documentation is provided and donated
to Massachusetts Institute of Technology by Group Bull including Bull HN Information Systems Inc. 
as a contribution to computer science knowledge.  
This donation is made also to give evidence of the common contributions of Massachusetts Institute of Technology,
Bell Laboratories, General Electric, Honeywell Information Systems Inc., Honeywell Bull Inc., Groupe Bull
and Bull HN Information Systems Inc. to the development of this operating system. 
Multics development was initiated by Massachusetts Institute of Technology Project MAC (1963-1970),
renamed the MIT Laboratory for Computer Science and Artificial Intelligence in the mid 1970s, under the leadership
of Professor Fernando Jose Corbato.Users consider that Multics provided the best software architecture for 
managing computer hardware properly and for executing programs. Many subsequent operating systems
incorporated Multics principles.
Multics was distributed in 1975 to 2000 by Group Bull in Europe , and in the U.S. by Bull HN Information Systems Inc., 
as successor in interest by change in name only to Honeywell Bull Inc. and Honeywell Information Systems Inc. .

                                          -----------------------------------------------------------

Permission to use, copy, modify, and distribute these programs and their documentation for any purpose and without
fee is hereby granted,provided that the below copyright notice and historical background appear in all copies
and that both the copyright notice and historical background and this permission notice appear in supporting
documentation, and that the names of MIT, HIS, Bull or Bull HN not be used in advertising or publicity pertaining
to distribution of the programs without specific prior written permission.
    Copyright 1972 by Massachusetts Institute of Technology and Honeywell Information Systems Inc.
    Copyright 2006 by Bull HN Information Systems Inc.
    Copyright 2006 by Bull SAS
    All Rights Reserved
