AUSAM/source/mdec/rkdfc.c

Compare this file to the similar file:
Show the results in this format:

#

/*	This program created by Ian J.	July '76
 *
 *	Performs certain necessary i/o functions for rk05 disks:
 *
 *	  h for help
 *	  c for copy function
 *	  f for format function
 *	  k for compare function
 *	  r for read function
 */

/*	Modified by Kevin Hill		Sep-Oct 78
 *
 *	1.	Corrected a bug which caused `rkdf' to lie about block nrs.
 *	2.	Extra commands added:
 *
 *		    `a' for assisting with drive alignment;
 *		    `b' for reboot (asks for a drive number);
 *		    `d' for the J. Rottman `dcopy' program (as modified
 *			by C. Maltby / K. Hill).
 *
 *	3.	Altered code slightly so that `help' is printed out at start.
 *	4.	Rewrote `gdrivnum' and `rkio' to avoid use of goto's.
 *	5.	Included a smaller `printf' - only `%d', `%o', and `%s' supported.
 *	6.	Added DELETE facility.
 *	7.	Added checks to detect:
 *
 *		    if selected drive is active;
 *		    if drive selected to be written on is write-protected;
 *		    if same drive numbers for compare (==> exit) or copy
 *			(==> copy-in-situ on a cylinder-by-cylinder basis).
 */

#define	DELETE	0177

#define	NRETRYS	10	/* number of attempts at disk i/o before collapsing in a heap */

#define	SSR0	0177572
#define	KISA0	0172340
#define	KISA6	0172354
#define	KISD0	0172300
#define	SWREG	0177570
struct { int i; };	/* structure to access integers */


#define	TT	0177560
#define	DONE	0200

struct
{
	int	csr;
	int	csb;
	int	tsr;
	int	tsb;
};


#define	RK	0177400

#define	NCYL		203
#define	BLKPERTRK	12
#define	NTRACK		2
#define	BLKPERCYL	NTRACK * BLKPERTRK
#define	WDSPERCYL	256 * BLKPERCYL
#define	BLKPERDSK	NCYL * BLKPERCYL	/* 4872 */

struct
{
	int	rkds;
	int	rker;
	int	rkcs;
	int	rkwc;
	int	rkba;
	int	rkda;
};

/* rk i/o requests */

#define	CNTRLR		   01		/* control reset */
#define	WRITE		   03	
#define	READ		   05
#define	WRITECHK	   07
#define	SEEK		  011
#define	READCHK		  013
#define	WRITEPROT	  017
#define	SSE		 0400		/* stop on soft error */
#define	FMTW		02003		/* format write */
#define	IBA		04000		/* inhibit incrementing RKBA */

#define	WTPROT		 040		/* write protect bit */
#define	DRY		0200		/* drive ready bit */


char *bigbuf;		/* pointer to buffer area */
int	iob;		/* number of blocks per big i/o */
int	nio;		/* number of i/o for these blocks */
int    szio;		/* number of words for each of above i/os */
int	iov;		/* number of blocks left after the big ones */
int    szov;		/* number of words for this last i/o */

int in, out;		/* drives for current input/output operations */
int inform, outform;	/* optimised-format flags - for dcopy */
int base;		/* base of number to be printed out */
int kludge;		/* prints out help first time only */



main()
{
	register i;

	setexit();
	if (kludge++ == 0) help();
	for (;;)
	{
		printf("\007\nWhich function is desired? \007");
		i = getchar();
		putchar('\n');
		switch(i)
		{
	    case 'a':	in = gdrivnum("Drive# ", 0);
			wrlock(in);
			align();	/* no return */

	    case 'b':	printf("Switches = %o - ", SWREG->i);
			base = gdrivnum("Boot from drive# ", 0) << 13;
			putchar(0); putchar(0);		/* flush */
			reboot();		/* no return */

	    case 'c':	in = gdrivnum("Read from drive# ", 0);
			out = gdrivnum(" Write to drive# ", 1);
			if (in == out)
			{
				if (iob >= BLKPERCYL)
				{
					reed(in);
					cpyinsitu();
					break;
				}
				printf("\007Not enough core to copy-in-situ.\n");
				break;
			}
			wrlock(in);
			reed(in);
			format();
			copy();
			check();
			break;

	    case 'd':	in = gdrivnum("Read from drive# ", 0);
			inform = formtype();
			out = gdrivnum("Write to drive# ", 1);
			if (in == out)
			{
				printf("Same drives (?)\n");
				break;
			}
			outform = formtype();
			wrlock(in);
			dcopy();		/* does the reed and format */
			break;

	    case 'f':	out = gdrivnum("Format drive# ", 1);
			format();
			reed(out);
			break;

	    case 'k':	in = gdrivnum("   Read from drive# ", 0);
			out = gdrivnum("Compare with drive# ", 0);
			if (in == out) printf("\007Same drives.\n");
			else check();
			break;

	    case 'r':	in = gdrivnum("Read from drive# ", 0);
			wrlock(in);
			reed(in);
			break;

	    default:	help();
		}

        }
}

help()
{
	printf("\nYou are reqd. to enter a char\n");
	printf("to indicate the function you want\n");
	printf("        a -- align\n");
	printf("        b -- reboot\n");
	printf("        c -- copy\n");
	printf("        d -- dcopy\n");
	printf("        f -- format\n");
	printf("        h -- help\n");
	printf("        k -- compare\n");
	printf("        r -- read\n");
}

wrlock(unit)
register unit;
{
	rkio(unit, 0, 0, WRITEPROT, 0);
	printf("Drive %d write protected.\n", unit);
}

format()
{
	register i, unit;

	bigbuf[0] = 0;
	printf("Begin formatting drive %d.\n", unit = out);
	for(i = 0; i < nio; i++) rkio(unit, bigbuf, iob * i, FMTW | IBA, szio);
	rkio(unit, bigbuf, iob * i, FMTW | IBA, szov);
	printf("Formatting complete.\n");
}

cpyinsitu()
{
	register i, unit;

	printf("Begin copying drive %d in situ.\n", unit = in);
	for (i = 0; i < NCYL; i++)
	{
		rkio(unit, bigbuf, i * BLKPERCYL, READ | SSE, WDSPERCYL);
		rkio(unit, bigbuf, i * BLKPERCYL, FMTW, WDSPERCYL);
		rkio(unit, bigbuf, i * BLKPERCYL, WRITECHK | SSE, WDSPERCYL);
	}
	wrlock(unit);
	printf("Copying complete.\n");
}

copy()
{
	register i, iunit, ounit;

	printf("Begin copying drive %d to drive %d.\n", iunit = in, ounit = out);
	for (i = 0; i < nio; i++)
	{
		rkio(iunit, bigbuf, i * iob, READ | SSE, szio);
		rkio(ounit, bigbuf, i * iob, WRITE | SSE, szio);
		rkio(ounit, bigbuf, i * iob, WRITECHK | SSE, szio);
	}
	rkio(iunit, bigbuf, i * iob, READ | SSE, szov);
	rkio(ounit, bigbuf, i * iob, WRITE | SSE, szov);
	rkio(ounit, bigbuf, i * iob, WRITECHK | SSE, szov);
	printf("Copy complete.\n");
}

check()
{
	register i, iunit, ounit;

	wrlock(iunit = in);
	wrlock(ounit = out);
	printf("Begin comparing drive %d to drive %d.\n", iunit, ounit);
	for (i = 0; i < nio; i++)
	{
		rkio(iunit, bigbuf, i * iob, READ | SSE, szio);
		rkio(ounit, bigbuf, i * iob, WRITECHK | SSE, szio);
	}
	rkio(iunit, bigbuf, i * iob, READ | SSE, szov);
	rkio(ounit, bigbuf, i * iob, WRITECHK | SSE, szov);
	printf("Compare complete.\n");
}

reed(unit)
register unit;
{
	register i;

	printf("Begin reading drive %d.\n", unit);
	for (i = 0; i < nio; i++) rkio(unit, bigbuf, i * iob, READCHK | SSE, szio);
	rkio(unit, bigbuf, i * iob, READCHK | SSE, szov);
	printf("Read complete.\n");
}

align()
{
	register block, unit;
	register char c;

	unit = in;
	for (;;)
	{
		printf("Cylinder# ");
		block = getnum(0, NCYL, 0) * BLKPERCYL;
		printf("0 = top head; 1 = bottom head; 'c' for new cyl; DEL to exit.\n");
		for (;;)
		{
			printf("Head# ");
			c = getchar();
			putchar('\n');
			if (c == 'c') break;
			if (c != '0' && c != '1')
			{
				printf("\007Illegal head.\n");
				continue;
			}
			rkio(unit, 0, 0, WRITEPROT, 0);		/* safety first! */
			rkio(unit, 0, c == '1' ? block + BLKPERTRK : block, SEEK | SSE, 0);
		}
	}
}

rkio(unit, buffer, block, cmd, wc)
register unit, block, wc;
{
	int nretry;

	for (nretry = 0; nretry < NRETRYS; nretry++)
	{
		while ((RK->rkcs & DONE) == 0);
		RK->rkda = ((block / 12) << 4) | (block % 12) | (unit << 13);
		RK->rkba = buffer;
		RK->rkwc = -wc;
		RK->rkcs = cmd;
		while ((RK->rkcs & DONE) == 0);
		if (RK->rkcs >= 0) return;
		printf("\007Error on drive %d block=%d. -- ds=%o da=%o cs=%o er=%o\n",
			(RK->rkda >> 13) & 07,
			12 * ((RK->rkda & 017760) >> 4) + (RK->rkda & 017),
			RK->rkds, RK->rkda, RK->rkcs, RK->rker);
		RK->rkcs = CNTRLR;
	}
	printf("\nRecovery impossible.\n");
	putchar(0); putchar(0);		/* flush */
	restart();		/* no return */
}

/********************************************************/


getnum(lower, upper, dflt)		/* returns lower <= num < upper */
register unsigned upper;
{
	register unsigned n;
	register c;

	for (;;)
	{
		n = 0;
		if ((c = getchar()) == '\r')
		{
			if (dflt < upper) n = dflt;
		}
		else while (c >= '0' && c <= '9' && (n = n * 10 + c - '0') < upper) c = getchar();
		putchar('\n');
		if (c == '\r' && n >= lower) return(n);
		printf("\007Illegal entry - try again ");
	}
}

formtype()
{
	register c;

	printf("Optimised format? (y-n) ");
	c = (getchar() == 'y');
	putchar('\n');
	return(c);
}

gdrivnum(s, chkwrprt)
register char *s;
register chkwrprt;
{
	register c;

	for (;;)
	{
		printf(s);
		c = getchar() - '0';
		putchar('\n');
		if (c < 0 || c > 7)
		{
			printf("\007Illegal drive.\n");
			continue;
		}
		RK->rkda = c << 13;
		if ((RK->rkds & DRY) == 0)
		{
			printf("\007Drive not active.\n");
			continue;
		}
		if (chkwrprt && (RK->rkds & WTPROT))
		{
			printf("\007Drive write protected.\n");
			continue;
		}
		return(c);
	}
}

printf(s, a)
register char *s;
{
	register c, *p;

	p = &a;
	while (c = *s++)
	{
		if (c == '%')
			switch (c = *s++)
			{
		    case 's':	printf(*p++);	/* CARE! */
				continue;

		    case 'o':	base = 8;
				printl(*p++);
				continue;

		    case 'd':	base = 10;
				printl(*p++);
				continue;

		    case 0:	return;
			}
		putchar(c);
	}
}

printl(n)
register unsigned n;
{
	register i;

	i = n % base;
	if (n =/ base) printl(n);
	putchar(i + '0');
}

putchar(c)
register char c;
{
	while ((TT->tsr & DONE) == 0);
	TT->tsb = c;
	if (c == '\n') putchar('\r');
}

getchar()
{
	register c;

	while ((TT->csr & DONE) == 0);
	if ((c = TT->csb & 0177) == DELETE)
	{
		putchar('\n');
		reset();
	}
	putchar(c);
	if (c >= 'A' && c <= 'Z') c =| 040;
	return(c);
}

/********************************************************/


#include	<local-system>
#include	<defines.h>
#include	<param.h>
#include	<filsys.h>
#include	<ino.h>


#define	INTL		3	/* magic interleaving number */

#define	IN_NODE		sizeof *superblk
#define	OUT_NODE	IN_NODE + 16 * sizeof *in_node
#define	GENERAL		OUT_NODE + 16 * sizeof *out_node
#define	IND0		GENERAL + 512
#define	IND1		IND0 + 512
#define	INDX		IND1 + 512

struct buffer
{
	int bn;		/* block number */
	int bb[256];	/* block */
} *startbuff, *currbuff;

struct filsys *superblk;	/* target super block */
struct inode *in_node;		/* inode blocks from source */
struct inode *out_node;		/* inode blocks to target */
int *general;			/* general usage */
int *ind0;			/* first indirect block */
int *ind1;			/* second indirect block */
int *indx;			/* start of dynamic array area */
int *iindex;			/* used with indx */
char adr[BLKPERTRK];		/* interleaving array */
int	nbuf;			/* number of buffers in the pool */
int	inpool;			/* number of used buffers */
struct	inode	*node;		/* the current inode being considered */
int	isize_sav;		/* i-list size from the source super block */
long	time_sav;		/* time from the source super block */
int	form;			/* format of current input unit */
int	upto;			/* where up to when compacting directories */
unsigned dirent;		/* counts the new directory entries */
int	*diraddr;		/* where up to in the current inode */
int	*dirind;		/* where up to in indirect block */
int	inext;
int	inum, iblk, onum, oblk;
int	lastblock, cyl, sector, track;

dcopy()
{
	register char *ptr1;
	register *ptr2;
	register icount;
	int isize1;

	superblk = bigbuf;
	in_node	= &bigbuf[IN_NODE];
	out_node = &bigbuf[OUT_NODE];
	general	= &bigbuf[GENERAL];
	ind0	= &bigbuf[IND0];
	ind1	= &bigbuf[IND1];
	indx	= &bigbuf[INDX];
	iindex = indx - 1;
	inum = 16;
	iblk = 1;
	onum = 0;
	oblk = 2;
	inpool = 0;
	cyl = sector = track = 0;

	form = inform;
	getblk(in, superblk, 1);		/* source super block */
	isize_sav = superblk->s_isize;
	time_sav = superblk->s_time;


/*	Test to see if there is room for the program plus reserved buffers
 *	plus at least 8 buffers in the pool, in the program's address space,
 *	and in the machine's core.
 */

	ptr1 = &bigbuf[INDX] + superblk->s_isize * 2 * 16;
	inext = 8 * sizeof *startbuff;
	if (ptr1 + inext < &bigbuf[INDX] || ((ptr1 + inext + 077) >> 6) > (icount = KISA6->i))
	{
		printf("Not enough core.\n");
		return;
	}
	inext = 1;
	if (icount > 01600) icount = 01600;		/* i/o page */
	icount = (icount << 6) - ptr1;
	currbuff = startbuff = ptr1;
	nbuf = ldiv(0, icount, sizeof *startbuff);
	printf("%d buffers in pool.\n\n", nbuf);

	printf("Source: size = %d blks (%d in use); i-list = %d blks (%d in use).\n",
		superblk->s_fsize, superblk->s_fsize - (ptr1 = countfree()), superblk->s_isize, ptr2 = counti());
	for (;;)
	{
		printf("Enter size (blocks) of target volume (%d less swap space) ", BLKPERDSK);
		icount = getnum(ptr2, BLKPERDSK + 1, superblk->s_fsize);
		printf("Enter size (blocks) of target i-list (16 inodes per block) ");
		isize1 = getnum(ptr2, icount, superblk->s_isize);
	
		if ((superblk->s_fsize - (superblk->s_isize + ptr1)) <= (icount - isize1)) break;
		printf("Insufficient file space.\n");
	}

	reed(in);
	format();		/* secondary effect is to clear the inodes */

	for (ptr2 = superblk; ptr2 < &superblk[1]; *ptr2++ = 0);	/* clear target super block */
	superblk->s_fsize = icount;
	superblk->s_isize = isize1;
	superblk->s_time = time_sav;

	ptr1 = "optimised ";
	printf("Begin dcopy from %sdrive %d to %sdrive %d.\n",
		inform ? ptr1 : "", in, outform ? ptr1 : "", out);
	getblk(in, grablock(0), 0);		/* bootstrap block */

	for (ptr2 = general; ptr2 < &general[BLKPERTRK]; *ptr2++ = 0);
	icount = 0;
	for (ptr1 = adr; ptr1 < &adr[BLKPERTRK]; )	/* set up interleaving template */
	{
		while (general[icount]) icount = (icount + 1) % BLKPERTRK;
		*ptr1++ = icount;
		general[icount]++;
		icount = (icount + INTL) % BLKPERTRK;
	}

	printf("Copy files.\n");
	icount = 16 * isize_sav;
	do
	{
		geti();
		if (node->i_mode & IALLOC)
		{
			copyi();
			*indx++ = inext++;
		}
		else *indx++ = 0;
	} while (--icount);

	printf("Complete i-list.\n");
	if (onum)
	{
		ptr1 = &out_node[onum];
		icount = 32 * (16 - onum);
		do *ptr1++ = 0; while (--icount);
		putblk(out_node, oblk);
	}
	purgepool();		/* remaining inodes zero due to the format */

	printf("Correct directory entries.\n");
	iblk = 1;
	inum = 16;
	in = out;		/* so `geti' gets from target */
	form = outform;
	for (icount = 0; icount < inext; icount++)
	{
		geti();
		if ((node->i_mode & IFMT) == IFDIR) reldir();
	}

	printf("Construct the free list.\n");
	ptr1 = 16 * isize1;
	icount = inext;
	while (icount <= ptr1 && superblk->s_ninode != 100)
		superblk->s_inode[superblk->s_ninode++] = icount++;
	ptr2 = &general[100];
	for (icount = 100; icount < 256; icount++) *ptr2++ = 0;
	freelist();
	putblk(superblk, 1);
	purgepool();
	wrlock(out);
	printf("Dcopy complete.\n");
}

getblk(unit, buff, block)
register unit, buff, block;
{
	rkio(unit, buff, map(form, block), READ | SSE, 256);
}

grablock(n)		/* grabs the next free block from the pool */
register n;
{
	if (inpool == nbuf) purgepool();
	inpool++;
	currbuff->bn = map(outform, n);
	return((currbuff++)->bb);
}

putblk(buff, block)
register int *buff;
{
	register int *w, q;

	w = grablock(block);
	q = 256;
	do *w++ = *buff++; while (--q);
}

purgepool()
{
	register int i;

	currbuff = startbuff;
	for (i = 0; i < inpool; i++)
	{
		rkio(out, currbuff->bb, currbuff->bn, WRITE | SSE, 256);
		currbuff++;
	}
	currbuff = startbuff;
	inpool = 0;
}

map(fmt, block)		/* translates optimised to equivalent unoptimised */
register fmt, block;
{
	if (! fmt || ! block) return(block);
	return(block > 2436 ? block - 2436 : block + 2435);
}

geti()
{
	if (inum++ < 16) node++;
	else
	{
		getblk(in, node = in_node, ++iblk);
		printf("%d\r", iblk);		/* keeps the user awake */
		inum = 1;
	}
}

puti()
{
	register *r, *w, q;

	w = &out_node[onum];
	r = node;
	q = 16;
	do *w++ = *r++; while (--q);
	if (++onum == 16)
	{
		putblk(out_node, oblk++);
		onum = 0;
	}
}

nxtblk()
{
	register block;

	do
	{
		block = (cyl * NTRACK + track) * BLKPERTRK + adr[sector];
		if (++sector >= BLKPERTRK)
		{
			sector = 0;
			if (++track >= NTRACK)
			{
				cyl++;
				track = 0;
			}
		}
	} while (block < 2 + superblk->s_isize);
	return(lastblock = block);
}

freelist()
{
	register rcyl, rsector, rtrack;
	extern int ldivr;
	unsigned block;

	rcyl = ldiv(0, superblk->s_fsize, NTRACK * BLKPERTRK);
	rtrack = ldivr / BLKPERTRK;
	rsector = ldivr % BLKPERTRK;	/* first unusable block */

	bfree(0);			/* end-of-list sentinel */

	while (rsector) bfree ((rcyl * NTRACK + rtrack) * BLKPERTRK + --rsector);
	for (;;)
	{
		if (rsector-- == 0)
		{
			rsector =+ BLKPERTRK;
			if (rtrack-- == 0)
			{
				rtrack =+ NTRACK;
				rcyl--;
			}
		}
		block = (rcyl * NTRACK + rtrack) * BLKPERTRK + adr[rsector];
		if (block == lastblock) return;
		if (block >= 2 + superblk->s_isize) bfree(block);
	}
}

bfree(b)
{
	register *r, *q, w;

	if (superblk->s_nfree == 100)
	{
		r = general;
		*r++ = superblk->s_nfree;
		q = superblk->s_free;
		w = 100;
		do *r++ = *q++; while (--w);
		putblk(general, b);
		superblk->s_nfree = 0;
	}
	superblk->s_free[superblk->s_nfree++] = b;
}

copyi()
{
	register *p, *pp, md;

	if (md = node->i_mode & IFMT)
	{
		if (md == IFDIR) copydir();
		puti();
		return;
	}

	if ((node->i_mode & ILARG) == 0)
	{
		for (p = node->i_addr; p < &node->i_addr[8]; p++)
			if (*p) getblk(in, grablock(*p = nxtblk()), *p);	/* stacking-dependent */
		puti();
		return;
	}

	for (p = node->i_addr; p < &node->i_addr[7]; p++)
		if (*p)
		{
			getblk(in, ind0, *p);
			*p = nxtblk();
			for (pp = ind0; pp < &ind0[256]; pp++)
				if (*pp) getblk(in, grablock(*pp = nxtblk()), *pp);
			putblk(ind0, *p);
		}
	if (node->i_addr[7])
	{
		getblk(in, ind0, node->i_addr[7]);
		node->i_addr[7] = nxtblk();
		for (p = ind0; p < &ind0[256]; p++)
			if (*p)
			{
				getblk(in, ind1, *p);
				*p = nxtblk();
				for (pp = ind1; pp < &ind1[256]; pp++)
					if (*pp) getblk(in, grablock(*pp = nxtblk()), *pp);
				putblk(ind1, *p);
			}
		putblk(ind0, node->i_addr[7]);
	}
	puti();
}

putdir(blk)
{
	register *to, *from, cntr;
	int count;

	to = upto;
	getblk(in, from = general, blk);
	for (count = 0; count < 32; count++)
	{
		if (! *from)
		{
			from =+ 8;
			continue;
		}
		if ((dirent & 037) == 0)
		{
			if (diraddr == 0 && dirent == 256)	/* convert to large file */
			{
				node->i_mode =| ILARG;
				*(diraddr = node->i_addr) = *ind1;
				for (cntr = 0; cntr < 7; cntr++)	/* at least 8 buffers have been guaranteed */
				{
					ind1[cntr] = ind1[cntr + 1];
					currbuff[cntr - 8].bn = currbuff[cntr - 7].bn;
				}
				currbuff[-1].bn = map(outform, ind1[7] = nxtblk());
			}
			else if (diraddr && (dirent & 017777) == 0)
			{
				putblk(ind1, *diraddr);
				*++diraddr = nxtblk();
				dirind = ind1;
			}
			to = grablock(*dirind++ = nxtblk());
		}
		for (cntr = 0; cntr < 8; cntr++) *to++ = *from++;
		dirent++;
	}
	upto = to;
}

copydir()
{
	register *p, *pp;

	dirent = diraddr = 0;
	dirind = ind1;
	if ((node->i_mode & ILARG) == 0)
	{
		for (p = node->i_addr; p < &node->i_addr[8]; p++)
			if (*p) putdir(*p);
	}
	else
	{
		if (nbuf - inpool < 8) purgepool();	/* guarantees 8 buffers - see `putdir' */
		node->i_mode =& ~ILARG;
		for (p = node->i_addr; p < &node->i_addr[7]; p++)
			if (*p)
			{
				getblk(in, ind0, *p);
				for (pp = ind0; pp < &ind0[256]; pp++)
					if (*pp) putdir(*pp);
			}
		if (diraddr) while (dirind < &ind1[256]) *dirind++ = 0;		/* clear rest of pointers */
	}
	node->i_size0 = (dirent >> 12) & 017;
	node->i_size1 = dirent << 4;
	if (diraddr == 0)
	{
		p = &node->i_addr[-1];
		for (pp = ind1; pp < dirind; *++p = *pp++);
	}
	for (pp = upto; pp < currbuff; *pp++ = 0);	/* clear rest of directory */
	if (diraddr) putblk(ind1, *(p = diraddr));
	while (p < &node->i_addr[7]) *++p = 0;
}

reldir()
{
	register i, j;

	if ((node->i_mode & ILARG) == 0)
	{
		for (i = 0; i < 8; i++) relx(node->i_addr[i]);
		return;
	}
	for (i = 0; i < 7; i++)
		if (node->i_addr[i])
		{
			getblk(out, ind0, node->i_addr[i]);
			for (j = 0; j < 256; j++) relx(ind0[j]);
		}
}

relx(b)
register b;
{
	register *p;
	register i;

	if (b == 0) return;
	getblk(out, p = grablock(b), b);
	i = 32;
	do
	{
		if (*p) *p = iindex[*p];
		p =+ 8;			/* 8 words per directory entry */
	} while (--i);
}

counti()
{
	register i, j, n;

	n = 0;
	for (i = 0; i < superblk->s_isize; i++)
	{
		getblk(in, in_node, 2+i);
		for (j = 0; j < 16; j++)
			if (in_node[j].i_mode & IALLOC) n++;
	}
	return((n + 15) / 16);
}

countfree()
{
	register n, m;

	n = superblk->s_nfree;
	if (n)
	{
		m = superblk->s_free[0];
		while (m)
		{
			getblk(in, general, m);
			n =+ general[0];
			m = general[1];
		}
	}
	return(n);
}

/********************************************************/


struct { unsigned u; };

initial()
{
	register j, *a, *d;
	extern char end;	/* identify the end of the bss  */

	bigbuf = (&end + 077) & 0177700;	/* start on 32w boundary */
	a = KISA0;
	d = KISD0;
	for (j = 0; j < 01600; j =+ 0200)
	{
		*a++ = j;
		*d++ = 077406;		/* 4kw r/w  */
	}
	*d = 077406;
	*a-- = 07600;		/* i/o page */
	SSR0->i++;		/* start mem-mngmnt */
	for (*a = (bigbuf >> 6); *a < 07600; (*a)++)
		for (d = 0140000; d.u < 0140100; *d++ = 0);	/* 32 w */
}

initiam()
{
	int register j;

	SSR0->i = 0;		/* stop mem-mngmnt */
	printf("\n\nMem =  %dkw\n", (j = KISA6->i) >> 5);
	iob = ((--j) - (bigbuf >> 6)) >> 3;
	if (iob > 256) iob = 256;	/* Max. disk i/o size */
	nio = BLKPERDSK / iob;
	szio = iob * 256;
	iov = BLKPERDSK - (nio * iob);
	szov = iov * 256;
	printf("%d big i/o's (each %d blocks)", nio, iob);
	if (iov) printf(" + one at %d blocks", iov);
	printf("\n\n");
}