|
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 - download
Length: 17937 (0x4611) Types: TextFile Notes: UNIX file Names: »TP1.C«
└─⟦f27320a65⟧ Bits:30001972 Commodore 900 hard disk image with partial source code └─⟦2d53db1df⟧ UNIX Filesystem └─ ⟦this⟧ »frankh/src/tape_drive/TP1.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> #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); }