|
|
DataMuseum.dkPresents historical artifacts from the history of: Commodore CBM-900 |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about Commodore CBM-900 Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - download
Length: 12615 (0x3147)
Types: TextFile
Notes: UNIX file
Names: »hd.c«
└─⟦f27320a65⟧ Bits:30001972 Commodore 900 hard disk image with partial source code
└─⟦2d53db1df⟧ UNIX Filesystem
└─⟦this⟧ »sys/z8001/drv/hd.c«
/* (-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) */
/*
* DTC 535AS hard disk & floppy disk driver for the Commodore Z8000M.
* This is yet another sasi driver with complications for funny hardware.
* Added 5.25" floppy support....
*
* LUN 0 = hard disk, LUNs 1 and 2 = floppy disk.....
* October, 1983.
*/
/*
* default drives are 8" floppy and full height winchester.
* to use 5.25" floppy, define FIVEINCH.
* to use half height wini, define HALFHIGH.
*/
#include <coherent.h>
#include <buf.h>
#include <con.h>
#include <stat.h>
#include <uproc.h>
#include <errno.h>
int hdload();
int hdopen();
int hdread();
int hdwrite();
int hdblock();
int hdintr();
int nulldev();
int nonedev();
CON hdcon = {
DFBLK|DFCHR, /* Flags */
6, /* Major index */
hdopen, /* Open */
nulldev, /* Close */
hdblock, /* Block */
hdread, /* Read */
hdwrite, /* Write */
nonedev, /* Ioctl */
nulldev, /* Powerfail */
nulldev, /* Timeout */
hdload, /* Load */
nulldev /* Unload */
};
/*
* DTC port offsets...
*/
#define HDIO 0x0600 /* Data Register (word mode) */
#define HDCS 0x0641 /* command & status port */
#define HDRESET 0x06c0 /* Reset (WO) */
#define HDSEL 0x0680 /* Select pulse (WO) */
#define HDMASK 0x06c0 /* Mask (WO) */
#define HDSSTAT 0x0680 /* SASI status lines (RO) */
#define HDREAD 0x08 /* Read */
#define HDWRITE 0x0A /* Write */
#define HDRSS 0x03 /* Request status */
#define HDSMSG 0x02 /* Message phase */
#define HDSREQ 0x08 /* Request from Xebec */
#define HDSIO 0x10 /* In/out */
#define HDSCD 0x04 /* Command/data */
#define HDSBUSY 0x01 /* Busy */
#define HDSERR 0x02 /* Error bit */
#define HDIRQ 0xC0 /* base of hd interrupt vectors */
#ifdef FIVEINCH
char fiveinch = 1;
#endif
#ifdef HALFHIGH
char halfhigh = 1;
#endif
/*
* This structure holds the disk parameters for the disk drive
* being used on the controller. As drives are added, the hdload()
* routine will have to be modified to accomodate them.
*/
struct hdp {
unsigned char hdp_idc[10]; /* Data block for init hd cmd */
unsigned char hdp_control; /* Control byte */
long hdp_size; /* Size of device, blocks */
int hdp_nparts; /* Number of minor partitions */
} hdp[] = {
#if HALFHIGH
{ { 1, 1, 0, 1, 2, 98, 0, 0, 0, 0 }, 0x00, 20672, 5 },
#else
{ { 1, 1, 0, 3, 1, 49, 0, 0, 0, 0 }, 0x00, 20672, 5 },
#endif
#if FIVEINCH
{ { 31, 31, 39, 35, 102, 51, 11, 0x80, 0, 0 }, 0x00, 640, 1 }
#else
{ { 0,0,0,0,0,0,0,0,0,0 }, 0x00, 500, 1 } /* floppy params */
#endif
};
/*
* Minor # bits: 210 partition or f.s. number
* 43 drive number
* 765 drive type
*/
#define drive(n) ((minor((n))>>3)&0x03)
#define partition(n) (minor((n))&0x07)
#define drivetype(n) (minor((n))>>5)
struct mintab {
long minbase;
long minsize;
} mintab[] = {
0, 5168,
5168, 5168,
10336, 5168,
15504, 5168,
0, 20672
};
/*
* Per disk controller data.
* Only one controller; no more, no less.
*/
struct hd {
BUF *hd_actf; /* Link to first */
BUF *hd_actl; /* Link to last */
paddr_t hd_addr; /* DMA address */
long hd_secn; /* Sector # */
int hd_nsec; /* # of sectors */
char hd_unit; /* Unit # */
char hd_busy; /* Busy flag */
char hd_iswr; /* Is a write flag */
} hd;
BUF hdbuf; /* For raw I/O */
struct hdp *hdparm(); /* For C */
/*
* Load. The controller is
* reset and the interrupt vector is
* grabbed. The drive characteristics are
* set up at this time.
*/
hdload()
{
register int i;
register int j;
register int status;
unsigned char cmd[6];
out(HDRESET, 0);
out(HDRESET, 1);
cmd[0] = 0xc2; /* assign drive params for hd */
cmd[1] = 0<<5; /* unit # */
cmd[2] = cmd[3] = cmd[4] = cmd[5] = 0;
hdsendcmd(cmd, 0);
while ((in(HDSSTAT) & 0x1F) != 0x16)
;
for (i = 0; i < 10; i += 2)
out(HDIO, (hdp[0].hdp_idc[i]<<8) | hdp[0].hdp_idc[i+1]);
status = hdstatus();
for (j = 1; j <= 2; j++) {
cmd[0] = 0xC0; /* define floppy track format */
cmd[1] = j<<5; /* unit # */
cmd[2] = cmd[3] = cmd[4] = 0;
#if FIVEINCH
cmd[5] = 0x8B; /* DS, DD, 512 bytes/sector */
#else
cmd[5] = 0x00; /* SS, SD, 128 bytes/sector */
#endif
hdsendcmd(cmd, 0);
status |= hdstatus();
#if FIVEINCH
cmd[0] = 0xC0; /* turn off double step function */
cmd[1] = j<<5;
cmd[2] = 1;
cmd[3] = cmd[4] = cmd[5] = 0;
#else
cmd[0] = 0xC1; /* define floppy drive type */
cmd[1] = j<<5; /* unit # */
cmd[2] = cmd[3] = cmd[5] = 0;
cmd[4] = 2; /* SA800 - 8 ms step rate */
#endif
hdsendcmd(cmd, 0);
status |= hdstatus();
#if FIVEINCH
cmd[0] = 0xc2; /* assign drive params for floppy */
cmd[1] = j<<5; /* unit # */
cmd[2] = cmd[3] = cmd[4] = cmd[5] = 0;
hdsendcmd(cmd, 0);
while ((in(HDSSTAT) & 0x1F) != 0x16)
;
for (i = 0; i < 10; i += 2)
out(HDIO, (hdp[1].hdp_idc[i]<<8) | hdp[1].hdp_idc[i+1]);
status |= hdstatus();
#endif
}
cmd[0] = 1; /* recal drives */
for (i = 1; i < 6; i++)
cmd[i] = 0;
hdsendcmd(cmd, 0); /* for LUN 0 (hard disk) */
status |= hdstatus();
cmd[1] = 1<<5;
hdsendcmd(cmd, 0); /* for LUN 1 (floppy disk) */
hdstatus(); /* ignore errors */
cmd[1] = 2<<5;
hdsendcmd(cmd, 0); /* for LUN 2 (floppy disk) */
hdstatus(); /* ignore errors */
if ((status & HDSERR) != 0)
panic("hdload()");
for (i = 0; i < 64; i += 2)
setivec(HDIRQ+i, hdintr);
}
/*
* Open routine.
* Check that the minor is
* in range.
*/
hdopen(dev, mode)
dev_t dev;
{
if (drive(dev) > 2
|| partition(dev) >= hdparm(drive(dev))->hdp_nparts) {
u.u_error = ENXIO;
return;
}
}
/*
* The read routine just calls
* off to the common raw I/O processing
* code, using a static buffer header in
* the driver.
*/
hdread(dev, iop)
dev_t dev;
IO *iop;
{
ioreq(&hdbuf, iop, dev, BREAD, BFIOC|BFBLK|BFRAW);
}
/*
* The write routine is just like the
* read routine, except that the function code
* is write instead of read.
*/
hdwrite(dev, iop)
dev_t dev;
IO *iop;
{
ioreq(&hdbuf, iop, dev, BWRITE, BFIOC|BFBLK|BFRAW);
}
/*
* Queue a block to the disk. Make
* sure that the count is modulo 512, and
* that the transfer is within the partition
* of the disk.
*/
hdblock(bp)
register BUF *bp;
{
register struct hdp *hdp;
hdp = hdparm(drive(bp->b_dev));
if ((bp->b_count&0x1FF) != 0
|| bp->b_bno+(bp->b_count>>9) > mintab[partition(bp->b_dev)].minsize) {
bp->b_flag |= BFERR;
bdone(bp);
return;
}
bp->b_actf = NULL;
if (hd.hd_actf == NULL)
hd.hd_actf = bp;
else
hd.hd_actl->b_actf = bp;
hd.hd_actl = bp;
if (hd.hd_busy == 0) {
hddequeue();
hdstart();
}
}
/*
* Pull some work from the
* disk queue. True return if
* successful.
*/
hddequeue()
{
register BUF *bp;
if ((bp=hd.hd_actf) == NULL)
return (0);
hd.hd_iswr = 0;
if (bp->b_req == BWRITE)
++hd.hd_iswr;
hd.hd_unit = drive(bp->b_dev);
hd.hd_addr = bp->b_paddr;
hd.hd_secn = mintab[partition(bp->b_dev)].minbase+bp->b_bno;
hd.hd_nsec = bp->b_count >> 9;
return (1);
}
/*
* Start or restart the disk.
*/
hdstart()
{
register struct hdp *hdp;
register long dsecn;
#if FIVEINCH
register int dtrkn;
register int dheadn;
#endif
char cmd[6];
hdp = hdparm(hd.hd_unit);
#if FIVEINCH
dsecn = hd.hd_secn;
if (hd.hd_unit != 0) { /* floppy disk ? */
dheadn = 0;
if (dsecn >= 320L) { /* 8 sectors * 40 tracks */
dsecn -= 320L;
dheadn = 1;
};
dtrkn = 2 * (dsecn / 8) + dheadn; /* convert ibm# to DTC */
dsecn %= 8; /* get sector offset on track */
dsecn += (dtrkn * 8); /* add track offset into LAD */
}
#else
dsecn = (hd.hd_unit == 0) ? hd.hd_secn : (hd.hd_secn<<2);
#endif
cmd[0] = hd.hd_iswr ? HDWRITE : HDREAD; /* Opcode */
cmd[1] = (hd.hd_unit<<5) | (dsecn>>16); /* Unit and LAD 2 */
cmd[2] = ((int) dsecn) >> 8; /* LAD 1 */
cmd[3] = ((int) dsecn); /* LAD 0 */
#if FIVEINCH
cmd[4] = 1;
#else
cmd[4] = (hd.hd_unit == 0) ? 1 : 4; /* 512 byte block */
#endif
cmd[5] = hdp->hdp_control; /* misc controller bits */
hdintenable(); /* let in the lions... */
hdsendcmd(cmd, 0);
++hd.hd_busy; /* Say we are running */
}
/*
* Interrupt routine.
* Lock out interrupts, then
* wake up the top level, if the
* state is ok.
*/
hdintr(id)
{
register BUF *bp;
register int s;
register int status;
#if VERBOSE
printf("hd:%x,%d,b=%d%c\n", id, hd.hd_busy, (int)hd.hd_secn,
(hd.hd_iswr != 0) ? 'W' : 'R');
#endif
id &= 0x1F; /* mask off nonsense */
if (hd.hd_busy != 0) {
bp = hd.hd_actf;
hd.hd_busy = 0;
if (id == 0x06) /* ready to read */
readblk(HDIO, pfix(ES, hd.hd_addr), 512);
else if (id == 0x16)
writeblk(HDIO, pfix(ES, hd.hd_addr), 512);
else {
s = sphi();
hdstatus();
hdintenable();
spl(s);
return;
}
s = sphi();
status = hdstatus();
if ((status&HDSERR)!=0 && hderror()!=0) {
bp->b_flag |= BFERR;
hd.hd_actf = bp->b_actf;
bdone(bp);
if (hddequeue() != 0)
hdstart();
} else if (--hd.hd_nsec == 0) {
hd.hd_actf = bp->b_actf;
bdone(bp);
if (hddequeue() != 0)
hdstart();
} else {
++hd.hd_secn;
hd.hd_addr += 512;
hdstart();
}
spl(s);
}
}
/*
* This routine reads the
* status bytes at the end of a disk
* command. If an error is detected it
* does a read status command to find out
* if the error is a soft ECC error. It
* returns true if a "real" error happens.
*/
hderror()
{
register int i;
char cmd[6];
char status[4];
cmd[0] = HDRSS;
cmd[1] = hd.hd_unit<<5;
for (i=2; i<6; ++i)
cmd[i] = 0;
hdsendcmd(cmd, 0);
for (i=0; i<4; ++i) {
while ((in(HDSSTAT)&HDSREQ) != 0)
;
status[i] = inb(HDCS);
}
while ((in(HDSSTAT)&HDSREQ) != 0)
;
if ((inb(HDCS)&HDSERR) != 0) {
printf("hd%d: error reading status\n", hd.hd_unit);
return (1);
}
if ((status[0]&0x3F) == 0x00) /* No error */
return (0);
if ((status[0]&0x3F) == 0x18) /* Soft ECC */
return (0);
printf("hd%d: ", hd.hd_unit);
if (hd.hd_iswr != 0)
printf("write");
else
printf("read");
printf(" error, sn=%d status=0x%x\n", (int)hd.hd_secn, status[0]&0x3F);
return (1);
}
/*
* This routine,
* given a unit number,
* returns a pointer to the "hdparm"
* block for this drive type.
*/
struct hdp *
hdparm(unit)
{
if (unit < 0 || unit > 2) {
printf("hd select error: unit %d\n", unit);
return(&hdp[0]);
} else
return ((unit == 0) ? &hdp[0] : &hdp[1]);
}
/*
* Send a command to the
* disk. The "masks" parameter gets
* ignored in this version of the driver.
*/
hdsendcmd(cmd, masks)
unsigned char cmd[6];
{
register int i;
while ((in(HDSSTAT)&HDSBUSY) == 0) /* Wait for Xebec not busy */
;
out(HDSEL, 0x01); /* Select pulse */
while ((in(HDSSTAT)&HDSBUSY) != 0) /* Wait for Xebec busy */
;
out(HDSEL, 0x00); /* Select off */
while ((in(HDSSTAT)&0x1F) != (HDSIO|HDSMSG))
;
for (i=0; i<6; ++i)
outb(HDCS, cmd[i]);
}
/*
* Enable interrupts on the host adapter.
*/
hdintenable()
{
out(HDMASK, 0x02);
}
/*
* transfer a 512 byte block of data to memory from the controller
* via programmed i/o. DMA would be much nicer....
*/
readblk(port, paddr, nb)
unsigned port;
paddr_t paddr;
unsigned nb;
{
#if !FIVEINCH /* only for 8" floppy */
do {
while ((in(HDSSTAT) & HDSREQ) != 0)
;
inwcopy(port, paddr, 2); /* read 1 word (2 bytes) */
paddr += 2; /* next word */
nb -= 2; /* decrement count */
} while (nb);
#else
while ((in(HDSSTAT) & HDSREQ) != 0)
;
inwcopy(port, paddr, nb);
#endif
}
/*
* transfer a 512 byte block of data to the controller from memory
* via programmed i/o.
*/
writeblk(port, paddr, nb)
unsigned port;
paddr_t paddr;
unsigned nb;
{
#if !FIVEINCH
do {
while ((in(HDSSTAT) & HDSREQ) != 0)
;
outwcopy(port, paddr, 2); /* write one word */
paddr += 2; /* next word */
nb -= 2;
} while (nb);
#else
while ((in(HDSSTAT) & HDSREQ) != 0)
;
outwcopy(port, paddr, nb);
#endif
}
/*
* Get and return status and wait
* for idle on the disk controller.
*/
hdstatus()
{
register int s;
while ((in(HDSSTAT) & 0x1F) != HDSMSG)
;
s = inb(HDCS);
while ((in(HDSSTAT) & 0x1f) != 0)
;
inb(HDCS); /* read message byte */
while ((in(HDSSTAT) & 0x1f) != 0x1f)
;
return (s);
}