/* DISK UTILITY PROGRAM Written by Richard Damon Version 1.0 May 1980 This program allows the operator to examine and modify a CPM diskette. The commands available in this package are : Tn set current track to n (0-76) Sn set current sector to n (1-26) Dn set current disk number to n (0-3) Bn set current track and sector to point to block n (0-F2) N set current track and sector to next sector/block next sector if last set command was for track or sector next block if last set command was for block I increment sector, if sector>26 set to 1 and increment track R read sector/block into buffer W write sector/block from buffer P print out contents of buffer, allong with track/sector/block information Ea n n n n edit buffer starting from location a filling with values n n n. Fn Fill buffer with value n X exit program M print disk allocation map Notes: 1) Multiple commands may be specified on a line except for X which must be the only command on the line followed by return. 2) Commands may be in upper or lower case letters 3) Spaces are ignored except in the E command where they are used as separaters for the numbers Typical commands: d0t0s1rp read in the track 0 sector 1 of disk 0 (Drive A) and print it e1A 4F 20 11 set buffer location 1A to 4F, 1B to 20, and 1C to 11. e0a 00w set buffer location 0a to 0 and write buffer Note no space after last data byte nrp get next buffer and print it Disk Allocation Map The M command is designed to allow the directory blocks (blocks 0 and 1) to be printed out in a convient format. The directory is print out in the following format: Section 1: The top half of the directory listing is a listing of the name inforamtion of the directory entries. Each line corresponds to 1 sector of the directory. A typical entry would be f=DISKTESTCOM/1 4c The first letter is a code letter used to referance into section 2. The equal sign indicats that the file exists, a star here indicates that this entry is a deleted file. Next comes the filename and extension. The following /n is printed if this is other then the first extent (extent 0) of a file where n is the extent number of this entry. The following number is the hex record count for this extent. Section 2: The bottom half of the directory listing is a disk allocation map showing which blocks are in use and by which file. Free blocks are indicated by a dot while used blocks are marked by the file control letter asigned to a file in section 1. This listing has been blocked off in groups of 8 and 16 to ease reading. CPM FILE STRUCTURE To help the user of this program the following is a brief description of the format used in CPM. The first 2 tracks of a disk are reserved for the bootstrap and the copy of the CPM operating system. Tracks 2 through 76 store the data. To speed up disk access CPM does not store consecutive data in consecutive sectors. Insteed it uses every 6th sector for data. Thus to read logical consecutive sectors you must read the sectors in the following order: 1 7 13 19 25 5 11 17 23 3 9 15 21 2 8 14 20 26 6 12 18 24 4 10 16 22 This interleaving is taken care of when reading in multiple sectors or when incrementing the disk address with the N command. To simplify the disk allocation scheme the sectors are the collected into groups of 8 sectors forming a 1k block. These blocks are numbered from 0 starting at the begining of the dirctory. (track 2 sector 1). Block numbers range from 0 to F2. The directory is organized to use 2 block of storage (16 sectors) to store information on 64 file extensions. A file extension is a part of a file up to 16k bytes long. The directory entry for a file extension is as follows: byte 0 : file code : 0 if file exists, E5 if file is deleted byte 1- 8: file name : ascii representation of file name byte 9-11: file type : ascii representation of file type byte 12 : file ext : binary number for extent number byte 13,14: unused byte 15 : rec count : count of number of sectors in extent byte 16-31: map : list of block numbers used by this extent */ #include "bdscio.h" main(){ int track,sector,disk,nsect,t,s,i,j,k,block; char buffer[1024],buff[80],*bufp,c,d,mc,dir[2048],map[256]; disk=0; track=0; sector=1; nsect=1; while(tolower(*(bufp=gets(buff))) != 'x' || *(bufp+1) != '\0'){ while((c=*bufp++) != '\0') switch(toupper(c)){ case 'T' : track=getnum(&bufp,0,76,10); nsect=1; break; case 'S' : sector=getnum(&bufp,1,26,10); nsect=1; break; case 'D' : disk=getnum(&bufp,0,1,10); break; case 'B' : block=getnum(&bufp,0,0xf2,16); nsect=8; track=2+block*8/26; s=block*8%26; sector=s*6%26+1; if(s>12)sector++; break; case 'N' : for(i=0;i26)sector-=26; if(sector==2){ sector=1; track++; } else if(sector==1) sector=2; } break; case 'I' : sector+=nsect; if(sector>26){ sector-=26; track++; } break; case 'R' : bios(SELECT_DISK,disk); t=track; s=sector; for(i=0;i26) s-=26; if(s==2){ s=1; t++; } else if(s==1) s=2; } break; case 'W' : bios(SELECT_DISK,disk); t=track; s=sector; for(i=0;i26) s-=26; if(s==2){ s=1; t++; } else if(s==1) s=2; } break; case 'P' : switch (sector%6){ case 0: block=17+sector/6; break; case 1: block= 0+sector/6; break; case 2: block=13+sector/6; break; case 3: block= 9+sector/6; break; case 4: block=22+sector/6; break; case 5: block= 5+sector/6; break; } block=block+26*(track-2); printf("track %d sector %d ",track,sector); printf(" block %x.%d ",block/8,block%8); for(i=0;i<128*nsect;i+=16){ printf("\n %4x ",i); for(j=0;j<16;j++){ printf("%2x ",buffer[i+j]); if(j%4 == 3) printf(" "); } for(j=0;j<16;j++){ c=buffer[i+j]&0x7f; if(c<' '||c==0x7f)c='.'; /* BUG in compiler!!!! c=c<' '||c==0x7f ? '.' : c; */ putch(c); } if(kbhit()) break; } putch('\n'); break; case 'E' : i=getnum(&bufp,0,nsect*128-1,16); while(*bufp==' '){ buffer[i++]=getnum(&bufp,0,255,16); if(i>=nsect*128) break; } break; case 'F' : i=getnum(&bufp,0,255,16); for(j=0;j26)s-=26; if(s==1)s=2; } setmem(map,256,0); for(i=0;i<64;i++){ if(i%4==0) putch('\n'); j=32*i; c=(dir[j]==0) ? '=' : '*'; d=dir[j+12]; mc=mapchar(i); if(d==0xe5){ printf("%c%19s",mc,""); continue; } dir[j+12]=0; printf("%c%c%s",mc,c,&dir[j+1]); if(d != 0) printf("/%x",d%0x10); else printf(" "); printf(" %2x ",dir[j+15]); if(c=='*')mc+=128; for(k=16;k<32 && dir[j+k];k++){ d=dir[j+k]; if(mc<128) map[d]=mc; } } for(i=0;i<0xf3;i++){ if(i%64==0) putch('\n'); else if(i%16==0) printf(" "); else if(i%8==0) printf(" "); putch(map[i] ? map[i] : '.'); } putch('\n'); break; case ' ' : break; default : printf("%c ??????\n",c); *bufp='\0'; break; } if(kbhit()) getchar(); } } 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 (numberhigh){ printf("bad number %d ",number); bp=gets(buffer); number=getnum(&bp,low,high,base); } return (number); } mapchar(i) char i; { if(i<10) return(i+'0'); if(i<36) return(i-10+'a'); return(i-36+'A'); }