	.title	latwatch
	.ident	"X5-005"

;+
; Version:	X5-005
;
; Facility:	Diagnostic Utilities.
;
; Abstract:	This is similar to ETHERWATCH but will only process LAT
;		protocol messages.
;
; Environment:	PHY_IO privilege is needed.
;
;	The following is the CLD used in LATWATCH:
;
; module latwatch_cld
;
; define verb latwatch
;	noparameters
;	qualifier debug
;	qualifier device,	value (required)
;	qualifier display,	default, value (type = display_options)
;	qualifier both
;	qualifier from,		default, value (default = "*")
;	qualifier to,		default, value (default = "*")
;	qualifier message_type,	default, value (list, type = message_options)
;	qualifier nonames
;	qualifier begin,	value (required, type = $datetime)
;	qualifier end,		value (required, type = $datetime)
;	qualifier count,	value (required, type = $number)
;	qualifier output,
;			value (type = $outfile, default = "LATWATCH.LOG")
;	qualifier record,
;			value (type = $outfile, default = "LATWATCH.RECORD")
;	qualifier playback,
;			value (type = $infile, default = "LATWATCH.RECORD")
;
;	disallow (end and count)
;	disallow (record and playback)
;	disallow (playback and (begin or end))
;
; define type display_options
;	keyword	all
;	keyword both
;	keyword	none
;	keyword ascii, default
;	keyword	text
;	keyword	hexadecimal
;	keyword fast
;
; define type message_options
;	keyword	all, default
;	keyword run_message
;	keyword type0
;	keyword	start_message
;	keyword type1
;	keyword stop_message
;	keyword type2
;	keyword type3
;	keyword type4
;	keyword type5
;	keyword type6
;	keyword type7
;	keyword type8
;	keyword type9
;	keyword	command_message
;	keyword type10
;	keyword type11
;	keyword	status_message
;	keyword type12
;	keyword	service_info
;	keyword type13
;	keyword solicit_info
;	keyword type14
;	keyword	response_info
;	keyword type15
;	keyword	unknown
;
; History:
;
;	02-Apr-1990, DBS; Version X1-001
; 001 -	Original version.  Based on ETHUTIL by Mark Myers of the University
;	of Melbourne.
;	02-Apr-1990, DBS; Version X1-002
; 002 -	Added code to handle /both, /from and /to.
;	03-Apr-1990, DBS; Version X1-003
; 003 -	Added code for the /unkown qualifier and changed the code that matches
;	the addresses to nodenames.
;	09-Apr-1990, DBS; Version X1-004
; 004 -	Fix to pick up the value returned in the iosb when starting up the
;	device.
;	11-Apr-1990, DBS; Version X1-005
; 005 -	Use for_signal/stop to handle errors.
;	23-May-1990, DBS; Version X1-006
; 006 -	Added code to unpack LAT messages.
;	05-Jun-1990, DBS; Version X1-007
; 007 -	Modified to always do the LAT format on LAT messages.
;	06-Jun-1990, DBS; Version X1-008
; 008 -	Added more specific lat packet processing.
;
;	07-Jun-1990, DBS; Version X2-001
; 001 -	Major rehash for lat processing.
;	Became LATWATCH about this time...
;	15-Jun-1990, DBS; Version X2-002
; 002 -	Changed the format of the ascii/hex displays.
;	15-Jun-1990, DBS; Version X2-003
; 003 -	Cleanup time...
;
;	27-Jun-1990, DBS; Version X3-001
; 001 -	Added wildcard matching on addresses and protocols.
;	09-Jul-1990, DBS; Version X3-002
; 002 -	Added a control C trap so we can exit cleanly.
;	20-Nov-1991, DBS; Version X3-003
; 003 -	Added call to sys_find_ether_device to establish default.
;	17-Jan-1992, DBS; Version X3-004
; 004 -	Added software expiry check.
;
;	08-Jan-1993, DBS; Version X4-001
; 001 -	Major rehash to change the qualifiers so source and destination
;	addresses can be specified.
;	08-Oct-1993, DBS; Version X4-002
; 002 -	Added reason descriptions for stop messages.
;	11-Oct-1993, DBS; Version X4-003
; 003 -	Fixed bug with header size and first six bytes of packet data being
;	overwritten.
;	11-Oct-1993, DBS; Version X4-004
; 004 -	Added /message_type qualifier.
;
;	01-Aug-1994, DBS; Version X5-001
; 001 -	Major rewrite in Macro (this one stolen from ETHERWATCH and the LAT
;	stuff put in).
;	03-Aug-1994, DBS; Version X5-002
; 002 -	Fixed a bug with the CLD specifications.
;	15-Aug-1994, DBS; Version X5-003
; 003 -	Added /BEGIN and /END to specify run times and /COUNT to allow
;	termination after processing a specified number of LAT packets.
;	Added /RECORD qualifier to dump raw data to a file for later playback
;	and of course /PLAYBACK to look at the recorded data.
;	Added /OUTPUT qualifier (long overdue).
;	23-Aug-1994, DBS; Version X5-004
; 004 -	Made /RECORD imply /NONAMES - waste of time while recording.
;	21-Oct-1994, DBS; Version X5-005
; 005 -	Fixed bug that reported the buffer size incorrectly on /PLAYBACK.
;-

	.subtitle Psect definitions, external definitions

	.library	"SYS$LIBRARY:LIB.MLB"
	.library	"SYS$LIBRARY:STARLET.MLB"
	.library	"DBSLIBRARY:SYS_MACROS.MLB"
	.link		"SYS$SYSTEM:SYS.STB" /selective_search

	.disable global

	.external	cli$dcl_parse
	.external	cli$get_value
	.external	cli$present
	.external	lan_format_header
	.external	lan_startup_prm
	.external	latwatch_cld
	.external	lib$free_vm
	.external	lib$get_foreign
	.external	lib$get_vm
	.external	lib$init_timer
	.external	lib$lookup_key
	.external	lib$show_timer
	.external	lib$show_vm
	.external	lib$signal
	.external	lib$stop
	.external	lib_cvt_t_l
	.external	lib_output_seg_t
	.external	lib_output_seg_tzb
	.external	lib_output_seg_zb
	.external	str$match_wild
	.external	str_collapse
	.external	str_len
	.external	str_uppercase
	.external	sys_find_ether_device
	.external	sys_trap_controlc

	enb_long			; for structured macro stuff
	_landef
	_lanhdrdef
	$clidef
	$climsgdef
	$fabdef
	$iodef
	$latdef
	$lnmdef
	$psldef
	$rabdef
	$rmsdef
	$ssdef
	$stsdef

	def_psect _util_data, type=DATA, alignment=LONG
	def_psect _util_code, type=CODE, alignment=LONG

	.subtitle Local macros

; .macro IF_PRESENT
; All we do here is set bits in cli_defaulted and cli_present to indicate
; which qualifiers were used and whether or not they used the default.
; This information is used later for matching packet information against
; what the user wants to see.

.macro if_present item

	call cli$present cli_'item
	if <eql,#cli$_defaulted,r0> then
	  bisl	#m_'item, cli_defaulted
	else
	if <eql,#cli$_present,r0> then
	  bisl	#m_'item, cli_supplied
	endif
	endif

.endm if_present

.macro vector location, value

	.address location
	.long	value

.endm vector

.macro pad location

	bicl3	#^XFFFFFFFE, location, r0
	addl2	r0, location

.endm pad

.macro init_item location

	movab	location, r3

.endm init_item

.macro item description

	movab	description, r2
	call	display_desc_ascic

.endm item

	.subtitle Impure data areas

	set_psect _util_data

program_id:	.ascid	"LATWATCH  X5-005"
loaded_fao:	.ascid	"!UL names and addresses were loaded"
intro_line1:	.ascid	"Starting a watch on device !AS"
intro_line2:	.ascid	"LAT packets from !AS [!AS] to !AS [!AS]!/"-
			"Message type is !AS, Display option is !AS"
playing_back:	.ascid	"Reading recorded data from !AS"
display_header:	.ascid	"!78*-!/From !AS [!AC] to !AS [!AC]"-
			"!/ !4UW byte buffer at !%D"
virt_circ_hdr:	.ascid	"!28AC Dest !XW, Source !XW, !UB slot!%S,"-
			" Seq/Ack !UB/!UB!/!AC, !AC"
start_message:	.ascid	"  Protocol Version V!UB.!UB, Product !UB V!UB, "-
			"Facility !XW, Circuit Timer !UBms!/"-
			"  Keep Alive Timer !UB, MinRcvDGsize !UW, "-
			"MaxSimSlots !UB, DataLinkBuffs !UB"
stop_message:	.ascid	"  Circuit disconnect <!AC> (!XB)"
command_message:.ascid	"Command Message !30<<!AC>!> "-
			"Command type !UB, modifier !UB!/"-
			"  Protocol format !UB, Current protocol V!UB.!UB"-
			", High version !UB, Low version !UB!/"-
			"  Datalink receive frame size !UW, Request ID !UW"-
			", Entry ID !UW"
status_message:.ascid	"Status Message, Protocol format !UB, "-
			"Current protocol V!UB.!UB, High V!UB, Low V!UB!/"-
			"  Datalink receive frame size !UW, "-
			"Retransmit Timer !UW"-
			", Entry count !UW"
solicit_message:.ascid	"Solicit Information, Protocol format !UB, "-
			"Current protocol V!UB.!UB, High V!UB, Low V!UB!/"-
			"  Datalink receive frame size !UW, "-
			"Solicit identifier !UW"-
			", Response timer !UW"
response_message:.ascid	"Response Information, Protocol format !UB, "-
			"Current protocol V!UB.!UB, High V!UB, Low V!UB!/"-
			"  Datalink receive frame size !UW, "-
			"Solicit identifier !UW!/"-
			"  Response status !XW, Source status !XW!/"-
			"  Source node address !XB-!XB-!XB-!XB-!XB-!XB, "-
			"Multicast timer !UW"
service_message:.ascid	"Service Announcement, Circ Timer !UBms, "-
			"Current protocol V!UB.!UB, High V!UB, Low V!UB!/"-
			"  Datalink receive frame size !UW, "-
			"Multicast timer !UW, Node status !XB"
service_entries:.ascid	"  !UB Service!%S"
service_number:	.ascid	"  Service !UL"
service_status:	.ascid	"    Service status <!XB>!/    Service rating <!UB>"
status_entries:	.ascid	"  !UB Status entr!1%Cy!%Eies!%F"
unknown_message:.ascid	"Unknown message type !UB"
slot_header:	.ascid	" Slot !3UB !20<<!AC>!> (!XB), Dest !XB, Source !XB"-
			", !3UB Byte!%S, !UB Credit!%S"
start_slot:	.ascid	"  Service Class !UB, Min Attn Size !UB, Data Size !UB"
stop_slot:	.ascid	"  Stop reason (!XB) <!AC>"
item_format:	.ascid	"    !AC <!AC>"
entry_header:	.ascid	"  Entry status !XB, Error !XB, Req ID !XW, "-
			"Entry ID !XW!/"-
			"  Queue elapsed time !UWmins, Min position !UW, "-
			"Max position !UW"
wildcard:	.ascid	"*"
null_ascic:	.ascic	""
bad_item:	.ascic	"**bad packet data**"
vm_allocated:	.ascid	"LIB$GET_VM allocated !UL bytes at !XL"
tally:		.ascid	"Of the !UL LAT packets read "-
			"(!UL total), !UL packets were !AS"
displayed:	.ascid	"displayed"
recorded:	.ascid	"recorded"
faol_prmlst:	.blkl	40
		.long	^XFFBADBAD

dst_node_name:		.ascic	"Destination node"
dst_srvc_name:		.ascic	"Requested service"
location_text:		.ascic	"Location"
master_node_name:	.ascic	"Master node"
node_name:		.ascic	"Node name"
node_desc:		.ascic	"Desc'n"
obj_node_name:		.ascic	"Destination node"
obj_port_name:		.ascic	"Destination port"
obj_srvc_name:		.ascic	"Destination service"
reason_given:		.ascic	"Reason"
slave_node_name:	.ascic	"Slave node"
src_node_name:		.ascic	"Source node"
src_node_desc:		.ascic	"Desc'n"
srvc_class:		.ascic	"Service class"
srvc_name:		.ascic	"Service name"
srvc_desc:		.ascic	"Desc'n"
subj_group:		.ascic	"Subject group"
subj_node_name:		.ascic	"Soliciting node"
subj_port_name:		.ascic	"Soliciting port"
subj_dscr:		.ascic	"Source service"

	alloc_string	_faobuf, 2048	; used for all output - needs to
					;  be big enough to hold an ethernet
					;  packet (in case /disp=fast is used)
	alloc_string	command, 256

; Here we have to fudge the command line by loading the verb and then
; tacking on whatever the user specifies to the end of it.  This we can
; then pass on the the cli routines.

command_buffer:		.long	command_buffer_extra-command_buffer_t
			.address command_buffer_t
command_buffer_t:	.ascii	"LATWATCH"
command_buffer_extra:	.blkb	<8+256>

; Some general bits and pieces

	lat_protocol = ^X0460

iosb:			.word	0
iosb_xfr_size:		.word	0
iosb_unused1:		.byte	0
iosb_status:		.byte	0
iosb_error_summary:	.byte	0
iosb_unused2:		.byte	0
log_channel:		.long	0
packets_read:		.long	0	; used for some stats on the way
lat_packets_read:	.long	0	;  out...
packets_displayed:	.long	0	;  ...
stats_context:		.long	0	; used by lib$init/show_timer
slot_type:		.long	0

; These next bits are used to convert decnet node numbers in the format
; area.node to aa type addresses

area_number:		.long	0
node_number:		.long	0
area_number_s:		.long	0
area_number_addr:	.long	0
node_number_s:		.long	0
node_number_addr:	.long	0
aa_format:		.ascid	"AA-00-04-00-!XB-!XB"

; The following definitions specifiy the sizes of the text representations
; of the various fields that are used.

	s_address = 17			; xx-xx-xx-xx-xx-xx
	s_name = 32			; maximum allowed name size
	s_sap = 23
	s_protocol = 5

; The next bits are used to set up some virtual memory to contain the
; name and address lists (for use by lib$lookup_key) and pointers to the
; lists and data area.

vm_bytecount:		.long	0	; used by lib$get_vm/lib$free_vm
vm_base_address:	.long	0
address_vector_size:	.long	0	; filled in later
name_vector_size:	.long	0
data_area_size:		.long	0

	max_nodecount = 30000		; we won't load any more than this
					;  many nodes from the nodelist

user_nodecount:		.long	0	; this is from reading nodelist
node_count:		.long	0	; this is how many real nodes we got
address_vector:		.long	0	; these will contain pointers to
name_vector:		.long	0	;  the memory allocated by lib$get_vm
name_address_data:	.long	0

; These are some dummy headers that we need for various routines to point
; to the header and data buffers.

packet_header_ds:	.long	lanhdr_s_lanhdrdef
packet_header_addr:	.address packet_header
packet_data_ds:		.long	lan_s_ethernet
packet_data_addr:	.address packet_data

; This is the buffer where the header and data are returned by $qio

packet_length:		.long	0	; length of received packet
record_buffer:
rec_type:		.byte	0
rec_time:		.quad	0
	rec_hdrsize = .-record_buffer
packet_buffer:
packet_header:		.blkb	lanhdr_s_lanhdrdef
packet_data:		.blkb	lan_s_ethernet
	rec_rsize = .-record_buffer
	rec_ascii = 1
	rec_binary = 2

; Here we define some descriptors that will be used to check the details
; of the incoming packet.  We basically have three strings, the source and
; destination address and the sap details (returned by LAN_FORMAT_HEADER).
; We then use "fake" descriptors to address sub-fields within the sap details.
; The strings are those returned by lan_format_header and are therefore
; fixed length and a known format.
; pkt_destination and pkt_source will look like "XX-XX-XX-XX-XX-XX".
; pkt_sap will look like "XX-XX XX XX-XX-XX-XX-XX", where "XX-XX" is the
; protocol (also DSAP plus SSAP), the "XX" is the control byte and the rest
; is the PID consisting of COPID and IPID fields.

pkt_destination:	.long	s_address
			.address pkt_destination_t
pkt_destination_t:	.blkb	s_address
pkt_source:		.long	s_address
			.address pkt_source_t
pkt_source_t:		.blkb	s_address
pkt_sap:		.long	s_sap
			.address pkt_sap_t
pkt_sap_t:		.blkb	s_sap
pkt_protocol:		.long	s_protocol
			.address pkt_sap_t+0
pkt_message_type:	.long		; extracted...

; The following areas are used to store the items returned from the command
; line.  The sizes here should give enough room for valid addresses etc.
; and also allow for the use of keywords on those items that allow keywords.

	alloc_string	from, 64
	alloc_string	to, 64
	alloc_string	device, 64
	alloc_string	display, 32
	alloc_string	message_type, 32
	alloc_string	begin, 64
	alloc_string	end, 64
	alloc_string	count, 32
	alloc_string	output, 255
	alloc_string	record, 255

	alloc_string	from_name, s_name ; filled in if available from the
	alloc_string	to_name, s_name	; nodelist entries
from_ascic:		.long	0
to_ascic:		.long	0
message_value:		.long	0	; filled in by lib$lookup_key if the
					;  /message value is a keyword
selected_messages:	.long	0	; bit mask of selected message types
default_end:		.ascid	"0 00:30:00.00"
begin_time:		.quad	0
end_time:		.quad	0
count_l:		.long	0	; use time limit

lnm_tabnam:	.ascid	"LNM$PROCESS_TABLE"
lnm_lognam:	.ascid	"SYS$OUTPUT"
lnm_acmode:	.long	psl$c_user
lnm_itmlst:	.word	0		; buffer length - filled in later
		.word	lnm$_string
		.long	0		; buffer address - filled in later
		.long	0		; return address - not used
		.long	0		; to end the list

	m_run_msg = 1
	m_start_msg = 2
	m_stop_msg = 4
	m_service_msg = 1024
	m_command_msg = 4096
	m_status_msg = 8192
	m_solicit_msg = 16384
	m_response_msg = 32768
	m_all_msg = ^XFFFFFFFF
	m_msg_type0 = 1
	m_msg_type1 = 2
	m_msg_type2 = 4
	m_msg_type3 = 8
	m_msg_type4 = 16
	m_msg_type5 = 32
	m_msg_type6 = 64
	m_msg_type7 = 128
	m_msg_type8 = 256
	m_msg_type9 = 512
	m_msg_type10 = 1024
	m_msg_type11 = 2048
	m_msg_type12 = 4096
	m_msg_type13 = 8192
	m_msg_type14 = 16384
	m_msg_type15 = 32768
	m_unknown_msg = <m_msg_type3!m_msg_type4!m_msg_type5!m_msg_type6-
			!m_msg_type7!m_msg_type8!m_msg_type9!m_msg_type11>

	alloc_string	message_choice, 16 ; filled in later

destination_ascic:	.address null_ascic ; filled in by lookup_name when
source_ascic:		.address null_ascic ;  processing a packet

display_option:	.long	display_c_ascii	; the default
	display_c_all = 1
	display_c_none = 2
	display_c_ascii = 3
	display_c_hex = 4
	display_c_fast = 5

	alloc_string	display_choice, 16 ; filled in by lib$lookup_key to
					; be the full keyword
all_segment_size:	.long	16
hex_segment_size:	.long	20
ascii_segment_size:	.long	64
fast_format:		.ascid	"!AF"	; minimum formatting required

; Here are the valid command line qualifiers.

cli_debug:		.ascid	"DEBUG"
cli_device:		.ascid	"DEVICE"
cli_both:		.ascid	"BOTH"
cli_display:		.ascid	"DISPLAY"
cli_from:		.ascid	"FROM"
cli_to:			.ascid	"TO"
cli_message_type:	.ascid	"MESSAGE_TYPE"
cli_nonames:		.ascid	"NONAMES"
cli_count:		.ascid	"COUNT"
cli_end:		.ascid	"END"
cli_begin:		.ascid	"BEGIN"
cli_output:		.ascid	"OUTPUT"
cli_record:		.ascid	"RECORD"
cli_playback:		.ascid	"PLAYBACK"

; The following definitions are used for checking whether a command line
; item was defaulted or not.  This is used in an attempt to increase the speed
; with which we determine whether or not we display a particular packet.
; The flags are set by the command parsing routine.

flag1:		.long	0		; this is a general flag area but uses
					;  the stuff defined below...
cli_supplied:	.long	0		; to keep track of what was given
cli_defaulted:	.long	0		;  and what was defaulted
	m_from		= 1
	v_from		= 0
	m_to		= 2
	v_to		= 1
	m_both		= 512
	v_both		= 9
	m_unknown	= 1024
	v_unknown	= 10
	m_from_unknown	= 2048
	v_from_unknown	= 11
	m_to_unknown	= 4096
	v_to_unknown	= 12
	m_device	= 8192
	v_device	= 13
	m_display	= 16384
	v_display	= 14
	m_source_unknown = 32768
	v_source_unknown = 15
	m_dest_unknown = 65536
	v_dest_unknown = 16
	m_nonames	= 131072
	v_nonames	= 17
	m_debug		= 262144
	v_debug		= 18
	m_message_type	= 524288
	v_message_type	= 19
	m_count		= 1048576
	v_count		= 20
	m_end		= 2097152
	v_end		= 21
	m_begin		= 4194304
	v_begin		= 22
	m_output	= 8388608
	v_output	= 23
	m_record	= 16777216
	v_record	= 24
	m_playback	= 33554432
	v_playback	= 25

; The following keyword table (for lib$lookup_key) is used to check the
; single entry UNKNOWN to be used in /from and /to choices.

unknown_vector:	.long	1*2		; only one keyword
		.address unknown_keyword
		.long	m_unknown	; value to return
unknown_keyword:
		.ascic	"UNKNOWN"

unknown_ascid:	.ascid	"UNKNOWN"	; not part of the table, but used

; The following keyword table (for lib$lookup_key) contains the valid options
; available on the /display qualifier.

display_vector:	.long	7*2		; seven choices available
		vector	ascii_keyword, display_c_ascii
		vector	text_keyword, display_c_ascii
		vector	all_keyword, display_c_all
		vector	both_keyword, display_c_all
		vector	none_keyword, display_c_none
		vector	hex_keyword, display_c_hex
		vector	fast_keyword, display_c_fast
all_keyword:	.ascic	"ALL"
both_keyword:	.ascic	"BOTH"
none_keyword:	.ascic	"NONE"
ascii_keyword:	.ascic	"ASCII"
text_keyword:	.ascic	"TEXT"
hex_keyword:	.ascic	"HEXADECIMAL"
fast_keyword:	.ascic	"FAST"

; The following keyword table (used by lib$lookup_key) contains valid options
; for the /message_type qualifier

message_vector:		.long	26*2	; 26 choices
			vector	all_types_keyword, m_all_msg
			vector	unknown_types_keyword, m_unknown_msg
			vector	run_keyword, m_run_msg
			vector	start_keyword, m_start_msg
			vector	stop_keyword, m_stop_msg
			vector	service_keyword, m_service_msg
			vector	command_keyword, m_command_msg
			vector	status_keyword, m_status_msg
			vector	solicit_keyword, m_solicit_msg
			vector	response_keyword, m_response_msg
			vector	type0_keyword, m_msg_type0
			vector	type1_keyword, m_msg_type1
			vector	type2_keyword, m_msg_type2
			vector	type3_keyword, m_msg_type3
			vector	type4_keyword, m_msg_type4
			vector	type5_keyword, m_msg_type5
			vector	type6_keyword, m_msg_type6
			vector	type7_keyword, m_msg_type7
			vector	type8_keyword, m_msg_type8
			vector	type9_keyword, m_msg_type9
			vector	type10_keyword, m_msg_type10
			vector	type11_keyword, m_msg_type11
			vector	type12_keyword, m_msg_type12
			vector	type13_keyword, m_msg_type13
			vector	type14_keyword, m_msg_type14
			vector	type15_keyword, m_msg_type15
all_types_keyword:	.ascic	"ALL"
unknown_types_keyword:	.ascic	"UNKNOWN"
run_keyword:		.ascic	"RUN_MESSAGE"
start_keyword:		.ascic	"START_MESSAGE"
stop_keyword:		.ascic	"STOP_MESSAGE"
service_keyword:	.ascic	"SERVICE_INFO"
command_keyword:	.ascic	"COMMAND_MESSAGE"
status_keyword:		.ascic	"STATUS_MESSAGE"
solicit_keyword:	.ascic	"SOLICIT_INFO"
response_keyword:	.ascic	"RESPONSE_INFO"
type0_keyword:		.ascic	"TYPE0"
type1_keyword:		.ascic	"TYPE1"
type2_keyword:		.ascic	"TYPE2"
type3_keyword:		.ascic	"TYPE3"
type4_keyword:		.ascic	"TYPE4"
type5_keyword:		.ascic	"TYPE5"
type6_keyword:		.ascic	"TYPE6"
type7_keyword:		.ascic	"TYPE7"
type8_keyword:		.ascic	"TYPE8"
type9_keyword:		.ascic	"TYPE9"
type10_keyword:		.ascic	"TYPE10"
type11_keyword:		.ascic	"TYPE11"
type12_keyword:		.ascic	"TYPE12"
type13_keyword:		.ascic	"TYPE13"
type14_keyword:		.ascic	"TYPE14"
type15_keyword:		.ascic	"TYPE15"

; The next bits are the message type display texts

message_display:
		.address 10$
		.address 11$
		.address 12$
		.address 13$
		.address 14$
		.address 15$
		.address 16$
		.address 17$
		.address 18$
		.address 19$
		.address 20$
		.address 21$
		.address 22$
		.address 23$
		.address 24$
		.address 25$
		.address 26$
10$:	.ascic	"Run Message"
11$:	.ascic	"Start Message"
12$:	.ascic	"Stop Message"
13$:	.ascic	"Message Type 3"
14$:	.ascic	"Message Type 4"
15$:	.ascic	"Message Type 5"
16$:	.ascic	"Message Type 6"
17$:	.ascic	"Message Type 7"
18$:	.ascic	"Message Type 8"
19$:	.ascic	"Message Type 9"
20$:	.ascic	"Service Info"
21$:	.ascic	"Message Type 11"
22$:	.ascic	"Command Message"
23$:	.ascic	"Status Message"
24$:	.ascic	"Solicit Info"
25$:	.ascic	"Response Info"
26$:	.ascic	"Unknown Message Type"

; The next bits are the slot type display texts

slot_display:	.address 10$
		.address 11$
		.address 12$
		.address 13$
		.address 14$
		.address 15$
		.address 16$
		.address 17$
		.address 18$
		.address 19$
		.address 20$
		.address 21$
		.address 22$
		.address 23$
		.address 24$
		.address 25$
10$:	.ascic	"Data-A-Slot"
11$:	.ascic	"SlotType_1"
12$:	.ascic	"SlotType_2"
13$:	.ascic	"SlotType_3"
14$:	.ascic	"SlotType_4"
15$:	.ascic	"SlotType_5"
16$:	.ascic	"SlotType_6"
17$:	.ascic	"SlotType_7"
18$:	.ascic	"SlotType_8"
19$:	.ascic	"Start-Slot"
20$:	.ascic	"Data-B-Slot"
21$:	.ascic	"Attention-Slot"
22$:	.ascic	"Reject-Slot"
23$:	.ascic	"Stop-Slot"
24$:	.ascic	"SlotType_14"
25$:	.ascic	"SlotType_15"

; The next bits are the display texts for the stop reasons

stop_display:	.address 10$
		.address 11$
		.address 12$
		.address 13$
		.address 14$
		.address 15$
		.address 16$
		.address 17$
		.address 18$
		.address 19$
		.address 20$
		.address 21$
10$:	.ascic	"Reason is unknown"
11$:	.ascic	"No slots connected on VC"
12$:	.ascic	"Illegal message or slot received"
13$:	.ascic	"User requested disconnect"
14$:	.ascic	"No progress is being made"
15$:	.ascic	"Time limit expired"
16$:	.ascic	"LAT message retry limit reached"
17$:	.ascic	"Insufficient resources"
18$:	.ascic	"Server circuit timer out of range"
19$:	.ascic	"Number of VCs is exceeded"
20$:	.ascic	"Incompatible protocol version"
21$:	.ascic	"**Reason outside known range**"

; The next bits are the display texts for the slot stop reasons

slot_stop_display:
		.address 10$
		.address 11$
		.address 12$
		.address 13$
		.address 14$
		.address 15$
		.address 16$
		.address 17$
		.address 18$
		.address 19$
		.address 20$
		.address 21$
		.address 22$
		.address 23$
		.address 24$
		.address 25$
10$:	.ascic	"Reason is unknown"
11$:	.ascic	"User requested disconnect"
12$:	.ascic	"System shutdown in progress"
13$:	.ascic	"Invalid slot received"
14$:	.ascic	"Invalid service class"
15$:	.ascic	"Insufficient resources"
16$:	.ascic	"Service in use"
17$:	.ascic	"No such service"
18$:	.ascic	"Service is disabled"
19$:	.ascic	"Service not offered by port"
20$:	.ascic	"Port name is unknown"
21$:	.ascic	"Invalid password"
22$:	.ascic	"Entry is not in queue"
23$:	.ascic	"Immediate access rejected"
24$:	.ascic	"Access denied"
25$:	.ascic	"Corrupted solict request"

; The next two bits relate tp the message originator

who_display:	.address 10$
		.address 11$
10$:	.ascic	"Slave"
11$:	.ascic	"Master"

answer_display:	.address 10$
		.address 11$
10$:	.ascic	"No Response Required"
11$:	.ascic	"Response Required"

command_display:
		.address 17$
		.address 11$
		.address 12$
		.address 13$
		.address 14$
		.address 15$
		.address 16$
		.address 17$
11$:	.ascic	"Solicit non-queued access"
12$:	.ascic	"Solicit queued access"
13$:	.ascic	"Cancel queue entry"
14$:	.ascic	"Send status of entry"
15$:	.ascic	"Send status of queue"
16$:	.ascic	"Send status of multiple entries"
17$:	.ascic	"**Unknown command type**"

	lat_c_service_msg = 10
	lat_c_command_msg = 12
	lat_c_max_valid_stop = 10
	lat_c_unknown_stop = 11
	lat_c_min_valid_command = 1
	lat_c_max_valid_command = 6
	lat_c_unknown_command = 7
	lat_c_min_valid_message = 0
	lat_c_max_valid_message = 15
	lat_c_unknown_message = 16
	lat_c_min_slot_type = 0
	lat_c_max_slot_type = 15

; offsets for virtual circuit header info
	lat_r_vc_header = 0
	lat_s_vc_header = 8
	lat_b_msg_type = 0
	lat_m_rrf = 1
	lat_v_rrf = 0
	lat_m_master = 2
	lat_v_master = 1
	lat_s_msg_type = 6
	lat_v_msg_type = 2
	lat_b_nbr_slots = 1
	lat_w_dst_cir_id = 2
	lat_w_src_cir_id = 4
	lat_b_msg_seq_nbr = 6
	lat_b_msg_ack_nbr = 7

; offsets for start messages		at end of virtual circuit header
	lat_w_min_rcv_datagram_size = 8
	lat_b_prtcl_ver = 10
	lat_b_prtcl_eco = 11
	lat_b_max_sim_slots = 12
	lat_b_nbr_dl_bufs = 13
	lat_b_srv_circt_tmr = 14
	lat_b_keep_alive_timer = 15
	lat_w_facility_number = 16
	lat_b_prod_type_code = 18
	lat_b_prod_vers_numb = 19
	lat_r_start_data = 20

; offsets for stop messages
	lat_b_stop_reason = 8
	lat_t_stop_reason = 9

; offsets for command messges		map over the virtual circuit header
	lat_b_prtcl_fmt = 1
	lat_b_high_prtcl_ver = 2
	lat_b_low_prtcl_ver = 3
	lat_b_curr_prtcl_ver = 4
	lat_b_curr_prtcl_eco = 5
	lat_w_dl_rcv_frame_size = 6
	lat_w_req_id = 8
	lat_w_entry_id = 10
	lat_b_cmd_type = 12
	lat_b_cmd_modifier = 13
	lat_r_cmd_data = 14

; offsets for status messages		same as command in first 8 bytes
	lat_w_status_retrans_timer = 8
	lat_b_entries_counter = 10
	lat_r_status_data = 11

; offsets for solicit messages		same as command in first 8 bytes
	lat_w_solicit_id = 8
	lat_w_response_timer = 10
	lat_r_solicit_data = 12

; offsets for response messages		same as solicit in first 10 bytes
	lat_w_response_status = 10
	lat_w_src_node_status = 12
	lat_r_source_node_addr = 14
	lat_w_node_mc_timer = 20
	lat_r_response_data = 22

; offsets for service announcements
	lat_b_srv_circt_tmr = 1
	lat_b_msg_incarnation = 6
	lat_b_change_flags = 7
	lat_w_dl_rcv_frame_size2 = 8
	lat_b_node_multi_timr = 10
	lat_b_node_status = 11
	lat_r_service_data = 12

; offsets for slot header fields
	slot_c_slot_header = 8		; offset to first slot header
	slot_b_dst_slot_id = 0
	slot_b_src_slot_id = 1
	slot_b_slot_byte_count = 2
	slot_b_status_byte_count = 2
	slot_b_credits = 3
	slot_s_credits = 4
	slot_v_credits = 0
	slot_s_slot_type = 4
	slot_v_slot_type = 4
	slot_c_header_size = 4

; offset to start of slot data a & b type slots
	slot_t_slot_data = 4

; offsets for start slot
	slot_b_service_class = 4
	slot_b_min_attn_slot_size = 5
	slot_b_min_data_slot_size = 6
	slot_r_start_slot = 7

; offsets for stop slot
	slot_b_reason = 3		; is the same as the credits field
	slot_s_reason = 4
	slot_v_reason = 0

; offets for entries within status messages
	entry_b_length = 0
	entry_b_status = 1
	entry_b_error = 2
	entry_b_fill_1 = 3
	entry_w_request_id = 4
	entry_w_entry_id = 6
	entry_w_elapsed_queue_time = 8
	entry_w_min_queue_position = 10
	entry_w_max_queue_position = 12
	entry_r_entry_data = 14

	reset_psect

	.subtitle Work areas for reading of the nodelist file

	set_psect _util_data

	_rsize = 256			; not really but should be plenty
_record:	.blkb	_rsize		; buffer for the record
_record_ds:	.long	_rsize		; descriptor to point to the buffer
_record_addr:	.address _record

	alloc_string	play_thing, _rsize ; used for juggling the record
	alloc_string	play_thing2, _rsize

	_mbc = 127
	_mbf = 16
	_rtv = 255

	.align	long
_fab:	$fab	fac=<GET>, -
		fnm=<NODELIST>, -
		dnm=<ETHERWATCHER:NODELIST.DAT>, -
		fop=<SQO>, -
		rtv=_rtv, -
		shr=<GET,PUT,DEL,UPD>
_rab:	$rab 	fab=_fab, -
		mbc=_mbc, -
		mbf=_mbf, -
		rac=<SEQ>, -
		rop=<WAT,RAH,NLK,RRL,LOC>, -
		ubf=_record, -
		usz=_rsize

	reset_psect

	.subtitle Work areas for recording/playback file

	set_psect _util_data

	rec_alq = 2400			; initial allocation
	rec_deq = 2400			; default extension quantity
	rec_mbc = 127			; multi block count
	rec_mbf = 16			; multi buffer count
	rec_mrs = rec_rsize		; maximum record size
	rec_rtv = 255			; retreival pointers

	.align long
rec_fab:
	$fab	alq=rec_alq, -
		deq=rec_deq, -
		dnm=<LATWATCH.RECORD>, -
		fop=<SQO,TEF>, -
		mrs=rec_mrs, -
		org=<SEQ>, -
		rat=<CR>, -
		rfm=<VAR>, -
		rtv=rec_rtv, -
		shr=<NIL>
rec_rab:
	$rab	fab=rec_fab, -
		mbc=rec_mbc, -
		mbf=rec_mbf, -
		rac=<SEQ>, -
		rbf=record_buffer, -
		rop=<RAH,WBH>, -
		ubf=record_buffer, -
		usz=rec_rsize

	reset_psect

	.subtitle Mainline

	set_psect _util_code

	.entry -
latwatch, ^m<>

	call lib$init_timer -		; initialize stats in case we
		stats_context		;  want to look at them

	call sys_trap_controlc -	; so we can exit gracefully
		process_controlc, -	; ... (hopefully)
		iosb

	call read_nodelist		; need some of this info when
					;  parsing the command...
	call parse_command		; get all the gory details

	call startup_device		; fire up the controller

	$hiber_s			; ...and wait

90$:	ret

	.subtitle Read the nodelist file

	.entry -
read_nodelist, ^m<r2,r3,r4,r5,r6,r7>

;++
; Functional Description:
;	The first thing we do is a quick scan of the nodelist file to see
;	how much memory we need to allocate for the names and addresses.
;	Here we open the nodelist file, read each record in the file and
;	pass it on to the process_nodelist_entry routine to validate it
;	and store it in the appropriate tables.  When complete we just
;	close the file.  If we don't find the file, we don't generate any
;	errors, we just continue but don't have any names and addresses in
;	the tables so every address is UNKNOWN when we display the packet
;	details.
;
; Calling Sequence:
;	call	read_nodelist
;
; Formal Argument(s):
;	None.
;
; Implicit Inputs:
;	None.
;
; Implicit Outputs:
;	None.
;
; Completion Codes:
;	None
;
; Side Effects:
;	None
;--

	call	scan_nodelist		; to see how many are there
	clrl	node_count		; we use this for counting
	movl	address_vector, r2	; we will use this later
	addl	#4, r2			; skip the count
	movl	name_vector, r3		; we need this also
	addl	#4, r3			; skip the count
	movl	name_address_data, r4	; we need this as well

	$open	fab=_fab		; open the nodelist
	blbc	r0, close
	$connect rab=_rab		; connect to it
	blbc	r0, close

10$:	$get	rab=_rab		; grab a record
	blbc	r0, close

	movzwl	_rab+rab$w_rsz, r5	; these get passed on to the
	movl	_rab+rab$l_rbf, r6	;  processing routine

	call	process_nodelist_entry

	cmpl	node_count, -		; how many have we done so far
		user_nodecount
	blss	10$			; still got room, try again

close:	$close	fab=_fab

	mull3	#2, node_count, -	; setup the vector count to be the
		@address_vector		;  number of longwords (2 per node)
	movl	@address_vector, -	; should be the same number of entries
		@name_vector		;  in each list

	ret

	.subtitle Routine to scan the nodelist and allocate virtual memory

	.entry -
scan_nodelist, ^m<>
;+
; Here we do a quick scan of the nodelist file to see how many entries are
; in it.  We assume each line is valid so that if there are comments and
; blank lines we will over allocate the real space that we need.  (I can live
; with that).
;-
	clrl	user_nodecount
	$open	fab=_fab		; open the nodelist
	blbc	r0, 90$
	$connect rab=_rab		; connect to it
	blbc	r0, 90$
10$:	$get	rab=_rab		; grab a record
	blbc	r0, 90$
	incl	user_nodecount
	brb	10$
90$:	$close	fab=_fab
	if <gtr,user_nodecount,#max_nodecount> then
	  movl	#max_nodecount, user_nodecount
	endif
	if <eql,user_nodecount> then	; if the file is missing, or empty
	  movl	#1, user_nodecount	;  allocate space for 1 node to
	  bisl	#m_nonames, cli_supplied;  prevent accvio's, and set /nonames
	endif
	mull3	#8, user_nodecount, -	; that's how many bytes in the vector
		address_vector_size
	addl2	#4, address_vector_size	; allow for the vector count
	movl	address_vector_size, -	; they will be the same size
		name_vector_size
	addl3	#s_address, #s_name, -	; that's the size of each entry
		data_area_size
	addl2	#2, data_area_size	; plus byte count overhead
	mull2	user_nodecount, -	; and this is how much we really need
		data_area_size		;  for the data portion
	mull3	#2, address_vector_size, -
		vm_bytecount
	addl2	data_area_size, -	; this is the total allocation we
		vm_bytecount		;  need
	call lib$get_vm -		; now try to allocate it
		vm_bytecount, -
		vm_base_address
	if <lbc,r0> then		;  and signal any problems
	  signal code=r0
	endif
	movl	vm_base_address, -	; that's the pointer to the address
		address_vector		;  vector
	addl3	address_vector_size, -	; this is the pointer to the name
		address_vector, -	;  vector
		name_vector
	addl3	name_vector_size, -	; and the pointer to the data area
		name_vector, -
		name_address_data
	clrl	@address_vector		; in case we have no entries
	clrl	@name_vector

	ret

	.subtitle Process a nodelist entry

	.entry -
process_nodelist_entry, ^m<>

;++
; Functional Description:
;	This routine will verify (in a limited way) the record from the
;	nodelist file.  Blank lines are allowed, comments can begin with
;	either "!" or ";" and can really be any line without an equals sign
;	in it.
;	The expected format of the record is
;		xx-xx-xx-xx-xx-xx = nodename
;	Where the xx-xx... stuff is the address of the node with name
;	nodename.  All characters are converted to uppercase.
;	Once we get a "valid" record, we load it into the address and name
;	tables, sort out the cross referencing between the two and that's
;	that.
;
; Calling Sequence:
;	call	process_nodelist_entry
;
; Formal Argument(s):
;	None.
;
; Implicit Inputs:
;	R2	pointer to next longword entry in address vector
;	R3	pointer to next longword entry in name vector
;	R4	pointer to next byte in name/address data
;	R5	size of record
;	R6	address of record buffer
;
; Implicit Outputs:
;	R5 and R6 are trashed to reflect the size of the record after we
;		have done an str_collapse.
;
; Completion Codes:
;	None
;
; Side Effects:
;	None
;--

	movl	r5, _record_ds		; load up a descriptor for us to
	movl	r6, _record_addr	;  use
	call	str_uppercase -		; convert the record to uppercase
			play_thing_ds, -
			_record_ds
	call	str_collapse -		; get rid of spaces etc...
			play_thing2_ds, -
			play_thing_ds
	call	str_len -		; and get the real length
			play_thing2_ds, -
			play_thing2
	movq	play_thing2, r5		; now point to the real data
	tstl	r5			; anything there?
	beql	90$			; nope, so go away
	cmpb	#^A/!/, (r6)		; is this a comment
	beql	90$			; yep, so ignore it
	cmpb	#^A/;/, (r6)		; allow ";" as a comment as well
	beql	90$			; yep, so ignore it
	cmpb	#^A/=/, (r6)		; see if address is null
	beql	90$			; yep, so ignore it
	locc	#^A/=/, r5, (r6)	; look for an "="
	cmpl	r0, #1			; how many characters left?
	bleq	90$			; not enough, so skip it
	subl3	r6, r1, r7		; R7 = address length
	movl	r4, (r2)+		; pointer to address in address vector
	movb	r7, (r4)+		; move the address byte count to the
					;  data area
10$:	movb	(r6)+, (r4)+		; now move the address
	sobgtr	r7, 10$			;  until we have it all
	movl	r4, (r2)+		; pointer to name in address table
	decl	r0			; this skips the "=" by reducing the
					;  length by one
	incl	r6			;  and upping the address by one
	cmpl	r0, #s_name		; check that the name is not too long
	bleq	15$			; it's ok, so keep going
	movl	#s_name, r0		; force it to our maximum length
15$:	movb	r0, (r4)+		; move the name byte count to the
					;  data area
20$:	movb	(r6)+, (r4)+		; now move the name
	sobgtr	r0, 20$			;  until it's all done
	movl	-4(r2), (r3)+		; pointer to name in name vector
	movl	-8(r2), (r3)+		; pointer to address in name vector
	incl	node_count		; keep track of how many we have

90$:	ret

	.subtitle Parse the command

	.entry -
parse_command, ^m<>

;++
; Functional Description:
;	Here we validate the command and extract everything we can and
;	validate any qualifier values that were suppplied.
;
; Calling Sequence:
;	call parse_command
;
; Formal Argument(s):
;	None
;
; Implicit Inputs:
;	None.
;
; Implicit Outputs:
;	None.
;
; Completion Codes:
;	None
;
; Side Effects:
;	None
;--

	pushaw	command			; here we get the command they
	pushl	#0			;  used to invoke us - we don't
	pushaq	command_ds		;  prompt even if they didn't give
	calls	#3, g^lib$get_foreign	;  any options

	pushr	#^m<r2,r3,r4,r5>
	movc3	command, -		; tack the foreign command
		command_t, -		;  onto the primed buffer
		command_buffer_extra
	popr	#^m<r2,r3,r4,r5>
	addl2	command, command_buffer	; and fixup the length

	call cli$dcl_parse -		; see if the cli routines think
		command_buffer, -	;  the command is ok
		latwatch_cld
	if <lbc,r0> then		; if it didn't work
	  bisl	#sts$m_inhib_msg, r0	;  then don't signal when we exit
	  pushl	r0			;    cli$... has already done that
	  calls	#1, g^lib$stop		;  bail out
	endif ;<lbc,r0> then

	if_present from			; here we check all the possible
	if_present to			;  qualifiers and set bits in
	if_present device		;  the cli_supplied and _defaulted
	if_present display		;  flags to say what was what
	if_present message_type
	if_present both
	if_present nonames
	if_present debug
	if_present count
	if_present end
	if_present output
	if_present record
	if_present playback

; get the output details if any

	if <bs,#v_output,cli_supplied> then
	  call cli$get_value cli_output, output_ds, output
	  movw	output, lnm_itmlst
	  movab output_t, lnm_itmlst+4
	  $crelnm_s -
		tabnam=lnm_tabnam, -
		lognam=lnm_lognam, -
		acmode=lnm_acmode, -
		itmlst=lnm_itmlst
	  if <lbc,r0> then
	    signal code=r0
	  endif
	endif

; now see if we are recording or playing back

	if <bs,#v_record,cli_supplied> then
	  call cli$get_value cli_record, record_ds, record
	  call create_record_file
	  bisl	#m_nonames, cli_supplied ; prevent name processing
	endif

	if <bs,#v_playback,cli_supplied> then
	  call cli$get_value cli_playback, record_ds, record
	endif

	display	program_id		; say who we are

	$fao_s	ctrstr=loaded_fao, -	; say how many names/addresses we
		outbuf=_faobuf_ds, -	;  loaded
		outlen=_faobuf, -
		p1=node_count
	display	_faobuf

; Now we get the values they supplied, if any

	if <bs,#v_device,cli_supplied> then
	  call cli$get_value -		; they supplied a device, so use it
		cli_device, device_ds, device
	else
	call sys_find_ether_device -	; else see what's on the system
		device_ds		;  and use that one
	if <lbc,r0> then		; if any problems at this point
	  pushl	r0
	  calls	#1, g^lib$stop		;  bail out
	endif ;<lbc,r0> then
	call str_len -			; we got one, now fix up the
		device_ds, -		;  string
		device
	endif ;<bs,#v_device,cli_supplied> then

; ...now get all the other values, there should be no errors since all these
; have defaults i.e. there should not be any cli$_absent returned

	call cli$get_value cli_from, from_ds, from
	call cli$get_value cli_to, to_ds, to
	call cli$get_value cli_display, display_ds, display

	if <bs,#v_message_type,cli_defaulted> then
	  bisl	#m_all_msg, selected_messages
	  movzbl all_keyword, message_choice
	  pushr	#^m<r2,r3,r4,r5>
	  movc3	message_choice, -
		all_keyword+1, -
		message_choice_t
	  popr	#^m<r2,r3,r4,r5>
	else
10$:	call cli$get_value cli_message_type, message_type_ds, message_type
	if <lbs,r0> then
	  call lib$lookup_key -		; see if the value is a keyword
		message_type, -
		message_vector, -
		message_value, -
		message_choice_ds, -
		message_choice
	  bisl	message_value, selected_messages
	  brb	10$
	endif
	endif

	call	check_display_option

	if <bc,#v_from,cli_defaulted> then ; if /from not defaulted
	  call	check_from_option	; check what they gave us
	else
	clrl	from_name
	endif

	if <bc,#v_to,cli_defaulted> then ; if /to not defaulted
	  call	check_to_option		; check what they gave us
	else
	clrl	to_name
	endif

	if <bs,#v_debug,cli_supplied> then
	  $fao_s ctrstr=vm_allocated, -
		outbuf=_faobuf_ds, -
		outlen=_faobuf, -
		p1=vm_bytecount, -
		p2=vm_base_address
	  display _faobuf
	  call lib$show_vm
	endif

	if <bs,#v_begin,cli_supplied> then
	  call cli$get_value cli_begin, begin_ds, begin
	  $bintim_s -
		timbuf=begin, -
		timadr=begin_time
	  $setimr_s -
		efn=#0, -
		daytim=begin_time
	  $wfland_s -
		efn=#0, -
		mask=#1
	endif

	if <bs,#v_count,cli_supplied> then
	  call cli$get_value cli_count, count_ds, count
	  call lib_cvt_t_l count, count_l
	endif
	if <eql,count_l> then		; if it's zero
	  decl	count_l			;  make it -l i.e. use time to end
	endif
	bbs	#v_count, cli_supplied, - ; if /count specified, then
		exit_parse		;  don't use the timer to exit

	if <bs,#v_end,cli_supplied> then
	  call cli$get_value cli_end, end_ds, end
	  $bintim_s -
		timbuf=end, -
		timadr=end_time
	else
	$bintim_s -
		timbuf=default_end, -
		timadr=end_time
	endif
	$setimr_s -
		daytim=end_time, -
		astadr=process_controlc

exit_parse:

	ret

	.subtitle Routines to check the command line qualifier values

	.entry -
check_display_option, ^m<>
;+
; Here we take what was given and return the full keyword for display
; purposes.  There should be no errors since the cli parsing would have
; picked them up and we wouldn't have got this far.
;-
	call lib$lookup_key -
		display, -		; this is what they said
		display_vector, -	; ...the table of valid keywords
		display_option, -	; ...the coded value
		display_choice_ds, -	; ...the full keyword
		display_choice		; ...and the keyword length

	ret

;+
; The check_from_option and check_to_option routines do the same work but
; just use different strings as the source.
; The strategy is as follows:
; 1.  See if the keyword UNKNOWN was used.  If so then set the appropriate
;	flag for later... that's all we need to do.
; 2.  Now check to see if the string is in the form area.node i.e. a DECnet
;	address.  If it is, then we convert it into a string of the form
;	AA-00-04-00-XX-XX and make it look like the user typed it.
; 3.  Assume the string is a name that will be found in the nodename list.
; 4.  If we find the name in the nodename list then use the associated address
;	as our address used for matching against packets and use the given
;	name to display in the header.  At this point we have finished.
; 5.  If the name is not in the nodename list, assume it is an address in the
;	address list.
; 6.  If we find it in the address list then use the address for matching
;	purposes and use the associated name to display in the header.
;	At this point we have finished.
; 7.  At this point the given value is neither a known name nor a known
;	address therefore just use what we were given as it is probably an
;	address that is not currently in the list or contains wildcards.
;-

	.entry -
check_from_option, ^m<>

	call lib$lookup_key from, unknown_vector
	if <lbs,r0> then		; if they said /from=unknown
	  bisl	#m_from_unknown, flag1	;  set the flag
	  movq	wildcard, from
	  movq	unknown_ascid, from_name
	else
	pushr	#^m<r10,r11>
	movaq	from_ds, r10		; check for a DECnet address
	movaq	from, r11
	call	convert_decnet_to_aa
	popr	#^m<r10,r11>
	call lib$lookup_key from, @name_vector, from_ascic, from_ds, from
	if <lbs,r0> then		; if it is a name
	  pushr	#^m<r2,r3,r4,r5>
	  movc3	from, @from+4, -	;  copy "from" into "from_name"
		@from_name+4
	  movl	from, from_name		;  and fix the length
	  movl	from_ascic, r0		;  just to play with
	  movzbl (r0)+, from		;  that's the address length
	  movc3	from, (r0), @from+4	;  copy the address to the "from" field
	  popr	#^m<r2,r3,r4,r5>
	else				; wasn't a name...
	call lib$lookup_key from, @address_vector, from_ascic
	if <lbs,r0> then		; if it is an address
	  pushr	#^m<r2,r3,r4,r5>
	  movl	from_ascic, r0
	  movzbl (r0)+, from_name
	  movc3	from_name, (r0), -	; copy the ascic stuff to the ascid
		@from_name+4		;   area
	  popr	#^m<r2,r3,r4,r5>
	else				; wasn't name or address - use as is
	clrl	from_name		; nothing to display
	endif ;<lbs,r0> then
	endif ;<lbs,r0> then
	endif ;<lbs,r0> then

	ret

	.entry -
check_to_option, ^m<>

	call lib$lookup_key to, unknown_vector
	if <lbs,r0> then		; if they said /to=unknown
	  bisl	#m_to_unknown, flag1	;  set the flag
	  movq	wildcard, to
	  movq	unknown_ascid, to_name
	else
	pushr	#^m<r10,r11>
	movaq	to_ds, r10		; see if it is a DECnet address
	movaq	to, r11
	call	convert_decnet_to_aa
	popr	#^m<r10,r11>
	call lib$lookup_key to, @name_vector, to_ascic, to_ds, to
	if <lbs,r0> then		; if it is a name
	  pushr	#^m<r2,r3,r4,r5>
	  movc3	to, @to+4, @to_name+4	;  copy "to" into "to_name"
	  movl	to, to_name		;  and fix the length
	  movl	to_ascic, r0		;  just to play with
	  movzbl (r0)+, to		;  that's the address length
	  movc3	to, (r0), @to+4		;  copy the address to the "to" field
	  popr	#^m<r2,r3,r4,r5>
	else				; wasn't a name...
	call lib$lookup_key to, @address_vector, to_ascic
	if <lbs,r0> then		; if it is an address
	  pushr	#^m<r2,r3,r4,r5>
	  movl	to_ascic, r0
	  movzbl (r0)+, to_name
	  movc3	to_name, (r0), -	; copy the ascic stuff to the ascid
		@to_name+4		;   area
	  popr	#^m<r2,r3,r4,r5>
	else				; wasn't name or address - use as is
	clrl	to_name			; nothing to display
	endif ;<lbs,r0> then
	endif ;<lbs,r0> then
	endif ;<lbs,r0> then

	ret

	.entry -
convert_decnet_to_aa, ^m<>
;+
; Here we check to see if the specified address is a possible decnet type
; address in the form area.node.  If it is then we convert it to the format
; AA-00-04-00-XX-XX and set this up to look like the user supplied this value.
;
; Inputs:
;	R10	Address of the output buffer descriptor
;	R11	Address of the input buffer descriptor
;-
	pushr	#^m<r8,r9>
	movq	(r11), r8		; grab the input buffer descriptor
	locc	#^A/./, r8, (r9)	; try to find a dot
	tstl	r0			; is there one?
	bneq	10$			; yes, so play with it
	brw	90$			; no, bail out
10$:	movq	(r11), area_number_s	; load the area number descriptor
	subl2	r0, area_number_s	;  and fix up the length
	decl	r0			; shorten length by one
	incl	r1			;  and increase address by one
					;  to skip the dot
	movq	r0, node_number_s	; load the node number descriptor
	call lib_cvt_t_l area_number_s, area_number
	blbc	r0, 90$			; bail out if no good
	call lib_cvt_t_l node_number_s, node_number
	blbc	r0, 90$			; bail out if no good
	mull3	#1024, area_number, r0	; now convert the two numbers into
	addl2	node_number, r0		;  a single word value
	movl	r0, r1			; we need a copy to split it
	bicl	#^XFFFFFF00, r0		; now do the jiggery pokery to
	bicl	#^XFFFF00FF, r1		;  get the bytes swapped and
	divl2	#^X100, r1		;  cleaned up for the aa format
	$fao_s	ctrstr=aa_format, -
		outbuf=(r10), -
		outlen=(r11), -
		p1=r0, -
		p2=r1
90$:	popr	#^m<r8,r9>

	ret

	.subtitle Startup the ethernet device in promiscuous mode

	.entry -
startup_device, ^m<>

;++
; Functional Description:
;	This routine attempts to startup the ethernet device, set it up so
;	we can see everything then queue the first read request to it.
;	It also displays some informational messages along the way.
;
; Calling Sequence:
;	call startup_device
;
; Formal Argument(s):
;	None
;
; Implicit Inputs:
;	None.
;
; Implicit Outputs:
;	None.
;
; Completion Codes:
;	None
;
; Side Effects:
;	None
;--

	if <bs,#v_playback,cli_supplied> then
	  $fao_s ctrstr=playing_back, -
		outbuf=_faobuf_ds, -
		outlen=_faobuf, -
		p1=#record
	  display _faobuf
	  call open_record_file
10$:	  $get	rab=rec_rab
	  blbc	r0, 20$
	  call process_packet
	  brb	10$
20$:	  call process_controlc
	else
	$fao_s	ctrstr=intro_line1, -
		outbuf=_faobuf_ds, -
		outlen=_faobuf, -
		p1=#device
	display	_faobuf

	call lan_startup_prm -
		device, -
		log_channel, -
		iosb
	if <lbc,iosb> then		; if anything goes wrong with that
	  pushl	iosb			;  then we can't go any further
	  calls	#1, g^lib$stop
	endif ;<lbc,r0> then

	$fao_s	ctrstr=intro_line2, -
		outbuf=_faobuf_ds, -
		outlen=_faobuf, -
		p1=#from, -
		p2=#from_name, -
		p3=#to, -
		p4=#to_name, -
		p5=#message_choice, -
		p6=#display_choice
	display	_faobuf
	call queue_async_read
	endif ;<bs,#v_playback,cli_supplied> then

	ret

	.subtitle Queue an asynchronous read I/O to the ethernet device

	.entry -
queue_async_read, ^m<>

;++
; Functional Description:
;	Here we issue a read qio to the ethernet device and setup an ast to
;	invoke the process_packet routine when we have something to read.
;
; Calling Sequence:
;
;	call queue_async_read
;
; Formal Argument(s):
;	None
;
; Implicit Inputs:
;	ether_watch_data common area.
;
; Implicit Outputs:
;	None
;
; Completion Codes:
;	None
;
; Side Effects:
;	None
;--

	$qio_s	chan=log_channel, -
		func=#io$_readvblk, -
		iosb=iosb, -
		astadr=process_packet, -
		p1=packet_data, -	; data location
		p2=#lan_s_ethernet, -	; size of data area
		p5=#packet_header	; header location
	if <lbc,r0> then
	  signal code=r0
	endif ;<lbc,r0> then

90$:	ret

	.subtitle Lookup a nodename given an address

	.entry -
lookup_name, ^m<>

;++
; Functional Description:
;	Check the ethernet address given and return the address of ascic
;	string for the associated name.  If the address was not found, then
;	we return the address of the name UNKNOWN.
;
; Calling Sequence:
;	call	lookup_name (%descr(address), %ref(name_location))
;
; Formal Argument(s):
;	address.rt.ds	Address of the descriptor for the string containing
;			the ethernet address to be checked.
;	name_location.wl.r  Address of a longword that will contain the
;			address of the ascic string containing the node name.
;
; Implicit Inputs:
;	4(ap)	Address of descriptor of the ethernet address
;	8(ap)	Address of a longword to receive the address of the ascic
;		string containing the node name
;
; Implicit Outputs:
;	None.
;
; Completion Codes:
;	The low bit in R0 can be checked on return to see if the address was
;	found or not (saves doing string comparisons etc.).
;
; Side Effects:
;	None
;--

	pushl	8(ap)
	pushl	address_vector
	pushl	4(ap)
	calls	#3, g^lib$lookup_key

	if <lbc,r0> then
	  movab	unknown_keyword, @8(ap)
	endif ;<lbc,r0> then

	ret

	.subtitle Lookup an address given a nodename

	.entry -
lookup_address, ^m<>

;++
; Functional Description:
;	Check the name given and return the address of ascic string for the
;	associated ethernet address.  If the name was not found, then we
;	return the address of the name UNKNOWN.
;
; Calling Sequence:
;	call	lookup_name (%descr(nodename), %ref(address_location))
;
; Formal Argument(s):
;	nodename.rt.ds	Address of the descriptor for the string containing
;			the node name to be checked.
;	address_location.wl.r  Address of a longword that will contain the
;			address of the ascic string containing the ethernet
;			address.
;
; Implicit Inputs:
;	4(ap)	Address of descriptor of the node name
;	8(ap)	Address of a longword to receive the address of the ascic
;		string containing the node address
;
; Implicit Outputs:
;	None.
;
; Completion Codes:
;	The low bit in R0 can be checked on return to see if the name was
;	found or not (saves doing string comparisons etc.).
;
; Side Effects:
;	None
;--

	pushl	8(ap)
	pushl	name_vector
	pushl	4(ap)
	calls	#3, g^lib$lookup_key

	if <lbc,r0> then
	  movab	unknown_keyword, @8(ap)
	endif ;<lbc,r0> then

	ret

	.subtitle Here's where we get to if they type control/c

	.entry -
process_controlc, ^m<>

;++
; Functional Description:
;	This is where we end up if a control c is used.  We just exit.
;
; Calling Sequence:
;	via an ast...
;
; Formal Argument(s):
;	None.
;
; Implicit Inputs:
;	None.
;
; Implicit Outputs:
;	None.
;
; Completion Codes:
;	None
;
; Side Effects:
;	None
;--

	if <bs,#v_record,cli_supplied> then
	  movaq recorded, r0
	else
	movaq	displayed, r0
	endif
	$fao_s	ctrstr=tally, -
		outbuf=_faobuf_ds, -
		outlen=_faobuf, -
		p1=lat_packets_read, -
		p2=packets_read, -
		p3=packets_displayed, -
		p4=r0
	display	_faobuf

	call lib$free_vm -
		vm_bytecount, -
		vm_base_address

	if <bs,#v_debug,cli_supplied> then
	  call lib$show_vm
	  call lib$show_timer stats_context
	endif

	display	command_buffer

	if <bs,#v_record,cli_supplied> then
	  call close_record_file
	endif
	if <bs,#v_playback,cli_supplied> then
	  call close_record_file
	endif

	pushl	#^X10000001
	calls	#1, g^lib$stop

	ret

	.subtitle Process a packet

	.entry -
process_packet, ^m<>
	
;++
; Functional Description:
;	This routine gets the packet, formats the header and does the
;	necessary checking to see if we want to look at it based on the
;	selection criteria supplied by the user on the command line.
;	We also queue another i/o request for the next packet.
;
; Calling Sequence:
;	call	process_packet
;
; Formal Argument(s):
;	None.
;
; Implicit Inputs:
;	None.
;
; Implicit Outputs:
;	None.
;
; Completion Codes:
;	None
;
; Side Effects:
;	None
;--

	incl	packets_read

	if <bs,#v_playback,cli_supplied> then
	  movzwl rec_rab+rab$w_rsz, packet_length
	  subl	#rec_hdrsize, packet_length
	  subl	#lanhdr_s_lanhdrdef, packet_length
	else
	movzwl	iosb_xfr_size, -	; grab the length of the packet we
		packet_length		;  are going to look at
	endif

	cmpw	#lat_protocol, -	; see if this is a LAT packet
		packet_header+lanhdr_w_protocol
	beql	check_message_type	; yes, so keep checking
	brw	exit			; no good, so do no more

check_message_type:
	incl	lat_packets_read	; 'tis LAT so count it
	extzv	#lat_v_msg_type, -	; extract the message type from
		#lat_s_msg_type, -	;  the virtual circuit header
		@packet_data_addr, -
		pkt_message_type
	movl	pkt_message_type, r0
	cmpl	r0, #lat_c_max_valid_message
	bleq	10$
	movl	#m_msg_type3, r0	; any "unknown" one will do
10$:	bbs	r0, selected_messages, check_header
	brw	exit

check_header:
	call lan_format_header -	; format the header so we can start
		packet_header_ds, -	;  doing our matching
		pkt_destination, -
		pkt_source, -
		pkt_sap

	bicl	#<m_dest_unknown-	; reset the unknown flags
		 !m_source_unknown>, -
		flag1

	if <bc,#v_nonames,cli_supplied> then
	  call lookup_name -		; get the friendly names if they
		pkt_destination, -	;  exist in our list
		destination_ascic
	  if <lbc,r0> then		; couldn't find them in the list
	    bisl #m_dest_unknown, flag1	;   flag an unknown destination
	  endif
	endif

	if <bc,#v_nonames,cli_supplied> then
	  call lookup_name -		; see if the source is in our list
		pkt_source, -		;  of known addresses
		source_ascic
	  if <lbc,r0> then		; couldn't find this one either
	    bisl #m_source_unknown, flag1	;   flag an unknown source
	  endif
	endif


check_source:
	bbs	#v_from, cli_defaulted, - ; if /from was defaulted
		check_destination	; it always matches, check destination
	if <bs,#v_from_unknown,flag1> then ; if /from=unknown
	  if <bs,#v_source_unknown,flag1> then ; if source is unknown
	    brb check_destination	; then it's ours, check destination
	  else
	  if <bs,#v_both,cli_supplied> -
		and <bs,#v_dest_unknown,flag1> then
	    brw do_display
	  else
	  brw	exit			; else we don't want it
	  endif
	  endif
	endif
	call str$match_wild -		; check source of the packet
		pkt_source, from	;  against the /from value
	blbs	r0, check_destination	; it's a winner
	bbs	#v_both, cli_supplied, - ; no match, so bail out unless they
		10$			;  specified /both
	brw	exit
10$:	call str$match_wild -		; want /both so try to match dest
		pkt_destination, from	;  address
	blbs	r0, check_destination
	brw	exit			; if that failed, just get out
check_destination:
	bbs	#v_to, cli_defaulted, -	; if /to was defaulted
		do_display		;  it always matches, so display it
	if <bs,#v_to_unknown,flag1> then ; if /to=unknown
	  if <bs,#v_dest_unknown,flag1> then ; if destination is unknown
	    brb do_display		;  then we want to look at it
	  else
	  if <bs,#v_both,cli_supplied> -
		and <bs,#v_source_unknown,flag1> then
	    brw do_display
	  else
	  brb	exit			; else we don't want it
	  endif
	  endif
	endif
	call str$match_wild -		; check destination of the packet
		pkt_destination, to	;  against the /to value
	blbs	r0, do_display		; it matches, so look at it
	bbc	#v_both, cli_supplied, - ; no match, so bail out unless they
		exit			;  said /both in the command
	call str$match_wild -		; want /both so try to match source
		pkt_source, to		;  address
	blbc	r0, exit		; if that failed, try another packet

do_display:
	incl	packets_displayed
	if <bs,#v_record,cli_supplied> then
	  call record_a_packet
	else
	call display_a_packet
	endif

exit:	cmpl	lat_packets_read, count_l ; see if we should finish yet
	blssu	10$			; not yet...
	call process_controlc		; finish, don't want to look anymore
10$:	bbs	#v_playback, cli_supplied, 90$
	call queue_async_read

90$:	ret

	.subtitle Display a packet

	.entry -
display_a_packet, ^m<r2,r3,r4,r5,r6,r7,r8,r9,r10,r11>

;++
; Functional Description:
;	Here we display the packet header in a nice formatted way.  Then,
;	based on the display option chosen, display (or not) the contents
;	of the packet in the chosen way.
;	All registers are saved on the way in so that the display routines
;	can have their way with them.
;
;	R0	
;	R1	
;	R2	used to display ASCIC stuff
;	R3	used to display ASCIC stuff
;	R4	address of the byte after the last byte in the packet
;	R5	
;	R6	
;	R7	slot/service counter - loop control
;	R8	address of slot header
;	R9	
;	R10	address of fao parameter list
;	R11	address of the start of the packet data
;
; Calling Sequence:
;	call	display_a_packet
;
; Formal Argument(s):
;	None.
;
; Implicit Inputs:
;	None.
;
; Implicit Outputs:
;	None.
;
; Completion Codes:
;	None
;
; Side Effects:
;	None
;--

	if <bs,#v_playback,cli_supplied> then
	  movaq	rec_time, r0
	else
	clrl	r0
	endif
	$fao_s	ctrstr=display_header, - ; always display the header
		outbuf=_faobuf_ds, -
		outlen=_faobuf, -
		p1=#pkt_source, -
		p2=source_ascic, -
		p3=#pkt_destination, -
		p4=destination_ascic, -
		p5=packet_length, -
		p6=r0
	display	_faobuf

	movab	packet_data, r11	; address of packet data buffer
					;  used by all display routines
	addl3	packet_length, r11, r4	; address not to go past...
	cmpl	pkt_message_type, -	; check for vc header requirements
		#lat_c_command_msg
	bgeq	10$			; yes, don't display the vc header
	cmpl	pkt_message_type, -
		#lat_c_service_msg
	beql	10$
	call	display_vc_header	; no, so look at the vc header

10$:	casel	pkt_message_type, -
		#lat_c_min_valid_message, -
		#lat_c_max_valid_message
dispatch:	.word	100$-dispatch	; run message		0
		.word	101$-dispatch	; start message		1
		.word	102$-dispatch	; stop message		2
		.word	116$-dispatch	; unknown		3
		.word	116$-dispatch	; unknown		4
		.word	116$-dispatch	; unknown		5
		.word	116$-dispatch	; unknown		6
		.word	116$-dispatch	; unknown		7
		.word	116$-dispatch	; unknown		8
		.word	116$-dispatch	; unknown		9
		.word	110$-dispatch	; service info		10
		.word	116$-dispatch	; unknown		11
		.word	112$-dispatch	; command message	12
		.word	113$-dispatch	; status message	13
		.word	114$-dispatch	; solicit info		14
		.word	115$-dispatch	; response info		15
	brb	116$
100$:	call	display_run_message
	brw	exit1
101$:	call	display_start_message
	brw	exit1
102$:	call	display_stop_message
	brw	exit1
110$:	call	display_service_info
	brw	exit1
112$:	call	display_command_message
	brw	exit1
113$:	call	display_status_message
	brw	exit1
114$:	call	display_solicit_message
	brw	exit1
115$:	call	display_response_message
	brw	exit1
116$:	call	display_unknown_message

exit1:	ret

	.subtitle Display the virtual circuit header details

	.entry -
display_vc_header, ^m<>

; First extract all the bits we need...

	moval	faol_prmlst, r10	; address of the parameter list
	movab	message_display, r1	; pointer to message type descr's
	movl	pkt_message_type, r0	; grab the message type
	movl	(r1)[r0], (r10)+	; put it on the parameter list
	movzwl	lat_w_dst_cir_id(r11), (r10)+
	movzwl	lat_w_src_cir_id(r11), (r10)+
	movzbl	lat_b_nbr_slots(r11), (r10)+
	movzbl	lat_b_msg_seq_nbr(r11), (r10)+
	movzbl	lat_b_msg_ack_nbr(r11), (r10)+
	movab	who_display, r0		; assume slave
	bbc	#lat_v_master, -	; see if master bit is set
		lat_b_msg_type(r11), 10$
	addl	#4, r0			;  bit set so must be master
10$:	movl	(r0), (r10)+
	movab	answer_display, r0	; assume no response required
	bbc	#lat_v_rrf, -		; see if flag bit is set
		lat_b_msg_type(r11), 20$
	addl	#4, r0			;  bit set so response required
20$:	movl	(r0), (r10)+

	$faol_s	ctrstr=virt_circ_hdr, - ; always display the vc header
		outbuf=_faobuf_ds, -
		outlen=_faobuf, -
		prmlst=faol_prmlst
	display	_faobuf

	ret

	.subtitle Format and display a RUN message

	.entry -
display_run_message, ^m<>

	movzbl	lat_b_nbr_slots(r11), r9 ; number of slots in this message
	beql	90$			; none, so nothing to do
	movab	slot_c_slot_header(r11), r8 ; address of first header
	movl	#1, r7			; counter for display purposes

10$:	moval	faol_prmlst, r10	; address of fao parameter list
	movl	r7, (r10)+		; that's the counter
	call	display_slot
	incl	r7
	cmpl	r7, r9
	bleq	10$

90$:	ret

	.entry -
display_slot, ^m<>

; Inputs:
;	R8	Address of the slot header
; Outputs:
;	R8	On exit should point to the start of the next slot

	movzbl	slot_b_credits(r8), r0	; contains slot type & credits
	extzv	#slot_v_slot_type, -
		#slot_s_slot_type, -
		r0, slot_type		; that's the slot type
	movab	slot_display, r1	; address of descriptions
	movl	slot_type, r0
	movl	(r1)[r0], (r10)+	; this is the one we want
	movl	slot_type, (r10)+
	movzbl	slot_b_dst_slot_id(r8), (r10)+
	movzbl	slot_b_src_slot_id(r8), (r10)+
	movzbl	slot_b_slot_byte_count(r8), (r10)+
	extzv	#slot_v_credits, -
		#slot_s_credits, -
		slot_b_credits(r8), (r10)+
	$faol_s	ctrstr=slot_header, -
		outbuf=_faobuf_ds, -
		outlen=_faobuf, -
		prmlst=faol_prmlst
	display	_faobuf			; that's the slot header

	casel	slot_type, -		; now do the contents
		#lat_c_min_slot_type, -
		#lat_c_max_slot_type
slot_dispatch:	.word	100$-slot_dispatch	; data a	0
		.word	116$-slot_dispatch	; unknown	1
		.word	116$-slot_dispatch	; unknown	2
		.word	116$-slot_dispatch	; unknown	3
		.word	116$-slot_dispatch	; unknown	4
		.word	116$-slot_dispatch	; unknown	5
		.word	116$-slot_dispatch	; unknown	6
		.word	116$-slot_dispatch	; unknown	7
		.word	116$-slot_dispatch	; unknown	8
		.word	109$-slot_dispatch	; unknown	9
		.word	110$-slot_dispatch	; data b	10
		.word	111$-slot_dispatch	; attention	11
		.word	112$-slot_dispatch	; reject	12
		.word	113$-slot_dispatch	; stop		13
		.word	116$-slot_dispatch	; unknown	14
		.word	116$-slot_dispatch	; unknown	15
	brb	116$
100$:	call	display_data_a_slot
	brw	exit2
109$:	call	display_start_slot
	brw	exit2
110$:	call	display_data_b_slot
	brw	exit2
111$:	call	display_attention_slot
	brw	exit2
112$:	call	display_reject_slot
	brw	exit2
113$:	call	display_stop_slot
	brw	exit2
116$:	call	display_slot_contents

exit2:	movzbl	slot_b_slot_byte_count(r8), r0
	addl2	r0, r8
	addl2	#slot_c_header_size, r8
	pad	r8			; for the next time round

	ret

	.entry -
display_start_slot, ^m<>

	moval	faol_prmlst, r10
	movzbl	slot_b_service_class(r8), (r10)+
	movzbl	slot_b_min_attn_slot_size(r8), (r10)+
	movzbl	slot_b_min_data_slot_size(r8), (r10)+
	$faol_s	ctrstr=start_slot, -
		outbuf=_faobuf_ds, -
		outlen=_faobuf, -
		prmlst=faol_prmlst
	display	_faobuf
	init_item slot_r_start_slot(r8)
	item	obj_srvc_name
	item	subj_dscr

	ret

	.entry -
display_reject_slot, ^m<>
	call display_stop_slot
	ret

	.entry -
display_stop_slot, ^m<>

	moval	faol_prmlst, r10
	movab	slot_stop_display, r1	; address of descriptions
	extzv	#slot_v_reason, -
		#slot_s_reason, -
		slot_b_reason(r8), r0
	movl	r0, (r10)+
	movl	(r1)[r0], (r10)+	; this is the one we want
	$faol_s	ctrstr=stop_slot, -
		outbuf=_faobuf_ds, -
		outlen=_faobuf, -
		prmlst=faol_prmlst
	display	_faobuf

	ret

	.entry -
display_data_a_slot, ^m<>
	call display_slot_contents
	ret

	.entry -
display_data_b_slot, ^m<>
	call display_slot_contents
	ret

	.entry -
display_attention_slot, ^m<>
	call display_slot_contents
	ret

	.entry -
display_slot_contents, ^m<>

	pushl	packet_length
	pushq	packet_data_ds
	movzbl	slot_b_slot_byte_count(r8), -
		packet_length
	movl	packet_length, -
		packet_data_ds
	movl	r8, packet_data_addr
	call	display_packet_contents
	popq	packet_data_ds
	popl	packet_length

	ret

	.subtitle Format and display a START message

	.entry -
display_start_message, ^m<>

	moval	faol_prmlst, r10	; address of parameter list
	movzbl	lat_b_prtcl_ver(r11), (r10)+
	movzbl	lat_b_prtcl_eco(r11), (r10)+
	movzbl	lat_b_prod_type_code(r11), (r10)+
	movzbl	lat_b_prod_vers_numb(r11), (r10)+
	movzwl	lat_w_facility_number(r11), (r10)+
	movzbl	lat_b_srv_circt_tmr(r11), r0
	mull2	#10, r0
	movl	r0, (r10)+
	movzbl	lat_b_keep_alive_timer(r11), (r10)+
	movzwl	lat_w_min_rcv_datagram_size(r11), (r10)+
	movzbl	lat_b_max_sim_slots(r11), (r10)+
	movzbl	lat_b_nbr_dl_bufs(r11), (r10)+
	$faol_s	ctrstr=start_message, -
		outbuf=_faobuf_ds, -
		outlen=_faobuf, -
		prmlst=faol_prmlst
	display	_faobuf

	init_item lat_r_start_data(r11)
	item	slave_node_name
	item	master_node_name
	item	location_text

	ret

	.subtitle Format and display a STOP message

	.entry -
display_stop_message, ^m<>

	movzbl	lat_b_stop_reason(r11), r0 ; grab the stop reason code
	movl	r0, r1			; and save a copy of it
	cmpl	r1, #lat_c_max_valid_stop ; is it valid?
	bleq	10$			; yes
	movl	#lat_c_unknown_stop, r1	; no, so fudge it
10$:	movab	stop_display, r9	; here we get a pointer to some
	movl	(r9)[r1], r1		;  text
	$fao_s	ctrstr=stop_message, -
		outbuf=_faobuf_ds, -
		outlen=_faobuf, -
		p1=r1, -
		p2=r0
	display	_faobuf
	init_item lat_t_stop_reason(r11)
	item	reason_given

90$:	ret

	.subtitle Format and display a SERVICE INFO message

	.entry -
display_service_info, ^m<>

	moval	faol_prmlst, r10	; address of parameter list
	movzbl	lat_b_srv_circt_tmr(r11), r0
	mull2	#10, r0
	movl	r0, (r10)+
	movzbl	lat_b_curr_prtcl_ver(r11), (r10)+
	movzbl	lat_b_curr_prtcl_eco(r11), (r10)+
	movzbl	lat_b_high_prtcl_ver(r11), (r10)+
	movzbl	lat_b_low_prtcl_ver(r11), (r10)+
;	movzbl	lat_b_msg_incarnation(r11) ...?
;	movzbl	lat_b_change_flags(r11) ...?
	movzwl	lat_w_dl_rcv_frame_size2(r11), (r10)+
	movzbl	lat_b_node_multi_timr(r11), (r10)+
	movzbl	lat_b_node_status(r11), (r10)+
	$faol_s	ctrstr=service_message, -
		outbuf=_faobuf_ds, -
		outlen=_faobuf, -
		prmlst=faol_prmlst
	display	_faobuf
	init_item lat_r_service_data(r11)
	movzbl	(r3)+, r0		; skip subj_group ... a bit field
	addl2	r0, r3
	item	node_name
	item	node_desc

	movzbl	(r3)+, r7		; service count
	tstl	r7
	beql	90$
	call	display_services_list

90$:	ret

	.entry -
display_services_list, ^m<>

	$fao_s	ctrstr=service_entries, -
		outbuf=_faobuf_ds, -
		outlen=_faobuf, -
		p1=r7
	display	_faobuf
	clrl	r6			; counter for display purposes
10$:	incl	r6
	$fao_s	ctrstr=service_number, -
		outbuf=_faobuf_ds, -
		outlen=_faobuf, -
		p1=r6
	display	_faobuf
	movzbl	(r3)+, r0		; service rating
	$fao_s	ctrstr=service_status, -
		outbuf=_faobuf_ds, -
		outlen=_faobuf, -
		p1=#0, -
		p2=r0
	display	_faobuf
	item	srvc_name
	item	srvc_desc
	sobgtr	r7, 10$

	ret

	.subtitle Format and display a COMMAND message

	.entry -
display_command_message, ^m<>

	moval	faol_prmlst, r10	; address of parameter list
	movab	command_display, r1	; address of descriptions
	movzbl	lat_b_cmd_type(r11), r0	; grab the command type
	tstb	r0			; is it zero?
	beql	10$			; yes, then it's invalid
	cmpl	r0, -			; no, is it within the maximum valid
		#lat_c_max_valid_command ; value?
	bleq	20$			; yes, so use the value
10$:	movl	#lat_c_unknown_command, r0 ; else fudge it for the description
20$:	movl	(r1)[r0], (r10)+	; now point to the ascic text
	movzbl	lat_b_cmd_type(r11), (r10)+
	movzbl	lat_b_cmd_modifier(r11), (r10)+
	movzbl	lat_b_prtcl_fmt(r11), (r10)+
	movzbl	lat_b_curr_prtcl_ver(r11), (r10)+
	movzbl	lat_b_curr_prtcl_eco(r11), (r10)+
	movzbl	lat_b_high_prtcl_ver(r11), (r10)+
	movzbl	lat_b_low_prtcl_ver(r11), (r10)+
	movzwl	lat_w_dl_rcv_frame_size(r11), (r10)+
	movzwl	lat_w_req_id(r11), (r10)+
	movzwl	lat_w_entry_id(r11), (r10)+
	$faol_s	ctrstr=command_message, -
		outbuf=_faobuf_ds, -
		outlen=_faobuf, -
		prmlst=faol_prmlst
	display	_faobuf
	movzbl	lat_b_cmd_type(r11), r0
	tstb	r0
	beql	30$
	cmpb	r0, #lat_c_max_valid_command
	blequ	40$
30$:	call	display_packet_contents
	brb	90$
40$:	init_item lat_r_cmd_data(r11)
	item	obj_node_name
	movzbl	(r3)+, r0		; skip subj_group ... a bit field
	addl2	r0, r3
	item	subj_node_name
	item	subj_port_name
	item	subj_dscr
	item	obj_srvc_name
	item	obj_port_name

90$:	ret

	.subtitle Format and display a STATUS message

	.entry -
display_status_message, ^m<>

	moval	faol_prmlst, r10	; address of parameter list
	movzbl	lat_b_prtcl_fmt(r11), (r10)+
	movzbl	lat_b_curr_prtcl_ver(r11), (r10)+
	movzbl	lat_b_curr_prtcl_eco(r11), (r10)+
	movzbl	lat_b_high_prtcl_ver(r11), (r10)+
	movzbl	lat_b_low_prtcl_ver(r11), (r10)+
	movzwl	lat_w_dl_rcv_frame_size(r11), (r10)+
	movzwl	lat_w_status_retrans_timer(r11), (r10)+
	movzbl	lat_b_entries_counter(r11), (r10)+
	$faol_s	ctrstr=status_message, -
		outbuf=_faobuf_ds, -
		outlen=_faobuf, -
		prmlst=faol_prmlst
	display	_faobuf
	init_item lat_r_status_data(r11)
	item	subj_node_name
	movzbl	lat_b_entries_counter(r11), r9
	tstl	r9			; how many entries
	beql	90$			; none, so bail out
	call	display_status_entries

90$:	ret

	.entry -
display_status_entries, ^m<>

	$fao_s	ctrstr=status_entries, -
		outbuf=_faobuf_ds, -
		outlen=_faobuf, -
		p1=r9
	display	_faobuf
10$:	pad	r3
	movzbl	(r3), r0		; entry length, not used by us
	moval	faol_prmlst, r10	; address of parameter list
	movzbl	entry_b_status(r3), (r10)+
	movzbl	entry_b_error(r3), (r10)+
	movzwl	entry_w_request_id(r3), (r10)+
	movzwl	entry_w_entry_id(r3), (r10)+
	movzwl	entry_w_elapsed_queue_time(r3), (r10)+
	movzwl	entry_w_min_queue_position(r3), (r10)+
	movzwl	entry_w_max_queue_position(r3), (r10)+
	$faol_s	ctrstr=entry_header, -
		outbuf=_faobuf_ds, -
		outlen=_faobuf, -
		prmlst=faol_prmlst
	display	_faobuf
	init_item entry_r_entry_data(r3)
	item	obj_srvc_name
	item	obj_port_name
	item	subj_dscr
	decl	r9
	bleq	90$
	brw	10$

90$:	ret

	.subtitle Format and display a SOLICIT INFO message

	.entry -
display_solicit_message, ^m<>

	moval	faol_prmlst, r10	; address of parameter list
	movzbl	lat_b_prtcl_fmt(r11), (r10)+
	movzbl	lat_b_curr_prtcl_ver(r11), (r10)+
	movzbl	lat_b_curr_prtcl_eco(r11), (r10)+
	movzbl	lat_b_high_prtcl_ver(r11), (r10)+
	movzbl	lat_b_low_prtcl_ver(r11), (r10)+
	movzwl	lat_w_dl_rcv_frame_size(r11), (r10)+
	movzwl	lat_w_solicit_id(r11), (r10)+
	movzwl	lat_w_response_timer(r11), (r10)+
	$faol_s	ctrstr=solicit_message, -
		outbuf=_faobuf_ds, -
		outlen=_faobuf, -
		prmlst=faol_prmlst
	display	_faobuf
	init_item lat_r_solicit_data(r11)
	item	dst_node_name
	movzbl	(r3)+, r0		; skip subj_group ... a bit field
	addl2	r0, r3
	item	src_node_name
	item	dst_srvc_name

	ret

	.subtitle Format and display a RESPONSE INFO message

	.entry -
display_response_message, ^m<>

	moval	faol_prmlst, r10	; address of parameter list
	movzbl	lat_b_prtcl_fmt(r11), (r10)+
	movzbl	lat_b_curr_prtcl_ver(r11), (r10)+
	movzbl	lat_b_curr_prtcl_eco(r11), (r10)+
	movzbl	lat_b_high_prtcl_ver(r11), (r10)+
	movzbl	lat_b_low_prtcl_ver(r11), (r10)+
	movzwl	lat_w_dl_rcv_frame_size(r11), (r10)+
	movzwl	lat_w_solicit_id(r11), (r10)+
	movzwl	lat_w_response_status(r11), (r10)+
	movzwl	lat_w_src_node_status(r11), (r10)+
	movab	lat_r_source_node_addr(r11), r0
	movzbl	(r0)+, (r10)+		; ethernet address... 6 bytes
	movzbl	(r0)+, (r10)+
	movzbl	(r0)+, (r10)+
	movzbl	(r0)+, (r10)+
	movzbl	(r0)+, (r10)+
	movzbl	(r0)+, (r10)+
	movzwl	lat_w_node_mc_timer(r11), (r10)+
	$faol_s	ctrstr=response_message, -
		outbuf=_faobuf_ds, -
		outlen=_faobuf, -
		prmlst=faol_prmlst
	display	_faobuf
	init_item lat_r_response_data(r11)
	item	dst_node_name
	movzbl	(r3)+, r0		; skip subj_group ... a bit field
	addl2	r0, r3
	item	src_node_name
	item	src_node_desc

	movzbl	(r3)+, r7		; service count
	tstl	r7
	beql	90$
	call	display_service_entries

90$:	ret

	.entry -
display_service_entries, ^m<>

	$fao_s	ctrstr=service_entries, -
		outbuf=_faobuf_ds, -
		outlen=_faobuf, -
		p1=r7
	display	_faobuf
10$:	movzbl	(r3)+, r0		; service entry length, not used by us
	item	srvc_class
	moval	faol_prmlst, r10	; address of parameter list
	movzbl	(r3)+, (r10)+		; service status
	movzbl	(r3)+, (r10)+		; service rating
	$faol_s	ctrstr=service_status, -
		outbuf=_faobuf_ds, -
		outlen=_faobuf, -
		prmlst=faol_prmlst
	display	_faobuf
	movzbl	(r3)+, r0		; skip subj_group ... a bit field
	addl2	r0, r3
	item	srvc_name
	item	srvc_desc
	sobgtr	r7, 10$

	ret

	.subtitle Format and display an UNKNOWN message

	.entry -
display_unknown_message, ^m<>

	$fao_s	ctrstr=unknown_message, -
		outbuf=_faobuf_ds, -
		outlen=_faobuf, -
		p1=pkt_message_type
	display	_faobuf
	call	display_packet_contents

	ret

	.subtitle Display a counted byte field with description

	.entry -
display_desc_ascic, ^m<>

; Inputs:
;	R2	Address of .ascic string to use as a description
;	R3	Address of the item to display
;	R4	Address of the byte after the last byte in the packet
; Outputs:
;	R3	Is updated to point to the next (or past the last) item

	tstb	(r3)			; anything to display
	beql	20$			; no so don't...
	movl	r3, r0
	movzbl	(r0), r1
	addl2	r0, r1
	cmpl	r1, r4			; at end of packet yet?
	blssu	10$			; not yet
	movab	bad_item, r0		; yes, say it's bad
10$:	$fao_s	ctrstr=item_format, -
		outbuf=_faobuf_ds, -
		outlen=_faobuf, -
		p1=r2, -
		p2=r0
	display	_faobuf
20$:	movzbl	(r3)+, r0		; grab the byte count
	addl2	r0, r3			;  and update R3 to point to the start
					;  of the next field (if any)
	ret

	.subtitle Display the packet contents according to their wishes

	.entry -
display_packet_contents, ^m<>

; Display the packet based on what /display option was chosen.

	if <neq,#display_c_none,display_option> then
	  if <eql,#display_c_ascii,display_option> then
	    pushal	ascii_segment_size
	    pushal	packet_length
	    pushaq	packet_data_ds
	    calls	#3, g^lib_output_seg_t
	  else
	  if <eql,#display_c_all,display_option> then
	    pushal	all_segment_size
	    pushal	packet_length
	    pushaq	packet_data_ds
	    calls	#3, g^lib_output_seg_tzb
	  else
	  if <eql,#display_c_hex,display_option> then
	    pushal	hex_segment_size
	    pushal	packet_length
	    pushaq	packet_data_ds
	    calls	#3, g^lib_output_seg_zb
	  else
	  $fao_s ctrstr=fast_format, -
		outbuf=_faobuf_ds, -
		outlen=_faobuf, -
		p1=packet_length , -
		p2=#packet_data
	  display _faobuf
	  endif ;<eql,#display_c_hex,display_option> then
	  endif ;<eql,#display_c_all,display_option> then
	  endif ;<eql,#display_c_ascii,display_option> then
	endif ;<neq,#display_c_none,display_option> then

	ret

	.subtitle Routines to handle the recording/playback file

	.entry -
create_record_file, ^m<>

	movab	record_t, rec_fab+fab$l_fna
	movb	record, rec_fab+fab$b_fns
	movb	#fab$m_put, rec_fab+fab$b_fac
	$create	fab=rec_fab
	if <lbc,r0> then
	  signal code=r0
	endif
	$connect rab=rec_rab
	if <lbc,r0> then
	  signal code=r0
	endif

	ret

	.entry -
open_record_file, ^m<>

	movab	record_t, rec_fab+fab$l_fna
	movb	record, rec_fab+fab$b_fns
	movb	#fab$m_get, rec_fab+fab$b_fac
	$open	fab=rec_fab
	if <lbc,r0> then
	  signal code=r0
	endif
	$connect rab=rec_rab
	if <lbc,r0> then
	  signal code=r0
	endif

	ret

	.entry -
close_record_file, ^m<>

	$close	fab=rec_fab

	ret

	.entry -
record_a_packet, ^m<>

	movb	#rec_binary, rec_type
	$gettim_s -
		timadr=rec_time
	addl3	packet_length, #rec_hdrsize, r0
	addl2	#lanhdr_s_lanhdrdef, r0
	movw	r0, rec_rab+rab$w_rsz
	$put	rab=rec_rab
	if <lbc,r0> then
	  signal code=r0
	endif

	ret

	.end	latwatch
