/* Disk Utility Program for CP/M version 2.x Original May, 1980 by Richard Damon Modified Sept 9, 1981 by Robert Ward Modified July, 1982 by Edward K. Ream Please send all reports of bugs to: Edward K. Ream 1850 Summit Ave. Madison, WI 53705 (608) 231 - 2952 See the file anydisk.doc for documentation. */ #define VERSION "\nJuly 27, 1982\n\n" /* Define the data structures pointed to by the BIOS select disk function. */ /* DPH -- Disk Parameter Header. One for each disk */ struct DPH { /* XLT: address of translation table. */ /* one table for each TYPE of disk */ char * d_XLT; /* scratchpad area for BDOS use only */ int d_TEMP1; int d_TEMP2; int d_TEMP3; /* DIRBUF: address of 128-byte directory buffer */ /* all disks use the same buffer */ char * d_DIRBUF; /* DPB: address of DPB. One for each type of drive */ char * d_DPB; /* CSV: address of changed-disk check buffer */ /* one for each drive */ /* size of buffer is DPB -> CSK bytes */ char * d_CSV; /* ALV: address of allocation buffer */ /* one for each drive */ /* size of buffer is (DPB -> DSM / 8) + 1 bytes */ char * d_ALV; }; /* DPB -- Disk Parameter Block One for each TYPE of disk */ struct DPB { /* sector per track */ int d_SPT; /* BSH: block shift factor */ /* BLM = (2 ** BSH) - 1 */ /* BLS: block size. Note size VARIES */ /* BLS = (BLM + 1) * 128 */ char d_BSH; char d_BLM; /* EXM: extent mask */ /* if DSM > = 256 then block numbers take two bytes */ /* Thus, each extent can hold only 8 block numbers */ char d_EXM; /* DSM maximum disk BLOCK number */ /* BLOCKS numbered starting at zero */ /* (DSM + 1) * BLS = # of bytes per drive */ int d_DSM; /* DRM: directory max. directory entries 0 -- DRM */ int d_DRM; /* AL0, AL1: allocation vector for directory */ /* each bit reserves one BLOCK */ char d_AL0; /* high bit = block 0 */ char d_AL1; /* high bit = block 8 */ /* CKS: size of directory check vector (or 0) */ /* fixed media: CKS = 0 */ /* removeable media: CKS = (DRM + 1)/4 */ int d_CKS; /* OFF: track offset. number of skipped tracks */ int d_OFF; }; /* Define BIOS entry points. */ #define SELDSK 9 #define SETTRK 10 #define SETSEC 11 #define SETDMA 12 #define RDSEC 13 #define WTSEC 14 #define SECTRAN 16 /* BE = logical sec # */ /* DE = address of translation table */ /* Set this constant to the number of drives you have. */ #define NDRVS 5 /* Set BBUFSIZ to the size of the largest block that any of your disks use. Set MBUFSIZ to the largest number of directory entries that are placed on any of your disks. There is code (in the select routine) which makes sure that these constants are, in fact, large enough. If not, a message is issued and the program aborts, so you shouldn't be able to bomb the program (or your disks). */ #define BBUFSIZ 4096 #define MBUFSIZ 4096 /* Do not change ANY of the following constants. */ #define PHYS 0 #define LOG 1 #define YES 1 #define NO 0 #define SECSIZE 128 #define table TXTABLE /* Define global constants set by the select() routine. */ struct DPH * d_header; /* pointer to DPH */ struct DPB * d_disk; /* pointer to DPB */ int TPD; /* tracks per disk */ int SPT; /* sectors per track */ int BPD; /* blocks per disk */ int SPB; /* sectors per block */ char * TXTABLE; /* pointer to translate table */ int DIRBLKS; /* blocks in the directory */ int DIRSIZE; /* entries in the directory */ int OFFSET; /* offset of first track */ main() { char buffer [BBUFSIZ]; char buff[80], *bufp, c; char *temp; int mode, nsects, disk, b, i, j; int block, track, sector; /* Sign on. */ printf("Welcome to ddndisk version 3: "); printf(VERSION); /* Initialize */ track = 0; sector = 0; block = 0; mode = PHYS; nsects = 1; /* The default drive is drive A: */ disk = 0; select(disk); for (;;) { bufp = gets(buff); /* The 'X' request must be the only thing on a line. */ if (tolower(*bufp) == 'x' && *(bufp + 1) == '\0') { break; } while (c = *bufp++) switch (toupper(c)) { case 'B': mode = LOG; nsects = SPB; /* Get block number from the user. */ block = getnum(&bufp, 0, BPD-1 ,16); /* Convert block number to track/sector. A loop is used to avoid overflow. */ track = sector = 0; for(b = block; b; b--) { sector += SPB; if (sector >= SPT) { track++; sector -= SPT; } } break; case 'C': /* Fill the buffer with a signed constant. */ temp = getnum(&bufp, -32765, 32765, 16); for(i = 0; i < nsects * SECSIZE; i++) { buffer[i] = temp[i]; } break; case 'D': /* Select the disk and print disk info. */ disk = getnum(&bufp, 0, NDRVS-1, 10); select(disk); info(); break; case 'E': /* Patch the buffer with a list of chars. */ i = getnum(&bufp, 0, nsects*SECSIZE-1, 16); while(*bufp ==' ') { buffer [i++] = getnum(&bufp,0,255,16); if(i >= nsects*SECSIZE) { break; } } break; case 'F': /* Fill the buffer with an unsigned constant */ i = getnum(&bufp, 0, 255, 16); for(j = 0; j < nsects*SECSIZE; j++) { buffer [j] = i; } break; case 'H': /* Print a help message. */ help(); break; case 'I': /* Enter track/sector mode. */ if (mode != PHYS) { mode = PHYS; nsects = 1; /* Convert block to track/sector. */ track = sector = 0; for (b = block; b; b--) { sector += SPB; if (sector >= SPT) { track++; sector -= SPT; } } track += OFFSET; } break; case 'M': /* Print the directory and allocation map. */ ptmap(disk, table); break; case 'N': /* Go to next sector or block. */ if (mode == LOG) { block++; sector += SPB; } else { sector++; } if (sector >= SPT) { track++; sector -= SPT; if (track >= TPD) { /* Stop the scan. */ *bufp = '\0'; printf("No next sector.\n"); } } break; case 'P': /* Print the buffer. */ dmpbuff (buffer, nsects, mode, disk, block, track, sector, table); break; case 'R': /* Read a block or sector into the buffer. */ rdbuff (buffer, nsects, mode, disk, block, track, sector, table); break; case 'S': /* Set LOGICAL sector #. Enter track/sector mode. */ if (mode == LOG) { mode = PHYS; track = OFFSET; } nsects = 1; sector = getnum(&bufp, 0, SPT - 1, 10); break; case 'T': if (mode == LOG) { mode = PHYS; sector = 0; } nsects = 1; track = getnum(&bufp, 0, TPD - 1, 10); break; case 'W': /* Write the buffer to the disk. */ wrbuff (buffer, nsects, mode, disk, block, track, sector, table); break; case ' ': /* Ignore spaces. */ break; default: /* Unknown request. */ printf("%c ?????\n",c); *bufp = '\0'; break; } /* end switch */ if(kbhit()) getchar(); } } /* Call Bios. Return a word, not just a byte. */ bioshl(function, bc, de) int function, bc, de; { int * p; int q; /* get address of BIOS jump table */ p = 0x0001; q = *p + (3*function - 3); /* go to BIOS */ return call(q,0,0,bc,de); } /* Dump out the buffer in hex and in ascii. */ dmpbuff(pntr, nsects, mode, disk, block, track, sector, table) char *pntr; int nsects, mode, disk, block, track, sector; char *table; { int i,j,lchar; prthdr(mode,nsects,disk,block,track,sector,table); for (i = 0; i < nsects * SECSIZE; i += 16) { if(i % SECSIZE == 0 && mode == LOG) { printf("\n\nRecord %d\n", i/SECSIZE); } printf("\n%04x ", i); for (j = 0; j < 16; j++){ if(j%4 == 0) { printf(" "); } if (j%8 == 8) { printf(" "); } printf("%02x", pntr [i+j]); } printf(" "); for (j = 0; j < 16; j++) { lchar = pntr [i+j]; if (lchar < 0x1f || lchar > 0x7d) { printf("."); } else { printf("%c",lchar); } } if (kbhit()) break; } printf("\n"); } /* Get a number from the keyboard. */ getnum(pntr, low, high, base) int low, high, base; char **pntr; { int number; char c, buffer[50], *bp; number = 0; while(**pntr == ' ') (*pntr)++; while((c = toupper(*(*pntr)++))>='0' && c<= '9' || base ==16 && (c-=7) > '9' && c<= ('F'-7)) number = base*number+c-'0'; (*pntr)--; if (number < low || number > high) { if (base == 16) { printf("%x is out of range.\n",number); printf("Enter a hex number between "); printf("%x and %x: ", low, high); } else { printf("%d is out of range.\n",number); printf("Enter a decimal number "); printf("between %d and %d: ", low, high); } bp = gets(buffer); number = getnum(&bp, low, high, base); } return number; } /* Print a helpful message. */ help() { printf("Commands are...\n\n"); printf("Bn enter block mode and set block n.\n"); printf("Cn fill buffer with short n.\n"); printf("Dn set disk to n and print disk info.\n"); printf("Ea n1 n2 ... edit buffer[a] with n1 n2 ...\n"); printf("Fn fill buffer with unsigned n.\n"); printf("H print this message.\n"); printf("I enter track/sector mode.\n"); printf("M print disk allocation map.\n"); printf("N go to next block or track/sector.\n"); printf("P print buffer.\n"); printf("R read sector or block into buffer.\n"); printf("Sn set current sector to n (first sector is 0).\n"); printf("Tn set current track to n (first track is 0).\n"); printf("W write sector or block from buffer.\n"); printf("X exit program (must be alone on line).\n"); } /* Print information about the selected disk. */ info() { printf("Disk Parameter Header = %x (hex)\n", d_header); printf("Disk Parameter Block = %x (hex)\n", d_disk); printf("Sector Translate Table = %x (hex)\n", TXTABLE); printf("Track offset: %d\n", OFFSET); printf("Tracks per disk: %d\n", TPD); printf("Blocks per disk: %d, %x (hex)\n", BPD, BPD); printf("Sectors per track: %d\n", SPT); printf("Sectors per block: %d\n", SPB); printf("Chars per block: %d\n", SPB * SECSIZE); printf("Directory blocks: %d\n", DIRBLKS); printf("Directory entries: %d\n", DIRSIZE); printf("\n"); } /* Print the header message of a dump. */ prthdr(mode, nsects, disk, block, track, sector, table) int nsects, mode, disk, block, track, sector; char *table; { printf("\ndisk %c, ", disk + 'A'); if (mode == LOG) { printf("block %d, ", block); } printf("track %d, ", track + (mode == LOG ? OFFSET : 0)); printf("physical sector %d, ", bioshl(SECTRAN, sector, table)); printf("logical sector %d", sector); printf("\n"); } /* Print the directory and a disk allocation map. */ #define M1COL 3 /* Entries/line: directory */ #define M2COL 16 /* Entries/line: alloc map */ ptmap(disk, table) int disk; char *table; { int count, i, j, k; int track, sector, d, ex; char dir[BBUFSIZ]; /* Current directory bloc. */ int map[MBUFSIZ]; /* The disk allocation map. */ int *intdir, id; intdir = dir; /* Clear the map. */ setmem(&map, 2*MBUFSIZ, 0); printf("The directory is...\n"); track = sector = 0; for (count = i = 0; i < DIRSIZE; i++) { if (i%(4*SPB) == 0) { /* Read the next next block. */ rdbuff (dir, SPB, LOG, disk, 0, track, sector, table); sector += SPB; if (sector >= SPT) { track++; sector -= SPT; } j = 0; } else { j += 32; } /* Skip never allocated entries completely. */ if (dir [j+1] == 0xe5) { continue; } /* Print M1COL entries per line. */ if (count%M1COL == 0) { putchar('\n'); } count++; /* Print directory number. */ printf("%3x", i); /* Print '=' for nondeleted extents */ printf("%s", dir[j]==0 ? " = " : " * "); /* Save extent number. */ ex = dir [j + 12]; /* Print the file name and extent. */ dir [j + 12] = 0; printf("%s:%x ", &dir [j+1], ex); /* Skip deleted blocks. */ if (dir [j] == 0xe5) { continue; } /* See whether block # fits in 8 bits. */ if (BPD <= 256){ for(k = 16; k < 32 && dir [j+k]; k++) { d = dir [j + k]; if (d) { map [d] = i; } else { break; } } } else { for(k = 8; k<16 && intdir[j/2 + k]; k++){ id = intdir [j/2 + k]; if (id) { map [id] = i; } else { break; } } } } printf("\n\nThe disk map is...\n\n"); for(i = 0; i < BPD; i++) { if (i%M2COL == 0) { printf("%3x: ", i); } if (map [i]) { printf("%3x ", map [i]); } else { printf("... "); } if(i%M2COL == M2COL-1) { putchar('\n'); } } putchar('\n'); } #undef M1COL #undef M2COL /* Read a block or sector into the buffer. */ rdbuff(pntr, nsects, mode, disk, block, track, sector, table) char *pntr; int nsects, mode, disk, block, track, sector; char *table; { int i; int s; bios(SELDSK, disk); for (i = 0; i < nsects; i++, sector++){ if (sector >= SPT) { track++; sector = 0; } if (track >= TPD) { printf("Read truncated!\n"); return; } bios(SETTRK, track + (mode==LOG ? OFFSET : 0)); s = bioshl(SECTRAN, sector, table); bios(SETSEC, s); bios(SETDMA, pntr + (i * SECSIZE)); bios(RDSEC); } } /* Select a drive for all future disk operations. drive is 0 for drive A:, 1 for drive B:, etc. */ select (drive) char drive; { int alloc, i, nsectors; printf("select drive = %c\n", drive + 'A'); /* Point at the DPH and DPB. */ d_header = bioshl(SELDSK, drive, 0); if (d_header == 0) { printf("Select failed!\n"); exit(); } d_disk = d_header -> d_DPB; /* calculate global constants for this drive */ TXTABLE = d_header -> d_XLT; SPB = d_disk -> d_BLM + 1; BPD = d_disk -> d_DSM + 1; SPT = d_disk -> d_SPT; OFFSET = d_disk -> d_OFF; /* Compute number of logical tracks on the disk. For hard disks nsectors can overflow. thus, we must use a for loop instead of: */ /* comment out ----- nsectors = SPB * BPD; ntracks = nsectors / SPT; if (nsectors % SPT != 0) { TPD++; } ----- end comment out */ nsectors = 0; TPD = 1; for (i = 0; i < BPD; i++) { nsectors += SPB; if (nsectors > SPT) { nsectors -= SPT; TPD++; } } /* Add the number of hidden tracks. */ TPD += OFFSET; /* Compute the number of blocks in the directory. */ alloc = ((d_disk -> d_AL0) << 8) | (d_disk -> d_AL1); DIRBLKS = 0; for (i = 0; i < 16; i++) { if ((alloc & 1) != 0) { DIRBLKS++; } alloc = alloc >> 1; } /* Compute the number of entries in the directory. There are 4 entries in each 128-byte sector. */ DIRSIZE = DIRBLKS * SPB * 4; /* Make sure the directory buffer is big enough. */ if (SPB * SECSIZE > BBUFSIZ) { printf("Block buffer is too small.\n"); exit(); } /* Make sure the map buffer is big enough. */ if (DIRSIZE > MBUFSIZ) { printf("Map buffer is too small.\n"); exit(); } } /* Write the buffer to the disk. */ wrbuff(pntr, nsects, mode, disk, block, track, sector, table) char *pntr; int nsects, mode, disk, block, track, sector; char *table; { int i; int s; bios (SELDSK, disk); for (i = 0; i < nsects; i++, sector++) { if (sector >= SPT) { sector = 0; track++; } if (track >= TPD) { printf("Write truncated!\n"); return; } bios(SETTRK, track + (mode==LOG ? OFFSET : 0)); s = bioshl(SECTRAN, sector, table); bios(SETSEC, s); bios(SETDMA, pntr + (i * SECSIZE)); /* Force immediate write. */ bios(WTSEC, 1); } }