Google
 

Trailing-Edge - PDP-10 Archives - decuslib10-10 - 43,50524/d2d.mac
There are no other files named d2d.mac in the archive.
title	D2D - disk to disk copy; Richard Denney UTAustin; 1981
	subttl	Definitions and accumulator assignments

	search	uuosym
	sall		;No expansion of macro definitions.
	nosym		;No symbol table listing.

;	Accumulator assignments
	t1=1		;Temporary.
	t2=2	
	t3=3
	t4=4
	t5=5
	t6=6
	t7=7
	t8=10
	t9=11
;	Note: Be careful eliminating or reordering assignment of registers.
;	IDIVs are done in this program which deposits the remainder in the
;	register following the one upon which the division was done.
;	Eg. a IDIV on T9 would be disastorous since MAPIDX follows it.
	mapidx=12	;First word of current MAP entry being processed.
	maplst=13	;First word of last entry in MAP.
	maptmp=14	;Temporary pointer to move about MAP.
	oldidx=15	;Monitors progress of MAPIDX for security.
	wrtblk=16	;Block address for writing to new disk.
	p=17		;Jump stack.

;	Definitions
	pdlen==100	;Length of push down stack.
	mapsiz==7	;Number of words per entry in MAP.
	inchnl==1	;Input channel for reading UFDs, etc.
	dmpchn==2	;Dump channel for dumping SATs and MAP.
	outchn==3	;Output channel for writing to new disk.
	blksiz==200	;Words in a block.

;	Following are indexes to data items that compose each entry of MAP.
	name==0		;Index to FileName within a MAP entry.
	ext==1		;Index to FileExtension within a MAP entry.
	oldcfp==2	;Index to old CFP within a MAP entry.
	newcfp==3	;Index to new CFP within a MAP entry.
	newufd==4	;Index to new CFP of UFD pointing to file.
	nxtnod==5	;Index to address of next node in MAP chain.
	blkuse==6	;Number of blocks used.
;	End of MAP entry indexes.

	define die(error) <
		xlist
		jrst [outstr [asciz/error/]
		      exit]
		list
>

	define savreg <
		xlist
		push	p,t1
		push	p,t2
		push	p,t3
		push	p,t4
		push	p,t5
		push	p,t6
		push	p,t7
		push	p,t8
		list
>

	define resreg <
		xlist
		pop	p,t8
		pop	p,t7
		pop	p,t6
		pop	p,t5
		pop	p,t4
		pop	p,t3
		pop	p,t2
		pop	p,t1
		list
>

;	NOTE: making the PJRST definition more than one line of code
;	will cause problems since skip type instructions are used 
;	in places to skip over the PJRST. The skip instructions
;	skip only a single instruction.
	define	pjrst(x) <
		xlist
		jrst	x
		list
>
	subttl	HOME Block Definitions

;	Note: D2D computes logical block addresses by multiplying
;	Compressed File Pointers (CFP), by Blocks Per Cluster, which
;	is word HOMPBC in the HOME block. This procedure is satifactory
;	as long as Super Clusters = Clusters and a disk structure consists
;	of only one physical unit, eg. DSKH = rpa0.

	HOMNAM==0	; "HOM" in SIXBIT
	HOMHID==1	; SIXBIT unit id
	HOMPHY==2	; Physical address of this block,,other home block
	HOMSRC==3	; Position of this STR in SYS search list
	HOMSNM==4	; SIXBIT structure name
	HOMNXT==5	; ID of next unit in file structure
	HOMPRV==6	; ID of previous unit in file structure
	HOMLOG==7	; SIXBIT logical unit # within file structure
	HOMLUN==10	; Logical unit in STR
	HOMPPN==11	; Proj-prog # which refreshed STR
	HOMHOM==12	; LH==Logical block # within unit of Home block
			; RH==Log. block # within unit for extra Home block
	HOMGRP==13	; # of blocks per group to try for
	HOMBSC==14	; # blocks per supercluster on this unit
	HOMSCU==15	; # of superclusters per unit
	HOMCNP==16	; Byte ptr for cluster count in RIBS
	HOMCKP==17	; Byte ptr for checksum in RIB
	HOMCLP==20	; Byte ptr for cluster address in RIB
	HOMBPC==21	; # blocks per cluster for this STR
	HOMK4S==22	; # K words for swapping on this unit
	HOMREF==23	; Non-zero if file must be refreshed
	HOMSIC==24	; # SAT blocks in core
	HOMSID==25	; Unit ID of next unit in active swapping list
	HOMSUN==26	; Logical unit # in active swapping list
	HOMSLB==27	; First log. block # for swapping on this unit
	HOMCFS==30	; Swapping class
	HOMSPU==31	; # SAT blocks per unit
	HOMOVR==32	; Overdrawn limit per user on this STR
	HOMGAR==33	; Upper bound on total reserved blocks
	HOMSAT==34		; SAT.SYS	(Log. block within STR of first RIB)
	HOMHMS==35		; HOME.SYS
	HOMSWP==36		; SWAP.SYS
	HOMMNT==37		; MAINT.SYS
	HOMBAD==40		; BADBLK.SYS
	HOMCRS==41		; CRASH.SAV
	HOMSNP==42		; SNAP.SAV
	HOMRCV==43		; RECOV.SYS
	HOMSUF==44		; SYS UFD	[1,4] UFD
	HOMPUF==45		; Printer UFD	[3,3]
	HOMMFD==46		; MFD		[1,1]
	HOMPT1==47	; First retrieval ptr for MFD
	HOMUN1==50	; Logical unit # where MFD starts
	HOMLEN==51	; Table of lengths of files created by refresh - 6 words
	HOMEND==56	; Last word kept in UDB copy of Home block

	HOMUTP==57	; Unit type on which HOM block was written
	HOMRIP==60	; Used by RIPOFF (That's not us)
	HOMKLB==61	; First of 20 words used by PDP-11 in KL10 systems
	HOMKLE==104	; Last of the 20 words
	HOMK4C==105	; K for CRASH.EXE file
	HOMBTS==106	; Bits in the HOM block
	  HOMPVS==1B35	; Unit contained in a private STR
	HOMVID==165	; Volume ID (3 words, 12 PDP-11 bytes)
	HOMOWN==170	; Owner name (3 words, 12 PDP-11 bytes)
	HOMVSY==173	; System type (3 words, 12 PDP-11 bytes)
	HOMCOD==176	; Contains XWD 0 ,, 707070 (unlikely code)
	  CODHOM==707070	; Unlikely code for HOMCOD
	HOMSLF==177	; This block # within unit
	subttl	RIB Definitions

	RIBFIR==0	; XWD  -Nr. of retrieval ptrs ,, First pointer adress
	RIBPPN==1	; XWD Project ,, Programmer number
	RIBNAM==2	; SIXBIT file name
	RIBEXT==3	; SIXBIT file extension ,, Access date
	RIBSIZ==5	; File length in words
	RIBVER==6	; Prog # making last change ,, octal version #
	RIBSPL==7	; Spooled device
	RIBEST==10	; Estimated length of file in blocks
	RIBALC==11	; # of blocks allocated for file
	RIBPOS==12	; Log block # in STR of last group
	RIBFT1==13	; Reserved for future use by DEC
	RIBNCA==14	; Word for customer to define
	RIBMTA==15	; Tape label if file on magtape
	RIBDEV==16	; Name of STR containing file
	RIBSTS==17	; Status bits
	RIBELB==20	; Log block # where bad region begins
	RIBEUN==21	; Err unit # in STR ,, Nr bad blocks in region
	RIBQTF==22	; FCFS quota for this PPN in this STR (UFD only)
	RIBQTO==23	; Logged out quota  (UFD only)
	RIBQTR==24	; Reserved quota (UFD only)
	RIBUSD==25	; Nr blocks used when job was last logged out (UFD only)
	RIBAUT==26	; Author - PPN writing the file
	RIBNXT==27	; Next STR for this file (unused level D)
	RIBPRD==30	; Previous STR for file  (unused level D)
	RIBPCA==31	; Privileged arg for customer definition
	RIBUFD==32	; Block # in STR of UFD data block with ptr to this RIB
	RIBFLR==33	; First logical block in file pointed to by this RIB
		;  (zero if first RIB)
	RIBXRA==34	; Extended rib address
	RIBTIM==35	; Time,,Date word in universal standard
	RIBLAD==36	; Last accounting date (UFD)
	RIBDED==37	; Directory expiration date (UFD)
	RIBACT==40	; AOBJN pointer to account string
	RIBENT==RIBACT	; Last arg or value on extended lookup/enter/rename
	RIBCOD==176	; Contains 777777 (unlikely code)
	RIBSLF==177	; This logical block number in STR
	Subttl	BRKPTx - Routine to break info out of RIB retrieval pointers.

;	For RIB Retrieval Pointers on OLD pack.
;	Call with (t2)=retrieval pointer.
;	PUSHJ P,BRKPTO
;	Return here with (t2)=address (t3)=blocks in file.
;	(t4)=checksum.

brkpto:	movem	t2,o.ribp		;Move pointer to word that byte
					;pointers look at.
	ldb	t2,o.ptra		;Get CFP of first cluster.
	ldb	t3,o.ptrc		;Get count of clusters.
	ldb	t4,o.ptrs		;Check Sum.
	imul	t2,o.home+hombpc	;Change CFP into blocks.
	imul	t3,o.home+hombpc	;Change clusters into blocks.
					;HOMBPC is blocks per cluster.
	popj	p,			;And return.

;	For RIB Retrieval Pointers on NEW pack.
;	Call with (t2)=retrieval pointer.
;	PUSHJ P,BRKPTN
;	Return here with (t2)=address (t3)=blocks in file.

brkptn:	movem	t2,n.ribp		;Move pointer to word that byte
					;pointers look at.
	ldb	t2,n.ptra		;Get CFP of first cluster.
	ldb	t3,n.ptrc		;Get count of clusters.
	ldb	t4,n.ptrs		;Get checksum.
	imul	t2,n.home+hombpc	;Change CFP into blocks.
	imul	t3,n.home+hombpc	;Change clusters into blocks.
					;HOMBPC is blocks per cluster.
	popj	p,			;And return.
	subttl	UPDRIB - Update RIB of file with new disk information.

;	Call with file's RIB in O.RIB, MAPIDX pointing to file's MAP entry.
;	Updates RIBUFD, Retrieval Pointers, and RIBSLF.
;	Updated RIB is kept in N.RIB.
;	Consider all registers used.

;	Make copy of O.RIB in N.RIB.
updrib:	hrli	t1,o.rib		;Prepare to BLT O.RIB into N.RIB.
	hrri	t1,n.rib
	movei	t2,n.rib+ribslf		;Last word of a RIB.
	blt	t1,@t2			;Do it.
	move	t1,o.rib+ribslf		;Confirm BLT worked.
	came	t1,n.rib+ribslf		;Did it?
	  die(?UPDRIB: BLT failed making copy of old RIB.)

;	RIBXRA.
	setzm	n.rib+ribxra		;Rib will not be extended.

;	RIBUFD.
	move	t1,newufd(mapidx)	;New disk block address of UFD
	movem	t1,n.rib+ribufd		;that points to me.

;	RIBSLF.
	move	t2,newcfp(mapidx)	;New disk block address of file itself.
	movem	t2,n.rib+ribslf

;	RIBALC.
	move	t1,blkuse(mapidx)	;Blocks allocated to file.
	movem	t1,n.rib+ribalc		;Computed in DONTRY.

;	Build Retrieval Pointers.
;	setzm	n.ribp			;Where we will build the pointer.

;	CHECKSUM.
	pushj	p,chksum		;Called with word to be checksummed
					;in N.CHKS, which is set by routine
					;calling UPDRIB.
	move	t1,n.chks		;Checksum returned in N.CHKS.
	dpb	t1,n.ptrs		;Deposit into pointer.

;	CLUSTER COUNT.
	move	t1,blkuse(mapidx)	;Blocks allocated to file.
	idiv	t1,n.home+hombpc	;Convert to clusters.
	skipe	t2			;Remainder? Shouldn't be.
	  die(?UPDRIB: Blocks allocated not factor of Blocks Per Cluster.)
	dpb	t1,n.ptrc		;Deposit cluster count.

;	CFP - Compressed File Pointer of Block Address.
	move	t1,newcfp(mapidx)	;New disk address.
	idiv	t1,n.home+hombpc	;Compute CFP.
	skipe	t2			;Remainder? Shouldn't be.
	  die(?UPDRIB: CFP calculation bad.)
	dpb	t1,n.ptra		;Deposit in pointer.

;	Put pointer in N.RIB.
	move	t2,n.ribp		;Get the built Retrieval Pointer.
	hrrz	t1,n.rib+ribfir		;Relative location of Retrieval Pointer
	aoj	t1,			;in n.rib.
	movem	t2,n.rib(t1)		;Put pointer in.

;	Clear remaining Retrieval Pointers since this RIB isnt extended.
;	T1 should be set as was after having put rib pointer in N.RIB.
updri3:	aoj	t1,
	cail	t1,ribcod		;Done?
	jrst	updri4			;Yes.
	setzm	n.rib(t1)		;Clear it.
	jrst	updri3

;	Here with pointer still in T2.
updri4:	skipe	mufd			;Working with [1,1].UFD RIB?
	movem	t2,n.home+hompt1	;Yes. Put in HOME block of new disk.
	setzm	mufd			;Clear MFD flag.
	popj	p,			;Return.
	subttl	CHKSUM - Routine to compute checksum.

;	Call with word to be checksummed in N.CHKS.
;	Return with checksum in N.CHKS.

chksum:	move	t1,n.chks
	move	t2,n.chks
	move	t4,n.home+homckp	;Checksum pointer.
	hrri	t4,t2			;Make it point to T2.
	ldb	t3,[point 6,t4,11]	;Get size field of checksum ptr.
	movns	t3			;Make negative for upcoming LSH.
	tlza	t4,770000		;Set to bit 35.
chksu1:	add	t2,t1			;Add byte to rest of word.
	ldb	t1,t4			;Get a byte of checksum size.
	lsh	t2,(t3)			;Throw away the byte.
	jumpn	t2,chksu1		;Finished when no more of original
					;word is left.
	movem	t1,n.chks
	popj	p,			;Done. Return.
	subttl	CORGET - routine to get more core for MAP.

;	CORGET is called before adding entries to bottom of MAP.
;	It checks to see that an entry of MAPSIZ can be added without
;	exceeding .JBREL, if not it gets more core.
;	Call	PUSHJ P,CORGET
;	Return	c(maplst)=address where next entry is to start,
;		c(.JBFF)=c(old .JBFF)+MAPSIZ
;	Uses	t1,t2

corget:	move	t1,.jbff	;First free word at end of MAP.
	addi	t1,mapsiz	;Size of one MAP entry.
	camg	t1,.jbrel	;Are we going to exceed our space?
	jrst	corgot		;No. Update parameters and return.

;	Here if we need to have more core allocated.
	move	t2,t1
	core	t2,		;Request more core up to address specified.
	  die(?Insufficient core to build MAP. Set CORMAX up and retry.)

corgot:	move	maplst,.jbff	;Indicate where the start of our free core is.
	movem	t1,.jbff	;Update.
	popj	p,		;Normal return.
	subttl	DMPMAP - debugging routine to dump MAP to DSK:MAP.FIL

dmpmap:	movei	t1,.ioimg	;Open a channel for writing.
	move	t2,[sixbit/dsk/]
	hrlzi	t3,dmphdr	;Buffer control block address.
	open	dmpchn,t1
	  die(?MAP output open failed.)
	enter	dmpchn,entblk
	  die(?MAP enter failed)
	outbuf	dmpchn,0

;	Start writing of entries with the top of MAP.
	move	t1,map
;	Write entry pointed to t1.
dmpma2:	movei	t2,mapsiz	;Number of words in entry.
	move	t4,t1		;Address of entry to write.
	pushj	p,dmpit		;Dump (t2) words starting at address (t4).

;	Write out all entries pointed to by this entry.
	skipn	nxtnod(t1)	;Are there any?
	jrst	dmpma3		;No. Got to end of this loop.
	move	t4,nxtnod(t1)	;Get start of entries this entry points to.
	hrrz	t2,ext(t1)	;Get the number of entries involved.
	imuli	t2,mapsiz	;Multiply time words per entry.
	pushj	p,dmpit		;Dump (t2) words starts at address (t4).
dmpma3:	addi	t1,mapsiz	;Incriment and loop back.
	camg	t1,maplst	;Have we done all entries in MAP?
	jrst	dmpma2		;No. Loop for next.
	close	dmpchn,		;Yes.
	popj	p,

;	Routine to write (t2) words starting at (t4). Uses t3.
dmpit:	move	t3,(t4)		;Get a word.
	pushj	p,dmpbyt	;Dump the word to MAP.FIL.
	sosle	t2		;Written all the entries?
	aoja	t4,dmpit	;No. Loop for next word.
	popj	p,		;Yes. Done.

;	Here to write the word in T3 to MAP.FIL.
dmpbyt:	sosge	dmpcnt			;Output buffer full?
	  jrst	dmpby2			;Yes. Write out what we have.
	idpb	t3,dmpptr		;Put character in output buffer.
	popj	p,			;Return to caller.
dmpby2:	out	dmpchn,			;Write out buffer.
	jrst	dmpbyt			;;Start new buffer.

entblk:	sixbit/map/
	sixbit/fil/
	z
	z

dmphdr:	z
dmpptr:	z
dmpcnt:	z
	subttl	DMPSAT - Debugging routine to dump in-core SAT to disk.

dmpsat:	movei	t1,.iodmp		;Specify dump mode.
	move	t2,[sixbit/dsk/]	
	setz	t3,			;No inbuf/outbuf in dump mode.

;	Open a channel.
	open	dmpchn,t1
	  die(? Error trying to open channel for SAT block dump.)

;	Enter a file.
	enter	dmpchn,entbl2
	  die(? Enter failure for SAT dump.)

;	Prepare IOWD for dump.
	movn	t1,n.home+homspu	;Number of SATs on new disk,
	imul	t1,n.satw		;times words in block = words to dump.
	hrls	t1,t1			;To left half.
	move	t2,n.sat		;Address to dump from.
	soj	t2,			;Address minus one.
	hrr	t1,t2			;Finish the IOWD.
	setz	t2,			;Termination word.
	out	dmpchn,t1		;Dump it.
	skipa				;Good dump.
	  die(? Error while dumping in-core SAT.)
	close	dmpchn,
	popj	p,

entbl2:	sixbit/sat/
	sixbit/fil/
	z
	z
	subttl	READ - Read in dump mode from old disk.

;	Assumes INCHNL has been OPENed in dump mode (.IODMP) on
;	the original disk of which we are making a map.
;	Call with,
;	t1=block number on disk from which to read.
;	t2=buffer address in core to which the data is to be dumped.
;	t3=number of words to transfer.
;	PUSHJ P,READ
;	Return here. If errors occur it will process error and die.
;	Uses t4.

;	Use SUSET. to position to block.
read:	hrlzi	t4,inchnl		;Channel for SUSET.
	lsh	t4,5			;Shift to proper place in word.
	ior	t4,t1			;And the block to be read.
	SUSET.	t4,			;Position to block.
	  die(?READ: Error doing SUSET. UUO.)

;	Build an I/O command list in registers to assure it will be low seg.
	movns	t3,t3			;Negative number of words to read.
	movs	t4,t3
	subi	t2,1			;Location of buffer minus 1.
	hrr	t4,t2
	setz	t5,			;A zero word to terminate IO command.
	in	inchnl,t4		;Do it.
	popj	p,			;The read was good. Return.
	  die(?READ: Error doing IN.)
	subttl	bfread - buffer read in dump mode from disk.

;	Similar to routine READ but buffers input.
;	Assumes INCHNL has been OPENed in dump mode (.IODMP) on
;	the original disk of which we are making a map.
;	Call with,
;	t1=block number on disk from which to bfread.
;	t2=buffer address in core to which the data is to be dumped.
;	t3=number of words to transfer.
;	PUSHJ P,bfread
;	Return here. If errors occur it will process error and die.

bfread:	skipge	inaddr			;Is Input buffer empty? (Never used).
	jrst	bfrea3			;Yes. Go fill buffer.

;	Here when buffer has data already in it.
;	Is the data we need part of what is albfready there?
bfrea2:	move	t4,t1			;Make copy.
	sub	t4,inaddr		;Determine relative block difference.
	skipge	t4			;Equal or greater than 1st block addr?
	jrst	bfrea3			;No. Buffer doesn't have block we need.
	cail	t4,inblks		;Less than number of blks in buffer?
	jrst	bfrea3			;No. Buffer doesn't have block we need.

;	Here if Input Buffer does have the block needed.
;	T4 is set as index of needed block in the buffer.
	imuli	t4,blksiz		;From blocks to words.
	addi	t4,inbuf		;From index to absolute core location.
	movs	t4,t4			;Prepare for BLT.
	hrr	t4,t2
	add	t2,t3			;Compute last word of BLT transfer.
	soj	t2,
	blt	t4,@t2			;Transfer data to specified core area.
	popj	p,			;Done.

;	Here if buffer is empty (never been used) or if block needed is
;	not in current contents of buffer.
;	Use SUSET. to position to block.
bfrea3:	hrlzi	t4,inchnl		;Channel for SUSET.
	lsh	t4,5			;Shift to proper place in word.
	ior	t4,t1			;And the block to be bfread.
	SUSET.	t4,			;Position to block.
	  die(?bfread: Error doing SUSET. UUO.)

;	Build an I/O command list in registers to assure it will be low seg.
	movei	t4,inblks		;Number of blks in Input Buffer.
	imuli	t4,-blksiz		;Make it negative # of words.
	movs	t4,t4			;Swapped it to left half of word.
	hrri	t4,inbuf		;Address to bfread into,
	soj	t4,			;minus one.
	setz	t5,			;A zero word to terminate IO command.
	in	inchnl,t4		;Do it.
	skipa				;Read was good.
	  die(?BFREAD: Error doing IN.)
	movem	t1,inaddr		;Reset block address of first 
					;block in INBUF.
	jrst	bfrea2			;Loop.
	subttl	WRITE - Write out in dump mode to new disk.

;	Assumes OUTCHN has been OPENed in dump mode (.IODMP) to
;	disk to which we are copying.
;	WRITE buffers output to free core starting at the address 
;	pointed to by OUTBUF. As long as free core is available and
;	the data being written is contiguous with that already in 
;	OUTBUF, the data will be kept in core.
;	When core is exhausted or data not contiguous to contents of
;	OUTBUF is to be written, OUTBUF is dumped to disk and the 
;	data then placed at the top of OUTBUF.
;	Call with,
;	t1=block number at which to begin writing.
;	t2=buffer address in core of data to be written.
;	t3=number of words to write.
;	PUSHJ P,WRITE
;	Return here. If errors occur it will process error and die.

;	Compute new disk address of last block in OUTBUF.
write:	move	t5,.jbff		;First word after OUTBUF data.
	sub	t5,outbuf		;number of words used in OUTBUF.
	skipge	t5			;Check for legal value.
	  die(?WRITE: .JBFF out of range.)
	skipg	t5			;Buffer empty?
	jrst	write2			;Yes.
;	Here if buffer not empty.
	idivi	t5,blksiz		;number of words per block.
	add	t5,topblk		;Next contiguous block address.
	came	t1,t5			;Is our data to be written contiguous?
	pjrst	write3			;No.

;	Here if data is contiguous to current contents of OUTBUF,
;	or if buffer is empty.
write2:	move	t4,.jbff		;Determine core required to add data.
	add	t4,t3
	camg	t4,.jbrel		;More than we currently have?
	pjrst	wriadd			;No. We are doing fine. Add the data.

;	Here to get more core.
	core	t4,200000		;Accept physical core only.
	skipa				;No more core.
	pjrst	wriadd			;Got it. Add data to OUTBUF.

;	Here if can't get anymore core.
	pushj	p,wridmp		;Dump contents of OUTBUF to 
	jrst	write2			;disk then try again.

;	Here if data to be written is NOT contiguous with current
;	contents of OUTBUF.
write3:	pushj	p,wridmp		;Dump contents of OUTBUF to disk.
	pjrst	wriadd			;Now add data to emptied OUTBUF.

;	Here to add data to OUTBUF.
wriadd:	move	t7,.jbff		;First free word in outbuf buffer area.
	camge	t7,outbuf
	  die(?WRITE: .JBFF out of range.)
	camg	t7,outbuf		;Is buffer empty?
	movem	t1,topblk		;Yes. Keep new disk address of the
					;data we are adding.
	hrl	t7,t2			;Current location of data.
	move	t8,.jbff		;Determine last word data will use.
	add	t8,t3
	soj	t8,
	blt	t7,@t8			;Add it to buffer.
	movem	t8,.jbff		;Compute new first free.
	aos	.jbff
	popj	p,			;Return.
;	Here to dump contents of OUTBUF to new disk.
;	Use SUSET. to position to block.
wridmp:	hrlzi	t4,outchn		;Channel for SUSET.
	lsh	t4,5			;Shift to proper place in word.
	ior	t4,topblk		;And the block to write.
	SUSET.	t4,			;Position to block.
	  die(?WRITE: Error doing SUSET. UUO.)

;	Build an I/O command list in registers to assure it will be low seg.
	move	t5,.jbff		;Determine number of words to write.
	sub	t5,outbuf
	movns	t5,t5			;Negative number of words to write.
	movs	t4,t5
	move	t5,outbuf		;Location of buffer,
	soj	t5,			;minus one.
	hrr	t4,t5
	setz	t5,			;A zero word to terminate IO command.
	out	outchn,t4		;do it.
	skipa				;Write was good.
	  die(?WRITE: Error doing OUT.)

;	Set variables to reflect state of buffer.
	move	t4,outbuf		;Set first free word back to top.
	movem	t4,.jbff
	setom	topblk			;Clear this out.
	popj	p,			;Return.
	subttl	ALLOC - allocate disk space on new pack using in-core SAT.

;	Searchs for a contiguos number of blocks and returns the disk
;	address where they start. Leaves the clusters marked in SATs.
;	Call with number of blocks needed in T1.
;	PUSHJ	P,ALLOC
;	Return here with T1=0 if couldn't grant request,
;	         or with T1=address if found.

alloc:	idiv	t1,n.home+hombpc	;Convert blocks to clusters.
	skipe	t2			;Was there a remainder?
	  die(?ALLOC: Blocks allocated is not a factor of Blocks Per Cluster.)

	move	t2,n.satt		;Get word in SAT with which we began
					;our search last time.
alloc2:	camle	t2,n.sate		;Have we passed the end of SAT?
	  jrst	[setz t1,		;No more space in SAT.
	         popj p,]
	move	t3,(t2)			;Get that top word of SAT.
	aosn	t3			;Any free clusters? -1 indicates this.
	aoja	t2,alloc2		;No. Get next word.
	movem	t2,n.satt		;Yes. Make a new n.satt for next time.
	hll	t2,n.satp		;Build a pointer to this location.
	movem	t2,n.fwdp		;Start our search for contiguous
					;clusters here.
	setzm	n.bckp			;Indicate starting out.

;	Here to look for a free cluster.
alloc3:	ildb	t3,n.fwdp		;Get a cluster.
	hrrz	t4,n.fwdp		;Get theword address.
	camle	t4,n.sate		;Have we passed last word in SAT?
	pushj	p,[ movei t3,1		;Yes. Fake a marked cluster.
		    move  t2,t1		;Fake clusters found = clusters needed.
		    popj  p,]
	jumpe	t3,alloc4		;Is the cluster free?
	setzm	n.bckp			;No. Indicate we haven't found the 
					;start of our contiguos blocks yet.
	jrst	alloc5

;	Here if cluster was free.
alloc4:	skipe	n.bckp			;Is this the first cluster found?
	aoja	t2,alloc5		;No. Count another contiguos one.
	move	t2,n.fwdp		;Yes. Mark this as start of contigous
					;clusters.
	movem	t2,n.bckp
	movei	t2,1			;Set count of clusters found to one.

;	Here to see if we have all the clusters needed.
alloc5:	camge	t2,t1			;Is clusters found >or= to needed?
	jrst	alloc3			;No. Look for another.

;	Here when we have either found the number of contiguous clusters
;	needed, or have failed to do so.
	skipn	n.bckp			;Did we fail?
	jrst	[setz t1,		;Yes. Indicate this and return.
		 popj p,]
;	Here if we found the number of contiguous clusters needed.
;	Calculate logical block address of disk area allocated.
;	First determine logical block address of SAT block from which
;	the space was allocated. 
	hrrz	t1,n.bckp		;Address of word in in-core SAT.
	sub	t1,n.sat		;Make relative to top of SAT.
	idiv	t1,n.satw		;Divide by words in each SAT to get
					;number of SATs preceding current one.
					;We will use the remainder in T2 soon.
	imul	t1,n.satb		;finally, number of blocks per sat
					;times number of SATs preceding gives
					;the block address of first block of
					;SAT we are allocating space from.

;	After the above division, T2 will be the number of words in this block
;	that precede the word we point to.
	imuli	t2,^d36			;Each word represents 36 clusters,
	imul	t2,n.home+hombpc	;and each cluster has x number blocks.

;	Finally...
	hlrz	t4,n.bckp		;What bit within word was pointed to?
					;Calculate bits to the left of it.
	lsh	t4,^d-12		;This leaves "P" portion of pointer,
	movei	t3,^d36			;Bits in a word.
	sub	t3,t4			;Gives bits to left plus 1.
	sos	t3			;Minus 1 gives us bits to left of 
					;one pointed to.
	imul	t3,n.home+hombpc	;Each cluster(1 bit) has x no. blocks.

;	The block address of contiguos blocks allocated is then...
	add	t1,t2			;Block address of first block,
	add	t1,t3			;plus total blocks into the area.

;	Here to mark all clusters used in SATs.
	movei	t2,1
	move	t3,n.bckp		;Will point to first cluster.
	skipa
alloc6:	ibp	t3			;Incriment pointer to next cluster.
	dpb	t2,t3			;Mark cluster as used.
	came	t3,n.fwdp		;Done?
	jrst	alloc6			;No. Go mark next cluster.
	popj	p,			;Yes. Return to calling routine.
	subttl	Initializations

d2d:	jfcl		;In case of CCL entry.
	reset		;Clear channels etc..
	move	p,[iowd pdlen,pdlist]	;Set up stack.

;	Get disk copying from and I.D. and name of disk copying to.
getdsk:	setzm	frompk		;Clear words where pack names are
	setzm	topk		;to be built.
	setzm	topkid
	setzm	name
	clrbfi

;	Get name of disk from which we are copying.
	move	t1,[point 6,frompk]
	outstr	[asciz/Copy from:/]
Getds1:	inchwl	t2		;Get a character.
	caig	t2,40		;Break character?
	jrst	Getds2		;Yes. Proceed with check.
	caig	t2,140		;Is char lowercase?
	addi	t2,40		;No. Convert to lowercase.
	idpb	t2,t1		;Add character to name being built.
	jrst	Getds1		;Get another character.
getds2:

;	Get name of disk to which we are copying.
	move	t1,[point 6,topk]
	outstr	[asciz/Copy to  :/]
	clrbfi
	setzm	name
Getds3:	inchwl	t2		;Get a character.
	caig	t2,40		;Break character?
	jrst	Getds4		;Yes. Proceed with check.
	caig	t2,140		;Is char lowercase?
	addi	t2,40		;No. Convert to lowercase.
	idpb	t2,t1		;Add character to name being built.
	jrst	Getds3		;Get another character.
getds4:

;	Get I.D. of pack to which we are copying.
	move	t1,[point 6,topkid]
	outstr	[asciz/I.D. of pack copying to:/]
	clrbfi
	setzm	name
Getds5:	inchwl	t2		;Get a character.
	caig	t2,40		;Break character?
	jrst	Getds6		;Yes. Proceed with check.
	caig	t2,140		;Is char lowercase?
	addi	t2,40		;No. Convert to lowercase.
	idpb	t2,t1		;Add character to name being built.
	jrst	Getds5		;Get another character.
getds6:

;	Get characteristics of disk from which we are copying.
	move	t1,frompk		;Sixbit name of pack copying from.
	movem	t1,dsktab		;Put it in disk table.
	move	t1,[xwd TabLen,DskTab]
	dskchr	t1,		;Put disk info in dsktab.
	  jrst	error2		;Give a message and die.

;	Get characteristics of disk to which we are copying.
	move	t1,topk		;Sixbit name of pack copying to.
	movem	t1,dsktab	;Put it in disk table.
	move	t1,[xwd TabLen,DskTab]
	dskchr	t1,		;Put disk info in dsktab.
	  jrst	error3		;Give a message and die.

;	Check to see that proper disk is mounted on drive to which we
;	will be copying.
	move	t1,ToPkId		;Correct disk?
	came	t1,DskTab+.dcuid	;I.D.s must match.
	  jrst	error1			;No. Give a message and die.
	jrst	satget			;Yes. Proceed.

;	Here on errors.
Error1:	outstr	[asciz/?Pack I.D. given does not match that
of Pack you are copying to.
Try again.
/]
	jrst	getdsk

Error2:	outstr	[asciz/?Disk you are copying from does not exist.
Try again.
/]
	jrst	getdsk

Error3:	outstr	[asciz/?Disk you are copying to does not exist.
Try again.
/]
	jrst	getdsk

DskTab:	z			;Physical device being checked.
	block	23		;Room for rest of table.
	TabLen=.-DskTab		;TabLen is lenght of table DskTab.

	subttl	Build target pack's SATs in core.

;	SATGET reads all SAT blocks on the target pack, building
;	an in core Storage Allocation Table.
;	How many words of each SAT block are actually used is dependant
;	on number of blocks on the pack, cluster size, and number of SAT
;	blocks. These calculations are done in this routine.

satget:	movei	t1,.iodmp		;Specify dump mode.
	move	t2,topk			;Target pack.
	setz	t3,			;No inbuf/outbuf in dump mode.

;	Open a channel.
	open	inchnl,t1
	  die(?SATGET: Error trying to open channel for HOME block read-in.)

;	Read the home block of target pack.
	movei	t1,1			;Home block is block 1.
	movei	t2,n.home		;Where to put it in core.
	movei	t3,blksiz		;The number of words to read.
	pushj	p,read			;Get it.
	move	t1,n.home+homnam	;Check to see if it is a HOME block.
	came	t1,[sixbit/HOM/]
	  die(?SATGET: Logical block 1 is not a HOME block.)

;	Read SAT.SYS RIB from target pack.
	move	t1,n.home+homsat	;Address of SAT RIB on target pack.
	movei	t2,n.satr		;Buffer in which to place SAT's RIB.
	movei	t3,blksiz		;Size of a RIB.
	pushj	p,read			;Read the RIB.

;	Build byte pointer for RIB retrieval pointers on target pack.
	move	t1,n.home+homcnp	;Pointer for Cluster Count.
	hllm	t1,n.ptrc		;Where we will keep the pointer.
	move	t1,n.home+homclp	;Pointer for Adress of first cluster.
	hllm	t1,n.ptra		;Where we will keep the pointer.
	move	t1,n.home+homckp		;Pointer for checksum.
	hllm	t1,n.ptrs		;Where we will keep the pointer.

;	Set up area for reading in target packs SATs.
	move	t1,.jbff
	movem	t1,n.sat		;Address of 1st word in SAT buffer.
					;N.SAT permanently points to this.
	movem	t1,n.satt		;N.SATT is initialized to this address.
					;It may move as space is allocated.
	movem	t1,n.sate		;Address of last word.
	sos	n.sate			;Start the end at top-1.

;	Calculate number of words actually used in each SAT block.
	move	t1,n.home+hombsc	;Blocks per supercluster.
	imul	t1,n.home+homscu	;Times superclusters on pack equals
					;blocks on pack.
	idiv	t1,n.home+hombpc	;Divided by blocks per cluster equals
					;clusters on pack.
	skipe	t2
	  die(?SATGET: Calculation of clusters on pack was bad.)
	idiv	t1,n.home+homspu	;Divided by SATs on pack equals
	skipe	t2			;Cluster per SAT.
	  aoj	t1,			;Round up if a remainder.
	movem	t1,n.satb		;Save for later use.
	idivi	t1,^d36			;Divided by 36(bits per word) equals
	skipe	t2			;number of words actually used.
	aoj	t1,			;Round up if there is a remainder.
	movem	t1,n.satw		;Save it.

;	Finally, get the core needed for the SAT buffer.
	imul	t1,n.home+homspu	;Words per SAT block times SATs on pack
					;equals total words needed for buffer.
	addb	t1,.jbff		;Calculate address to expand core to.
					;Update .JBFF to relect core we took.
	camg	t1,.jbrel		;Are we exceeding our core limit?
	jrst	satge1			;No.
	core	t1,			;Yes. Request more.
	  die(?insufficient core to build SATs. Set CORMAX up and retry.)

;	Also calculate number of blocks per sat.
	move	t1,n.home+hombpc	
	imulm	t1,n.satb		;Blocks per SAT.
;	For each pointer in SAT's RIB to a SAT block, read the block and
;	use the number of words actually used(just calculated) to place 
;	only the used portion of each SAT block into buffer.
;	T1,T5 and T6 once set by this routine are dedicated until MAPINI.
satge1:	move	t6,n.home+homspu	;Number of SAT blocks on target pack.
	hrrz	t5,n.satr+ribfir	;RIB index to 1st retrieval pointer.
	addi	t5,2			;1st pointer is for RIB itself. Skip.
					;2nd pointer is for 1st real SAT block.
satge2:	move	t2,n.satr(t5)		;Get a pointer.
	pushj	p,brkptn		;Break info out of pointer. Returns
					;t2=block where SAT starts t3=# blocks,
					;t4=checksum.

;	Read the SAT block into the in-core SAT area.
	move	t1,t2			;Logical block address of SAT block.
	move	t2,n.sate		;Last used word of in-core SAT area.
	aoj	t2,			;First word after SATEnd.
	move	t3,n.satw		;Read this many words.
	savreg
	pushj	p,read			;Read it.
	resreg
	addm	t3,n.sate		;New SAT block end.

;	Remember the address at which this SAT block is located on new disk.
	move	t2,n.home+homspu	;SATs per unit,
	sub	t2,t6			;minus SATs unread,
	movem	t1,n.dska(t2)		;is the SAT currently being read.

	sosle	t6			;Have we done all SATs on unit?
	aoja	t5,satge2		;No. Go read next SAT on unit.

;	Fall through to SUFINI.
;	Leave INCHNL open for SUFINI.
	subttl	SUFINI - routine to initialize [1,4].UFD

;	The System UFD is treated seperately because of certain files 
;	there which are set up during the new disk's initialization, 
;	which we do not want to copy over from the old disk.
;	Eg. SAT.SYS, HOME.SYS, SWAP.SYS.
;	These files are to retain there same physical location on the
;	new disk although the [1,4].UFD which lists them may be moved.
;	Their physical location on the disk is gotten from the HOME 
;	block, logical block 1 on disk.
;	The [1,4].UFD is built in a buffer N.SUFD and written out seperatly
;	later on. This routine initialized N.SUFD with the special files
;	from the new disks [1,4] area.
;	Will use the channel initialzed in SATINI, INCHNL.

sufini:	move	t1,n.home+homsuf		;Location of SYS.UFD RIB.
	aoj	t1,				;Add one to get UFD block.
	movei	t2,n.sufd			;Where to put it.
	movei	t3,blksiz			;Number of words to read.
	pushj	p,read

;	Here to find the first file entry NOT amoung our special ones.
;	The special ones will be grouped at top. When we have found a file
;	that is not a special one, we can clear N.SUFD from there on.
	movni	t1,2				;Index for N.SUFD.
sufin2:	addi	t1,2				;Next entry.
	cail	t1,blksiz-2			;Check for in bounds.
	  die(?SUFINI: New disk SYS.UFD has suspicious number of entries.)
	move	t2,n.sufd(t1)			;Get a file name.
	skipn	t2				;Zero means SYS.UFD is already
						;just as we wish it.
	jrst	sufin3				;Done.
	camn	t2,[sixbit/SAT/]
	jrst	sufin2				;One of our special files.
	camn	t2,[sixbit/HOME/]
	jrst	sufin2				;One of our special files.
	camn	t2,[sixbit/SWAP/]
	jrst	sufin2				;One of our special files.
	camn	t2,[sixbit/MAINT/]
	jrst	sufin2				;One of our special files.
	camn	t2,[sixbit/BADBLK/]
	jrst	sufin2				;One of our special files.
	camn	t2,[sixbit/CRASH/]
	jrst	sufin2				;One of our special files.
	camn	t2,[sixbit/SNAP/]
	jrst	sufin2				;One of our special files.
	camn	t2,[sixbit/RECOV/]
	jrst	sufin2				;One of our special files.

;	Here when we have found the first non-special file.
;	Clear N.SUFD from here down. This keeps only our special files.
	movei	t1,n.sufd(t1)
	setzm	@t1
	hrls	t1,t1				;Prepare for BLT.
	aoj	t1,
	blt	t1,n.sufe			;Clear through last word.

;	Done with Channel.
sufin3:	close	inchnl,
;	Fall through to INIINI.
	subttl	Initialize MAP.

;	Set up the MAP area.
mapini:	move	t1,.jbff	;Get address of first free core.
	movem	t1,map		;Nothing should be written after the map
				;for the balance of map building section
				;since it will expand to an undetermined size.
				;.JBFF will be last word in MAP, plus one.

;	Start the MAP out with the [1,1] UFD entry.
	pushj	p,corget	;Return with first available address in maplst.
	move	t1,[xwd 1,1]
	movem	t1,name(maplst)	;Moves 1,1 to FileName position of MAP.
	move	t1,[sixbit/ufd/]
	movem	t1,ext(maplst)	;Moves UFD to FileExtension position of MAP.

;	To get the location of [1,1] UFD on old disk we must read HOME block.
;	First prepare the open block.
	movei	t1,.iodmp		;Specify dump mode.
	move	t2,frompk		;Specify pack copying from.
	setz	t3,			;No inbuf/outbuf in dump mode.

;	Open a channel for now and future input from old disk.
	open	inchnl,t1
	  die(? Error trying to open channel for HOME block read-in.)

;	Read the home block.
	movei	t1,1			;Home block is block 1.
	movei	t2,o.home		;Where to put it in core.
	movei	t3,blksiz		;The number of words to read.
	pushj	p,read			;Get it.
	move	t1,o.home+homnam	;Make sure it's a HOME block.
	came	t1,[sixbit/HOM/]
	  die(?MAPINI: Logical block 1 is not a HOME block.)

	move	t1,o.home+hommfd	;Get location of [1,1] UFD on old disk.
	movem	t1,oldcfp(maplst)	;Finish our MAP entry.

;	Build byte pointer for RIB retrieval pointers.
	move	t1,o.home+homcnp	;Pointer for Cluster Count.
	hllm	t1,o.ptrc		;Where we will keep the pointer.
	move	t1,o.home+homclp	;Pointer for Adress of first cluster.
	hllm	t1,o.ptra		;Where we will keep the pointer.
	move	t1,o.home+homckp	;Pointer for Checksum.
	hllm	t1,o.ptrs		;Where we will keep the pointer.
	subttl	MISC Initializations.

;	D2D uses a single RIB Retrieval Pointer when constructing the
;	RIBs of files copied to the new disk. Consequently, the size
;	of the Cluster Count field in the pointers limits the size of
;	files that can be copied. Determine what that size is.
	move	t1,n.home+homcnp	;The pointer for Cluster Count in
					;RIB Retrieval Pointers.
	hrri	t1,[-1]			;Point it to a word = 777777,777777
	ldb	t2,t1			;T2 now has max cluster count that
					;a single Retrieval Ptr can hold.
	imul	t2,n.home+hombpc	;Convert to blocks.
	movem	t2,maxfil		;Keep this value.

	subttl	Top level to build in core map of disk to be copied.

;	Here to add to MAP everyone our initial [1,1].ufd entry points to.
bldmap:	outstr	[asciz/Building Map...
/]
	move	mapidx,map		;MAPIDX will serve as our index
					;to processing entries in MAP.
	move	oldidx,mapidx		;Security check on MAPIDX value.
	pushj	p,dontry		;Start off processing [1,1].ufd.
	came	oldidx,mapidx		;Confirm its value.
	  die(?BLDMAP: MAPIDX modified in DONTRY.)

;	[1,1].UFD's first entry is to itself. Rather than repeat the
;	DONTRY process, make a copy of [1,1].ufd immediately following
;	the one we just placed in MAP.
	hrl	t1,mapidx		;Prepare for BLT.
	addi	mapidx,mapsiz
	hrr	t1,mapidx
	blt	t1,blkuse(mapidx)
	addi	mapidx,mapsiz		;Now set MAPIDX to skip over the
					;second [1,1].UFD entry.

;	Here to finish building MAP.
bldma2:	camle	mapidx,maplst		;More entries to be processed?
	jrst	bldend			;No. Map is built.

;	Here to process next entry in MAP.
	move	oldidx,mapidx		;Security check on MAPIDX value.
	pushj	p,dontry		;Process entry pointed to by MAPIDX.
	came	oldidx,mapidx		;Confirm its value.
	  die(?BLDMAP: MAPIDX modified in DONTRY.)
	addi	mapidx,mapsiz		;Point to next entry.
	jrst	bldma2			;Loop back.

bldend:	jrst	wrtmap			;Here when map is built.
	subttl	DONTRY - a subprocess of BLDMAP

;	Process the entry pointed to by MAPIDX.
;	Read the RIB of file the entry represents, determine number of
;	of blocks it requires on new disk, allocate space, and if the 
;	file is a directory add an entry to MAP for each file the UFD
;	points to.

dontry:	move	t1,oldcfp(mapidx)	;Address of RIB on old disk.
	movei	t2,o.rib		;Buffer in which to place RIB.
	movei	t3,blksiz		;Size of a RIB.
	pushj	p,bfread		;Read the RIB.
	move	t1,o.rib+ribnam		;Do security check on name.
	came	t1,name(mapidx)		;RIBNAM match that in MAP?
	  die(?DONTRY: RIBNAM does not match that in MAP.)

;	Determine how many blocks this file will need.
	move	t1,o.rib+ribsiz		;Size of file in words.
	idivi	t1,blksiz		;Convert to words.
	skipe	t2			;Remainder?
	aoj	t1,			;Yes. Round up block count.
	addi	t1,2			;Plus two RIBs.
	idiv	t1,n.home+hombpc	;Convert to clusters.
	skipe	t2			;Remainder?
	aoj	t1,			;Yes. Round up cluster count.
	imul	t1,n.home+hombpc	;And back to blocks.
					;Note that if the Blks Per Cluster on
					;new disk is different from old, the
					;blocks allocated will be different.
	movem	t1,blkuse(mapidx)	;Keep blocks used in MAP entry.

;	Check that file size is such that we can copy the file.
	camle	t1,maxfil		;A file our RIB Retrieval Pointer
					;cluster counts can handle?
	  die(?DONTRY: File too large for current RIB Ret. Ptr. cluster count.)

;	Allocate space on new disk, updating SATS.
	pushj	p,alloc			;T1 contains number of blocks needed.
	movem	t1,newcfp(mapidx)	;Update entry.
	skipn	t1			;Space allocated?
	  die(?DONTRY: Could not allocate space for file in SAT.)
	skipg	t1			;Value in bounds?
	  die(?DONTRY: Value returned for logical disk address is bad.)

;	Are we a directory? If so make entries for all files we point to.
	move	t1,ext(mapidx)		;Get file extension.
	camn	t1,[sixbit/UFD/]	;an UFD?
	pjrst(dontr2)			;Yes.
	camn	t1,[sixbit/SFD/]	;an SFD?
	pjrst(dontr2)			;Yes.
;	Here if not a directory.
	setzm	nxtnod(mapidx)		;Indicate not a directory.
	popj	p,			;Done. Return.
;	Here if a directory. Make MAP entries for each file in directory.
dontr2:	move	t1,o.rib+ribnam		;Check for the three special UFDs.
	setzm	sufd			;Assume not [1,4].
	came	t1,[xwd 1,1]		; [1,1].ufd?
	jrst	dontr3			;No.

;	Here for [1,1].ufd.
	move	t2,newcfp(mapidx)	;Update new disk HOME block to reflect
	movem	t2,n.home+hommfd	;new location of MFD.
	movem	t2,newufd(mapidx)	;Also, [1,1].ufd is the UFD which
					;points to itself.
	jrst	dontr5

dontr3:	came	t1,[xwd 1,4]		; [1,4].ufd?
	jrst	dontr4			;No.
;	Here for [1,4].ufd.
	setom	sufd			;Set flag for later routines.
	move	t2,newcfp(mapidx)	;Update new disk HOME block to reflect
	movem	t2,n.home+homsuf	;new location of SYS UFD.
	jrst	dontr5

dontr4:	came	t1,[xwd 3,3]		; [3,3].ufd?
	jrst	dontr5			;No.
;	Here for [3,3].ufd.
	move	t2,newcfp(mapidx)	;Update new disk HOME block to reflect
	movem	t2,n.home+hompuf	;new location of Printer UFD.

dontr5:	move	t1,.jbff		;First word after end of MAP.
	movem	t1,nxtnod(mapidx)	;All entries from this directory will
					;begin at end of MAP. Point to them.
	pushj	p,addmap		;Add entries to map for files.
	move	t1,.jbff		;Get address of end of MAP now.
	sub	t1,nxtnod(mapidx)	;Get difference in old .JBFF and now.
	idivi	t1,mapsiz		;Compute number of entries UFD has.
	skipge	t1			;Check for strange value.
	  die(? .JBFF was reset in DONTR2. MAP has been lost.)
	skipn	t1			;Has anything been added to MAP?
	setzm	nxtnod(mapidx)		;No. Indicate an empty UFD.
	hrrm	t1,ext(mapidx)		;Put in right half of file extension.
	popj	p,
subttl	ADDMAP - add entries to MAP for files in a directory.

;	ADDMAP is used by DONTRY, a subprocess of BLDMAP, to make MAP
;	entries for all files pointed to by the directory being processed
;	by DONTRY.
;	It is possible that directory RIBs are not compressed, ie. they
;	have RIB pointers that really aren't in use. Because of this 
;	we must use a RIBSIZ decrement counter to avoid processing such
;	RIB pointers.
;	Call with:
;	MAPIDX pointing to MAP entry of UFD being processed by DONTRY.
;	O.RIB has rib of UFD.
;	Uses t1,t2,t3.
;	Updates MAPEND and .JBFF as a consequence of adding to the MAP.

addmap:	move	t1,o.rib+ribnam		;Security check,
	came	t1,name(mapidx)		;RIBNAM match that in MAP?
	  die(?ADDMAP: RIBNAM doesn't match name in MAP.)
;	Initialize RIBSIZ decrement counter.
	move	t1,o.rib+ribsiz		;Words written in UFD.
	idivi	t1,blksiz		;Make into blocks.
	skipe	t2			;If a remainder,
	  die(?ADDMAP: RIBSIZ is not an even factor of block size.)
	movem	t1,totblk		;Keep the decremental counter here.
addma1:	hrrz	t1,o.rib+ribfir		;RIB index to 1st retrieval pointer.
	addi	t1,1			;You just have to do this.
	move	t2,o.rib(t1)		;And get the first pointer.
	pushj	p,brkpto		;Break info out of pointer. Returns
					;t2=block where UFD starts t3=# blocks.
	aoj	t2,			;The first pointer points to itself,
					;so the UFD we want follows. Add 1.
	soj	t3,			;Which also means one less block of
					;directory information for processing.
	savreg
	pushj	p,addma3		;Process it.
	resreg
addma2:	addi	t1,1			;Move to next retrieval pointer.
	cain	t1,ribcod		;Out of pointers?
	jrst	addmax			;Yes. Check if extended RIB.
	move	t2,o.rib(t1)		;Get next pointer.
	skipn	t2			;Is it empty?
	jrst	addmax			;Yes. Check if extended RIB.
	pushj	p,brkpto		;No. Get info and process it.
					;Returns t2=UFD address t3=# blocks.
	savreg
	pushj	p,addma3
	resreg
	jrst	addma2			;Loop for next.

;	Here to check for extended RIBs.
addmax:	skipn	t1,o.rib+ribxra		;Extended?
	popj	p,			;No. Return.
	move	t4,o.rib+ribnam		;For security check later.
	movei	t2,o.rib		;Yes. Prepare to read it.
	movei	t3,blksiz
	savreg
	pushj	p,bfread		;Read the extended RIB.
	resreg
	came	t4,o.rib+ribnam		;Do RIBNAMs match?
	  die(?ADDMAX: Extended RIBNAM doesn't match RIBNAM in prime RIB.)
	jrst	addma1			;And loop to process it.
;	ADDMA3 is a subprocess of ADDMAP.
;	It is called once for each of the UFD's rib retrieval pointers.
;	Called with t2=logical block number from rib retrieval pointer,
;	and t3=number of blocks from the rib retrieval pointer.
;	It makes an entry in MAP for each file entry in the UFD.

ADDMA3:	skipg	totblk			;Check RIBSIZ decrement counter.
	popj	p,			;We have already read as many blocks
					;in this UFD as are actually used.
					;The RIB retrieval pointer we have
					;been asked to process must be the
					;result of an Uncompressed RIB, and
					;hence is not really in use. Skip it.

;	Here if we haven't read all the blocks in the UFD yet.
	move	t1,t2			;Logical block of start of data.
	move	t4,t2			;Compute last block allocated+1.
	add	t4,t3
	movei	t2,o.ufd		;Where to read UFD block into.
	movei	t3,blksiz		;How many words.

;	Here to read one block of UFD.
addma4:	caml	t1,t4			;Processed the last blk of this ptr?
	popj	p,			; Yes. Return to get next retrieval ptr.
	sosge	totblk			;Decrement blocks used counter by one.
	popj	p,			; We have read all the used blocks in
					; in the UFD. Return.
	savreg				;No. Continue.
	movem	t1,ribtst		;Save for RIBSLF check later.
	pushj	p,bfread
	resreg

;	Here to process the block of the UFD just read.
	setz	t5,			;Index to entries in UFD.
addma5:	caile	t5,blksiz-2		;Finished this block?2 words/entry.
	aoja	t1,addma4		;Yes. Get next block.
	move	t6,o.ufd(t5)		;Get sixbit filename from UFD entry.
	skipn	t6			;Zero, meaning no more data in block?
	aoja	t1,addma4		;Yes. Go get another block.

;	Are we dealing with [1,4].ufd?
	skipn	sufd			;Processing [1,4].UFD?
	jrst	addma7			;No.

;	Here if dealing with [1,4].ufd.
;	There are certain files we will not want to copy from old to new disk,
;	for example BADBLK.SYS, HOME.SYS, etc..
	move	t8,t5			;T5 is index to entries in UFD block.
	aoj	t8,			;Was pointing to filename, now to ext.
	hllz	t7,o.ufd(t8)		;Get the extension.

;	Is the entry of UFD block being looked at one we don't want to copy?
addhom:	came	t6,[sixbit/home/]
	skipa
	came	t7,[sixbit/sys/]
	skipa
	jrst	addma6			
					
	came	t6,[sixbit/sat/]
	skipa
	came	t7,[sixbit/sys/]
	skipa
	jrst	addma6			
					
	came	t6,[sixbit/swap/]
	skipa
	came	t7,[sixbit/sys/]
	skipa
	jrst	addma6			
					
	came	t6,[sixbit/maint/]
	skipa
	came	t7,[sixbit/sys/]
	skipa
	jrst	addma6			
					
	came	t6,[sixbit/badblk/]
	skipa
	came	t7,[sixbit/sys/]
	skipa
	jrst	addma6			
					
	came	t6,[sixbit/crash/]
	skipa
	came	t7,[sixbit/exe/]
	skipa
	jrst	addma6			
					
	came	t6,[sixbit/snap/]
	skipa
	came	t7,[sixbit/exe/]
	skipa
	jrst	addma6			
					
	came	t6,[sixbit/recov/]
	jrst	addma7
	came	t7,[sixbit/sys/]
	jrst	addma7
	jrst	addma6			
					

;	Here to exclude certain [1,4].ufd files from MAP.
;	These are files created by TWICE when the NEW disk is initialized.
addma6:	addi	t5,2			;Bump index for UFD block from old 
					;disk being being processed.
	jrst	addma5			;Go to next entry.

;	Here to add entry to MAP.
addma7:	savreg
	pushj	p,corget		;Grab core to add to MAP.
	resreg				;Returns with MAPLST=next available.
	movem	t6,name(maplst)		;Put in MAP.
	aoj	t5,			;Incriment O.UFD index.
	hrrz	t6,o.ufd(t5)		;Get the CFP.
	imul	t6,o.home+hombpc	;Convert CFP to logical block address.
	movem	t6,oldcfp(maplst)	;Put in MAP.
	hllz	t6,o.ufd(t5)		;Get the sixbit file extension.
	movem	t6,ext(maplst)		;Put in MAP.
	move	t6,newcfp(mapidx)	;Get new CFP of UFD pointing to me.
	movem	t6,newufd(maplst)	;Put in MAP.
	setzm	nxtnod(maplst)		;Clear pointer.
	aoja	t5,addma5		;Incriment O.UFD index. Go do next.
	subttl	Top level to write entries listed in MAP to new disk.

;	WARNING: The remainder of free core will be now be used for
;	buffering output to the target pack.
wrtmap:	outstr	[asciz/Copy Beginning...
/]
	move	t1,.jbff		;Where free core begins. This is
	movem	t1,outbuf		;the first word after MAP.

;	Now prepare the open block.
	movei	t1,.iodmp		;Specify dump mode.
	move	t2,topk			;Specify pack copying to.
	setz	t3,			;No inbuf/outbuf in dump mode.

;	Open output channel.
	open	outchn,t1
	  die(?WRTMAP: Error trying to OPEN channel for output to new disk.)

;	Here to write out [1,1].ufd.
	move	mapidx,map		;MAPIDX will serve as our index
					;to processing entries in MAP.
	move	oldidx,mapidx		;Security check on MAPIDX value.
	pushj	p,writit		;Start off processing [1,1].ufd.
	came	oldidx,mapidx		;Confirm its value.
	  die(?WRTMAP: MAPIDX was modified in WRITIT.)
	addi	mapidx,mapsiz*2		;Since the first entry in [1,1].ufd
					;is a [1,1] entry pointing to itself, 
					;we now have two consectutive [1,1]
					;entries in MAP. Set MAPIDX to skip
					;over the second.

;	Here to write all other files.
wrtma2:	camle	mapidx,maplst		;More entries to be processed?
wrtend:	jrst	updhom			;No. All files copied.
					;Go finish up.

;	Here to process next entry in MAP.
	move	oldidx,mapidx		;Security check on MAPIDX value.
	pushj	p,writit		;Process entry pointed to by MAPIDX.
	came	oldidx,mapidx		;Confirm its value.
	  die(?WRTMAP: MAPIDX was modified in WRITIT.)
	addi	mapidx,mapsiz		;Point to next entry.
	jrst	wrtma2			;Loop back.
;	Here to write out HOME blocks to new disk.
;	First, update new disk's HOME block words HOMSNM and HOMLOG.
updhom:	move	t1,o.home+homsnm	;SIXBIT structure name.
	movem	t1,n.home+homsnm
	move	t1,o.home+homlog	;SIXBIT logical unit name.
	movem	t1,n.home+homlog

;	Prepare to write the HOME blocks on new pack.
	movei	t1,1
	movei	t2,n.home
	movei	t3,blksiz
	savreg
	pushj	p,write
	resreg
	movei	t1,12
	savreg
	pushj	p,write
	resreg

;	Here to write out SAT blocks to new disk.
	move	t4,n.home+homspu	;Number of SAT blocks on new disk.
updsat:	sosge	t4			;Written them all?
	jrst	updsuf			;Yes.

;	Prepare for write.
	move	t1,n.dska(t4)		;Where to write.
	move	t5,n.satw
	imul	t5,t4
	move	t2,n.sat		;Start of 1st SAT block in core.
	add	t2,t5
	move	t3,n.satw
	savreg
	pushj	p,write
	resreg
	jrst	updsat

;	Here to update RIBS of special files in SYS UFD.
;	Update consists of putting new disk address of SYS UFD 
;	into the RIBUFD word of these files.
updsuf:	close	inchnl,
	movei	t1,.iodmp		;Specify dump mode.
	move	t2,topk			;Pack copying to.
	setz	t3,			;No inbuf/outbuf in dump mode.

;	Open a channel.
	open	inchnl,t1
	  die(? updsuf: Error trying to open channel.)
	move	t1,n.home+homsat	;SAT.SYS
	pushj	p,updsu2
	move	t1,n.home+homhms	;HOME.SYS
	pushj	p,updsu2
	move	t1,n.home+homswp	;SWAP.SYS
	pushj	p,updsu2
	move	t1,n.home+hommnt	;MAINT.SYS
	pushj	p,updsu2
	move	t1,n.home+hombad	;BADBLK.SYS
	pushj	p,updsu2
	move	t1,n.home+homcrs	;CRASH.EXE
	pushj	p,updsu2
	move	t1,n.home+homrcv	;RECOV.SYS
	pushj	p,updsu2
	pushj	p,wridmp		;Flush output buffers.
	pushj	p,dmpmap		;dump the map into MAP.FIL.
	pushj	p,dmpsat		;dump the sat into SAT.FIL.
	outstr	[asciz/Copy complete.
/]
	exit				;PROGRAM ENDS HERE.

updsu2:	skipn	t1			;Address zero?
	popj	p,			;Yes. Skip it.
	hlrz	t2,t1			;Check for junk word.
	skipe	t2			;Was there junk in left half of word?
	popj	p,			;Yes. Skip it.

;	Here to do the update.
	movei	t2,n.rib		;Where to read the RIB into.
	movei	t3,blksiz		;Size of RIB.
	savreg
	pushj	p,read
	resreg
	move	t4,n.home+homsuf	;New address of SYS UFD.
	movem	t4,n.rib+ribufd
	savreg
	pushj	p,write			;Put it back.
	resreg
	add	t1,n.home+hombpc	;Address of 2nd RIB.
	pushj	p,write			;Write it also.
	popj	p,
	subttl	WRITIT - subprocess of WRTMAP

;	Top level to write single entry pointed to by MAPIDX to new disk.
;	Read RIB of file. Branch to seperate code for directories and 
;	non-directories.

writit:	move	t1,oldcfp(mapidx)	;Address of RIB on old disk.
	movei	t2,o.rib		;Buffer in which to place RIB.
	movei	t3,blksiz		;Size of a RIB.
	pushj	p,bfread			;Read the RIB.

;	Do some security checks to confirm that the MAP is pointing to
;	the correct file on the old disk.
	move	t1,o.rib+ribnam		;Filename in RIB.
	came	t1,name(mapidx)		;Does it match name in MAP entry.
	   die(?WRITIT: Filename in RIB doesn't match that in MAP entry.)
	move	t1,o.rib+ribcod		;Get what should be the RIB code.
	came	t1,[xwd 0,777777]	;Is it?
	   die(?WRITIT: File pointed to by MAP entry is not a RIB.)

;	Is it a directory?
	hllz	t1,ext(mapidx)		;Get file extension.
	camn	t1,[sixbit/UFD/]	;an UFD?
	pjrst(doufd)			;Yes.
	camn	t1,[sixbit/SFD/]	;an SFD?
	pjrst(doufd)			;Yes.
;	Fall through if not a directory.

subttl	DOFILE - a subprocess of WRITIT. Processes Files.

;	Call with:
;	MAPIDX pointing to MAP entry of File being processed by WRITIT.
;	O.RIB has rib of File.
;	Consider all registers used.

;	Update and write the first RIB.
;	Need to get 1st word of 1st block for RIB Retrieval Pointer Checksum.
dofile:	move	t1,oldcfp(mapidx)	;Address of first RIB on old disk.
	aoj	t1,			;Address of first data block.
	movei	t2,o.file
	movei	t3,blksiz
	movem	t1,ribtst		;Save for RIBSLF check later.
	pushj	p,bfread
	move	t1,o.file		;Get 1st word for checksumming.
	movem	t1,n.chks		;Keep it here for routine CHKSUM.

;	If this file has no data, only RIBs, then the block just read
;	for checksumming will be a RIB. In this instance we want a 
;	checksum of zero. The following checks for this.
	move	t1,o.file+ribcod	;The word where a RIB's RIBCOD is.
	move	t2,o.file+ribslf	;The word where a RIB's RIBSLF is.
	camn	t1,[xwd 0,777777]	;Is it a RIBCOD?
	came	t2,ribtst		;And is it a RIBSLF?
	skipa				;Here if NOT a RIB.
	setzm	n.chks			;Yes. Zero the Checksum word.

;	Update RIB.
	pushj	p,updrib		;Called with RIB in O.RIB and MAPIDX
					;pointing to file's entry in MAP.
					;Updated RIB will be in N.RIB.
;	Write RIB.
	move	t1,newcfp(mapidx)	;Prepare to write the first RIB.
	movei	t2,n.rib
	movei	t3,blksiz
	pushj	p,write

;	Process file.
	move	wrtblk,newcfp(mapidx)	;Incrimental keeper of the block
					;address to which we last wrote a
					;block of this file. Initialize to
					;block address of RIB on new disk.
dofil0:	hrrz	t1,o.rib+ribfir		;RIB index to 1st retrieval pointer.
	addi	t1,1			;You just have to do this.
	move	t2,o.rib(t1)		;And get the first pointer.
	pushj	p,brkpto		;Break info out of pointer. Returns
					;t2=block where File starts t3=#blocks,
					;t4=checksum.
	came	t2,o.rib+ribslf		;Confirm that it points to itself.
	  die(?DOFILE: Address from RIB retrieval pointer is bad.)

	aoj	t2,			;The first pointer points to itself,
					;so the File we want follows. Add 1.
	soj	t3,			;Which also means one less block of
					;data for copying.
	savreg
	pushj	p,dofil3		;Process it.
					;First call to DOFIL3 will Update
					;and write the first RIB.
					;Updated RIB will be in N.RIB.
	resreg
dofil1:	addi	t1,1			;Move to next retrieval pointer.
	caile	t1,ribcod		;Check to see it's in bounds.
	  die(?DOFILE: RIB retrieval pointer index out of bounds.)
	cail	t1,ribcod		;Out of pointers?
	jrst	dofilx			;Yes. Check if extended RIB.
	move	t2,o.rib(t1)		;Get next pointer.
	jumpe	t2,dofilx		;Is the retrieval pointer zero?
					;If so, check if RIB is extended.
	pushj	p,brkpto		;No. Get info and process it.
					;Returns t2=File address t3=# blocks.
	savreg
	pushj	p,dofil3		;Process it.
	resreg
	jrst	dofil1			;Loop for next.

;	Here to check for extended RIBs.
dofilx:	skipn	t1,o.rib+ribxra		;Extended?
	jrst	dofil2			;No. Go finish up.
	move	t4,o.rib+ribnam		;Yes. For security check later.
	movei	t2,o.rib		;Prepare to read it.
	movei	t3,blksiz
	savreg
	pushj	p,bfread		;Read the extended RIB.
	resreg
	came	t4,o.rib+ribnam		;Do RIBNAMs match?
	  die(?DOFILX: Extended RIBNAM doesn't match RIBNAM in prime RIB.)
	jrst	dofil0			;And loop to process it.

;	Here when done to write last RIB. Updated copy in N.RIB.
dofil2:	move	t1,wrtblk		;Block address of last block of file.
	aoj	t1,
	movem	t1,n.rib+ribslf		;Adjust RIBSLF word of 2nd RIB to point
					;to itself. Only difference from 1st.
	movei	t2,n.rib		;Location of RIB in core.
	movei	t3,blksiz		;Words to write.
	pushj	p,write			;Doit.
	popj	p,			;Return.
subttl	DOFIL3 - subprocess of DOFILE.

;	Read from old disk and write to new all blocks associated with
;	the RIB retrieval pointer currently being processed by DOFILE.
;	Called with T2=logical block address on old disk first block,
;	and T3=number of blocks to be copied.
;	WRTBLK is block address of where the last block of this file
;	was written on new disk.

dofil3:	move	t1,t2			;Logical block of start of File.
	move	t4,t2			;Compute last block allocated+1.
	add	t4,t3
	movei	t2,o.File		;Where to read File block into.
	movei	t3,blksiz		;How many words.

;	Here to read one block of File.
;	NOTE: t1,t2,t3 and t4 are in use for remainder of this routine.
dofil4:	caml	t1,t4			;Processed the last block?
	popj	p,			;Yes. Return. Get next retrieval ptr.
	savreg				;No.
	movem	t1,ribtst		;Save for RIBSLF check later.
	pushj	p,bfread
	resreg

;	Check to see if we have just read a RIB.
;	If the file doesn't have extended RIBs it will be ours.
;	If the file does have extended RIBs and we are processing the last
;	of the retrieval pointers, it will be ours.
;	If the file does have extended RIBs but we are not processing the
;	last of the retrieval poiters, it will be the 1st RIB of another file.
;	Regardless of whose RIB it is, it will indicate to us that we
;	should return to process the next retrieval pointer.
	move	t5,o.file+ribcod	;The word where a RIB's RIBCOD is.
	move	t6,o.file+ribslf	;The word where a RIB's RIBSLF is.
	camn	t5,[xwd 0,777777]	;Is it a RIBCOD?
	came	t6,ribtst		;And is it a RIBSLF?
	skipa				;Here if NOT a RIB.
	popj	p,			;Yes. Return.

;	Here to write the block out.
	savreg				;Save registers for DOFIL4 loop.
	aoj	wrtblk,
	move	t1,wrtblk		;Where to write this block on new disk.
	movei	t2,o.file		;Location in core of block.
	movei	t3,blksiz		;Words to write.
	pushj	p,write			;Doit.
	resreg				;Restore registers for DOFIL4 loop.
	aoja	t1,dofil4
subttl	DOUFD - a subprocess of WRITIT. Processes UFDs.

;	Call with:
;	MAPIDX pointing to MAP entry of UFD being processed by WRITIT.
;	O.RIB has rib of UFD.
;	Uses t1,t2,t3.

;	Check for special UFDs [1,1] and [1,4].
doufd:	move	t1,o.rib+ribnam		;Check for special cases, [1,4] [1,1].
	setzm	mufd			;Assume not MFD.
	camn	t1,[xwd 1,4]		;Is this it?
	pjrst(dosufd)			;Yes. Goto routine to process it.
	camn	t1,[xwd 1,1]		;Check for special case, [1,1].UFD.
	setom	mufd			;Set [1,1].UFD flag for UPDRIB rtn.

;	Here for directories other than [1,4].ufd
;	Initialize RIBSIZ decrement counter.
	move	t1,o.rib+ribsiz		;Words written in UFD.
	idivi	t1,blksiz		;Make into blocks.
	skipe	t2			;If a remainder,
	  die(?DOUFD: RIBSIZ is not an even factor of block size.)
	movem	t1,totblk		;Keep the decremental counter here.

;	Update and write the first RIB.
;	Need to get 1st word of 1st block for RIB Retrieval Pointer Checksum.
	move	t1,oldcfp(mapidx)	;Address of first RIB on old disk.
	aoj	t1,			;Address of first data block.
	movei	t2,o.file
	movei	t3,blksiz
	movem	t1,ribtst		;Save for RIBSLF check later.
	pushj	p,bfread
	move	t1,o.file		;Get 1st word for checksumming.
	movem	t1,n.chks		;Keep it here for routine CHKSUM.

;	If this UFD has no data, only RIBs, then the block just read
;	for checksumming will be a RIB. In this instance we want a 
;	checksum of zero. The following checks for this.
	move	t1,o.file+ribcod	;The word where a RIB's RIBCOD is.
	move	t2,o.file+ribslf	;The word where a RIB's RIBSLF is.
	camn	t1,[xwd 0,777777]	;Is it a RIBCOD?
	came	t2,ribtst		;And is it a RIBSLF?
	skipa				;Here if NOT a RIB.
	setzm	n.chks			;Yes. Zero the Checksum word.

;	Update RIB.
	pushj	p,updrib		;Called with RIB in O.RIB and MAPIDX
					;pointing to file's entry in MAP.
					;Updated RIB will be in N.RIB.
;	Write RIB.
	move	t1,newcfp(mapidx)	;Prepare to write the first RIB.
	movei	t2,n.rib
	movei	t3,blksiz
	pushj	p,write

;	Process UFD data.
	move	wrtblk,newcfp(mapidx)	;WRTBLK keeps new disk block
					;address to which we last wrote a
					;block of this file. Initialize to
					;block address of RIB on new disk.
	move	maptmp,nxtnod(mapidx)	;@MAPTMP is address of first
					;MAP entry to which current UFD entry
					;being processed by MAPWRT, points.
doufd0:	hrrz	t1,o.rib+ribfir		;RIB index to 1st retrieval pointer.
	addi	t1,1			;You just have to do this.
	move	t2,o.rib(t1)		;And get the first pointer.
	pushj	p,brkpto		;Break info out of pointer. Returns
					;t2=block where UFD starts t3=# blocks,
					;t4=checksum.
	came	t2,o.rib+ribslf		;Confirm that it points to itself.
	  die(?DOUFD: Address from RIB retrieval pointer is bad.)

	aoj	t2,			;The first pointer points to itself,
					;so the UFD we want follows. Add 1.
	soj	t3,			;Which also means one less block of
					;directory information for processing.
	savreg
	pushj	p,doufd3		;Process it.
					;First call to DOUFD3 will update
					;and wrie the first RIB.
					;Updated RIB will be in N.RIB.
	resreg
doufd1:	addi	t1,1			;Move to next retrieval pointer.
	caile	t1,ribcod		;Check to see it's in bounds.
	  die(?DOUFD: RIB retrieval pointer index is out of bounds.)
	cail	t1,ribcod		;Out of pointers?
	jrst	doufdx			;Yes. Check if RIB is extended.
	move	t2,o.rib(t1)		;Get next pointer.
	jumpe	t2,doufdx		;Is the retrieval pointer zero?
					;Yes. Go check if RIB is extended.
	pushj	p,brkpto		;No. Get info and process it.
					;Returns t2=UFD address t3=# blocks,
					;t4=checksum.
	savreg
	pushj	p,doufd3		;Process it.
	resreg
	jrst	doufd1			;Loop for next.

;	Here to check for extended RIBs.
doufdx:	skipn	t1,o.rib+ribxra		;Extended?
	jrst	doufd2			;No. Return.
	move	t4,o.rib+ribnam		;For security check later.
	movei	t2,o.rib		;Yes. Prepare to read it.
	movei	t3,blksiz
	savreg
	pushj	p,bfread		;Read the extended RIB.
	resreg
	came	t4,o.rib+ribnam		;Do RIBNAMs match?
	  die(?DOUFDX: Extended RIBNAM doesn't match RIBNAM in prime RIB.)
	jrst	doufd0			;And loop to process it.

;	Here when done to write last RIB. Updated copy in N.RIB.
doufd2:	move	t1,wrtblk		;Block address of last block of file.
	aoj	t1,
	movem	t1,n.rib+ribslf		;Adjust RIBSLF word of 2nd RIB to point
					;to itself. Only difference from 1st.
	movei	t2,n.rib		;Location of RIB in core.
	movei	t3,blksiz		;Words to write.
	pushj	p,write			;Doit.
	popj	p,
subttl	DOUFD3 - subprocess of DOUFD.

;	Ccalled once for each of the UFD's rib retrieval pointers.
;	Called with t2=logical block number from rib retrieval pointer,
;	and t3=number of blocks from the rib retrieval pointer.
;	Updates each entry in the UFD with new CFP.

doufd3:	skipg	totblk			;Check RIBSIZ decrement counter.
	popj	p,			;We have already read as many blocks
					;in this UFD as are actually used.
					;The RIB retrieval pointer we have
					;been asked to process must be the
					;result of an Uncompressed RIB, and
					;hence is not really in use. Skip it.

;	Here if we haven't read all the blocks in the UFD yet.
	move	t1,t2			;Logical block of start of data.
	move	t4,t2			;Compute last block allocated+1.
	add	t4,t3
	movei	t2,o.ufd		;Where to read UFD block into.
	movei	t3,blksiz		;How many words.

;	Here to read one block of UFD.
;	note: t1,t2,t3,t4 are in use for balance of routine.
doufd4:	caml	t1,t4			;Processed the last blk of this ptr?
	popj	p,			; Yes. Return to get next retrieval ptr.
	sosge	totblk			;Decrement blocks read counter by one.
	popj	p,			; Have we read all the used blocks in
					; in the UFD? Return.
	savreg				;No. Continue.
	pushj	p,bfread
	resreg

;	Here to process the block of the UFD just read.
	setz	t5,			;Index to entries in UFD.
doufd5:	caile	t5,blksiz-2		;Finished this block? 2 words/entry.
	jrst	doufd7			;Yes. Go write this block to new disk.
	move	t6,o.ufd(t5)		;Get sixbit filename from UFD entry.
	jumpe	t6,doufd7		;Zero, meaning no more data in block?

;	Here if with t5 pointing to UFD block entry to be updated.
;	Update the entry with MAP entry pointed to by MAPTMP.
;	Check that the FileNames correspond for added security.
doufd6:	came	t6,name(maptmp)		;Same as in MAP entry?
	  die(?DOUFD6: UFD entry in MAP does not correspond with UFD block.)
	aoj	t5,
	hlrz	t6,ext(maptmp)		;File Exntension from MAP entry.
	hlrz	t7,o.ufd(t5)		;File Extension from UFD.
	came	t6,t7			;Same?
	   die(?DOUFD6: UFD entry in MAP does not correspond with UFD block.)
	move	t6,newcfp(maptmp)	;File's block address on new disk.
	idiv	t6,n.home+hombpc	;Covert to CFP.
	skipe	t7			;Remainder from division?
	  die(?DOUFD6: Conversion of block address to CFP not exact.)
	hrrm	t6,o.ufd(t5)		;Update UFD pointer to file.
	addi	maptmp,mapsiz		;Next entry in MAP.
	aoja	t5,doufd5		;Next entry in UFD block.

;	Here to write out updated UFD block.
doufd7:	savreg				;Save registers for DOUFD4 loop.
	aoj	wrtblk,
	move	t1,wrtblk		;Where to write this block on new disk.
	movei	t2,o.ufd		;Location in core of block.
	movei	t3,blksiz		;Words to write.
	pushj	p,write			;Doit.
	resreg				;Restore registers for DOUFD4 loop.
	aoja	t1,doufd4
	subttl	DOSUFD - a subprocess of WRITIT. Processes [1,4].UFD only.

;	Call with:
;	MAPIDX pointing to the [1,4].UFD entry in MAP.
;	O.RIB has the [1,4].UFD RIB.
;	WRTBLK is block address of where the 1st RIB will be on new disk.
;	Uses all registers.
;	t1 and t2 are dedicated indexes, not to destroyed in this routine.

;	Update and write the first RIB.
;	Need to get 1st word of 1st block for RIB Retrieval Pointer Checksum.
dosufd:	move	t1,oldcfp(mapidx)	;Address of first RIB on old disk.
	aoj	t1,			;Address of first data block.
	movei	t2,o.file
	movei	t3,blksiz
	movem	t1,ribtst		;Save for RIBSLF check later.
	pushj	p,bfread
	move	t1,o.file		;Get 1st word for checksumming.
	movem	t1,n.chks		;Keep it here for routine CHKSUM.

;	If the SYS UFD has no data, only RIBs, then the block just read
;	for checksumming will be a RIB. In this instance we want a 
;	checksum of zero. The following checks for this.
	move	t1,o.file+ribcod	;The word where a RIB's RIBCOD is.
	move	t2,o.file+ribslf	;The word where a RIB's RIBSLF is.
	camn	t1,[xwd 0,777777]	;Is it a RIBCOD?
	came	t2,ribtst		;And is it a RIBSLF?
	skipa				;Here if NOT a RIB.
	setzm	n.chks			;Yes. Zero the Checksum word.

;	Update RIB.
	pushj	p,updrib		;Called with RIB in O.RIB and MAPIDX
					;pointing to file's entry in MAP.
					;Updated RIB will be in N.RIB.
;	Write RIB.
	move	t1,newcfp(mapidx)	;Prepare to write the first RIB.
	movei	t2,n.rib
	movei	t3,blksiz
	pushj	p,write

;	Process SYS UFD data.
	move	wrtblk,newcfp(mapidx)	;WRTBLK keeps new disk block
					;address to which we last wrote a
					;block of this file. Initialize to
					;block address of RIB on new disk.
	setz	t2,			;Find 1st free filename position
dosuf1:	skipe	n.sufd(t2)		;in N.SUFD.
	aoja	t2,dosuf1		;Failed, try next word.
	cail	t2,blksiz-2		;Check to see the word's in bounds.
	  die(?DOSUFD: N.SUFD has no free words in it.)
	move	maptmp,nxtnod(mapidx)	;First entry pointed to by [1,4].
	hrrz	t1,ext(mapidx)		;Number of entries.

dosuf2:	sosge	t1			;For each of [1,4]'s entries do:
	jrst	dosuf6			;Here when done. Go finish up.
	pushj	p,dosuf3		;Add entry to UFD block being built.
	addi	maptmp,mapsiz		;Point to next [1,4] entry.
	jrst	dosuf2			;Go process it.

;	Here to add [1,4] entry pointed to by MAPTMP to UFD block.
dosuf3:	caile	t2,blksiz-2		;This UFD block full?
	pushj	p,dosuf4		;Yes. Write the block out, reset
					;index t2, and zero out N.SUFD.
	move	t3,name(maptmp)		;File name.
	movem	t3,n.sufd(t2)		;Put in UFD block.
	aoj	t2,			;Incriment index to extension word.
	move	t3,newcfp(maptmp)	;File's address on new disk.
	idiv	t3,n.home+hombpc	;Change to a Compressed File Ptr.
	skipe	t4			;A remainder from division?
	  die(?DOSUFD: Conversion from block address to CFP not exact.)
	hll	t3,ext(maptmp)		;and the File extension.
	movem	t3,n.sufd(t2)		;Add to UFD block.
	aoj	t2,			;Incriment index to next entry.
	popj	p,			;Return.

;	Here to write out N.SUFD when it has become full.
;	Also resets index t2 to zero, and zeroes the N.SUFD.
dosuf4:	savreg				;Save registers for DOSUF2, DOSUF3.
	aoj	wrtblk,
	move	t1,wrtblk		;Where on new disk to write this block.
	movei	t2,n.sufd		;Blocks location in core.
	movei	t3,blksiz		;Number of words to write.
	pushj	p,write			;Doit.
	resreg				;Restore registers for DOSUF2, DOSUF3

	setzm	n.sufd			;Zero out N.SUFD buffer.
	hrli	t2,n.sufd		;Prepare for BLT.
	hrri	t2,n.sufd+1
	blt	t2,n.sufe		;Zero through to end word.
	setz	t2,			;Reset buffer index to top.
	popj	p,

;	Here when done to write current contents of N.SUFD and
;	to write last RIB.
dosuf6:	pushj	p,dosuf4		;Write out contents of N.SUFD.
;	Now the RIB.
	move	t1,wrtblk		;Block address of last block of file.
	aoj	t1,
	movem	t1,o.rib+ribslf		;Adjust RIBSLF word of 2nd RIB to point
					;to itself. Only difference from 1st.
	movei	t2,o.rib		;Location of RIB in core.
	movei	t3,blksiz		;Words to write.
	pushj	p,write			;Doit.
	popj	p,
	subttl	Data Storage

totblk:	block	1	;Use for counting blocks read of a UFD.
foo:	block	1
Ribtst:	block	1	;Temp word used for checking if a block is a RIB.
Frompk:	block	1	;Name of pack copying from.
Topk:	block	1	;Name of pack copying to.
ToPkId:	block	1	;I.D. of pack copying to.
maxfil:	block	1	;Used to determine if a file is too large for D2D.
map:	block	1	;Will contain starting address of core map.
pdlist:	block	pdlen	;Push down stack.
sufd:	block	1	;Flag to indicate to MAP builder that 
			;SYS.UFD ([1,4]) is being processed (nonzero).
mufd:	block	1	;Flag to indicate to UPDRIB rtn that [1,1].UFD is
			;bein processed.
outbuf:	block	1	;Pointer to top of free core used for buffering
			;output to target disk.
			;WARNING: nothing should be placed in free core
			;after the point to which this word points.
topblk:	block	1	;Logical disk address on new pack of first block
			;of data in buffer area pointed to by OUTBUF.
inblks==^d3		;Number of blocks in INBUF.
inbuf:	block	inblks*blksiz	;Pointer to Input buffer used by BFREAD.
inaddr:	-1		;Logical disk address on first block of data
			;in INBUF.

;	The following apply to the old pack from which data is being copied.
o.home:	block	blksiz	;For reading in HOME block.
o.rib:	block	blksiz	;For reading in RIBs.
o.file:	block	blksiz	;For reading in data portion of files.
o.ufd:	block	blksiz	;For reading in UFDs.
o.ribp:	block	1	;RIB retrieval pointer word.
o.ptra:	xwd	0,o.ribp	;Point to cluster add in retrieval pointer.
o.ptrc:	xwd	0,o.ribp	;Point to cluster count in retrieval pointer.
o.ptrs:	xwd	0,o.ribp	;Point to checksum in retrieval pointer.

;	The following apply to the new pack to which data is being copied.
n.home:	block	blksiz	;For reading in HOME block.
n.rib:	block	blksiz	;For building new RIBs from old ones.
n.satr:	block	blksiz	;For target packs SAT.SYS RIB.
n.tmp:	block	1	;Misc. read-in's.
n.ribp:	block	1	;RIB retrieval Pointter word.
n.ptra:	xwd	0,n.ribp	;PoinTR to cluster Address in RIBP.
n.ptrc:	xwd	0,n.ribp	;PoinTR to cluster Count in RIBP.
n.ptrs:	xwd	0,n.ribp	;PoinTR to checksum in RIBP.
n.sufd:	block	blksiz		;For building a System UFD block for new disk.
				;[1,4].ufd is treated seperately because of
				;certain files there not be be copied.
n.sufe:	.-1			;Last word of N.SUFD.
n.chks:	block	1		;For computing checksums.

;	The following refer to the New pack's SAT which is built in-core.
n.sat:	block	1	;Permanent pointer to in-core SAT.
n.satt:	block	1	;SAT Top. Address of 1st word in-core SAT with
			;free clusters. May move as space allocation proceeds.
n.dska:	block	50	;DiSK Address. c(N.DSKA+n) is the
			;logical block address of the first cluster pointed to
			;by SAT block n.
n.sate:	block	1	;SAT End. Address of last word in-core SAT.
n.satw:	block	1	;SAT Words. Number of words in each SAT block 
			;that are actually used to represent clusters.
			;used to respresent clusters.
n.satp:	point	1,0	;Used for marking single bits(one cluster) in SAT.
n.bckp:	block	1	;BaCKPointer will be built here.
			;It will be a pointer into the SAT area used to
			;mark the Back or start of a contiguos group of
			;free cluster pointers.
n.fwdp:	block	1	;ForWarDPointer will be built here.
			;It will be a pointer into the SAT area used to
			;search forward from where the BaCKPointer points
			;to determine if there are a enough contiguous
			;clusters to meet the request made on routine ALLOC.
n.satb:	block	1	;Blocks per SAT on new pack.

	end	d2d	;Show where to start execution.