title 'LEVEL3.ASM'
;************************************************
;*						*
;*		LEVEL3.ASM			*
;*						*
;*  X.25 level 3 (packet) protocol handler	*
;*  (implements single logical channel DTE  	*
;*   for use in a Virtual Call circuit (VC)	*
;*   or Permanent Virtual Circuit (PVC))	*
;*						*
;*  rev 1.30	08/21/84	E. Elizondo	*
;*						*
;*  (c) 1984 E. Elizondo - all rights reserved. *
;*						*
;*    This program may be used freely for non-  *
;*  commercial applications. It may not be sold *
;*  or used for commercial applications without *
;*  written permission of the author.           *
;* 						*
;************************************************
;	
	maclib	Z80	;DR Z80 macro library

;	assembly time options
false	equ	0
true	equ	not false
debug	equ	false		;display P(s) value

;	design parameters
	
n3	equ	2	;retry count for level 3 timeout
kpack	equ	4	;acknowledgement delay count

;	X.25 standard parameters:

;	logical group & channel number (para 2.4.1)
lchan	equ	1		;logical channel 1
lgroup	equ	0		;logical group 0

;	flow control parameters:
wsize	equ	2		;window size (para 4.4.1.2)

;	packet type identifiers (table 8/X.25)
;	bit #   8765$4321
calrid:	equ	0000$1011b	;call request
calaid:	equ	0000$1111b	;call accepted
clrrid:	equ	0001$0011b	;clear request
clrcid:	equ	0001$0111b	;clear confirmation
intid:	equ	0010$0011b	;interrupt
intcid:	equ	0010$0111b	;interrupt confirmation
rrid:	equ	0000$0001b	;RR
rnrid:	equ	0000$0101b	;RNR
rstrid:	equ	0001$1011b	;reset request
rstcid:	equ	0001$1111b	;reset confirmation
starid:	equ	1111$1011b	;restart request
stacid:	equ	1111$1111b	;restart confirmation
diagid:	equ	1111$0001b	;diagnostic

;	misc constants
cr	equ	0dh		;carriage ret
lf	equ	0ah		;line feed

;	hooks for main program
;	subroutines

	public	initl3		;initialize level 3 parameters
	public	txdpk		;transmit data packet if avail
	public	rxpk		;receive packet if available
	public	txintp		;transmit interrupt packet
	public	txstar		;transmit restart packet
	public	txclrr		;transmit clear request packet

;	addresses
	public	chstat		;level 3 logical channel state
	public	l3stat		;level 3 flow control flags

;	level 3 parameters
	public	ps		;P(s)
	public	pr		;P(r)
	public	lastpr		;last rx P(r)
	public	ltxpr		;last tx P(r)
	public	lrxps		;last rx P(s)
	public	ltxps		;last tx P(s)
	public	pvcmod		;PVC mode flag
	public	laddr		;local DTE address
	public	raddr		;remote DTE address
	public	caddr		;rx calling DTE address 
	public	laddrl		;local DTE address length
	public	raddrl		;remote DTE address length
	public	caddrl		;rx calling DTE address length
	public	qbit		;Q bit
	public	dbit		;D bit
	public	chan		;logical channel #
	public	group		;logical group #

;	diagnostic counters
	public	txpct		;total tx packets
	public	txfpct		;total tx file data packets
	public	txcpct		;total tx console data packets
	public	ntxbct		;errors due to no free tx buffer
;
	public	rxpct		;total rx packets
	public	rxdpct		;total rx data packets
	public	rbfpct		;total rx bad format packets
	public	rbcpct		;total rx bad channel packets
	public	rbapct		;total rx bad address packets
	public	rbipct		;total rx bad id packets
	public	rbgpct		;total rx bad group packets
	public	rxxpct		;total discarded rx packets


;	definition of channel status (chstat) bits
;	(note that state r1 (packet level ready)
;	 corresponds to level 2 link conn)
;	bit	set condition
;	0	flow control ready state (d1)
;	1	DTE restart request state (r2)
;	2	DTE waiting state (p2)
;	3	DTE reset request state (d2)
;	4	DTE clear request state (p6)
;	5	ready (p1)
;	6	undefined
;	7	undefined

;	definition of flow control (l3stat) status bits
;	bit	set condition
;	0	DTE busy
;	1	DCE busy
;	2	DTE interrupt pending confirmation
;	3	undefined
;	4	undefined
;	5	ualled DTE address error semaphore (internal)
;	6	transmission completed - ready to clear
;	7	outgoing message waiting for call setup


;	external subroutines

;	from buffers module:
	extrn	inibuf		;initialize all buffers
	extrn	putbuf		;put char in buffer
	extrn	getbuf		;get char from buffer
	extrn	topbuf		;put char at beginning of buffer
	extrn	bpoint		;point to selected bcb
	extrn	rlsrxb		;release rx buffer
	extrn	clrbuf		;clear buffer for new use
	extrn	getbct		;get buffer count
	extrn	getrdy		;get state of buffer ready flag
	extrn	setrdy		;set buffer ready flag
	extrn	clrrdy		;clear ready flag

;	from level1 module:
	extrn	t20on		;turn on timer T20
	extrn	t20off		;turn off timer T20
	extrn	t21on		;turn on timer T21
	extrn	t21off		;turn off timer T21
	extrn	t22on		;turn on timer T22
	extrn	t22off		;turn off timer T22
	extrn	t23on		;turn on timer T23
	extrn	t23off		;turn off timer T23


;	from level2 module:
	extrn	gettxb		;get address of free tx bcb

;	from files module
	extrn	gfdata		;get byte from transmit file
	extrn	pfdata		;write file to receive file
	extrn	ctxfil		;close transmit file
	extrn	crxfil		;close receive file

;	from xutil module:
	extrn	ilprt		;in line print routine
	extrn	ctype		;print char in <a>
	extrn	pdec		;print <hl> in decimal
	extrn	phex		;print <a> in hex
	extrn	phex1		;print nibble in <a> in hex
	extrn	delay		;wait a bit

;	external addresses
	
;	from level 1 module
	extrn	tistat		;timer status flags
	extrn	rxstat		;receive status

;	from level 2 module
	extrn	lkstat		;level 2 status flags

;	from buffers module
	extrn	rxplst	;A(list of rx packt buffer #'s)
	extrn	rxbtab	;A(table of rx bcb pointers)
	extrn	txbtab	;A(table of tx bcb pointers)
	extrn	ctbcb	;A(bcb for console transmit)
	extrn	crbcb	;A(bcb for console receive)

;	from files module
	extrn	fstat		;file status flags

;	from x25 module
	extrn	l4stat		;level 4 status flags

	cseg		;code section


;	**************************
;	* initialization section *
;	**************************


;	initialize level 3 parameters
;	(externally and internally called)
;	on entry:	no parameters
;	on exit:	all regs, flags clobbered

initl3:
	mvi	a,lchan		;get default channel number
	sta	chan		;and initialize logical channel #
	mvi	a,lgroup	;get default group number
	sta	group		;and initialize logical group #
	mvi	a,0000$0001b	;get modulo 8 general format id
	sta	gfi		;initialize gfi
	call	reset		;reset flow control variables
	xra	a
	sta	qbit		;clear Q bit
	sta	dbit		;clear D bit
	sta	chstat		;clear logical channel state
	sta	l3stat		;channel in ready (p1) state
	ret



;	reset flow control variables
;	(internally called)
;	on entry:	no parameters
;	on exit:	<a>,flags clobbered
;			all other regs unchanged
reset:	xra	a
	sta	pr		;clear P(r)
	sta	ps		;clear P(s)
	sta	lastpr		;clear last rx P(r)
	sta	ltxpr		;and last tx P(r)
	mvi	a,7		;initialize last rx P(s)
	sta	lrxps		;	/
	sta	ltxps		;and last tx P(s)
	ret


 
 	****************************
;	*  packet transmit section *
;	****************************

;	check and process timeout condition or
;	transmit data packet if available and
;		a) Level 3 timers not timed out
;		b) level 2 link is connected
;		c) level 3 in data transfer ready state
;		d) DCE not busy
;		e) new frame P(s) is within window
;		f) free tx buffer available
;	and either	g) transmit file is open
;	or		h) console transmit buffer is ready
;		(file data takes precedence, as it's
;		 transmitted as a continuous sequence)
;	if no I is available, transmit RR or RNR if any
;	received I packets need acknowledgement
;	(externally called)
;	on entry:	no parameters
;	on exit:	all regs, flags clobbered
;

txdpk:
	lxi	h,tistat	;point to timer status flags
	bit	1,m		;timer T20 timed out?
	jnz	t20to		;yes, service it
;
	bit	2,m		;timer T21 timed out?
	jnz	t21to		;yes, service it
;
	bit	3,m		;timer T22 timed out?
	jnz	t22to		;yes, service it
;
	bit	4,m		;timer T23 timed out?
	jnz	t23to		;yes, service it
;
	lxi	h,lkstat	;point to level 2 status
	bit	2,m		;link connected?
	rz			;no, return
;
	lxi	h,chstat	;point back to channel status
	bit	5,m		;ready state (p1)?
	jnz	setup		;yes, see if we want to call
;
	bit	0,m		;flow control ready state (d1)?
	rz			;no, return
;
	lxi	h,l3stat	;point to level 3 status
	bit	1,m		;DCE busy?
	rnz			;yes, return
;
	bit	6,m		;message complete?
	jnz	clear		;yes, see if we want to clear
;
;
;	did we transmit data packet already?
	lda	ltxps		;get last tx P(s)
	mov	b,a		;save in <b>
	lda	ps		;get current ps
	cmp	b		;same?
	jz	txackp		;yes, we did
;
;	check that P(s) is within window
	lda	lastpr		;get last rx P(r)
	adi	wsize+1		;calculate just past window
	ani	7		;..mod 7
	mov	b,a		;save in <b>
	lda	ps		;get P(s)
	cmp	b		;is P(s)=past top of window?
	jz	txackp		;yes, don't transmit it yet
;
txdp0:	lxi	h,fstat		;point to file status
	bit	1,m		;tx file open?
	jz	txcdpk		;no, see if console packet avail
;
;	transmit disk file data packet
;	(default to category A packet)
	call	gettxb		;else get free tx bcb
	rc			;return if none avail
;
	xra	a		;clear <a>
	sta	qbit		;clear Q bit
	sta	dbit		;clear D bit
	call	octet1		;assemble octet 1 in <a>
	call	putbuf		;put octet 1 in tx buffer
	lda	chan		;get logical channel #
	call	putbuf		;put octet 2 in tx buffer
	call	octet3		;assemble octet 3 in <a>
	setb	4,a		;set M=1 for now
	call	putbuf		;store octet 3 in tx buffer

;	fill up user data field in packet
	mvi	b,128		;max user data octets in packet
txdp1:	
	call	gfdata		;get data byte from file
	jz	txdp2		;exit if end of file
	call	putbuf		;else put in buffer
	dcr	b		;is packet full?
	jnz	txdp1		;no, keep going
;
	jmp	txdp3		;else exit
;
;	end of file - make into category B packet
txdp2:
	call	getbuf		;get octet 1
	rc			;exit if not there
;
	mov	b,a		;save in <b>
	call	getbuf		;get octet 2
	rc			;exit if not there
;
	mov	c,a		;save in <c>
	call	getbuf		;get octet 3
	rc			;exit if not there
;
	res	4,a		;set M=0
	call	topbuf		;put back octet 3
	mov	a,c		;get octet 2
	call	topbuf		;and put it back
	mov	a,b		;get octet 1
	setb	6,a		;set D=1
	call	topbuf		;and put it back
	call	ctxfil		;close tx file

;	common exit routine
txdp3:
	call	txgo		;transmit packet
	lda	ps		;P(s)=P(s)+1 mod 7
;	
	if	debug		;if debug mode
	call	prtps		;print P(s)
	endif
;
	sta	ltxps		;update last tx P(s)
	inr	a		;increment P(s)
	ani	7		;      /
	sta	ps		;update P(s)
	lhld	txfpct		;increment tx file packet count
	inx	h		;	/
	shld	txfpct		;      /
	mvi	a,kpack		;initiaize ack delay count
	sta	packct		;	/
	call	delay		;and wait a bit
	ret



;	transmit console data packet if available
;	(internally called)
;	on entry:	no parameters
;	on exit:	all regs, flags clobbered
;
txcdpk:
	lxi	h,ctbcb		;point to console transmit bcb
	call	getrdy		;is it ready?
	jz	txackp		;no, see if we want to acknowledge
;
	call	gettxb		;else get free tx bcb
	rc			;return if none avail
;
;	this is a category B packet
	push	h		;save bcb address
	lxi	h,qbit		;point to Q bit
	res	7,m		;set Q=0
	lxi	h,dbit		;point to D bit
;***	setb	6,m		;set D=1  (if desired)
	pop	h		;restore bcb address
	call	octet1		;assemble octet 1 in <a>
	call	putbuf		;put octet 1 in tx buffer
	lda	chan		;get logical channel #
	call	putbuf		;put octet 2 in tx buffer
	call	octet3		;build octet 3 in <a> with M=0
	call	putbuf		;store in tx buffer
	mvi	b,128		;max user data octets in packet

;	fill user data fields from console tx buffer
	xchg			;save <hl> in <de>
	lxi	h,ctbcb		;point to console transmit bcb
txcdp1:	
	call	getbuf		;get data byte from console buffer
	jc	txcdp2		;exit if end of buffer
	xchg			;restore tx buffer bcb address
	call	putbuf		;else put into tx buffer
	xchg			;point back to console xmit bcb
	dcr	b		;is packet full?
	jnz	txcdp1		;no, keep going
;
;	common exit routine
txcdp2:
	call	clrrdy		;clear console xmit buffer flag
	xchg			;point back to tx buffer bcb
	call	txgo		;transmit packet
	lda	ps		;P(s)=P(s)+1 mod 7
;
	if debug
	call	prtps		;print P(s)
	endif
;
	sta	ltxps		;update last tx P(s)
	inr	a		;incrment P(s)
	ani	7		;      /
	sta	ps		;update P(s)
	lxi	h,l4stat	;point to level 4 status
	setb	0,m		;signal prompt for next packet
	lhld	txcpct		;increment tx console packet count
	inx	h		;	/
	shld	txcpct		;      /
	lxi	h,l3stat	;point to flow status
	res	7,m		;reset message waiting flag
	mvi	a,kpack		;initialize ack delay count
	sta	packct		;	/
	ret


;	transmit acknowledgement (RR or RNR) packet 
;	(internally called)
;	on entry:	no parameters
;	on exit:	all flags, regs clobbered
txackp:
	lda	ltxpr		;get last tx P(r)
	mov	b,a		;save in <b>
	lda	pr		;get current P(r)
	cmp	b		;same?
	rz			;yes, nothing to acknowledge
;
;	check delay count to see if any data packets are coming
	lda	packct		;get delay count
	dcr	a		;decrement it
	sta	packct		;	/
	rnz			;wait a bit till it's 0
;
;	delay completed, transmit acknowledge packet
	mvi	a,kpack		;initialize delay count
	sta	packct		;	/
	lxi	h,l3stat	;point to level 3 flow status
	bit	0,m		;DTE busy?
	jnz	txrnr		;yes, transmit RNR packet
;
	jmp	txrr		;else transmit RR packet



;	print P(s)
;	(internally called)
;	on entry:	<a>=P(s)
;	on exit:	all flags, regs unchanged

prtps:	push	psw		;save P(s)
	mvi	a,'['
	call	ctype
	pop	psw		;restore P(s)
	push	psw		;and save it again
	adi	'0'		;convert to ASCII
	call	ctype		;display it
	mvi	a,']'
	call	ctype
	pop	psw		;restore P(s)
	ret


;	set up call if in VC mode
;	(internally called)
;	on entry:	no parameters
;	on exit:	all flags, regs clobbered

setup:
	lxi	h,chstat	;point to channel status
	bit	2,m		;DTE waiting state (p2)?
	rnz			;yes, do nothing
;
	lxi	h,l3stat	;point to flow status
	bit	7,m		;message ready?
	rz			;no, return
;
;	ready to set up call
	res	7,m		;reset message waiting flag
	lxi	h,chstat	;point back to channel status
	bit	0,m		;flow control ready state (d1)?
	rnz			;yes, no setup needed
;
	jmp	txcr		;else transmit call request packet



;	clear call if in VC mode
;	(internally called)

clear:
	lxi	h,l3stat	;point to level 3 status
	res	6,m		;reset message complete flag
	lda	pvcmod		;get VC/PVC mode flag
	cpi	2		;PVC?
	rz			;yes, do nothing if PVC
;
	jmp	txclrr		;else transmit clear request packet


;	transmit call request packet
;	(internally called)
;	on entry:	no parameters
;	on exit:	all flags, regs clobbered

txcr:	
	call	gettxb		;get free tx bcb
	jc	enotxb		;error if none available
;
	mvi	c,calrid	;get call request id
	call	put3oct		;put octets 1-3 in tx buffer
	call	putaddr		;put address octets in tx buffer

;***	(no facilities or call user data implemented)
	mvi	a,0		;facilities length=0
	call	putbuf		;put octet in tx buffer
	call	txgo		;transmit packet
	call	reset		;reset flow control variables
	call	t21on		;start timer T21
	lxi	h,chstat	;point to channel status
	bit	0,m		;flow control ready state?
	rnz			;yes, all done
;
	setb	2,m		;else signal DTE waiting state (p2)
	ret



;	transmit call accepted packet
;	(internally called)
;	on entry:	no parameters
;	on exit:	all flags, regs clobbered

txca:	
	call	gettxb		;get free tx bcb
	jc	enotxb		;error if none available
;
	mvi	c,calaid	;get call accepted id
	call	put3oct		;put octets 1-3 in tx buffer
	call	putaddr		;put address octets in tx buffer
;***	(no facilities implemented)
	call	txgo		;transmit packet
	ret



;	transmit clear request packet
;	(internally called)
;	on entry:	no parameters
;	on exit:	all flags, regs clobbered

txclrr:
	call	gettxb		;get free tx bcb
	jc	enotxb		;error if none available
;
	mvi	c,clrrid	;get clear request id
	call	put3oct		;put octets 1-3 in tx buffer
	mvi	a,0		;clearing cause=0 for DTE
	call	putbuf		;put octet 4 in tx buffer
	call	txgo		;transmit packet
	call	reset		;reset flow control variables
	call	t23on		;start timer T23
	lxi	h,chstat	;indicate DTE clear reqest state (P6)
	setb	4,m
	call	ctxfil		;close transmit file
	call	crxfil		;and receive file
	ret		



;	transmit clear confirmation packet
;	(internally called)
;	on entry:	no parameters
;	on exit:	all flags, regs clobbered

txclrc:
	call	gettxb		;get free tx bcb
	jc	enotxb		;error if none available
;
	mvi	c,clrcid	;get clear confirmation id
	call	put3oct		;put octets 1-3 in tx buffer
	call	txgo		;transmit packet
	ret		



;	transmit interrupt packet
;	(externally called)
;	on entry:	<b>=interupt user data for octet 4
;	on exit:	all flags, regs clobbered

txintp:	lxi	h,lkstat	;point to level 2 status
	bit	2,m		;link connected?
	jnz	txint1		;yes, keep going
;
	call	ilprt		;else tell operator
	db	'L3: link not connected - ',0
	jmp	txint5		;and exit
;
txint1:	lxi	h,chstat	;point to channel status
	bit	0,m		;flow control ready state?
	jnz	txint2		;yes, keep going
;
	call	ilprt		;else tell operator
	db	'L3: link not in data xfer state - ',0
	jmp	txint5		;and exit

txint2:	lxi	h,l3stat	;point to level 3 flow status
	bit	2,m		;interrupt already pending
	jz	txint3		;no, keep going
;
	call	ilprt		;else tell operator
	db	'L3: DTE interrupt is pending - ',0
txint5:	call	ilprt
	db	'cannot send interrupt',cr,lf,0
	ret

txint3:	call	gettxb		;get free tx bcb
	jc	enotxb		;error if none available
;
	mvi	c,intid		;get interrupt id
	call	put3oct		;put octets 1-3 in tx buffer
	mov	a,b		;get interrupt user data
	call	putbuf		;put octet 4 in tx buffer
	call	txgo		;transmit packet
	lxi	h,l3stat	;point to level 3 flow status
	setb	2,m		;set interrupt pending flag
	ret		



;	transmit interrupt confirmation packet
;	(internally called)
;	on entry:	no parameters
;	on exit:	all flags, regs clobbered

txintc:
	call	gettxb		;get free tx bcb
	jc	enotxb		;error if none available
;
	mvi	c,intcid	;get interrupt confirmation id 
	call	put3oct		;put octets 1- 3 in tx buffer
	call	txgo		;transmit packet
	ret		



;	transmit RR packet
;	(internally called)
;	on entry:	no parameters
;	on exit:	all flags, regs clobbered

txrr:
	mvi	c,rrid		;<c>=RR packet identifier
	jmp	txrcom		;and do common stuff



;	transmit RNR packet
;	(internally called)
;	on entry:	no parameters
;	on exit:	all flags, regs clobbered

txrnr:
	mvi	c,rnrid		;<c>=RNR packet identifier
;
;	common stuff for RR and RNR
;	on entry:	<c>=packet identifier
txrcom:	call	gettxb		;get free tx bcb
	jc	enotxb		;error if none available
;
	xra	a		;clear Q and D bits
	sta	qbit		;	/
	sta	dbit		;      /
	call	octet1		;assemble octet 1 in <a>
	call	putbuf		;put octet 1 in tx buffer
	lda	chan		;get logical channel #
	call	putbuf		;put octet 2 in tx buffer
	lda	pr		;get P(r)
	sta	ltxpr		;update last tx P(r)
	rlc			;move to bits 8-6
	rlc			;	/
	rlc			;      /
	rlc			;     /
	rlc			;    /
	ani	1110$0000b	;zero other bits
	ora	c		;merge with P(r)
	call	putbuf		;put octet 3 in tx buffer
	call	txgo		;transmit packet
	ret		



;	transmit reset request packet
;	(internally called)
;	on entry:	no parameters
;	on exit:	all flags, regs clobbered

txrstr:	call	gettxb		;get free tx bcb
	jc	enotxb		;error if none available
;
	mvi	c,rstrid	;get reset request id
	call	put3oct		;put octets 1-3 in tx buffer
	mvi	a,0		;resetting cause=0 for DTE
	call	putbuf		;put octet 4 in tx buffer
	call	txgo		;transmit packet
	call	reset		;reset flow control variables
	call	t22on		;start timer T22
	lxi	h,chstat	;point to channel status
	setb	3,m		;signal reset request state
	ret		



;	transmit reset confirmation packet
;	(internally called)
;	on entry:	no parameters
;	on exit:	all flags, regs clobbered

txrstc:	call	gettxb		;get free tx bcb
	jc	enotxb		;error if none available
;
	mvi	c,rstcid	;get reset confirmation id
	call	put3oct		;put octets 1-3 in tx buffer
	call	txgo		;and transmit packet
	ret



;	transmit restart request packet
;	(internally & externally called)
;	on entry:	no parameters
;	on exit:	all flags, regs clobbered

txstar:	lxi	h,lkstat	;point to level 2 status
	bit	2,m		;link connected?
	jnz	txstar1		;yes, keep going
;
	call	ilprt		;else tell operator
	db	'L3: link not connected',cr,lf,0
	ret
;
txstar1:
	call	gettxb		;get free tx bcb
	jc	enotxb		;error if none available
;
	mvi	c,starid	;get restart request packet id
	call	txscom		;do common stuff
	mvi	a,0		;set restarting cause=0 for DTE
	call	putbuf		;	/
	call	txgo		;transmit packet
	call	reset		;reset flow control variables
	lxi	h,chstat	;point to channel status
	setb	1,m		;indicate restart request state
	call	t20on		;start timer T20
	call	t21off		;and stop all others
	call	t22off		;	/
	call	t23off		;      /
	ret


;	transmit restart confirmation packet
;	(internally called)
;	on entry:	no parameters
;	on exit:	all flags, regs clobbered

txstac:	call	gettxb		;get free tx bcb
	jc	enotxb		;error if none available
;
	mvi	c,stacid	;get restart confirmation id
	call	txscom		;do common stuff
	call	txgo		;transmit packet
	ret

;	common stuff for restart request/confirmation
;	on entry:	<c>=packet id
txscom:	lda	gfi		;get general format identifier
	rlc			;move to bits 8-5
	rlc			;	/
	rlc			;      /
	rlc			;     /
	ani	1111$0000b	;make sure lower bits are all 0
	call	putbuf		;put octet 1 in tx buffer
	xra	a		;next octet is all 0's
	call	putbuf		;put octet 2 in tx buffer
	mov	a,c		;get packet identifier
	call	putbuf		;put octet 3 in tx buffer
	ret		



;	error handling routine for no available free tx buffer
;	(internally called)

enotxb:
	lhld	ntxbct		;increment error count
	inx	h		;	/
	shld	ntxbct		;      /
	ret


;	*****************************************
;	* utility routines for tx section	*
;	*****************************************

;	transmit packet
;	(internally called)
;	on entry:	<hl>=address of tx buffer bcb
;	on exit:	all regs, flags clobbered
 
txgo:	call	setrdy		;set tx buffer ready flag
	lhld	txpct		;increment total tx packet count
	inx	h		;	/
	shld	txpct		;      /
	ret


;	put octets 1-3 in tx buffer
;	(internally called)
;	on entry:	<hl>=bcb address of tx buffer
;			<c>=packet identifier
;	on exit:	<a>,flags clobbered
;			all other regs unchanged
put3oct:
	xra	a		;clear Q and D bits
	sta	qbit		;	/
	sta	dbit		;      /
	call	octet1		;assemble octet 1 in <a>
	call	putbuf		;put octet 1 in tx buffer
	lda	chan		;get logical channel #
	call	putbuf		;put octet 2 in tx buffer
	mov	a,c		;get packet identifier
	call	putbuf		;put octet 3 in tx buffer
	ret		


;	build octet 1
;	(internally called)
;	on entry:	no parameters
;	on exit:	<a>=octet 1
;			flags clobbered
;			all other regs unchanged

octet1:	push	b		;save <bc>
	lda	gfi		;get general format identifier
	rlc			;move to bits 8-5
	rlc			;	/
	rlc			;      /
	rlc			;     /
	mov	b,a		;save result in <b>
	lda	qbit		;get q bit
	ora	b		;merge with gfi
	mov	b,a		;and save result in <b>
	lda	dbit		;get d bit
	ora	b		;merge with gfi
	ani	0f0h		;make sure lower 4 bits are 0
	mov	b,a		;and save result in <b>
	lda	group		;get logical group #
	ani	0fh		;zero upper 4 bits
	ora	b		;merge with gfi
	pop	b		;restore <bc>
	ret		



;	build octet 3 of data packet
;	(internally called)
;	on entry:	no parameters
;	on exit:	<a>= octet 3
;			all other regs unchanged

octet3:
	push	b		;save <bc>
	lda	pr		;get P(r)
	sta	ltxpr		;update last tx P(r)
	rlc			;move to bits 8-6
	rlc			;	/
	rlc			;      /
	rlc			;     /
	rlc			;    /
	ani	1110$0000b	;zero other bits
	mov	b,a		;save result in <b>
	lda	ps		;get P(s)
	rlc			;move to bits 4-2
	ani	0000$1110b	;zero other bits
	ora	b		;merge with P(r) and M
	pop	b		;restore <bc>
	ret


;	put address octets in tx buffer
;	(internally called)
;	on entry:	<hl>=tx bcb address
;	on exit:	<a>,flags clobbered
;			all other regs unchanged

putaddr:
	push	b		;save <bc>
	push	d		;save <de>
;
;	build octet 4 in tx buffer
	lda	laddrl		;get local address length
	rlc			;move to bits 8-5
	rlc			;	/
	rlc			;      /
	rlc			;     /
	ani	1111$0000b	;make sure lower 4 bits are 0
	mov	b,a		;save result in <b>
	lda	raddrl		;get remote address length
	ani	0000$1111b	;make sure upper 4 bits are 0
	ora	b		;merge with local address length
	call	putbuf		;put octet 4 in tx buffer
;
;	now build called DTE address, if present
	lda	raddrl		;get remote address length
	ora	a		;length=0?
	jz	putad2		;yes, skip remote address
;
	mov	b,a		;save length in <b>
	lxi	d,raddr		;point <de> to remote address
putad1:	ldax	d		;get first char
	rlc			;move to bits 8-5
	rlc			;	/
	rlc			;      /
	rlc			;     /
	ani	1111$0000b	;make sure lower bits are 0
	mov	c,a		;save result in <c>
	inx	d		;point to next address digit
	dcr	b		;last digit?
	jz	putad3		;yes, get local address
;
	ldax	d		;else get next char
	ani	0000$1111b	;make sure upper bits are 0
	ora	c		;merge with last digit
	call	putbuf		;and put in tx buffer
	inx	d		;point to next address digit
	dcr	b		;last digit
	jnz	putad1		;no, get another
;
;	
;	build calling DTE address, if present
;	entry point if called DTE address is even # of digits
putad2:	lda	laddrl		;get local address length
	ora 	a		;length=0?
	jz	putadexi	;yes, exit
;
	mov	b,a		;else save length in <b>
	lxi	d,laddr		;point <de> to local address
	jmp	putad4		;and keep going
;
;	entry point if called DTE address is odd # of digits
putad3:	lda	laddrl		;get local address length
	ora	a		;length=0?
	jz	putadexi	;yes, exit
;
	mov	b,a		;else save length in <b>
	lxi	d,laddr		;point <de> to local address
	jmp	putad5		;and keep going
;
;	build msb digit
putad4:	ldax	d		;get first char
	rlc			;move to bits 8-5
	rlc			;	/
	rlc			;      /
	rlc			;     /
	ani	1111$0000b	;make sure lower bits are 0
	mov	c,a		;save result in <c>
	inx	d		;point to next address digit
	dcr	b		;last digit?
	jnz	putad5		;no, get next character
;
	call	putbuf		;else put last octet in tx buffer
	jmp	putadexi	;and exit
;
;	build lsb digit
putad5:	ldax	d		;get next char
	ani	0000$1111b	;make sure upper bits are 0
	ora	c		;merge with last digit
	call	putbuf		;and put in tx buffer
	inx	d		;point to next address digit
	dcr	b		;last digit
	jnz	putad4		;no, get another
;
;	common exit
putadexi:
	pop	d		;restore <de>
	pop	b		;restore <bc>
	ret



;	*************************
;	*  process timeouts	*
;	*************************



;	process timeout of DTE timer T20
;	(internally called)
;	on entry:	no parameters
;	on exit:	all flags, regs clobbered

t20to:	
	call	t20off		;turn off timer
	lxi	h,rtryct	;get retry count
	dcr	m		;last retry?
	jz	t20to1		;yes, other end is dead
;
	call	ilprt		;else try once more
	db	'L3: T20 timed out - '
	db	'retransmitting restart request packet',cr,lf,0
	call	t20on		;restart timer T20
	jmp	txstar		;and transmit restart
;
t20to1:	call	ilprt
	db	'L3: tx retry count exhausted - '
	db	'no reply from DCE',cr,lf,0
	ret			



;	process timeout of DTE timer T21
;	(internally called)
;	on entry:	no parameters
;	on exit:	all flags, regs clobbered

t21to:	
	call	t21off		;turn off timer
	call	ilprt		;and tell operator
	db	'L3: T21 timed out - '
	db	'transmitting clear request packet',cr,lf,0
	jmp	txclrr		;and transmit clear request



;	process timeout of DTE timer T22
;	(internally called)
;	on entry:	no parameters
;	on exit:	all flags, regs clobbered

t22to:	
	call	t22off		;turn off timer
	lxi	h,rtryct	;get retry count
	dcr	m		;last retry?
	jz	t22to1		;yes, other end is dead
;
	call	ilprt		;else try once more
	db	'L3: T22 timed out - '
	db	'retransmitting reset request packet',cr,lf,0
	call	t22on		;restart timer T22
	jmp	txrstr		;and transmit reset request
;
t22to1:	call	ilprt
	db	'L3: tx retry count exhausted - '
	db	'logical channel out of order',cr,lf,0
	ret			



;	process timeout of DTE timer T23
;	(internally called)
;	on entry:	no parameters
;	on exit:	all flags, regs clobbered

t23to:	
	call	t23off		;turn off timer
	lxi	h,rtryct	;get retry count
	dcr	m		;last retry?
	jz	t23to1		;yes, other end is dead
;
	call	ilprt		;else try once more
	db	'L3: T23 timed out - '
	db	'retransmitting clear request packet',cr,lf,0
	call	t23on		;restart timer T23
	jmp	txclrr		;transmit clear request
;
t23to1:	call	ilprt
	db	'L3: tx retry count exhausted - '
	db	'logical channel out of order',cr,lf,0
	ret			



;	***************************
;	*  packet receive section *
;	***************************

;	receive packet if available
;	on entry:	no parameters
;	on exit:	<hl>=bcb address if packet avail
;			<b>= packet octet # 3 (packet id)
;			<c>= rx buffer #


rxpk:	lxi	h,rxplst	;point to received packet list
	call	getbuf		;any there?
	rc			;no, exit
;
	lhld	rxpct		;increment rx packet count
	inx	h		;	/
	shld	rxpct		;      /
;
	mov	c,a		;else save rx buffer # in <c>
	lxi	h,rxbtab	;get buffer bcb address
	call	bpoint		;	/
	call	getbuf		;get octet 1
	jc	rxfmer		;format error if not there
;
	mov	d,a		;save octet 1 in <d>
	call	getbuf		;get octet 2
	jc	rxfmer		;format error if not there
;
	mov	e,a		;save octet 2 in <e>
	call	getbuf		;get octet 3
	jc	rxfmer		;format error if not there
;
	mov	b,a		;save octet 3 in <b>
	cpi	starid		;restart request?
	jz	chkrst		;yes, check format
;
	cpi	stacid		;restart confirmation
	jz	chkrst		;yes, check format
;
	cpi	diagid		;diagnostic
	jz	chkrst		;yes, check format
;
;	process packets having logical group and channel
	lda	chan		;get logical channel #
	cmp	e		;match?
	jnz	rxcher		;no, channel # error
;
	mov	a,d		;get back octet 1
	ani	0011$0000b	;get bits 6-5
	cpi	0001$0000b	;numbering modulo 8?
	jnz	rxfmer		;no, format error
;
	mov	a,d		;get back octet 1
	ani	0000$1111b	;get bits 4-1
	push	b		;save <bc>
	mov	b,a		;save logical group # in <b>
	lda	group		;get our logical group #
	cmp	b		;same?
	pop	b		;restore <bc>
	jnz	rxgrer		;no, group # error
;
;	branch to process known packet types
	mov	a,b		;get back octet 3
	bit	0,a		;data packet?
	jz	rxdata		;yes, process it
;
	cpi	intid		;interrupt packet?
	jz	rxint		;yes, process it
;
	cpi	intcid		;interrupt confirmation?
	jz	rxintc		;yes, process it
;
	cpi	rstrid		;reset request?
	jz	rxrstr		;yes, process it
;
	cpi	rstcid		;reset confirmation?
	jz	rxrstc		;yes, process it
;
	cpi	calrid		;call request?
	jz	rxcr		;yes, process it
;
	cpi	calaid		;call accepted?
	jz	rxca		;yes, process it
;
	cpi	clrrid		;clear request?
	jz	rxclr		;yes, process it
;
	cpi	clrcid		;clear confirmation?
	jz	rxclc		;yes, process it
;
;	process packets numbered modulo 8
rxpk3:	ani	0001$1111b	;leave only bits 5-1
	cpi	rrid		;RR
	jz	rxrr		;yes, process it
;
	cpi	rnrid		;RNR?
	jz	rxrnr		;yes, process it
;
	jmp	rxpk4		;else process id error

;	check format of restart and diagnostic packets
chkrst:	mov	a,d		;get back octet 1
	cpi	0001$0000b	;modulo 8 format?
	jnz	rxfmer		;no, format error
;
	mov	a,e		;get back octet 2
	ora	a		;all zero?
	jnz	rxfmer		;no, format error
;
;	now branch to known restart packet types
	mov	a,b		;get back octet 3
	cpi	starid		;restart?
	jz	rxstar		;yes, process it
;
	cpi	stacid		;restart confirmation?
	jz	rxstac		;yes, process it
;
	cpi	diagid		;diagnostic?
	jz	rxdiag		;yes, process it
;	else id is unrecognized so drop through to
;
;	process unrecognized id error
rxpk4:	call	flush		;discard packet
	lhld	rbipct		;increment bad id counter
	inx	h		;	/
	shld	rbipct		;      /
	ret

;	process packet format error
rxfmer:	call	flush		;discard packet
	lhld	rbfpct		;increment bad format counter
	inx	h		;	/
	shld	rbfpct		;      /
	ret

;	process packet group error
rxgrer:	call	flush		;discard packet
	lhld	rbgpct		;increment bad group counter
	inx	h		;	/
	shld	rbgpct		;      /
	ret

;	process packet channel error
rxcher:	call	flush		;discard packet
	lhld	rbcpct		;increment bad channel counter
	inx	h		;	/
	shld	rbcpct		;      /
	ret



;	process rx data packet
;	(internally called)
;	on entry:	<hl>=rx buffer bcb address
;			<b>=packet octet #3
;			<c>=rx buffer #
;	on exit:	all regs, flags clobbered

rxdata:
	xchg			;save bcb address
	lxi	h,chstat	;point to channel state
	bit	0,m		;flow control ready state?
	xchg			;get back bcb address
	jz	flush		;no, discard packet
;
	call	ackd		;process P(s) and P(r)
	jc	dterst		;reset channel if invalid
;
	xchg			;save bcb address
	lxi	h,fstat		;point to file status
	bit	0,m		;receive file open?
	xchg			;restore bcb address
	jnz	rxfdat		;yes, write packet in file
;
;	else write packet to console receive buffer
	lxi	d,crbcb		;point to console rx buffer
rxcdat:	call	getbuf		;get data octet from rx buffer
	jc	rxcd1		;exit when empty
;
	xchg			;point to console rx buffer
	call	putbuf		;put octet in console rx buffer
	xchg			;point back to rx buffer
	jmp	rxcdat		;and go back for more
;
;	process end of packet to console
rxcd1:	xchg			;point to console rx buffer
	call	setrdy		;set buffer ready flag
	jmp	rxdexi		;do common exit stuff


;	write packet into disk file
rxfdat:
	call	getbuf		;get data octet from rx buffer
	jc	rxfd1		;exit if empty
;
	call	pfdata		;else put data in file
	jmp	rxfdat		;and loop until empty
;
;	process end of rx packet data
rxfd1:
	mvi	a,'+'		;tell console packet rx
	call	ctype		;	/
	jmp	rxdexi		;do common exit stuff

;	common exit for rx packet data
rxdexi:
	mov	a,c		;get rx buffer #
	call	rlsrxb		;release rx buffer
	lhld	rxdpct		;increment rx data packet count
	inx	h		;	/
	shld	rxdpct		;      /
	ret



;	process rx incoming call packet
;	(internally called)
;	on entry:	<hl>=bcb address
;			<b>=packet octet 3 (id)
;			<c>=rx buffer #

rxcr:
	call	getbuf		;get octet 4
	jc	rxfmer		;format error if not there
;
	sta	rxoc4		;save the octet
	xchg			;save bcb address
	lxi	h,chstat	;point to channel status
	bit	0,m		;flow control ready state (d1)?
	jnz	rxcr1		;yes, accept packet
;
	bit	1,m		;DTE restart request state (r2)?
	jnz	flush		;yes, discard packet 
;
	bit	2,m		;waiting state (p2)?
	jnz	rxcr1		;yes, accept packet
;
	bit	3,m		;reset request state (d2)?
	jnz	rxcr1		;yes, accept packet
;
	bit	4,m		;clear request state (p6)?
	jnz	rxcr1		;yes, accept packet
;
	bit	5,m		;ready state (p1)?
	jnz	rxcr1		;yes, accept packet
;
	jmp	flush		;else discard packet
;
rxcr1:	xchg			;get back bcb address
	call	chkcadr		;check whether address is for us
	jc	badadr		;refuse call if invalid
;
	call	getbuf		;get facilities length octet
	jc	rxfmer		;format error if not there
;	
;***	special facilities not implemented
	ora	a		;facilities requested?
	jnz	calrej		;yes, refuse call
;
	mov	a,c		;get rx buffer #
	call	rlsrxb		;release it
	call	reset		;reset flow control variables
	call	t21off		;turn off timer T21
	call	t22off		;turn off timer T22
	call	t23off		;turn off timer T23
	call	ilprt
	db	'L3: rx incoming call ',0
	lda	caddrl		;get calling DTE address length
	ora	a		;=0?
	jz	rxca3		;yes, keep going
;
	inr	a		;increment length by one
	mov	b,a		;and save in <b>
	call	ilprt		;display calling address
	db	'from: ',0
	lxi	h,caddr		;point to calling address
rxca2:	dcr	b		;end of address?
	jz	rxca3		;yes, exit loop
;
	mov	a,m		;else get nibble
	call	phex1		;display it in hex
	inx	h		;bump pointer
	jmp	rxca2		;and get next nibble
;
rxca3:	call	ilprt		;terminate message
	db	cr,lf,0
	lxi	h,l3stat	;point to flow status
	res	1,m		;reset DCE busy flag
	res	2,m		;reset interrupt pending flag
	res	6,m		;reset end of message flag
	lxi	h,chstat	;point to channel status
	setb	0,m		;set flow control ready state (d1)
	res	3,m		;reset DTE reset request state (d2)
	res	4,m		;reset DTE clear request state (p6)
	res	5,m		;reset ready state (p1)
	bit	2,m		;DTE waiting state (p2)?
	jz	txca		;no, tx call accepted packet
;
;	call collision state (p5)
	res	2,m		;else reset state p2
	ret			;and exit



;	process rx call connected packet
;	(internally called)
;	on entry:	<hl>=bcb address
;			<b>=packet octet 3 (id)
;			<c>=rx buffer #

rxca:
	xchg			;save bcb address
	lxi	h,chstat	;point to channel status
	bit	2,m		;DTE waiting state (p2)?
	jnz	rxca0		;yes, accept packet
;
	jmp	flush		;else discard packet
;
rxca0:	xchg			;get back bcb address
	call	getbuf		;get octet 4 if present
	jc	rxca1		;skip address check if not present
;
	sta	rxoc4		;save octet 4 if present
	call	chkadr		;check addresses
	jc	badadr		;refuse call if invalid
;
	call	getbuf		;get octet 5 if present
	jc	rxca1		;skip facilities check if not present
;
;***	special facilities not implemented
;
;	checks all done, accept call
rxca1:	mov	a,c		;get rx buffer #
	call	rlsrxb		;release it
	call	reset		;reset flow control variables
	call	t21off		;turn off timer T21
	call	t22off		;turn off timer T22
	call	t23off		;turn off timer T23
	call	ilprt
	db	'L3: rx call connected',cr,lf,0
	lxi	h,l3stat	;point to flow control status
	res	1,m		;reset DCE busy flag
	res	2,m		;reset interrupt pending flag
	res	6,m		;reset message complete flag
	lxi	h,chstat	;point to channel status
	setb	0,m		;set flow control ready state (d1)
	res	2,m		;reset DTE waiting state (p2)
	res	3,m		;reset DTE reset request state (d2)
	res	4,m		;reset DTE clear request state (p6)
	res	5,m		;reset ready state (p1)
	ret			;and exit


;	process invalid address
;	(internally called)
;	on entry:	<c>=rx buffer #
;	on exit:	all flags, regs clobbered

;	
badadr:	call	flush		;discard packet
	lhld	rbapct		;increment bad address count
	inx	h		;	/
	shld	rbapct		;      /
	call	ilprt
	db	'L3: bad address - tx clear request',cr,lf,0
	jmp	txclrr		;transmit clear request


;	refuse call
;	(internally called)
;	on entry:	<c>=rx buffer #
;	on exit:	all flags, regs clobbered


calrej:	call	flush		;discard packet
	call	ilprt
	db	'L3: call refused - tx clear request',cr,lf,0
	jmp	txclrr		;transmit clear request



;	process rx clear indication (clear request) packet
;	(internally called)
;	on entry:	<hl>=bcb address
;			<b>=packet octet 3 (id)
;			<c>=rx buffer #

rxclr:
	call	getbuf		;get octet 4
	jc	rxfmer		;format error if not there
;
	mov	b,a		;save octet 4 in <b>
	xchg			;save bcb address
	lxi	h,chstat	;point to channel status
	bit	0,m		;flow control ready state (d1)?
	jnz	rxclr0		;yes, accept packet
;
	bit	4,m		;clear request state (p6)?
	jnz	rxclr0		;yes, accept packet
;
	jmp	flush		;else discard packet
;
rxclr0:	xchg			;restore bcb address
	call	ilprt		;tell operator
	db	'L3: rx clear indication packet, cause: ',0
	mov	a,b		;get back octet 4
	call	phex		;print in hex
	call	ilprt		;	/
	db	' (hex)',cr,lf,0		;      /
	call	getbuf		;get octet 5
	jc	rxclr2		;keep going if not there
;
	mov	b,a		;save octet 5 in <b>
	call	ilprt
	db	'    diagnostic code      : ',0
	mov	l,b		;print diagnostic code
	mvi	h,0		;	/
	call	pdec		;print in decimal
	call	ilprt
	db	cr,lf,0
;
;	perform flow control actions
rxclr2:
	mov	a,c		;get rx buffer #
	call	rlsrxb		;and release it
	call	reset		;reset flow control variables
	call	t21off		;stop timers
	call	t22off		;      /
	call	t23off		;     /
	call	ctxfil		;close transmit file
	call	crxfil		;close receive file
	lxi	h,chstat	;point to channel status
	res	0,m		;reset state d1
	res	2,m		;reset state p2
	res	3,m		;reset state d2
	setb	5,m		;set ready state p1
	bit	4,m		;was it DTE clear request state (p6)?
	jz	txclrc		;no, tx clear confirmation packet
;
;	DTE clear request state (p6)
	res	4,m		;else reset clear request state 
	ret			;and exit



;	process rx clear confirmation packet
;	(internally called)
;	on entry:	<hl>=bcb address
;			<b>=packet octet 3 (id)
;			<c>=rx buffer #

rxclc:
	call	getbuf		;get octet 4
	jnc	rxfmer		;format error if there
;
	lxi	h,chstat	;point to channel status
	bit	4,m		;DTE clear request state (p6)?
	jz	flush		;no, discard packet
;
;	DTE clear request state (p6)
	res	4,m		;reset back to ready state
	mov	a,c		;get rx buffer #
	call	rlsrxb		;and release it
	call	reset		;reset flow control variables
	call	ilprt		;tell operator
	db	'L3: rx clear confirmation packet',cr,lf,0
	ret


	
;	process rx INT packet
;	(internally called)
;	on entry:	<hl>=bcb address
;			<c>=rx buffer #

rxint:
	call	getbuf		;get octet 4
	jc	rxfmer		;format error if not there
;
	xchg			;save bcb address in <de>
	lxi	h,chstat	;point to channel status
	bit	0,m		;flow control ready state (d1)?
	jz	flush		;no, discard packet
;
	xchg			;restore bcb address
	mov	b,a		;save octet 4 in <b>
	call	ilprt
	db	'L3: rx INT packet - user data: ',0
	mov	a,b		;get back octet 4
	call	phex		;print in hex
	call	ilprt		;terminate line
	db	cr,lf,0
	mov	a,c		;get rx buffer #
	call	rlsrxb		;release it
	jmp	txintc		;and transmit int confirmation


;	process rx INT confirmation packet
;	(internally called)
;	on entry:	<hl>=bcb address
;			<c>=rx buffer #

rxintc:
	call	getbuf		;tryto get octet 4
	jnc	rxfmer		;format error if there
;
	lxi	h,chstat	;point to channel status
	bit	0,m		;flow control ready state (d1)?
	jz	flush		;no, discard packet
;
	lxi	h,l3stat	;point to level 3 status
	bit	2,m		;interrupt pending?
	jz	flush		;no, discard packet
;
	res	2,m		;else clear int pending flag
	call	ilprt
	db	'L3: rx interrupt confirmation packet',cr,lf,0
	mov	a,c		;get rx buffer #
	call	rlsrxb		;release it
	ret


;	process DIAG packet
;	(internally called)
;	on entry:	<hl>=bcb address
;			<b>=packet octet #3 (packet id)
;			<c>=rx buffer #

rxdiag:
	call	getbuf		;get octet 4
	jc	rxfmer		;format error if not there
;
	xchg			;save bcb address in <de>
	mov	b,a		;save octet 4 in <b>
	call	ilprt
	db	'L3: rx DIAG packet - diagnostic # ',0
	mov	l,b		;print diagnostic code
	mvi	h,0		;	/
	call	pdec		;      /
	call	ilprt		;print explanation if present
	db	cr,lf,'L3: diagnostic explanation: ',0
	xchg			;get back bcb address

;	print diagnostic explanation
diaglp:	call	getbuf		;get next octet
	jc	diagexi		;exit if not there
;
	call	phex		;print in hex
	call	ilprt		;and separator
	db	' ',0
	jmp	diaglp		;and try for next byte
;
diagexi:
	call	ilprt		;terminate explanation line
	db	cr,lf,0
	mov	a,c		;get rx buffer
	call	rlsrxb		;release it
	ret



;	process rx RR packet
;	(internally called)
;	on entry:	<b>=octet 3		
;			<c>=rx buffer #

rxrr:	lxi	h,chstat	;point to channel status
	bit	0,m		;flow control ready state?
	jz	flush		;no, discard packet
;
	call	chkpr		;check P(r) and update window
	jc	dterst		;local procedure error
;
	lxi	h,l3stat	;point to level 3 status
	res	1,m		;clear DCE busy
	mov	a,c		;release rx buffer
	call	rlsrxb		;	/
	ret



;	process rx RNR packet
;	(internally called)
;	on entry:	<b>=octet 3		
;			<c>=rx buffer #

rxrnr:	lxi	h,chstat	;point to channel status
	bit	0,m		;flow control ready state?
	jz	flush		;no, discard packet
;
	call	chkpr		;check P(r) & update window
	jc	dterst		;local procedure error
;
	lxi	h,l3stat	;point to level 3 status
	setb	1,m		;signal DCE busy
	mov	a,c		;release rx buffer
	call	rlsrxb		;	/
	ret



;	process received reset indication
;	(internally called)
;	on entry:	<b>=octet 3		
;			<c>=rx buffer #

rxrstr:	lxi	h,chstat	;point to channel status
	bit	3,m		;reset request state (d2)?
	jnz	rxstr0		;yes, accept packet
;
	bit	0,m		;flow control ready state (d1)
	jnz	rxstr1		;yes, accept packet
;
	jmp	flush		;else discard packet
;
;	reset collision (para 4.4.3.3)
rxstr0:
	res	3,m		;clear reset request state
	setb	0,m		;set flow control ready state d1
	call	t22off		;stop timer T22
	mov	a,c		;get rx buffer #
	call	rlsrxb		;release buffer
	ret			;and exit
;
;	DTE in flow control ready (d1) state
rxstr1:
	mov	a,c		;release rx buffer
	call	rlsrxb		;	/
	call	reset		;reset flow control variables
	lxi	h,l3stat	;point to level 3 status
	res	1,m		;clear DCE busy status
	jmp	txrstc		;and transmit reset confirmation



;	process received reset confirmation
;	(internally called)
;	on entry:	<b>=octet 3		
;			<c>=rx buffer #

rxrstc:	lxi	h,chstat	;point to channel status
	bit	3,m		;DTE reset request state?
	jz	flush		;no, discard packet
;
;	DTE in reset request state (d2)
	res	3,m		;clear state d2
	setb	0,m		;set state d1
	call	t22off		;stop timer T22
	mov	a,c		;get rx buffer #
	call	rlsrxb		;release buffer
	lxi	h,l3stat	;point to level 3 status
	res	1,m		;clear DCE busy status
	ret			;and exit
;


;	process received restart indication
;	(internally called)
;	on entry:	<b>=octet 3		
;			<c>=rx buffer #

rxstar:	lxi	h,chstat	;point to channel status
	res	0,m		;clear state d1 for now
	res	2,m		;and state p2
	res	3,m		;and state d2
	res	4,m		;and state p6
	setb	5,m		;set ready state p1
	lda	pvcmod		;get pvc mode flag
	cpi	1		;VC?
	jz	rstar1		;yes, keep going
;
;	set flow control ready if in PVC mode
	res	5,m		;reset ready state p1
	setb	0,m		;and set flow control state d1
;
rstar1:
	mov	a,c		;release buffer
	call	rlsrxb		;	/
	bit	1,m		;DTE restart request state (r2)?
	jz	rstar2		;no, keep going
;
;	DTE in restart request state (r2)
	res	1,m		;clear state r2
	call	t20off		;stop timer T20
	ret			;and exit
;
;	DTE not in restart request state
rstar2:
	call	reset		;reset flow control variables
	lxi	h,l4stat	;point to level 4 status
	setb	0,m		;signal console prompt

;***	do other things here?
;
	call	ilprt
	db	'L3: rx restart',cr,lf,0
	jmp	txstac		;and transmit restart confirmation



;	process received restart confirmation
;	(internally called)
;	on entry:	<b>=octet 3		
;			<c>=rx buffer #

rxstac:	lxi	h,chstat	;point to channel status
	bit	1,m		;DTE restart request state?
	jz	flush		;no, discard packet
;
;	DTE in restart request state (r2)
	res	0,m		;clear state d1 for now
	res	1,m		;and state r2
	res	2,m		;and state p2
	res	3,m		;and state d2
	res	4,m		;and state p6
	setb	5,m		;set ready state p1
	lda	pvcmod		;get PVC mode flag
	cpi	1		;VC?
	jz	rstac1		;yes, keep going
;
;	set flow control ready if in PVC mode
	res	5,m		;reset ready state p1
	setb	0,m		;and set flow control state d1
;
rstac1:
	call	t20off		;stop timer T20
	mov	a,c		;get rx buffer #
	call	rlsrxb		;release buffer
	lxi	h,l4stat	;point to level 4 status
	setb	0,m		;signal console prompt
	ret			;and exit
;



;	reset DTE

dterst:
	mov	a,c		;release buffer
	call	rlsrxb		;	/
	call	ilprt
	db	cr,lf,'L3: local procedure error - '
	db	'resetting channel',cr,lf,0
	call	reset		;reset flow control variables
	jmp	txrstr		;and transmit reset request






;	*************************************************
;	*  utility subroutines for receive section	*
;	*************************************************


;	discard data packet
;	(internally called)
;	on entry:	<c>=rx buffer #
;	on exit:	all regs, flags clobbered

flush:	
	mov	a,c		;get rx buffer #
	call	rlsrxb		;release it
	lhld	rxxpct		;increment discarded packet count
	inx	h		;	/
	shld	rxxpct		;      /
	ret


;	acknowledge received data packet & update window
;	(internally called)
;	on entry:	<b>=octet 3 of data packet
;	on exit:	carry set if P(s) or P(r) sequence error
;			<a>, other flags clobbered
;			all other regs unchanged

ackd:	
	push	h		;save regs
	push	d		;	/
	push	b		;      /
	call	chkpr		;check P(r) and update window
	jc	ackdexi		;exit with carry if invalid P(s)

;	check for valid rx P(s)
	mov	a,b		;get octet 3
	ani	0000$1110b	;extract rx P(s)
	rrc			;move to bits 0-2
	mov	c,a		;save rx P(s) in <c>
;
;	check for P(s) next in sequence
	lda	lrxps		;get last rx P(s)
	inr	a		;bump mod 7
	ani	7		;	/
	cmp	c		;next in sequence?
	jnz	ackderr		;no, invalid
;
;	calculate top of valid range for rx P(s)
	lda	pr		;get local P(r)
	adi	wsize+1		;add window size+1
	ani	7		;mod 7
	mov	e,a		;and save in <e>
;
;	check for rx P(s) within window
	lda	pr		;bottom edge= local P(r)
ackd3:	cmp	e		;past top of window?
	jz	ackderr		;yes, error
;
	cmp	c		;rx P(s) at bottom edge?
	jz	ackdok		;yes, valid rx P(r)
;
	inr	a		;else bump bottom edge
	ani	7		;	/
	jmp	ackd3		;and keep looping
;
;	valid rx P(s)
ackdok:	mov	a,c		;get rx P(s)
	sta	lrxps		;update last rx P(s)
	inr	a		;let P(r)=P(s)+1..
	ani	7		;..mod 7
	sta	pr		;and update P(r)
	stc			;clear carry bit
	cmc			;	/
	jmp	ackdexi		;and exit
;
;	invalid  rx P(s)
ackderr:
	stc			;set carry bit

;	common exit
ackdexi:
	pop	b		;restore regs
	pop	d		;	/
	pop	h		;      /
	ret


;	check received P(r) and update window if valid
;	(internally called)
;	on entry:	<b>=octet 3 of rx packet
;	on exit:	carry set if invalid rx P(r)
;			<a>,other flags clobbered
;			all other regs unchanged

chkpr:	push	h		;save regs
	push	d		;	/
	push	b		;      /
	mov	a,b		;get octet 3
	ani	1110$0000b	;extract rx P(r)
	rrc			;move to bits 0-2
	rrc			;	/
	rrc			;      /
	rrc			;     /
	rrc			;    /
	mov	c,a		;save rx P(r) in <c>
	lda	lastpr		;get last rx P(r)
	cmp	c		;same as this one?
	jz	chkexi		;yes, nothing new
;
;	calculate top of valid range for rx P(r)
	lda	ps		;get local P(s)
	inr	a		;increment mod 7...
	inr	a		;..past top of valid range
	ani	7		;	/
	mov	e,a		;save in <e>
;
;	check for rx P(r) within window
	lda	lastpr		;bottom edge=last rx P(r)
chkpr1:	cmp	e		;past top of window?
	jz	chkerr		;yes, invalid
	cmp	c		;rx P(r) at bottom edge?
	jz	chkpr2		;yes, valid
	inr	a		;else bump bottom edge
	ani	7		;mod 7
	jmp	chkpr1		;and keep looping
;
;	valid rx P(r)
chkpr2:	mov	a,c		;get rx P(r)
	sta	lastpr		;update lower window edge
	xra	a		;clear carry flag
	jmp	chkexi		;and exit
;
;	invalid rx P(r)
chkerr:	stc			;set carry flag
;
;	common exit
chkexi:	pop	b		;restore regs
	pop	d		;	/
	pop	h		;      /
	ret


;	check for valid local DTE address in call request packet
;	(internally called)
;	on entry:	<hl>=bcb address
;			<c>=rx buffer #
;	on exit:	carry set if address is invalid
;			<a>,flags clobbered
;			all other regs unchanged

chkcadr:
	push	b		;save regs
	push	d		;	/
	push	h		;      /
	lxi	h,l3stat	;point to level 3 status
	res	5,m		;clear address error flag
	pop	h		;restore bcb address
	lda	rxoc4		;get rx address length octet
	mov	b,a		;save in <b>
	call	getmsn		;get high nibble
	sta	caddrl		;store calling address length
	mov	a,b		;get back octet 4
	ani	0000$1111b	;get called address length
	mov	b,a		;save it in <b>
	lda	laddrl		;get our address length
	cmp	b		;same?
	cnz	adderr		;no, signal error
	lxi	d,laddr		;<de> points to our address
	inr	b		;bump received called address length
;
;	check to see if called address is ours
laddr1:	dcr	b		;last called address byte?
	jz	caddr1		;yes, go read calling address
;
	call	getbuf		;else get next address octet
	cc	adderr		;signal error if not there
	mov	c,a		;save octet in <c>
	call	getmsn		;get high nibble
	xchg			;point to our address
	cmp	m		;match?
	cnz	adderr		;no, signal error
	xchg			;point back to bcb
	inx	d		;bump pointers
	dcr	b		;last called address byte?
	jz	caddr0		;yes, go read calling address
;
	mov	a,c		;get back octet
	ani	0000$1111b	;get low nibble
	xchg			;point to our address
	cmp	m		;match?
	cnz	adderr		;no, signal error
	xchg			;point back to bcb
	inx	d		;bump pointer
	jmp	laddr1		;and look for next octet
;
;	read and store calling address if at high nibble
caddr0:	lda	caddrl		;get calling address length
	mov	b,a		;save in <b>
	lxi	d,caddr		;point to calling address buffer
	inr	b		;bump received calling address length
	jmp	caddr3		;and keep going
;
;	read and store calling address if at low nibble
caddr1:	lda	caddrl		;get calling address length
	mov	b,a		;save in <b>
	lxi	d,caddr		;point to calling address buffer
	inr	b		;bump received calling address length
;
;	read address if at low nibble
caddr2:	dcr	b		;last address nibble?
	jz	cadexi		;yes, exit
;
	call	getbuf		;get next octet
	cc	adderr		;else signal error
	mov	c,a		;save in <c>
	call	getmsn		;get high nibble
	stax	d		;save at pointer location
	inx	d		;bump pointers
;
;	read address if at high nibble
caddr3:	dcr	b		;last address nibble?
	jz	cadexi		;yes, exit
;
	mov	a,c		;else get back octet
	ani	0000$1111b	;get low nibble
	stax	d		;and save at pointer location
	inx	d		;bump pointer
	jmp	caddr2		;and get next octet
;
;	exit with carry set if error occured
cadexi:	stc			;set carry flag
	push	h		;save bcb
	lxi	h,l3stat	;point to level 3 status
	bit	5,m		;called address error?
	pop	h		;restore bcb
	jnz	cadexi1		;and exit with carry
;
	cmc			;else clear carry
cadexi1:
	pop	d		;restore regs
	pop	b		;	/
	ret


;	get high nibble of octet
;	(internally called)
;	on entry:	<a>=octet
;	on exit:	<a>=high nibble of octet
;			all other regs unchanged

getmsn:
	ani	1111$0000b	;strip out high nibble
	rrc			;and move to bits 0-4
	rrc			;	/
	rrc			;      /
	rrc			;     /
	ret


;	signal called DTE address error
;	(internally called)
;	on entry:	no parameters
;	on exit:	l3stat bit 5=1
;			all regs unchanged
;
adderr:	push	h		;save <hl>
	lxi	h,l3stat	;point to level 3 status
	setb	5,m		;set address error bit
	pop	h		;restore <hl>
	ret

;	check for valid addresses in call accepted packet
;	(internally called)
;	on entry:	<hl>=rx bcb address
;	on exit:	carry set if address error
;			all other regs unchaged
chkadr:
;***	not implemented yet
	xra	a
	ret




;	*****************
;	*  data area	*
;	*****************

	dseg		;data area

;	level 3 status indicators

l3stat:	db	0	;level 3 status flags
chstat:	db	0	;logical channel state

;	X.25 address and channel parameters
group	db	0	;logical group number
chan	db	lchan	;logical channel number
gfi	db	0	;general format identifier
laddrl:	db	0	;local address length (# of hex chars)
raddrl:	db	0	;remote address length (# of hex chars)
caddrl:	db	0	;rx calling DTE address length
laddr:	ds	15	;local DTE address (1 hex char/byte)
raddr:	ds	15	;remote DTE address (1 hex char/byte)
caddr:	ds	15	;rx calling DTE address (1 hex char/byte)
rxoc4:	db	0	;received address length octet 4


;	X.25 packet flow control variables

pr	db	0	;P(r)
ps	db	0	;P(s)
lastpr	db	0	;last rx P(r)
ltxpr	db	0	;last tx P(r)
lrxps	db	7	;last rx P(s)
ltxps	db	7	;last tx P(s)
qbit	db	0	;Q (qualifier) bit (bit 7)
dbit	db	0	;D (delivery confirmation) bit (bit 6)
rtryct	db	n3	;tx retry count
pvcmod	db	1	;VC/PVC mode flag (1=VC,2=PVC)
packct	db	kpack	;packet acknowledgement delay count

;	level 3 diagnostic counters
	
txpct:	dw	0	;total tx packets
txfpct:	dw	0	;tx file data packets
txcpct:	dw	0	;tx console data packets
ntxbct:	dw	0	;errors due to no free tx bcb
;
rxpct:	dw	0	;total rx packets
rxdpct:	dw	0	;total rx data packets
rbfpct:	dw	0	;total rx bad format packets
rbcpct:	dw	0	;total rx bad channel packets
rbapct:	dw	0	;total rx bad address packets
rbipct:	dw	0	;total rx bad id packets
rbgpct:	dw	0	;total rx bad group packets
rxxpct:	dw	0	;total discarded rx packets
