/****************************************************************************

	M5b BIOS Function Call Utility

*****************************************************************************

	Known bugs:



*****************************************************************************

Revision History:

0.0 -	10-31-90   grh
	First cut. Extracted from Format.c v2.01b.

*/
#define	signon    "\nM5b BIOS Function Test Utility   Vers. 0.0\n"

/****************************************************************************/

#include stdio.h
#include ctype.h
#include io.h
#include farcall.h
#include b:m5zf200.h


BYTE
	this_bnk,	/* Location of our memory bank */
	fbuf[1024],	/* Format data buffer */
	sbuf[1024],	/* Sector buffer */
	*fdptr,
	*bytptr,
	*temp_p,	/* Temporary ptr */
	*cbptr; 	/* Ptr to command block array elements */


char
	*gets(),	/* Tell cc that this is a char ptr */
	*toupper(),
	*cptr,		/* User command text ptr */
	tbuf[80];	/* Input buffer */


WORD	
	a,
	n_ents,		/* Number of format track table entries */
	*fmt_tblp,	/* Format table ptr */
	*trk_fmtp,	/* Track format data ptr */
	fmt_len;	/* Length of format table */


int	i,
	d,
	j,
	temp,
	c;


unsigned int
	utemp,
	cnt;


FILE	*infile,
	*fopen();


/*
 IOPB definitions
*/
struct cb {
/* Arguments */
	BYTE	pb_cmd;	/* command */
	BYTE	pb_drv;	/* drive */
	WORD	pb_trk;	/* track */
	WORD	pb_sec;	/* sector */
	BYTE	pb_flags; /* flags */
	WORD	pb_dma;	/* Unused dma address */
	BYTE	pb_dmax;/* Unused dma extended address */
/* Argument in, Return value out */
	WORD	pb_bcnt; /* Xfer count */
/* Return values */
	BYTE	pb_sts;	/* Command result status */
	BYTE	pb_st1;	/* fdc status if pb_mod == 0x1f */
	BYTE	pb_st2;	/* returned value #1 */
	BYTE	pb_st3;	/* returned value #2 */
	} cmd_blk, liopb;
#define pb_fnskb 0x01	/* Inhibit implied seek flag mask */
#define pb_fmtd 0x20	/* Format drive option */
#define pb_inhx	0x40	/* Inhibit data xfer to host flag mask */
#define pb_fnrb 0x80	/* Inhibit retries flag mask */


/*
 Format overlay header data 
*/
struct {
	WORD	fov_txt;	/* Text ptr */
	WORD	fov_tsz;	/* Byte count of text */
	WORD	fov_idp;	/* ID Sector image ptr */
	WORD	fov_ids;	/* ID Sector byte count */
	WORD	fov_fmt;	/* Format table ptr */
	WORD	fov_fsz;	/* Format section byte count */
	WORD	fov_prt;	/* Controller port # */
	} *fmt_buf,		/* Ptr to the structure */
	  *fov_p;

/*
 Format overlay file data ptrs
*/
struct {
	BYTE	fil_name[11];	/* FCB filename */
	char	fil_txt[];	/* File text description */
	} *fov_file,		/* Base ptr to array */
	  *fov_ptr[64];		/* Ptrs to each file data */
BYTE	  *file_bp;		/* Ptr into array */
unsigned int
	max_files;		/* max files found */

/*
 CP/M File Control Block
*/
struct	fcb
	*srch_fst(),
	*srch_nxt(),
	f_cb,
	*fcb_ptr;

/*
 farcall registers
*/
struct regs
	cpui,
	cpuo;



/****************************************************************************

			main program

****************************************************************************/
main () {

/* Display signon */
   printf(signon);

/* Initialize data */
   cpui.AF = (MF_GBNK) << 8;	/* get current bank # */
   farcall( (WORD) MR_FUN, 0, &cpui, &cpui);
   this_bnk = cpui.AF >> 8;

/*   drive = -1;		 Assume no controller */

   cmd_blk.pb_sts = -1;	/* Force error if not accessed */

/* Allocate heap buffer */
   file_bp = fov_file = malloc(7000);	/* Allocate Filename buffer */
   if (fov_file == 0)  {
      puts("\nInsufficient Memory...Aborting!");
      exit(1);
      }
   (BYTE *) fmt_buf = ((BYTE *) fov_file) + 5976; /* Allocate format overlay buffer */

/* Top of command loop */
   while (TRUE) {

/**** Display Options */
      printf("\n\n\
Command Options:\n\
0   Display BIOS Version\n\
1   Display monitor drive number assigned to CP/M drive\n\
2   Disable dynamic format selection (LOGON)\n\
3   Enable dynamic format selection\n\
4   Set disk format\n\
\n\
^C  Exit to DOS\n\
\n\
Enter Command # - ");

/**** Fetch command */
      gets(tbuf);
      switch (tbuf[0]) {

         case '0':		/* display BIOS version */
	 cpui.AF = 0;
	 if (bf_exec() < 0)   break;
	 printf("\nBIOS Version = %d.%2d\n", cpui.HL / 100, cpui.HL % 100);
         break;

         case '1':		/* display monitor drive # */
	 if ( (cpui.BC = get_drv()) < 0)   break;
	 cpui.AF = 1 << 8;
	 if (bf_exec() < 0 )   break;
	 printf("\nMonitor Drive for this CP/M drive = %u\n", cpui.AF >> 8);
         break;

         case '2':		/* disable logon */
	 if ( (cpui.BC = get_drv()) < 0)   break;
	 cpui.AF = 2 << 8;
	 bf_exec();
         break;

         case '3':		/* enable logon */
	 if ( (cpui.BC = get_drv()) < 0)   break;
	 cpui.AF = 3 << 8;
	 bf_exec();
         break;

         case '4':		/* set disk format */
/******* Request drive for overlays */
         f_cb.f_driv = 0;	/* assume default */
         printf("\n\
Enter drive letter containing format database files {A..P} - ");
         gets(tbuf);
         if ( *tbuf != '\0')   f_cb.f_driv = ( toupper( *tbuf) - 'A' + 1);

/******* Read directory */
         for (i = 0, cptr = &f_cb.f_name[0]; i < 8; i++)   *cptr++ = '?';
         *cptr++ = 'F';   *cptr++ = 'O';   *cptr = 'V';
         fcb_ptr = srch_fst();
         if (fcb_ptr == -1)   {
            printf("\nNo Format Overlays Found... Aborting!");
            exit(1);
            }
         for (i = 0; i < 64 && fcb_ptr != -1; i++, file_bp += 11) {
            movmem( ((BYTE *) fcb_ptr) + 1, file_bp, 11);
            fov_ptr[i] = file_bp;
            fcb_ptr = srch_nxt();
            }
         max_files = i;
         if (i < 63)   fov_ptr[i] = -1;
         for (i = 0; i < max_files; i++ )  {
            if ( get_fov( &f_cb, fov_ptr[i] ) != 0) {
	       printf("\nOverlay read error... aborting!");
	       exit(1);
	       }

/********** Insert text */
            cnt = fmt_buf -> fov_tsz;
            file_bp = ((BYTE *) fov_ptr[i]) + 11;
            movmem( file_bp, 
		&file_bp[cnt], 
		5975 - cnt - (file_bp - ((BYTE *) fov_file) ) );
            bytptr = fmt_buf;
            movmem( &bytptr[fmt_buf -> fov_txt], file_bp, cnt);

/********** Adjust ptrs */
            for (j = i + 1; j < max_files; j++) {
	       fov_ptr[j] = ((BYTE *) fov_ptr[j]) + cnt;
	       }
            }

/******* Sort the files */
         for (d = TRUE; d == TRUE; ) {
            d = FALSE;	/* Assume sorted */
            for (i = 0; i < (max_files - 1); i++) {
	       temp = strncmp( fov_ptr[i], fov_ptr[i+1], 8); /* Test for sort */
	       if (temp > 0) {		/* If larger first then swap */
	          utemp = fov_ptr[i];
	          fov_ptr[i] = fov_ptr[i+1];
	          fov_ptr[i+1] = utemp;
	          d = TRUE;			/* Set changed flag */
	          }
	       }
            }

/******* Display formats */
         for (i = j = 0; i < max_files; i++, j++) {
            if (j == 0)   printf("\n\033E\
-------------------------- Format Directory ----------------------------");
            if (j == 20) {
               printf("\n\n\
Space for additional, '-' to back up, # to select - ");
	       c = getchar();
	       if (c == '-') {
	          j -= 20;
	          i -= 20;
	          }
	       else if (c == ' ') {
	          j = 0;
	          }
	       else {
	          ungetc( c, "stdin");
	          break;
	          }
	       }
            printf("\n%2d: ", i + 1);
            for (d = 0, cptr = &fov_ptr[i] -> fil_name; d < 8; d++, cptr++) {
	       if (*cptr == '\0')   putchar(' ');
	       else   putchar(*cptr);
	       }
            printf(" - %s", &fov_ptr[i] -> fil_txt );
            }
         printf("\nEnter selection - ");
         gets(tbuf);
         temp = atoi(tbuf);

/******* Got selection, read format overlay */
         if (get_fov(&f_cb, fov_ptr[temp - 1 ]) != 0) {
	    printf("Cannot read overlay...aborting!");
	    exit(1);
	    }

/******* Transfer ID sector data to sector buffer */
	 setmem(&sbuf, 1024, 0);
	 fdptr = fmt_buf;
	 cnt = fmt_buf -> fov_idp;
	 if ((fmt_buf -> fov_ids) > 1024) {
	    printf("\nFormat ID sector data size error:(%d)! Aborting!",
		fmt_buf -> fov_ids);
	    break;
	    }
	 movmem(&fdptr[cnt], sbuf, fmt_buf -> fov_ids);

/******* Set function arguments */
	 cpui.BC = get_drv();	/* drive # */
         cpui.DE = this_bnk;
         cpui.HL = &sbuf;		/* ID sector buffer */
         cpui.AF = 4 << 8;
printf("\nID at %04x", &sbuf);
         temp = bf_exec();
	 if (temp != 0 && temp > 0)   printf("\nError - %x", temp);
         break;

/******* Unrecognized command */
         default:
         printf("Command Not Implemented! Try again.");
         break;
         }

      }

/* Done. */
   exit(0);
   }


/****************************************************************************

	Get format overlay into buffer
	Entry - arg1= ptr to fcb to use
		arg2= ptr to filename from directory
	Returns 0: ok, /0: error

****************************************************************************/
int get_fov(f_cb, fnp)   struct fcb *f_cb;   BYTE *fnp; {
   int	i,
	rv;
   BYTE	*bp;

/* Insert file name */
   movmem(fnp, f_cb -> f_name, 11);
/* Init fcb */
   f_cb->f_ext = f_cb->f_resv[0] = f_cb->f_resv[1] = f_cb->f_rc = f_cb->f_cr = 0;
/* Open the fcb */
   rv = bdos(OPNFIL, f_cb);
   if (rv == 0xff)   return rv;
/* Read the file */
   for (bp = fmt_buf, rv = 0, i = 1024; 
	i > 0 && rv == 0; 
	i -= 128, bp += 128 ) {
      bdos( SETDMA, bp);
      rv = bdos( READSQ, f_cb);
      }
   return 0;
   }


/****************************************************************************

	Get the CP/M drive from user
	exit -	-1: abort
		 0..15: drive number = A..P

****************************************************************************/
get_drv() {

/* Stay in loop until valid data given */
   while (TRUE) {

/**** Request drive data */
      printf("\nEnter CP/M Drive {A..P} (X: abort) - ");
      gets(tbuf);
      tbuf[0] = toupper(tbuf[0]);

/**** If abort requested then return -1 */
      if (tbuf[0] == 'X')   return -1;

/**** Else if valid drive letter then return it's numeric value */
      if (tbuf[0] >= 'A' && tbuf[0] <= 'P')   return tbuf[0] - 'A';

/**** Else display error message & try again */
      printf("\nDrive must be A..P! Try again!\n");
      }
   }


/****************************************************************************

	Dump the fcb file data
	Assumes f_cb has proper name

****************************************************************************/
dmp_fcb(f_cb)	struct fcb *f_cb; {
   int i;
   char *cp;

   printf("\n%2x ", f_cb -> f_driv );
   for (i = 0, cp = &f_cb -> f_name; i < 8; i++, cp++ ) {
      if (*cp == '\0')   putchar(' ');
      else   putchar(*cp);
      }
   putchar('.');
   for ( ; i < 11; i++, cp++ ) {
      if (*cp == '\0')   putchar(' ');
      else   putchar(*cp);
      }
   }


/****************************************************************************

	Search for 1st directory match
	Assumes f_cb has proper search name
	Returns ptr to directory fcb data (-1 = not found)

****************************************************************************/
struct fcb *srch_fst()   {
   int	r;

/* Set buffer ptr to directory segment */
   bdos( SETDMA, Wrkbuf);

/* Search the directory */
   r = bdos( SRCHFL, &f_cb);

   return (r < 4) ? (Wrkbuf + (r << 5 )) : -1;
   }


/****************************************************************************

	Search for next directory match
	Assumes f_cb has proper search name
	Returns ptr to directory fcb data (-1 = not found)

****************************************************************************/
struct fcb *srch_nxt()   {
   int r;

/* Set buffer ptr to directory segment */
   bdos( SETDMA, Wrkbuf);

/* Search the directory */
   r = bdos( SRCHNX, &f_cb);

   return (r < 4) ? (Wrkbuf + (r << 5 )) : -1;
   }


/****************************************************************************

	Execute the BIOS function
	exit -	TRUE: abort
		FALSE: no error
		any_other: error

****************************************************************************/
int bf_exec() {
   int i;

/* Execute the command */
   farcall( (WORD) 0x0028, this_bnk, &cpui, &cpui);

/* if error then display it */
   switch (cpui.AF >> 8) {

      case 255:
      printf("Illegal Function!");
      return -1;

      case 254:
      printf("Argument Out of Range Error!");
      return -2;

      default:
      return cpui.AF >> 8;
      break;
      }
   }


/****************************************************************************

			Dump command block

****************************************************************************/
dmp_cdb() {

      cpui.AF = (MF_DIOB) << 8;
      cpui.DE = this_bnk;
      cpui.HL = &cmd_blk;
      farcall( (WORD) MR_FUN, this_bnk, &cpui, &cpui);

   }
