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

⟦17127d800⟧ TextFile

    Length: 13261 (0x33cd)
    Types: TextFile
    Notes: UNIX file
    Names: »junkwd.c«

Derivation

└─⟦f27320a65⟧ Bits:30001972 Commodore 900 hard disk image with partial source code
    └─⟦2d53db1df⟧ UNIX V7 Filesystem
        └─ ⟦this⟧ »sys/z8001/drv/junkwd.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) */
/*
 * Driver for Western Digital hard disk controller for the Z8000HR.
 * 
 * Uses Commodore's SASI-like command block structure.
 *
 * Last Revision: January 29, 1985  (nrb)
 */

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

int	wdload();
int	wduload();
int	wdopen();
int	wdread();
int	wdwrite();
int	wdblock();
int	wdintr();
int	nulldev();
int	nonedev();
char	*pfix();			/* map phys addr to a char * */

CON	wdcon	= {
	DFBLK|DFCHR,			/* Flags */
	2,				/* Major index */
	wdopen,				/* Open */
	nulldev,			/* Close */
	wdblock,			/* Block */
	wdread,				/* Read */
	wdwrite,			/* Write */
	nonedev,			/* Ioctl */
	nulldev,			/* Powerfail */
	nulldev,			/* Timeout */
	wdload,				/* Load */
	wduload				/* Unload */
};

/*
 * Western Digital Controller port addresses
 */
#define	WDIO	0x0500			/* Data Register (word mode) */

#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	NSEC	17			/* number of sectors per track */
#define	WDVEC	0x80			/* base of wd interrupt vector */
#define	NDRIVES	2			/* max drives per controller */

typedef	struct	wdcmd {			/* 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 */
		} wdcmdop;
		unsigned long	c_lunaddr;	/* lun and whole address */
	} wdcmdu;
	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	wdcmdu.wdcmdop.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 wdbtab/sizeof wdbtab[0])

struct {
	unsigned long	bstart;		/* starting block # */
	unsigned long	bcount;		/* size in blocks */
} wdbtab[] = {
	0L,	5168L,
	5168L,	5168L,
	10336L,	5168L,
	15504L,	5168L,
	0L,	0L,
	0L,	0L,
	0L,	0L,
	0L,	0L,
	0L,	0L,
	0L,	0L,
	0L,	0L,
	0L,	0L,
	0L,	0L,
	0L,	0L,
	0L,	0L,
	0L,	20672L
};

/*
 * 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	wdp {
	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	wdparam = {			/* patch these values as needed: */
	0xF,				/* no options, 16 uS fast step */
	(4 << 4) | 1,			/* 4 heads, MSB of cyl = 1 */
	0x32,				/* # cyl = 0x132 = 306 */
	8,				/* precomp starts at cyl 128. */
	8,				/* reduced write cur. at cyl 128. */
	17				/* number of sectors per track */
};

/*
 * housekeeping
 */
typedef	struct wdunit {
	bool		wdu_active;	/* drive/controller busy */
	bool		wdu_dma_aligned;/* dma address is 512 aligned */
	struct wdunit	*wdu_forw;	/* next drive in controller chain */
	struct wdunit	*wdu_back;	/* last drive */
	BUF		*wdu_actf;	/* next BUF in drive chain */
	paddr_t		wdu_paddr;	/* physical address of buffer */
} WDU;

WDU	wdtab;				/* controller header */
WDU	wdutab[NDRIVES];		/* drive headers */
BUF	wdbuf;				/* 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 !!!
 */
extern	char	cmdblk[];
#ifndef	WDCMDBLKPADDR
#define	WDCMDBLKPADDR	0x20000L	/* default to 2|0000 based RAM */
#endif
#ifndef	WDBUFPADDR
#define	WDBUFPADDR	0x20400L	/* above cmd blk and ECC buffer */
#endif


/*
 * set up the driver and the actual controller. this involves setting the
 * drive parameters for the two drives.
 */
wdload()
{
	setivec(WDVEC, wdintr);
	wdsetparam();
}


wduload()
{

	clrivec(WDVEC);
}

/*
 * Open routine. Check that the minor # is in range.
 */
wdopen(dev, mode)
dev_t	dev;
{
	if (drive(minor(dev)) >= NDRIVES || pseudo(minor(dev)) >= NPSEUDO)
		u.u_error = ENXIO;
}

/*
 * perform a RAW read operation
 */
wdread(dev, iop)
dev_t	dev;
IO	*iop;
{
	ioreq(&wdbuf, iop, dev, BREAD, BFIOC|BFBLK|BFRAW);
}


/*
 * perform a RAW read operation
 */
wdwrite(dev, iop)
dev_t	dev;
IO	*iop;
{
	ioreq(&wdbuf, iop, dev, BWRITE, BFIOC|BFBLK|BFRAW);
}

/*
 * block I/O routine.
 */
wdblock(bp)
register BUF	*bp;
{
	register int	d;			/* drive # */
	register int	pd;			/* partition # */
	register long	bno;			/* block # */
	register int	s;			/* saved machine status */
	bool	wdstart();

	d = drive(minor(bp->b_dev));
	pd = pseudo(minor(bp->b_dev));
	bno = bp->b_bno;
#if	VERBOSE
	printf("wdblock: d=%d, pd=%d, bno=%d enter\n", d, pd, (int)bno);
	wdbufdump(bp);
#endif
	if (d >= NDRIVES || pd >= NPSEUDO || bno >= wdbtab[pd].bcount) {
		bp->b_flag |= BFERR;
		bdone(bp);
		return;
	}
	bp->b_bno += wdbtab[pd].bstart;
	s = sphi();
	disksort(&wdutab[d], bp, bp->b_bno);
	wdustart(d);
	while (!wdstart())
		;
	spl(s);
}

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

	if (wdutab[d].wdu_active)		/* active already ? */
		return;
	bp = wdutab[d].wdu_actf;
	if (bp == (BUF *)0)			/* any work to do ? */
		return;
	wdutab[d].wdu_active = TRUE;

	if (wdtab.wdu_forw != (WDU *)0)
		wdtab.wdu_back->wdu_forw = &wdutab[d];
	else
		wdtab.wdu_forw = &wdutab[d];
	wdtab.wdu_back = &wdutab[d];
	wdutab[d].wdu_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 wdiobuf which is aligned. If a read,
 * do the copy in the interrupt routine after the data is in the wdiobuf
 * buffer.
 * This is the price we have to pay for a poorly designed DMA controller.
 */
bool
wdstart()
{
	register BUF	*bp;
	register WDCMD	*cbp;
	register int	d;
	int	dma_error = FALSE;

	if (wdtab.wdu_active)
		return (TRUE);
	if (wdtab.wdu_forw == (WDU *)0)
		return (TRUE);
	bp = wdtab.wdu_forw->wdu_actf;
	wdtab.wdu_paddr = bp->b_paddr;
	d = drive(minor(bp->b_dev));
	wdtab.wdu_dma_aligned = TRUE;
	if ((wdtab.wdu_paddr&0x1FFL) != 0) {	/* make sure aligned 512 */
		wdtab.wdu_dma_aligned = FALSE;
		if (bp->b_flag&BFRAW) {
			printf("wd: non-aligned dma in RAW mode\n");
			++dma_error;
		} else if (bp->b_count > 512) {
			printf("wd: non-aligned multi-block dma error\n");
			++dma_error;
		}
	}
	if (dma_error) {
		bp->b_flag |= BFERR;
		bdone(bp);
		wdtab.wdu_forw->wdu_active = 0;
		wdtab.wdu_forw->wdu_actf = bp->b_actf;
		wdtab.wdu_forw = wdtab.wdu_forw->wdu_forw;
		wdustart(d);
		return (FALSE);
	}
	wdtab.wdu_active = TRUE;
	if (!wdtab.wdu_dma_aligned && bp->b_req == BWRITE) /* one block only! */
		kkcopy(pfix(0x3C, wdtab.wdu_paddr), cmdblk+0x400, 512);
	cbp = (WDCMD *) pfix(0x3C, WDCMDBLKPADDR); /* map onto cmd block */
	wdcmdclr(cbp);				/* clear out block */
	cbp->wdcmdu.c_lunaddr = bp->b_bno;
	if (d)
		cbp->wdcmdu.wdcmdop.c_lunhiaddr |= d << 5;
	cbp->c_blockcnt = bp->b_count >> 9;
	if (wdtab.wdu_dma_aligned) {
		cbp->c_highdma = wdtab.wdu_paddr >> 16;
		cbp->c_middma = wdtab.wdu_paddr >> 8;
		cbp->c_lowdma = wdtab.wdu_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
	wdcbdump(cbp);
#endif
	out(WDIO, 1);				/* wake up controller */
	return (TRUE);
}

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

	if (wdtab.wdu_active) {
		cbp = (WDCMD *) pfix(0x3C, WDCMDBLKPADDR); /* map onto cmd blk */
		bp = wdtab.wdu_forw->wdu_actf;
		d = drive(minor(bp->b_dev));
		if ((cbp->c_errorbits & 0x7F) == 0) {
			wdtab.wdu_forw->wdu_actf = bp->b_actf;
			if (!wdtab.wdu_dma_aligned)	/* one block only !! */
			    if (bp->b_req == BREAD)
			      kkcopy(cmdblk+0x400,pfix(0x3C,wdtab.wdu_paddr),512);
			bdone(bp);
		} else 
			wdharderr(bp, cbp);
		wdtab.wdu_forw->wdu_active = 0;
		wdtab.wdu_forw = wdtab.wdu_forw->wdu_forw;
		wdtab.wdu_active = 0;
		wdustart(d);
	}
	out(WDIO, 0);				/* reset IEO on DMA chip */
	while (!wdstart())
		;
}

/*
 * got an error - inform the user and try to recover.
 */
wdharderr(bp, cbp)
register BUF	*bp;
register WDCMD	*cbp;
{
	register int	d;
	register unsigned char *ucp;
	register int pd;

	pd = pseudo(minor(bp->b_dev));
	printf("wd: err=%x [ ", cbp->c_errorbits);
	for (d = 0, ucp = (unsigned char *)cbp; d < sizeof(WDCMD); ++d)
		printf("%x ", *ucp++);
	printf("] ");
	devmsg(bp->b_dev, " b=%d", (int)(bp->b_bno-wdbtab[pd].bstart));
	d = drive(minor(bp->b_dev));
	wdutab[d].wdu_actf = bp->b_actf;
	bp->b_flag |= BFERR;
	bdone(bp);
}

/*
 * clear out the command block pointed to by cbp.
 */
wdcmdclr(cbp)
WDCMD	*cbp;
{
	register unsigned char *ucp;
	register int i;

	ucp = (unsigned char *)cbp;
	for (i = 0; i < sizeof(WDCMD); ++i)
		*ucp++ = 0;
}

/*
 * 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 wdunit	*dp;
register BUF	*bp;
daddr_t	bno;
{
	register BUF	*last;
	register BUF	*next;


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

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

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

#if	VERBOSE
/*
 * dump a BUF struct to the terminal
 */
wdbufdump(bp)
register BUF *bp;
{
	printf("BUF: flag=%x dev=%x bno=%d 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);
}

/*
 * dump a command block to the terminal
 */
wdcbdump(cbp)
register WDCMD	*cbp;
{
	register int	d;
	register unsigned char *ucp;

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

/*
 * perform controller and drive initialization. note that the parameters
 * given to the controller are used for both hard disk drives.
 */
wdsetparam()
{
	register WDCMD *cbp;			/* cmd block pointer */
	register WDP *dpp;			/* drive param pointer */

	cbp = (WDCMD *) pfix(0x3C, WDCMDBLKPADDR); /* map onto cmd block */
	wdcmdclr(cbp);				/* clear out block */
	cbp->c_highdma = (WDBUFPADDR >> 16) & 0xFF;
	cbp->c_middma =  (WDBUFPADDR >> 8) & 0xFF;
	cbp->c_lowdma =  (WDBUFPADDR) & 0xFF;
	cbp->c_op = WDSDP;			/* set drive params */
	cbp->c_errorbits = 0xFF;		/* mark block as valid */
	dpp = (WDP *) pfix(0x3C, WDBUFPADDR);
	*dpp = wdparam;				/* to achieve DMA alignment */
	out(WDIO, 1);				/* strobe line to DMA part */
}