.TITLE  my_uwss
	.IDENT  'V01-001'

	.PAGE
	.SBTTL  External symbol definitions

	.LIBRARY		/SYS$LIBRARY:LIB/

	$LNMDEF                         ; Logical name definitions
	$PCBDEF                         ; Process control block definitions
	$PLVDEF				; PLVDEF definitions
	$PRVDEF                         ; Privilege definitions
	$PSLDEF                         ; Process status longword definitions
	$SSDEF                          ; System status codes

; Set up the psect for the dispatching to be done.  Note that all routines are
; kernel mode 
 	.psect user_services, page, vec, nowrt, exe, pic, shr
 	.long	PLV$C_TYP_CMOD	; Type of vector
 	.long	0		; System Version
 	.long	4		; Kernel routine count
 	.long	0		; Exec routine count
 	.address kernel_vector	; Kernel routine list
 	.address exec_vector	; Exec routine list
 	.long	0		; Rundown routine
 	.long	0		; Reserved
 	.long	0		; RMS dispatcher
 	.long	0		; Reserved
 	.long	0		; Reserved

; Here's the list of routines that can be called from kernel mode

; Note that kernel mode system services receive R4 loaded with the address of 
; the PCB as input.

kernel_vector:
	.address	initialize
	.address	set_value
	.address	print_message
	.address	get_value

; Here's the (boring) list of routines that can be called from executive mode
exec_vector:

	.PSECT  $DATA$,4,WRT,NOEXE

; execlet_vector_address will receive the translation of the logical name 
; defined by the execlet.  If will be the address of the vector of information 
; provided by the execlet.  This will include a version number, the number of 
; data and routine entries to follow, and the addresses of those data and 
; routine PDs.

; Note that get_value_addr is a global symbol.  It is exported via a
; symbol_vector as a datum, not as a procedure.  This allows programs that use 
; it to call directly to the execlet, rather than calling first to this system 
; service and then to the execlet.  I still export the get_value routine, 
; however, so that a program can come through here first if it wants to.

; Make sure these stay initialized to "0", since routines in this image won't
; call initialize unless it sees them as zero.  Also, the sample calling program 
; won't call initialize when it calls get_value_addr unless it sees it as zero.

execlet_vector_address:	.long	0

version_number:		.long	0
entry_count:		.long	0

kernel_routines:
value_addr:		.long	0
set_value_addr:		.long	0
get_value_addr::	.long	0
print_message_addr:	.long	0

ret_len:
	.long	0

; Stuff for translating the logical name which was defined by the execlet
system_table:
	.ascid	/LNM$SYSTEM_TABLE/
my_execlet_vector:
	.ascid	/MY_EXECLET_VECTOR/
.align 3
crlnm_item_list:
	.word 4
	.word LNM$_STRING
	.address execlet_vector_address
	.address ret_len
	.long 0

	.psect  exec$init_code, exe,  wrt,pic,4

initialize::	.call_entry preserve=<R2, R3, R4, R5>

; Programs need to call this initialize routine first so that they will know 
; where the routines live.  This call may be done explicitly, or implicitly 
; through other routines in this UWSS.

; First, see if we've been here before

	TSTL	execlet_vector_address
	BNEQ	88$

; Translate the logical name, and put the translation into "execlet_vector_address"

$TRNLNM_S -
	TABNAM  = system_table, -
	LOGNAM  = my_execlet_vector, -
	ACMODE  = #PSL$C_KERNEL, -
	ITMLST  = crlnm_item_list

	CMPL	r0, #SS$_NORMAL
	BNEQ	99$

; Move the addresses pointed to by "execlet_vector_address" into the data and routine
; address locations.

; Note that this layout needs to be well coordinated with the vector in the execlet.

	movl    execlet_vector_address, R0

; R0 is pointing to the version number.  See if it is 1
	cmpl    (R0)+, #1
	bneq    77$

; R0 is now pointing to the routine count.  Multiply it by 4 (size of an
; address) to get the number of bytes to move to set the data & routine
; addresses.
	ashl    #2, (R0)+, R1

; Now move the list of addresses from the execlet to the UWSS COMMON_DATA psect
; MOVCx trashes R0-R5
	movc3   r1, (R0), kernel_routines
88$:
	movl    #SS$_NORMAL, R0
99$:
	RET
77$:

; If the version number isn't right, complain about IDMISMATCH
	MOVL	#SS$_IDMISMATCH, R0
	RET

	.PSECT  USER_CODE,4,NOWRT,EXE,PIC

; Note the "@" in the following calls, which says to call the routine whose
; address is in xxxx_addr -- this is not calling a routine whose name is
; xxxx_addr.  Minor point:  xxxx_addr contains the address of the
; Procedure Descriptor of the routine loaded by the execlet.  The second
; quadword of the PD is the address of the first instruction in the routine. 

set_value::	.call_entry

; See if the initialize routine needs to be called.
	TSTL	set_value_addr
	BNEQ	22$
	JSB	initialize
22$:
; Call the routine whose address is in set_value_addr
	CALLS	#0, @set_value_addr
	RET

print_message::	.call_entry

; Make sure the caller has CMKRNL.  If not, we don't want to let them do this.
	BBC S^#PRV$V_CMKRNL,-		; Check for CMKRNL privilege
	    PCB$Q_PRIV(R4),77$

; See if the initialize routine needs to be called.
	TSTL	print_message_addr
	BNEQ	22$
	JSB	initialize
22$:
; Call the routine whose address is in print_message_addr
	calls	#0, @print_message_addr
	RET
77$:
	MOVL	#SS$_NOPRIV,R0
	RET

get_value::	.call_entry

; See if the initialize routine needs to be called.
	TSTL	get_value_addr
	BNEQ	22$
	JSB	initialize
22$:

; Make sure the address they passed is writeable.
	IFNOWRT	#4, @4(ap), 99$
	pushl	4(ap)
; Call the routine whose address is in get_value_addr
	calls	#1, @get_value_addr

	RET
99$:	MOVL	#SS$_ACCVIO,R0
	RET

	.END