Trailing-Edge - PDP-10 Archives - decuslib20-10 - decus/20-185/xmit.mac
There are no other files named xmit.mac in the archive.
;Edit number 32 by SST.D-BIGELOW on  7-Nov-84
;	Add feature test LWrite so that vaxmit can be assembled without
;	support for last writer string.
;Edit number 31 by SST.D-BIGELOW on  2-Nov-84
;	Fix bug where second attempt to delete a file would jump off into
;	literal space in DELFIL: routine.
;Edit number 30 by SST.D-BIGELOW on 25-Oct-84
;	Remove ASND call to get I/O port, since it hinders debugging when
;	a system fork is killed and port ownership is retained.  Also change
;	feature test switches.
;Edit number 27 by SST.D-BIGELOW on 24-Oct-84
;	Remove GNJFN call in file sending routine.  It seems to malfunction
;	after a deletion of the file it refers to, causing many race
;	conditions.
;Edit number 26 by SST.D-BIGELOW on 24-Oct-84
;	Insert last writer string into filename packet, to preserve file
;	author names across the network.
;Edit number 25 by SST.D-BIGELOW on  7-Sep-84
;	Make XMIT handle eight bit files.
;Edit number 24 by SST.D-BIGELOW on  4-Sep-84
;	Break program into header and main file, and explicitly specify which
;	system each version is to talk to.
;     * Begin minor version 3.
;Edit number 23 by SST.D-BIGELOW on  2-Aug-84
;	Add code to detect DDT startup and not hang awaiting LF after
;	rescan buffer is empty.
;Edit number 22 by SST.D-BIGELOW on  1-Aug-84
;	Correct bug in nak counting caused by use of T4 in INPACK routine.
;Edit number 21 by SST.D-BIGELOW on  1-Aug-84
;	Add routine to clear input and output buffer of transmit line after
;	a nak or retransmit.  Try to prevent nak cascading.
;Edit number 20 by SST.D-BIGELOW on 24-Jul-84
;	Inprove error handling of NAK packets.
;Edit number 17 by SST.D-BIGELOW on 24-Jul-84
;	Make the INPACK routine re-shuffle the packet so that it is always
;	alligned with PACKET despite preceeding nulls.  Handle the quoting
;	internally to use SOUT rather than BOUT later on.
;     * Begin minor version 2.
;Edit number 16 by SST.D-BIGELOW on 23-Jul-84
;	Remove debugging conditional, and make debugging output always
;	available by switch.  Make logging settable by feature test but let
;	it be set by switch too.
;Edit number 15 by SST.D-BIGELOW on 20-Jul-84
;	Add /S switch to start with sending rather than receiving.  Used
;	during debugging.
;Edit number 14 by SST.D-BIGELOW on 20-Jul-84
;	Add packet number or ack/nak codes to ack/nak packets.	Avoid
;	problems getting out of sync.
;     * Begin minor version 1.
;Edit number 13 by SST.D-BIGELOW on 19-Jul-84
;	Yet more error reporting in receiving routine -- report the difference
;	between packet received and packet expected.
;Edit number 12 by SST.D-BIGELOW on 19-Jul-84
;	Change available byte sizes from eight and six to seven and six.
;	Exchanging bytes with the VAX is only profitable in seven-bit mode.
;Edit number 11 by SST.D-BIGELOW on 18-Jul-84
;	Include complete error logging if log file is on.  Report the cause
;	of all error aborts.
;Edit number 10 by SST.D-BIGELOW on 16-Jul-84
;	Include VAX Xmit support.  If on KLA, decide whether we're talking to
;	KLB or VAX and open appropriate port.
;Edit number 7 by SST.D-BIGELOW on 13-Jul-84
;	Tidy up abort processing -- make low level routines always decide
;	when to clear a jfn rather than leaving it to the main loop.
;Edit number 6 by SST.D-BIGELOW on 12-Jul-84
;	Make sure F.Abrt is always cleared in main loop.
;Edit number 5 by SST.D-BIGELOW on 10-Jul-84
;	Add ack and nak logging, with name of file, to SY:XMIT.LOG.
;Edit number 4 by SST.D-BIGELOW on 10-Jul-84
;	Change again to make count and checksum and packet number use offsets
;	rather than parity.  Must avoid sending 0 or 200 characters, since
;	they seem very prone do dropping at high speeds.
;Edit number 3 by SST.D-BIGELOW on 10-Jul-84
;	Change checksum and count to use parity bit instead of offset.
;Edit number 2 by SST.D-BIGELOW on  6-Jul-84
;	First complete version of program, ready for first stage
;	debugging.  No debugging aids yet, and poor error recovery.
Subttl	Table of contents for Xmit

;	   -- Section --					   -- Page --
;  1.   Edit history.................................................. 1
;  2.	Table of contents............................................. 2
;  3.	Program Initialization........................................ 3
;  4.	Priority Interrupt System routines............................ 8
;  5.	Interrupt processing routines................................. 9
;  6.	Open and close port for I/O................................... 10
;  7.	Start of main program......................................... 11
;  8.	Getpkt - Check for incoming packets........................... 14
;  9.	Inpack - routine to check and valididate incoming packets..... 15
;  10.	Ack, Nak and Can routines..................................... 18
;  11.	Packet control routines....................................... 19
;  12.	File receiving routine........................................ 20
;  13.	Incoming data packet handling................................. 21
;  14.	Chkfil - Send outgoing files.................................. 23
;  15.	Sndpak - loop sending data packets............................ 25
;		     (End of table of contents)
Subttl Program Initialization

;Version information
	Vmajor==1			; Major version
	Vminor==3			; Minor version
	Vedit==32			; Edit number
	Vcust==4			; WCC edit code

	Pgver.	(VMajor,VMinor,VEdit,VCust)


;Program feature test switches

	Omit	Comman			;no comnd jsys routines
	Omit	Memory			;no need for memory management
	Omit	Sorter			;or sorting either
	Omit	String			;no string package

	Include F..UUO			; Local uuo features
					; If included, requires UUOCON.REL

;*			===   Abstract	 ===
;* Written by SST.D-BIGELOW on 02-Jul-84
;* Description: This program runs on a DEC-20 to automatically handle file
;*	transfers between systems.  It reads files from OUTBOX: and sends
;*	them across the line, and puts incoming files into INBOX:.
;Protocol explanation

Comment	%

There are six possible types of packets, as follows:

	A	Ack a packet (standard good response)
	B	Nak a packet (standard response to bad packet)
	C	Cancel transaction (no response appropriate)
	D	Data packet (requires A, B or C back)
	E	EOF packet (requires A, B or C back)
	F	File init packet (requires A, B or C back)

;Proper packet format is:

	[SOP]  [CODE]  [COUNT]	[D1] .. [Dn]  [CHK]  [EOP]

	SOP == start of packet, ascii code 1 (^A)
	CODE == type of packet, A through F
	COUNT == number of data packets, 0 through MAXPKT-n
	D1..Dn == data packet, numbered in sequence
	CHK == seven bit checksum
	EOP == end of packet, ascii code 2 (^B)

The COUNT and the CHK values are constrained to be 7 bits, from 0 - 177.
To avoid problems with ascii codes 0-37, these values are translated to
the values 240-277.  The count is added to the checksum before this
translation takes place, and the checksum includes all bytes after SOP.

In data packets, values in the range 0-2 or 200-202 are quoted, with a
quote byte ("\") before and octal 40 added to the character itself,
coming after.  A sequence "\\" gives a single "\".

;Symbols and storage

;Abort macro

Define	Abort(ax),<
	jrst	[skpoff f.log		;;logging?
		  print asc<%P ax %C>	;;yes, print message
		 jrst	p.can]		;;and cancel packet

Define	JAbort(ax),<
	erjmp	[skpoff f.log		;;logging?
		  print asc<%P ax -- %E%C>	;;yes, print message
		 jrst	p.can]		;;and cancel packet

Define	GAbort(ax),<
	jrst	[skpoff f.log		;;logging?
		  print asc<%P ax %C>	;;yes, print message
		 jrst	clrfil] 	;;clear file and return
;Switches and locations

Switch	F.Ieof				;input end of file
Switch	F.Abrt				;aborting (can)
Switch	F.Dbug				;debugging
Switch	F.Log				;information logging
Switch	F.Send				;start with send (debugging)

;Feature tests

	Ifndef	Delay,<Delay==^D45000>	;Delay time
	Ifndef	Logit,<Logit==1>	;Logging on
	Ifndef	LWrite,<LWrite==1>	;last writer string written

Dsend==delay * 2 / 3			;sending delay 2/3 of regular delay
Dwait==^D20000				;20 seconds for data wait

Loging:	exp	logit			;state of default logging

Maxpkt==^D100				;maximum packet size
Maxsnd==maxpkt/2-6			;maximum send size, assuming quoting
Blksiz==maxpkt/4+1			;storage for packets
Maxnak==^D12				;maximum naks before aborting
Quote=="\"				;Quote character
;More symbols

	sop==1				;start of packet
	eop==2				;end of packet
	ack==1				;begin "A" offset
	nak==2				;nak
	can==3				;cancel or abort transaction
	dat==4				;data packet
	eof==5				;end of file
	sof==6				;start of file

Code:	block	1			;ack code
Dat1:	block	1			;first data item
Intmsk: block	1			;interrupt mask word
Intblk: block	3			;interrupt levels
Numack: block	1			;number of acks per file
Numnak: block	1			;number of naks per file
Nakcnt:	block	1			;count of naks received
Pktnum: block	1			;packet number
Chksum: block	1			;checksum
Logjfn: block	1			;logging file
Ccoc:	block	2			;ccoc words
Data:	block	blksiz			;incoming file characters
Outpkt: block	blksiz			;outgoing data
Packet: block	blksiz*2		;incoming data

;**; Filename and writer strings are cleared together -- don't separate
Filnam: block	24			;filename block
Writer:	block	24			;last writer block
	strlen==.-filnam-1		;size to clear

Stak..: block	stksiz			;standard stack


;Priority interrupt storage

Chntab: xwd	1,ctrlc 		;channel 1 for control-C
	xwd	2,timint		;channel 2 for timer interrupts

Levtab: exp	intblk,intblk+1,intblk+2
Subttl Priority Interrupt System routines

;Routine to initialize the pi system

Inipi:	movei	a,.fhslf		;init levtab and chntab
	move	b,[levtab,,chntab]
	movei	a,.fhslf		;turn on desired channels
	movx	b,1B0!1B1		;channels 0 and 1
	movei	a,.fhslf		;enable interrupt system
	movei	a,.fhjob		;job code
	movem	b,intmsk		;save mask
	movx	b,1B3			;control-C
	 erjmp	.+1			;ignore errors
	move	a,[3,,0]		;enable for control-c
	ati%				;to channel zero
	 erjmp	.+1			;ignore errors
	movei	a,.cttrm		;this tty
	rfcoc%				;read our coc words
	dmovem	b,ccoc			;save them
	dmove	b,[252525,,313125	;all codes to ^ format except crlf
	sfcoc%				;set coc words
	ret				;done

;Routine to disable the pi system

Dispi:	movei	a,.fhslf		;disable the pi system
	movei	a,.fhjob		;this job
	move	b,intmsk		;interrupt mask
	stiw%				;reset terminal interrupt mask
	 erjmp .+1
	movei	a,.fhslf		;disable all channels
	movei	b,0
	movei	a,.cttrm		;this terminal
	dmove	b,ccoc			;get old ccoc words
	sfcoc%				;set them back
	ret				;done
Subttl Interrupt processing routines

;Here to intercept a ^C

Ctrlc:	push	p,a			;save acs
	push	p,b
	push	p,c
	call	clsprt			;close the port
	call	dispi			;and clear pi system
	haltf%				;and exit

;If continued..

	call	inipi			;set up pi
	call	opnprt			;open port again
	pop	p,c			;restore acs
	pop	p,b
	pop	p,a
	debrk%				;dismiss the interrupt

;Here for a timer interrupt

Timint: push	p,a			;save an ac
	hrrz	a,intblk+1		;get interrupt pc from location two
	caie	a,intpc 		;are we at interrupt pc?
	ifskp.				;yes
	  movx	a,1B5			;user mode bit
	  iorm	a,intblk+1		;set user mode to return
	pop	p,a
	debrk%				;and dismiss
Subttl Open and close port for I/O


;(30)	movei	a,port	 		;get port to use
;(30)	asnd%				;try to assign it
;(30)	 dblerr	(<Can't assign system I/O port>,EXIT)
	movx	a,gj%sht		;now get jfn
	hrroi	b,aport			;ascii port name
	 dblerr (<Can't get jfn on system I/O port>,EXIT)
	movem	a,p1			;store jfn
	movx	b,fld(10,of%bsz)!of%rd!of%wr
	openf%				;open the jfn
	 dblerr (<Can't open the I/O port>,EXIT)
	rfmod%				;get jfn mode word
	txz	b,tt%pgm		;disable ^S/^Q mode
	stpar%				;set mode
	 erjmp	.+1

	skipn	loging			;are we logging?
	ifskp.				;yes
	 movx	a,gj%sht!gj%fou 	;new generation
	 bpm	b,syslog		;log file to use
	  erjmp	r			;can't
	 movx	b,fld(7,of%bsz)!of%wr	;writing
	 openf%				;open it
	  erjmp	r			;can't
	 movem	a,logjfn		;save jfn
	 setjfn	(a)			;set this print jfn
	 flgon	f.log			;set flag
	ret				;done
;Here to close the port and the I/O jfn

Clsprt: move	a,p1			;get tty jfn
	rfmod%				;get jfn mode word
	txo	b,tt%pgm		;enable ^S/^Q mode
	stpar%				;set mode
	 erjmp	.+1
	move	a,p1			;get jfn
	txo	a,cz%abt		;abort close
	closf%				;do it
	 erjmp	.+1			;ignore errors
;(30)	movei	a,port	 		;default port
;(30)	reld%				;deassign it
;(30)	 erjmp	.+1			;ignore errors
	move	a,logjfn		;get logging jfn
	skpoff	f.log			;open?
	 closf% 			;yes, close it
	jfcl				;ignore errors
	ret				;done

;Here to clear the port I/O line after an error

Clrprt:	move	a,p1			;get i/o port
	cfibf%				;clear input buffer
	 erjmp	.+1			;ignore errors
	move	a,p1			;just in case
	cfobf%				;clear output buffer
	 erjmp	.+1			;just in case
	movei	a,^D1000		;one second
	disms%				;pause
	ret				;and return
Subttl Start of main program

Xmit:	reset%
	setz	f,			;flags
	stack	stak..			;stack
	movei	a,.fhslf		;set this fork's capabilities
	rpcap%				;read them first
	txnn	c,sc%whl!sc%opr 	;must be able to set whoper
	 noerr	(<? Insufficient capabilities to run program>,EXIT)
	call	inipi			;set interrupts

	movei	a,.rsini		;get our rescan buffer
	 erjmp	.+1
	movei	a,.rscnt		;now get count
	rscan%				;do it
	 setz	a,			;none to read
	jumpe	a,setprt		;nothing to rescan, proceed

Xmi.a:	pbin%				;now look for switch
Xmi.b:	cain	a,12			;end of line?
	 jrst	setprt			;yes, proceed
	caie	a,"/"			;switch?
	 jrst	xmi.a			;nope
	pbin%				;get next
	andi	a,137			;turn off upper case
	cain	a,"D"			;debug?
	 flgon	f.dbug			;yes
	cain	a,"L"			;logging?
	 setom	loging			;yes, set it
	cain	a,"S"			;start with send?
	 flgon	f.send			;yes
	jrst	xmi.b			;keep looping

;Set up the port

Setprt: call	opnprt			;open the port
	seto	p2,			;initialize to no files
;Main processing loop

	clrsks	f.send			;skip getpkt if /S specified
Main:	call	getpkt			;await incoming packets
	clrskp	f.abrt			;aborting?
	 jrst	main			;yes, retry
	call	chkfil			;check for incoming files
	jrst	main			;do it forever

;Routine to clear open files
Clrfil: skipg	a,p2			;any open files?
	 ret				;nope
	txo	a,cz%abt		;abort close
	closf%				;close the file
	 erjmp	.+1			;ignoring errors
Subttl Getpkt - Check for incoming packets

;First level of processing -- wait for incoming characters for up to five
;minutes before returning.

Getpkt: movei	b,delay 		;first delay
	call	inpack			;input a packet
	jrst	@disp1(p3)		;dispatch to proper routine

;Dispatch table

Disp1:	exp	r			;invalid packet
	exp	r			;ack
	exp	r			;nak
	exp	r			;cancel
	abort	<Received Data packet without init>
	abort	<Received EOF packet without init>
	exp	p.sof			;start of file
Subttl Inpack - routine to check and valididate incoming packets

;We check type, contents and checksum before returning a valid packet code.
; Packets are returned in PACKET, code in P3, count in P4.
; Call this routine with timeout period in B.

Inpack: setzb	p3,t2			;assume bad packet code
	move	a,[.fhslf,,.timel]	;elapsed time
	movei	c,1			;channel 1
	timer%				;set interval timer
	 dblerr (<Can't set timer interrupt>,EXIT)

;Read in a line
	move	a,p1			;input jfn
	move	b,[point 8,packet]	;pointer
	movei	c,maxpkt*2		;maximum size
	movei	d,eop			;end of packet character
	sin%				;read it

;INTPC is sacred... the timer interrupt trap looks for the PC set to
; this location and continues the JSYS if found there.	This makes the
; SIN return even if not completed.

Intpc:	 jfcl				;wait here
	move	a,[.fhslf,,.timal]	;remove all requests
	timer%				;cancel
	 erjmp	.+1			;ignore errors

;Analyze the line read in
	cain	c,maxpkt*2		;anything happen?
	 ret				;nope
	idpb	d,b			;make sure packet ends with an eop
	move	t1,[point 8,packet]	;load packet pointer
	move	t4,t1			;copy pointer

;Print data if appropriate

	ifon.	f.dbug			;debugging?
	 typncr <
Incoming: >				;type prompt
	 movei	a,.priou		;output
	 movx	c,no%lfl!fld(4,no%col)!10	;how to type it
	 move	t3,[point 8,packet]	;source
	 ildb	b,t3			;get a byte
	 nout%				;type it
	  erjmp .+1
	 caie	b,eop			;end?
	  jrst	.-4			;nope, loop
;Find a sop character

Inpk.a: ildb	b,t1			;get char
	cain	b,eop			;end of packet?
	 jrst	inpk.e			;error
	caie	b,sop			;start of packet?
	 jrst	inpk.a			;nope, keep looking
	idpb	b,t4			;redeposit

;Here if we have a packet start.  Get code character
	ildb	b,t1			;next
	idpb	b,t4			;redeposit
	move	t2,b			;copy into checksum
	subi	b,100			;make into an offset
	jumple	b,inpk.e		;return if too low
	caile	b,sof			;or if too high
	 jrst	inpk.e			;error

;Get the count of remaining characters
	ildb	t3,t1			;count of characters remaining
	idpb	t3,t4			;redeposit
	cail	t3,240			;value offset?
	 subi	t3,240			;yes, make true value
	jumpl	t3,inpk.e		;check for too small
	caile	t3,maxpkt		;check for too large
	 jrst	inpk.e			;error
	move	p4,t3			;copy the count for later use
	add	t2,t3			;add into checksum
	sojl	t3,inpk.c		;count down
	ildb	a,t1			;get first data character
	idpb	a,t4			;deposit it
	add	t2,a			;add to checksum
	movem	a,dat1			;and store it
;Loop checksuming the data

Inpk.b: sojl	t3,inpk.c		;count down
	ildb	a,t1			;get a character
	add	t2,a			;add it in
	caie	a,quote			;quoted?
	ifskp.				;yes
	  ildb	a,t1			;get next character
	  add	t2,a			;add to checksum
	  subi	a,40			;remove offset
	  soj	t3,			;count down
	  soj	p4,			;also change permanent count
	idpb	a,t4			;replace in new position
	jrst	inpk.b			;and loop

;Now check the checksum
Inpk.c: andi	t2,177			;reduce to seven bits
	caige	t2,40			;printable?
	 addi	t2,240			;no, make it so
	ildb	a,t1			;get checksum
	came	a,t2			;match?
	 jrst	inpk.e			;error

;Finally, the EOP character should follow
	ildb	a,t1			;get last char
	caie	a,eop
	 jrst	inpk.e			;error
	move	p3,b			;copy the code
	ifon.	f.dbug			;debugging?
	 type	<  (good)>
	ret				;return success

Inpk.e:	ifon.	f.dbug			;debugging?
	 type	<  (bad)>
	ret				;return failure
Subttl Ack, Nak and Can routines

P.Ack:	aos	numack			;bump ack number
	movei	p3,"A"			;packet code
P.All:	movei	p4,1			;one data
	call	pinit			;packet init
	move	a,code			;get packet code
	idpb	a,t1			;store the packet code
	add	a,chksum		;add in
	andi	a,177			;make seven bits
	caige	a,40			;printable?
	 addi	a,240			;no, convert it
	idpb	a,t1			;store it
	movei	a,eop			;end of packet
	idpb	a,t1			;store it
	call	outsnd			;send it out
	ret				;and return

;Nak routine -- called as a response to anything that you
; don't understand.

P.Nak:	aos	numnak			;bump nak count
	movei	p3,"B"			;packet code
	setom	code			;no special code for nak
	jrst	p.all			;send it

;Cancel routine -- Called to send an abort packet across the line.

P.Can:	call	clrfil			;close the file
P.Can2: movei	p3,"C"			;packet code
	setom	code			;no special code for can
	call	p.all			;send it
	skpoff	f.log			;logging?
	 call	endf.a			;yes, close the log file
	time%				;get a random number
	andi	a,1777			;10 bits
	lsh	a,4			;shift to range of up to 16 seconds
	disms%				;random wait
	flgon	f.abrt			;aborting...
	ret				;done
Subttl Packet control routines

;Pinit routine -- prepare an outgoing packet
; Leaves T1 set up as a byte pointer

Pinit:	move	t1,[point 8,outpkt]	;point to packet area
	movei	a,sop			;packet start
	idpb	a,t1
	idpb	p3,t1			;deposit packet code
	movem	p3,chksum		;start the checksum
	move	a,p4			;count of data
	addm	a,chksum		;add to checksum
	caige	a,40			;printable?
	 addi	a,240			;no, make it so
	idpb	a,t1			;add it to packet
	ret				;done

;Outsnd routine -- send the packet

Outsnd: move	a,p1			;I/O jfn
	move	b,[point 8,outpkt]	;pointer to data
	movei	c,maxpkt		;maximum size
	movei	d,eop			;end of packet
	sout%				;send it

	ifon.	f.dbug			;debugging?
	 typncr <
Outgoing: >				;tell user
	 movei	a,.priou		;output
	 movei	a,.priou		;output
	 movx	c,no%lfl!fld(4,no%col)!10	;how to type it
	 move	t3,[point 8,outpkt]	;source
	 ildb	b,t3			;get a byte
	 nout%				;type it
	  erjmp .+1
	 caie	b,eop			;end?
	 jrst	.-4			;nope, loop
	 type				;crlf

	ret				;done
Subttl File receiving routine

;This routine is called for a start of file packet.  Open a file with the
; proper name and then await the arrival of data packets.

P.Sof:	move	a,[filnam,,filnam+1]	;set up to blt zeroes
	setzm	filnam			;clear first word
	blt	a,filnam+strlen		;through writer string
	bpl	a,<INBOX:>		;source
	move	b,[point 7,filnam]	;destination
	movei	c,6			;count
	ildb	d,a			;get a byte
	idpb	d,b			;deposit it
	sojg	c,.-2			;loop
	move	a,[point 8,packet+1]	;source
	sosg	c,p4			;count (minus bytesize char)
	 abort	<Received inadequately sized data packet>

Psof1:	ildb	d,a			;get a byte
	cain	d,"/"			;name/writer separator?
	 skipa	b,[point 7,writer]	;yes, write to new location
	idpb	d,b			;deposit it
	sojg	c,psof1			;loop

;Open a file
	movx	a,gj%sht!gj%fou 	;new file
	hrroi	b,filnam		;name
	gtjfn%				;get jfn
	 erjmp	[skpoff f.log		;logging?
		  print asc<%C%P Unable to GTJFN incoming file -- %E%C>
		 jrst	p.can2] 	;cancel
	movem	a,p2			;store jfn
	skpoff	f.log			;logging?
	 print	a,asc<%CReceiving file %J at %D...%C>	;yes
	move	b,dat1			;get the byte size
	lsh	b,^d30			;shift into proper place
	txo	b,of%wr 		;write the file
	openf%				;open it
	 jabort <Can't open incoming file>
	hrli	a,.sflwr		;set name of last writer
	hrroi	b,writer		;string
	skipe	writer			;anything there?
	 sfust%				;yes, set string
	setzm	numack			;clear acks and naks
	setzm	numnak

;Ack the sof packet
	movei	a,"F"			;file init
	movem	a,code			;store it
	call	p.ack			;send a response
	setzm	pktnum			;initialize packet number
	jrst	datpak			;and get first packet
Subttl Incoming data packet handling

Datpak:	movei	t4,maxnak		;maximum number of naks
	movem	t4,nakcnt		;store them
	jrst	dtp.a1			;skip countdown
Datp.a:	sosg	nakcnt			;count down
	 jrst	nakout			;reached the limit
Dtp.a1:	movei	b,dwait 		;data wait
	call	inpack			;get a packet
	jrst	@disp2(p3)		;dispatch on answer

;Good response, process incoming data
Datp.b:	move	a,dat1			;get packet number
	movem	a,code			;store as ack/nak code
	cail	a,240			;value offset?
	 subi	a,240			;yes, make true value
	move	b,pktnum		;get packet number
	camge	a,b			;compare packet numbers
	 jrst	datpc1			;obsolete, ack it
	camg	a,b			;too large this time?
	 jrst	dtp.b1			;nope, continue
	jumpe	b,datpc1		;catch obsolete during 128-0 wrap
	skpon	f.log			;logging?
	 jrst	p.can			;nope, just cancel
	print	b,asc<%P Expecting packet %N but received packet >
	print	a,asc<%N%C>		;show discrepancy
	jrst	p.can			;now cancel

;Right packet number, output contents
Dtp.b1:	aos	t3,pktnum		;bump packet count
	andi	t3,177			;make 7-bit only
	movem	t3,pktnum		;restore
	sosg	t1,p4			;get count
	 jrst	datpc2			;no data
	movn	c,t1			;get negative count
	move	b,[point 8,packet+1]	;point to data
	move	a,p2			;get file jfn
	sout%				;output the data
	jrst	datpc2			;done

;Here to ack and get next
Datpc1:	call	clrprt			;clear the line
	call	p.ack			;ack obsolete packet
	jrst	datp.a			;but count it with naks

Datpc2: call	p.ack			;ack it
	jrst	datpak			;and get next
;End of file and error handling

Datp.d: move	b,pktnum		;get number
	move	a,dat1			;get packet number
	movem	a,code			;save code
	cail	a,240			;value offset?
	 subi	a,240			;yes, remove offset added before
	came	a,b			;compare packet numbers
	 abort	<Skipped an incoming packet before EOF received>
	move	a,p2			;get file jfn
	closf%				;close the file
	 jabort <Can't close incoming file>
	skpoff	f.log			;logging?
	 call	endfil			;yes
	setz	p2,			;no file
	call	p.ack			;ack the packet
	jrst	getpkt			;try next incoming file

;Nak handling
Datp.e:	call	clrprt			;clear the line
	call	p.nak			;nak it
	jrst	datp.a			;and try again

;Too many naks
Nakout: abort	<Sent or received too many naks>

;Dispatch table

Disp2:	exp	datp.e			;illegal, nak
	exp	datp.e			;ack
	exp	datp.e			;nak
	gabort	<Received CAN while expecting data packet>
	exp	datp.b			;data packet
	exp	datp.d			;end of file
	abort	<Received SOF while expecting data packet>
Subttl Chkfil - Send outgoing files

;Loop looking for files in OUTBOX: and sending them over the line.
; If the line is inactive, wait here forever, periodically sending SOF
; packets, until the other line activates.

Chkfil: movx	a,gj%old!gj%sht!gj%ifg!.gjall   ;find *.*.*
	bpm	b,sysbox		;outbox: or vaxbox:
	gtjfn%				;get first in list
	 erjmp	r			;none, return

;Here on each file

Opnfil:	hrrzm	a,p2			;save the individual jfn
	movei	p3,"F"			;file init
	setz	p4,			;count filled in later
	call	pinit			;init a packet
	move	a,p2			;get jfn
	move	b,[1,,.fbbyv]		;word containing byte size
	movei	c,t2			;where to store size
	gtfdb%				;get the word
	ldb	t1,[point 6,t2,11]	;get size
	caie	t1,7			;if it isn't seven,
	cain	t1,10			;or eight,
	caia				;(one or the other)
	 movei	t1,6			;then we'll make it six
	move	a,p2			;get jfn
	movx	b,of%rd 		;read mode
	dpb	t1,[point 6,b,5]	;deposit byte size
	openf%				;open it
	 erjmp	delfil			;open error, delete and try again
	skpoff	f.log			;logging?
	 print	a,asc<%CSending file %J at %D...%C>
	setzm	numack			;clear acks and naks
	setzm	numnak
;Calculate the size of the file name and writer string and write them out

	hrroi	a,filnam		;destination
	move	b,p2			;jfn
	movx	c,js%nam!js%typ!js%paf	;name and type only
	jfns%				;copy it
	movei	d,1			;initialize counter
	bpm	a,filnam		;start
	move	b,[point 8,outpkt+1]	;destination
	call	copchk			;copy name with checksumming

;See if last writer string is required
IFN LWrite,<
	push	p,b			;save pointer
	move	a,p2			;get jfn
	hrli	a,.gflwr		;last writer
	hrroi	b,filnam		;reuse string location
	gfust%				;get the string
	pop	p,b			;restore destination
	bpm	a,filnam		;where it starts
	movei	c,"/"			;name/writer separator
	addm	c,chksum		;add to checksum
	idpb	c,b			;into string
	aoj	d,			;count it
	call	copchk			;now copy the writer string
>;End IFN LWrite
;Done with name

Chkf.b: move	a,chksum		;get checksum
	add	a,d			;add in data count
	add	a,t1			;also add size field
	andi	a,177			;make seven bits
	caige	a,40			;printable?
	 addi	a,240			;no, make it so
	idpb	a,b			;deposit it
	movei	a,eop			;end of packet
	idpb	a,b			;add that too
	caige	d,40			;printable count?
	 addi	d,240			;no, make it so
	dpb	d,[point 8,outpkt,23]	;deposit in count field
	dpb	t1,[point 8,outpkt,31]	;follow with size field
	setom	pktnum			;start with packet -1
	flgoff	f.ieof			;no end of file

;Await response, keep trying forever
Filsnd: call	outsnd			;send it off
	movei	b,dsend 		;how long to wait
	call	inpack			;get a packet
	jrst	@disp3(p3)		;dispatch

;Check response to see if it's right
Rspchk:	move	a,dat1			;get packet number
	caie	a,"F"			;file init?
	 jrst	filsnd			;nope, try again
	jrst	sndpak			;yes, proceed

;Copchk -- copy string from (a) to (b) with checksumming

Copchk: ildb	c,a			;get a character
	jumpe	c,r			;no more
	addm	c,chksum		;add to checksum
	idpb	c,b			;deposit it
	aoja	d,copchk		;loop until done

;Dispatch table

Disp3:	exp	filsnd			;garbage, try again
	exp	rspchk			;ack, success
	exp	filsnd			;nak, try again
	gabort	<Received CAN after sending SOF packet>
	abort	<Received data packet after sending SOF packet>
	abort	<Received EOF packet after sending SOF packet>
	abort	<Received SOF packet after sending SOF packet>
Subttl Sndpak - loop sending data packets

;The file init has been acknowledged, send the data

Sndpak: aos	t3,pktnum		;next packet
	andi	t3,177			;make it 7-bits
	movem	t3,pktnum		;restore
	move	a,p2			;file jfn
	move	b,[point 8,data]	;data pointer
	movni	c,maxsnd		;data to read - overhead bytes
	sin%				;read data in
	 erjmp	sndp.b			;probably eof with data

;Send some data
Sndp.b: addi	c,maxsnd		;find number read
	skipg	t2,c			;test and copy number
	 jrst	sndp.g			;must be eof, nothing read
	movei	p3,"D"			;data packet
	setz	p4,			;initialize count to zero
	call	pinit			;initialize packet
	move	a,pktnum		;get packet number
	caige	a,40			;printable?
	 addi	a,240			;no, make it so
	movem	a,code			;save to check ack code later
	addm	a,chksum		;add to checksum
	idpb	a,t1			;put into outgoing packet
	move	b,[point 8,data]	;pointer to source

;Data loop
Sndp.c: ildb	a,b			;get data byte
	ldb	d,[point 7,a,35]	;get only seven bits of it
	caie	a,quote 		;a quote character?
	caig	d,eop			;or end of packet char?
	 jrst	sndpc1			;nope, continue
	movei	d,quote 		;get quote char
	addm	d,chksum		;add to checksum
	idpb	d,t1			;deposit it
	addi	a,40			;add offset
	aoj	t2,			;and bump the character count

Sndpc1: idpb	a,t1			;store the character
	addm	a,chksum		;and checksum it
	sojg	c,sndp.c		;loop for all characters
;Now do the checksum

	aos	a,t2			;copy count and bump it
	caige	t2,40			;printable?
	 addi	t2,240			;no, make it so
	dpb	t2,[point 8,outpkt,23]	;deposit it
	add	a,chksum		;now get checksum and add in count
	andi	a,177			;make seven bits
	caige	a,40			;printable?
	 addi	a,240			;no, make it so
	idpb	a,t1			;store in the packet
	movei	a,eop			;end it
	idpb	a,t1

;Send the packet
Sndp.d:	movei	t4,maxnak		;get maximum naks
	movem	t4,nakcnt		;store them
	jrst	sdp.e1			;and hop
Sndp.e: aos	numnak			;got a nak
	call	clrprt			;clear the line
	sosg	nakcnt			;count down naks
	 jrst	nakout			;too many
Sdp.e1:	call	outsnd			;send the packet
	movei	b,dwait 		;data wait
	call	inpack			;get a packet
	jrst	@disp4(p3)		;dispatch on answer

;Here when deciding whether we're done or not
Sndp.f:	move	a,dat1			;get packet number
	came	a,code			;match the code?
	 jrst	sndp.e			;nope, try again
	aos	numack			;got an ack
	skpon	f.ieof			;input end of file?
	 jrst	sndpak			;no, do next

;Now close, delete and expunge the file
	move	a,p2			;get jfn
	txo	a,co%nrj		;don't release it
	closf%				;close the file
	 erjmp	.+1			;ignore errors
	skpoff	f.log			;logging?
	 call	endfil			;yes

;Alternate entry to delete file if we can't open it
Delfil:	move	a,p2			;get jfn
	txo	a,df%exp 		;expunge and keep jfn
	delf%				;delete the file
	 erjmp	[move	a,p2		;try again
		 delf%			;delete only
		  jfcl			;ignore errors
		 jrst	.+1]		;and return in-line
	jrst	chkfil			;look for next file
;Here when file is done

Sndp.g: movei	p3,"E"			;end file
	movei	p4,1			;one data
	call	pinit			;set it up
	move	a,pktnum		;install packet number
	andi	a,177			;make seven bits
	caige	a,40			;printable?
	 addi	a,240			;no, make it so
	movem	a,code			;store code
	addm	a,chksum		;add to checksum
	idpb	a,t1			;deposit it
	move	a,chksum		;get checksum
	andi	a,177			;make seven bits
	caige	a,40			;printable?
	 addi	a,240			;no, make it so
	idpb	a,t1			;tack that on
	movei	a,eop			;end of packet
	idpb	a,t1			;tack it on
	flgon	f.ieof			;set eof
	jrst	sndp.d			;and send it away

;Dispatch table

Disp4:	exp	sndp.e			;illegal, retry
	exp	sndp.f			;ack, do the next unless eof
	exp	sndp.e			;nak, try again
	gabort	<Received CAN after sending data packets>
	abort	<Received data packet after sending data packet>
	abort	<Received EOF packet after sending data packet>
	abort	<Received SOF packet after sending data packet>
;Endfil -- print ending information

Endfil: move	a,numack		;number of acks
	print	a,asc<...Finished file at %D with %N acks and >
	move	a,numnak		;number of naks
	print	a,asc<%N naks%C>
Endf.a: move	a,logjfn		;get logging file
	txo	a,co%nrj		;don't release jfn
	closf%				;close it
	 ret				;on error
	move	a,logjfn		;get jfn again
	movx	b,fld(7,of%bsz)!of%app	;append mode
	openf%				;open it
	 flgoff f.log			;can't, turn off logging
	ret				;done

;End of program
	end	xmit