/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION 1986,1987,1988
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */
/* $Header:format.c 12.0$ */
/* $ACIS:format.c 12.0$ */
/* $Source: /ibm/acis/usr/sys/standca/RCS/format.c,v $ */

#ifndef lint
static char *rcsid = "$Header:format.c 12.0$";
#endif

/*	format.c	6.3	83/09/23	*/


/* 
 * Standalone program to do media checking
 * and record bad block information on any 
 * disk with the appropriate driver and headers.
 * use IBM (AUSTIN) bad block table.
 */
#include "param.h"
#include "fs.h"
#include "inode.h"

#include "saio.h"
#include "sa.h"
#include "err.h"

#define nspt nsect
#define ntpc ntrak 
#define ncpd ncyl

#include "../machineio/hdconfig.h"

#ifdef ATR
#include "../ca_atr/pcif.h"		/* defined in ../pc_code/cbcb.h */
extern struct hdinfo *hdinfo[];		
#include <ctype.h>
#endif ATR

#define NBBTCPYS	1
#define BADPERBLOCK	64	       /* 64 8-byte entries per block */

#define dkbad (*dkbad_ptr)		/* now stored on stack */
struct hdbad  *dkbad_ptr;	       /* the drives bad sector maps */
#define hidtab (*hdhid_ptr)
struct hdhid  *hdhid_ptr;		/* the drives hidden defect table */
#define config (*config_ptr)		/* now stored on stack */
struct hdconfig *config_ptr;
#define	skew	config.conf_skew

int ignore_hid = IGNORE_HID;
#define Ignore_hid	(*(struct hidmap *)&ignore_hid)

int replace_block;		       /* start block number of replacement */

#define SAVERETRY	8	       /* number of times to read/write when preserving
				          data */
int saveretry = SAVERETRY;		/* number of times to retry it */
#define IOCTL(fd, what, arg) if (ioctl(fd, what, arg) < 0) \
	    stop_format("what failed");

/* 
 * Standalone program to do surface analysis
 * and record bad block information on any 
 * disk with the appropriate driver.
 */


int lasterrors;			       /* error count last time */
int pattern;			       /* Subscript for pat[] */
char *savebuff;			       /* buffer to save data */
int saveflag;			       /* if we are to save data */
int cyl_count;			       /* how often we write BB table */
int cyl_write;			       /* when to next write BB table */

/*
 * Purdue/EE severe burnin patterns.
 */

unsigned short ppat[] = {
	0xec6d, 0xf00f, 0031463,0070707,0133333,0155555,0161616,0143434,
	0107070,0016161,0034343,0044444,0022222,0111111,0125252, 052525,
	0125252,0125252,0125252,0125252,0125252,0125252,0125252,0125252,
	052525, 052525, 052525, 052525, 052525, 052525, 052525, 052525
};

static struct pattern {
	long pa_value;
	char *pa_name;
} pat[] = {
	{ 0xF00FF00F, "RH750 worst case" },
	{ 0xEC6DEC6D, "media worst case" },
	{ 0xA5A5A5A5, "alternate 1's and 0's" },
	{ 0x00000000, "zero disk" },
	{ 0xFFFFFFFF, "Severe burnin (1 to 32 passes)" },
};
#define countof(x) sizeof x/sizeof x[0]
#define NPATTERNS countof(pat)

#define DFLSECSIZE	512	       /* Default sector size */
#define DFLINTERLV	2	       /* Default interleave factor */
int interleavefactor = DFLINTERLV;
#define NPT (sizeof (ppat) / sizeof (short))
#define BLKNO(st, cyl, trk, sec) \
	    (((cyl) * (st)->nspt * (st)->ntpc) + ((trk) * (st)->nspt) + (sec))
#define	ON	1
#define	OFF	0

int npat;			       /* subscript for ppat[] */
int severe;			       /* running "severe" burnin */
int nbads;			       /* subscript for bads */
long *bads;			       /* Bad blocks accumulated */
int reformat = 0;		       /* reformat required to mark bad block(s) */
int wasreformatted = 0;		       /* reformat was required to mark bad block(s) */

union {
	u_short bufword[DFLSECSIZE >> 1];
	char buffer[DFLSECSIZE];
} interleave;

char	*itable;

char *malloc();
char *prompt();
int maxpass, lastcyl, startcyl;
int starterrors;
char clearscr[] = "\33K";	       /* to clear the screen */
char homescr[] = "\33H";	       /* to clear the screen */
int passes = 1;			       /* number of passes to make */
struct st st;
int fd;
char *index();
char device[16];		       /* name of device to format */
int tracksize;
char *bp;

#define BAD_MASK	0xffffff       /* mask for bad block number */
#define IGNORE_BAD	0xffffff	/* deleted bad block */

#ifndef SAUTIL
#undef main	/* debugging */
#endif

/* the N last sectors on each track are hidden - used as bad block
 * replacements.  this sort of formatting is assumed if a hidden
 * defect table is found. */
int hidden = 0;	/* how many hidden sectors/track, currently */
int make_hidden = 0;	/* how many hidden sectors/track we want */

int converting;

#define	NCFG	9
struct hdcfg cfg[NCFG] = {CFG_M40, CFG_R40, CFG_C70, CFG_E70, CFG_M70, CFG_R70,
			  CFG_E114, CFG_E310, CFG_H310};

/*
 * LOG_SECNO() is valid only while in format_track(), ie while
 * interleave.buffer reflects skewing of the track.  At other
 * times use log_secno().  Note that log_secno() always functions
 * as though there are no defects on the track, while LOG_SECNO
 * reflects any hiding of defects already done in interleave.buffer.
 */
#define	LOG_SECNO(sn)	(interleave.buffer[((sn) << 1) - 1])
#define	SECFLAG(sn)	(interleave.buffer[(sn)-1 << 1])

extern int screen_lines;

main(argc, argv)
	int argc;
	char **argv;
{
	register int block;
	int trk, cyl;
	struct hdbad dkbad_table;
	struct hdhid hdhid_table;
#define	MAXBADS	(MAXBADBLKS+MAXHIDBLKS)
	long bads_table[MAXBADS];	/* Bad blocks accumulated */
	struct hdconfig config_table;
	char itab[DFLSECSIZE];
	int kbd_flag = argc > 1 && (strcmp(argv[1], "-n") == 0);

	bads = bads_table;		/* use stack allocated space */
	dkbad_ptr = &dkbad_table;	/* use stack space */
	hdhid_ptr = &hdhid_table;
	config_ptr = &config_table;
	itable = itab;

	converting = argc > 0 && (strncmp(argv[0], "convert", 7) == 0);

again:
	if (make_hidden && !hidden)
		hide();
	if (fd) {
		close(fd);
		fd = 0;
	}

	printf("*** IBM 4.3 BSD Standalone Format $Revision: 12.0 $ ***\n");

	/*
	 * Get name of device to format
	 */

	fd = getdevice();

	/*
	 * Select test pattern and initialize variables
	 * for surface analysis.
	 */

	if (getpattern())
		goto again;

	/*
	 * Get device data and turn off status messages for this unit
	 */

loop:
	if (make_hidden && !hidden)
		hide();
	IOCTL(fd, SAIODEVDATA, &st);
	printf("Device data: #cylinders=%d, #tracks=%d, #sectors=%d\n\n",
	    st.ncpd, st.ntpc, st.nspt);
	if (converting) {
		if (st.adap != HD_ADAPTER_HESDI) {
			printf("Sorry, you must be using the new \"Extended ESDI\" adapter to generate an hd70e formatted disk.\n");
			goto again;
		}
		if (st.ncpd != 1 || st.ntpc != 2 || st.nspt != 17) {
			printf("Sorry, only R70 (also known as \"hd70r\") disks can be converted to E70 format.\n");
			goto again;
		}
		set_config("hd70e");
		if (!fmt_trk0() && !fmt_trk0()) {
			printf("Fatal error - STILL unable to format track zero!!!\n");
			exit_format(1);
		}
		write_config();
		make_hidden = cfg[iscfg("hd70r") - 1].cfg_lastsect -
			      cfg[iscfg("hd70e") - 1].cfg_lastsect;
		hidden = make_hidden;
		dkbad.hdcount = hidtab.count = 0;
	} else
		make_hidden = hidden = 0;
	nbads = 0;

	IOCTL(fd, SAIOECCMSGS, (char *)OFF);
	lseek(fd, CONFIG_BLOCK * st.nbps, 0);
	if (read(fd, &config, sizeof config) != sizeof config) {
		build_config("Unable to read configuration record"); goto loop;
	}
	if (config.conf_magic != CONFIG_MAGIC) {
		build_config("No configuration record found"); goto loop;
	}
	if (config.conf_lastsect != st.nspt) {
		build_config("Inconsistent configuration record"); goto loop;
	}
	if (config.conf_name[0] == '\0' && config.conf_name[1] == '\0' &&
	    config.conf_name[2] == 'h'  && config.conf_name[3] == 'd') {
		printf("Fixing up bad configuration record.\n");
		config.conf_adapter = config.conf_srn;
		bcopy(&config.conf_label, &config.conf_srn, sizeof config - ((char *)&config.conf_srn - (char *)&config));
		write_config(); 
		printf("Fixup complete.  Press control c if that is all you wanted to do.\n");
		goto again;
	}
	replace_block = (st.ncpd - 1) * st.ntpc * st.nspt - MAXBADBLKS;
	interleavefactor = config.conf_interleave;	/* initialize it */
	interlvinit(st.nspt, interleavefactor); /* init itable for log_secno */

	if (!converting && st.nspt >= HID_DEFECT_SECT + (sizeof hidtab)/st.nbps - 1) {
		lseek(fd, BLKNO(&st, 0, HID_DEFECT_HEAD, HID_DEFECT_SECT-1) * st.nbps, 0);
		if (read(fd, &hidtab, sizeof hidtab) != sizeof hidtab)
			printf("could not read hidden defect table\n");
		else {
			if (!HIDDEN_DEFECT_TABLE_OK(&hidtab))
				printf("No hidden defect table on disk.\n");
			else {
				register struct hidmap *mp;

				printf("Existing hidden defect table is %d entries long\n", hidtab.count);
				/*
				 * Currently there is no way for the disk to
				 * say that n sectors in each track are used as
				 * hidden defect replacement blocks.  We simply
				 * say that there is 1 hidden replacement per
				 * track if a valid "HIDDEN" table exists at the
				 * right location.
				 */
				make_hidden = 1;

				hidden = make_hidden;
				/*
				 * Convert to unhidden representation as we
				 * fill bads array.  Also compensate for
				 * interleave factor (sector #s in hdt are
				 * physical, not logical) & skew factor.
				 */
				for (mp = hidtab.hidmap;
				     mp < &hidtab.hidmap[hidtab.count]; mp++)
					(void)add_bads((mp->cyl*st.ntpc + mp->trk) * (st.nspt+hidden) + log_secno(mp->sec, mp->trk)-1);
			}
		}
	}
	hdt_init(nbads);

	if (!converting) {
		lseek(fd, BAD_BLOCK_START * st.nbps, 0);
		if (read(fd, &dkbad, sizeof dkbad) != sizeof dkbad)
			printf("could not read bad block table\n");
		else {
			if (!BAD_BLOCK_TABLE_OK(&dkbad))
				printf("No bad block table on disk\n");
			else {
				register struct hdmap	*hp;
	
				printf("Existing bad block table is %d entries long\n", dkbad.hdcount);
				/* convert to unhidden representation as we
				 * fill bads array */
				for (hp = dkbad.hdmap; hp < &dkbad.hdmap[dkbad.hdcount];
				     hp++)
					(void)add_bads(unhid_bno(hp->hdbad & BAD_MASK));
			}
		}
	}
	bbt_init(nbads - hidtab.count);

	{
		register int i;
		register int cnt = 0;
	
		for (i = 0; i < dkbad.hdcount; ++i)
			if (dkbad.hdmap[i].hdgood <= 0) {
				dkbad.hdmap[i].hdgood = replace_block + i;
				++cnt;
			}
		if (cnt) {
			printf("%d bad blocks forwarded to 0 or negative block #s.\n(Fixed after the following write succeeds.)\n", cnt);
			if (write_bbt("Writing Bad Block Table at block number %d ...\n"))
				printf("(Sorry.)\nRestarting...\n");
			else
				printf("The write succeeded!\nRestarting to proceed with format...\n");
			goto again;
		}
	}

	/*
	 * Allocate buffer to hold test pattern
	 */
	tracksize = st.nbps * (st.nspt+hidden);
	bp = malloc(tracksize);
	if (bp == 0)
		err("could not allocate track buffer");

	starterrors = nbads;	       /* initial error count */
	startcyl = 0;
	lastcyl = st.ncpd - 1;

	if (yes("Change defaults? [%c] ", 'n'))
		if (changedefaults(&st))
			goto loop;		/* start again */

	printf("\n\n");

	saveflag = yes("Attempt to preserve existing data (yes/no)? [%c] ",
			converting ? 'n' : 'y');
	if (converting && saveflag) {
		printf("Sorry, can't save anything when converting to E70!\n");
		saveflag = 0;
	}

	if (!yes("Start FORMAT (yes/no)? ", 0))
		exit_format(0);

	unhide();

	format_init();

	printf("\nFORMAT of %s ... [cylinders %d through %d]\nInterleave factor = %d, skew = %d\n",
	    device, startcyl, lastcyl, interleavefactor, skew);

	cyl_write = startcyl + cyl_count - 1; /* first one to write */
	if (!kbd_flag)
		printf("\nFormatting can be suspended (at end of a track) by pressing Shift-ENTER\n\n");


	for (block = startcyl * st.nspc; block < (lastcyl + 1) * st.nspc; block += st.nspt) {

		/*
		 * Compute current track
		 */

		trk = block%st.nspc / st.nspt;

		/*
		 * Compute cylinder number and announce progress 
		 */

		if (trk == 0) {
			cyl = block / st.nspc;
			printf("\rFORMATTING CYLINDER %d   ", cyl);
		}
		/*
		 * start format track
		 */
		if (!format_track(block, trk))
			break;
		/*
		 * check if we should update bad block table 
		 * do this at the last track of a cylinder.
		 */
		if (cyl_count && trk == st.ntpc - 1 && cyl == cyl_write) {
			if(nbads > lasterrors) {
				lasterrors = nbads; /* remember last error count */
				write_bbt(" * "); /* write bad block table */
				write_hdt("");	/* write hidden defect table */
			}
			cyl_write += cyl_count;	 /* next time to check it */

		} else if (cyl == 0 && trk == st.ntpc - 1) {
			write_bbt("");	/* quietly re-write bad block table */
			write_hdt("");	/* quietly re-write hidden defect tbl */
		}
		if (!kbd_flag && check_keyboard(block))
			break;
	}
	/*
	 * Surface analysis finished
	 */
	printf("\n\n");

	/*
	 * check to make sure that we haven't forwarded to a bad
	 * block. If so then we forward directly to the replacement
	 * block.
	 */
	checkbad();
	/*
	 * Announce to the world the number of bad blocks found.
	 */

	printbad();

	/*
	 * write the bad block table to the disk
	 */

	write_bbt("Writing Bad Block Table at block number %d\n");
	write_hdt("Writing Hidden Defect Table at block number %d\n\n");

	hide();
	printf("Format done...\n");
	close(fd);
}


format_init()
{
	starterrors = nbads;	       /* initial error count */
	/*
	 * Initialize interleave table
	 */
	interlvinit(st.nspt, interleavefactor);

	if (saveflag && savebuff == 0 && (savebuff = malloc(tracksize)) == 0) {
		printf("could not allocate %d byte track buffer\n", tracksize);
		saveflag = 0;
	}
}

format_track(block, trk)
register int block, trk;
{
	/* this routine assumes we are unhidden */
	register int pass, npasses;
	register int i;

	/*
	 * read existing data if we are operating in
	 * save mode 
	 */
	if (saveflag)
		readsave(block, savebuff);

	/*
	 * Format track
	 */
	lseek(fd, (long)block * st.nbps, 0);
	IOCTL(fd, SAIOFORMAT, (char *)0);
	interlvbuf(st.nbps, st.nspt, trk);
	write(fd, interleave.buffer, st.nbps);

	/*
	 * Flag known bad blocks first so they don't get lost
	 * (Provided that we have any known errors)
	 */
	if (starterrors)
		for (i = 0; i < st.nspt; ++i)
			if (inbads(block + i))
				flagsector(&st, block + i);
	/*
	 * Reset severe burnin pattern subscript
	 */

	if (severe)
		npat = 0;
	/*
	 * Start Surface analysis on track
	 */

	npasses = maxpass * passes;
	if (!saveflag && pat[pattern].pa_value != 0)
		npasses++;
	for (pass = 0; pass < npasses; pass++) {

		/*
		 * Intialize buffer with test pattern
		 */

		bufinit(bp, tracksize, pass);

		/* no retries while writing and verifying test pattern! */
		IOCTL(fd, SAIOSEVRE, (char *)0); /* speeds EESDI write */

		/*
		 * Write test pattern.
		 *
		 * If there is an error, retry the track one block at a
		 * time (per Austin engineering recommendation - hardware
		 * error status can come back on the wrong block sometimes
		 * on multi-block ops.)
		 */
		lseek(fd, (long)block * st.nbps, 0);
		if (write(fd, bp, tracksize) != tracksize)
			for (i = 0; i < st.nspt; i++ ) {
				lseek(fd, (long)(block + i) * st.nbps, 0);
				/*
				 * Ignore errors during write, all errors
				 * will be found during read verify
				 * performed below.
				 */
				(void)write(fd, bp + (i * st.nbps), st.nbps);
			}
		/*
		 * Read test pattern.
		 */
		lseek(fd, (long)block * st.nbps, 0);
		if (!(severe && npat == 0))
			IOCTL(fd, SAIOVERIFY, (char *)0);
		if (read(fd, bp, tracksize) != tracksize)
			for (i = 0; i < st.nspt; i++) {
				lseek(fd, (long)(block + i) * st.nbps, 0);
			/*
			 * use read verify except for one pass of severe burnin
			 * where we use a real read "just in case".
			 */
				if (!(severe && npat == 0))
					IOCTL(fd, SAIOVERIFY, (char *)0);
				if (read(fd, bp, st.nbps) != st.nbps)
					if (recorderror(block+i,&st) < 0 && pass>0) {
						IOCTL(fd, SAIONSEVRE,(char *)0);
						return(0); /* error return */
					}
			}
		IOCTL(fd, SAIONSEVRE, (char *)0);
		if (severe)
			if (++npat >= NPT)
				npat = 0;
	}
	/*
	 * If bad blocks were found in this track
	 * reformat it marking the bad blocks and
	 * reset interleave buffer.
	 */

	wasreformatted = reformat;
	if (reformat) {
		if (make_hidden)
			track_hide(block, make_hidden);
		lseek(fd, (long)block * st.nbps, 0);
		IOCTL(fd, SAIOFORMAT, (char *)0);
		write(fd, interleave.buffer, st.nbps);
		reformat = 0;
	}

	if (saveflag)
		writesave(block, savebuff);

	if (block == 0) { /* if just formatted 1st track we write config rec */
		lseek(fd, CONFIG_BLOCK * st.nbps, 0);
		write(fd, &config, sizeof config);
	}
	if (CONFIG_BLOCK2(st.nspt) >= block && CONFIG_BLOCK2(st.nspt) < block + st.nspt) {
		/* if just formatted track with secondary configuration block */
		lseek(fd, CONFIG_BLOCK2(st.nspt) * st.nbps, 0);
		write(fd, &config, sizeof config);
	}
	return(1);		/* normal return */
}


/*
 * write the bad block table to the disk 
 */
int
write_bbt(fmt)
	register char *fmt;
{
	register int block;

	/*
	 * Invalidate remaining entries in bad block table
	 */
	bbt_init(dkbad.hdcount);

	block = BAD_BLOCK_START;

	printf(fmt, block);

	/* place  BBT on disk */

	lseek(fd, st.nbps * block, 0);
	if (write(fd, &dkbad, sizeof dkbad) != sizeof dkbad) {
		printf("Write of bad block table failed!\n");
		return(1);
	}

#ifdef ATR
	hdinfo[getunit(device)]->flag |= HDINFO_BADBLOCK; /* force update */
#endif /* ATR */

	return(0);
}


/*
 * write the hidden defect table to the disk 
 */
int
write_hdt(fmt)
	register char	*fmt;
{
	register long	block;

	if (make_hidden) {
		hdt_init(hidtab.count);

		block =	HID_DEFECT_HEAD*st.nspt + HID_DEFECT_SECT-1;

		printf(fmt, block);

		lseek(fd, st.nbps * block, 0);
		if (write(fd, &hidtab, sizeof hidtab) != sizeof hidtab) {
			printf("Write of hidden defect table failed!\n");
			return(1);
		}
	}
	return(0);
}


printbad()
{
	register int i;
	register trk, cyl, sec;
	register int block;
	register int nspt;
	register int nspc;
	register int lines;
	char c;

	lines = 2;
	printf("\n\nA total of %d bad blocks were found\n\n", nbads);
	/*
	 * no matter whether or not we are hidden, printbad() only
	 * uses hidden numbering
	 */
	nspt = st.nspt - (make_hidden - hidden);
	nspc = nspt * st.ntpc;
	/*
	 * Print contents of Bad Block and Hidden Defect Tables if needed.
	 */
	if (nbads || dkbad.hdcount+hidtab.count) {
		if (starterrors != nbads) {
			lines += 2;
			printf("A total of %d of these were found on this format\n\n", nbads - starterrors);
		}
		if (dkbad.hdcount) {
			lines += 3;
			printf("Contents of Bad Block Table:\n");
			for (i = 0; i < 4; ++i)
				printf(" Cyl Trk Sct  Block ");
			printf("\n");
			for (i = 0; i < dkbad.hdcount; i++) {
				block = dkbad.hdmap[i].hdbad & BAD_MASK;
				if (dkbad.hdmap[i].hdbad == IGNORE_BAD) {
					block = cyl = trk = sec = -1;
				} else {
					cyl = block / nspc;
					trk = block%nspc / nspt;
					sec = block%nspt + 1;
				}
				switch (i & 3) {
				case 0:
					if (++lines >= screen_lines) {
						(void)pause();
						lines = 1;
					}	/* and fall thru... */
				case 1:
				case 2:
					c = ' ';
					break;
				case 3:
					c = '\n';
					break;
				}
				printf("%4d %3d %3d %6d%c",
					cyl, trk, sec, block, c);
			}
			if (i & 3)
				printf("\n");
			if (++lines >= screen_lines) {
				(void)pause();
				lines = 1;
			}
			putchar('\n');
		}
		if (hidtab.count) {
			if (++lines >= screen_lines) {
				(void)pause();
				lines = 1;
			}
			printf("Contents of Hidden Defect Table:\n");
			if (++lines >= screen_lines) {
				(void)pause();
				lines = 1;
			}
			for (i = 0; i < 4; ++i)
				printf(" Cyl Trk Sct  Block ");
			if (++lines >= screen_lines) {
				(void)pause();
				lines = 1;
			}
			printf("\n");
			for (i = 0; i < hidtab.count; i++) {
				if (((int *)hidtab.hidmap)[i] == IGNORE_HID) {
					block = cyl = trk = sec = -1;
				} else {
					cyl = hidtab.hidmap[i].cyl;
					trk = hidtab.hidmap[i].trk;
					sec = hidtab.hidmap[i].sec;
					sec = log_secno(sec, trk);
	    				block = cyl*nspc + trk*nspt + sec-1;
				}
				switch (i & 3) {
				case 0:
					if (++lines >= screen_lines) {
						(void)pause();
						lines = 1;
					}	/* and fall thru... */
				case 1:
				case 2:
					c = ' ';
					break;
				case 3:
					c = '\n';
					break;
				}
				printf("%4d %3d %3d %6d%c",
					cyl, trk, sec, block, c);
			}
			if (i & 3)
				printf("\n");
		}
	}
}


char *defaults[] = {
	 /* 1 */ "Change interleave [%d]",
	 /* 2 */ "Change first cylinder [%d]",
	 /* 3 */ "Change last cylinder [%d]",
	 /* 4 */ "Change number of entries in Bad and Hidden tables [%d]",
	 /* 5 */ "Print current Bad and Hidden tables",
	 /* 6 */ "Add new Bad Block Table entry",
	 /* 7 */ "Exit Format (with no more changes to disk)",
	 /* 8 */ "Change pattern",
	 /* 9 */ "Delete block from Bad and Hidden tables",
	 /* 10 */ "Convert block number to cyl, track, sector",
	 /* 11 */ "Convert cyl, track, sector to block number",
	 /* 12 */ "Number of passes [%d] ",
	 /* 13 */ "Write Bad and Hidden tables every [%d] cylinders",
	 /* 14 */ "Interactive addition of bad blocks",
	 /* 15 */ "Number of times to retry save read [%d]",
	 /* 16 */ "Change configuration record",
	0
};


changedefaults(st)
	register struct st *st;
{
	register int i;
	register char *p;
	register int *int_ptr;
	register int old;
	int *default_ints[] = {
/*	1		   2	      3		4	5  6 */
	&interleavefactor, &startcyl, &lastcyl, &nbads, 0, 0,
/*	7  8  9	10 11  12	13	   14  15	  16 */
	0, 0, 0, 0, 0, &passes, &cyl_count, 0, &saveretry, 0
	};

	for (;;) {
		printf("%s", clearscr);
	 	printf("%s", homescr);
		printf("Change Default Values (any other value starts FORMAT)\n");
		for (i = 0; p = defaults[i]; ++i) {
			printf("%2d   ", i + 1);
			int_ptr = default_ints[i];
			printf(p, int_ptr ? *int_ptr : 0);
			printf("       \n");
		}
		printf("Block numbers can be either 'nnn' or 'ccc.ttt.sss' (1234 or 0.0.1)\n");

		i = aton(prompt("Option: "));
		switch (i) {
		case 1:
			old = interleavefactor;
			if (nbads)
				printf("Warning: changing interleave factor will invalidate existing bad block table\n(Use option 4 to set the bad block table size to zero.)\n");
			printf("Note: ALL of a disk must be formatted to the SAME interleave.\n");
			if ((interleavefactor = aton(prompt("Interleave [%d] ",old))) <= 0)
				interleavefactor = old;
			if (interleavefactor >= st->nspt)
				interleavefactor = old;
			if (interleavefactor != old)
				printf("Don't forget to change interleave factor in the configuration record too!\n");
			break;
		case 2:
			startcyl = aton(prompt("start cylinder [0] "));
			if (startcyl < 0 || startcyl > lastcyl)
				printf("%d: invalid start cylinder\n", startcyl);
			break;
		case 3:
			lastcyl = getnumber("last cylinder [%d] (= end-of-disk) ", st->ncpd-1, 0, aton);
			if (lastcyl < startcyl || lastcyl >= st->ncpd)
				printf("%d: invalid last cylinder\n", lastcyl);
			break;
		case 4:
			i = aton(prompt("Set number of bad/hidden blocks [0=none] "));
			if (i < 0)
				printf("%d: cannot be less than zero\n", i);
			else if (i > nbads && i > starterrors) {
				if (yes("Are you sure? [%c] ", 'n'))
					nbads = i;
			} else if (i >= nbads) {
				nbads = i;
			} else
				while (nbads > i) {
					register int	hbn;

					nbads--;
					hbn = hid_bno(bads[nbads]);
					printf("Block number %d deleted from \"bads\" array", hbn);
					if (del_bbt(hbn))
						printf(" and from Bad Block Table");
					if (del_hdt(hbn))
						printf(" and from Hidden Defect Table");
					printf("\n");
				}
			break;
		case 5:
			printbad();
			break;
		case 6:
			i = getblock(prompt("New bad block "));
			if (check_block(i)) {
				if (add_bbt(i))
					(void)add_bads(unhid_bno(i));
			}
			break;
		case 7:
			exit_format(0);
			break;
		case 8:
			getpattern();
			break;
		case 9:
			i = getblock(prompt("Block to Delete "));
			if (del_bads(unhid_bno(i)))
				printf("Block number %d deleted from \"bads\" array.\n", i);
			else
				printf("Block number %d not found in \"bads\" array!\n", i);
			if (del_bbt(i))
				printf("Block number %d deleted from Bad Block Table.\n", i);
			else
				printf("Block number %d not found in Bad Block Table.\n", i);
			if (hidtab.count)
				if (del_hdt(i))
					printf("Block number %d deleted from Hidden Defect Table.\n", i);
				else
					printf("Block number %d not found in Hidden Defect Table.\n", i);
			break;

		case 10:
			{
				int sn, cn, tn;

				i = getblock(prompt("Block "));
				cn = i / st->nspc;
				tn = i%st->nspc / st->nspt;
				sn = i % st->nspt;
				printf("%d (0x%x): cyl=%d track=%d sector=%d     \n", i, i, cn, tn, sn + 1);
				break;
			}
		case 11:
			{
				int cyl = aton(prompt("Cylinder "));
				int trk = aton(prompt("Track "));
				int sec = aton(prompt("Sector "));

				i = (cyl * st->ntpc + trk) * st->nspt + sec - 1;
				printf("block = %d (0x%x)     \n", i, i);
				break;
			}
		case 12:
			i = getblock(prompt("Number of passes [1] "));
			if (i <= 0)
				i = 1;
			passes = i;
			break;

		case 13:
			cyl_count = aton(prompt("Write Bad Block Table Every ... Cylinders "));
			break;
		case 14:
			for (;;) {
				printf("%s\n\n", homescr);
				if ((i = getblock(prompt("Bad Block ")))==0)
					break;
				if (check_block(i)) {
					int savesave = saveflag;
					int sn, cn, tn;

					cn = i / st->nspc;
					tn = i%st->nspc / st->nspt;
					sn = i % st->nspt;
					printf("confirm block ");
					printf("%d (0x%x): cyl=%d track=%d sector=%d     ", i, i, cn, tn, sn + 1);
					if (!yes("yes/no? [%c] ", 'y'))
						continue;
					if (!add_bbt(i))
						break;
					i = unhid_bno(i);
					(void)add_bads(i);
					unhide();
					i = i/st->nspt * st->nspt;
					saveflag = 1;	/* always save data */
					format_init();
					sn = hid_bno(i);
					printf("\nFORMAT of %s ... [blocks %d ... %d]\n\n", device, sn, sn + st->nspt - make_hidden);
					
					format_track(i, tn);
					write_bbt("Writing bad block table at block number %d\n");
					write_hdt("Writing hidden defect table at block number %d\n\n");
					hide();
					reopen();
					saveflag = savesave;
				}
			}
			break;
		case 15:
			saveretry = aton(prompt("Number of times to retry save read "));
			if (saveretry > 8)
				printf("Warning: retry counts > 8 will suspend formatting upon non-recoverable errors\n");
			break;
		case 16:
			build_config((char *) 0);
			return(1);	/* insure new configuration picked up */
		default:
			return(0);	/* normal return */
		}
		prompt("Press enter to return to change menu ");
	}
}

/*
 * return true (1) if ok
 * otherwise false (0)
 */
check_block(i)
register int i;	/* better be a hidden logical number! */
{
	/* assumes that we aren't unhidden */
	if (i <= st.nspc)
		printf("%d: no bad blocks allowed in cyl 0\n", i);
	else if (i >= (lastcyl + 1) * st.nspc)
		printf("%d: this format ends with block %d\n", i, (lastcyl + 1)*st.nspc - 1);
	else if (i < startcyl * st.nspc)
		printf("%d: this format starts at block %d\n", i, startcyl * st.nspc);
	else if (inbbt(i))
		printf("block % already in Bad Block Table\n", i);
	else if (inhdt(cts(i)))
		printf("block % already in Hidden Defect Table\n", i);
	else
		return(1);	/* it is ok */
	return(0);		/* it not ok */
}

/*
 * Record an error, and if there's room, put
 * it in the bad block table. Also record
 * the bad block in the interleave buffer
 * for reformat of the track.
 *
 * If severe burnin store block in a list after making sure
 * we have not already found it on a prev pass.
 */

recorderror(bn, st)
	int bn;
	register struct st *st;
{
	if (inbads(bn))
		return (0);	       /* already flagged */

	if (errno != EECC && errno != EIO) {
		perror("\n\nRECORDERROR");
		printf("Fatal I/O error... Format terminated !!\n\n");
		exit_format(1);
	}

	if (!add_bbt(hid_bno(bn)) || !add_bads(bn)) {
		printf("Burnin terminating\n\n");
		return (-1);
	}

	flagsector(st, bn);

	return (0);
}


inbads(bn)
	register int bn;
{
	register i;

	/* bn and bads are all unhidden block numbers */
	for (i = 0; i < nbads; i++)
		if (bads[i] == bn)
			return (i + 1);	/* bn already flagged */
	return (0);		       /* not flagged yet */
}


/* record the bad block in interleave buffer for reformat */
flagsector(st, bn)
	register struct st *st;
	register int bn;	/* assumed unhidden */
{
	register int	sn = bn%st->nspt + 1;	/* assumes we are unhidden */

	if (sn = phy_secno(sn, bn%st->nspc / st->nspt))
		SECFLAG(sn) = FMT_BAD;
	else
		printf("help: lost sector %d (block=%d)\n", bn%st->nspt + 1, bn);
	reformat++;

}


phy_secno(sn, trk)
	register int	sn;
{
	register int	i, nspt;

	nspt = st.nspt - (make_hidden - hidden); /* get hidden sec/trk */
	if (sn <= nspt) {
		sn = 1 + (sn-1 + trk*skew)%nspt;
	}
	for (i = 0; i < nspt+make_hidden; i++) {
		if (itable[i] == sn)
			return(i + 1);
	}
	printf("help! phy_secno(%d) lost\n", sn);
	return(0);
}


/*
 * Prompt and verify a device name from the user.
 */

getdevice()
{
	register char *cp;
	int fd;
	extern int	hdquietinit;	/* see hd.c */
	extern int	hd_any_ucode;	/* see hd.c and kernel hd.c */

top:
	do {
		if (converting)
			hdquietinit = 1;
		cp = prompt("\nDevice to format? ");
		if (*cp == 'O' && *(cp+1) == '\0' ) {
			cp = prompt("\nDevice to format, Boss? ");
		}
		else
			hd_any_ucode = 0;	/* require new microcode */
		fd = open(cp, 2);
		if (fd < 0)
			perror("GETDEVICE");
	} while (fd < 0);

	strcpy(device, cp);
	printf("Formatting drive %c%c%d: ",
	    cp[0], cp[1], iob[fd - 3].i_unit);

	if (cp[0] != 'h' || cp[1] != 'd' || cp[5] != '2')
		printf("\nWarning: format only works if used with hd(?,2)\n");

	if (yes("verify (yes/no)? ", 0)) {
		IOCTL(fd, SAIODEVDATA, &st);
		return (fd);
	}
	close(fd);		       /* make unit usable again */
	goto top;
}


getpattern()
{
	register struct pattern *p;

	if (converting)
		printf("Warning: E70 conversion requires erasing the existing bad block table.\n\tSo you'd be wise to select the \"severe burnin\" option.\n\n");
	printf("Available test patterns are:\n\n");
	for (p = pat; p < pat + NPATTERNS; p++)
		printf("\t%d - (%x) %s\n", (p - pat) + 1,
		    p->pa_value & 0xffff, p->pa_name);
	pattern = getnumber("\nSelect Pattern (one of the above, other to restart) [%d]: ", NPATTERNS - 1, 1, aton);
	if (pattern < 0 || pattern >= NPATTERNS)
		return(1);
	severe = (pat[pattern].pa_value == -1);

	if (severe) {
		npat = 0;
		maxpass = getnumber("How many passes (from 1 to 32) [%d]?",
				    st.adap==HD_ADAPTER_HESDI ? 1 : 2, 0, aton);
		if (maxpass > NPT) {
			printf("You get %d passes (the maximum)\n", NPT);
			maxpass = NPT;
		} else if (maxpass < 1) {
			printf("You get 1 pass (the minimum)\n");
			maxpass = 1;
		}
	} else
		maxpass = 1;

	return (0);
}


/*
 * Initialize the buffer with the requested test pattern. 
 */

bufinit(bp, size, pass)
	register char *bp;
	int size;
	register int pass;
{
	register struct pattern *pptr;
	register char *bufend = bp + size;
	union {
		long word;
		char byte[4];
	} patt;

	if (pass == maxpass*passes)
		patt.word = 0;
	else if (severe) {
		patt.word = ppat[npat] | ((long)ppat[npat] << 16);
	} else {
		pptr = &pat[pattern];
		patt.word = pptr->pa_value;
	}
	do {
		*bp++ = patt.byte[0];
		*bp++ = patt.byte[1];
		*bp++ = patt.byte[2];
		*bp++ = patt.byte[3];
	} while (bp < bufend);

}


/*
 *
 * Interleave table initialization (itable)
 *
 */

interlvinit(nspt, interlv)
	register int	nspt, interlv;
{
	register int	i, j;

	nspt -= make_hidden-hidden;	/* gives us hidden spt */
	for (i = 0; i < nspt; i++)
		itable[i] = 0xff;

	for (j = 1, i = 0; j <= nspt; j++) {
		while (itable[i] != 0xff) {
			if (++i >= nspt)
				i = 0;
		}
		itable[i] = j;
		if ((i += interlv) >= nspt)
			i = 0;
	}
	for (i = nspt ; j <= nspt+make_hidden; j++, i++)
		itable[i] = j;
}


/*
 *
 * Interleave buffer initialization (interleave.buffer)
 *
 */

interlvbuf(nbps, nspt, trk)
	register int	nbps, nspt, trk;
{
	register int i, j;

	for (i = 0; i < (nbps >> 1); i++) {
		interleave.bufword[i] = 0;
	}

	/*
	 * Rotate our logical to physical sector mapping by
	 * skew*trk sectors.  Do NOT rotate sectors which may
	 * get used as hidden bad block replacement sectors.
	 */
	nspt += hidden-make_hidden; /* gives us logical ("hidden") spt */
	for (i = 0, j = skew*trk; i < nspt; i++, j++) {
		interleave.bufword[j % nspt] = itable[i];
	}

	nspt += make_hidden; /* gives us real ("unhidden") spt */
	for ( ; i < nspt; i++) {
		interleave.bufword[i] = itable[i];
	}
}


aton(str)
	register char *str;
{
	if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
		return (atox(str + 2));
	return (atoi(str));
}


/*
 * get a block number in the form of: number
 * or cyl.track.sector
 */
getblock(str)
	register char *str;
{
	register int bn;
	register char *p;

	bn = aton(str);
	if (p = index(str, '.')) {
		register int cyl = bn;
		register int track = aton(p + 1);
		register int sector = 1;

		if (p = index(p + 1, '.'))
			sector = aton(p + 1);
		bn = BLKNO(&st, cyl, track, sector - 1);
	}
	return (bn);
}


/*
 * read the current track into the "buffer" so that it can be
 * written back again.
 */
readsave(block, buffer)
	register char *buffer;
{
	register int length;
	register int i, j, l;

	/* we assume we are unhidden when called and that our block#
	 * arg is unhidden too.  We must become hidden for the read so
	 * that driver can do its usual bad block mapping.  (The drivers
	 * bad block map has only hidden block#s.)
	 */
	block = hid_bno(block);
	hide();
	length = st.nbps * st.nspt;
	lseek(fd, block * st.nbps, 0);
	l = read(fd, buffer, length);
	if (l != length)
/*
 * we had a read error on all/part of the track - try to read it 
 * block-by-block
 */
		for (i = 0; i < st.nspt; ++i) {
			for (j = 0; j < saveretry; ++j) {
				lseek(fd, (block + i) * st.nbps, 0);
				l = read(fd, buffer, st.nbps);
				if (l == st.nbps)
					break;
			}
			if (l != st.nbps) {
				printf("block %d: data could not be saved\n", block + i);
				if (saveretry > 8 && yes("Abort FORMAT (yes/no)? ", 0))
					exit_format(1);
				bzero(buffer, st.nbps);
			}
			buffer += st.nbps;
		}
	unhide();
}


writesave(block, buffer)
	register char *buffer;
{
	register int length;
	register int i, j, l;

	/* we assume we are unhidden when called and that our block#
	 * arg is unhidden too.
	 */
	block = hid_bno(block);
	hide();
	length = st.nbps * st.nspt;
	lseek(fd, block * st.nbps, 0);
	IOCTL(fd, SAIOSEVRE, (char *)0);	/* speeds EESDI writes */
	if (wasreformatted || write(fd, buffer, length) != length) {
/*
 * either we know that we will get an error (because a block has
 * been forwarded on this track) or
 * we had a write error on all/part of the track - try to write it 
 * block-by-block 
 */
		for (i = 0; i < st.nspt; ++i) {
			register int bn = newblock(block + i);

			IOCTL(fd, SAIOSEVRE, (char *)0);
			for (j = 0; j < saveretry; ++j) {
				lseek(fd, bn * st.nbps, 0);
				l = write(fd, buffer, st.nbps);
				if (l == st.nbps)
					break;
				IOCTL(fd, SAIONSEVRE, (char *)0);
			}
			if (l != st.nbps)
				printf("block %d: data could not be written\n", block + i);
			buffer += st.nbps;
		}
	}
	IOCTL(fd, SAIONSEVRE, (char *)0);
	unhide();
}


#ifndef SAUTIL
#define exit exit_format
#include "err.c"
#undef exit
#endif


/*
 * get the replacement block number for a given block 
 * if this block is marked as bad.
 * we work only with hidden block numbers
 */
newblock(block)
	register int block;
{
	register int b;

	if (b = inbbt(block))
		block = dkbad.hdmap[b - 1].hdgood;

	return (block);
}


extern int getchar_timeout;
extern char getchar_char;

check_keyboard(block)
{
	register int c;

	getchar_timeout = 1;
	getchar_char = 0x01;

	c = _getchar();
	switch (c) {
	case 0x01:
		break;
	case ' ':
		printf("%d", block);
		break;
	default:
		break;
	case '\n':
	case '\r':
		if (yes(" Stop Format? ", 0))
			return (1);
	}
	return (0);
}


/*
 * scan thru the bad block table and see if any blocks don't have
 * a replacement block; if not then provide one.
 * scan thru the bad block table and if we find any bad blocks that
 * are forwarded to bad blocks then change the original fowarding 
 * to the eventual location (this makes life easier for the disk driver
 * and also is faster).
 */
checkbad()
{
	register int i, k, count = 0;
	register int good, bad;

	for (i = 0; i < dkbad.hdcount; ++i)
		if (dkbad.hdmap[i].hdgood <= 0) {
			dkbad.hdmap[i].hdgood = replace_block + i;
			++count;
		}
	if (count) {
		printf("%d blocks had a 0 or negative replacement block; now forwarded.\n", count);
		count = 0;
	}

	for (i = 0; i < dkbad.hdcount; ++i) {
		good = dkbad.hdmap[i].hdgood;
		bad = dkbad.hdmap[i].hdbad;
		while (k = inbbt(good)) {
			--k;	       /* get subscript value */
			good = dkbad.hdmap[k].hdgood;
			printf("block %d remapped to %d (instead of %d which is bad)\n",
			    bad, good, dkbad.hdmap[i].hdgood);
			dkbad.hdmap[i].hdgood = good; /* replace original one */
			++count;
		}
	}
	if (count)
		printf("%d bad blocks found in bad block replacement region\n", count);
}

/*
 * close and then reopen the device 
 * this is so that the disk driver knows about the
 * new bad block table
 */
reopen()
{
	printf("reopening %s\n",device);
	close(fd);
	if ((fd = open(device,2)) < 0) {
		printf("could not reopen %s\n",device);
		exit(1);
	}
	IOCTL(fd, SAIODEVDATA, &st);
}


build_config(msg)
char *msg;
{
	register int i;
	char *result;

	if (msg) {
		printf("%s\n",msg);
#ifdef	ATR
		printf("Run fdisk on this partition before formatting.\n");
#else	/* ATR */
		if (!yes("Do you wish to build a new configuration record? ", 0))
#endif	/* ATR */
			exit_format(1);
		bzero(&config, sizeof config);
	}
	if (yes("Would you like to reset the entire configuration record to a standard\nconfiguration for your disk type?", 0)) {
		char		name[8];
		register	int j;

		printf("I know about the following disk types:\n");
		for (i = 0; i < NCFG; i++) {
			strncpy(name, cfg[i].cfg_name + 1, sizeof name);
			name[sizeof name - 1] = '\0';
			j = strlen(name) - 1;	/* index of last character */
			name[0] = name[j] - 'a' + 'A';
			name[j] = '\0';
			printf("\t%2d   %s(%s)\n", i+1, cfg[i].cfg_name, name);
		}
		printf("Enter your disk type number, or anything else to avoid resetting the entire\nconfig record\n");
		i = aton(prompt("Disk type number: "));
		i--;
		if (i >= 0 && i < NCFG) {
			set_config(cfg[i].cfg_name);
			printf("Press enter through the following prompts to verify what each field was set to.\n");
		} else {
			printf("OK, config record not reset.\n");
		}
	}
	printf("note: values in [...] are current values\n");
	printf("note: \"X\" represents the value\n");
#define GETDEC(value,msg,fudge) value = getnumber(msg,value,fudge,aton)
#define GETHEX(value,msg,fudge) value = getnumber(msg,value,fudge,atox)
	GETDEC(config.conf_sectsize, "Sector size = 128 << X (2=512 bytes) [%d] ",0);
	GETDEC(config.conf_interleave,"Interleave factor [%d] ",0);
	GETDEC(config.conf_maxcyl,"Number of cylinders [%d] ",1);
	GETDEC(config.conf_lasttrack,"Number of heads (tracks per cylinder) [%d] ",1);
	GETDEC(config.conf_lastsect,"Number of sectors (per track) [%d] ",0);
	GETDEC(config.conf_precomp,"Precompensation_cylinder/4 = X (255=no precomp) [%d] ",0);
	if (yes("Change seek curve values? [%c] ", 'n'))
		for (i=0; i<5; ++i) {
			GETDEC(config.conf_seek[i].seek_x,"x (cyl) [%d] ",0);
			GETDEC(config.conf_seek[i].seek_y,"y (ms) [%d] ",0);
			GETDEC(config.conf_seek[i].seek_slope,"slope (ms/cyl) [%d] ",0);
		}
	GETHEX(config.conf_size,"Size code (usually 70 or 40) [%x] ",0);
	GETHEX(config.conf_mfr,"Manufacturer code (usually 1, 2, or 3) [%x] ",0);
	GETDEC(config.conf_adapter,"Adapter type (0=PC/AT, 1=ESDI, 2=Enhanced ESDI) [%d] ",0);
	if (*(result = prompt("disk name (hd40r, hd70e, hd70r, etc) [%s] ", config.conf_name)))
		strncpy(config.conf_name, result, sizeof config.conf_name);
	GETDEC(config.conf_skew,"Skew (logical rotation of track) [%d] ",0);
	config.conf_magic = CONFIG_MAGIC;	/* set magic number */
	config.conf_sectorcount = (config.conf_maxcyl) *
		(config.conf_lasttrack+1) * config.conf_lastsect;
	config.conf_lastcyl = config.conf_maxcyl - 1;
	printf("total %d sectors\n",config.conf_sectorcount);
	write_config();
}


write_config()
{
	for(;;) {
		printf("writing new configuration record\n");
		lseek(fd, CONFIG_BLOCK * st.nbps, 0);
		if (write(fd, &config, sizeof config) == sizeof config)
			break;
		if (!yes("Unable to write configuration record - continue?", 0))
			exit_format(1);
		if (yes("Format track 0 of cylinder 0?", 0) &&
		    yes("This is an extreme measure!  It will destroy the boot block, configuration record, bad block table, and quite possibly all data on the disk.  Are you sure you want to try this???", 0))
			(void)fmt_trk0();
	}
	reopen();	/* tell disk driver about new configuration record */
	printf("New device data: #cylinders=%d, #tracks=%d, #sectors=%d\n",
			st.ncpd, st.ntpc, st.nspt);
}


fmt_trk0()
{
	register int	i;

	interlvinit(config.conf_lastsect+1, config.conf_interleave);
	interlvbuf(128 << config.conf_sectsize, config.conf_lastsect+1, 0);
	lseek(fd, 0, 0);
	IOCTL(fd, SAIOFORMAT, (char *)0);
	i = write(fd, interleave.buffer, 128 << config.conf_sectsize);
	if (i != 128 << config.conf_sectsize) {
		if (yes("Unable to format track 0 of cylinder 0 - exit? [%c] ", 'y'))
			exit_format(1);
		else
			return(0);
	}
	return(1);
}

/*
 * get a numeric response:
 * 1.	provide the current value as a default
 * 2.	if user doesn't provide a value then use the default given
 * 3.	value is used to change between internal format and external format.
 * 4.	cfn will be aton or atox.
 */
getnumber(msg,value,fudge, cfn)
char *msg;
int (*cfn)();
{
	char *result = prompt(msg,value+fudge);

	if (*result)
		value = cfn(result) - fudge;
	return(value);
}


unhide()
{
	if (make_hidden) {
		if (hidden == 0)
			printf("unhide: already unhidden!\n");
		else {
			st.nspt += hidden;
			st.nspc += hidden * st.ntpc;
			IOCTL(fd, SAIOWDEVDATA, &st);
			hidden = 0;
		}
	}
}


hide()
{
	if (make_hidden) {
		if (hidden != 0)
			printf("hide: already hidden!\n");
		else {
			hidden = make_hidden;
			st.nspt -= hidden;
			st.nspc -= hidden * st.ntpc;
			IOCTL(fd, SAIOWDEVDATA, &st);
		}
	}
}


exit_format(n)
	register int n;
{
	if (make_hidden && !hidden)
		hide();
	exit(n);
}


stop_format(s)
	register char *s;
{
	if (make_hidden && !hidden)
		hide();
	_stop(s);
}


track_hide(tbn, hidemax)
	register long	tbn;		/* Track Block Number (unhidden) */
	register int	hidemax;	/* max # blocks we could hide */
{
	register int	nhide = hidemax;
	register int	sn, rsn;	/* [Replacement] Sector Number */
	register int	first_hid;	/* lowest bad sec to be hidden */
	register int	j, k;		/* scratch */
	/*
	 * Check for possible bad blocks among those used as hidden replacement
	 * blocks.  Such are unusable and, since unused, need not be remembered
	 * in bad block table.
	 */
	for (sn = st.nspt; sn > st.nspt - hidemax; sn--)
		if (SECFLAG(sn)) {	/* bad? */
			nhide--;	/* can't use this one! */
			/* delete it from regular bad block table */
			(void)del_bbt(hid_bno(tbn + LOG_SECNO(sn)-1));
		}

	/* Ensure we don't overflow the hidden defect table. */
	if (nhide > MAXHIDBLKS - hidtab.count)
		nhide = MAXHIDBLKS - hidtab.count;
	/*
	 * Now nhide is # we could hide on this track.  We position ourselves
	 * so first_hid is the lowest (physical) sector we will hide and make
	 * nhide the # we *will* hide.
	 */
	first_hid = 1 + st.nspt - hidemax;
	for (j = nhide; j > 0 && sn > 0; sn--)
		if (SECFLAG(sn)) {	/* bad? */
			j--;
			first_hid = sn;
		}
	nhide -= j;

	for (rsn = 1 + st.nspt - hidemax, sn = first_hid; nhide > 0; sn++)
		if (SECFLAG(sn)) {	/* bad? */
			nhide--;
			SECFLAG(sn) |= FMT_HIDDEN;	/* for adapter */
			j = hid_bno(tbn + LOG_SECNO(sn)-1);
			(void)del_bbt(j);
			(void)add_hdt(j);
			/* find next valid replacement block */
			while (SECFLAG(rsn))
				rsn++;
			/*
			 * Now we must rotate right one place, from the last
			 * regular block down thru sn, filling sn from rsn, and
			 * putting the last regular block into rsn.
			 */
			k = LOG_SECNO(rsn);
			for (j = st.nspt - hidemax, LOG_SECNO(rsn) = LOG_SECNO(j); j > sn; j--)
				LOG_SECNO(j) = LOG_SECNO(j - 1);
			LOG_SECNO(sn) = k;
		}

	/*
	 * Remaining bad blocks now.  Make sure they're not in hidden table
	 * and are in the bad block table.
	 */
	for (sn = 1; sn < first_hid; sn++)
		if (SECFLAG(sn)) {
			j = hid_bno(tbn + LOG_SECNO(sn)-1);
			(void)del_hdt(j);
			(void)add_bbt(j);
		}
}


int
del_bbt(bn)
	register long	bn;
{
	register int	j;

	if (j = inbbt(bn)) {
		dkbad.hdmap[j - 1].hdbad = IGNORE_BAD;
		bbt_compress();
	}
	return(j);
}


int
add_hdt(bn)
	register long	bn;	/* better be a hidden logical bno! */
{
	register int	rv;
	int		search = cts(bn);

	if ((rv = inhdt(search)) == 0) {
		if ((rv = inhdt(IGNORE_HID)) == 0) {
			if (hidtab.count < MAXHIDBLKS) {
				hidtab.count++;
				rv = hidtab.count;
			} else
				printf("Hidden defect table overflowed!\n");
		}
		if (rv != 0)
			hidtab.hidmap[rv - 1] = *(struct hidmap *)&search;
	}
	return(rv);
}


int
inhdt(cts)
	register int	cts;	/* Cylinder/Track/Sector */
{
	register int	i;

	for (i = 0; i < hidtab.count; i++)
		if (((int *)hidtab.hidmap)[i] == cts)
			return(i + 1);	/* already hidden */
	return(0);	/* not hidden */
}


int
del_hdt(bn)
	register long	bn;
{
	register int	j;

	if (j = inhdt(cts(bn))) {
		hidtab.hidmap[j - 1] = Ignore_hid;
		hdt_compress();
	}
	return(j);
}


int
cts(bn)
	register long	bn;	/* assumed hidden# */
{
	register int	rv;
	register int	nspc;
	register int	nspt = st.nspt;
	register int	trk;

	nspt -= make_hidden-hidden;	/* generate "hidden" numbering */
	nspc = st.ntpc * nspt;

	rv = bn%nspt + 1;		/* = logical sector # */
	trk = bn%nspc / nspt;
	rv = phy_secno(rv, trk);	/* = physical sector # */
	rv |= trk << 8;			/* |= track << 8 */
	rv |= bn/nspc << 16;		/* |= cylinder << 16 */

	return(rv);
}


bbt_init(from)
	register int	from;
{
	bcopy("DEFECT", dkbad.hddefect, 6); /* fill in magic word */
	dkbad.hdcount = from;
	for ( ; from < MAXBADBLKS; ++from) {
		dkbad.hdmap[from].hdbad = 0;
		dkbad.hdmap[from].hdgood = 0;
	}
}


hdt_init(from)
	register int	from;
{
	bcopy("HIDDEN", hidtab.hidden, 6);	/* fill in magic word */
	hidtab.count = from;
	for ( ; from < MAXHIDBLKS; ++from) {
		hidtab.hidmap[from].cyl = 0;
		hidtab.hidmap[from].trk = 0;
		hidtab.hidmap[from].sec = 0;
	}
}


int
add_bbt(bn)
	register long	bn;	/* better be a hidden bno! */
{
	register int	rv;

	if ((rv = inbbt(bn)) == 0) {
		if ((rv = inbbt(IGNORE_BAD)) == 0) {
			if (dkbad.hdcount < MAXBADBLKS) {
				dkbad.hdcount++;
				rv = dkbad.hdcount;
			} else
				printf("Bad Block Table overflowed!\n");
		}
		if (rv != 0) {
			dkbad.hdmap[rv - 1].hdbad = bn;
			dkbad.hdmap[rv - 1].hdgood = replace_block + rv - 1;
		}
	}
	return(rv);
}


int
inbbt(bn)
	register int	bn;
{
	register int	i;

	for (i = 0; i < dkbad.hdcount; i++)
		if (dkbad.hdmap[i].hdbad == bn)
			return(i + 1);
	return(0);
}


hdt_compress()
{
	register int	j;

	for (j = hidtab.count - 1; j >= 0; j--)
		if (((int *)hidtab.hidmap)[j] != IGNORE_HID)
			break;
	hidtab.count = j + 1;
}


bbt_compress()
{
	register int	j;

	for (j = dkbad.hdcount - 1; j >= 0; j--)
		if (dkbad.hdmap[j].hdbad != IGNORE_BAD)
			break;
	dkbad.hdcount = j + 1;
}


int
del_bads(bn)
	register long	bn;
{
	register int	rv = inbads(bn);

	if (rv) {
		register int	j;

		for (j = rv; j < nbads; j++)
			bads[j - 1] = bads[j];
		nbads--;
	}
	return(rv);
}


int
add_bads(bn)
	register long	bn;	/* better be an unhidden bno */
{
	if (nbads >= MAXBADS) {
		printf("bads table overflowed!\n");
		return(0);
	}
	bads[nbads++] = bn;
	return(nbads);
}


set_config(s)
	register char	*s;
{
	register int	i = iscfg(s);
	
	if (i--) {
		register int	j;

		printf("Initializing configuration record for a \"%s\" disk.\n", s);
		bzero(&config, sizeof config);
		config.conf_magic = CONFIG_MAGIC;
		config.conf_sectorcount = cfg[i].cfg_maxcyl * (cfg[i].cfg_lasttrack+1) * cfg[i].cfg_lastsect;
		config.conf_landing = -1;
		config.conf_interleave = cfg[i].cfg_interleave;
		config.conf_sectsize = 2;
		config.conf_lastcyl = cfg[i].cfg_maxcyl - 1;
		config.conf_lasttrack = cfg[i].cfg_lasttrack;
		config.conf_lastsect = cfg[i].cfg_lastsect;
		config.conf_precomp = cfg[i].cfg_precomp;
		config.conf_maxcyl = cfg[i].cfg_maxcyl;
		config.conf_end_of_life = cfg[i].cfg_end_of_life;
		for (j = 0; j < 5; j++)
			config.conf_seek[j] = cfg[i].cfg_seek[j];
		config.conf_size = cfg[i].cfg_size;
		config.conf_mfr = cfg[i].cfg_mfr;
		config.conf_adapter = cfg[i].cfg_adapter;
		config.conf_srn = cfg[i].cfg_srn;
		config.conf_label = s[4] - 'a' + 'A';
		config.conf_skew = cfg[i].cfg_skew;
		config.conf_hidmax = cfg[i].cfg_hidmax;
		strncpy(config.conf_name, s, sizeof config.conf_name);
	} else
		printf("set_config(whatisthis?!!)\n");
}


int
iscfg(s)
	register char	*s;
{
	register int	i;

	for (i = 0; i < NCFG; i++)
		if (strcmp(s, cfg[i].cfg_name) == 0)
			return(i + 1);
	return(0);
}


int
hid_bno(bn)
	register int	bn;
{
	register int	cyl, trk, sec;
	register int	nspt, nspc;
	
	/* use unhidden spt to break up input bn */

	nspt = st.nspt + hidden;
	nspc = nspt * st.ntpc;
	cyl = bn / nspc;
	trk = bn%nspc / st.nspt;
	sec = bn % nspt;

	/* now return hidden form */

	nspt -= make_hidden;
	nspc -= make_hidden * st.ntpc;
	return(cyl*nspc + trk*nspt + sec);
}

int
unhid_bno(bn)
	register int	bn;
{
	register int	cyl, trk, sec;
	register int	nspt, nspc;

	/* use hidden spt to break up input bn */

	nspt = st.nspt - (make_hidden - hidden);
	nspc = nspt * st.ntpc;
	cyl = bn / nspc;
	trk = bn%nspc / st.nspt;
	sec = bn % nspt;

	/* now return unhidden form */

	nspt += make_hidden;
	nspc += make_hidden * st.ntpc;
	return(cyl*nspc + trk*nspt + sec);
}

#ifndef STANDALONE

pause()
{
	register int c;

	printf("<HOLDING>");
	c = getchar();
	printf("\r         \r");
	return (c);
}


#endif

int
log_secno(sec, trk)
	register int	sec;	/* a physical sector number */
{
	register int	nspt,	/* # sec per trk */
			lsec;	/* logical sector # to be returned */

	nspt = st.nspt - (make_hidden - hidden);	/* get hidden spt */
	if (sec > nspt)
		return(sec);
	lsec = itable[sec - 1];		/* compensate for interleave factor */
	return(1 + (lsec-1 + trk*(nspt - skew))%nspt);	/* & for skew factor */
}

#undef config		/* for when we're part of sautil */
#undef nspt
#undef ntpc
#undef ncpd
