/****************************************************************************

	Jade Double D floppy controller test/exercisor program

*****************************************************************************

	Revisions:

1.0 -	26 Nov 85   grh
	Release

****************************************************************************/

#include stdio.h
#include ctype.h
#define	TRUE -1
#define FALSE 0

unsigned
char	in();

char	sec_buf[2048],		/* Sector buffer */
	fmt_buf[] = {0x55,0xaa,0x24,0,0x21,0x10,0x80,0x11,
		     0x80,0x83,1,0x14,0,0xed,0xb0,0xc9,
		     'H','e','l','l','o',' ','W','o','r','l','d','!',0,0,0},
	*cptr,			/* Ptr to command block array elements */
	*gets(),			/* Tell cc that this is a char ptr */
	tbuf[80];		/* Input buffer */

unsigned atob(),
	atoh(),
	a,
	dd_addr,		/* Location of selected DD window */
	dd_port;		/* I/O port number of selected DD card */

int	i,
	d,
	j,
	c;

struct cb {
	unsigned char	dd_cmd;	/* command */
	unsigned char	dd_drv;	/* drive */
	unsigned 	dd_trk;	/* track */
	unsigned 	dd_sec;	/* sector */
	unsigned char	dd_ffg;	/* flags */
	unsigned	dd_dma;	/* Unused dma address */
	unsigned char	dd_dmax;/* Unused dma extended address */
	unsigned char	dd_sts;	/* Controller status */
	unsigned char	dd_st1;	/* fdc status if dd_mod == 0x1f */
	unsigned char	dd_st2;	/* returned value #1 */
	unsigned char	dd_st3;	/* returned value #2 */
	unsigned char	dd_st4;	/* returned value #3 */
	unsigned char	dd_st5;	/* returned value #4 */
	} cmd_blk;




/****************************************************************************

			main program

****************************************************************************/
main () {

   puts("\nJade Double D Controller Exercisor  Vers. 1.0\n");

   help();		/* Display the command list */

   dd_port = 0x40;	/* Initialize port & data area */
   dd_addr = sec_buf;	/* One = the other */

   while (TRUE) {
      crlf();
      printf("-");
      gets(tbuf);
      switch (c = toupper(tbuf[0])) {
         case 'X':	/* Exit to DOS */
         exit(0);
         break;

	 case 'H':	/* Display help message */
	 help();
	 break;

	 case 'Q':	/* Dump the DD ram */
	 printf("\nEnter start address (Hex) - ");
	 gets(tbuf);
	 i = atoh(tbuf);
	 if (i < 0)   break;
	 while (i < 2048) {
	    dump_16(i);
	    i += 16;
	    if ((i % 256) == 0) if (*gets(tbuf) != '\0') {
	       printf("\n%c\n", *tbuf);   break;
	       }
	    }
	 break;

	 case 'Z':	/* Dump the sector buffer */
	 for (i=0x400; i < 2048;) {
	    dump_16(i);
	    i += 16;
	    if ((i % 256) == 0) if (*gets(tbuf) != '\0')   break;
	    }
	 break;

	 case 'P':	/* Display ports from 0x40..0x43 */
	 for (i=0; i < 4; i++) {
	    c = in(0x40 + i);
	    if (c != 0xff)
	        printf("\nA Controller responds to port: %02xH", 0x40+i);
	    }

	 case 'S':	/* Set controller port to respond to */
	 printf("\nEnter controller port to respond to (Hex):");
	 dd_port = atoh(gets(tbuf));
	 crlf();
	 if(dd_port > 0x43 || dd_port < 0x40)   printf("Illegal port!");
	 c = in(dd_port);
	 dd_addr = (((c & 0x0e) << 1) | 0xe0) << 8;
	 printf("DD Window at %04xH",dd_addr);
	 break;

	 case 'T':	/* Run diagnostics */
	 out(dd_port,0x80);	/* reset dd processor */
	 wait_dd();
	 get_cdb();
	 dmp_cdb();
	 dd_dump();
	 break;

         case '0':	/* Log on drive */
	 cmd_blk.dd_cmd = 0;
	 if (get_drive())   break;
	 dd_exec();
         break;

         case '1':	/* Read sector */
	 cmd_blk.dd_cmd = 1;
	 if (get_drive())   break;
	 if (get_sec_trk())   break;
	 dd_exec();
         break;

	 case '2':	/* Write sector */
	 cmd_blk.dd_cmd = 2;
	 if (get_drive())   break;
	 if (get_sec_trk())   break;
	 dd_exec();
	 break;

	 case '3':	/* Format track */
	 cmd_blk.dd_cmd = 3;
	 if (get_drive())   break;
	 fmt_buf[2] = sizeof (fmt_buf);
	 for (a=0, i=0; i < ((sizeof (fmt_buf)) - 1); i++)   a += fmt_buf[i];
	 fmt_buf[sizeof (fmt_buf) - 1] = (-a);
	 out(dd_port, 1);
	 movmem(fmt_buf, dd_addr, sizeof (fmt_buf));
	 out(dd_port,0);
	 for (i=0, a=0; i<fmt_buf[2]; i++)   a += fmt_buf[i];
	 if (a)   printf("\nChecksum wrong!: %4x", a);
	 dd_exec();
	 break;

	 case '4':	/* Read address */
	 cmd_blk.dd_cmd = 4;
	 if (get_drive())   break;
	 dd_exec();
	 break;

	 case '5':	/* EIA output */
	 cmd_blk.dd_cmd = 5;
         printf("\nEnter character to output to EIA port: ");
	 gets(tbuf);
	 cmd_blk.dd_st5 = tbuf[0];
	 dd_exec();
	 break;

	 case '6':	/* EIA input/status */
	 cmd_blk.dd_cmd = 6;
	 dd_exec();
	 printf("\nEIA bit = %s", (cmd_blk.dd_st2 & 1) ? "TRUE" : "FALSE");
	 break;

	 case '7':	/* Idle */
	 cmd_blk.dd_cmd = 7;
	 dd_exec();
	 break;

	 case '8':	/* Return firmware version */
	 cmd_blk.dd_cmd = 8;
	 dd_exec();
	 printf("\nFirmware version = %c.%c",
		(cmd_blk.dd_st3 & 0xff) + '0',
		(cmd_blk.dd_st2 & 0xff) + '0');
	 break;

	 case '9':	/* Set disk flags/sectors per track */
	 cmd_blk.dd_cmd = 9;
	 if (get_drive())   break;
	 cmd_blk.dd_ffg = 0xff;		/* Display current data */
	 dd_exec();
	 printf("\nFlags    T0  T1  DT\n");
	 for (i=0, j=cmd_blk.dd_st2; i<8; i++, j = j << 1) {
	    printf("%c", (j & 0x80) ? '1' : '0');
	    }
	 printf(" %2d  %2d  %2d", cmd_blk.dd_st3, cmd_blk.dd_st4,
				  cmd_blk.dd_st5);
			/* Now request new data */
	 cptr = &cmd_blk.dd_st2;
	 printf("\nEnter flag value (binary): ");
	 gets(tbuf);
	 if (tbuf[0] != '\0')   *cptr = atob(tbuf);
	 cptr++;
	 printf("\nEnter track 0 sectors per track {1..48}: ");   gets(tbuf);
	 if (tbuf[0] != '\0')   *cptr = atoi(tbuf);
	 cptr++;
	 printf("Enter track 1 sectors per track {1..48}: ");   gets(tbuf);
	 if (tbuf[0] != '\0')   *cptr = atoi(tbuf);
	 cptr++;
	 printf("Enter data tracks sectors per track {1..48}: "); gets(tbuf);
	 if (tbuf[0] != '\0')   *cptr = atoi(tbuf);
	 cmd_blk.dd_ffg = 0;	/* Set parameters */
	 dd_exec();
	 break;

	 case 'A':	/* Load head & idle */
	 cmd_blk.dd_cmd = 10;
	 if (get_drive())   break;
	 dd_exec();
	 break;

	 case 'B':	/* Seek track */
	 cmd_blk.dd_cmd = 11;
	 if (get_drive())   break;
	 if (get_trk())   break;
	 dd_exec();
	 break;

	 case 'C':	/* Set drive parameters */
	 cmd_blk.dd_cmd = 12;
	 if (get_drive())   break;
	 cmd_blk.dd_ffg = -1;	/* Display current parameters */
	 dd_exec();
	 printf("\n\
Head engage time= %5d      Step interval= %5d\n\
Time after last step= %5d   Motor start delay= %5d",
cmd_blk.dd_trk, cmd_blk.dd_sec, cmd_blk.dd_st2 + cmd_blk.dd_st3 * 256,
cmd_blk.dd_st4 + cmd_blk.dd_st5 * 256);

			/* Now set the values */
	 i = 0;
	 printf("\nEnter head engage time: ");   gets(tbuf);
	 if (tbuf[0] != '\0') {
	    i = -1;
	    cmd_blk.dd_trk = atoi(tbuf);
	    }
	 printf("Enter Step interval: ");   gets(tbuf);
	 if (tbuf[0] != '\0') {
	    i = -1;
	    cmd_blk.dd_sec = atoi(tbuf);
	    }
	 printf("Enter time after last step: ");   gets(tbuf);
	 if (tbuf[0] != '\0') {
	    i = -1;
	    a = atoi(tbuf);
	    cmd_blk.dd_st2 = a & 0xff; cmd_blk.dd_st3 = a >> 8;
	    }
	 printf("Enter motor start delay: "); gets(tbuf);
	 if (tbuf[0] != '\0') {
	    i = -1;
	    a = atoi(tbuf);
	    cmd_blk.dd_st4 = a & 0xff;   cmd_blk.dd_st5 = a >> 8;
	    }
	 cmd_blk.dd_ffg = 0;	/* Set parameters */
	 if (i)   dd_exec();
	 break;

	 case 'D':		/* Return drive status */
	 cmd_blk.dd_cmd = 13;
	 if (get_drive())   break;
	 dd_exec();
	 printf("\nBoard level status= ");
	 for (i=0, j=cmd_blk.dd_st2; i<8; i++, j = j << 1) {
	    printf("%c", (j & 0x80) ? '1' : '0');
	    }
	 break;

	 case 'E':		/* Set/return baud rate */
	 cmd_blk.dd_cmd = 14;
	 cmd_blk.dd_st5 = 0xff;
	 dd_exec();
	 printf("\n\
 0: Level only   1: 600 baud (5\042)   2: 1200 baud   3: 2400 baud\n\
 4: 4800 baud    5: 9600 baud       6: 19,200 baud (8\042)\n\
Current baud rate= %u\n\
Enter new baud rate: ", cmd_blk.dd_st5);
	 gets(tbuf);
	 if (tbuf[0] != '\0') {
	    cmd_blk.dd_st5 = atoi(tbuf);
	    dd_exec();
	    }
	 break;

	 default:	/* Command error */
	 printf("\nCommand Error!\n");
	 break;
         }
      }
   }


/****************************************************************************

			Display help message

****************************************************************************/
help() {

   printf("\nCommands:\n\
H- Help                      X- Exit to DOS\n\
Q- Dump the entire RAM       Z- Dump the sector buffer\n\
P- Find the ports            S- Set the port\n\
T- Run diagnostics\n\
0- Log on drive              8- Return firmware version\n\
1- Read sector               9- Set disk flags/sectors per track\n\
2- Write sector              A- Load head & idle\n\
3- Format track              B- Seek track\n\
4- Read address              C- Set/return drive params\n\
5- EIA output                D- Return drive status\n\
6- EIA input                 E- Set EIA baud rate\n\
7- Idle\n");
   }


/****************************************************************************

			new line

****************************************************************************/
crlf() {
   printf("\n");
   }


/****************************************************************************

			Dump Jade controller ram

****************************************************************************/
dd_dump() {

   out(dd_port,1);	/* Fetch DD window */
   movmem(dd_addr,sec_buf,1024);	/* Move the window */
   out(dd_port,3);			/* Fetch 2nd window */
   movmem(dd_addr,&sec_buf[1024],1024);
   out(dd_port,1);			/* Restore DD */
   out(dd_port,0);
   }


/****************************************************************************

			Execute the command

****************************************************************************/
dd_exec() {
   int i;

   out(dd_port,1);
   movmem(&cmd_blk,dd_addr + 0x370, sizeof (cmd_blk));
   out(dd_port,2);
   wait_dd();
   get_cdb();
   dmp_cdb();
   dd_dump();
   }


/****************************************************************************

		Get command block from controller

****************************************************************************/
get_cdb() {
   out(dd_port,1);
   movmem(dd_addr + 0x370, &cmd_blk, sizeof (cmd_blk));
   out(dd_port,0);
   if (cmd_blk.dd_sts != 0)
      printf("\n**** ERROR! ****  ->  %2x - %2x\n",
			 cmd_blk.dd_sts, cmd_blk.dd_st1);
   }


/****************************************************************************

			Dump command block

****************************************************************************/
dmp_cdb() {
   printf("\n\
Cmd Drv Trk Sec Flg Sts St1 St2 St3 St4 St5\n\
%02x  %02x  %02x  %02x  %02x  %02x  %02x  %02x  %02x  %02x  %02x",\
cmd_blk.dd_cmd,
cmd_blk.dd_drv,
cmd_blk.dd_trk,
cmd_blk.dd_sec,
cmd_blk.dd_ffg,
cmd_blk.dd_sts,
cmd_blk.dd_st1,
cmd_blk.dd_st2,
cmd_blk.dd_st3,
cmd_blk.dd_st4,
cmd_blk.dd_st5);
   }


/****************************************************************************

			Get drive from user
   returns TRUE if error, FALSE if ok

****************************************************************************/
int get_drive() {
   int d;

   printf("\nEnter drive number {0..3}: ");
   gets(tbuf);
   d = atoi(tbuf);
   if (d > 3)   {printf("\nDrive error!");   return TRUE;}
   cmd_blk.dd_drv = d;
   return   FALSE;
   }

/****************************************************************************

			Get sector & track info from user
   returns TRUE if error, FALSE if ok

****************************************************************************/
int get_sec_trk() {
   int	d;

   printf("\nEnter sector number {1..48}: ");
   gets(tbuf);
   d = atoi(tbuf);
   if (d > 48)   {printf("\nSector error!"); return TRUE;}
   cmd_blk.dd_sec = d;
   printf("Enter Side {0,1}: ");
   gets(tbuf);
   d = atoi(tbuf);
   if (d < 0 || d > 1)   {printf("\nSide Error!"); return TRUE;}
   cmd_blk.dd_sec += (d == 0) ? 0 : 0x80;
   get_trk();
   }


/****************************************************************************

			Get track info from user
   returns TRUE if error, FALSE if ok

****************************************************************************/
int get_trk() {
   int d;

   printf("\nEnter track number {0..76}: ");
   gets(tbuf);
   d = atoi(tbuf);
   if (d > 76)   {printf("\nTrack error!"); return TRUE;}
   cmd_blk.dd_trk = d;
   return FALSE;
   }

   
/****************************************************************************

		Wait a specific time for controller completion
   returns TRUE if timeout, FALSE if no timeout (normal)

****************************************************************************/
int wait_dd() {
   unsigned i;

   for (i=0; i < 40000; i++)   if ((in(dd_port) & 1) == 0)   break;
   if (i == 40000) {
      printf("\nController timeout!");
      return TRUE;
      }
   printf("\nExecution time = %4u of 40000", i);
   return FALSE;
   }


/****************************************************************************

			Convert ASCII to binary
   returns value

****************************************************************************/
unsigned atob(p)   char *p;{
   unsigned a;

   for (a=0; (*p == '0' || *p == '1');)   a = (a << 1) + (*p++ - '0');
   return a;
   }


/****************************************************************************

			Convert ASCII to hex number
   returns value

****************************************************************************/
unsigned atoh(p)   char *p; {
   unsigned a;

   for (a = 0; (isdigit(*p) || (*p >= 'A' && *p <= 'F'));) {
      a = a << 4;
      if (*p > '9')   a += (*p++ - '7');
      else   a += (*p++ - '0');
      }
   return a;
   }


/****************************************************************************

		Dump a line of buffer data to console in Hex - ASCII
output format is <address:> <data> <ASCII equivalents>
   argument = offset address of sector buffer {0..size} ("address" of format)

****************************************************************************/
dump_16(addr)   char *addr; {
   int j;

   for (j=0; j < 16; j++) {
      if (j == 0)   printf("\n%04x: ",addr);
      printf("%02x ", sec_buf[addr + j]);
      }
   for (j=0; j < 16; j++) {
      c = sec_buf[addr + j] & 0x7f;
      if (c >= ' ' && c < 0x7f)   printf("%c", c);
      else   printf(".");
      }
   }
