.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= ; 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