/* floppy.c floppy driver for Miniframe. Tom Trebisky 6/4/90 ttrebisky@as.arizona.edu */ #define DISK_DMA_COUNT (short *) 0xc80000 /* r/w word count */ #define DISK_DMA_LADDR (short *) 0xc80002 /* wo low 16 bits */ #define DISK_DMA_UADDR_W (short *) 0xc80006 /* wo upper 5 bits */ #define DISK_DMA_UADDR_R (short *) 0xc80008 /* wo upper 5 bits */ /* Now here is a strange place to hide a bit. * In the slow communications status register, we have a bit * that goes low whenever a floppy drive is physically connected * to the the 34 pin connector, whether is is powered up, ready, or whatever. */ #define SC_STAT (short *) 0xc30008 #define SC_FDNP 0x01 #define FD_RESET_ON (short *) 0xc60020 #define FD_RESET_OFF (short *) 0xc60022 #define HD_RESET_ON (short *) 0xc60024 #define HD_RESET_OFF (short *) 0xc60026 #define FD_MOTOR_ON (short *) 0xc60028 #define FD_MOTOR_OFF (short *) 0xc6002A #define HD_DMA_ENABLE (short *) 0xc6002C #define FD_DMA_ENABLE (short *) 0xc6002E #define DISK_DMA_DISABLE (short *) 0xc60030 #define FD_SINGLE (short *) 0xc60032 #define FD_DOUBLE (short *) 0xc60034 #define DISK_BIU_RESET (short *) 0xc60036 /* the WD2797 FDC chip */ #define FD_COMMAND (short *) 0xc60010 #define FD_STATUS (short *) 0xc60010 #define FD_TRACK (short *) 0xc60012 #define FD_SECTOR (short *) 0xc60014 #define FD_DATA (short *) 0xc60016 /* bits in the WD2797 status register */ #define FD_NREADY 0x80 #define FD_WPROT 0x40 #define FD_HLOADED 0x20 #define FD_SEEKERR 0x10 #define FD_CRCERR 0x08 #define FD_TRACK0 0x04 #define FD_INDEX 0x02 #define FD_BUSY 0x01 /* these bits change for type II and III commands */ #define FD_RTYPE 0x20 /* on read, 1 = deleted data */ #define FD_RNF 0x10 /* record not found */ #define FD_LDATA 0x04 /* lost data */ #define FD_DRQ 0x02 /* the 8259 interrupt controller chip */ #define PIC_ICW (short *) 0xc90000 #define PIC_OCW (short *) 0xc90002 #define READ 1 #define WRITE 2 #define SINGLE 1 #define DOUBLE 2 /* Callan uses a "pseudo 8-inch format" with 77 cyl, 2 heads, 8 sectors * of 512 bytes, for 1232 blocks total (616K), actually 80 cyl could be used, * for a total of 640K. */ #define NTRACK 80 /* cylinders on a 720K floppy */ #define NIOTRACK 77 /* cylinders on a 720K floppy */ #define NSECTOR 8 /* sectors per track */ #define SSIZE 512 #define BUFLOC 0x20000 #ifdef NEVER char localbuf[SSIZE]; /* sector buffer */ #endif char *iobuf = (char *) BUFLOC; char *bigbuf = (char *) BUFLOC; char *prompt(); short density = DOUBLE; main() { int nio; int times; int track; register char *cp; printf("Floppy test starting\n"); iobuf = (char *) BUFLOC; for ( cp = iobuf; cp < &iobuf[SSIZE]; ) *cp++ = 0xae; printf("int is %d bytes\n",sizeof(int)); printf("io buffer is at: "); hex8(iobuf); putchar('\n'); fdinit(); for ( ;; ) { cp = prompt("floppy: "); if ( *cp == '\0' ) { printf("status: %x\n",(*FD_STATUS)&0xff); continue; } if ( *cp == 'm' ) /* m - motor on */ *FD_MOTOR_ON = 0; else if ( *cp == 'o' ) /* o - motor off */ *FD_MOTOR_OFF = 0; else if ( *cp == 'i' ) /* i - initialize */ fdinit(); else if ( *cp == 'r' ) { /* r - read sector */ sectorio(READ); nio = SSIZE; } else if ( *cp == 'w' ) /* w - write sector */ sectorio(WRITE); else if ( *cp == 'd' ) /* d - dump sector buffer */ vxdmp(iobuf,nio); else if ( *cp == 'x' ) { /* x - toggle density */ if ( density == SINGLE ) density = DOUBLE; else density = SINGLE; printf("density is now: %s\n", (density==SINGLE)?"SINGLE":"DOUBLE"); } else if ( *cp == 's' ) { /* s - seek */ ++cp; while ( *cp && *cp == ' ' ) ++cp; track = atol(cp); if ( track < 0 ) track = 0; if ( track > NTRACK-1 ) track = NTRACK-1; fdseek(track); } else if ( *cp == 'a' ) { /* a - seek test */ for ( times=0; times<1; ++times ) seektest(); } else if ( *cp == 'b' ) { /* b - read test */ readtest(); } else if ( *cp == 'c' ) { /* c - read/write test */ rwtest(); } else if ( *cp == 'e' ) { /* e - read track */ nio = read_track(bigbuf,20000,0); /* side 0 for now */ if ( nio > 0 ) { vxdmp(bigbuf,nio); printf("%d bytes read\n",nio); } /* } else if ( *cp == 'l' ) { printf("Snooping\n"); snoop((char *) 0x70000); snoop((char *) 0x70100); */ } else printf(" ?\n"); } } snoop(pat) char *pat; { register char *p; for ( p = (char *) 0; p < (char *) 0x80000; ++p ) { if ( check(p,pat, 256 ) ) { printf("Found first sector at "); hex8(p); putchar('\n'); } } } check(mem,pat,num) char *mem; char *pat; { register char *p, *q; p = mem; q = pat; while ( num-- ) if ( *p++ != *q++ ) return(0); return (1); } fdinit() { int stat; register timeout; /* *FD_RESET_ON = 0; */ *DISK_BIU_RESET = 0; /***/ *FD_RESET_OFF = 0; timeout = *FD_STATUS; /***/ *FD_MOTOR_ON = 0; if ( *SC_STAT & SC_FDNP ) printf("WARNING !! no drive or cable wrong !!\n"); for ( timeout=400000; timeout; --timeout ) if ( ! (*FD_STATUS & FD_NREADY) ) break; if ( density == SINGLE ) *FD_SINGLE = 0; else *FD_DOUBLE = 0; if ( ! timeout ) { fderr("Timeout: drive not ready",*FD_STATUS); return; } *FD_COMMAND = 0x0; /* restore */ fddone(); *FD_TRACK = 0; /***/ if ( ! ((stat = *FD_STATUS) & FD_TRACK0) ) fderr("Restore failed to get Track 0",stat); } sectorio(iotype) { int track, side, sector; register char *cp; cp = prompt("track (0-N)? "); track = atol(cp); if ( track < 0 ) track = 0; if ( track > NTRACK-1 ) track = NTRACK-1; cp = prompt("side (0,1)? "); side = atol(cp) & 1; cp = prompt("sector (0-N)? "); sector = atol(cp); if ( sector < 0 ) sector = 0; sector &= 0x1f; floppy (iobuf,iotype,sector,track,side); if ( iotype == READ ) vxdmp(iobuf,SSIZE); } readtest() { int track, side, sector; for ( track=0; track>1); *DISK_DMA_COUNT = nio; dmatmp = ((unsigned int) buf) >> 1; /* word address */ dmatmp &= 0xffff; #ifdef DEBUG printf("lower DMA address: "); hex4(dmatmp); putchar('\n'); #endif *DISK_DMA_LADDR = dmatmp; dmatmp = ((unsigned int) buf) >> 1; /* word address */ dmatmp >>= 16; #ifdef DEBUG printf("upper DMA address: "); hex4(dmatmp); putchar('\n'); #endif if ( io == READ ) *DISK_DMA_UADDR_R = dmatmp; else *DISK_DMA_UADDR_W = dmatmp; *FD_DATA = track; if ( track != curtrack ) { fdseek(track); curtrack = track; } *FD_DMA_ENABLE = 0; #ifdef DEBUG printf("Sector %d\n",sector); #endif *FD_SECTOR = sector + 1; if ( density == SINGLE ) *FD_SINGLE = 0; else *FD_DOUBLE = 0; /* using dmatmp as a scratch variable */ if ( io == READ ) dmatmp = 0x88; else dmatmp = 0xa8; if ( side ) dmatmp |= 0x02; #ifdef DEBUG printf("Command to floppy controller: "); hex2(dmatmp); putchar('\n'); #endif /* GO for it !! */ *FD_COMMAND = dmatmp; fddone(); stat = *FD_STATUS; #ifdef DEBUG printf("Done with floppy status: "); hex2(stat); putchar('\n'); #endif if ( stat & (FD_NREADY|FD_WPROT|FD_RNF|FD_CRCERR|FD_LDATA) ) fderr("IO error",stat); } read_track (buf,nio,side) char *buf; { int ocount; /* original count */ int stat; register unsigned dmatmp; *DISK_DMA_DISABLE = 0; *DISK_BIU_RESET = 0; ocount = -(nio>>1); *DISK_DMA_COUNT = ocount; dmatmp = ((unsigned int) buf) >> 1; /* word address */ *DISK_DMA_LADDR = dmatmp & 0xffff; dmatmp >>= 16; *DISK_DMA_UADDR_R = dmatmp; *FD_DMA_ENABLE = 0; dmatmp = (0xe0 | side<<1); *FD_COMMAND = dmatmp; fddone(); stat = *FD_STATUS; #ifdef DEBUG printf("Done with floppy status: "); hex2(stat); putchar('\n'); #endif ocount = *DISK_DMA_COUNT - ocount; printf("DMA moved %d words\n",ocount); if ( stat & (FD_NREADY|FD_WPROT|FD_RNF|FD_CRCERR|FD_LDATA) ) { fderr("IO error",stat); } return(ocount<<1); } #ifdef NEVER fdrestore() { int stat; *FD_COMMAND = 0x04; /* restore with verify */ fddone(); if ( (stat = *FD_STATUS) & (FD_NREADY|FD_SEEKERR|FD_CRCERR) ) fderr("Restore error",stat); } #endif seektest() { int t1 = 0; int t2 = NIOTRACK-1; while ( t1 <= t2 ) { printf("Seeking to track %d\n",t1); fdseek(t1++); printf("Seeking to track %d\n",t2); fdseek(t2--); } } fdseek(track) { int stat; #ifdef DEBUG printf("seeking to track %d\n",track); #endif *FD_DATA = track; if ( density == SINGLE ) *FD_SINGLE = 0; else *FD_DOUBLE = 0; *FD_COMMAND = 0x14; /* seek with verify */ fddone(); if ( (stat = *FD_STATUS) & (FD_NREADY|FD_SEEKERR|FD_CRCERR) ) fderr("Seek error",stat); } #define PICTIMEOUT 100000 /* was 400000 */ /* note: the "interrupt" is presented only on one read by the 8259 * when polling like this, you read 07 continuously, then once get * 0x87, then back to 07 as long as you care to keep reading. * Also note that on this machine, when reading an 8 bit register * on a 16 bit bus, the upper 8 bits gets set to ones, so you get * 0xff87 (or 0xff07, it is not sign extension). */ fddone() { long timeout; register picstat; #ifdef DEBUG int picold = -1; printf("begin fddone (wait for IO)\n"); #endif *PIC_OCW = 0x7f; timeout = PICTIMEOUT; while ( timeout-- ) { *PIC_ICW = 0x0e; picstat = *PIC_ICW; #ifdef DEBUG if ( picold == -1 ) { printf("PIC status = "); hex2(picstat); putchar('\n'); picold = picstat; } else if ( picstat != picold ) { printf("PIC status = "); hex2(picstat); putchar('\n'); picold = picstat; } #endif if ( (picstat & 0xff) == 0x87 ) break; } #ifdef DEBUG printf("Done with IO wait loop"); if ( timeout == -1 ) printf(" TIMEOUT"); #endif if ( timeout == 0 ) printf(" TIMEOUT\n"); *DISK_DMA_DISABLE = 0; /* *DISK_BIU_RESET = 0; */ *PIC_OCW = 0xff; #ifdef DEBUG putchar('\n'); #endif } fderr(msg,status) char *msg; { printf("%s, status = ",msg); hex2(status); putchar('\n'); } char * prompt(msg) char *msg; { static char buf[132]; printf("%s", msg); gets(buf); return (buf); } /********/ #define HEX(x) ((x)<10 ? '0'+(x) : 'a'+(x)-10) /* vxdmp - memory dump, vxworks style */ vxdmp(b,n) char *b; { register i; register j; register c; for ( i=0; i '~' ) c = '.'; putchar(c); } printf("*\n"); } } #ifdef NEVER hex8(val) long val; { hex2(val>>24); hex2(val>>16); hex2(val>>8); hex2(val); } hex4(val) { hex2(val>>8); hex2(val); } hex2(val) { putchar(HEX((val>>4)&0xf)); putchar(HEX(val&0xf)); } #endif