/*

BBFormat.c

experimental disk formatting - DAV 1May91

Manx Aztec C V5

*/

#define BADRAWREAD 1 /* Still can't RAWREAD WORDSYNC, use my code... */

#include <exec/types.h>
#include <exec/memory.h>
#include <devices/trackdisk.h>

#define __NO_PRAGMAS
#include <functions.h>
#include <stdio.h>

typedef unsigned char byte;
typedef unsigned short word;
typedef unsigned long ulong;


extern int Enable_Abort;

#define TDREQ ((struct IOExtTD *)TDReq)

 /* Hard coded disk data removed from 2.0 headers */

#define NUMCYLS		80	/* Number of cylinders. */
#define NUMHEADS	2	/* Number of heads. */
#define NUMTRACKS	(NUMCYLS * NUMHEADS)	/* Number of tracks. */

/* DOS block types */
#define T_SHORT		2	/* Primary type. */
#define ST_ROOT		1	/* Secondary type. */
#define ST_FILE		-3

byte *DiskTrack, *CheckTrack;
long TrackSize;
int Track;
int LowTrack;
word nsides;
struct IORequest *TDReq;
UBYTE *Device;

char *FromName,*ToName;
FILE *FromFile,*ToFile;

char *drivename = "DFx:";
char *DiskName ;			/* default name */
char trackerror[NUMTRACKS]; 		/* 0 for ok, else invalid */
char blockerror[NUMTRACKS*NUMSECS]; 	/* same, but 1 per block */

int DebugFlag = 0;
int HideFlag = 0;
int Quick = 0;
int BitOnly = 0;
int RawFlag = 0;

 /* This is the typical form of a disk bitmap (well, this
  * is how it would look like in 'C').
  */

struct DosBitmap {
	ULONG CheckSum;		/* Block checksum. */
	ULONG BitmapData[55];	/* 55 × 32 Bits = 1760 blocks. */
	ULONG WhoKnows0[72];	/* 72 unused bytes (to fit into 512 bytes). */
};

 /* This structure describes the root directory of a disk. */

struct DosDirectory {
	ULONG PrimaryType;	/* Primary type. */
	ULONG HeaderKey;	/* Header key, always null. */
	ULONG WhoKnows0;
	ULONG HashTableSize;	/* Size of hash table (128 - 56 = 72). */
	ULONG WhoKnows1;
	ULONG CheckSum;		/* Block checksum. */
	ULONG HashTable[72];	/* Hash table (filled in later). */
	ULONG BitmapFlag;	/* DOSTRUE if bitmap is valid. */
	ULONG BitmapPointers[25];	/* Pointers to bitmap blocks. */
	ULONG BitmapExtension;	/* Pointer to bitmap extra information. */
	struct DateStamp LastRootChange;/* Time and date of last change in root directory. */
	char DiskName[32];	/* Disk name as a BCPL string (max. 31 characters). */
	ULONG WhoKnows2[2];
	struct DateStamp LastDiskChange;	/* Time and date of last change on disk. */
	struct DateStamp CreationDate;	/* Time and date of disk creation. */
	ULONG NextHash;		/* Next entry in hash chain (userdir only). */
	ULONG ParentDir;	/* Pointer to block with parent dir (userdir only). */
	ULONG WhoKnows3;
	ULONG SecondaryType;	/* Secondary type */
};

/*
 * A DOS file header, also used for a file extension block
 */

struct DosFileHead {
	ULONG PrimaryType;	/* Primary type. */
	ULONG OwnKey;
	ULONG HighSeq;
	ULONG DataSize;
	ULONG FirstData;
	ULONG CheckSum;
	ULONG DataBlk[72];
	ULONG Spare[2];
	ULONG Protect;
	ULONG ByteSize;
	char  Comment[92];
	struct DateStamp CreationDate;
	char FileName[64];
	ULONG NextHash;
	ULONG ParentDir;
	ULONG Extension;
	ULONG SecondaryType;	/* Secondary type */
};


/* ConvertString():
 *
 *	Turn a 'C' string into a BCPL string.
 */

VOID
ConvertString(char *ToString,char *FromString)
{
	*ToString++ = strlen(FromString);

	while(*FromString)
		*ToString++ = *FromString++;
}

 /* GetBlockSum():
  *
  *	Calculate the checksum for a DOS disk block.
  *	Sum of all lonwords must be 0
  */

ULONG
GetBlockSum(ULONG * Array)
{
	ULONG Sum = 0;
	UBYTE i = 128;

	while (i--)
		Sum += Array[i];

	return (Sum);
}


 /* SetBitmapBlock():
  *
  *	Marks a block in the disk bitmap (bit by bit).
  *	A standard bitmap consists of fifty-five longwords
  *	in which each bit represents a disk block. If a
  *	bit is set, a block is unused. A cleared bit indicates
  *	an allocated block.
  */

VOID
SetBitmapBlock(ULONG * BitmapData, SHORT Block, BYTE SetFlag)
{
	if (SetFlag)
		BitmapData[Block / 32] &= ~(1 << (Block % 32));
	else
		BitmapData[Block / 32] |= (1 << (Block % 32));
}

/*
 * Mark one track as bad
 */

MarkBad(ULONG *block, int track)
{
	int i;
	int sec = track * NUMSECS - 2;

	for(i = 0; i<NUMSECS; ++i,++sec) {
		SetBitmapBlock(block,sec,TRUE);
	}
}

/*
 * return the next bad block from the list
 */

NextBadBlk(int i)
{
	if(i > -2) {
		while(++i < (NUMTRACKS*NUMSECS)) {
			if(blockerror[i]) {
				return i;
			}
		}
	}
	return -2;
}

 /* ClearBlock():
  *
  *	Set the contents of disk block to zero. It takes less
  *	time to clear the longwords contained in a disk track
  *	than to clear the single bytes.
  */

VOID
ClearBlock(ULONG * Block)
{
	UBYTE Offset = 128;

	while (Offset--)
		*Block++ = 0;
}

/* Format Data, Standard */

BaseFmt(byte *track, int FFS)
{
	/* Make em all the same, not the Rokicki 'clever' way */
	long *p,cs,i;
	p = (long *)track;
	cs = (FFS ? ID_FFS_DISK : ID_DOS_DISK);
	
	for (i=0; i<TrackSize/4; i++)
		*p++ = cs;
}

/* Fix up the root cylinder */

FixRoot(byte * RootTrack,int FFS)
{
	struct DosDirectory *RootBlock;
	struct DosBitmap *DosBitmap;
	struct DosFileHead *dfh,*dfe;
	int i,numbadblk=0,blk,dfenum;

	/* Tie them to the buffer. */

	RootBlock = (struct DosDirectory *) RootTrack;
	DosBitmap = (struct DosBitmap *) ((ULONG) RootTrack + TD_SECTOR);

	/* Clear the root block. */

	ClearBlock((ULONG *) RootBlock);

	/* Initialize the defaults. */

	RootBlock->PrimaryType = T_SHORT;
	RootBlock->SecondaryType = ST_ROOT;
	RootBlock->HashTableSize = 128 - 56;
	RootBlock->BitmapFlag = DOSTRUE;
	RootBlock->BitmapPointers[0] = (NUMTRACKS / 2) * NUMSECS + 1;

	/* Set the date stamps. */

	DateStamp(&RootBlock->LastRootChange);
	DateStamp(&RootBlock->LastDiskChange);
	DateStamp(&RootBlock->CreationDate);

	/* Copy the name string over. */

	ConvertString(RootBlock->DiskName, DiskName);


	/* Clear the disk bitmap. */

	ClearBlock((ULONG *) DosBitmap);

	/* Mark all bitmap blocks as unused. */

	for (i = 0; i < 55; i++)
		DosBitmap->BitmapData[i] = ~0L;

	/* Mark two blocks as used (880 -> Root block, 881 -> Bitmap).
	 * Note that the first two blocks by default are reserved
	 * and, not being part of the bitmap itself, have to
	 * be skipped.
	 */

	SetBitmapBlock(DosBitmap->BitmapData, 880 - 2, TRUE);
	SetBitmapBlock(DosBitmap->BitmapData, 881 - 2, TRUE);

	/* block out bad tracks */

	for(i=0; i<NUMTRACKS; ++i) {
		if(trackerror[i]) {
			int j;
			for(j=0;j<NUMSECS;++j) {
				blockerror[j+i*NUMSECS] = 1;
			}
			numbadblk += NUMSECS;
			MarkBad(DosBitmap->BitmapData,i);
		}
	}

	/* do From/To files */

	if(FromName) {
		int i;
		if(FromFile = fopen(FromName,"r")) {
			while(fscanf(FromFile," %d",&i) == 1) {
				if(((unsigned)i) < NUMTRACKS*NUMSECS) {
					if(!blockerror[i]) {
						++numbadblk;
						blockerror[i] = 2;
						SetBitmapBlock(DosBitmap->BitmapData,i-2,TRUE);
					}
				}
			}
			fclose(FromFile);
		} else
			printf("Cannot open %s\n",FromName);
	}

	if(ToName && numbadblk) {
		int i,j,b,flag;

		if(ToFile = fopen(ToName,"w")) {
			for(i=0; i<NUMTRACKS; ++i) {
				for(j=0,flag=0;j<NUMSECS;++j) {
					b = j+i*NUMSECS;
					if(blockerror[b]) {
						++flag;
						fprintf(ToFile," %4d",b);
					}
				}
				if(flag)
					fputc('\n',ToFile);
			}
			fclose(ToFile);
		} else
			printf("Cannot open %s\n",ToName);
	}
			
	/* try blockout by file */

	if(numbadblk && !BitOnly) {
		int datablk;

		dfh = (struct DosFileHead *) ((ULONG) RootTrack + 2*TD_SECTOR);

		ClearBlock((ULONG *)dfh);

		dfh->PrimaryType = T_SHORT;
		dfh->OwnKey = 882;
		dfh->HighSeq = numbadblk;
		dfh->DataSize = numbadblk > 72 ? 72 : numbadblk;
		numbadblk -= dfh->DataSize;

		dfh->FirstData = blk = NextBadBlk(-1);

		for(datablk=71;(datablk>=0)&&(blk >= 0);--datablk) {
			dfh->DataBlk[datablk] = blk;
			blk = NextBadBlk(blk);
		}

		dfh->Protect = 0xf;	/* Protect it */
		dfh->ByteSize = dfh->HighSeq*TD_SECTOR;
		DateStamp(&dfh->CreationDate);
		ConvertString(dfh->FileName, "__BadBlocks__");
		dfh->ParentDir = 880; /* Root*/	
		dfh->SecondaryType = ST_FILE;

		dfh->CheckSum -= GetBlockSum((ULONG *) dfh);

		SetBitmapBlock(DosBitmap->BitmapData, 882 - 2, TRUE);		

		/* 54 is hash loc. for "__BadBlocks__"
		 * In HIDE mode, may have problems pre-2.0 OS
		 */
		RootBlock->HashTable[HideFlag ? 71 : 54] = 882;	/* Point Root Here */

		/* Now any extensions, if required */

		dfe = dfh;
		for(dfenum=0;(blk >= 0)&&(dfenum<8)&&numbadblk;++dfenum) {

			/* extend and check previous block */
			dfe->Extension = 883 + dfenum;
			dfe->CheckSum -= GetBlockSum((ULONG *) dfe);
			++dfe;

			ClearBlock((ULONG *)dfe);

			dfe->PrimaryType = T_SHORT;
			dfe->OwnKey = 883+dfenum;

			dfe->HighSeq = 
			dfe->DataSize = numbadblk > 72 ? 72 : numbadblk;
			numbadblk -= dfe->DataSize;

			dfe->FirstData = blk;

			for(datablk=71;(datablk>=0)&&(blk >= 0);--datablk) {
				dfe->DataBlk[datablk] = blk;
				blk = NextBadBlk(blk);
			}

			dfe->ParentDir = 882; /* dfh */	
			dfe->SecondaryType = ST_FILE;

			dfe->CheckSum -= GetBlockSum((ULONG *) dfe);

			SetBitmapBlock(DosBitmap->BitmapData, dfenum+(883-2), TRUE);		

		}
		if(numbadblk)
			printf("\nWARNING: Too Many Bad Blocks !\n");
	}


	/* Fix the block checksums. */

	RootBlock->CheckSum -= GetBlockSum((ULONG *) RootBlock);

	DosBitmap->CheckSum -= GetBlockSum((ULONG *) DosBitmap);

}


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

char *progname;
long unitNr;	/* Needed for raw read presently */

main(argc, argv)
int argc;
char **argv;
{
	struct MsgPort *port, *CreatePort();
	byte *diskBlock;
	int i,abortflag=0;
	int bps = TD_SECTOR, spt = NUMSECS;
	int ncylinders, wholeDisk, endtrack;

	int UseFFS = 0;	/* Default OFS for now */
	progname = argv[0];

	doheader();

	if (argc < 2) {
		Help();
		exit(1);
	}
	Enable_Abort = 0;

	if(strlen(argv[1]) > 3)
		unitNr = (argv[1])[2] - '0';
	else
		unitNr = atoi(argv[1]);

	drivename[2] = unitNr + '0';	/* name df0: etc */
	Device = (UBYTE *) TD_NAME; /* = "trackdisk.device" */

	for(i=2; i<argc; ++i) {
		int fmterr = 0;

		if(!strcmp("OFS",argv[i]))
			UseFFS = 0;
		else if(!strcmp("FFS",argv[i]))
			UseFFS = 1;
		else if(!strcmp("QUICK",argv[i]))
			Quick = 1;
		else if(!strcmp("BIT",argv[i]))
			BitOnly = 1;
		else if(!strcmp("HIDE",argv[i]))
			HideFlag = 1;
		else if(!strcmp("RAW",argv[i]))
			RawFlag = 1;
		else if(!strcmp("NORAW",argv[i]))
			RawFlag = 0;
		else if(!strcmp("NAME",argv[i]))
			if(++i < argc)	DiskName = argv[i];
			else		++fmterr;		
		else if(!strcmp("FROM",argv[i]))
			if(++i < argc)	FromName = argv[i];
			else		++fmterr;		
		else if(!strcmp("TO",argv[i]))
			if(++i < argc)	ToName = argv[i];
			else		++fmterr;		
		else if(!strcmp("DEBUG",argv[i]))
			DebugFlag = 1;

		if(fmterr) {
			Help();
			exit(0);
		}
	}

	if (!(port = CreatePort(NULL, 0L))) {
		puts("No memory for replyport");
		goto abort1;
	}
	if (!(TDReq = CreateExtIO(port, (long) sizeof(*TDREQ)))) {
		puts("No memory for I/O request");
		goto abort2;
	}
	if (OpenDevice(Device, unitNr, TDReq, 0L)) {
		printf("Cannot OpenDevice %s\n", Device);
		goto abort3;
	}
	if(!DiskName)
		DiskName = UseFFS ? "BBFormat_FFS" : "BBFormat_OFS";

	printf("Preparing to format %s as %s\n",drivename,DiskName);
	if(UseFFS)
		printf("FastFileSystem (FFS) format\n");
	else
		printf("OldFileSystem (OFS) format\n");

	TrackSize = bps * spt;
	nsides = NUMHEADS;
	Track = 0;
	Track *= nsides;
	ncylinders = 80;
	endtrack = Track + nsides * ncylinders;

	if ((DiskTrack = AllocMem(TrackSize,
			MEMF_PUBLIC | MEMF_CHIP | MEMF_CLEAR)) == NULL) {
		puts("No memory for track buffer");
		goto abort4;
	}
	if ((CheckTrack = AllocMem(TrackSize,
			MEMF_PUBLIC | MEMF_CHIP | MEMF_CLEAR)) == NULL) {
		puts("No memory for Check buffer");
		goto abort4a;
	}

	if(Quick) {
		wholeDisk = 0;
		printf("Format Boot, Root and Bitmap\n");
	} else {
		wholeDisk = 1;
		printf("Whole Disk Format/Verify\n");
	}

	if (!askyn("Are you sure ?"))
		goto abort5;

	if (breakcheck())
		goto abort5;

	MyInhibit(drivename, 1);

	/* Is disk in drive ? */

	TDREQ->iotd_Req.io_Command = TD_CHANGESTATE;
	DoIO(TDReq);
	if(TDREQ->iotd_Req.io_Actual) {
		printf("No Disk in drive !\n");
		goto done;
	}

	/* Is disk writeable ? */

	TDREQ->iotd_Req.io_Command = TD_PROTSTATUS;
	DoIO(TDReq);
	if(TDREQ->iotd_Req.io_Actual) {
		printf("Disk is Write Protected !\n");
		goto done;
	}

	/* Lock changenum - don't change while formatting !*/

	TDREQ->iotd_Req.io_Command = TD_CHANGENUM;
	DoIO(TDReq);
	TDREQ->iotd_Count = TDREQ->iotd_Req.io_Actual;

	BaseFmt(DiskTrack,UseFFS);

	if(RawFlag && RawSetup()) { /* Test */
		printf("Testing Disk with RAW Data...\n");
		if (wholeDisk) {
#if 0 /* TEST */
#if 1
for(i=5; i>=0/*NUMTRACKS*/; --i)
	trackerror[i] |= RawCheck(i);
#endif
#if 0	
for(i=4; i<4/*NUMTRACKS*/; ++i)
	trackerror[i] |= RawCheck(i);		
#endif
RawDone();
goto done;
#endif
			for(i=NUMTRACKS-1; i>=0; --i) {
				if (breakcheck()) {++abortflag;break;}
				trackerror[i] |= RawCheck(i);
			}		
		} else {
			trackerror[80] |= RawCheck(80); /* Just Root */
			trackerror[0] |= RawCheck(0);  /* and Boot  */
		}
		RawDone();
	}

	printf("Format/Verify Disk    \n");

	if (wholeDisk) {
		for(i=0; i<NUMTRACKS; ++i) {
			if(breakcheck()){++abortflag;break;}
			trackerror[i] |= WriteTrack(i);
		}	
	} else {
		trackerror[0] |= WriteTrack(0);    /* just do Boot cylinder */
		trackerror[80] |= WriteTrack(80); /* and Root cylinder     */
	}

	if(trackerror[0])
		printf("\nWARNING: Boot track unusable, do not install disk !\n");

	if(trackerror[80]){
		printf("\nWARNING: Root Corrupt, toss disk away !\n");
	} else {
		printf("\nInstall Root Track\n");

		FixRoot(DiskTrack,UseFFS);
		WriteTrack(80);	/* Track 80 for root */
	}
	if(abortflag)
		printf("Formatting Aborted - Disk may be Corrupt\n");
      done:
	TDREQ->iotd_Req.io_Command = TD_MOTOR;
	TDREQ->iotd_Req.io_Length = 0;
	DoIO(TDReq);

	MyInhibit(drivename, 0);

	printf("\n\nDone !\n");

      abort5:
	FreeMem(CheckTrack, TrackSize);
      abort4a:
	FreeMem(DiskTrack, TrackSize);
      abort4:
	CloseDevice(TDReq);
      abort3:
	DeleteExtIO(TDReq);
      abort2:
	DeletePort(port);
      abort1:;

}


WriteTrack(int Track)
{
	int t, s, error=0;

	t = Track / nsides;
	s = Track % nsides;
	printf("  Writing cylinder %3d side %d...\r", t, s);
	fflush(stdout);
	TDREQ->iotd_Req.io_Command = ETD_FORMAT;
	TDREQ->iotd_Req.io_Data = (APTR) DiskTrack;
	TDREQ->iotd_Req.io_Length = TrackSize;
	TDREQ->iotd_Req.io_Offset = TrackSize * Track;
	DoIO(TDReq);

	if (TDREQ->iotd_Req.io_Error) {
		printf(" Write error %d on cylinder %d side %d.\n",
		       TDREQ->iotd_Req.io_Error, t, s);
		error = 1;
	}
	TDREQ->iotd_Req.io_Command = ETD_UPDATE;
	DoIO(TDReq);
	if (TDREQ->iotd_Req.io_Error) {
		printf("Update error %d on cylinder %d side %d.\n",
		       TDREQ->iotd_Req.io_Error, t, s);
		error = 2;
	}
	TDREQ->iotd_Req.io_Command = ETD_CLEAR;
	DoIO(TDReq);

	printf("  Read\r");
	fflush(stdout);
	TDREQ->iotd_Req.io_Command = ETD_READ;
	TDREQ->iotd_Req.io_Data = (APTR) CheckTrack;
	TDREQ->iotd_Req.io_Length = TrackSize;
	TDREQ->iotd_Req.io_Offset = TrackSize * Track;
	DoIO(TDReq);
	if (TDREQ->iotd_Req.io_Error) {
		printf("  Read error %d on cylinder %d side %d.\n",
		       TDREQ->iotd_Req.io_Error, t, s);
		error = 3;
	}
	printf("Verify\r");
	fflush(stdout);
	if (memcmp(DiskTrack, CheckTrack, TrackSize)) {
		printf("  Verify Error  on cylinder %d side %d.\n", t, s);
		error = 4;
	}
	return error;
}

ask(char *query)
{
	int c;

	printf(query);
	set_raw();
	c = getchar();
	set_con();
	printf("%c\n",c);
	return(c);
}

askyn(char *query)
{
	int c = ask(query);

	return(toupper(c)=='Y');
}

Help(char *name)
{
	printf("\n\n"
"Usage: %s <0|1|2|3> [FFS|OFS] [NAME <DiskName>] [QUICK] [[NO]RAW]\n"
		"\t\t[FROM <file>] [TO <file>] [BIT] [HIDE] [DEBUG]\n\n"    	
"Formats Disks with hard errors, mapping out Bad Blocks\n\n"		
"FFS\t= FastFileSystem\n"		
"OFS\t= OldFileSystem (default)\n"		
"TO/FROM <file> is a list of bad blocks, added to auto-generated list\n"
"QUICK\tBoot/Root/Map only, read bad blocks from FROM file\n"		
"HIDE\thides BadBlock file, but may cause problems pre 2.0\n"
"RAW\tPre-Tests Disk using Raw Data pattern (Experimental)\n"
"BIT\tmark BitMap, but don't create __BadBlocks__ File\n"
"\t(if validator re-creates bitmap, bad blocks will become useable)\n",	        
progname);
}

extern char **CDate;
doheader()
{
 printf("\n\n\n\n\t\t\033[33mBBFormat \033[3mAMIGA\033[0;33m Disk Format Utility\n"
   	"\t\t\033[31;1m\251 1991 C-Born Software Systems\033[0m\n"
   	"\t\tProgram and Source Freely Distributable"
   	"\n\t\t%s\n\n",&CDate);
}

_wb_parse()
{
}

/*
 * Check track using Raw mode
 */

/* Write works ok (verify with hack)
 * but I can get read to sync .. keep trying ?
 */

#define TRKLEN 0x2ec4L	/* Allowance for track raw data */
#define TRKGAP 0x0600L	/* Allowance for gap */

byte *Wbuf,*Rbuf,*Wdata;

/* Set up formatted track buffers for Raw check */

RawSetup()
{
	if ((Wbuf = AllocMem(TRKLEN+TRKGAP,
			MEMF_PUBLIC | MEMF_CHIP | MEMF_CLEAR)) == NULL) {
		puts("No memory for raw write buffer");
		return 0;
	}

	if ((Rbuf = AllocMem(TRKLEN,MEMF_PUBLIC|MEMF_CHIP|MEMF_CLEAR)) == NULL) {
		puts("No memory for raw read buffer");
		FreeMem(Wbuf,TRKLEN+TRKGAP);
		return 0;
	}

	Wdata = Wbuf + TRKGAP;

	memset(Wbuf,0xaa,TRKGAP);
#if 0
	/* This pattern is not particularly sensitive to problems
	 */

	memset(Wdata, 0x55, TRKLEN);	/* Fill with 55s */

#else	/* Put in a pattern */
	/* This pattern (and others) appear reasonable, but sometimes are
	 * too sensitive, especially on inner tracks
	 */
	{long *lp = (long *)Wdata;
	int i;
	for(i=0;i<TRKLEN/4;++i){
		*lp++ = 0x5aa55aa5; /*0x512a4445;*/
	}
	}
#endif
	*((unsigned short *)(Wdata-2)) = 0x4489; 		/* sync */
	*((unsigned short *)(Wdata)) = 0x5555;

	*((unsigned short *)(Wdata+TRKLEN-4)) = 0x9A59; /* tail-marker */

	return 1;
}

RawDone()
{
	FreeMem(Rbuf,TRKLEN);
	FreeMem(Wbuf,TRKLEN+TRKGAP);
}

/*
 * Check Track using Raw Data (mfm)
 */

RawCheck(int Track)
{
	int t, s, error=0;

	t = Track / nsides;
	s = Track % nsides;

	printf(" RawWrite cylinder %3d side %d...\r",t,s);
	fflush(stdout);

	TDREQ->iotd_Req.io_Command = ETD_RAWWRITE;
	TDREQ->iotd_Req.io_Data = (APTR) Wbuf;
	TDREQ->iotd_Req.io_Length = TRKLEN+TRKGAP;
	TDREQ->iotd_Req.io_Offset = Track;
	TDREQ->iotd_Req.io_Flags  = IOTDF_WORDSYNC;
	DoIO(TDReq);

	if (TDREQ->iotd_Req.io_Error) {
		printf(" RawWrite error %d on cylinder %3d side %d\n",
		       TDREQ->iotd_Req.io_Error,t,s);
		error = 1;
	}


	TDREQ->iotd_Req.io_Command = ETD_UPDATE;
	DoIO(TDReq);
	if (TDREQ->iotd_Req.io_Error) {
		printf("Update error %d on cylinder %3d side %d\n",
		       TDREQ->iotd_Req.io_Error, t,s);
		error = 2;
	}
	TDREQ->iotd_Req.io_Command = ETD_CLEAR;
	DoIO(TDReq);

	printf(" RawRead  cylinder %3d side %d...\r",t,s);
	fflush(stdout);

	TDREQ->iotd_Req.io_Command = ETD_RAWREAD;
	TDREQ->iotd_Req.io_Data = (APTR) Rbuf;
	TDREQ->iotd_Req.io_Length = TRKLEN;
	TDREQ->iotd_Req.io_Offset = Track;
	TDREQ->iotd_Req.io_Flags  = IOTDF_WORDSYNC;

#ifdef BADRAWREAD
	TDREQ->iotd_Req.io_Actual = unitNr; /* Should figure from IOReq... */
	MyTDIO(TDReq);
#else
	DoIO(TDReq);
#endif
	if (TDREQ->iotd_Req.io_Error) {
		printf(" RawRead error %d on cylinder %3d side %d\n",
		       TDREQ->iotd_Req.io_Error, t,s);
		error = 3;
	}

	/* Note: when we sync on 4489, data read does not include sync */

	if (memcmp(Wdata, Rbuf, TRKLEN-4)) {
		printf("  Verify Error on cylinder %3d side %d\n",t,s);
		error = 4;
	}

	return error;
}

breakcheck()
{
	return (int)(SetSignal(0L,0L) & SIGBREAKF_CTRL_C);
}

breakreset()
{
	SetSignal(0L,SIGBREAKF_CTRL_C);
}

Chk_Abort()
{
	return 0;
}
