/*
	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;i<nsect;i++){
					sector+=6;
					if(sector>26)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;i<nsect;i++){
					bios(SET_TRACK,t);
					bios(SET_SECTOR,s);
					bios(SET_DMA,&buffer[i*128]);
					bios(READ_SECTOR);
					s+=6;
					if(s>26) 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;i<nsect;i++){
					bios(SET_TRACK,t);
					bios(SET_SECTOR,s);
					bios(SET_DMA,&buffer[i*128]);
					bios(WRITE_SECTOR);
					s+=6;
					if(s>26) 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;j<nsect*128;j++) buffer[j]=i;
				break;
			case 'M' : bios(SELECT_DISK,disk);
				bios(SET_TRACK,2);
				s=1;
				for(i=0;i<16;i++){
					bios(SET_SECTOR,s);
					bios(SET_DMA,&dir[i*128]);
					bios(READ_SECTOR);
					s+=6;
					if(s>26)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 (number<low || number>high){
		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');
}
