Google
 

Trailing-Edge - PDP-10 Archives - bb-x130a-sb - dnsnup.mac
There are 9 other files named dnsnup.mac in the archive. Click here to see a list.
	TITLE	DNSNUP		DECnet-10 Message Trace program
	SUBTTL	Tarl Neustaedter
	SEARCH	DCN,SNUP,D36PAR,S
	.REQUI REL:SNUP
	XP $ONLY,I.LUO!I.PRM!I.GTT
	$INIT	MSG

;Revision history.
;1	Write the program.
;2	Add code to make sure program is running on same type of processor
;	it was assembled for. XBLT vs BLT data structures can look different.
;3	Search DCN.
;4	Don't call WAKJOB with the interlock set.
;5	Avoid trashing T1 before calling SAVT.
;6	Add entry point for local messages
;7	Add checks for monitor skew, add error (^C) trapping.

;Check for right version of SNUP.

IFN V.SNUP-5,<
	PRINTX ?Wrong version of SNUP. Please obtain the latest version.
>

;Make version global so we get a multiply defined if wrong version
;rel file is loaded after using the right universal.
	V.SNUP==:V.SNUP

	XP BUFSIZ,5000			;Buffer size.
	XP BPTN,30			;Number of breakpoints we will
					; ask SNUP to handle.

DEFINE POINTS,<
ZZ=.
	AT RTROTR,[PUSHJ %P,ORECVD]	;Output message received.
	AT RTRITR,[PUSHJ %P,IRECVD]	;Input message received
	AT RTRLTR,[PUSHJ %P,LRECVD]	;local message.
	AT BP$000,[PUSHJ %P,INITAL]	;Initialization
	AT BP$001,[PUSHJ %P,DESTRO]	;Destroy
TOTBPT=.-ZZ
>

;To flag a word as 'needs to be relocated'
DEFINE	.R.(SYM),<SYM
	SYMCNT==SYMCNT+1
	GENSYM(M,\SYMCNT)==.-1
	GENSYM(A,\SYMCNT)==LOKADR
>

DEFINE	.M.(SYM),<0
	SYMCNT==SYMCNT+1
	GENSYM(M,\SYMCNT)==.-1
	GENSYM(A,\SYMCNT)==SYM
>

SYMCNT==0

;To generate a symbol given a numeric argument
DEFINE GENSYM(PRE,NUM),<PRE'..'NUM>
;To delete symbol defined above
DEFINE DELSYM(PRE,NUM),<PURGE PRE'..'NUM>
;to define a symbol for SNOOP.
DEFINE SYMDEF(SYM),<SYM: AT SYM>

VRSN.(0,1,0,7)


	SUBTTL	Monitor code
$LOSEG

	%MB==%U			;Define the message block pointer

;Input and output message breakpoint receivers.

ORECVD:
	CAIE	%T1,KF.QOB	;Output?
	 POPJ	%P,		;no, ignore this one.
	PUSHJ	%P,.M.(SAVT)	;Save our ACs
	MOVSI	%T1,IOL.OT_9!TYP.MH ;Flag this is an output message
	JRST	.R.(RECVD)	;Join common code
LRECVD:	PUSHJ	%P,.M.(SAVT)	;Save acs
	MOVSI	%T1,IOL.LO_9!TYP.MH ;Flag this as a local message
	JRST	.R.(RECVD)	;join common code
IRECVD:	PUSHJ	%P,.M.(SAVT)	;Save acs
	MOVSI	%T1,IOL.IN_9!TYP.MH;Flag this is an input message
RECVD:	JSP	%R,.M.(D36PIF)	;Call d36pif to turn avoid races
	MOVEI	%T2,<MB.LEN+MH.LEN>*4;Length of the entry block. (in bytes)
	XMOVEI	%T4,UD.DAT(%MB)	;Get possible pointer to user data
	CAME	%T4,MD.ALA+UD.MSD(%MB) ;Compare against real pointer
	 ADD	%T2,MD.ALL+UD.MSD(%MB)	;Get number of bytes we have allocated
	ADDI	%T2,3		;Round up to nearest word.
	ASH	%T2,-2		;Convert to words
	CAIL	%T2,220+MB.LEN+MH.LEN ;Max length for one of our entries
	 JRST	.R.(FULLBF)	;Pretend buffer is full
	SKIPN	%T3,.R.(DEPADR)	;Next address to store stuff in
	 JRST	.R.(CONTIN)	;Must be unwinding. Ignore message.
	MOVE	%T4,%T3		;Copy address
	ADD	%T4,%T2		;point to where our block would end.
	CAMGE	%T3,.R.(EXMADR)	;Is start above reader's current pointer?
	 CAMGE	%T4,.R.(EXMADR)	;no, would this stomp on the reader's data?
	  SKP			;We won't overwrite the user's message
	 JRST	.R.(FULLBF)	;Would destroy un-read message. drop it.
	CAMGE	%T4,.R.(TOPADR)	;Would we go past the end of the buffer?
	 JRST	.R.(HAVADR)	;no, our pointer is good.
	SETOM	(%T3)		;We have to wrap around. Flag the fact.
	MOVE	%T3,.R.(ORGADR)	;Get start of buffer pointer
	MOVE	%T4,%T3		;copy address
	ADD	%T4,%T2		;Point to end of block
	CAML	%T4,.R.(EXMADR)	;Make sure I am below taker's address.
	 JRST	.R.(FULLBF)	;Would destroy un-read message. Drop it.
HAVADR:	MOVEM	%T2,MH.LNG(%T3)	;save length of this entry
	HRR	%T1,.R.(LSTMSG)	;Get cumulative number of lost messages
	MOVEM	%T1,MH.FLG(%T3)	;save flags for this entry
	MOVE	%T1,.M.(UPTIME)	;Get system uptime as of this message
	MOVEM	%T1,MH.UPT(%T3)	;save in time stamp for this entry
	MOVE	%T1,.M.(DATE)	;Get udt of right now.
	MOVEM	%T1,MH.UDT(%T3)	;save current date/time
	SKIPE	%T1,RM.ICP(%MB)	;Get input circuit pointer
	 MOVE	%T1,RC.LID(%T1)	;Get the line ID for this circuit
	MOVEM	%T1,MH.ILN(%T3)	;Store input circuit circuit id
	SKIPE	%T1,RM.OCP(%MB)	;Get output circuit pointer
	 MOVE	%T1,RC.LID(%T1)	;Get the line ID for this circuit
	MOVEM	%T1,MH.OLN(%T3)	;Store output circuit circuit id
	MOVEM	%MB,MH.OMB(%T3)	;save original message block pointer
	ADDI	%T3,MH.LEN	;Point past header part of message
	MOVE	%T2,%MB		;Copy source pointer
	MOVEI	%T1,MB.LEN	;Number of words to copy
	JSP	%R,.R.(BLTR..)	;Copy the data
	XMOVEI	%T2,UD.DAT(%MB)	;generate a possible pointer to user data
	CAMN	%T2,MD.ALA+UD.MSD(%MB) ;Was the user data within the MB?
	 JRST	.R.(GIVUSR)	;yes, just give current stuff to the user
	MOVE	%T2,MD.ALA+UD.MSD(%MB) ;Pointer to user data block
	SUB	%T4,%T3		;number of words to copy
	MOVE	%T1,%T4		;Put where xblt can find it
	JSP	%R,.R.(BLTR..)	;And copy the data
GIVUSR:
	MOVEM	%T3,.R.(DEPADR)	;Save new deposit address
	AOS	%T1,.R.(PENMSG)	;Bump count of pending messages
	TRNE	%T1,3		;Wake up every 4th message
	 JRST	.R.(CONTIN)	;Not 4th message, continue
	SKP			;Don't increment overflow count
FULLBF:
	AOS	.R.(LSTMSG)	;Bump count of lost messages
	JSP	%R,.M.(D36PIN)	;Turn interrupts back on
	MOVE	%T1,.R.(MYJOB)	;Get job to wake up
	PUSHJ	%P,.M.(WAKJOB)	;Wake him up.
	SKP			;don't try to turn them on twice.
CONTIN:
	JSP	%R,.M.(D36PIN)	;Turn interrupts back on.
	POPJ	%P,		;Return to user

;UUO-level initialization and termination code.
INITAL:
	MOVEI	%T2,BUFSIZ	;No, get some core to use as buffer
	XMOVEI	%T1,.R.(BUFFER)	;*Get pointer to our buffer
	MOVEM	%T1,.R.(ORGADR)	;set up start of buffer
	MOVEM	%T1,.R.(EXMADR)	;Initialize user's pointer.
	XMOVEI	%T2,BUFSIZ(%T1)	;Get pointer to end of buffer
	MOVEM	%T2,.R.(TOPADR)	;save as top address of buffer
	MOVEI	%T2,FH.LEN	;Size of this entry
	MOVEM	%T2,FH.LNG(%T1)	;Save as first entry in buffer
	MOVSI	%T2,TYP.FH	;message type
	HRRI	%T2,MB.LEN	;Message block length.
	MOVEM	%T2,FH.FLG(%T1)	;Save as second word in header
	MOVE	%T2,.M.(UPTIME)	;Get current uptime
	MOVE	%T3,.M.(DATE)	;Universal date/time
	DMOVEM	%T2,FH.UPT(%T1)	;say what time we started to trace
	MOVE	%T2,.R.(.JBVER)	;Get program's version number
	MOVE	%T3,.JBVER	;Get the monitor's real version
	DMOVEM	%T2,FH.PVR(%T1)	;Save as version numbers
	XMOVEI	%T3,FH.CFG(%T1)	;Where to copy config string to
	XMOVEI	%T2,.M.(CONFIG)	;Where the config string is
	MOVEI	%T1,^D5		;Number of words to copy
	JSP	%R,.R.(BLTR..)	;and copy the data
	MOVEM	%T3,.R.(DEPADR)	;And save deposit address, starting trace up.
	AOS	.R.(PENMSG)	;Increment number of messages pending.
	AOS	(%P)		;Bump return PC.
	POPJ	%P,		;And return success.

DESTROY:			;Entry to return freecore.
	SETZM	.R.(DEPADR)	;Stop the trace.
	SETZM	.R.(EXMADR)	;Wipe examine address.
	SETZM	.R.(ORGADR)	;Clear start of freecore address
	SETZM	.R.(TOPADR)	;Clear end of freecore address
	POPJ	%P,		;And return

;Routine to simulate an XBLT
BLTR..:
IFN FTKLP,<
	EXTEND	%T1,.R.(.+2)	;do a real xblt
	JRST	(%R)		;and return
	XBLT			;extend opcode
>
IFE FTKLP,<
	EXCH	%T1,%T3		;Put length in a safe place
	HRL	%T1,%T2		;Source of BLT data
	ADDI	%T3,(%T1)	;point to first word after data
	BLT	%T1,-1(%T3)	;Copy the data
	HLRZ	%T2,%T1		;Point to first word we didn't copy
	SETZ	%T1,		;and say zero words left to copy
	JRST	(%R)
>
	SUBTTL	General program variables
	ADVANC	BPTN,BPTN	;Number of symbols to set SNUP up for.

;^C intercept block
CCBLOK:	XWD	4,CCEXIT	;Length,,where to go
	ER.EIJ!ER.TLX!ER.QEX!ER.FUL!ER.OFL!ER.ICC!ER.IDV ;All errors trap
	EXP	0		;Place for monitor to store PC.
	EXP	0		;Place for monitor to store reason
;Filop argument list to do dump mode O
OUTFIL:	.FOOUT			;Argument block for FILOP

;To expand POINTS macro into radix50 names.
DEFINE	AT(NAME,STUFF),<
	RADIX50 0,NAME
>

LOCS:	POINTS			;Define breakpoint symbols
	ZZ=.			;Define other symbols we need
	SYMDEF(SAVT)		;Coroutine to save t1-t4.
	SYMDEF(WAKJOB)		;Routine to wake myself up.
	SYMDEF(D36PIF)		;Routine to turn off interrupts
	SYMDEF(D36PIN)		;Routine to turn interrupts back on
	SYMDEF(UPTIME)		;System uptime.
	SYMDEF(DATE)		;Universal date/time kept by monitor
	SYMDEF(CONFIG)		;Monitor name.
CHKKLP:	AT	FTKLP		;Make sure we are run on a correct monitor
CHKLEN:	AT	MB.LEN
CHKDAT:	AT	UD.DAT
CHKMSD:	AT	UD.MSD
CHKALA:	AT	MD.ALA
CHKALL:	AT	$MDALL
CHKAT1:	AT	T1		;Make sure the acs are correct
CHKAMB:	AT	MB		;Another AC
	TOTADR=.-ZZ
IFG <TOTADR-BPTN>,<PRINTX ?Quantity BPTN is set up incorrectly>

$BLOCK	SNPARG,TOTBPT*2+2	;Argument block for snoop uuo
$BLOCK	FOPBLK,.FOMAX
$BLOCK	SCBLK,.FXLEN
$BLOCK	LKBLK,.RBMAX
$BLOCK	PTBLK,.PTMAX
$BLOCK	BUFFER,BUFSIZ
$BLOCK	OBUF,3			;Output buffer ring header

$LVAR	LOKADR
$LVAR	MYJOB

;Words used to control buffer usage.
$LVAR	ORGADR			;Contains address of start of buffer
$LVAR	TOPADR			;Highest address in buffer.
$LVAR	DEPADR			;Next address to deposit into (Changed by EXEC)
$LVAR	EXMADR			;Next address to read from (Changed by USER)
$LVAR	LSTMSG			;number of lost messages
$LVAR	PENMSG			;Number of pending messages (Exec ups count
				; when finished putting message, user
				; decrements count when out to disk).
$LVAR	RELOFF			;Used to convert monitor buffer addresses to
				; user buffer addresses.

	SUBTTL	Structure definitions
	$HISEG

;Note that these BEGSTRs are copied in DNTATL, the formatting program
; for this trace.

BEGSTR FH			;File Header
	WORD	LNG		;Length of this header
	FIELD	FLG,9		;Flags to indicate what kind of entry
	FIELD	TYP,9		;Entry Type
		XP TYP.FH,1	;  Entry type of header record
	HWORD	MGL		;Length of a message block
	WORD	UPT		;UPTIME corresponding to UDT below
	WORD	UDT		;Universal Date/Time corresp to UPTIME
	WORD	PVR		;Trace program version number (loc 137)
	WORD	MVR		;Monitor Version (location 137)
	WORD	CFG,5		;CONFIG string from this monitor
ENDSTR

BEGSTR MH			;Message Header
	WORD	LNG		;Length of the entire entry
	FIELD	FLG,6		;Flags to indicate what kind of entry
	FIELD	IOL,3		;Input, output, or local
		XP IOL.IN,1	;Input
		XP IOL.OT,2	;Output
		XP IOL.LO,3	;locals
	FIELD	TYP,9		;Entry Type
		XP TYP.MH,2	;  Entry type of message record
	HWORD	LST		;Cumulative number of messages lost
	WORD	UPT		;UPTIME as of entry (reliable time stamp)
	WORD	UDT		;Universal date/time (readable time stamp)
	WORD	OMB		;Original MB pointer
	WORD	ILN		;Input line ID (or zero if none)
	WORD	OLN		;Output line ID (or zero if none)
ENDSTR


;To expand POINTS macro into pointers to values
DEFINE	AT(NAME,STUFF),<IFIW STUFF> ;Define pointers

VALS:	POINTS

;Words which must have relocation added to them.
ADDERS:
ZZ=0
REPEAT SYMCNT,<
	ZZ=ZZ+1
	XWD GENSYM(M,\ZZ),GENSYM(A,\ZZ)
	DELSYM(M,\ZZ)
	DELSYM(A,\ZZ)
>
	XWD 0,0			;End of list
DEFINE .R.,<PRINTX ?Cannot define relocatables here.>
DEFINE .M.,<PRINTX ?Cannot define monitor symbols here.>

	SUBTTL	Startup code. Read filespec.
DNSNUP:	$SETUP			;Initialize the world
	MOVE	T1,[.STDEF,,[1,,.STDSB
			4]]	;Set default large buffers to 4 blocks
	SETUUO	T1,		;While this program runs.
	 $WARN	CSL,<Can't set large buffers, code: >,.TOCTW##,T1
GETFIL:	
	MOVEI	T1,[ASCIZ \Trace file:\]
	CALL	.TSTRG##	;Type the prompt
	CALL	.FILIN##	;Get a filespec back
	SKIPL	T1		;make sure we got a filespec
	 $WARN	FSR,<File spec required>,,,GETFIL
	MOVEI	T1,SCBLK	;address of my scan block
	MOVEI	T2,.FXLEN	;length of my scan block
	CALL	.GTSPC##	;get the specification

;Default output extension to .MSG

	MOVE	T1,.FXEXT+SCBLK	;Get extension user specified
	TLNN	T1,-1		;Anything in the mask?
	HRLOI	T1,'MSG'	;No, use default
	MOVEM	T1,.FXEXT+SCBLK	;Restore defaulted extension

	MOVE	T1,[.FXLEN,,SCBLK] ;args for .STOPB
	MOVEI	T2,FOPBLK+1	;open block is hidden in the filop block
	MOVE	T3,[.RBMAX,,LKBLK] ;lookup block
	MOVEI	T4,PTBLK	;path block
	CALL	.STOPB##	;create the lookup and open block stuff
	 $ERROR	BFS,<Bad file spec>
	MOVX	T1,FO.ASC!.FOWRT ;Assign me a channel, for writing
	MOVX	T2,.IOIMG	;Image buffered mode.
	DMOVEM	T1,FOPBLK+.FOFNC ;save as function for filop
	MOVSI	T1,OBUF		;And define output buffer ring header
	MOVSI	T2,1		;Set up 3 buffers
	DMOVEM	T1,FOPBLK+.FOBRH ;Point to the ring headers
	MOVEI	T1,LKBLK	;Pointer to the lookup block
	MOVEM	T1,FOPBLK+.FOLEB
;Now open our file
	MOVE	T1,[.FOMAX,,FOPBLK]
	FILOP.	T1,		;do the enter on the file
	 $ERROR	FEF,<Filop enter failure, >,<flerr. t1,>,t1
	HLL	T1,FOPBLK	;Get channel number
	HLLM	T1,OUTFIL	;save in output block
;Get our monitor symbols.
	MOVE	T1,[TOTBPT+TOTADR,,LOCS] ;Pointer to our symbol names
	CALL	GETADR##	;ask snup what the symbol values are
;Initialize pointers and counters here **&&**
	MOVE	T1,CHKKLP	;Check the monitor we are running on
	CAXE	T1,FTKLP	;specifically, whether or not kl paging is on.
	 JRST	ERRMVS		;Monitor doesn't match us.
	MOVE	T1,CHKLEN
	CAXE	T1,MB.LEN
	 JRST	ERRMVS		;Skew
	MOVE	T1,CHKDAT
	CAXE	T1,UD.DAT
	 JRST	ERRMVS		;Skew
	MOVE	T1,CHKMSD
	CAXE	T1,UD.MSD
	 JRST	ERRMVS		;Skew
	MOVE	T1,CHKALA
	CAXE	T1,MD.ALA
	 JRST	ERRMVS		;Skew
	MOVE	T1,CHKALL
	CAXE	T1,MD.ALL
	 JRST	ERRMVS		;Skew
	MOVE	T1,CHKAT1
	CAXE	T1,%T1		;Do we agree on AC definitions?
	 JRST	ERRMVS		;Skew
	MOVE	T1,CHKAMB	;Check other AC, message block pointer
	CAXE	T1,%MB
	 JRST	ERRMVS		;Skew
	PJOB	T1,		;Find out what job I am
	MOVEM	T1,MYJOB	;save it for later wakes.
	MOVEI	T1,1		;An HPQ to put us into
	HPQ	T1,		;WHAM!
	 $WARN	HPQ,<HPQ set failed>
	SUBTTL	Relocate and insert breakpoints
;Following code must be nailed down.
	MOVX	T1,1		;loseg only, and nail us down in low EVM
	LOCK	T1,		;Lock!
	 $ERROR	CNL,<Could not lock, code >,.TOCTW##,T1
	LSH	T1,^D9		;make it an address, rather than a page
	HRRZM	T1,LOKADR	;save as displacement into the monitor
;Now we are starting to be dangerous. Trap errors.
	MOVEI	T1,CCBLOK	;Point to ^C intercept block
	MOVEM	T1,.JBINT	;And set up trapping.
;Relocate address
	MOVEI	T4,ADDERS	;Pointer to list of addresses
RELADR:	SKIPN	T1,(T4)		;Get a word
	 JRST	SNPBPT		;do snoop breakpoints
	MOVE	T2,(T1)		;Get relocation
	MOVSS	T1		;pointer to word itself
	ADDM	T2,(T1)		;add in the relocation
	AOJA	T4,RELADR	;and go do another

SNPBPT:	MOVEI	T4,TOTBPT	;number of addresses to transfer
	MOVEI	T1,1+TOTBPT*2	;Size of argument block we are feeding it
	MOVE	T2,CHKSUM	;Get the checksum that GETADR left us
	DMOVEM	T1,SNPARG	;Save double word in argument list
	MOVEI	T1,TOTBPT	;Number of breakpoints
XFRBPT:	SOJL	T1,INSBPT	;After done copying points, insert them.
	MOVE	T2,LOCS(T1)	;Get a breakpoint location
	MOVE	T3,@VALS(T1)	;Get this breakpoint's value
	ADD	T3,LOKADR	;relocate it.
	MOVE	T4,T1		;Copy breakpoint number
	ASH	T4,1		;multiply by 2..
	DMOVEM	T2,SNPARG+2(T4)	;Store in breakpoints part of argument block
	JRST	XFRBPT		;Transfer another breakpoint

INSBPT:	MOVE	T1,[.SODBP,,SNPARG] ;Argument to define breakpoints
	SNOOP.	T1,		;define them!
	 $ERROR	BDF,<Breakpoint define failed! code >,.TOCTW##,T1
	MOVSI	T1,.SOIBP	;Insert the breakpoints!
	SNOOP.	T1,
	 $ERROR	BIF,<Breakpoint insert failed!>
	SUBTTL Setup buffer.
;Now to set up the buffer
	MOVE	T1,[.SONUL,,SNPARG] ;Execute null snoop function
	SNOOP.	T1,		;To grab freecore.
	 JSP	T1,QUIT		;Failed, get out of here quick.
	SKIPE	T2,ORGADR	;Get address buffer starts at.
	 SKIPN	T3,TOPADR	;Get address buffer ends in.
	  JSP	T1,QUIT		;A bad address, quit.
	MOVE	T1,ORGADR	;Get monitor address of buffer
	SUBI	T1,BUFFER	;subtrace user address of buffer
	MOVEM	T1,RELOFF	;Save as relocation offset
	$INFORM	TIS,<Trace is starting at >,.TDATN##
	SUBTTL	Main loop.
LOOP:
	SKIPE	P2,PENMSG	;Any messages pending?
	 JRST	REDMSG		;Yes, read them out of the buffer
	INCHRS	T1		;Get a character if there is one
	JRST	SNOOZE		;nope, sleep for a while
	CAIL	T1,140		;Are we in upper case range?
	 SUBI	T1,40		;no, put us there.
	CAIN	T1,"Q"		;Quit command?
	  JRST	ABORT		;yes, exit!!!
	CAIN	T1,"Z"-100	;^Z command?
	  JRST	ABORT		;yes, exit!!!
	CAIN	T1,"E"		;Exit command?
	  JRST	ABORT		;yes, exit!!!
	CAIN	T1,"D"		;is this a DDT command?
	 JRST	GODDT		;do to DDT.
	$WARN	UNC,<Unknown command >,.TCHAR##,T1
	JRST	LOOP

GODDT:	SKIPN	.JBBPT		;Make sure we have a breakpoint address first
	 $WARN	NDL,<No ddt loaded, continuing...>,,,LOOP
	JSR	@.JBBPT		;and go to DDT
	JRST	LOOP

SNOOZE:	MOVX	T1,<HB.RTL!HB.RTC!HB.RWJ!HB.RWP!HB.RWT>
	HIBER	T1,		;Go to sleep for a minute
	 $ERROR	HUF,<Hiber UUO failed, aborting>,,,ABORT
	JRST	LOOP		;Got woken up for something

CCEXIT:	$ERROR	FEJ,<Fatal error in job, exiting>,,,.+1
ABORT:	CLOSE
	EXIT			;Implicit reset removes breakpoints

QUIT:	RESET			;First thing, clean up neat
	$ERROR	FAE,<Fatal dangerous error at user PC >,.TXWDW##,T1,.+1
	EXIT

ERRMVS:	RESET			;First thing, clean up
	$ERROR	MVS,<DNSNUP version skew, rebuild with new universals>,,,.+1
	EXIT
	SUBTTL	Buffered mode I/O routine.
REDMSG:	MOVE	P3,EXMADR	;Get address of next message
	SUB	P3,RELOFF	;Relocate to user virtual address
	SKIPL	T1,(P3)		;Get length of the entry
	 JRST	OUTMSG		;Length is good, send it out
	JUMPE	T1,[$ERROR ZER,<Zero word encountered in buffer>,,,ABORT]
	MOVE	P3,ORGADR	;Get starting address of buffer
	MOVEM	P3,EXMADR	;save as address for next buffer
	SUB	P3,RELOFF	;Relocate to user virtual address
	MOVE	T1,(P3)		;Length of message to copy
OUTMSG:	MOVE	T2,P3		;Pointer to message
OUTMS1:	SETZ	T4,		;Number of words left over for next pass
	MOVE	T3,OBUF+2	;Get number of words left in buffer
	SUB	T3,T1		;Get number of words left after us
	JUMPGE	T3,OUTMS2	;If it fits, we are o.k.
	MOVN	T4,T3		;Number of words left for next pass
	SETZ	T3,		;Number of words left in current buffer
	MOVE	T1,OBUF+2	;Number of words we can actually copy this time
	JUMPE	T1,OUTMS4	;If we can't copy anything, just do an out.
OUTMS2:	MOVEM	T3,OBUF+2	;set current number of words left
	HRRZ	T3,OBUF+1	;get current pointer-1 to disk buffer
	ADD	T1,T3		;make pointer to last word to copy
	AOJ	T3,		;Point to first word to leave data in
	HRL	T3,T2		;Point to first word to take data from
	BLT	T3,(T1)		;Copy the data
	HLRZ	T2,T3		;Get new source address
	HRRM	T1,OBUF+1	;and save updated byte pointer
	SKIPE	OBUF+2		;Are there any words left in the buffer?
	 JRST	OUTMS3		;Yes, continue
	JUMPE	T4,OUTMS3	;Return to caller
OUTMS4:	MOVE	T1,[1,,OUTFIL]	;Args for filop
	FILOP.	T1,		;Do an out UUO
	 $ERROR	OUF,<OUT uuo failed, code: >,.TOCTW##,T1
OUTMS3:	SKIPE	T1,T4		;Any words left to do?
	JRST	OUTMS1		;and do the rest of this buffer
	ADD	P3,(P3)		;Point to next message in sequence
	ADD	P3,RELOFF	;Convert to exec vm address
	MOVEM	P3,EXMADR	;Save as location for next message we look for
	SOSG	PENMSG		;Decrement number of messages pending
	 JRST	LOOP		;none pending, go to sleep
	JRST	REDMSG		;Messages pending, go read them

	XLIST
	LIT
	LIST

	END	DNSNUP