/****************************************************************************

   Intelligent Sasi Host Adapter (ISHA) controller test/exercisor program

*****************************************************************************

	Revisions:

1.00 -	11-2-88   grh
	Convert from V2.00 of JDD.C exerciser.

****************************************************************************/
#define	SIGNON	"\nISHA Monitor/Controller Exercisor  Vers. 1.00a\n"


#include stdio.h
#include ctype.h

#define	BYTE	unsigned char
#define	WORD	unsigned int

#define DIAG 	FALSE		/* Turn off execution code */

#define n_args	8		/* Max number of command args */

#define IDATA	0xd9		/* ISHA data port */
#define ICMD	0xd8		/* ISHA command port */
#define ISTAT	0xd8		/* ISHA status port */


/****************************************************************************

	Local global vars

****************************************************************************/
char
	*cptr,			/* module global text ptr */
	tbuf[80];		/* Input buffer */


BYTE
	*sbuf,		/* ptr to sector buffer dynamically allocated */
	*memtop;	/* ptr to last byte of buffer */

WORD
	arg[ n_args + 1 ];	/* storage for command arguments */

int
	hexfile;	/* 'HEX' keyword found for file I/O */
FILE
	*filekey;


/****************************************************************************

	Function templates

****************************************************************************/
char
	CI(),			/* Console input */
	*gets(),		/* Tell cc this is a char ptr */
	*uc_gets(),
	*skip(),
	*ret_siz();

BYTE
	xwait(),
	execute_i();

WORD
	ret_cmd(),
	str_size(),
	get_n_args(),
	scan_hex();


FILE
	*fopen();


/****************************************************************************

	Structure declarations

****************************************************************************/

/*	I/O Parameter Block (IOPB) declaration */
struct iopb_struct{
   BYTE
	pb_cmd,	/* command */
	pb_drv;	/* drive */
   WORD
	pb_trk,	/* track */
	pb_sec;	/* sector */
   BYTE
	pb_flg;	/* flags */
   WORD
	pb_dma;	/* Unused dma address */
   BYTE
	pb_dmax;/* Unused dma extended address */
   WORD
	pb_bcnt; /* Byte count to xfer */
   BYTE
	pb_stat, /* Controller status */
	pb_st1,	/* fdc status if dd_mod == 0x1f */
	pb_st2,	/* returned value #1 */
	pb_st3;	/* returned value #2 */
   }	iopb;


/*	SASI Command Descriptor Block (CDB) declaration */
struct {
   BYTE
	cmd,	/* command */
	lun,	/* logical unit number */
	la1,	/* logical address 1 */
	la0,	/* logical address 0 */
	blk_cnt, /* block count */
	ctrl;	/* control byte */
   } cdb;


/*	Disk descriptor sector physical data declarations */	
struct d {
   BYTE
	flags,
	spt0,
	ssz0,
	spt1,
	ssz1,
	sptd,
	sszd,
	type;
   WORD
	cyls;
   BYTE
	heads;
   WORD
	rwcc;
   BYTE
	pcm,
	pch,
	ecc,
	cntl;
   };

struct	v {
   WORD
	hd_eng,	/* Head engage time */
	step,	/* Step interval */
	tls,	/* Time after last step */
	mo_dly;	/* Motor on delay */
   };







/****************************************************************************

			main program

****************************************************************************/
main () {
   static WORD
	temp,		/* temporary storage */
	i;		/* counter */


/* Display signon and prompt user with command list */
   puts(SIGNON);
   help();

/* Initialize data buffer */
   sbuf = malloc(8192);
   memtop = (*((BYTE *) 0x6) - 2048) & 0xff00;
   crlf();
   puthxw(memtop - sbuf);
   puts(" byte buffer size.");

/* Do forever (almost) */
   while (TRUE) {

/**** Prompt user for command */
      crlf();
      CO('-');

/**** Get user option */
      uc_gets(cptr = tbuf);
      temp = ret_cmd(cptr);

/**** Pre-position to possible argument */
      nxt_field();

/**** Execute option */
      switch (temp) {

/*-------------------------------------------------------------------------
H	Display help message
-------------------------------------------------------------------------*/
	 case 1:
	 help();
	 break;


/*-------------------------------------------------------------------------
EX	Exit to DOS
-------------------------------------------------------------------------*/
         case 2:
         exit(0);
         break;


/*-------------------------------------------------------------------------
DB 1st last	Dump the sector buffer
-------------------------------------------------------------------------*/
	 case 3:
/******* Fetch & test user args */
	 if ((temp = get_n_args(2)) < 1)   { invargs();   break;  }

/******* Set defaults */
	 if (temp < 2)   arg[2] = arg[1] + 127;

/******* Process */
	 for ( ; arg[1] <= arg[2] && CIS() == 0; arg[1] += 16) {
	    crlf();   puthxw(arg[1]);   puts(" : ");
	    dump_16( &sbuf[ arg[1]],
		     ((arg[2] - arg[1]) >= 16) ? 16 : (arg[2] - arg[1] + 1) );
	    }

/******* Dump any abort character */
	 if (CIS())   CI();

	 break;


/*-------------------------------------------------------------------------
DC	Display CDB
-------------------------------------------------------------------------*/
	 case 4:
/******* Process */
	 puts("\nCommand = ");   puthxb(cdb.cmd);
	 puts("\nLUN     = ");   puthxb(cdb.lun);
	 puts("\nLA1     = ");   puthxb(cdb.la1);
	 puts("\nLA0     = ");   puthxb(cdb.la0);
	 puts("\nBlk Cnt = ");   puthxb(cdb.blk_cnt);
	 puts("\nControl = ");   puthxb(cdb.ctrl);
	 crlf();
	 break;


/*-------------------------------------------------------------------------
DI	Dump the IOPB
-------------------------------------------------------------------------*/
	 case 5:
/******* Process */
	 dmp_iopb();

	 break;


/*-------------------------------------------------------------------------
FI 1st last value	Fill buffer with data
-------------------------------------------------------------------------*/
	 case 6:
/******* Fetch & test user args */
	 if ((temp = get_n_args(3)) < 3)   { invargs();   break;  }
	 if (arg[1] > arg[2])   { invargs();   break;  }

/******* Process */
	 while (arg[1] <= arg[2])   sbuf[ arg[1]++] = (BYTE) arg[3];

	 break;


/*-------------------------------------------------------------------------
MO 1st last dest	Move buffer data
-------------------------------------------------------------------------*/
	 case 7:
/******* Fetch & test user args */
	 if ((temp = get_n_args(3)) < 3)   { invargs();   break;  }
	 if (arg[1] > arg[2])   { invargs();   break;  }

/******* If overlap then use top down process */
	 if (arg[3] > arg[1] && arg[3] <= arg[2]) {
	    arg[3] += (arg[2] - arg[1] + 1);
	    while ( arg[1] <= arg[2])   sbuf[ arg[3]--] = sbuf[ arg[2]--];
	    }

/******* Else bottom up process */
	 else {
	    while ( arg[1] <= arg[2])   sbuf[ arg[3]++] = sbuf[ arg[1]++];
	    }

	 break;


/*-------------------------------------------------------------------------
SU 1st			Substitute buffer data
-------------------------------------------------------------------------*/
	 case 8:
/******* Fetch & test user args */
	 if ((temp = get_n_args(1)) < 1)   { invargs();   break;  }

/******* Process */
	 for (temp = arg[1]; TRUE; temp += 1) {
	    puthxw(temp);  puts(" = "); puthxb( (BYTE) sbuf[temp]);
	    puts(" - ");
	    uc_gets(cptr = tbuf);
	    if (tbuf[0] == '.')   break;
	    if (get_n_args(1) == 1)   sbuf[temp] = (BYTE) arg[1];
	    }

	 break;


/*-------------------------------------------------------------------------
LO 1st <HEX> file.name	Load data file into buffer
-------------------------------------------------------------------------*/
	 case 9:
/******* Fetch & test user args */
	 if ((temp = get_n_args(1)) < 1)   { invargs();   break;  }

/******* Select file type */
	 if (strncmp(cptr, "HEX", 3) == 0) {
	    hexfile = TRUE;
	    nxt_field();
	    }
	 else {
	    hexfile = FALSE;
	    }

/******* Open file for reading */
	 filekey = fopen(cptr, "r");
	 if (filekey == 0) {
	    puts("\nFile open error!\n");
	    break;
	    }

/******* Process */
	 notimp();
	 break;


/*-------------------------------------------------------------------------
SA 1st last <HEX> file.name	Save buffer data to file
-------------------------------------------------------------------------*/
	 case 10:
/******* Fetch & test user args */
	 if ((temp = get_n_args(2)) < 2)   { invargs();   break;  }

/******* Select file type */
	 if (strncmp(cptr, "HEX", 3) == 0) {
	    hexfile = TRUE;
	    nxt_field();
	    }
	 else {
	    hexfile = FALSE;
	    }

/******* Open file for reading */
	 filekey = fopen(cptr, "w");
	 if (filekey == 0) {
	    puts("\nFile open error!\n");
	    break;
	    }

/******* Process */
	 notimp();
	 break;


/*-------------------------------------------------------------------------
RE addr drv trk sect <cnt>		Read sector(s)
-------------------------------------------------------------------------*/
	 case 11:
/******* Fetch & test user args */
	 if ((temp = get_n_args(5)) < 4)   { invargs();   break;  }

/******* Set defaults */
	 if (temp < 5)   arg[5] = 1;

/******* Set up iopb */
	 iopb.pb_cmd = 1;
	 iopb.pb_drv = arg[2];
	 iopb.pb_trk = arg[3];
	 iopb.pb_sec = arg[4];
	 iopb.pb_dma = &sbuf[ arg[1]];
	 iopb.pb_dmax = 0;
	 iopb.pb_flg = 0;
	 iopb.pb_bcnt = 128;

/******* Process */
	 for ( ; arg[5] > 0; arg[5]--) {
	    if (execute_i( &iopb) != 0)   break;
	    }

/******* Display results */
	 if (iopb.pb_stat != 0) {
	    dmp_iopb();
	    puts("\nError!!");
	    } 
	 break;


/*-------------------------------------------------------------------------
WR addr drv trk sect <cnt>		Write sector(s)
-------------------------------------------------------------------------*/
	 case 12:
/******* Fetch & test user args */
	 if ((temp = get_n_args(5)) < 4)   { invargs();   break;  }

/******* Set defaults */
	 if (temp < 5)   arg[5] = 1;

/******* Set up iopb */
	 iopb.pb_cmd = 2;
	 iopb.pb_drv = arg[2];
	 iopb.pb_trk = arg[3];
	 iopb.pb_sec = arg[4];
	 iopb.pb_dma = &sbuf[ arg[1]];
	 iopb.pb_dmax = 0;
	 iopb.pb_flg = 0;
	 iopb.pb_bcnt = 128;

/******* Process */
	 for ( ; arg[5] > 0; arg[5]--) {
	    if (execute_i( &iopb) != 0)   break;
	    }

/******* Display results */
	 if (iopb.pb_stat != 0) {
	    dmp_iopb();
	    puts("\nError!!");
	    } 
	 break;


/*-------------------------------------------------------------------------
T	Run diagnostics
-------------------------------------------------------------------------*/
	 case 13:
	 notimp();
	 break;


/*-------------------------------------------------------------------------
XC <target cmd lun la1 la0 cnt ctrl>	Execute the CDB
-------------------------------------------------------------------------*/
	 case 14:
	 notimp();
	 break;


/*-------------------------------------------------------------------------
XI	execute the IOPB
-------------------------------------------------------------------------*/
	 case 15:
	 x_iopb();
	 break;


/*-------------------------------------------------------------------------
??	Undocumented command
-------------------------------------------------------------------------*/
	 default:	/* Command error */
	 cmderr();
	 help();
	 break;
         }
      }
   }


/***************************************************************************

	Command error

***************************************************************************/
cmderr() {
   puts("\nCommand Error!\n");
   }


/***************************************************************************

	command not implemented function

***************************************************************************/
notimp() {
   puts("\nThat command isn't implemented yet!\n");
   }


/***************************************************************************

	Invalid arguments function

***************************************************************************/
invargs() {
   puts("\nInvalid Arguments!\n");
   }


/****************************************************************************

	Display help message

****************************************************************************/
#define n_cmds 15
static char
	*cmds[n_cmds] = {				/* command index */
"H                            Display Help messages",		/* 1 */
"EX                           Exit to DOS",			/* 2 */
"DB 1st last                  Display sector buffer data",	/* 3 */
"DC                           Display CDB",			/* 4 */
"DI                           Display IOPB data",		/* 5 */
"FI 1st last value            Fill buffer with value",		/* 6 */
"MO 1st last dest             Move buffer data",		/* 7 */
"SU 1st                       Substitute buffer data",		/* 8 */
"LO 1st <HEX> file.name       Load data file into buffer",	/* 9 */
"SA 1st last <HEX> file.name  Save buffer data to file",	/* 10 */
"RE addr drv trk sect <cnt>   Read sector(s)",			/* 11 */
"WR addr drv trk sect <cnt>   Write sector(s)",			/* 12 */
"T                            Execute self test",		/* 13 */
"XC targt b1 b2 b3 b4 b5 b6   Execute CDB",			/* 14 */
"XI                           Execute IOPB"			/* 15 */
   };

help() {

   register WORD
	i;		/* counter */

   puts("\nCommands:\n");
   for (i = 0; i < n_cmds; i++)  { puts(cmds[i]);   crlf(); }
   }


/****************************************************************************

	Return command index function
	exit -	0: Command not found in table
		/0: command index {1..n}

****************************************************************************/
WORD ret_cmd(text)
char
	*text;		/* ptr to potential command text */   {

   register WORD
	i;		/* command counter */

   for (i = 0; i < n_cmds; i++) {
      if (strncmp(text, cmds[i], str_size( cmds[i])) == 0)   return i + 1;
      }
   return 0;
   }


/****************************************************************************

	return size of null terminated character string function
returns number of non-space chars in string, not counting the '\0' char.

****************************************************************************/
WORD str_size(sptr)
char
	*sptr;		/* ptr to string to size up */   {

   register WORD
	cnt;		/* character counter */


   for (cnt = 0; *sptr != '\0' && *sptr != ' '; cnt++, sptr++)   {}
   return cnt;
   }


/****************************************************************************

	new line

****************************************************************************/
crlf() {
   puts("\n");
   }


/***************************************************************************

	get text and convert to upper case

***************************************************************************/
char *uc_gets(tptr)
char
	*tptr;		/* ptr to text buffer */   {

   WORD
	i;		/* character index */

/* Fetch text */
   gets(tptr);

/* Convert to upper case if needed */
   for ( i = 0; tptr[i] != '\0'; i++)   tptr[i] = toupper( tptr[i]);

/* Return ptr for gets() compatability */
   return tptr;
   }


/***************************************************************************

	Skip to next field procedure

***************************************************************************/
nxt_field() {
   cptr = skip(cptr, ' ');
   }


/***************************************************************************

	Skip chars function

***************************************************************************/
char *skip(ptr, c)
char
	*ptr,		/* ptr to string to skip */
	c;		/* character to skip over */   {

   register int
	flag;		/* flag to determine when skip character is found */

   for (flag = FALSE; *ptr != '\0'; ptr++) {
      if (*ptr != c && flag)   break;
      else if (*ptr == c)   flag = TRUE;
      }
   return ptr;
   }


/***************************************************************************

	Get argument(s) in text string function
 Uses global 'cptr' variable pointing to 1st argument field as text ptr &
leaves cptr pointing to next field or '\0'
Returns number of args actually processed.

***************************************************************************/
WORD get_n_args(n)
WORD
	n;		/* max number of args to scan */   {

   register unsigned
	argn;		/* current argument number {1..n_args} */

/* If args out of range then return error */
   if (n < 1 || n > n_args)   return 0;

/* Else process up to n arguments */
   for (argn = 1; *cptr != '\0' && n > 0; n--, argn++) {
      arg[argn] = scan_hex(cptr);
      nxt_field();
      }

/* Return number of args processed */
   return argn -1;
   }


/***************************************************************************

	Return hex word function

***************************************************************************/
WORD scan_hex(ptr)
char
	*ptr;		/* ptr to text to convert to binary */   {

   WORD
	acc;		/* accumulator for value to return */
   register char
	c;		/* fast storage for current working char */

/* Convert chars until a non-hex char is encountered */
   for (acc = 0, c = *ptr; TRUE; c = *(++ptr)) {

/**** If char not hex then abort with current value */
      if (c < '0' || (c > '9' && c < 'A') || c > 'F')   break;

/**** Else convert to binary and add to accumulated value */
      acc = (acc << 4) + ( (c > '9') ? (c - 7 -'0') : (c - '0') );
      }

/* Done, return accumulated value */
   return acc;
   }





/****************************************************************************

	Query function returns user option
	entry-	ptr to text question
	exit -	TRUE= user answered Yes
		FALSE= User did not answer Yes

****************************************************************************/
query(cptr)
char
	*cptr; 	/* question prompt text */ {

   crlf();   puts(cptr);   puts("? (Y/N) : ");
   uc_gets(tbuf);
   return ( tbuf[0] == 'Y') ? TRUE : FALSE;
   }



/****************************************************************************

		Dump IOPB function

****************************************************************************/
dmp_iopb() {

	 puts("\nCommand:                Status Returned:\n\
Command = "	   );   puthxb(iopb.pb_cmd);
	      puts("            Status  = ");   puthxb(iopb.pb_stat);
   puts("\nDrive   = ");   puthxb(iopb.pb_drv);
	      puts("            St1     = ");   puthxb(iopb.pb_st1);
   puts("\nTrack   = ");   puthxw(iopb.pb_trk);
	      puts(  "          St2     = ");   puthxb(iopb.pb_st2);
   puts("\nSector  = ");   puthxw(iopb.pb_sec);
	      puts(  "          St3     = ");   puthxb(iopb.pb_st3);
   puts("\nFlags   = ");   puthxb(iopb.pb_flg);
   puts("\nDMA     = ");   puthxb(iopb.pb_dmax);   puthxw(iopb.pb_dma);
   puts("\nByte Cnt= ");   puthxw(iopb.pb_bcnt);
   }


/****************************************************************************

	Dump a line of buffer data to console in Hex - ASCII
output format is ": <data> <ASCII equivalents>"
   argument = address of data area

****************************************************************************/
dump_16(addr, cnt)
BYTE
	*addr; 		/* ptr to data area to output */
WORD
	cnt;		/* byte count to display */   {

   register char
	c;		/* fast storage for current char */
   unsigned int
	j;		/* loop counter */

/* Display hex data bytes */
   for (j = 0; j < 16; j++) {
      if (j == 8)   puts("- ");
      if (j < cnt) {
         puthxb(addr[j]);   CO(' ');
         }
      else puts("   ");
      }

/* Now repeat for ascii */
   puts("  ");
   for (j = 0; j < 16; j++) {
      if (j < cnt) {
         c = addr[j] & 0x7f;
         CO( (c >= ' ' && c < 0x7f) ? c : '.');
         }
      else   CO(' ');
      }
   }


/***************************************************************************

	Execute the IOPB function

***************************************************************************/
x_iopb() {

   register BYTE
	*bptr;		/* ptr to data in sector buffer */
   register WORD
	i;		/* temporary storage */
   static char
	*iopb_cmds[] = {
		"0 : Log on drive",		/* 0 */
		"3 : Format track",		/* 1 */
		"8 : Return Firmware Version",	/* 2 */
		"9 : Get Disk Physical Data",	/* 3 */
		"A : Set disk Physical Data",	/* 4 */
		"B : Seek track",		/* 5 */
		"F : Clear Controller"		/* 6 */
		};


/* Display commands */
   for (i = 0; i < 7; i++)   { puts(iopb_cmds[i]);   crlf(); }

/* Fetch user command */
   puts("Enter Command - ");
   uc_gets(cptr = tbuf);
   for (i = 0; i < 7; i++) {
      if (strncmp( cptr, iopb_cmds[i], str_size( iopb_cmds[i])) == 0)   break;
      }

/* Select command */
   switch (i) {

/*---------------------------------------------------------------------------
0	Log on drive
---------------------------------------------------------------------------*/
      case 0:
      iopb.pb_cmd = 0;
      iopb.pb_flg = 0;
      iopb.pb_trk = 0;	/* Now need to specify trk & sector */
      iopb.pb_sec = 2;
      iopb.pb_bcnt = 128;
      if (get_drive())   break;

/**** Get buffer location from user */
      if ( get_bufr())   break;

      if ( execute_i( &iopb) )   dmp_iopb();
      break;


/*---------------------------------------------------------------------------
3	Format Track
---------------------------------------------------------------------------*/
      case 1:
      notimp();
      break;


/*---------------------------------------------------------------------------
8	Return firmware version
---------------------------------------------------------------------------*/
      case 2:

/**** set up iopb */
      iopb.pb_cmd = 8;

/**** execute it */
      if ( execute_i( &iopb) )   dmp_iopb();

/**** display version data */
      i = (iopb.pb_st3 << 8) + iopb.pb_st2;
      puts("\nFirmware version = ");
      putdec(i / 100);   CO('.');   putdec(i % 100 );
      break;


/*---------------------------------------------------------------------------
9	Get Physical disk parameters
---------------------------------------------------------------------------*/
      case 3:
      iopb.pb_cmd = 9;

/**** Get buffer location from user */
      if ( get_bufr())   break;

/**** Get drive from user */
      get_drive();

/**** Fetch user option */
      iopb.pb_flg = 1 + (( query( "Disable logon") ) ? 0 : 2);

/**** Execute the request */
      if ( execute_i( &iopb) ) 	 dmp_iopb();

/**** Display current data */
      bptr = &sbuf[arg[1]];
      puts("\nFlags          = "); puthxb( *bptr++);
      puts("\nTrk0 SPT, Size = "); puthxb( *bptr++); puthxb( *bptr++);
      puts("\nTrk1 SPT, Size = "); puthxb( *bptr++); puthxb( *bptr++);
      puts("\nData SPT, Size = "); puthxb( *bptr++); puthxb( *bptr++);
      puts("\nDisk Type      = "); puthxb( *bptr++);
      puts("\nCylinders      = "); puthxw( *(WORD *) bptr++);
      puts("\nHeads          = "); puthxb( *bptr++);
      puts("\nRed. Wr. I Cyl = "); puthxw( *(WORD *) bptr++);
      puts("\nPrecomp Cyl    = "); puthxw( *(WORD *) bptr++);
      puts("\nECC Burst Len. = "); puthxb( *bptr++);
      puts("\nControl Byte   = "); puthxb( *bptr++);
      crlf();
   
      break;


/*---------------------------------------------------------------------------
A	Set disk physical data
---------------------------------------------------------------------------*/
      case 4:
      notimp();
      break;


/*---------------------------------------------------------------------------
B	Seek track
---------------------------------------------------------------------------*/
      case 5:
      iopb.pb_cmd = 11;

/**** Get drive from user */
      get_drive();

/**** Get track from user */
      if (get_trk())   break;

/**** Execute it */
      if ( execute_i( &iopb) )   dmp_iopb();
      break;


/*---------------------------------------------------------------------------
F	Clear controller
---------------------------------------------------------------------------*/
      case 6:
      iopb.pb_cmd = 15;
      if ( execute_i( &iopb) )   dmp_iopb();
      break;


/*---------------------------------------------------------------------------
???	Unknown command
---------------------------------------------------------------------------*/
      default:
      cmderr();
      }
   }


/***************************************************************************

	Fetch console input status function
	exit -	0: no char ready, /0: char ready

***************************************************************************/
int CIS() {

/* Use get console status CP/M function */
   return bdos(11, 0);
   }


/***************************************************************************

	Return next console char function
	exit -	char

***************************************************************************/
char	CI() {

/* Use console read CP/M function */
   return bdos(1, 0);
   }


/***************************************************************************

	Console output procedure

***************************************************************************/
CO(c)
char
	c;   {

   bdos(2, c);
   }


/***************************************************************************

	Output hex nibble to console procedure

***************************************************************************/
puthxn(data)
BYTE
	data;		/* Nibble to convert */   {

   data &= 0xf;
   CO( (data <= 9) ? (data + '0') : (data + '0' + 7) );
   }


/***************************************************************************

	Output hex byte to console procedure

***************************************************************************/
puthxb(data)
BYTE
	data;		/* Byte to convert */   {

/* Do upper nibble 1st */
   puthxn(data >> 4);

/* Now do lower nibble */
   puthxn( data );
   }


/***************************************************************************

	Output hex word to console procedure

***************************************************************************/
puthxw(data)
WORD
	data;		/* word to convert to hex */   {

/* Do upper byte 1st */
   puthxb( data >> 8 );

/* Now do lower byte */
   puthxb( data );
   }


/***************************************************************************

	Output decimal number procedure does not output any preceeding
 blanks or zeros.

***************************************************************************/
putdec( n)
WORD n;			/* number to output */   {

   int
	flag;		/* flag to determine when to output zeros */
   WORD
	divisor,	/* digit divisor */
	a;		/* accumulator for digit */

   for (flag = FALSE, divisor = 10000; divisor > 0; divisor /= 10) {

/**** Fetch digit from number */
      a = n / divisor;

/**** Extract remainder for next loop */
      n %= divisor;

/**** If not 0 then set flag for subsequent zero printing */
      if (a > 0)   flag = TRUE;

/**** If flag set or last digit then start printing */
      if (flag || divisor == 1)   CO(a + '0');

/**** Do next digit until no more digits */
      }
   }


/***************************************************************************

	Print text string on console procedure

***************************************************************************/
puts(tptr) 
char
	*tptr;		/* text ptr */   {

/* Do until end of text char */
   for ( ; *tptr != '\0'; tptr++) {

/**** If char == new line then output carriage return 1st */
      if (*tptr == '\n')   CO('\r');

/**** Now output character */
      CO(*tptr);
      }

/* Done */
   }


/***************************************************************************

	Execute the IOPB function
	returns iopb.pb_stat

***************************************************************************/
BYTE execute_i(iptr)
struct iopb_struct
	*iptr;		/* ptr to the iopb data */   {

   BYTE
	temp;		/* temporary result storage */

#if DIAG
   iptr->pb_stat = -1;
   return -1;
   }

#else
/* Output the iopb address to the host adapter */
   out(IDATA, ((WORD) iptr) & 0xff);
   out(ICMD, 1);
   temp = xwait();
   if (temp) {
      iptr->pb_stat = temp;
      return temp;
      }
   
   out(IDATA, ( ( (WORD) iptr) >> 8) & 0xff);
   out(ICMD, 3);
   temp = xwait();
   if (temp) {
      iptr->pb_stat = temp;
      return temp;
      }

   out(IDATA, 0);
   out(ICMD, 5);
   temp = xwait();
   if (temp) {
      iptr->pb_stat = temp;
      return temp;
      }

/* Now execute the iopb */
   out(ICMD, 0x41);
   temp = xwait();
   if (temp)   return temp;
   else   return  iptr->pb_stat;
   }
#endif


/***************************************************************************

	Wait for command execution function
	returns command status

***************************************************************************/
BYTE xwait() {
   BYTE
	temp;		/* temporary result storage */

   WORD
	i;		/* timer count */

/* Wait for busy bit to clear */
   while (TRUE) {
      for (i = 0; i < 40000; i++) {
	 if ((( temp = in( ISTAT) ) & 0x01) == 0)   break;
	 }

/**** If timed out then ask user to continue */
      if (i == 40000) {
	 if (query("Controller timeout, Quit") )   return temp;
	 }

/**** Else stop */
      else   break;
      }

/* Return no error */
   return 0;
   }


/****************************************************************************

	Get drive from user
   returns TRUE if error, FALSE if ok

****************************************************************************/
int get_drive() {

   puts("\nEnter drive number {0..3}: ");
   uc_gets(cptr = tbuf);
   iopb.pb_drv = scan_hex( cptr);
   return   FALSE;
   }


/****************************************************************************

	Get sector & track info from user
   returns TRUE if error, FALSE if ok

****************************************************************************/
int get_sec_trk() {

   puts("\nEnter sector number : ");
   uc_gets(tbuf);
   iopb.pb_sec = atoi(tbuf);
   get_trk();
   }


/****************************************************************************

	Get track info from user
   returns TRUE if error, FALSE if ok

****************************************************************************/
int get_trk() {

   unsigned int
	d;	/* temporary track storage */

   puts("\nEnter decimal track number : ");
   uc_gets(cptr = tbuf);
   d = atoi(tbuf);
   iopb.pb_trk = d;
   return FALSE;
   }

   
/***************************************************************************

	get track data function
	entry -	cptr= ptr to place to put data
	exit -	cptr= sectors per track
		cptr + 1= sector size {0,1,..n}
		cptr = cptr + 2

***************************************************************************/
gt_trk(tptr)
char
	*tptr; {

   unsigned
	x;

   puts("\nEnter "); puts(tptr); puts(" sectors per track : ");  uc_gets(tbuf);
   if (tbuf[0] != '\0')   *cptr = atoi(tbuf);
   cptr++;
   puts("Enter "); puts(tptr); puts(" sector size {128, 256, 512, 1024}: ");
   uc_gets(tbuf);
   if (tbuf[0] != '\0') {
      x = atoi(tbuf);
      switch (x) {
         case 128:   x = 0; break;
	 case 256:   x = 1; break;
	 case 512:   x = 2; break;
	 case 1024:  x = 3; break;
	 default:    x = 100;
	 }
      if (x < 100)   *cptr = x;
      }
   cptr++;
   }


/***************************************************************************

	get buffer address from user

***************************************************************************/
get_bufr() {

   WORD
	d;		/* buffer offset address */

   puts("\nEnter buffer address - ");   uc_gets( cptr = tbuf);

   d = scan_hex(cptr);

   iopb.pb_dma = &sbuf[d];
   iopb.pb_dmax = 0;
   }

