DataMuseum.dk

Presents historical artifacts from the history of:

Commodore CBM-900

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about Commodore CBM-900

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - download

⟦b8bbb65f8⟧ TextFile

    Length: 17937 (0x4611)
    Types: TextFile
    Notes: UNIX file
    Names: »TP1.C«

Derivation

└─⟦f27320a65⟧ Bits:30001972 Commodore 900 hard disk image with partial source code
    └─⟦2d53db1df⟧ UNIX V7 Filesystem
        └─ ⟦this⟧ »frankh/src/tape_drive/TP1.C« 

TextFile

/* (-lgl
 * 	The information contained herein is a trade secret of Mark Williams
 * 	Company, and  is confidential information.  It is provided  under a
 * 	license agreement,  and may be  copied or disclosed  only under the
 * 	terms of  that agreement.  Any  reproduction or disclosure  of this
 * 	material without the express written authorization of Mark Williams
 * 	Company or persuant to the license agreement is unlawful.
 * 
 * 	COHERENT Version 0.7.3
 * 	Copyright (c) 1982, 1983, 1984.
 * 	An unpublished work by Mark Williams Company, Chicago.
 * 	All rights reserved.
 -lgl) */
/*
 * 
 *
 *
 */

#define FMTCMD  3			/* for tpioctl */
#define	FDFORMAT 0x181

#include	<coherent.h>
#include	<buf.h>
#include	<con.h>
#include	<sched.h>
#include	<stat.h>
#include	<uproc.h>
#include	<errno.h>
#include	<ioport.h>

int	tpload();
int	tpuload();
int	tpopen();
int	tpread();
int	tpwrite();
int	tpblock();
int	tpintr();
int	tpioctl();
int	nulldev();
int	nonedev();
char	*pfix();			/* map phys addr to a char * */
daddr_t	tpbno();			/* returns actual block offset */

CON	tpcon	= {
	DFBLK|DFCHR,			/* Flags */
	11,				/* Major index */
	tpopen,				/* Open */
	nulldev,			/* Close */
	tpblock,			/* Block */
	tpread,				/* Read */
	tpwrite,			/* Write */
	tpioctl,			/* Ioctl */
	nulldev,			/* Powerfail */
	nulldev,			/* Timeout */
	tpload,				/* Load */
	tpuload				/* Unload */
};

/*
 * Western Digital Controller port addresses
 */
#define	WDIO	0x0500			/* Data Register (word mode) */
/*
 * QIC-02 command lines
 */

#define	ONL	0x01			/* 8036 #0 PB0	*/
#define REQ	0x02			/* 8036 #0 PB1	*/
#define	RST	0x04			/* 8036 #0 PB2	*/
#define	XFR	0x04			/* 8036 #1 PB2	*/
#define	ACK	0x08			/* 8036 #1 PB3	*/
#define	RDY	0x01			/* 8036 #1 PC0	*/
#define	EXC	0x01			/* 8036 #0 PC0	*/
#define	DIR	0x02			/* 8036 #1 PB1	*/

#define	MAXBUF	256
#define	WDTDR	0x00			/* test drive ready */
#define	WDREST	0x01			/* restore to cyl 0 */
#define	WDRSS	0x03			/* Request status */
#define	WDCTF	0x05			/* check track format */
#define	WDFMTT	0x06			/* format track */
#define	WDREAD	0x08			/* Read */
#define	WDWRITE	0x0A			/* Write */
#define	WDSDP	0x0C			/* set drive parameters */
#define	WDCCBA	0x0F			/* change command block address */
#define	WDDDIAG	0xE3			/* run drive diagnostics */
#define	WDCDIAG	0xE4			/* run controller diagnostics */
#define	CFFMT	0x04			/* format floppy disk */
#define	CFCCB	0x0F			/* change command block address */

#define	FWPERROR 0x77			/* write protect error */
#define	FNOSENSE 0x70			/* no sense error */
#define	FMERROR	 0x73			/* non recoverable media error */
#define FCMERROR  0x76			/* changed-media error */

#define	NSEC	17			/* number of sectors per track */
#define	WDVEC	0x80			/* base of tp interrupt vector */
#define	NDRIVES	1			/* max 1 tape drive		*/
#define	NHDISKS	2			/* first 2 drives are hard */
#define	NFBLK	2392			/* number of blocks per floppy */

typedef	struct	tpcmd {			/* command block layout */
 	union {
		struct {
			unsigned char	c_opcode; /* command class & opcode */
			unsigned char	c_lunhiaddr; /* lun & addr [4:0] */
			unsigned char	c_midaddr; /* middle sector address */
			unsigned char	c_lowaddr; /* low sector address */
		} tpcmdop;
		unsigned long	c_lunaddr;	/* lun and whole address */
	} tpcmdu;
	unsigned char	c_blockcnt;	/* number of blocks in I/O */
	unsigned char	c_control;	/* reserved control byte */
	unsigned char	c_highdma;	/* high DMA addr (phys segment) */
	unsigned char	c_middma;	/* middle DMA address */
	unsigned char	c_lowdma;	/* low DMA address */
	unsigned char	c_rsvd1;	/* reserved */
	unsigned char	c_rsvd2;	/* reserved */
	unsigned char	c_rsvd3;	/* reserved */
	unsigned char	c_errorbits;	/* error infowdation */
	unsigned char	c_lunladd2;	/* error lun and high sector addr */
	unsigned char	c_ladd1;	/* error middle address */
	unsigned char	c_ladd0;	/* error low address */
} WDCMD;
#define	c_op	tpcmdu.tpcmdop.c_opcode

#define	bool	int
#define	TRUE	(0 == 0)
#define	FALSE	(0 != 0)


/*
 * minor device layout
 *	bits 7-4: physical drive number
 *	bits 3-0: pseudo-drive number (partition) within the physical drive
 */
#define	drive(mdev)	((mdev) >> 4)		/* get drive # */
#define	pseudo(mdev)	((mdev) & 0xF)		/* get partition # */

#define	NPSEUDO	(sizeof tpbtab/sizeof tpbtab[0])

struct {
	unsigned int	padd;		/* port address	*/
	unsigned int	pdata;		/* port data	*/
} savport[] = {
	0,	0,
	0,	0,
	0,	0,
	0,	0,
	0,	0,
	0,	0,
	0,	0,
	0,	0,
	0,	0,
	0,	0,
	0,	0
};

/*
 * Printer database.
 */
typedef	struct	lpinfo {
	int	lpcol;			/* Current horizontal position */
	int	lpnbuf;			/* # of bytes in buffer */
	char	*lpin;			/* Input pointer */
	char	*lpout;			/* Output pointer */
	char	lpflag;			/* Flags */
	char	lpbuf[MAXBUF];		/* Buffer */
}	LP;

extern LP *lp;

/* lpflag bits */
#define	LPOPEN	0x01			/* Printer is open */


struct {
	unsigned long	bstart;		/* starting block # */
	unsigned long	bcount;		/* size in blocks */
} tpbtab[] = {
	0L,	20765L,
	20765L,	20765L,
	41530L,	20765L,
	62295L,	20765L,
	0L,	0L,
	0L,	0L,
	0L,	0L,
	0L,	0L,
	0L,	0L,
	0L,	0L,
	0L,	0L,
	0L,	0L,
	0L,	0L,
	0L,	0L,
	0L,	0L,
	0L,	83060L
};

/*
 * drive & controller parameters used during initialization. 
 * note that the parameters are used for both drives by the controller
 * (sorry, no mix and match on drive types).
 */
typedef	struct	tpp {
	unsigned char	p_options;	/* options <7:4>, step rate <3:0> */
	unsigned char	p_head_cyl;	/* # heads <6:4>, MSB # cyl <3:0> */
	unsigned char	p_cyl;		/* LSB # cylinders <7:0> */
	unsigned char	p_precomp;	/* precomp cyl/16  <7:0> */
	unsigned char	p_reduced;	/* reduced write cur. cyl/16 <7:0> */
	unsigned char	p_nsectors;	/* sectors per track <7:0> */
} WDP;
WDP	tpparam = {			/* patch these values as needed: */
	0xF,				/* no options, 16 uS fast step */
	(7 << 4) | (698/256),		/* 7 heads, MSB of cyl */
	698 % 256,			/* # cyl LSB */
	348 / 16,			/* precomp starts at cyl 128. */
	348 / 16,			/* reduced write cur. at cyl 128. */
	17				/* number of sectors per track */
};

/*
 * housekeeping
 */
typedef	struct tpunit {
	bool		tpu_active;	/* drive/controller busy */
	bool		tpu_dma_aligned;/* dma address is 512 aligned */
	struct tpunit	*tpu_forw;	/* next drive in controller chain */
	struct tpunit	*tpu_back;	/* last drive */
	BUF		*tpu_actf;	/* next BUF in drive chain */
	paddr_t		tpu_paddr;	/* physical address of buffer */
} WDU;

WDU	tptab;				/* controller header */
WDU	tputab[NDRIVES];		/* drive headers */
BUF	tpbuf;				/* BUF structure */
/*
 * cmdblk is a 3*512 byte block aligned on a 512 byte boundary in physical
 * memory. It is declared in md.s to use here. WDBUFPADDR must be a paddr_t
 * pointing to cmdblk+(2*512) for the code to work correctly !!!
 * the CF entries are for the floppy command block.
 */
extern	char	cmdblk[];
/* NORM compiled this with non-default values which aren't recorded
 * anywhere I can find. Inserted the values determined from disassem-
 * bling the system.  rec 6.V.85 */
#define WDCMDBLKPADDR	0x80000L
#define WDBUFPADDR	0x80400L
#ifndef	WDCMDBLKPADDR
#define	WDCMDBLKPADDR	0x20000L	/* default to 2|0000 based RAM */
#endif
#ifndef	WDBUFPADDR
#define	WDBUFPADDR	0x20400L	/* above cmd blk and ECC buffer */
#endif
#define	CFCMDBLKPADDR	(WDCMDBLKPADDR+0x10L)

/*
 * set up the driver and the actual controller. this involves setting the
 * drive parameters for the two drives on the hard disk. the floppy uses
 * the default internal parameters, so no need to set them here.
 */
tpload()
{
	register int i;

	printf("tpload: load driver\n");
/*	if (lp->lpflag)		 if lp is open, don't try this!
		return(-1);
	lp->lpflag = LPOPEN;	 mark busy!	*/
	svp(0, PBDPP);
	svp(1, PBDD);
	svp(2, PBD);
	svp(3, PBDPP | CIO1);
	svp(4, PBDD | CIO1);
	svp(5, PBD | CIO1);
	svp(6, PCDPP | CIO1);
	svp(7, PCDD | CIO1);
	svp(8, 0);

	for (i = 0; i < NDRIVES; ++i)
		tputab[i].tpu_active = 0;
	tptab.tpu_active = 0;
/*	setivec(WDVEC, tpintr);		*/
}

svp(pos, add)
int pos;
unsigned int add;
{
	savport[pos].padd = add;
	if (add)
		savport[pos].pdata = inb(add);
	else
		savport[pos].pdata = 0;
}

tpuload()
{
	register int cnt = 0;

	printf("tpuload: unload driver\n");
/*	lp->lpflag = 0;		*/
	while(savport[cnt].padd) {
		outb(savport[cnt].padd, savport[cnt].pdata);
		++cnt;
	}
	printf("made it!\n");

}

/*
 * Open routine. Check that the minor # is in range. Note that the
 * floppy minor # controls formatting. 0 == normal operation, 1 == format.
 */
tpopen(dev, mode)
dev_t	dev;
{
	register unsigned int d, p;		/* drive & partition #'s */

	d = drive(minor(dev));
	p = pseudo(minor(dev));
	printf("tpopen: d = %d, p = %d\n", d, p);
	if (d >= NDRIVES
	    || (p >= NPSEUDO && d < NHDISKS)
	    || (p > 1 && d >= NHDISKS))
		u.u_error = ENXIO;
}

/*
 * perform a RAW read operation
 */
tpread(dev, iop)
dev_t	dev;
IO	*iop;
{
	printf("tpread: begin\n");
	ioreq(&tpbuf, iop, dev, BREAD, BFIOC|BFBLK|BFRAW);
}


/*
 * perform a RAW read operation
 */
tpwrite(dev, iop)
dev_t	dev;
IO	*iop;
{
	printf("tpwrite: begin\n");
	ioreq(&tpbuf, iop, dev, BWRITE, BFIOC|BFBLK|BFRAW);
}

/*
 * block I/O routine.
 */
tpblock(bp)
register BUF	*bp;
{
	register unsigned int	d;		/* drive # */
	register unsigned int	pd;		/* partition # */
	register daddr_t  bno;			/* block # */
	register int	s, fh;			/* saved machine status */
	bool	tpstart();

	d = drive(minor(bp->b_dev));
	pd = pseudo(minor(bp->b_dev));
	bno = bp->b_bno;

/*	printf("tpblock: d=%d, pd=%d, bno=%d enter\n", d, pd, (int)bno);
	tpbufdump(bp); */

	if (d >= NDRIVES
	    || (d < NHDISKS && (pd >= NPSEUDO || bno >= tpbtab[pd].bcount))
	    || (d >= NHDISKS && (pd != 0 || bno >= NFBLK))) {
		bp->b_flag |= BFERR;
		bdone(bp);
		return;
	}
	if (d < NHDISKS)			/* only for hard disks */
		bno += tpbtab[pd].bstart;
	s = sphi();
	disksort(&tputab[d], bp, bno);
	tpustart(d);
	while (!(fh = tpstart()))
		;
	spl(s);
/*	printf("*buf = %s\n", bp->b_vaddr);	*/
}

/*
 * start up a disk I/O request to the controller if one is not already
 * in progress.
 */
tpustart(d)
register int	d;					/* drive # */
{
	register BUF	*bp;

/*	printf("tpustart: d = %d\n", d);	*/

	if (tputab[d].tpu_active)		/* active already ? */
		return;
	bp = tputab[d].tpu_actf;
	if (bp == (BUF *)0)			/* any work to do ? */
		return;
	tputab[d].tpu_active = TRUE;

	if (tptab.tpu_forw != (WDU *)0)
		tptab.tpu_back->tpu_forw = &tputab[d];
	else
		tptab.tpu_forw = &tputab[d];
	tptab.tpu_back = &tputab[d];
	tputab[d].tpu_forw = (WDU *)0;
	return;
}

/*
 * actually set up the command block for the WD controller to perform 
 * the I/O.  Check to see if DMA address is aligned on a 512 byte
 * boundary as required by the Commodore (MOS Technology) DMA controller.
 * All RAW I/O must be aligned !!! All other I/O must be aligned if greater
 * than 512 bytes (1 block) of I/O is performed. If a non-RAW non-aligned
 * write, copy the user's data to our tpiobuf which is aligned. If a read,
 * do the copy in the interrupt routine after the data is in the tpiobuf
 * buffer.
 */
bool
tpstart()
{
	register BUF	*bp;
	register WDCMD	*cbp;
	register unsigned int	d, pd;
	int	dma_error = FALSE;

/*	printf("tpstart: begin\n");	*/
	if (tptab.tpu_active)
		return (TRUE);
	if (tptab.tpu_forw == (WDU *)0)
		return (TRUE);
	bp = tptab.tpu_forw->tpu_actf;
	printf("tpstart: block = %u\n", (int)bp->b_bno);
	tptab.tpu_paddr = bp->b_paddr;
	d = drive(minor(bp->b_dev));
	pd = pseudo(minor(bp->b_dev));
	tptab.tpu_dma_aligned = TRUE;
	if ((tptab.tpu_paddr&0x1FFL) != 0) {	/* make sure aligned 512 */
		tptab.tpu_dma_aligned = FALSE;
		if (bp->b_flag&BFRAW) {
			printf("tp: non-aligned dma in RAW mode\n");
			tpbufdump(bp);
		} else if (bp->b_count > 512) {
			printf("tp: non-aligned multi-block dma error\n");
			++dma_error;
		}
	}
	if (dma_error) {
		tptab.tpu_forw->tpu_active = 0;
		tptab.tpu_forw->tpu_actf = bp->b_actf;
		tptab.tpu_forw = tptab.tpu_forw->tpu_forw;
		bp->b_flag |= BFERR;
		bdone(bp);
		tpustart(d);
		return (FALSE);
	}
	tptab.tpu_active = TRUE;
/*	if (!tptab.tpu_dma_aligned && bp->b_req == BWRITE)  one block only! */
/*		kkcopy(pfix(WDS, tptab.tpu_paddr), cmdblk+0x400, 512);       */
/*	if (d >= NHDISKS)			 map appropriate cmd block */
/*		cbp = (WDCMD *) pfix(WDS, CFCMDBLKPADDR);	 floppy */
/*	else								*/
/*		cbp = (WDCMD *) pfix(WDS, WDCMDBLKPADDR);	 hard */
/*	tpcmdclr(cbp);				 clear out block */
	if (bp->b_req == FMTCMD)
		return tpformat(bp, cbp);
/*	cbp->tpcmdu.c_lunaddr = bp->b_bno + tpbtab[pd].bstart;	*/
/*	if (d == 1 || d == NHDISKS+1)		 second hard or floppy disk
		cbp->tpcmdu.tpcmdop.c_lunhiaddr |= 1 << 5;
	cbp->c_blockcnt = bp->b_count >> 9;
	if (tptab.tpu_dma_aligned) {
		cbp->c_highdma = tptab.tpu_paddr >> 16;
		cbp->c_middma = tptab.tpu_paddr >> 8;
		cbp->c_lowdma = tptab.tpu_paddr;
	} else {
		cbp->c_highdma = (WDBUFPADDR >> 16) & 0xFF;
		cbp->c_middma =  (WDBUFPADDR >> 8) & 0xFF;
		cbp->c_lowdma =  (WDBUFPADDR) & 0xFF;
	}
	cbp->c_errorbits = 0xFF;		 indicate ready
	cbp->c_op = ((bp->b_req==BREAD) ? WDREAD : WDWRITE);		*/
#if	VERBOSE
	tpcbdump(cbp);
#endif
/*	out(WDIO, 1);				 wake up controller */
	return (tpintr());
}

/*
 * got an interrupt from the controller - see if it was a valid I/O
 * completion or an error.
 */
tpintr()
{
	register BUF	*bp;
	register WDCMD	*cbp;
	register unsigned int	d;

/*	printf("tpintr: begin\n");	*/
	if (tptab.tpu_active) {
		bp = tptab.tpu_forw->tpu_actf;
		d = drive(minor(bp->b_dev));
/*		if (d >= NHDISKS)
			cbp = (WDCMD *) pfix(WDS, CFCMDBLKPADDR);  floppy
		else
			cbp = (WDCMD *) pfix(WDS, WDCMDBLKPADDR);  hard
*/
/*		if ((cbp->c_errorbits & 0x7F) == 0) { 		*/
		if ( 0 == 0 ) {
			tptab.tpu_forw->tpu_actf = bp->b_actf;
			if (!tptab.tpu_dma_aligned)	/* one block only !! */
			    if (bp->b_req == BREAD)
			      kkcopy(cmdblk+0x400,pfix(WDS,tptab.tpu_paddr),512);
			bdone(bp);
		} else 
			tpharderr(bp, cbp);
		tptab.tpu_forw->tpu_active = 0;
		tptab.tpu_forw = tptab.tpu_forw->tpu_forw;
		tptab.tpu_active = 0;
		tpustart(d);
	}
/*	out(WDIO, 0);				 reset IEO on DMA chip */
	while (!tpstart())
		;
	return(1);
}

/*
 * got an error - inform the user and try to recover.
 */
tpharderr(bp, cbp)
register BUF	*bp;
register WDCMD	*cbp;
{
	register unsigned int	d;
	register unsigned char *ucp;
	register unsigned int pd;
	register unsigned int bno;	/* only used for display !! */

	pd = pseudo(minor(bp->b_dev));
	bno = bp->b_bno;
	pd = cbp->c_errorbits & 0x7F;
	if (pd == FCMERROR)		/* media changed? */
		return;			/* ignore */
	if ((d = drive(minor(bp->b_dev))) >= NHDISKS)
		printf("fd: floppy disk #%d: ", d-NHDISKS);
	else
		printf("hd: hard disk #%d", d);
	if (pd == FWPERROR && d >= NHDISKS)		/* write protected ?? */
		printf("write protect error. ");
	else if (pd == FNOSENSE && d >= NHDISKS)	/* no floppy ? */
		printf("no floppy in drive. ");
	else if (pd == FMERROR && d >= NHDISKS)
		printf("irrecoverable media error. ");
	else {
		printf(" error=%x [ ", pd);
		for (d = 0, ucp = (unsigned char *)cbp; d < sizeof(WDCMD); ++d)
			printf("%x ", *ucp++);
		printf("] ");
	}
	devmsg(bp->b_dev, " b=%u\n", bno);
	d = drive(minor(bp->b_dev));
	tputab[d].tpu_actf = bp->b_actf;
	bp->b_flag |= BFERR;
	bdone(bp);
}

/*
 * return the actual device block offset given a buffer. this makes
 * the disk sorting process so much easier.
 */
daddr_t
tpbno(bp)
register BUF *bp;
{
	register unsigned int pd;

	pd = pseudo(minor(bp->b_dev));
	if (drive(minor(bp->b_dev)) >= NHDISKS)		/* floppies ??? */
		return (bp->b_bno);
	return (bp->b_bno + tpbtab[pd].bstart);
}

/*
 * sort request into queue
 * 'bp is inserted in the queue of the device (should be a disk drive)
 * given by 'dp'.  A weaving algorithm is used.
 */
disksort(dp, bp, bno)
struct tpunit	*dp;
register BUF	*bp;
daddr_t	bno;
{
	register BUF	*last;
	register BUF	*next;


	next = dp->tpu_actf;
	if (next == (BUF *)0) {
		dp->tpu_actf = bp;
		bp->b_actf = next;
		return;
	}

	while (last=next, next=next->b_actf)
		if ((tpbno(last) <= bno && bno <= tpbno(next))
		|| (tpbno(last) >= bno && bno >= tpbno(next))) {
			last->b_actf = bp;
			bp->b_actf = next;
			return;
		}

	last->b_actf = bp;
	bp->b_actf = next;
}


/*
 * dump\ a BUF struct to the terminal
 */
tpbufdump(bp)
register BUF *bp;
{
	printf("BUF: flag=%x dev=%x bno=%u req=%x count=%d\n", bp->b_flag,
		bp->b_dev, (int)bp->b_bno, bp->b_req, (int)bp->b_count);
	printf("     resid=%d vaddr=%p paddr=%p bp=%p\n", (int)bp->b_resid,
		bp->b_vaddr, bp->b_paddr, bp);
}
#if	VERBOSE
/*
 * dump a command block to the terminal
 */
tpcbdump(cbp)
register WDCMD	*cbp;
{
	register int	d;
	register unsigned char *ucp;

	printf("tp/cf: cb = [ ", cbp->c_errorbits);
	for (d = 0, ucp = (unsigned char *)cbp; d < sizeof(WDCMD); ++d)
		printf("%x ", *ucp++);
	printf("] ");
}
#endif

/*
 * format the Commodore floppy disk specified. Note that this command can
 * only format an entire disk, unlike the hard disk counterpart.
 */
tpioctl(dev, com, vec)
dev_t dev;
int com;
char *vec;
{
	register int s;


	if (dev != makedev(2, 32) && dev != makedev(2, 48)) {
		u.u_error = ENODEV;
		return;
	}
	if (com != FDFORMAT) {
		u.u_error = EINVAL;
		return;
	}
	if (! super())
		return;
	lock(tpbuf.b_gate);
	tpbuf.b_flag = BFNTP;
	tpbuf.b_req = FMTCMD;
	tpbuf.b_dev = dev;
	tpbuf.b_bno = 0;
	tpbuf.b_count = 512;
	tpbuf.b_paddr = 0;
	s = sphi();
	dblock(dev, &tpbuf);
	while ((tpbuf.b_flag&BFNTP) != 0)
		sleep((char *)&tpbuf, CVBLKIO, IVBLKIO, SVBLKIO);
	spl(s);
	if ((tpbuf.b_flag&BFERR) != 0)
		u.u_error = tpbuf.b_err ? tpbuf.b_err : EIO;
	unlock(tpbuf.b_gate);
}
tpformat(bp, cbp)
register BUF *bp;
register WDCMD *cbp;
{
	register int i;
	register unsigned char *cp;

	cbp->c_opcode = CFFMT;			/* format device */
	cbp->c_highdma =(WDBUFPADDR >> 16);	/* phys segment */
	cbp->c_middma = (WDBUFPADDR >> 8);	/* offset */
	cbp->c_lowdma = 0x00;
	cbp->c_lunhiaddr = (drive(bp->b_dev) << 5);
	cbp->c_errorbits = 0xff;		/* make ready */
#if	VERBOSE
	tpcbdump(cbp);
#endif
/*	out(WDIO, 1);				 wake up controller */
	return (TRUE);
}