/* * Priam Datatower Disk and Streaming Tape Driver * * (C) 1985 UniSoft Corp. of Berkeley CA * * UniPlus Source Code. This program is proprietary * with Unisoft Corporation and is not to be reproduced * or used in any manner except as authorized in * writing by Unisoft. */#include "sys/param.h"#include "sys/mmu.h"#include "sys/types.h"#include "sys/inode.h"#include "sys/ino.h"#include "saio.h"#include "streamer.h"#include "sys/cops.h"#include "sys/local.h"#include "sys/priam.h"/*#define DELAY()	asm("\tnop");*/#define DELAY()	;char pmiflag[NPM];char pmresults[NPMRES];		/* result regs. for last command comp ack */int pmnblks[NPM];		/* number of blocks on disk */int pmcblkcnt;			/* current block count */struct pmheader pmhdr;		/* write header (write boot fileid for blk 0) *//* variables needed to share info for interrupt routines */struct pm_base *pmhwbase;struct datacopyparms dcparms;struct pmfmtstat dcstat;struct results reslt;char	pmstate;		/* idling or waiting for interrupt */char	pmxstate;		/* idling or waiting for interrupt */char	pmunit;			/* card (0,1,2) */daddr_t	pmbn;			/* bn for pmwdsect, block 0 gets special ID in header */caddr_t	pmma;			/* memory address */char	pmierror;		/* error value from intr */pmopen(iobp)register struct iob *iobp;{	register unit = iobp->i_unit;	register short slotid;	int pm0intr(), pm1intr(), pm2intr();	register long *ip;	register short *waddr, i;	if (unit >= NPM) {		printf("Invalid device 0x%x\n", unit);		return -1;	}	slotid = (unit==0) ? *SLOTIDS : 		((unit==1) ? *SLOTID2 : *SLOTID3);	if ((slotid & SLOTMASK) != ID_PRIAM) {		printf("pmopen: not a Priam card\n");		return -1;	}	ip = &((long *) 0)[EXPIVECT+devtoslot(unit)];	/* point to int vect */	*ip = (unit==0) ? (long)pm0intr :	/* set to Priam intr */		((unit==1) ? (long)pm1intr : (long)pm2intr);	waddr = (short *)&pmhdr;	/* clear header to zeroes */	i = HDRSIZE / sizeof(short);	do {		*waddr++ = 0;	} while ( --i );	if (pmiflag[unit] == 0) {		if (pmcinit(unit))			return(-1);		if (pmdinit(unit))			return(-1);		pmiflag[unit]++;	}	return 0;}/* * pmcinit - initialize controller (turn on motor) * from PRIAMCARDASM.TEXT - INITCONT */pmcinit(unit)register unit;{	register struct pm_base *addr;	register i;	addr = pmaddr(unit);	/* hwbase */	addr = pmBWaddr(addr);	/* byte mode - waiting */	if ((addr->status & CMD_DONE) != 0) {	/* cmd complete must be false */		if (pmwaitrf(addr)) {			printf("timeout with status CMD_DONE\n");			goto err;		}		addr->cmdreg = 0;	/* command ack power up (clrb) */		i = TIMELIMIT;		while ((addr->status & CMD_DONE) == 0) {			if (--i)				continue;			printf("timeout waiting for power up\n");			goto err;		}	}	if (pmwaitrf(addr)) {		printf("timeout before software reset\n");		goto err;	}	addr->cmdreg = PMRESET;		/* issue software reset */	if ((i=pmackcc(addr)) != PMICOMP) {		printf("software reset error 0x%x\n", i);		goto err;	}	if (pmwaitrf(addr)) {		printf("timeout before read mode\n");		goto err;	}	addr->p0 = 0;		/* (clrb) */	addr->cmdreg = PMRMODE;	if (pmackcc(addr)) {		printf("read mode cmd ack failed\n");		goto err;	}	if (pmwaitrf(addr)) {		printf("timeout after read mode\n");		goto err;	}	addr->p0 = 0;		/* (clrb) */	if (pmresults[3] == 2) {		printf("Smart E controller not implemented\n");		goto err;	}	addr->p1 = PMLOGSECT;		/* set mode "logical sector mode" */	addr->p2 = 0;		/* (clrb) */	addr->cmdreg = PMSMODE;	if (pmackcc(addr)) {		printf("set mode cmd ack failed\n");		goto err;	}	if (pmwaitrf(addr)) {		printf("timeout after set mode\n");		goto err;	}	addr->p0 = 0x40;	addr->p1 = PMPSEL1;	addr->p2 = PM1PARMS;	addr->cmdreg = PMSETP;	if (pmackcc(addr)) {		printf("set parms 1 cmd ack failed\n");		goto err;	}	if (pmwaitrf(addr)) {		printf("timeout after set parms 1\n");		goto err;	}	addr->p0 = 0x40;	addr->p1 = PMPSEL0;	addr->p2 = PM0PARMS;	addr->cmdreg = PMSETP;	if (pmackcc(addr)) {		printf("set parms 0 cmd ack failed\n");		goto err;	}	return 0;err:	printf("can't initialize controller\n");	return(-1);}/* * pmdinit - initialize device (sequence up) * from PRIAMASM.TEXT - INITDEV */pmdinit(unit)register unit;{	register struct pm_base *addr;	register tmp;	register pmnheads, pmncyls, pmnsecs;	/* no. of heads, cylinders, sectors */	pmhwbase = pmaddr(unit);	/* base address for this card */	addr = pmBWaddr(pmhwbase);	/* byte mode - waiting */	if (pmwaitrf(addr)) {		printf("timeout before read dev parms\n");		goto err;	}/*****/	addr->p0 = 0;			/* device always 0 */	addr = pmBIaddr(pmhwbase);	/* byte mode - intr enabled */	pmunit = unit;			/* save for pmiintr */	pmierror = 0;			/* clear for pmiintr */	pmstate = INITING;	tmp = spl2();	addr->cmdreg = PMRDEVPMS;	/* sequence up disk & read dev parms */	while (pmstate != IDLING)	;	splx(tmp);	if (pmierror) {		printf("transaction status = 0x%x\n", pmresults[0]);		goto err;	}	pmnheads = PMNH(pmresults[1]);	pmncyls = PMNC(pmresults[1], pmresults[2]);	pmnsecs = PMNS(pmresults[3]);	pmnblks[unit] = pmnheads * pmncyls * pmnsecs;	return 0;err:	printf("can't initialize disk\n");	return(-1);}/* * disk functions */pmdstrategy(ip, func)register struct iob *ip;{	register unit = ip->i_unit;	register daddr_t bn;	register resid;	int offset;	caddr_t addr;	if (unit >= NPM)		return(-1);	pmhwbase = pmaddr(unit);	/* base address for this card */	if (ip->i_bn < 0) {		printf("pmdstrategy: bn < 0\n");		return(-1);	}	resid = ip->i_cc;dloop:	if (resid < PBSIZE)		goto dout;	offset = ip->i_cc - resid;	addr = ip->i_ma + offset;	bn = ip->i_bn + (offset >> PBSHIFT);	if (bn >= pmnblks[unit]) {		printf("pmdstrategy: %d blocks on disk\n", pmnblks[unit]);		goto dout;	}	if (pmdrw(unit, bn, func&F_READ, addr) != 0)		return(-1);	resid -= PBSIZE;	goto dloop;dout:	if (resid != 0)		printf("pmdstrategy: bn=%d resid=%d\n", ip->i_bn, resid);	return(ip->i_cc - resid);}pmdrw(unit, bn, rflag, addr)register unit;daddr_t bn;register rflag;register caddr_t addr;{	register struct pm_base *hwaddr;	register int tmp;	hwaddr = pmBWaddr(pmhwbase);	/* byte mode - waiting */	if (pmwaitrf(hwaddr)) {		printf("pmdrw: timeout before setting %s parameters ",			rflag?"read":"write");		goto derr;	}/*****/	hwaddr->p0 = DISK;		/* for disk operations */	tmp = bn;	hwaddr->p3 = tmp & 0xFF;	/* most sig. byte */	tmp = tmp >> 8;	hwaddr->p2 = tmp & 0xFF;	/* middle byte */	tmp = tmp >> 8;	hwaddr->p1 = tmp & 0xFF;	/* least sig. byte */	hwaddr->p4 = 1;			/* operation count = 1 sector */	hwaddr->p5 = 0;	pmcblkcnt = 1;	hwaddr = pmBIaddr(pmhwbase);	/* byte mode - intr enabled */	pmunit = unit;			/* save for pmrwintr */	pmbn = bn;			/* pmwdsect - block 0 is special */	pmma = addr;			/* save for pmrwintr */	pmierror = 0;			/* clear for pmrwintr */	pmstate = rflag ? DREADING : DWRITING;	tmp = spl2();	hwaddr->cmdreg = rflag ? PMREAD : PMWRITE;	/* start r/w */	while (pmstate != IDLING)	;	splx(tmp);	if (pmierror) {		if (pmierror & PMPERROR)			printf("parity error\n");		if (pmierror & 0x40)			printf("previous error\n");		if (tmp = (pmierror & ~PMPERROR))			printf("error code 0x%x\n", tmp);		printf("pmdrw: %s failed on ", rflag?"read":"write");		goto derr;	}	return(0);derr:	printf("card %d, bn %d, addr 0x%x\n", unit, bn, addr);	return(-1);}/* interrupt on card 0 */pm0intr(){	if ((pmunit != 0) || (pmstate == IDLING)) {		register struct pm_base *addr;		printf("unexpected interrupt, unit 0, pmunit %d\n", pmunit);		addr = pmBWaddr(pmaddr(0));		addr->cmdreg = 0;	/* clear spurious intr (clrb) */	} else if (pmstate == INITING)	/* initialization */		pmiintr();	else if (pmstate == TCONT)	/* tape control */		pmtintr();	else if (pmstate == PMPKTXFER)		pmxintr();	else	/* pmstate is READING, WRITING, RFLUSH or WFLUSH */		pmrwintr();	asm("unlk a6");	asm("rte");}/* interrupt on card 1 */pm1intr(){	if ((pmunit != 1) || (pmstate == IDLING)) {		register struct pm_base *addr;		printf("unexpected interrupt, unit 1, pmunit %d\n", pmunit);		addr = pmBWaddr(pmaddr(1));		addr->cmdreg = 0;	/* clear spurious intr (clrb) */	} else if (pmstate == INITING)	/* initialization */		pmiintr();	else if (pmstate == TCONT)	/* tape control */		pmtintr();	else if (pmstate == PMPKTXFER)		pmxintr();	else	/* pmstate is READING, WRITING, RFLUSH or WFLUSH */		pmrwintr();	asm("unlk a6");	asm("rte");}/* interrupt on card 2 */pm2intr(){	if ((pmunit != 2) || (pmstate == IDLING)) {		register struct pm_base *addr;		printf("unexpected interrupt, unit 2, pmunit %d\n", pmunit);		addr = pmBWaddr(pmaddr(2));		addr->cmdreg = 0;	/* clear spurious intr (clrb) */	} else if (pmstate == INITING)	/* initialization */		pmiintr();	else if (pmstate == TCONT)	/* tape control */		pmtintr();	else if (pmstate == PMPKTXFER)		pmxintr();	else	/* pmstate is READING, WRITING, RFLUSH or WFLUSH */		pmrwintr();	asm("unlk a6");	asm("rte");}/* * interrupt on read/write operation */pmrwintr(){	register struct pm_base *hwaddr;	register status;	hwaddr = pmBWaddr(pmhwbase);	/* byte mode - waiting */	if ((hwaddr->status & CMD_DONE) == 0) {	/* block transfer interrupt */		if (!pmcblkcnt)		/* 2nd interrupt */			goto done;		/* 1st interrupt */		pmcblkcnt--;		switch (pmstate) {			case DREADING:				pmrdsect();	/* sets pmierror for parity */				break;			case DWRITING:				pmwdsect();				break;			case TREADING:				pmrtsect();				break;			case TWRITING:				pmwtsect();				break;		}		hwaddr->cmdreg = PMCBTI;	/* clear block transfer intr */		hwaddr = pmBWIaddr(pmhwbase);	/* setup for next intr */		status = hwaddr->status;	} else {		/* cmd completed (error) */		pmierror |= 0x40;	done:#ifdef notdef		if (pmierror) {			if (pmstate == READING)				pmrflush();			else				pmwflush();			hwaddr->cmdreg = PMCBTI;	/* clear block transfer intr */			hwaddr = pmBWIaddr(pmhwbase);	/* setup for next intr */			status = hwaddr->status;		}#endif notdef		if (status = pmackcc(hwaddr)) {	/* ack intr */			if ((pmstate==DREADING) && (pmresults[0] == PMECCERR))				/* ECC err requiring scavenger write */				status = PMECCERR;			else if (pmresults[0] == PMDTIMOUT)				/* dev timeout - they reissue read/write */				status = PMDTIMOUT;		}		pmierror |= status;		if (pmierror) {			pmcinit(pmunit);		}		pmstate = IDLING;	}}/* * interrupt on initialize (read drive parameters) operation */pmiintr(){	register struct pm_base *addr;	addr = pmBWaddr(pmhwbase);	/* byte mode - waiting */	if (pmierror = pmackcc(addr)) {	/* ack intr */		printf("pmiintr: read dev parms failed\n");	}	pmstate = IDLING;}/* * read block to memory at pmma from disk * from PRIAMASM.TEXT - READ_SECT */pmrdsect(){	register struct pm_base *hwaddr;	register short *waddr;	register i;	if ((long)pmma & 0x1) {		/* cannot handle odd memory addr */		printf("odd addr=0x%x\n", pmma);		pmierror = 1;		pmstate = IDLING;		return;	}	hwaddr = pmBWaddr(pmhwbase);	/* byte mode - waiting */	while ((hwaddr->status & DTREQ) == 0)	;	hwaddr = pmPaddr(pmhwbase);	/* parity checking enabled */	waddr = (short *)pmma;	*waddr = hwaddr->data;		/* start read of first word */	i = 12;	do {				/* read and throw away header */		DELAY();		*waddr = hwaddr->data;	} while ( --i );		/* read 24-byte header */	waddr = (short *)pmma;	i = 255;	do {		DELAY();		*waddr++ = hwaddr->data;	} while ( --i );		/* read 510 bytes */	hwaddr = pmNaddr(hwaddr);	/* read last 2 avoiding device cycle */	*waddr = hwaddr->data;	hwaddr = pmpaddr(pmunit);	/* parity in lo select space */	pmierror |= (hwaddr->parity & PMPERROR);}/* * write block from memory at pmma from disk * from PRIAMASM.TEXT - WRITE_SECT */pmwdsect(){	register struct pm_base *hwaddr;	register short *waddr;	register i;	if ((long)pmma & 0x1) {		/* cannot handle odd memory addr */		printf("odd addr=0x%x\n", pmma);		pmierror = 1;		pmstate = IDLING;		return;	}	if (pmbn == 0)		/* boot block 0 gets boot ID in header */		pmhdr.pm_fileid = FILEID;	else		pmhdr.pm_fileid = 0;	hwaddr = pmBWaddr(pmhwbase);	/* byte mode - waiting */	while ((hwaddr->status & DTREQ) == 0)	;	hwaddr = pmhwbase;	waddr = (short *)&pmhdr;	i = HDRSIZE / sizeof(short);	do {		DELAY();		hwaddr->data = *waddr++;	} while ( --i );		/* write 24 byte header */	waddr = (short *)pmma;	i = 256;	do {		DELAY();		hwaddr->data = *waddr++;	} while ( --i );		/* write 512 bytes */}/* * after error flush controller with dummy read to complete request from disk * from PRIAMASM.TEXT - READ_FLUSH */pmrflush(){	register struct pm_base *hwaddr;	register short tmp;	register i;	hwaddr = pmBWaddr(pmhwbase);	/* byte mode - waiting */	while ((hwaddr->status & DTREQ) == 0)	;	hwaddr = pmPaddr(pmhwbase);	/* parity checking enabled */	tmp = hwaddr->data;		/* start read of first word */	i = 267;	do {		DELAY();		tmp = hwaddr->data;	} while ( --i );		/* read 534 bytes */	hwaddr = pmNaddr(hwaddr);	/* read last 2 avoiding device cycle */	tmp = hwaddr->data;}/* * after error flush controller to complete request from disk * from PRIAMASM.TEXT - WRITE_FLUSH */pmwflush(){	register struct pm_base *hwaddr;	register i;	register short tmp = 0;	hwaddr = pmBWaddr(pmhwbase);	/* byte mode - waiting */	while ((hwaddr->status & DTREQ) == 0)	;	hwaddr = pmhwbase;	i = 268;	do {		DELAY();		hwaddr->data = tmp;	} while ( --i );		/* write 536 bytes */}/* * streaming tape functions */char pmcmd[20];pmtstrategy(ip, func)register struct iob *ip;{	register unit = ip->i_unit;	register resid;	int offset;	caddr_t addr;	if (unit >= NPM)		return(-1);	pmhwbase = pmaddr(unit);	/* base address for this card */	resid = ip->i_cc;tloop:	if (resid < PBSIZE)		goto tout;	offset = ip->i_cc - resid;	addr = ip->i_ma + offset;	if (pmtrw(unit, func&F_READ, addr) != 0)		return(-1);	resid -= PBSIZE;	goto tloop;tout:	if (resid != 0)		printf("pmtstrategy: resid=%d\n", resid);	return(ip->i_cc - resid);}pmtrw(unit, rflag, addr)register unit;register rflag;register caddr_t addr;{	register struct pm_base *hwaddr;	register int tmp;	hwaddr = pmBWaddr(pmhwbase);	/* byte mode - waiting */	if (pmwaitrf(hwaddr)) {		printf("pmtrw: timeout before setting %s parameters ",			rflag?"read":"write");		goto terr;	}/*****/	hwaddr->p0 = (TAPE << 4)| unit;	/* for tape operations */	hwaddr->p3 = 0;			/* most sig. byte */	hwaddr->p2 = 0;			/* middle byte */	hwaddr->p1 = 0;			/* least sig. byte */	hwaddr->p4 = 1;			/* operation count = 1 sector */	hwaddr->p5 = 0;	pmcblkcnt = 1;	hwaddr = pmBIaddr(pmhwbase);	/* byte mode - intr enabled */	pmunit = unit;			/* save for pmrwintr */	pmma = addr;			/* save for pmrwintr */	pmierror = 0;			/* clear for pmrwintr */	pmstate = rflag ? TREADING : TWRITING;	tmp = spl2();	hwaddr->cmdreg = rflag ? PMNREAD : PMNWRITE;	/* start r/w */	while (pmstate != IDLING)	;	splx(tmp);	if ((pmierror = pmresults[0] & 0x3f)) {		if (pmierror == 4)			printf("pmtrw: READ failure due to EOF being encountered\n");		else if (pmierror == 5)			printf("pmtrw: WRITE error due to end of tape\n");		else if (pmierror == 0x14) {			switch (pmresults[1]) {				case 0: case 1: printf("Unrecoverable data error ");						break;				case 2: printf("No recorded data error ");					break;				case 3: printf("Error in positioning tape deck ");					break;				case 4: printf("QIC status error ");					break;				case 5: printf("Tape Drive reset ");					break;				case 6: printf("Operation aborted ");					break;				case 7: printf("Command sequence error ");					break;				case 8: printf("No tape in the drive ");					break;				case 9:				default: printf("Undefined command ");			}			printf("pmtrw: %s failed on ", rflag?"read":"write");		}		goto terr;	}	return(0);terr:	printf("card %d, addr 0x%x\n", unit, addr);	return(-1);}abortpkt(){	register struct pm_base *hwaddr;	register int s;	hwaddr = pmBWaddr(pmhwbase);		/* byte mode - waiting */	if (pmwaitrf(hwaddr)) {		printf("abortpkt: timeout before sending abort command\n");		return;	}	hwaddr->p0 = 0;				/* packet #, ALWAYS 0 */	hwaddr = pmBIaddr(pmhwbase);		/* byte mode - interrupt */	pmstate = PMPKTXFER;	pmxstate = ABORT;	pmierror = 0;				/* for interrupt routine */	s = spl2();	hwaddr->cmdreg = PMPKTABRT;	while (pmstate != IDLING)	;	splx(s);}pmioctl(unit, cmd, parm)int unit, cmd, parm;{	register struct pm_base *hwaddr;	register int s;	if (unit >= NPM)		return(-1);	pmhwbase = pmaddr(unit);		/* base address for this card */	hwaddr = pmBWaddr(pmhwbase);		/* byte mode - waiting */	if (pmwaitrf(hwaddr)) {		printf("pmioctl: timeout before setting %s parameters ", pmcmd);		goto err;	}	hwaddr->p0 = unit | (TAPE << 4);	/* unit # */	if (cmd == PMADVANCE)			/* file mark count */		hwaddr->p4 = parm;	hwaddr = pmBIaddr(pmhwbase);		/* byte mode - interrupt */	pmstate = TCONT;	pmierror = 0;				/* for interrupt routine */	s = spl2();	hwaddr->cmdreg = cmd;	while (pmstate != IDLING)	;	splx(s);	if ((pmierror = pmresults[0] & 0x3f)) {		if (pmierror == 4)			printf("pmtioctl: READ failure due to EOF being encountered\n");		else if (pmierror == 5)			printf("pmtioctl: WRITE error due to end of tape\n");		else if (pmierror == 0x14) {			printf("pmioctl: ");			switch (pmresults[1]) {				case 0: case 1: printf("Unrecoverable data error ");						break;				case 2: printf("No recorded data error ");					break;				case 3: printf("Error in positioning tape deck ");					break;				case 4: printf("QIC status error ");					break;				case 5: printf("Tape Drive reset ");					break;				case 6: printf("Operation aborted ");					break;				case 7: printf("Command sequence error ");					break;				case 8: printf("No tape in the drive ");					break;				case 9:				default: printf("Undefined command ");			}		}		goto err;	}	return(0);err:	printf("card %d\n", unit);	return(-1);}/* * interrupt on tape control operations */pmtintr(){	register struct pm_base *addr;	addr = pmBWaddr(pmhwbase);	/* byte mode - waiting */	(void)pmackcc(addr);	pmstate = IDLING;}/* * Interrupt on packet operations */pmxintr(){	register struct pm_base *addr;	register char status;	register cnt;	addr = pmBWaddr(pmhwbase);	/* byte mode - waiting */	switch (pmxstate) {		case ISSUE:		/* intr to transfer packet */			cnt = TIMELIMIT;			while ((addr->status & DTREQ) == 0 && --cnt) ;			if(cnt <= 0) {				pmxstate = RESUME;				goto resume;			}			{		/* send packet */				register char *cp = (char *)&dcparms;				register i = sizeof(struct datacopyparms);				do {					addr->pdata = *cp++;				} while ( --i );			}			(void)pmackdt(addr);	/* Apple's code doesn't check */	wait:		pmxstate++;	/* not done yet - wait for next intr */			return;		case ABORT:		/* intr after abort command */			 (void)pmackcc(addr);			 if(pmresults[0] & 0x30) {				reslt.errcode = -1;				break;			}			pmxstate = RESUME;			goto getstat;	/* get packet status block */		case RESUME:		/* intr after packet complete */	resume:		 (void)pmackcc(addr);			if(pmresults[0] != 0x8 && pmresults[0] != 0x28) {				if(pmresults[0] == 0x29 && pmresults[1] == 0x2F)					printf("Resume timeout error, start again\n");				reslt.errcode = -1;				break;			}	getstat:			(void)pmwaitrf(addr);			addr->p0 = 0;	/* packet ID = 0 */			addr = pmBIaddr(pmhwbase);			addr->cmdreg = PMPKTRST;/* read packet status */			goto wait;		case RPSTAT:		/* intr when status can be read */			if ((addr->status & DTREQ) == 0)				goto ackcc;	/* if not waiting for data */			{		/* read packet status */				register char *cp = (char *)&dcstat;				register i = sizeof(struct pmfmtstat);				do {					*cp++ = addr->pdata;				} while ( --i );			}			(void)pmackdt(addr);	/* Apple's code doesn't check */			goto wait;		case RPSTAT2:		/* intr after read packet status */	ackcc:		(void)pmackcc(addr);			if(pmresults[0] & 0x30) {				reslt.errcode = -1;				break;			}			if((reslt.errcode = dcstat.pm_pstate) == 0x0D 						|| reslt.errcode == 0x03)				break;			else reslt.errcode = -1;		}		pmstate = IDLING;}/* * read block to memory at pmma from tape * from ARCHIVEASM.TEXT - READ_SECT */pmrtsect(){	register struct pm_base *hwaddr;	register short *waddr;	register i;/*	if ((long)pmma & 0x1) {		/* cannot handle odd memory addr		printf("odd addr=0x%x\n", pmma);		pmierror = 1;		pmstate = IDLING;		return;	}*/	hwaddr = pmBWaddr(pmhwbase);	/* byte mode - waiting */	while ((hwaddr->status & DTREQ) == 0)	;	hwaddr = pmPaddr(pmhwbase);	/* parity checking enabled */	waddr = (short *)pmma;	*waddr = hwaddr->data;		/* start read of first word */	i = 255;	do {		DELAY();		*waddr++ = hwaddr->data;	} while ( --i );		/* read 510 bytes */	hwaddr = pmNaddr(hwaddr);	/* read last 2 avoiding device cycle */	*waddr = hwaddr->data;	hwaddr = pmpaddr(pmunit);	/* parity in lo select space */	pmierror = pmresults[0];}/* * write block from memory at pmma from tape * from ARCHIVEASM.TEXT - WRITE_SECT */pmwtsect(){	register struct pm_base *hwaddr;	register short *waddr;	register i;/*	if ((long)pmma & 0x1) {		/* cannot handle odd memory addr		printf("odd addr=0x%x\n", pmma);		pmierror = 1;		pmstate = IDLING;		return;	}*/	hwaddr = pmBWaddr(pmhwbase);	/* byte mode - waiting */	while ((hwaddr->status & DTREQ) == 0)	;	hwaddr = pmhwbase;	waddr = (short *)pmma;	i = 256;	do {		DELAY();		hwaddr->data = *waddr++;	} while ( --i );		/* write 512 bytes */}/* * wait for register file not busy * from PRIAMASM.TEXT and PRIAMCARDASM.TEXT - WAITRF */pmwaitrf(addr)register struct pm_base *addr;{	register i;	i = TIMELIMIT;	while ((addr->status & ISR_BUSY) != 0) {		if (--i)			continue;		return(-1);	}	return(0);}/* * acknowledge command completion * from PRIAMASM.TEXT and PRIAMCARDASM.TEXT - ACKCC */pmackcc(addr)register struct pm_base *addr;	/* pmBWaddr */{	register i;	i = TIMELIMIT;	while ((addr->status & CMD_DONE) == 0) {	/* wait for cmd done */		if (--i)			continue;		printf("pmackcc: timeout waiting for CMD_DONE\n");		return(-1);	}	pmresults[0] = addr->r0;	/* transaction status */	pmresults[1] = addr->r1;	pmresults[2] = addr->r2;	pmresults[3] = addr->r3;	pmresults[4] = addr->r4;	pmresults[5] = addr->r5;	addr->cmdreg = 0;		/* send command acknowledge (clrb) */	i = TIMELIMIT;	while ((addr->status & CMD_DONE) != 0) {	/* wait 'til reset */		if (--i)			continue;		printf("pmackcc: timeout waiting for CMD_DONE reset\n");		return(-1);	}	return(pmresults[0] & PMCTYPE);	/* error completion type */}/* * acknowledge data transfer * from PRIAMASM.TEXT - ACKDT * Their code does not check the return value, so who knows what'll happen * if it times out? */pmackdt(addr)register struct pm_base *addr;	/* pmBWaddr */{	register i;	addr->cmdreg = PMCBTI;		/* clear block transfer intr */	i = TIMELIMIT;	while ((addr->status & BTR_INT) != 0) {		if (--i)			continue;		printf("Unix pmackdt: timeout\n");		return(-1);	}	addr = pmIaddr(addr);		/* pmBWIaddr */	i = addr->status & BTR_INT;	/* setup for next intr */	return(0);}