#-h- srdat.mac       3042  asc  23-apr-81 06:56:04  [002,010]
	.title	srdrv
;
;
;	Send/receive driver for RSX-11M
;
;
	.ident	/01/
;
;
;	Joe Sventek
;	Computer Science and Applied Mathematics
;	Lawrence Berkeley Laboratory
;	Berkeley, CA  94720
;
;	15-aug-80
;
;
;	This driver implements a variable length send/receive capability for
;	RSX-11M.  Packets are queued in internal buffers for a task which
;	need not be installed.  A timeout is associated with each message,
;	and the buffers are recycled if the time expires before the message
;	is read.
;
;	macro library calls
;
	.mcall	pktdf$,clkdf$,tcbdf$,ucbdf$,qiosy$
	pktdf$				; define I/O packet offsets
	clkdf$				; define clock queue block offsets
	tcbdf$				; define task control block offsets
	ucbdf$				; define unit control block offsets
	qiosy$				; define qio symbols locally
	.page
	.sbttl	local symbols
;
;
;	local symbols
;
;
;	offsets for header blocks
;
	.asect
.=0
h_flnk:	.blkw	1			; forward link
h_blnk:	.blkw	1			; backward link
h_stsk:	.blkw	2			; sender task name (rad50)
h_rtsk:	.blkw	2			; receiver task name (rad50)
h_size:	.blkw	1			; size of message in bytes
h_time:	.blkw	1			; seconds until purge of message
h_bufp:	.blkw	1			; pointer to first chained data buffer
h_lgth=.-2				; length of block minus flink word
	.psect
;
;
;	local offset definition for clock queue block
;
c.ucb=c.ar5+2				; UCB address of SR: stored here
					; this word unused in clock block
c.cqid=c.tcb				; field for cqid
;
;
;	local offset definitions for u.cw2 and u.cw3 in UCB
;
u.hdrf=u.cw2				; points to most recently inserted hdr
u.hdrb=u.cw3				; points to least recently inserted hdr
;
;
;	local offset definitions for I/O request packet
;
i.p1=i.prm				; p1
i.p2=i.prm+4				; p2
i.p3=i.p2+2				; p3
i.p4=i.p3+2				; p4
i.p5=i.p4+2				; p5
i.p6=i.p5+2				; p6
;
;
;	definitions for timer processing (all in units of seconds)
;
tdeflt=30.				; default timeout period for a packet
tlow=2*tintpt				; minimum timeout period
thigh=10.*60.				; maximum timeout period (10 minutes)
	.page
	.sbttl	local macros
;
;
;	local macros
;
;
;	jcs - jump on c bit set
;
	.macro	jcs	dest,?lab
	bcc	lab
	jmp	dest
lab:
	.endm	jcs
;
;	jeq - jump on equal (z bit set)
;
	.macro	jeq	dest,?lab
	bne	lab
	jmp	dest
lab:
	.endm	jeq
;
;	push - push register onto stack
;
	.macro	push	reg
	mov	reg,-(sp)
	.endm	push
;
;
;	pop - pop register from stack
;
	.macro	pop	reg
	mov	(sp)+,reg
	.endm	pop
;
;	putnod - put a node back onto free list
;
	.macro	putnod	reg,listhd
	mov	listhd,('reg')
	mov	reg,listhd
	.endm	putnod
;
;	error - place return code in r0 and finish io
;
	.macro	error	reason
	mov	#ie.'reason'&377,r0
	callr	zrfin
	.endm	error
	.page
	.sbttl	local data and dispatch table
;
;
;	local data (except for headers and buffers, which follow driver code)
;
;
hdrpt:	.word	hdr0			; free list listhead for msg headers
bufpt:	.word	buf0			; free list listhead for buffers
;
;
;	driver dispatch table
;
;
$srtbl::
	.word	srini			; device initiator entry point
	.word	srnop			; cancel io - NOOP
	.word	srnop			; device timeout - NOOP
	.word	srpwf			; powerfail
#-h- srini.mac       1991  asc  15-apr-81 08:18:38  [002,010]
	.page
	.sbttl	srini - initiate io for send/receive device
;+
;	srini - send/receive driver initiator
;
;	this routine is entered from DRQIO when an io request is queued
;
;	all operations are handled at queue level
;
;	inputs
;		r1	IRP of request
;		r5	UCB of controller
;
;	outputs
;		if request is a write, message is queued, if buffer
;		space is available
;
;		if request is a read, the next message buffered for the
;		requesting task is dequeued
;
; format of IRP for send/receive driver
;
;	wd 00	io queue thread word
;	wd 01	request priority, event flag number
;	wd 02	TCB address of requestor
;	wd 03	pointer to second LUN word in task header
;	wd 04	contents of first LUN word in task header
;	wd 05	io function code (io.rlb, io.wlb)
;	wd 06	virtual address of io status block
;	wd 07	relocation bias of io status block
;	wd 10	io status block address (real or displacement + 140000)
;	wd 11	virtual address of AST service routine
;	wd 12	relocation bias of io buffer
;	wd 13	buffer address (real or displacement + 140000)
;	wd 14	number of bytes to transfer or size of buffer
;	wd 15	high order half of receiver task name (WLB)
;	wd 16	low order half (WLB)
;	wd 17	time out period in seconds (WLB)
;	wd 20	if non-zero, all packets queued for this sender/receiver
;		pair are flushed before the current message is queued
;	wd 21	unused
;
;-
	.enabl	lsb
srini:
	mov	kisar6,-(sp)		; save current APR6 mapping
	mov	r1,r3			; place IRP in r3 for duration
	mov	i.tcb(r3),r4		; place TCB address for duration
	mov	i.p2(r3),u.cnt(r5)	; place byte count in UCB
	movb	i.fcn+1(r3),r0		; fetch function code
	cmpb	r0,#io.wlb/256.		; write logical block?
	jeq	write			; yes, go do it
	cmpb	r0,#io.rlb/256.		; read logical block?
	jeq	read			; yes, go do it
	cmpb	r0,#io.stp/256.		; stop timer?
	jeq	stop			; yes, go do it
	mov	#ie.ifc&377,r0		; illegal function code
zrfin:
	clr	r1			; iosb(2) = 0 on errors
srfin:
	mov	(sp)+,kisar6		; restore mapping
	callr	$iofin			; finish up
	.dsabl	lsb
#-h- write.mac       1751  asc  15-apr-81 08:00:13  [002,010]
	.page
	.sbttl	process write requests
;+
;	this routine alocates a header and the necessary buffers.
;	if all are successful, the user's data is then
;	copied into the driver buffers, the header is linked into the UCB's
;	queue.
;
;	inputs
;		r3	IRP address
;		r4	TCB address
;		r5	UCB address
;-
	.enabl	lsb
write:
	tst	i.p3(r3)		; see if user supplied name
	bne	10$			; if !=, yes
	tst	i.p4(r3)		; check if second half is non-zero
	bne	10$			; if !=, yes
	error	bad			; bad parameters
10$:
	tst	i.p6(r3)		; flush old messages?
	beq	20$			; if == 0, NO
	call	mflush			; flush them
20$:
	mov	#hdrpt,r1		; header free listhead in r1
	call	getpkt			; get a header into r0
	jcs	hdrerr			; c set => no more headers
	mov	r0,r2			; need header address in r2
	mov	i.p2(r3),h_size(r2)	; copy message size into header
	call	getbuf			; allocate buffers for message
	jcs	buferr			; c set => no room to buffer message
	mov	i.p3(r3),h_rtsk(r2)	; copy receiver taskname into header
	mov	i.p4(r3),h_rtsk+2(r2)
	mov	t.nam(r4),h_stsk(r2)	; copy sender taskname into header
	mov	t.nam+2(r4),h_stsk+2(r2);
	call	usrsys			; copy user's data into buffers
	mov	i.p5(r3),r0		; get user supplied timeout
	bne	100$			; if !=, got one
	mov	#tdeflt,r0		; default time limit in r0
100$:					; see if too large
	cmp	r0,#thigh
	ble	110$
	mov	#thigh,r0
110$:					; see if too small
	cmp	r0,#tlow
	bge	120$
	mov	#tlow,r0
120$:					; just right
	mov	r0,h_time(r2)		; store time limit in header
	call	inshdr			; insert header into UCB queue
	mov	#is.suc&377,r0		; success return
	mov	i.p2(r3),r1		; return byte count
	callr	srfin			; finish up
buferr:
	putnod	r2,hdrpt		; return header to free list
	error	alc			; buffers exhausted
hdrerr:
	error	nod			; headers exhausted
	.dsabl	lsb
#-h- read.mac        1174  asc  15-apr-81 08:00:15  [002,010]
	.page
	.sbttl	process read requests
;+
;	scan through header list for oldest packet matching the
;	taskname of the requestor.  if found, copy the buffer to the user's
;	buffer and recycle the header and driver buffers
;
;	inputs
;		r3	IRP address
;		r4	TCB address
;		r5	UCB address
;-
	.enabl	lsb
read:
	mov	u.hdrb(r5),r2		; oldest header block in r2
10$:
	beq	50$			; last move == 0, no match
	cmp	t.nam(r4),h_rtsk(r2)	; compare first half of task name
	bne	20$			; if !=, try next header in list
	cmp	t.nam+2(r4),h_rtsk+2(r2); compare second half of task name
	beq	30$			; if ==, copy message
20$:
	mov	h_blnk(r2),r2		; get next header
	br	10$			; try again
30$:
	call	sysusr			; copy to user's buffer
	call	putbuf			; return buffers to free list
	call	remhdr			; remove header from UCB list
	putnod	r2,hdrpt		; return header to free list
	mov	#is.suc&377,r0		; return success status
	mov	h_size(r2),r1		; bytes copied
	cmp	r1,i.p2(r3)		; see if data overrun
	ble	40$			; if <=, no
	mov	#ie.dao&377,r0		; return data overrun status
	mov	i.p2(r3),r1		; only copied usersize bytes
40$:
	callr	srfin			; done with request
50$:
	error	dna			; data not available
	.dsabl	lsb
#-h- stop.mac        1255  asc  15-jun-81 07:30:22  [307,034]
	.page
	.sbttl	stop - stop timer
;+
;	this routine, called when a task issues a qio with a function
;	code of io.stp, cancels the current timer request for the driver.
;	It checks that the terminal for which the calling task
;	is running is priveleged.  If so, it proceeds to cancel the timer.
;
;	inputs:
;		r3	IRP address
;		r4	TCB address
;		r5	UCB address
;
;	outputs:
;		the clock queue entry for the driver is canceled and
;		the clock queue control block is returned to the DSR
;-
	.enabl	lsb
stop:
	mov	t.ucb(r4),r0		; get TI: UCB address
	bit	#u2.prv,u.cw2(r0)	; see if tty is priveleged
	bne	10$			; if !=, YES - proceed
	error	pri			; privelege violation
10$:
	mov	u.clqb(r5),r0		; place clock block addr in r0
	beq	20$			; if == 0, timer not active
	push	r3			; save r3
	push	r5			; save r5
	mov	#c.syst,r4		; clock queue request type
	mov	r0,r5			; clock queue id is block address
	call	$clrsm			; remove entry from clock queue
	pop	r5			; restore UCB address
	mov	u.clqb(r5),r0		; address of block to return
	mov	#c.lgth,r1		; size of block
	call	$deacb			; return block to DSR
	clr	u.clqb(r5)		; timer no longer active
	pop	r3			; restore IRP address
20$:
	mov	#is.suc&377,r0		; return success status
	callr	zrfin			; finish up
	.dsabl	lsb
#-h- srpwf.mac       1972  asc  15-apr-81 08:00:18  [002,010]
	.page
	.sbttl	srpwf - powerfail entry point
;+
;	this routine is called when the system is booted, upon restoration
;	of power, and when the driver is loaded, since the uc.pwf bit
;	is set in the u.ctl field of the UCB
;
;	this routine allocates a clock queue control block from pool
;	it then fills it in and queues it
;	the interrupt service routine propagates this interrupt.
;
;	If the block cannot be allocated from pool, the driver indicates
;	that the device is offline by setting the bit in the u.st2 field
;	of the UCB and having TKTN issue a device not ready message on
;	the console.
;
;	inputs:
;		r3	controller index
;		r4	SCB address
;		r5	UCB address
;
;	outputs:
;	   1.	the delta time for each clock interrupt is calculated and
;		stored in the UCB
;	   2.	the address of the allocated clock block is stored in the UCB
;	   3.	a clock interrupt is queued for the driver
;
;-
	.enabl	lsb
srpwf:
	tst	u.clqb(r5)		; see if timer already active
	bne	20$			; if !=, YES
	push	r0			; save all registers
	push	r1
	push	r2
	push	r3
	push	r4
	push	r5
	mov	#tintpt,r0		; seconds between clock interrupts
	mov	$tkps,r1		; clock ticks/second
	call	$mul			; double word delta time in r0,r1
	mov	r0,u.tim1(r5)		; save in UCB
	mov	r1,u.tim2(r5)
	mov	#c.lgth,r1		; size of clock queue core block
	call	$alocb			; allocate from pool
	bcs	clkerr			; c set => allocation failure
	mov	r0,u.clqb(r5)		; save address of clock block
	mov	r5,c.ucb(r0)		; save UCB addr in clock block
	mov	#timer,c.sub(r0)	; address of interrupt service rtn
	mov	u.tim1(r5),r1		; delta time for $clins
	mov	u.tim2(r5),r2
	mov	#c.syst,r4		; clock queue request type
	mov	r0,r5			; clock queue id is block address
	call	$clins			; insert into clock queue
	br	10$
clkerr:
	bisb	#us.ofl,u.st2(r5)	; set device offline
	mov	#t.ndnr,r0		; issue a device not ready
	call	$dvmsg			; to console via TKTN
10$:
	pop	r5			; restore registers
	pop	r4
	pop	r3
	pop	r2
	pop	r1
	pop	r0
20$:
	return
	.dsabl	lsb
#-h- srnoop.mac       150  asc  15-apr-81 08:00:20  [002,010]
	.page
	.sbttl	noop - cancel io and timeout entry points
;+
;	both of these entry points correspond to a return to the
;	executive.
;-
srnop:
	return
#-h- timer.mac       1587  asc  15-apr-81 08:00:21  [002,010]
	.page
	.sbttl	timer - process clock queue interrupts
;+
;	this routine is entered when the driver's clock queue entry comes
;	due.  it traverses the list of messages, decrementing the timeout
;	field in the message headers.  If the timeout field drops below 0,
;	the header and message buffers are recycled.  Another clock queue
;	request is then made, thus propagating this self-cleansing process.
;
;	inputs
;		r4	address of clock queue core block
;
;	outputs
;		if matching header is found, that message is purged from list
;		another clock queue request is made
;-
	.enabl	lsb
timer:
	push	r0			; this register manipulation is
	push	r1			; done to permit timer to call
	push	r2			; putbuf and remhdr, which conform
	push	r3			; to the internal register standard
	push	r4			; of the driver
	push	r5
	mov	c.ucb(r4),r5		; place UCB address in r5
	mov	u.hdrf(r5),r3		; get first header address in r3
10$:
	mov	r3,r2			; get next header address
	beq	20$			; if last move == 0, done scanning
	mov	h_flnk(r2),r3		; place next header addr in r3
	sub	#tintpt,h_time(r2)	; subtract no of seconds from limit
	bgt	10$			; if > 0, try next packet
	call	putbuf			; return buffers
	call	remhdr			; remove header from queue
	putnod	r2,hdrpt		; return header to free list
	br	10$
20$:
	mov	u.clqb(r5),r0		; clock block in r0
	mov	u.tim1(r5),r1		; delta time in r1-r2
	mov	u.tim2(r5),r2
	mov	#c.syst,r4		; request type
	mov	r0,r5			; clock queue id is address of block
	call	$clins			; insert into clock queue
	pop	r5			; restore registers
	pop	r4
	pop	r3
	pop	r2
	pop	r1
	pop	r0
	return
	.dsabl	lsb
#-h- mflush.mac      1180  asc  15-apr-81 08:01:31  [002,010]
	.page
	.sbttl	mflush - flush old messages
;+
;	this routine traverses the list of messages, purging any matching
;	the current sender-receiver task pair.  The receiver name is in the
;	I/O request packet, and the sender name is in the TCB
;
;	inputs
;		r3	IRP address
;		r4	TCB address
;		r5	UCB address
;
;	outputs
;		old messages matching the current task pair are purged
;-
	.enabl	lsb
mflush:
	mov	u.hdrf(r5),r1		; first header address in r1
10$:
	mov	r1,r2			; next header address in r2
	beq	20$			; if == 0, done scanning list
	mov	h_flnk(r2),r1		; next header address in r1
	cmp	i.p3(r3),h_rtsk(r2)	; first half of rcvr task?
	bne	10$			; NO, try next message
	cmp	i.p4(r3),h_rtsk+2(r2)	; second half of rcvr task?
	bne	10$			; NO, try next message
	cmp	t.nam(r4),h_stsk(r2)	; first half of sender task?
	bne	10$			; NO, try next message
	cmp	t.nam+2(r4),h_stsk+2(r2); second half of sender task?
	bne	10$			; NO, try next message
	push	r1			; save volatile register
	call	putbuf			; return message buffers
	call	remhdr			; remove header from list
	putnod	r2,hdrpt		; return header to free list
	pop	r1			; restore r1
	br	10$			; try next message
20$:
	return
	.dsabl	lsb
#-h- getbuf.mac      1317  asc  15-apr-81 08:01:33  [002,010]
	.page
	.sbttl	getbuf - get buffers necessary to store message
;+
;	inputs:
;		r2	header block
;		r3	IRP address
;		r4	TCB address
;		r5	UCB address
;		r0,r1	free for use
;
;	outputs:
;		c set	insufficient buffer space
;		c clear	buffers were allocated. h_bufp has address of first
;-
	.enabl	lsb
getbuf:
	push	r3		; save registers used
	push	r4
	mov	h_size(r2),r0	; get number of bytes needed
	cmp	r0,u.cw4(r5)	; see if within limits
	bgt	errout		; too big, return
	dec	r0		; ((n-1)/B)+1
	mov	#bufsiz,r1	; divisor in r1
	call	$div		; divide r0 by r1, quotient in r0
	inc	r0		; r0 = number of bufs needed (>=1)
	mov	r0,r3		; save this number
	mov	#bufpt,r1	; buffer listhead
10$:
	mov	(r1),r1		; get next buffer address
	beq	errout		; if == 0, not enough buffers
	sob	r0,10$		; decrement count and try again
;
;	at this point, we are assured of enough buffers for message
;
	mov	#bufpt,r1	; listhead again
	clr	r4		; address of previous node
20$:
	call	getpkt		; get node address in r0
	mov	r4,(r0)		; link in previous packets
	mov	r0,r4		; save address
	sob	r3,20$		; decrement count and try again
	mov	r4,h_bufp(r2)	; place first buf addr in header
	clc			; c clear => success
	br	30$
errout:
	sec			; c set => allocation failure
30$:
	pop	r4		; restore registers - doesn't affect c bit
	pop	r3
	return
	.dsabl	lsb
#-h- putbuf.mac       619  asc  15-apr-81 08:01:34  [002,010]
	.page
	.sbttl	putbuf - return buffers to free list
;+
;	putbuf restores the buffers pointer to by the header block to the
;	free list of buffers
;
;	inputs
;		r2	header pointing to buffers
;		r3	IRP address
;		r4	TCB address
;		r5	UCB address
;		r0,r1	free for use
;
;	outputs
;		the buffers are returned to the free list of buffers
;-
	.enabl	lsb
putbuf:
	mov	h_bufp(r2),r1		; address of first buffer in r1
10$:
	mov	r1,r0			; address of next buffer in r0
	beq	20$			; if == 0, done
	mov	(r0),r1			; next buffer address in r1
	putnod	r0,bufpt		; return node to free list
	br	10$			; do again
20$:
	return
	.dsabl	lsb
#-h- inshdr.mac       909  asc  15-apr-81 08:01:35  [002,010]
	.page
	.sbttl	inshdr - inserts header into UCB queue
;+
;	this routine inserts the specified header into the queue pointed
;	to by the UCB.  The header is inserted on the forward link side
;	of the UCB.  Refer to the documentation on the driver for the
;	exact structure of the headers and the linkages.
;
;	inputs
;		r2	header to insert
;		r3	IRP address
;		r4	TCB address
;		r5	UCB address
;		r0,r1	free for use
;
;	outputs
;		header is linked into UCB queue
;-
	.enabl	lsb
inshdr:
	mov	u.hdrf(r5),r0		; most recently inserted header
	beq	10$			; queue was empty
	mov	r2,h_blnk(r0)		; make that node point to this one
	clr	h_blnk(r2)		; new node has no backward link
	mov	r0,h_flnk(r2)		; new node points to old first node
	mov	r2,u.hdrf(r5)		; UCB queue points to new node
	br	20$
10$:
	mov	r2,u.hdrf(r5)		; initialize new queue
	mov	r2,u.hdrb(r5)
	clr	h_flnk(r2)
	clr	h_blnk(r2)
20$:
	return
	.dsabl	lsb
#-h- remhdr.mac       706  asc  15-apr-81 08:01:37  [002,010]
	.page
	.sbttl	remhdr - remove header from UCB queue
;+
;	inputs
;		r2	header to remove
;		r3	IRP address
;		r4	TCB address
;		r5	UCB address
;		r0,r1	free for use
;
;	outputs
;		header is removed from queue and queue is relinked
;-
	.enabl	lsb
remhdr:
	mov	h_flnk(r2),r0		; get forward link of ill-fated node
	beq	10$			; if == 0, then end of list
	mov	h_blnk(r2),h_blnk(r0)	; redo neighbor's blink
	br	20$
10$:
	mov	h_blnk(r2),u.hdrb(r5)	; redo listhead's blink
20$:
	mov	h_blnk(r2),r0		; get backward link of ill-fated node
	beq	30$			; if == 0, then end of list
	mov	h_flnk(r2),h_flnk(r0)	; redo neighbor's flink
	br	40$
30$:
	mov	h_flnk(r2),u.hdrf(r5)	; redo listhead's flink
40$:
	return
	.dsabl	lsb
#-h- sysusr.mac      1452  asc  15-apr-81 08:32:39  [002,010]
	.page
	.sbttl	sysusr - copy contents of driver buffer to user
;+
;	this routine copies the contents of the driver buffer[s] into
;	the user's buffer.  If the user's buffer is smaller than the
;	amount of data buffered, then only that many bytes are copied.
;	When this occurs, the user receives the code ie.dao (data
;	overrun) in iosb(1).
;
;	inputs:
;		r2	header address
;		r3	IRP address
;		r4	TCB address
;		r5	UCB address
;		r0,r1	free for use
;
;	outputs:
;		the data has been copied into the user's buffer
;-
	.enabl	lsb
sysusr:
	push	r4			; save registers
	push	r2			; ...
	cmp	h_size(r2),u.cnt(r5)	; compare size of msg with user's buf
	bgt	10$			; msg size too big, leave u.cnt as is
	mov	h_size(r2),u.cnt(r5)	; only move <size of msg> bytes
10$:
	tst	u.cnt(r5)		; see if zero length transfer
	beq	30$			; if == 0, YES
	mov	h_bufp(r2),r4		; get first buffer address
	mov	i.p1(r3),kisar6		; map to user's buffer
	mov	i.p1+2(r3),r2		; kernel virtual address
	br	25$			; start copy
20$:
	movb	(r0)+,(r2)+		; place byte in user buffer
	dec	u.cnt(r5)		; decrement count
	beq	30$			; if == 0, all done
	sob	r1,20$			; decrement bytes/buffer count
	mov	(r4),r4			; get next chained buffer
	beq	30$			; if == 0, all done
25$:
	mov	r4,r0			; need buffer address in r0
	tst	(r0)+			; bump pointer past link field
	mov	#bufsiz,r1		; bytes/buffer counter
	br	20$			; move some more
30$:
	pop	r2			; restore registers
	pop	r4			; ...
	return
	.dsabl	lsb
#-h- usrsys.mac      1120  asc  23-apr-81 06:56:24  [002,010]
	.page
	.sbttl	usrsys - copy contents of user buffer to driver
;+
;	this routine copies the contents of the user's buffer into
;	the driver's internal buffer[s].
;
;	inputs:
;		r2	header address
;		r3	IRP address
;		r4	TCB address
;		r5	UCB address
;		r0,r1	free for use
;
;	outputs:
;		the contents of the user's buffer are copied into the
;		driver's buffer[s]
;-
	.enabl	lsb
usrsys:
	push	r4			; save registers
	push	r2			; ...
	tst	u.cnt(r5)		; see if zero length transfer
	beq	20$			; if == 0, YES
	mov	h_bufp(r2),r4		; address of first buffer
	mov	i.p1(r3),kisar6		; map to user's buffer
	mov	i.p1+2(r3),r2		; kernel virtual address
	br	15$			; start copy
10$:
	movb	(r2)+,(r0)+		; copy byte into driver buffer
	dec	u.cnt(r5)		; decrement count
	beq	20$			; if == 0, all done
	sob	r1,10$			; decrement byte/buffer counter
	mov	(r4),r4			; address of next chained buffer
	beq	20$			; if == 0, all done
15$:
	mov	r4,r0			; need address in r0
	tst	(r0)+			; bump pointer past link field
	mov	#bufsiz,r1		; bytes/buffer
	br	10$			; copy some more
20$:
	pop	r2			; restore registers
	pop	r4			; ...
	return
	.dsabl	lsb
#-h- getpkt.mac       633  asc  15-apr-81 08:01:41  [002,010]
	.page
	.sbttl	getpkt - fetch a node from a free list
;+
;	this routine fetches a single node from a singly linked
;	list.  It is assumed that the forward link field in each node in the
;	list is contained at an offset of 0 from the node address.
;
;	inputs
;		r1	listhead address
;
;	outputs
;		r0	node fetched from list
;		c set	if no more nodes left
;		c clear if successfully fetched a node
;-
	.enabl	lsb
getpkt:
	mov	(r1),r0			; address of next node in r0
	beq	10$			; if == 0, no more nodes
	mov	(r0),(r1)		; relink list
	clc				; c clear => success
	br	20$
10$:
	sec				; c set => allocation failure
20$:
	return
	.dsabl	lsb
#-h- srbufs.mac       797  asc  15-apr-81 08:01:42  [002,010]
	.page
	.sbttl	free lists of headers and buffers
;+
;	the free list of buffers and headers fill up the rest of the
;	driver.
;	The appropriate constants below may be changed if more or less
;	of these data blocks are needed.
;-
;
;
;	symbols
;
;
bufsiz=32.			; number of bytes of data in each buffer
avnbuf=3			; average number of buffers per msg
nbufs=avnbuf*nhdrs		; number of buffers
;
;
;	macros for building linked lists
;
;
	.macro	trmn8r	str,num
str'num=0
	.endm	trmn8r
	.macro	bldnod	str,size,num,nxt
str'num:
	.word	str'nxt
	.blkb	size
	.endm	bldnod
;
;
;	build header list
;
;
	trmn8r	hdr,\nhdrs
ctr=0
	.rept	nhdrs
	bldnod	hdr,h_lgth,\ctr,\<ctr+1>
ctr=ctr+1
	.endr
;
;
;	build buffer list
;
;
	trmn8r	buf,\nbufs
ctr=0
	.rept	nbufs
	bldnod	buf,bufsiz,\ctr,\<ctr+1>
ctr=ctr+1
	.endr
	.end
