|
|
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: 16368 (0x3ff0)
Types: TextFile
Notes: UNIX file
Names: »TP.C«
└─⟦f27320a65⟧ Bits:30001972 Commodore 900 hard disk image with partial source code
└─⟦2d53db1df⟧ UNIX Filesystem
└─⟦this⟧ »frankh/src/tape_drive/TP.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) */
/*
*
*
*
*/
#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>
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) */
#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 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");
for (i = 0; i < NDRIVES; ++i)
tputab[i].tpu_active = 0;
tptab.tpu_active = 0;
/* setivec(WDVEC, tpintr); */
}
tpuload()
{
printf("tpuload: unload driver\n");
/* clrivec(WDVEC); */
}
/*
* 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()))
;
if (fh == 22)
tpintr();
spl(s);
}
/*
* 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;
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");
++dma_error;
} 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 (22);
}
/*
* 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())
;
}
/*
* 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);
}