 	.TITLE	MONLAT
	.IDENT	/V1.1/

; This program will listen for LAT multicast messages and display information
; from the messages it finds.

; Author = David Gagne

	.LIBRARY	"SYS$LIBRARY:LIB.MLB"

	$IODEF				;Define I/O functions and modifiers
	$NMADEF				;Define Network Management parameters
	$LNMDEF				;Define logical name parameters

; Modification history
;
;	1.1	David Gagne
;		01 Modify the specific node display


; QIO data structures for the LAT channel.

; Setmode parameter buffer

SETPARM:
	.WORD	NMA$C_PCLI_PTY		; Protocol type
		.LONG	^X0460
	.WORD	NMA$C_PCLI_BFN		; Number of buffers to save
		.LONG	4
	.WORD	NMA$C_PCLI_BUS		; Max. receivable buffer size
		.LONG	1500
	.WORD	NMA$C_PCLI_PAD		; Padding
		.LONG	NMA$C_STATE_OFF
	.WORD	NMA$C_PCLI_MCA		; Multicast address
		.WORD	8
		.WORD	NMA$C_LINMC_SET
		.BYTE	09,00,^X2B,00,00,^X0F

SETPARMLEN = .-SETPARM

SETPARMDSC:
	.LONG		SETPARMLEN
	.ADDRESS	SETPARM

; Read buffer for LAT multicast messages

RCVBUF:
	.BLKB	1504

RCVBUFLEN = .-RCVBUF

; Place to store incoming P5 buffer

RCVP5:
RCVDA:	.BLKB	6
RCVSA:	.BLKB	6
RCVPTY:	.BLKB	2


; QIO data structures for the MOP (SYSTEM ID) channel.

; P2 parameter buffer for finding devices of other nodes.

SIDPARM:
	.WORD	NMA$C_PCLI_PTY		; Protocol Type
		.LONG	^X0260

SIDPARMLEN = .-SIDPARM

SIDPARMDSC:
	.LONG		SIDPARMLEN
	.ADDRESS	SIDPARM

; P2 transmit data buffer for sending request SYSID messages.

XMTP2BUF:
	.BYTE	05			; SYSTEM ID request code
	.BYTE	00			; Reserved
XMTP2RCT:
	.WORD	4242			; Receipt number (to be returned)

XMTP2LEN = .-XMTP2BUF

; Address of the node whose Device ID we would like to find.

SIDADDR:
	.BLKB	6

; Read buffer for receiving System IDs

RCVBUF2:
	.BLKB	1504

RCVBUFLEN2 = .-RCVBUF2

; Place to store incoming P5 buffer

RCVP52:
RCVDA2:	.BLKB	6
RCVSA2:	.BLKB	6
RCVPT2:	.BLKB	2


; Message structures

; General message buffer

FAODESC:
FAOLEN:	.LONG		80
	.ADDRESS	FAOBUF
FAOBUF:
	.BLKB		80

HDRMSG1:
	.ASCID	"MONLAT - Ethernet LAT monitoring program. Version 1.1"
INPMSG1:
	.ASCID	"Monitor requests available:"
INPMSG2:
	.ASCID	"    1) Monitor service announcements from all nodes"
INPMSG3:
	.ASCID	"    2) Monitor service announcements for a specific group code"
INPMSG4:
	.ASCID	"    3) Monitor service announcement  from a specific node"
INPMSG5:
	.ASCID	"    any other input will stop the program"
IOMSG:
	.ASCID	"!/The contents of the 2nd longword in the IOSB is !XL"
NDMSG:
	.ASCID	"No device found.  Please define ETH appropriately."
DNEMSG:
	.ASCID	"MONLAT complete.  Thank you for your continued support."
BLNKMSG:
	.ASCID	""

; Output strings for monitoring service announcements from all nodes

M1ALL1:
	.ASCID	"Address: AA-00-04-00-!XB-!XB; Controller !2UB=!AS; DECnet node: !2UB.!UW"
M1ALL2:
	.ASCID	"Address: !XB-!XB-!XB-!XB-!XB-!XB; Controller !2UB=!AS"
M1MASK:
	.ASCID	"           Group code bit mask:"
M1BIT1:
	.ASCID	"           (063-000) !XB !XB !XB !XB !XB !XB !XB !XB"
M1BIT2:
	.ASCID	"           (127-064) !XB !XB !XB !XB !XB !XB !XB !XB"
M1BIT3:
	.ASCID	"           (191-128) !XB !XB !XB !XB !XB !XB !XB !XB"
M1BIT4:
	.ASCID	"           (255-192) !XB !XB !XB !XB !XB !XB !XB !XB"
M1NONE:
	.ASCID	"No service announcement messages were found"

; Output strings for monitoring service announcements for a specific group

M2HDR0:
	.ASCID	"Group Code #!3UB"
M2ALL1:
	.ASCID	"   Address: AA-00-04-00-!XB-!XB; Controller !2UB=!AS; DECnet node !2UB.!UW"
M2ALL2:
	.ASCID	"   Address: !XB-!XB-!XB-!XB-!XB-!XB; Controller !2UB=!AS"
M2NONE:
	.ASCID	"No service announcement messages were found using Group Code #!3UB"

; Output strings for monitoring service announcements from a specific node

M3ADDR:
	.ASCID	"Address: !XB-!XB-!XB-!XB-!XB-!XB"
M3NODE:
	.ASCID	"   DECnet node:  !2UB.!UW"
M3CONT:
	.ASCID	"   Controller:   !UB = !AS"
M3IDN1:
	.ASCID	"   Node name:    !AD"
M3IDN2:
	.ASCID	"   Node desc:    !AD"
M3IDN3:
	.ASCID	"   Service name: !AD"
M3IDN4:
	.ASCID	"   Service desc: !AD"
M3HDR1:
	.ASCID	"   Group Codes Set:"
M3GCOD:
	.ASCID	"       !3UW"
M3NONE:
	.ASCID	"No service announcement message found from !XB-!XB-!XB-!XB-!XB-!XB"

; Strings for various device names

DEV01:	.ASCID	"DEUNA"			; Strings for various devices
DEV05:	.ASCID	"DEQNA"
DEV11:	.ASCID	"DELUA"
DEV17:	.ASCID	"LANCE"
DEV23:	.ASCID	"DEBNA"
DEV33:	.ASCID	"DS200"
DEV35:	.ASCID	"DS500"
DEV37:	.ASCID	"DELQA"
DEV39:	.ASCID	"DESVA"
DEV65:	.ASCID	"DEBNI"
DEVNR:	.ASCID	"Noans"
DEVUK:	.ASCID	"Unkwn"
DEVMS:	.ASCID	"Misng"

DEVDSC:	.BLKQ				; General device name descriptor
DEVID:	.BLKB				; Device ID from SYSID response

; Buffers, variables, and strings for the time control of the program

HOURS:	.BLKB	1			; Storage of requested input
MINUTES:.BLKB	1
SECONDS:.BLKB	1

; The following variable are for reading input from the user after prompting.

INPSTRDSC:				; Input buffer descriptor
	.LONG		0
	.ADDRESS	INPSTR

INPSTR:	.BLKB		18		; Input buffer

INPSIZ:	.BLKL	1			

; The prompts are defined next.

RPRMT:	.ASCID	"Which monitor request would you like to make: "
HPRMT:	.ASCID	"How many hours   would you like to monitor: "
MPRMT:	.ASCID	"How many minutes would you like to monitor: "
SPRMT:	.ASCID	"How many seconds would you like to monitor: "
GPRMT:	.ASCID	"Which group code would you like to monitor: "
APRMT:	.ASCID	"Enter the address of the node (ex: AA-00-04-00-75-4C): "

; Input variables from prompts

DSTADR:	.BLKB	6			; Address to look for
GCODE:	.BYTE	1			; Group code to look for

ENDTIM:	.BLKQ	1			; Time to end test
TIME:	.BLKQ	1			; Temporary time buffer
DTIME:	.ASCID	/0 !2ZB:!2ZB:!2ZB.00/	; String for calculating delta time

; Miscellaneous variables

GCBITS: .BLKB	32			; Used for examining group codes
RCVTRY:	.BLKL	1			; Counter for receive attempts
IOSB:	.BLKQ	1			; I/O status block

; Device names

DEVDSC1:.ASCID	'ETH'			; Units to use for test
DEVDSC2:.ASCID	'ESA0'
DEVDSC3:.ASCID	'XQA0'
DEVDSC4:.ASCID	'ETA0'
DEVDSC5:.ASCID	'XEA0'
DEVDSC6:.ASCID	'EXA0'
DEVDSC7:.ASCID	'EZA0'

; Table of pointers to device names

DEVADR:	.ADDRESS	DEVDSC1
	.ADDRESS	DEVDSC2
	.ADDRESS	DEVDSC3
	.ADDRESS	DEVDSC4
	.ADDRESS	DEVDSC5
	.ADDRESS	DEVDSC6
	.ADDRESS	DEVDSC7
	.LONG	0

; Channels - one for LAT and one for Remote Console

CHNLAT:	.BLKL	1
CHNRMC:	.BLKL	1


	.ENTRY	START,^M<>

; Assign both channels to the first device found which is available

	CLRL	R5			; Check each channel name to see if one
10$:	TSTL	DEVADR(R5)		; is available until one is found: the
	BEQL	30$			; first name checked is "ETH", a dummy
	MOVL	DEVADR(R5),R4		; name which can be defined to the
	$ASSIGN_S-			; device desired if either:
		DEVNAM=(R4),-		;      1) An unregistered device is used
		CHAN=CHNLAT		;  or  2) One device is prefered
	BLBS	R0,20$			; If success, assign the 2nd channel
	ADDL	#4,R5			; Skip to next device name
	CMPW	R0,#SS$_NOSUCHDEV	; Was the error "no such device"?
	BEQL	10$			; If yes, try next device name
	BRW	ERROR			; Else, exit with error
20$:	$ASSIGN_S-			; Assign the 2nd channel to the same
		DEVNAM=(R4),-		; device name
		CHAN=CHNRMC
	BLBS	R0,ASSIGN_OK		; If success, continue
	BRW	ERROR			; Else, exit with an error

; No device was found.

30$:	BSBW	BLANK			; No device was found, so say so and
	PUSHAB	NDMSG			; then exit.
	CALLS	#1,G^LIB$PUT_OUTPUT
	BRW	EXIT

ASSIGN_OK:

; Start up the first channel for examining LAV packets.

	$QIOW_S	FUNC=#<IO$_SETMODE!IO$M_CTRL!IO$M_STARTUP>,-
		CHAN=CHNLAT,-
		IOSB=IOSB,-
		P2=#SETPARMDSC

	BLBS	R0,START_REQ_OK1
	BRW	ERROR

START_REQ_OK1:
	MOVZWL	IOSB,R0
	BLBS	R0,START_IO_OK1
	BRW	ERROR

START_IO_OK1:

; Start up the second channel for getting the device ID and name.

	$QIOW_S	FUNC=#<IO$_SETMODE!IO$M_CTRL!IO$M_STARTUP>,-
		CHAN=CHNRMC,-
		IOSB=IOSB,-
		P2=#SIDPARMDSC

	BLBS	R0,START_REQ_OK2
	BRW	ERROR

START_REQ_OK2:

	MOVL	IOSB,R0
	BLBS	R0,START_IO_OK2
	BRW	ERROR

START_IO_OK2:

; Print program header

	BSBW	BLANK
	PUSHAB	HDRMSG1
	CALLS	#1,G^LIB$PUT_OUTPUT

GET_TEST:

; Print the prompt that requests which monitor request the user wants.  This
; is also the top of the loop that allows the user to make multiple monitor
; requests.

	BSBW	BLANK
	PUSHAB	INPMSG1
	CALLS	#1,G^LIB$PUT_OUTPUT
	PUSHAB	INPMSG2
	CALLS	#1,G^LIB$PUT_OUTPUT
	PUSHAB	INPMSG3
	CALLS	#1,G^LIB$PUT_OUTPUT
	PUSHAB	INPMSG4
	CALLS	#1,G^LIB$PUT_OUTPUT
	PUSHAB	INPMSG5
	CALLS	#1,G^LIB$PUT_OUTPUT
	BSBW	BLANK

; Read the user's input to our prompt for the number of the test to run.

	MOVL	#2,INPSTRDSC		; Set number of bytes to read
	PUSHAB	INPSIZ			; Push input size parameter
	PUSHAB	RPRMT			; Push prompt string parameter
	PUSHAB	INPSTRDSC		; Push String descriptor parameter
	CALLS	#3,G^LIB$GET_INPUT	; Read the user's input

; Convert the input to a test number.

	CLRL	R1			; Start with zero in test number
	CLRL	R2			; Clear # of input characters done
10$:	CMPW	R2,INPSIZ		; Have we processed all the input?
	BGEQU	20$			; If EQL, yes
	MOVZBL	INPSTR(R2),R0		; Get an input character
	SUBL2	#^A/0/,R0		; Convert to a digit
	MULL2	#^D10,R1		; Shift current decimal digits
	ADDL2	R0,R1			; Add new digit
	INCL	R2			; Bump # of input characters done
	BRB	10$			; Check next character

; Clear the per-test global variables.

20$:	CLRL	DECCNT
	CLRL	OTHCNT
	CLRB	HOURS
	CLRB	MINUTES
	CLRB	SECONDS

; Now perform the appropriate test based on the test number.

	CMPL	R1,#1			; Check if test #1
	BNEQ	30$			; If NEQ, no
	BSBW	TEST_1			; Do test
	BRW	GET_TEST		; Check for another test to run
30$:	CMPL	R1,#2			; Check if test #2
	BNEQ	40$			; If NEQ, no
	BSBW	TEST_2			; Do test
	BRW	GET_TEST		; Check for another test to run
40$:	CMPL	R1,#3			; Check if test #3
	BNEQ	50$			; If NEQ, no
	BSBW	TEST_3			; Do test
	BRW	GET_TEST		; Check for another test to run

; Not a supported test, so exit.

50$:	BRW	EXIT			; Not any of above, so exit


; Test #1
;
; This test receives all LAT packets coming into the node where the program
; is run for a length of time requested by the user. The packets are
; examined, and if the address is the first occurance thereof, the packet
; is stored.  After the set time is over, all packets stored (along with
; other information) are displayed.

TEST_1:

	BSBW	GET_TIME
	BSBW	SET_TIME

RCV_T1:

	BSBW	RCV_LAT			; Get a message
	BLBS	R0,10$			; If we have a message, process it
	BRW	PRINT_T1		; Else time ran out, so print results

; Check/store the message based on whether it's a DECnet address or not.

10$:	MOVZBL	RCVBUF+12,R1		; First store the group codes
	MOVC5	R1,RCVBUF+13,#0,-
		#^X20,GCBITS
	CMPL	RCVSA,#^X000400AA	; Is this a DECnet address?
	BNEQ	OTHER_T1		; If NEQ, no, goto OTHER
	BRW	DECNET_T1		; Else goto DECNET

; Process packets from non-DECnet addresses.

OTHER_T1:

; Check to see if the source address is already in the OTHER table.  If so,
; throw it away and read another one.

	CLRL	R1			; Start counter at zero
10$:	CMPL	R1,OTHCNT		; Have we looked at all entries?
	BEQL	40$			; If EQL, yes, so add to table
	MULL3	#^D38,R1,R2		; Calc entry to check
	CMPL	RCVSA,OTHTBL(R2)	; Source address match?
	BNEQ	20$			; If NEQ, no, so check next entry
	CMPW	RCVSA+4,OTHTBL+4(R2)	; Source address match?
	BEQL	30$			; If EQL, yes, so ignore message
20$:	INCL	R1			; Increment counter
	BRB	10$			; Check next entry in table
30$:	BRW	RCV_T1			; Get another message

; The address was not in the table, so add it if there's room.

40$:	CMPL	OTHCNT,#^D100		; Is there room?
	BEQL	50$			; If EQL, no, so ignore message
	MULL3	#^D38,OTHCNT,R2		; Calc where to store entry
	MOVL	RCVSA,OTHTBL(R2)	; Store SA
	MOVW	RCVSA+4,OTHTBL+4(R2)	; Store SA
	MOVO	GCBITS+00,OTHTBL+06(R2)	; Store group code mask
	MOVO	GCBITS+08,OTHTBL+14(R2)	; Store group code mask
	MOVO	GCBITS+16,OTHTBL+22(R2)	; Store group code mask
	MOVO	GCBITS+24,OTHTBL+30(R2)	; Store group code mask
	INCL	OTHCNT			; Bump number of used entries
50$:	BRW	RCV_T1

; Process packets from DECnet addresses.

DECNET_T1:

; Check to see if the source address is already in the DECNET table.  If so,
; throw it away and read another one.

	CLRL	R1			; Start counter at zero
10$:	CMPL	R1,DECCNT		; Have we looked at all entries?
	BEQL	40$			; If EQL, yes, so add to table
	MULL3	#^D34,R1,R2		; Calc entry to check
	CMPL	RCVSA,DECTBL(R2)	; Source address match?
	BNEQ	20$			; If NEQ, no, so check next entry
	CMPW	RCVSA+4,DECTBL+4(R2)	; Source address match?
	BEQL	30$			; If EQL, yes, so ignore message
20$:	INCL	R1			; Increment counter
	BRB	10$			; Check next entry in table
30$:	BRW	RCV_T1			; Get another message

; The address was not in the table, so add it if there's room.

40$:	CMPL	DECCNT,#^D1000		; Is there room?
	BEQL	50$			; If EQL, no, so ignore message
	MULL3	#^D34,DECCNT,R2		; Calc where to store entry
	MOVW	RCVSA+4,DECTBL(R2)	; Store SA
	MOVO	GCBITS+00,DECTBL+02(R2)	; Store group code mask
	MOVO	GCBITS+08,DECTBL+10(R2)	; Store group code mask
	MOVO	GCBITS+16,DECTBL+18(R2)	; Store group code mask
	MOVO	GCBITS+24,DECTBL+26(R2)	; Store group code mask
	INCL	DECCNT			; Bump number of used entries
50$:	BRW	RCV_T1

PRINT_T1:

; If we have no data, then print appropriate message.

	TSTL	DECCNT			; Anything in DECNET table?
	BNEQ	PRINT_DEC_T1		; If NEQ, yes, so print them
	TSTL	OTHCNT			; Anything in OTHER table?
	BNEQ	PRINT_DEC_T1		; If NEQ, yes, so print them

; Print "no messages found" message.

	BSBW	BLANK
	PUSHAB	M1NONE
	CALLS	#1,G^LIB$PUT_OUTPUT
	RSB

PRINT_DEC_T1:

; First print out all nodes in the DECnet table

	DECL	DECCNT
10$:	MULL3	#^D34,DECCNT,R2
	TSTL	DECCNT
	BGEQ	20$
	BRW	PRINT_OTH_T1
20$:	DECL	DECCNT
	BSBW	BLANK
	MOVL	#^X000400AA,SIDADDR
	MOVW	DECTBL(R2),SIDADDR+4
	BSBW	GET_DEVID
	MOVW	DECTBL(R2),R0
	BICW	#^XFC00,R0
	MOVZBL	DECTBL+1(R2),R1
	ASHL	#-2,R1,R3
	MOVL	#80,FAOLEN
	$FAO_S	CTRSTR=M1ALL1,-
		OUTLEN=FAOLEN,-
		OUTBUF=FAODESC,-
		P1=DECTBL(R2),-
		P2=DECTBL+1(R2),-
		P3=DEVID,-
		P4=#DEVDSC,-
		P5=R3,-
		P6=R0
	PUSHAB	FAODESC
	CALLS	#1,G^LIB$PUT_OUTPUT
	PUSHAB	M1MASK
	CALLS	#1,G^LIB$PUT_OUTPUT
	MOVL	#80,FAOLEN
	$FAO_S	CTRSTR=M1BIT1,-
		OUTLEN=FAOLEN,-
		OUTBUF=FAODESC,-
		P1=DECTBL+9(R2),-
		P2=DECTBL+8(R2),-
		P3=DECTBL+7(R2),-
		P4=DECTBL+6(R2),-
		P5=DECTBL+5(R2),-
		P6=DECTBL+4(R2),-
		P7=DECTBL+3(R2),-
		P8=DECTBL+2(R2)
	PUSHAB	FAODESC
	CALLS	#1,G^LIB$PUT_OUTPUT
	MOVL	#80,FAOLEN
	$FAO_S	CTRSTR=M1BIT2,-
		OUTLEN=FAOLEN,-
		OUTBUF=FAODESC,-
		P1=DECTBL+17(R2),-
		P2=DECTBL+16(R2),-
		P3=DECTBL+15(R2),-
		P4=DECTBL+14(R2),-
		P5=DECTBL+13(R2),-
		P6=DECTBL+12(R2),-
		P7=DECTBL+11(R2),-
		P8=DECTBL+10(R2)
	PUSHAB	FAODESC
	CALLS	#1,G^LIB$PUT_OUTPUT
	MOVL	#80,FAOLEN
	$FAO_S	CTRSTR=M1BIT3,-
		OUTLEN=FAOLEN,-
		OUTBUF=FAODESC,-
		P1=DECTBL+25(R2),-
		P2=DECTBL+24(R2),-
		P3=DECTBL+23(R2),-
		P4=DECTBL+22(R2),-
		P5=DECTBL+21(R2),-
		P6=DECTBL+20(R2),-
		P7=DECTBL+19(R2),-
		P8=DECTBL+18(R2)
	PUSHAB	FAODESC
	CALLS	#1,G^LIB$PUT_OUTPUT
	MOVL	#80,FAOLEN
	$FAO_S	CTRSTR=M1BIT4,-
		OUTLEN=FAOLEN,-
		OUTBUF=FAODESC,-
		P1=DECTBL+33(R2),-
		P2=DECTBL+32(R2),-
		P3=DECTBL+31(R2),-
		P4=DECTBL+30(R2),-
		P5=DECTBL+29(R2),-
		P6=DECTBL+28(R2),-
		P7=DECTBL+27(R2),-
		P8=DECTBL+26(R2)
	PUSHAB	FAODESC
	CALLS	#1,G^LIB$PUT_OUTPUT
	BRW	10$

; Now print out the data for any non-DECnet nodes

PRINT_OTH_T1:

	DECL	OTHCNT
10$:	MULL3	#^D38,OTHCNT,R2
	TSTL	OTHCNT
	BGEQ	20$
	RSB
20$:	DECL	OTHCNT
	BSBW	BLANK
	MOVL	OTHTBL(R2),SIDADDR
	MOVW	OTHTBL+4(R2),SIDADDR+4
	BSBW	GET_DEVID
	MOVL	#80,FAOLEN
	$FAO_S	CTRSTR=M1ALL2,-
		OUTLEN=FAOLEN,-
		OUTBUF=FAODESC,-
		P1=OTHTBL(R2),-
		P2=OTHTBL+1(R2),-
		P3=OTHTBL+2(R2),-
		P4=OTHTBL+3(R2),-
		P5=OTHTBL+4(R2),-
		P6=OTHTBL+5(R2),-
		P7=DEVID,-
		P8=#DEVDSC
	PUSHAB	FAODESC
	CALLS	#1,G^LIB$PUT_OUTPUT
	PUSHAB	M1MASK
	CALLS	#1,G^LIB$PUT_OUTPUT
	MOVL	#80,FAOLEN
	$FAO_S	CTRSTR=M1BIT1,-
		OUTLEN=FAOLEN,-
		OUTBUF=FAODESC,-
		P1=OTHTBL+13(R2),-
		P2=OTHTBL+12(R2),-
		P3=OTHTBL+11(R2),-
		P4=OTHTBL+10(R2),-
		P5=OTHTBL+9(R2),-
		P6=OTHTBL+8(R2),-
		P7=OTHTBL+7(R2),-
		P8=OTHTBL+6(R2)
	PUSHAB	FAODESC
	CALLS	#1,G^LIB$PUT_OUTPUT
	MOVL	#80,FAOLEN
	$FAO_S	CTRSTR=M1BIT2,-
		OUTLEN=FAOLEN,-
		OUTBUF=FAODESC,-
		P1=OTHTBL+21(R2),-
		P2=OTHTBL+20(R2),-
		P3=OTHTBL+19(R2),-
		P4=OTHTBL+18(R2),-
		P5=OTHTBL+17(R2),-
		P6=OTHTBL+16(R2),-
		P7=OTHTBL+15(R2),-
		P8=OTHTBL+14(R2)
	PUSHAB	FAODESC
	CALLS	#1,G^LIB$PUT_OUTPUT
	MOVL	#80,FAOLEN
	$FAO_S	CTRSTR=M1BIT3,-
		OUTLEN=FAOLEN,-
		OUTBUF=FAODESC,-
		P1=OTHTBL+29(R2),-
		P2=OTHTBL+28(R2),-
		P3=OTHTBL+27(R2),-
		P4=OTHTBL+26(R2),-
		P5=OTHTBL+25(R2),-
		P6=OTHTBL+24(R2),-
		P7=OTHTBL+23(R2),-
		P8=OTHTBL+22(R2)
	PUSHAB	FAODESC
	CALLS	#1,G^LIB$PUT_OUTPUT
	MOVL	#80,FAOLEN
	$FAO_S	CTRSTR=M1BIT4,-
		OUTLEN=FAOLEN,-
		OUTBUF=FAODESC,-
		P1=OTHTBL+37(R2),-
		P2=OTHTBL+36(R2),-
		P3=OTHTBL+35(R2),-
		P4=OTHTBL+34(R2),-
		P5=OTHTBL+33(R2),-
		P6=OTHTBL+32(R2),-
		P7=OTHTBL+31(R2),-
		P8=OTHTBL+30(R2)
	PUSHAB	FAODESC
	CALLS	#1,G^LIB$PUT_OUTPUT
	BRW	10$


; Test #2
;
; This test asks the user for both a length of time to run the test for and
; a group code to watch for. Once the end time has been reached, the addresses
; of all nodes received with the specific group code enabled are printed.

TEST_2:

	BSBW	GET_TIME
	BSBW	BLANK
	BSBW	GET_GCODE
	BSBW	SET_TIME

RCV_T2:

	BSBW	RCV_LAT			; Get a message
	BLBS	R0,10$			; If we have a message, process it
	BRW	PRINT_T2		; Else time ran out, so print results

; First check if the appropriate group code is set in this message.

10$:	MOVZBL	RCVBUF+12,R1		; Get group code length
	MOVC5	R1,RCVBUF+13,#0,-	; Copy (with zero extend) into
		#^X20,GCBITS		; a fixed place
	MOVZBL	GCODE,R1		; Get requested group code
	CLRL	R0			; To calc group code bit number,
	CLRL	R2			; start with 0 in R0 and R2
	EDIV	#8,R1,R0,R2		; R0=byte to check in the bit mask
					; R2=bit to check in that byte
	BBS	R2,GCBITS(R0),20$	; If group code enabled, continue
	BRW	RCV_T2			; Else, ignore message

; Store the message based on whether it's a DECnet address or not.

20$:	CMPL	RCVSA,#^X000400AA	; Is this a DECnet address?
	BNEQ	OTHER_T2		; If NEQ, no, goto OTHER
	BRW	DECNET_T2		; Else goto DECNET

; Process packets from non-DECnet addresses.

OTHER_T2:

; Check to see if the source address is already in the OTHER table.  If so,
; throw it away and read another one.

	CLRL	R1			; Start counter at zero
10$:	CMPL	R1,OTHCNT		; Have we looked at all entries?
	BEQL	40$			; If EQL, yes, so add to table
	MULL3	#6,R1,R2		; Calc entry to check
	CMPL	RCVSA,OTHTBL(R2)	; Source address match?
	BNEQ	20$			; If NEQ, no, so check next entry
	CMPW	RCVSA+4,OTHTBL+4(R2)	; Source address match?
	BEQL	30$			; If EQL, yes, so ignore message
20$:	INCL	R1			; Increment counter
	BRB	10$			; Check next entry in table
30$:	BRW	RCV_T2			; Get another message

; The address was not in the table, so add it if there's room.

40$:	CMPL	OTHCNT,#^D633		; Is there room?
	BEQL	50$			; If EQL, no, so ignore message
	MULL3	#6,OTHCNT,R2		; Calc where to store entry
	MOVL	RCVSA,OTHTBL(R2)	; Store SA
	MOVW	RCVSA+4,OTHTBL+4(R2)	; Store SA
	INCL	OTHCNT			; Bump number of used entries
50$:	BRW	RCV_T2

; Process packets from DECnet addresses.

DECNET_T2:

; Check to see if the source address is already in the DECNET table.  If so,
; throw it away and read another one.

	CLRL	R1			; Start counter at zero
10$:	CMPL	R1,DECCNT		; Have we looked at all entries?
	BEQL	40$			; If EQL, yes, so add to table
	MULL3	#2,R1,R2		; Calc entry to check
	CMPL	RCVSA,DECTBL(R2)	; Source address match?
	BNEQ	20$			; If NEQ, no, so check next entry
	CMPW	RCVSA+4,DECTBL+4(R2)	; Source address match?
	BEQL	30$			; If EQL, yes, so ignore message
20$:	INCL	R1			; Increment counter
	BRB	10$			; Check next entry in table
30$:	BRW	RCV_T2			; Get another message

; The address was not in the table, so add it if there's room.

40$:	CMPL	DECCNT,#^D17000		; Is there room?
	BEQL	50$			; If EQL, no, so ignore message
	MULL3	#2,DECCNT,R2		; Calc where to store entry
	MOVW	RCVSA+4,DECTBL(R2)	; Store SA
	INCL	DECCNT			; Bump number of used entries
50$:	BRW	RCV_T2

PRINT_T2:

; If we have no data, then print appropriate message.

	TSTL	DECCNT			; Anything in DECNET table?
	BNEQ	10$			; If NEQ, yes, so print them
	TSTL	OTHCNT			; Anything in OTHER table?
	BNEQ	10$			; If NEQ, yes, so print them

; Print "no messages found" message.

	BSBW	BLANK
	MOVL	#80,FAOLEN
	$FAO_S	CTRSTR=M2NONE,-
		OUTLEN=FAOLEN,-
		OUTBUF=FAODESC,-
		P1=GCODE
	PUSHAB	FAODESC
	CALLS	#1,G^LIB$PUT_OUTPUT
	RSB

; Some nodes were found, print the header.

10$:	BSBW	BLANK
	MOVL	#80,FAOLEN
	$FAO_S	CTRSTR=M2HDR0,-
		OUTLEN=FAOLEN,-
		OUTBUF=FAODESC,-
		P1=GCODE
	PUSHAB	FAODESC
	CALLS	#1,G^LIB$PUT_OUTPUT

PRINT_DEC_T2:

	MOVZWL	DECCNT,R10		; Save original node count
10$:	TSTL	DECCNT
	BGTR	20$
	BRW	PRINT_OTH_T2

; Find the smallest node.  We will print this list in order.

20$:	MOVAL	DECTBL,R2		; Assume the first entry is smallest
	MOVAL	DECTBL+2,R9		; This is the first place to compare
	SUBL3	#1,R10,R8		; Number of slots to compare
30$:	TSTL	R8			; Any left to compare?
	BEQL	70$			; Br if not
	TSTW	(R9)			; Is there an address here?
	BEQL	60$			; Br if not
	TSTW	(R2)			; Do we have an address yet?
	BEQL	50$			; Br if not
	CMPW	(R9),(R2)		; Is the new one smaller?
	BGEQU	60$			; Br if not, else ...
50$:	MOVL	R9,R2			; Copy current entry into R2 and
					; continue the search
60$:	ADDL	#2,R9			; Skip to next address
	DECL	R8			; One less address to compare
	BRB	30$			; Check next entry

70$:	MOVL	#^X000400AA,SIDADDR
	MOVW	(R2),SIDADDR+4
	BSBW	GET_DEVID
	MOVW	(R2),R0
	BICW	#^XFC00,R0
	MOVZBL	1(R2),R1
	ASHL	#-2,R1,R3
	MOVL	#80,FAOLEN
	$FAO_S	CTRSTR=M2ALL1,-
		OUTLEN=FAOLEN,-
		OUTBUF=FAODESC,-
		P1=(R2),-
		P2=1(R2),-
		P3=DEVID,-
		P4=#DEVDSC,-
		P5=R3,-
		P6=R0
	PUSHAB	FAODESC
	CALLS	#1,G^LIB$PUT_OUTPUT
	DECL	DECCNT
	CLRW	(R2)
	BRW	10$

; Now print out the data for any other nodes found.

PRINT_OTH_T2:

	DECL	OTHCNT
10$:	MULL3	#6,OTHCNT,R2
	TSTL	OTHCNT
	BGEQ	20$
	RSB
20$:	DECL	OTHCNT
	MOVL	OTHTBL(R2),SIDADDR
	MOVW	OTHTBL+4(R2),SIDADDR+4
	BSBW	GET_DEVID
	MOVL	#80,FAOLEN
	$FAO_S	CTRSTR=M2ALL2,-
		OUTLEN=FAOLEN,-
		OUTBUF=FAODESC,-
		P1=OTHTBL(R2),-
		P2=OTHTBL+1(R2),-
		P3=OTHTBL+2(R2),-
		P4=OTHTBL+3(R2),-
		P5=OTHTBL+4(R2),-
		P6=OTHTBL+5(R2),-
		P7=DEVID,-
		P8=#DEVDSC
	PUSHAB	FAODESC
	CALLS	#1,G^LIB$PUT_OUTPUT
	BRB	10$


; Test #3
;
; This test asks the user for one address, and waits for a message from that
; address. Once a message from that address is found, a listing of all group
; codes enabled is given.

TEST_3:

; Start the test; continue until the end time is reached.

	BSBW	GET_TIME
	BSBW	BLANK
	BSBW	GET_ADDR
	BSBW	SET_TIME

RCV_T3:

	BSBW	RCV_LAT			; Get a message
	BLBS	R0,10$			; If we have a message, process it

; Print "no messages found" message.

	BSBW	BLANK
	MOVL	#80,FAOLEN
	$FAO_S	CTRSTR=M3NONE,-
		OUTLEN=FAOLEN,-
		OUTBUF=FAODESC,-
		P1=DSTADR,-
		P2=DSTADR+1,-
		P3=DSTADR+2,-
		P4=DSTADR+3,-
		P5=DSTADR+4,-
		P6=DSTADR+5
	PUSHAB	FAODESC
	CALLS	#1,G^LIB$PUT_OUTPUT
	RSB

; Check if the source address is the desired one.

10$:	MOVZBL	RCVBUF+12,R1
	MOVC5	R1,RCVBUF+13,#0,#^X20,GCBITS
	CMPC3	#6,RCVSA,DSTADR
	BNEQ	RCV_T3

; The address was the desired one, so print the results

PRINT_T3:

; Print the address.

	BSBW	BLANK
	MOVL	#80,FAOLEN
	$FAO_S	CTRSTR=M3ADDR,-
		OUTLEN=FAOLEN,-
		OUTBUF=FAODESC,-
		P1=RCVSA,-
		P2=RCVSA+1,-
		P3=RCVSA+2,-
		P4=RCVSA+3,-
		P5=RCVSA+4,-
		P6=RCVSA+5
	PUSHAB	FAODESC
	CALLS	#1,G^LIB$PUT_OUTPUT

; Print the DECnet node if this is a DECnet address

	CMPL	#^X000400AA,RCVSA	; Is this a DECnet address?
	BNEQ	1$			; If NEQ, no
	MOVW	RCVSA+4,R0
	BICW	#^XFC00,R0
	MOVZBL	RCVSA+5,R3
	ASHL	#-2,R3,R3
	MOVL	#80,FAOLEN
	$FAO_S	CTRSTR=M3NODE,-
		OUTLEN=FAOLEN,-
		OUTBUF=FAODESC,-
		P1=R3,-
		P2=R0
	PUSHAB	FAODESC
	CALLS	#1,G^LIB$PUT_OUTPUT


; Print the device ID and device name.

1$:	MOVL	DSTADR,SIDADDR
	MOVW	DSTADR+4,SIDADDR+4
	BSBW	GET_DEVID
	MOVL	#80,FAOLEN
	$FAO_S	CTRSTR=M3CONT,-
		OUTLEN=FAOLEN,-
		OUTBUF=FAODESC,-
		P1=DEVID,-
		P2=#DEVDSC
	PUSHAB	FAODESC	
	CALLS	#1,G^LIB$PUT_OUTPUT

; Print the node name if there is one.

	MOVZBL	RCVBUF+12,R1		; Get size of group code bits
	ADDL3	#^D13,R1,R4		; Offset to node name size
	MOVAL	RCVBUF+1,R5		; Calc address of string
	ADDL	R4,R5			; Address of string
	MOVZBL	RCVBUF(R4),R3		; Get size
	BEQL	10$			; If EQL, none to print
	MOVL	#80,FAOLEN
	$FAO_S	CTRSTR=M3IDN1,-
		OUTLEN=FAOLEN,-
		OUTBUF=FAODESC,-
		P1=R3,-
		P2=R5
	PUSHAB	FAODESC	
	CALLS	#1,G^LIB$PUT_OUTPUT

; Print the node descriptor if there is one.

10$:	ADDL	R3,R5			; Address of size
	MOVZBL	(R5)+,R3		; Get size
	BEQL	20$			; If EQL, none to print
	MOVL	#80,FAOLEN
	$FAO_S	CTRSTR=M3IDN2,-
		OUTLEN=FAOLEN,-
		OUTBUF=FAODESC,-
		P1=R3,-
		P2=R5
	PUSHAB	FAODESC	
	CALLS	#1,G^LIB$PUT_OUTPUT

; Print each service set.

20$:	ADDL	R3,R5			; Address of number of sets
	MOVZBL	(R5)+,R6		; Get number of sets
	BEQL	60$			; Branch if none
	CLRL	R3			; Nothing to skip right now

; Print the service name if there is one.

30$:	INCL	R5			; Skip service rating
	ADDL	R3,R5			; Address of size
	MOVZBL	(R5)+,R3		; Get size
	BEQL	40$			; If EQL, none to print
	MOVL	#80,FAOLEN
	$FAO_S	CTRSTR=M3IDN3,-
		OUTLEN=FAOLEN,-
		OUTBUF=FAODESC,-
		P1=R3,-
		P2=R5
	PUSHAB	FAODESC	
	CALLS	#1,G^LIB$PUT_OUTPUT

; Print the service descriptor if there is one.

40$:	ADDL	R3,R5			; Address of size
	MOVZBL	(R5)+,R3		; Get size
	BEQL	50$			; If EQL, none to print
	MOVL	#80,FAOLEN
	$FAO_S	CTRSTR=M3IDN4,-
		OUTLEN=FAOLEN,-
		OUTBUF=FAODESC,-
		P1=R3,-
		P2=R5
	PUSHAB	FAODESC	
	CALLS	#1,G^LIB$PUT_OUTPUT

; If more service sets, print them.

50$:
	SOBGTR	R6,30$

; Print the group code header.

60$:	PUSHAB	M3HDR1
	CALLS	#1,G^LIB$PUT_OUTPUT

; Print all the enabled group code's.

	CLRL	R1
70$:	CLRL	R0
	CLRL	R2
	EDIV	#8,R1,R0,R2
	BBC	R2,GCBITS(R0),80$
	PUSHL	R1
	MOVL	#80,FAOLEN
	$FAO_S	CTRSTR=M3GCOD,-
		OUTLEN=FAOLEN,-
		OUTBUF=FAODESC,-
		P1=R1
	PUSHAB	FAODESC
	CALLS	#1,G^LIB$PUT_OUTPUT
	POPL	R1
80$:	INCL	R1
	CMPL	R1,#^D256
	BLSS	70$
	RSB


RCV_LAT:

; Read a LAT multicast service announcement frame.  Return if time has run
; out (R0=0) or if a frame was received successfully (R0=1).

; First check if time has run out.

	$GETTIM_S-			; Get current time
		TIMADR=TIME
	CMPL	TIME+4,ENDTIM+4		; Did we look long enough?
	BEQL	10$			; If equal, need to check low longword
	BLSSU	30$			; If LSSU, continue
	BRB	20$			; Else end search for the node
10$:	CMPL	TIME,ENDTIM		; Check low longword
	BLSSU	30$			; If LSSU, continue

20$:	CLRL	R0			; Say that time ran out
	RSB

30$:	$QIOW_S	FUNC=#IO$_READVBLK!IO$M_NOW,-
		CHAN=CHNLAT,-
		IOSB=IOSB,-
		P1=RCVBUF,-
		P2=#RCVBUFLEN,-
		P5=#RCVP5

	BLBS	R0,RCV_REQ_OK
	BRW	ERROR

RCV_REQ_OK:
	MOVZWL	IOSB,R0
	BLBS	R0,RCV_IO_OK
	CMPW	R0,#SS$_ENDOFFILE
	BEQL	RCV_LAT
	BRW	ERROR

RCV_IO_OK:

; Check if this is a multicast message.  If not, throw it away and look for
; another message.

	BLBC	RCVDA,RCV_LAT		; Ignore packet if sent to physical

; Make sure this is a service announcement message.  If not, throw it away
; and look for another message.

	CMPB	#^X28,RCVBUF		; Is this a service announcement?
	BNEQ	RCV_LAT			; If NEQ, no, try again

; The message is good, return success

	MOVZBL	#1,R0
	RSB


GET_DEVID:

; Get the node's device ID and device name.  This is done by requesting a
; SYSID message from the node.  The response (if any) contains the device
; ID.  From the device ID, we will select the correct device name.  Upon
; completion of this routine, DEVID (a byte) will have the device ID and
; DEVDSC will point to the device name.

; Transmit the request ID message to the node we want to find out about.

	$QIOW_S	FUNC=#IO$_WRITEVBLK,-
		CHAN=CHNRMC,-
		IOSB=IOSB,-
		P1=XMTP2BUF,-
		P2=#XMTP2LEN,-
		P5=#SIDADDR

	BLBS	R0,DXMT_REQ_OK
	BRW	ERROR

DXMT_REQ_OK:

	MOVL	IOSB,R0
	BLBS	R0,DXMT_IO_OK
	BRW	ERROR

DXMT_IO_OK:

; Receive the SYSID response.  If there is no data ready to receive, then the
; Ethernet driver will return immediately with SS$_ENDOFFILE.

	CLRL	RCVTRY			; Clear the number of read attempts

DRCV:

	$QIOW_S	FUNC=#IO$_READVBLK!IO$M_NOW,-
		CHAN=CHNRMC,-
		IOSB=IOSB,-
		P1=RCVBUF2,-
		P2=#RCVBUFLEN2,-
		P5=#RCVP52

	BLBS	R0,DRCV_REQ_OK
	BRW	ERROR

DRCV_REQ_OK:

	MOVL	IOSB,R0
	BLBS	R0,DRCV_IO_OK
	CMPW	IOSB,#SS$_ENDOFFILE
	BEQL	DRCV_TRY
	BRW	ERROR

DRCV_TRY:
	CMPL	RCVTRY,#4000		; Have we tried enough times?
	BGTR	20$			; If GTR, yes, so message is lost
	INCL	RCVTRY			; Count this attempt
	BRB	DRCV			; Try again
20$:	CLRB	DEVID			; Clear the Device ID number
	MOVQ	DEVNR,DEVDSC		; Set the device name to no response
	RSB				; Return to caller

DRCV_IO_OK:

; Check that this is a SYSTEM ID message from the correct node.

	CMPB	RCVBUF2,#7		; Is this a SYSTEM ID message?
	BNEQ	DRCV_TRY		; If NEQ, no, try again
	CMPL	RCVSA2,SIDADDR		; Correct node?
	BNEQ	DRCV_TRY		; If NEQ, no
	CMPW	RCVSA2+4,SIDADDR+4	; Correct node?
	BNEQ	DRCV_TRY		; If NEQ, no

; We received a message from the remote node.  Look for the Device ID entry
; in this SYSID message.

	MOVL	#4,R3			; Skip over SYSID header

; Loop through the entries in the SYSID looking for the Device ID entry.

10$:	CMPW	R3,IOSB+2		; Any buffer left to look at?
	BLSSU	20$			; If LSSU, yes, so look for more
	BRW	130$			; Else report Device ID missing
20$:	CMPW	RCVBUF2(R3),#^D100	; Is this the device ID entry?
	BEQL	30$			; If EQL, yes
	MOVZBL	RCVBUF2+2(R3),R1	; Get size of this entry
	ADDL	#3,R3			; Skip over entry type and size
	ADDL	R1,R3			; Skip over entry value
	BRW	10$			; Check next entry

; The Device ID was found.  Store the Device ID and select a device name.

30$:	ADDL	#3,R3			; Skip over Device ID header
	MOVB	RCVBUF2(R3),DEVID	; Store the Device ID
	CMPB	DEVID,#^D01		; Is this Device 1?
	BNEQ	40$			; Branch if not
	MOVQ	DEV01,DEVDSC		; Store Device Name for ID 1
	BRW	140$			; Exit
40$:	CMPB	DEVID,#^D05
	BNEQ	50$
	MOVQ	DEV05,DEVDSC
	BRW	140$
50$:	CMPB	DEVID,#^D11
	BNEQ	60$
	MOVQ	DEV11,DEVDSC
	BRW	140$
60$:	CMPB	DEVID,#^D17
	BNEQ	70$
	MOVQ	DEV17,DEVDSC
	BRB	140$
70$:	CMPB	DEVID,#^D23
	BNEQ	80$
	MOVQ	DEV23,DEVDSC
	BRB	140$
80$:	CMPB	DEVID,#^D39
	BNEQ	90$
	MOVQ	DEV39,DEVDSC
	BRB	140$
90$:	CMPB	DEVID,#^D33
	BNEQ	100$
	MOVQ	DEV33,DEVDSC
	BRB	140$
100$:	CMPB	DEVID,#^D35
	BNEQ	110$
	MOVQ	DEV35,DEVDSC
	BRB	140$
110$:	CMPB	DEVID,#^D37
	BNEQ	115$
	MOVQ	DEV37,DEVDSC
	BRB	140$
115$:	CMPB	DEVID,#^D65
	BNEQ	120$
	MOVQ	DEV65,DEVDSC
	BRB	140$
120$:	MOVQ	DEVUK,DEVDSC		; This is an unknown device
	BRB	140$
130$:	CLRB	DEVID			; No Device ID was found
	MOVQ	DEVMS,DEVDSC		; Device ID is missing
140$:	RSB


GET_GCODE:

; Get group code to watch for

	MOVL	#18,INPSTRDSC
	PUSHAB	INPSIZ
	PUSHAB	GPRMT
	PUSHAB	INPSTRDSC
	CALLS	#3,G^LIB$GET_INPUT

; Convert group code

	CLRL	R1
	CLRW	R2
10$:	MOVZBL	INPSTR(R2),R0
	SUBL2	#^A/0/,R0
	MULL2	#^D10,R1
	ADDL2	R0,R1
	INCW	R2
	CMPW	R2,INPSIZ
	BLSS	10$
	MOVB	R1,GCODE
	RSB


GET_ADDR:

; Get the address to look for from the user.

	MOVL	#18,INPSTRDSC		; Maximum input size is 18 bytes
	PUSHAB	INPSIZ			; Place to store # of bytes entered
	PUSHAB	APRMT			; Prompt to display
	PUSHAB	INPSTRDSC		; Place to store input string
	CALLS	#3,G^LIB$GET_INPUT

	CLRL	R1			; Index to next input character
	CLRL	R3			; Index to next address byte

; Get the first digit of the next address byte.

10$:	MOVZBL	INPSTR(R1),R0		; Get next input character
	INCL	R1			; Bump input character index
	CMPB	R0,#^A/A/		; Is this a letter or greater?
	BGEQU	20$			; If GEQU, yes, so branch
	SUBL3	#^A/0/,R0,R2		; Convert digit character to digit
	BRB	30$			; Get the next input character
20$:	SUBL2	#^A/A/,R0		; Convert letter character to digit
	ADDL3	#^D10,R0,R2		; Add ten to have correct digit

; Get the second digit of the next address byte.

30$:	MULL2	#^D16,R2		; Put the first digit into its place
	MOVZBL	INPSTR(R1),R0		; Get next input character
	INCL	R1			; Bump input character index
	CMPB	R0,#^A/A/		; Is this a letter or greater?
	BGEQU	40$			; If GEQU, yes, so branch
	SUBL2	#^A/0/,R0		; Convert digit character to digit
	BRB	50$			; Go to add the second digit
40$:	SUBL2	#^A/A/,R0		; Convert letter character to digit
	ADDL2	#^D10,R0		; Add ten to have correct digit
50$:	ADDL	R0,R2			; Add lower digit to address

; Store this address byte and see if we need to do more.

	MOVB	R2,DSTADR(R3)		; Store this address byte
	INCL	R3			; Bump to next address byte
	CMPL	R3,#^D06		; Have we processed 6 bytes?
	BLSS	60$			; Branch if not, more to do
	RSB				; Else return to caller
60$:	INCL	R1			; Skip over "-" in input string
	BRW	10$			; Do next address byte


GET_TIME:

; Get number of hours to run test for

GET_HOUR:
	BSBW	BLANK
	MOVL	#6,INPSTRDSC
	PUSHAB	INPSIZ
	PUSHAB	HPRMT
	PUSHAB	INPSTRDSC
	CALLS	#3,G^LIB$GET_INPUT

; Convert number of hours

	CLRL	R1
	CLRW	R2
NUM_LOOP2:
	MOVZBL	INPSTR(R2),R0
	SUBL2	#^A/0/,R0
	MULL2	#^D10,R1
	ADDL2	R0,R1
	INCW	R2
	CMPW	R2,INPSIZ
	BLSS	NUM_LOOP2
	CMPL	R1,#^D23
	BGTR	GET_HOUR
	MOVL	R1,HOURS

; Get number of minutes to run test for

GET_MINUTE:
	MOVL	#6,INPSTRDSC
	PUSHAB	INPSIZ
	PUSHAB	MPRMT
	PUSHAB	INPSTRDSC
	CALLS	#3,G^LIB$GET_INPUT

; Convert number of minutes

	CLRL	R1
	CLRW	R2
NUM_LOOP3:
	MOVZBL	INPSTR(R2),R0
	SUBL2	#^A/0/,R0
	MULL2	#^D10,R1
	ADDL2	R0,R1
	INCW	R2
	CMPW	R2,INPSIZ
	BLSS	NUM_LOOP3
	CMPL	R1,#^D59
	BGTR	GET_MINUTE
	MOVL	R1,MINUTES

; Get number of seconds to run test for

GET_SECOND:
	MOVL	#6,INPSTRDSC
	PUSHAB	INPSIZ
	PUSHAB	SPRMT
	PUSHAB	INPSTRDSC
	CALLS	#3,G^LIB$GET_INPUT

; Convert number of seconds

	CLRL	R1
	CLRW	R2
NUM_LOOP4:
	MOVZBL	INPSTR(R2),R0
	SUBL2	#^A/0/,R0
	MULL2	#^D10,R1
	ADDL2	R0,R1
	INCW	R2
	CMPW	R2,INPSIZ
	BLSS	NUM_LOOP4
	CMPL	R1,#^D59
	BGTR	GET_SECOND
	MOVL	R1,SECONDS
	RSB

SET_TIME:

; Determine the time to stop the test

; Now put the total time into one string

	MOVL	#80,FAOLEN
	$FAO_S	CTRSTR=DTIME,-
		OUTLEN=FAOLEN,-
		OUTBUF=FAODESC,-
		P1=HOURS,-
		P2=MINUTES,-
		P3=SECONDS

; Change the ASCII string for the time to a quadword value.

	$BINTIM_S-
		TIMBUF=FAODESC,-
		TIMADR=TIME
	MNEGL	TIME+4,TIME+4
	MNEGL	TIME,TIME
	SBWC	#0,TIME+4

; Now get the present time and add the test time to get the end time.

	$GETTIM_S-
		TIMADR=ENDTIM
	ADDL	TIME,ENDTIM
	ADWC	TIME+4,ENDTIM+4
	RSB


EXIT:
	BSBW	BLANK
	PUSHAB	DNEMSG
	CALLS	#1,G^LIB$PUT_OUTPUT
	BSBW	BLANK

	$DASSGN_S-
		CHAN=CHNLAT
	$DASSGN_S-
		CHAN=CHNRMC

	$EXIT_S


BLANK:

; Print blank line

	PUSHAB	BLNKMSG
	CALLS	#1,G^LIB$PUT_OUTPUT
	RSB


; An error has occured, so print R0 value and 2nd longword of IOSB.

ERROR:	PUSHL	R0			; Pass R0 error code
	CALLS	#1,RBL$ERCODE		; Print error code

; Print IOSB 2nd longword value in hex

	MOVL	#80,FAOLEN
	$FAO_S	CTRSTR=IOMSG,-
		OUTLEN=FAOLEN,-
		OUTBUF=FAODESC,-
		P1=IOSB+4

	PUSHAB	FAODESC
	CALLS	#1,G^LIB$PUT_OUTPUT

	BRW	EXIT			; Now exit normally

	.PAGE

; RBL$ERCODE
;
; This subroutine accepts a VMS error code in binary
; and print the message on the terminal.
;
; INPUT:	4(AP) = Error Code
;
; OUTPUT:	all registers saved except R0 and R1
;
; CALLING SEQ:	PUSHx	error code
;		CALL_S	#1,RBL$ERCODE
;

	.ENTRY	RBL$ERCODE,^M<R3,R4>

	MOVL	4(AP),R3		; Get error code

	$GETMSG_S-			; Convert error code to message
		MSGID=R3,-
		MSGLEN=ERRMSG_LEN,-
		BUFADR=ERRMSG_BUF_DESC

	PUSHAB	ERRMSG_BUF_DESC
	CALLS	#1,G^LIB$PUT_OUTPUT

	RET				; Return to caller

; Structures used to build error message from error code

ERRMSG_BUF_DESC:
ERRMSG_LEN:
	.LONG	256
	.ADDRESS-
		ERRMSG_BUF
ERRMSG_BUF:
	.BLKB	256

; Tables for addresses already found

OTHCNT:	.LONG	0
OTHTBL:	.BLKB	100*38			; Maximum of 100 DECnet nodes

DECCNT:	.LONG	0
DECTBL:	.BLKB	1000*34			; Maximum of 1000 DECnet nodes

	.END	START
