/* * check - a program to check out mini-winis * * L.W. Edmondson * */ # define HARD 0 # define SOFT 1 # define CHANNEL 0x50 #define tolower(c) (isupper(c) ? ((c) + ('a' - 'A')) : (c)) #define isupper(c) ('A' <= (c) && (c) <= 'Z') # define UINT unsigned # define UCHAR unsigned char /* * CP/M system calls */ # define GETC 1 # define PUTC 2 # define S128 0 # define S256 1 # define S512 3 # define S1024 7 # define S2048 0xf /* * Controller commands */ #define READS 0 #define WRITES 1 #define READH 2 #define FORM 3 #define LOAD 4 #define STAT 5 #define HOME 6 /* * Controller constants */ #define HOMDEL 30 /* step pulse delay during home in 100 us */ #define SETTLE 0 /* controller head-settle time */ #define INT 0x80 /* Interrupt enable bit with step delay */ #define RESET 0x54 /* Reset to controller */ #define ATTN 0x55 /* Attention to controller */ #define PRECOMP 0x80 /* Write precompensation */ #define HIGHCUR 0x40 /* Use high write current */ #define STEPOUT 0x10 /* step out toward track 0 */ #define LCONST 0x30 /* must be set for LOAD constants command */ #define NOTRDY 4 /* bit 2 of sense status */ #define BUSY 0 /* controller is busy */ #define OK 0xff /* operation completed w/o error */ #define INTOFF 0 /* turn off completion interrupts */ #define YES 1 #define NO 0 #define KERNEL 0 struct specs { UINT tracks; /* number of tracks per surface */ UCHAR heads; /* number of heads */ UCHAR sectors; /* number of sectors per track */ UCHAR stpdel; /* delay between step pulses, 100 us */ UINT precomp; /* track where precomp begins */ UINT lowcur; /* track where low current begins */ }; /* * Controller command structure * (with additions) * See HD-DMA manual */ struct cmd { UCHAR seksel; /* out<<4 | drv */ UINT steps; /* number of steps */ UCHAR hedsel; /* pcmp<<7 | hicur<<6 | (~head&7)<<2 | drv */ UINT dma; /* dma address */ UCHAR xdma; /* high byte of 24-bit address */ union { UINT word; struct { UCHAR low; UCHAR high; } byte; } arg0; UCHAR arg2; UCHAR arg3; UCHAR op; /* op code */ UCHAR stat; /* completion status */ UINT link; /* address of next command */ UCHAR xlink; /* high byte of address */ }; /* * Globals */ char *secbuf = 0; UINT curcyl = 0, /* current cylinder */ drive = 0, errors = 0, secsize = S512; struct specs specs[] = { {153, 4, 17, 30, 128, 128}, /* Seagate 5 meg */ {306, 4, 17, 2, 128, 128}, /* generic 10 meg */ {306, 6, 17, 2, 128, 128}, /* CMI 16 meg */ {640, 6, 17, 0, 256, 256}, /* CMI 32 */ }; struct cmd cmd = {0}; struct specs *mwinfo = &specs[2]; prn (a) { put (itoa (a)); } itoa (a) { static char buf [16]; buf [itob (buf, a, 10)] = 0; return buf; } /* * Reset (initialize) the controller */ reset () { static **p; out(RESET, 0); p = CHANNEL; *p++ = &cmd; *p = 0; cmd.link = &cmd; cmd.xlink = 0; cmd.dma = secbuf; cmd.xdma = KERNEL; } char *errtab [] = { "Timeout", "Drive not ready", "Status code 2", "Status code 3", "Sector header not found", "Data not found (no data preamble)", "Data overrun (channel error)", "Data CRC error", "Write fault", "Sector header CRC error", "Illegal command" }; mwerror (c, h, s, t) { if (t == SOFT) { put ("Soft error: "); } else if (cmd.stat <= 10) /* valid status code */ { put (errtab[cmd.stat]); put (": "); } else { prn (cmd.stat); put (": Unknown controller return status: "); } if (cmd.stat == BUSY) { reset (); mwinit (); } put ("cylinder "), prn (c), put (", "); put ("head "), prn (h), put (", "); put ("sector "), prn (s), put ("\n"); errors++; } # define TIME 8000 mwwait () { long i; attn (); for (i = TIME; i; i--) if (cmd.stat != BUSY) break; } /* * status command - hddma */ mwstat () { cmd.arg0.byte.high = HOMDEL; cmd.hedsel |= LCONST; cmd.op = LOAD; mwwait(); cmd.steps = 0; /* don't move the head */ cmd.seksel = drive; cmd.hedsel = drive; cmd.arg2 = SETTLE; cmd.arg3 = secsize; cmd.op = STAT; mwwait(); if (cmd.stat == 0) { put ("Controller does not respond\n"); return NO; } if (cmd.stat & NOTRDY) { put ("Drive not ready\n"); return NO; } return YES; } mwhome () { cmd.arg0.byte.high = HOMDEL; cmd.hedsel |= LCONST; cmd.op = LOAD; mwwait(); cmd.steps = -1; cmd.seksel |= STEPOUT; cmd.op = HOME; curcyl = 0; mwwait(); } mwload () { cmd.arg0.byte.high = mwinfo->stpdel; cmd.hedsel |= LCONST; cmd.op = LOAD; mwwait(); } /* * Select a new drive */ mwinit () { if (!mwstat ()) return NO; mwhome (); mwload (); return YES; } mwread (cyl, head, sector) { cmd.seksel = drive; if (cyl > curcyl) { cmd.steps = cyl - curcyl; } else { cmd.steps = curcyl - cyl; cmd.seksel |= STEPOUT; } curcyl = cyl; cmd.hedsel = drive | ((~head & 7) << 2) | HIGHCUR; if (cyl >= mwinfo->precomp) cmd.hedsel |= PRECOMP; if (cyl >= mwinfo->lowcur) cmd.hedsel &= ~HIGHCUR; cmd.arg0.word = cyl; cmd.arg2 = head; cmd.arg3 = sector; cmd.op = READS; mwwait(); return cmd.stat == OK; } /* * Nudge the controller */ attn () { cmd.stat = BUSY; out(ATTN, 0); } /* * Issue a command and wait for completion */ getsize () { for (;;) { put ("\ \n\ \n\ A) m5\n\ B) m10\n\ C) m16\n\ \n\ Drive type: " ); switch (cgetc ()) { case 'a': return mwinfo = &specs[0]; case 'b': return mwinfo = &specs[1]; case 'c': return mwinfo = &specs[2]; } } } getsystem () { char c; for (;;) { put ("\ \n\ \n\ A) CP/M (1024 bytes per sector)\n\ B) Micronix ( 512 bytes per sector)\n\ \n\ Format: " ); c = cgetc (); put ("\r\n"); switch (c) { case 'a': { secsize = S1024; return mwinfo->sectors = 9; } case 'b': { secsize = S512; return mwinfo->sectors = 17; } } } } check () { static c, h, s, r; reset (); if (!mwinit ()) return; for (c = 0; c < mwinfo->tracks; c++) { for (h = 0; h < mwinfo->heads; h++) { for (s = 0; s < mwinfo->sectors; s++) { for (r = 0; r < 10; r++) { if (mwread (c, h, s)) break; if (r == 4) mwinit (); /* recal */ } if (r == 10) /* hard */ { mwerror (c, h, s, HARD); } else if (r != 0) /* soft */ { mwerror (c, h, s, SOFT); } } } } if (errors == 0) put ("No errors.\n"); else if (errors == 1) put ("1 error.\n"); else prn (errors), put (" errors.\n"); } logo () { put ("Mini-wini check for HDDMA (c) 1983 Morrow Inc.\n\n"); } getdrive () { char c; for (;;) { put ("\ \n\ 0 (if you have only one drive)\n\ 1\n\ 2\n\ 3\n\ \n\ Drive: "); c = cgetc (); put ("\n\n"); switch (c) { case '0': return drive = 0; case '1': return drive = 1; case '2': return drive = 2; case '3': return drive = 3; } } } main () { char b [2048]; secbuf = b; logo (); getsize (); getsystem (); getdrive (); check (); mwhome (); } cgetc () { static char c; c = cpm (GETC); if (c == 3) /* control C checking */ exit (); c = tolower (c); return c; } put (a) register char *a; { for (; *a; a++) { if (*a == '\n') cputc ('\r'); cputc (*a); } } cputc (a) char a; { cpm (PUTC, a); }