/* * check - a program to check out mini-winis * * L.W. Edmondson * */ #define BUFSIZE 512 /* * Bad block accounting */ #define MAXBAD 200 /* the maximum rememberable number of unreadable */ /* * Morrow bad map for hddma */ #define BADCYL 0 #define BADHEAD 2 #define BADSEC 0 struct mapent { unsigned m_cyl, /* zero origin cylinder */ m_sec, /* one origin sector of cylinder */ m_altcyl, m_altsec; }; /* * Constants - file system - block numbers - UNIX */ #define BOOT 0 /* the boot block */ #define SUPER 1 /* the super block */ #define ILIST 2 /* first block of the inode list */ /* * Structure of a directory */ struct dir { unsigned int ino; char name [14]; }; /* * Structure of the Micronix super block */ struct super { unsigned int isize, fsize, nfree, free [100], ninode, inode[100]; unsigned char flock, ilock, fmod; unsigned long time; }; /* * Structure of a Micronix disk inode */ struct inode { unsigned int flags; unsigned char nlinks, uid, gid, size0; unsigned int size1, addr [8]; unsigned long actime, modtime; }; # define S_ALLOC 0100000 # define S_TYPE 0060000 # define S_PLAIN 0000000 # define S_ISDIR 0040000 # define S_ISCHAR 0020000 # define S_ISBLOCK 0060000 # define S_LARGE 0010000 # define S_SUID 0004000 # define S_STICKY 0001000 # define S_PERM 0000777 # 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, mos = NO; /* Micronix Operating System */ UINT curcyl = 0, /* current cylinder */ drive = 0, errors = 0, secsize = S512, mapsize = 64, badlist [MAXBAD] = {0}, nbad = 0; 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) { prs (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" }; /* * we have a failure * * t is the type: HARD or SOFT */ mwerror (c, h, s, t) { unsigned b; if (t == SOFT) { prs ("Soft error: "); } else if (cmd.stat <= 10) /* valid status code */ { prs (errtab[cmd.stat]); prs (": "); } else { prn (cmd.stat); prs (": Unknown controller return status: "); } if (cmd.stat == BUSY) { reset (); mwinit (); } b = toblock (c, h, s); prtriple (c, h, s); prs (": "); if (isbad (b)) { prs (" Listed in the bad map"); } else { newbad (b); prs ("Not listed in the bad map"); } prs ("\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) { prs ("Controller does not respond\n"); return NO; } if (cmd.stat & NOTRDY) { prs ("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; } /* * read the given triple on the Morrow HDDMA * data goes in secbuf. * * return YES for success * NO for failure */ 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 (;;) { prs ("\ \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 (;;) { prs ("\ \n\ \n\ A) CP/M (1024 bytes per sector)\n\ B) Micronix ( 512 bytes per sector)\n\ \n\ Format: " ); c = cgetc (); prs ("\r\n"); switch (c) { case 'a': { mos = NO; secsize = S1024; mapsize = 128; return mwinfo->sectors = 9; } case 'b': { mos = YES; secsize = S512; mapsize = 64; return mwinfo->sectors = 17; } } } } check () { static c, h, s, r; reset (); if (!mwinit ()) return; prs ("Checking drive.\n"); 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) prs ("No errors.\n"); else if (errors == 1) prs ("1 error.\n"); else prn (errors), prs (" errors.\n"); } logo () { prs ("Mini-wini check for HDDMA (c) 1983 Morrow Inc.\n\n"); } getdrive () { char c; for (;;) { prs ("\ \n\ 0 (if you have only one drive)\n\ 1\n\ 2\n\ 3\n\ \n\ Drive: "); c = cgetc (); prs ("\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 (); getmosbad (); /* micronix bad map */ getfmwbad (); /* formatmw bad map */ check (); mwhome (); prbad (); } cgetc () { static char c; c = cpm (GETC); if (c == 3) /* control C checking */ exit (); c = tolower (c); return c; } prs (a) register char *a; { for (; *a; a++) { if (*a == '\n') cputc ('\r'); cputc (*a); } } cputc (a) char a; { cpm (PUTC, a); } /* * bread - read the given micronix block from the HDDMA * return YES for sucess * NO for failure */ bread(blk) unsigned blk; { static UINT cyl, head, sector; totriple (blk, &cyl, &head, §or); return mwread (cyl, head, sector); } mwcyl(blk, info) UINT blk; struct specs *info; { static UINT cyl, roll; cyl = blk / (info->sectors * info->heads); roll = info->tracks; cyl += roll / 2; if (cyl >= roll) cyl -= roll; return cyl; } /* * getmosbad - glean from the micronix file system * the list of bad blocks */ getmosbad () { static unsigned ilimit, n, b; static struct super *su; static struct inode *ip; /* * Make sure we are dealing with a Micronix formatted * disk. */ if (!mos) { return; } reset (); if (!mwinit ()) return; /* * make sure there is a file system here */ if (!isfs ()) { prs ("(No Micronix file system.\n"); return; } prs ("Reading Micronix bad sector list.\n"); /* * find tthe number of iistt blocks */ bread (SUPER); su = secbuf; ilimit = su->isize + 2; /* * read the ilist looking for bad block pointters */ for (b = 2; b < ilimit; b++) { if (!bread (b)) { #ifdef DEBUG prs ("Unreadable block in I-list\n"); #endif continue; } ip = secbuf; for (n = 16; n; n--, ip++) if (ip->flags & S_ALLOC) if (badpoint (ip)) newbad (*ip->addr); } } /* * determine whether the given disk inode is a badblock place holder */ badpoint (a) struct inode *a; { unsigned char i; if (a->size1 != 8 * BUFSIZE) return NO; if (a->size0) return NO; if (!a->addr[0]) return NO; for (i = 1; i < 8; i++) if (a->addr[i]) return NO; return YES; } /* * Determine whether or not the drive connected * to the HDDMA contains a Micronix file system. * * return YES | NO */ isfs () { static unsigned int isize; struct super *super; struct dir *dir; struct inode *inode; /* * read the super block */ if (!bread (SUPER)) { #ifdef DEBUG prs ("Can't read the super block\n"); #endif return NO; } /* * save the isize */ super = secbuf; isize = super->isize; /* * read the root inode */ if (!bread (ILIST)) { #ifdef DEBUG prs ("Can't read the root inode\n"); #endif return NO; } inode = secbuf; if ((inode->flags & S_TYPE) != S_ISDIR) { return NO; } /* * read the first 2 entries of the root directory */ if (!bread (2 + isize)) { #ifdef DEBUG prs ("Can't read root directory\n"); #endif return NO; } dir = secbuf; return ( dir[0].ino == 1 && dir[1].ino == 1 && cmpstr (dir[0].name, ".") && cmpstr (dir[1].name, "..") ); } newbad (a) unsigned int a; { if (isbad (a)) return; /* already listed */ if (nbad < MAXBAD) { badlist [nbad++] = a; } else { prs ("Unreadable sector table overflow\n"); } } /* * Does the given sector number appear in the bad sector list? */ isbad (a) unsigned a; { unsigned b; for (b = 0; b < nbad; b++) if (a == badlist [b]) return YES; return NO; } /* * Convert a micronix block number to a cyl, head, sector triple. */ totriple (a, c, h, s) unsigned int a, *c, *h, *s; { static unsigned int csec, nsecs; csec = a % (mwinfo->sectors * mwinfo->heads); /*sector of cylinder */ nsecs = mwinfo->sectors; /* sectors per track */ *c = mwcyl (a, mwinfo); /* cylinder of volume */ *h = csec / nsecs; /* head */ *s = csec % nsecs; /* sector of track */ } /* * convert a cyl, head, sector triple to a micronix block number */ int toblock (c, h, s) int c, h, s; { int roll; /* * unroll the cylinder number */ roll = mwinfo->tracks; c -= roll / 2; if (c < 0) c += roll; return c * mwinfo->sectors * mwinfo->heads + h * mwinfo->sectors + s; } /* * display the unreadable sector list */ prbad () { int i, c, h, s; prs ("Unreadable sectors\n"); for (i = 0; i < nbad; i++) { totriple (badlist [i], &c, &h, &s); prtriple (c, h, s); prs ("\n"); } } prtriple (c, h, s) { prs ("("); prn (c), prs (", "); prn (h), prs (", "); prn (s), prs (") or block "); prn (toblock (c, h, s)); } /* * read the formatmw bad sector map */ getfmwbad () { int n; struct mapent *m; mwread (BADCYL, BADHEAD, BADSEC); if (!ismap (secbuf)) { prs ("No formatmw bad map.\n"); return; /* No valid bad map */ } prs ("Reading formatmw bad map.\n"); n = mapsize; m = secbuf; for (; n; m++, n--) { if (m->m_cyl == 0 && m->m_sec == 0) break; newbad (pairtoblock (m->m_cyl, m->m_sec)); } } /* * Convert track, sector to block */ pairtoblock (a, b) unsigned a, b; { unsigned c, h, s; b--; /* correct for 1-origin */ c = a; h = b / mwinfo->sectors; s = b % mwinfo->sectors; return toblock (c, h, s); } /* * is this a valid formatmw bad map ?? */ ismap (m) struct mapent *m; { int n, spc; /* * sectors per cylinder */ spc = mwinfo->heads * mwinfo->sectors; for (n = mapsize; n; n--, m++) { if (m->m_cyl == 0 && m->m_sec == 0) /* end of map */ return YES; if (m->m_cyl >= mwinfo->tracks) return NO; if (m->m_sec > spc) return NO; } return NO; }