#include <stdio.h>

/* ---- JRSASI HARD DISK UTILITY.  REV 1.2  2/14/86

Copyright 1985,86 by TURNING POINT LOGIC CORPORATION.
Released into the public domain for personal, non-commercial use only.

This module is a standalone utility to aid in the setup and debugging
of the JRSASI hard disk interface.  It allows the operator to format
the hard disk, set up the drive characteristics, read sectors, modify
sector data, and perform the necessary initialization for the PC-DOS
device driver interface.

To build this module perform the following steps:

1. Compile this module.
2. Assemble SDRV.ASM
3. Assemble SINTF.ASM
4. Assemble KCTEST.ASM
5. Link the modules together using the following command:

LINK cinitroutines+SDUTIL+SDRV+SINTF+KCTEST,SDUTIL,NUL,libfile /STACK:4096

cinitroutines = File name of the C compiler's initialization routine(s).
libfile = File name of the library of C interface routines (e.g. printf).

The above two files (cinitroutines and libfile) should be provided with the 
C compiler used to compile SDUTIL.C although, depending upon the compiler,
neither may be necessary.  For example, using the Lattice C compiler with
the small program model:
cinitroutines = CS (as in CS.OBJ) and
libfile = LCS (as in LCS.LIB).

*/

#define CR 13           /* Carriage return or ENTER key definition      */
#define BS 8            /* ASCII Backspace code.                        */
#define UPCUR 72        /* Cursor-up keystroke code.                    */
#define DWCUR 80        /* Cursor-down keystroke code.                  */
#define LFCUR 75        /* Cursor-left keystroke code.                  */
#define RTCUR 77        /* Cursor-right keystroke code.                 */
#define PGUPC 73        /* Page Up (PgUp) keystroke code.               */
#define PGDWC 81        /* Page Down (PgDn) keystroke code.             */
#define SETCURSOR 0     /* Command code to SINTF.ASM for setting cursor
                           position.                                    */
#define RETCURSOR 1     /* Command code to SINTF.ASM for retrieving the
                           current position of the cursor.              */
#define RCHAR 2         /* Command code to SINTF.ASM for retrieving the
                           character and attribute at the cursor pos.   */
#define WCHAR 3         /* Command code to SINTF.ASM for writing a char-
                           acter and attribute at the cursor position.  */
#define SCLUP 4         /* Command code to SINTF.ASM for scrolling the
                           contents of a window on the screen.          */
#define STROW 0         /* Default starting row for screen display.     */
#define STCOL 0         /* Default starting column for screen display.  */
#define SCCLR 0         /* Number of lines to scroll to clear the window
                           (used in connection with SINTF.ASM command for
                            scrolling a window).                        */
#define ENDROW 24       /* Last usable row for display on the screen.   */
#define ENDCOL 79       /* Last usable column for display on the screen.*/
#define DSSIZE 256      /* Number of bytes of the sector data to display
                           at one time.                                 */ 
#define NO 0
#define YES 1
#define CMAX 8          /* Maximum number of SASI controllers.          */
#define INTLV 7         /* Default sector interleave value (used for
                           formatting the hard disk).                   */
#define CNMAX 153       /* Default number of cylinders on drive (5MB).  */
#define HNMAX 4         /* Default number of heads (or tracks) per 
                           cylinder (5MB drive)                         */
#define WCCYL 128       /* Default starting cylinder number for reduced
                           write current (specified by drive manuf.).   */ 
#define WPCYL 64        /* Default starting cylinder number for write
                           precompensation (specified by drive manuf.)  */
#define ECCVAL 11       /* Default maximum ECC data burst length
                           (specified by SASI controller manufacturer). */
#define CVDEF 0         /* DCB Control value byte (half step options and
                            etc, specified by drive manufacturer).      */              
#define SCTRK 17        /* Default number of sectors per track
                           (specified by drive manufacturer).           */
#define CLSTSZ 4        /* Default number of sectors per DOS cluster.   */
#define FATNM 2         /* Default number of DOS File Allocation Tables.*/
#define RSVSCT 1        /* Default number of reserved sectors.          */
#define RTNUM 256       /* Default number of root directory entries.    */
#define HIDSCT 0        /* Default number of hidden sectors.            */
#define FATSCT 11       /* Default number of sectors in FAT (5MB drive).*/
#define SSIZE 512       /* Sector size in bytes.                        */
#if SSIZE == 256
#define IMAX 31
#else
#define IMAX 16
#endif

/* - Parameters Passed to SASI Hard Disk Driver.                */

int cnum = 0;           /* Controller number [0-7].             */
int cmmd = 0;           /* Command code:
                            0 - Read sector.
                            1 - Write sector.
                            2 - Issue drive characteristics.
                            3 - Format drive.
                            4 - Verify sector.
                            5 - Test drive ready.               */
long csect = 0;         /* Starting sector number of operation. */
int ccnt = 0;           /* Sector count for operation.          */
int intlv = INTLV;      /* Interleave factor for format command */
int cmax = CNMAX;       /* Maximum number of cylinders.         */
int hmax = HNMAX;       /* Maximum number of heads.             */
int srwcc = WCCYL;      /* Starting reduced write current cyl.  */
int swpcc = WPCYL;      /* Starting write precompensation cyl.  */
int eccmax = ECCVAL;    /* Maximum ECC data burst length        */
int dcbcv = CVDEF;      /* DCB Control value byte (half step
                            options and etc)                    */
int sectrk = SCTRK;     /* Number of sector per track.          */
int bytsec = SSIZE;     /* Number of bytes per sector           */
int secclt = CLSTSZ;    /* Number of sectors per cluster        */
int fatnum = FATNM;     /* Number of FATs                       */
long ressct = RSVSCT;   /* Number of reserved sectors           */
int rootnm = RTNUM;     /* Number of entries in root directory  */
long hdnsct = HIDSCT;   /* Number of hidden sectors             */
long imgsz = CNMAX*HNMAX*SCTRK; /* Image size in sectors        */
int sctfat = FATSCT;    /* Number of sectors per FAT            */
char dbuf[SSIZE];       /* Sector data buffer.                  */

/* - Global Variables.                                          */

int sattr = 15;         /* Screen character attribute value.    */
int row;                /* Row number of cursor.                */
int col;                /* Column number of cursor.             */
int secoffs;            /* Offset into sector data buffer.      */


main()
{

/* - General variables. */

int ocmmd = 0;          /* Console input value.                         */
int exflg = 0;          /* Main loop control flag.                      */
int mflag = 0;          /* Subloop control flag.                        */
int wflag;              /* Modify (write) Sector loop control flag.     */
int nkflag;             /* Retrieve next-character-from-console control
                           flag.  Used within Modify Sector operation to
                           allow an editing feature (e.g. Cursor Right)
                           to be used by another editing feature without
                           duplicating that features code or making it
                           into a function.  A label and a goto state-
                           ment would have provided the same result but
                           the flag was used to follow K&R's coding
                           structures.                                  */
int edflag;             /* Modify Sector edit loop control flag.        */
int schar;
int tcol;               /* General - used to temporarily hold column
                                     number.                            */
int i;                  /* General                                      */
int t;                  /* General                                      */
int v;                  /* General                                      */
int pwrty;              /* Password retry counter.                      */
long seccnt = 1;        /* Current sector count remaining - used in
                           read and write multiple sector loops.        */
char instg[SSIZE];      /* Input string buffer - used in modify sector
                           operation for accepting string input.        */
char *oem;              /* OEM name string pointer - used in DOS pre-
                           paration for initializing boot sector.       */
char *pswd;             /* Pointer to password string.                  */
pswd = "JRSASI";


/* ---- OPENING DISPLAY AND PASSWORD ENTRY.     */

    scintf(SCLUP,STROW,STCOL,ENDROW,ENDCOL,sattr,SCCLR);  /* Clear Screen. */
    scintf(SETCURSOR,STROW+2,STCOL);
    printf("\t\t      *** JRSASI HARD DISK UTILITY ***\n\n");
    printf("\t      This utility has been provided in source form by:\n\n");
    printf("\t\t      TURNING POINT LOGIC CORPORATION\n");
    printf("\t\t      7012 DAKOTA AVENUE\n");
    printf("\t\t      CHANHASSEN, MN  55317\n");
    printf("\t\t      (612) 937-9032\n");
    printf("\t\t      BBS: (612) 926-5666 300/1200 24Hrs\n\n");
    printf("It has been placed into the public domain for non-commercial use only.\n");
    printf("The purpose of this utility is for initializing (formatting) a hard disk\n");
    printf("drive with a SASI interface and preparing the drive for DOS use.  If used\n");
    printf("improperly some or all data previously stored on the drive may be lost.\n");
    printf("To deter this situation from occurring a password is required for initiating\n");
    printf("utility operation.  Please enter your password below.\n\n");

    exflg = 1;
    pwrty = 3;
    while (pwrty > 0) {
         printf("Password: ");
         getline(&instg[0], SSIZE, NO);
         i = t = 0;
         while (instg[i] != CR) {
              if (instg[i] != pswd[i])
                   t = 1;
              i++;
         }
         if (t == 0 && i != 0) {
              pwrty = 0;
              exflg = 0;
         }
         else {
              printf("Incorrect\n");
              --pwrty;
         }
    }


/* ---- MAIN UTILITY LOOP.      */

    while(exflg == 0) {
         scintf(SCLUP,STROW,STCOL,ENDROW,ENDCOL,sattr,SCCLR);
         scintf(SETCURSOR,STROW+2,STCOL);
         printf("\n\n\n\t\t\t  JRSASI HARD DISK UTILITY\n\n\n");
         printf("\t\t1 - Read sector\n");
         printf("\t\t2 - Modify sector\n");
         printf("\t\t3 - Set drive characteristics\n");
         printf("\t\t4 - Format drive\n");
         printf("\t\t5 - Read multiple sectors (for testing)\n");
         printf("\t\t6 - Write multiple sectors (for testing)\n");
         printf("\t\t7 - Initialize disk for DOS\n");
         printf("\t\t9 - Exit\n\n");
         printf("Please enter desired function number - ");
         scanf("%d",&ocmmd);
         switch(ocmmd) {

         /* PERFORM READ SECTOR OPERATION       */

         case 1:
              mflag = 0;
              while(mflag == 0) {
                   scintf(SCLUP,STROW,STCOL,ENDROW,ENDCOL,sattr,SCCLR);
                   scintf(SETCURSOR,STROW+2,STCOL);
                   printf("\n\t\t\t\tREAD SECTOR\n\n");
                   printf("\t\t 1   - Change controller number [0-%d] - %d\n",CMAX - 1, cnum);
                   printf("\t\t 2   - Change logical sector number - %ld\n",csect);
                   printf("\t\t\"+\"  - Read/display sector and increment\n");
                   printf("\t\t\"-\"  - Read/display sector and decrement\n");
                   printf("\t\t<CR> - Read/display sector\n");
                   printf("\t\t 9 -   Return to main menu\n");
                   printf("\n\nEnter desired operation - ");
                   ocmmd = getch();
                   putch('\n');
                   putch('\n');
                   switch(ocmmd) {
                   case '1':
                        printf("Controller number [0-%d] - ",CMAX - 1);
                        scanf("%d",&ocmmd);
                        if (ocmmd >= 0 && ocmmd < CMAX)
                             cnum = ocmmd;
                        printf("\n\n");
                        break;
                   case '2':
                        printf("Logical sector number - ");
                        scanf("%ld",&csect);
                        break;
                   case '+':                    /* Read/display sector and  */
                        cmmd = 0;               /* increment sector number. */
                        ccnt = 1;
                        printf("\n\nREADING SECTOR...");
                        i = hdop();
                        printf("\n\n");
                        if (i == 0) {
                             dspc(SSIZE,0,csect,&dbuf[0],YES);
                             csect++;
                        }
                        break;
                   case '-':                    /* Read/display sector and  */
                        cmmd = 0;               /* decrement sector number. */
                        ccnt = 1;
                        printf("\n\nREADING SECTOR...");
                        i = hdop();
                        printf("\n\n");
                        if (i == 0) {
                             dspc(SSIZE,0,csect,&dbuf[0],YES);
                             csect--;
                        }
                        break;
                   case CR:                     /* Read/display sector. */
                        cmmd = 0;
                        ccnt = 1;
                        printf("\n\nREADING SECTOR...");
                        i = hdop();
                        printf("\n\n");
                        if (i == 0)
                             dspc(SSIZE,0,csect,&dbuf[0],YES);
                        break;
                   case '9':                    /* Exit from Read Sector. */
                        mflag = 1;
                        break;
                   default:
                        break;
                   }
              }
              break;

         /* PERFORM MODIFY SECTOR OPERATION.    */

         case 2:
              mflag = 0;
              while(mflag == 0) {
                   scintf(SCLUP,STROW,STCOL,ENDROW,ENDCOL,sattr,SCCLR);
                   scintf(SETCURSOR,STROW+2,STCOL);
                   printf("\n\t\t\t\tMODIFY SECTOR\n\n");
                   printf("\t\t1 - Change controller number [0-%d] - %d\n",CMAX - 1, cnum);
                   printf("\t\t2 - Change logical sector number - %ld\n",csect);
                   printf("\t\t3 - Modify and/or write sector\n");
                   printf("\t\t9 - Return to main menu\n");
                   printf("\n\nEnter desired operation - ");
                   scanf("%d",&ocmmd);
                   putch('\n');
                   switch(ocmmd) {
                   case 1:
                        printf("Controller number [0-%d] - ",CMAX - 1);
                        scanf("%d",&ocmmd);
                        if (ocmmd >= 0 && ocmmd < CMAX)
                             cnum = ocmmd;
                        printf("\n\n");
                        break;
                   case 2:
                        printf("Logical sector number - ");
                        scanf("%ld",&csect);
                        break;
                   case 3:
                        printf("Read in sector data then modify (y/n)? - ");
                        ocmmd = getch();
                        if (ocmmd == 'y' || ocmmd == 'Y') {
                             cmmd = 0;
                             ccnt = 1;
                             printf("\n\nREADING SECTOR...");
                             hdop();
                        }
                        secoffs = 0;
                        wflag = 0;

                        /* HANDLE EDITING OF SECTOR BUFFER DATA.        */

                        while (wflag == 0) {
                             scintf(SETCURSOR,STROW,STCOL);
                             scintf(RCHAR,&schar,&sattr);
                             scintf(SCLUP,STROW,STCOL,ENDROW,ENDCOL,sattr,SCCLR);
                             scintf(SETCURSOR,STROW,STCOL);
                             dspc(DSSIZE,secoffs,csect,&dbuf[0]+secoffs,NO);
                             dwpmpt();
                             row = STROW+1;
                             col = STCOL+6;
                             edflag = 0;
                             nkflag = 1;
                             while (edflag == 0) {
                                  scintf(SETCURSOR,row,col);
                                  if (nkflag == 0)
                                       nkflag = 1;
                                  else
                                       ocmmd = getch();
                                  if (ocmmd == 0)
                                       ocmmd = getch();
                                  switch(ocmmd) {
                                  case UPCUR:             /* Up Cursor  */
                                       if (row > STROW+1)
                                            row--;
                                       break;
                                  case DWCUR:             /* Down Cursor */
                                       if (row < (DSSIZE/16)+STROW)
                                            row++;
                                       break;
                                  case LFCUR:             /* Left Cursor */
                                       if (col < STCOL+29 && col != STCOL+6){
                                            if (col == (((col-STCOL-6)/3)*3)+STCOL+6)
                                                 col = col-2;
                                            else
                                                 col--;
                                       }
                                       else if (col > STCOL+31) {
                                                 if (col == (((col-STCOL-31)/3)*3)+STCOL+31)
                                                      col = col-2;
                                                 else
                                                      col--;
                                       }
                                       else if (col == STCOL+6 && row > STROW+1) {
                                                 col = STCOL+53;
                                                 row--;
                                       }
                                       else if (col == STCOL+31)
                                                 col = col-3;
                                       break;
                                  case RTCUR:             /* Right Cursor */
                                       if (col > STCOL+30 && col != STCOL+53){
                                            if (col == (((col-STCOL-32)/3)*3)+STCOL+32)
                                                 col = col+2;
                                            else
                                                 col++;
                                       }
                                       else if (col < STCOL+28) {
                                                 if (col == (((col-STCOL-7)/3)*3)+STCOL+7)
                                                      col = col+2;
                                                 else
                                                      col++;
                                       }
                                       else if (col == STCOL+53 && row < (DSSIZE/16)+STROW) {
                                                 col = STCOL+6;
                                                 row++;
                                       }
                                       else if (col == STCOL+28)
                                                 col = col+3;
                                       break;
                                  case 'S':               /* String Input */
                                       scintf(SCLUP,(DSSIZE/16)+2,STCOL,ENDROW,ENDCOL,sattr,SCCLR);
                                       scintf(SETCURSOR,(DSSIZE/16)+2,STCOL);
                                       printf("Enter ASCII string below (CR to complete):\n");
                                       getline(&instg[0], SSIZE, YES);
                                       if (col < STCOL+29) 
                                            i = ((row-1)*16)+((col-STCOL-6)/3)+secoffs;
                                       else
                                            i = ((row-1)*16)+((col-STCOL-31)/3)+8+secoffs;
                                       t = 0;
                                       while (i < SSIZE && instg[t] != CR) {
                                            dbuf[i] = instg[t];
                                            t++;
                                            i++;
                                       }
                                       edflag = 1;
                                       break;
                                  case 'V':               /* Fill Value */
                                       scintf(SCLUP,(DSSIZE/16)+2,STCOL,ENDROW,ENDCOL,sattr,SCCLR);
                                       scintf(SETCURSOR,(DSSIZE/16)+2,STCOL);
                                       printf("Fill value (hex) - ");
                                       scanf("%x",&ocmmd);
                                       for (i = 0; i < SSIZE; i++)
                                            dbuf[i] = ocmmd;
                                       edflag = 1;
                                       break;
                                  case PGDWC:             /* Page Down  */
                                       if (secoffs+DSSIZE < SSIZE)
                                            secoffs = secoffs+DSSIZE;
                                       edflag = 1;
                                       break;
                                  case PGUPC:             /* Page Up    */
                                       if (secoffs-DSSIZE >= 0)
                                            secoffs = secoffs-DSSIZE;
                                       edflag = 1;
                                       break;
                                  case 'W':               /* Write Data */
                                       cmmd = 1;
                                       ccnt = 1;
                                       scintf(SCLUP,(DSSIZE/16)+2,STCOL,ENDROW,ENDCOL,sattr,SCCLR);
                                       scintf(SETCURSOR,(DSSIZE/16)+2,STCOL);
                                       printf("WRITING SECTOR...");
                                       hdop();
                                       scintf(SETCURSOR,ENDROW,STCOL);
                                       edflag = 1;
                                       wflag = 1;
                                       break;
                                  case 'X':               /* Abort Editing */
                                       scintf(SETCURSOR,ENDROW,STCOL);
                                       edflag = 1;
                                       wflag = 1;
                                       break;
                                  default:        /* Hexidecimal Input  */
                                       v = -1;
                                       if (ocmmd >= 'A' && ocmmd <= 'F')
                                            v = ocmmd - 'A' + 10;
                                       if (ocmmd >= '0' && ocmmd <='9')
                                            v = ocmmd - '0';
                                       if (v != -1) {
                                            scintf(WCHAR,ocmmd,sattr,1);
                                            if (col > STCOL+30)
                                                 tcol = col-STCOL-7;
                                            else
                                                 tcol = col-STCOL-6;
                                            t = tcol/3;
                                            i = secoffs+((row-1)*16)+t;
                                            if (tcol-(t*3) == 1)
                                                 dbuf[i] = (dbuf[i] & 240) + v;
                                            else
                                                 dbuf[i] = (dbuf[i] & 15) + (v*16);
                                            if (dbuf[i] >= ' ' && dbuf[i] < 128)
                                                 v = dbuf[i];
                                            else
                                                 v = '.';
                                            scintf(SETCURSOR,row,STCOL+57+t);
                                            scintf(WCHAR,v,sattr,1);
                                            ocmmd = RTCUR;
                                            nkflag = 0;
                                       }
                                       break;
                                  }
                             }
                        }
                        break;
                   case 9:              /* Exit Modify Sector Operation */
                        mflag = 1;
                        break;
                   default:
                        break;
                   }
              }
              break;

         /* PERFORM ISSUE-DRIVE-CHARACTERISTIC OPERATION.       */

         case 3:
              mflag = 0;
              while(mflag == 0) {
                   scintf(SCLUP,STROW,STCOL,ENDROW,ENDCOL,sattr,SCCLR);
                   scintf(SETCURSOR,STROW+2,STCOL);
                   printf("\n\t\t\t  ISSUE DRIVE CHARACTERISTICS\n\n");
                   printf("\t\t 1 - Controller number [0-%d] - %d\n",CMAX - 1, cnum);
                   printf("\t\t 2 - Maximum number of cylinders - %d\n",cmax);
                   printf("\t\t 3 - Maximum number of heads - %d\n",hmax);
                   printf("\t\t 4 - Starting reduced write current cyl - %d\n",srwcc);
                   printf("\t\t 5 - Starting write precompensation cyl - %d\n",swpcc);
                   printf("\t\t 6 - Maximum ECC data burst length - %d\n",eccmax);
                   printf("\t\t 7 - DCB Control value (options) - %d\n",dcbcv);
                   printf("\t\t 8 - Number of sectors per track - %d\n",sectrk);
                   printf("\t\t 9 - Perform operation\n");
                   printf("\t\t10 - Return to main menu\n");
                   printf("\n\nEnter desired operation - ");
                   scanf("%d",&ocmmd);
                   putch('\n');
                   switch(ocmmd) {
                   case 1:
                        printf("Controller number [0-%d] - ",CMAX - 1);
                        scanf("%d",&ocmmd);
                        if (ocmmd >= 0 && ocmmd < CMAX)
                             cnum = ocmmd;
                        printf("\n\n");
                        break;
                   case 2:
                        printf("Maximum number of cylinders - ");
                        scanf("%d",&cmax);
                        break;
                   case 3:
                        printf("Maximum number of heads [0-15] - ");
                        scanf("%d",&hmax);
                        break;
                   case 4:
                        printf("Starting reduced write current cylinder - ");
                        scanf("%d",&srwcc);
                        break;
                   case 5:
                        printf("Starting write precompensation cylinder - ");
                        scanf("%d",&swpcc);
                        break;
                   case 6:
                        printf("Maximum ECC data burst length - ");
                        scanf("%d",&eccmax);
                        break;
                   case 7:
                        printf("DCB Control value - ");
                        scanf("%d",&dcbcv);
                        break;
                   case 8:
                        printf("Number of sectors per track - ");
                        scanf("%d",&sectrk);
                        break;
                   case 9:
                        cmmd = 2;
                        printf("\n\nISSUING DRIVE CHARACTERISTICS...\n\n");
                        hdop();
                        mflag = 1;
                        break;
                   case 10:
                        mflag = 1;
                        break;
                   default:
                        break;
                   }
              }
              break;

         /* PERFORM FORMAT DRIVE OPERATION.                     */

         case 4:
              scintf(SCLUP,STROW,STCOL,ENDROW,ENDCOL,sattr,SCCLR);
              scintf(SETCURSOR,STROW+2,STCOL);
              printf("\n\n\t\t\t\tFORMAT DISK\n\n");
              printf("WARNING: Failure to set the proper drive characteristics before\n");
              printf("\t formatting will set the drive to a default size of 5 MB.\n\n\n");
              printf("Please enter controller number [0-%d] - ",CMAX - 1);
              scanf("%d",&ocmmd);
              if(ocmmd >= 0 && ocmmd < CMAX)
                   cnum = ocmmd;
              else {
                   printf("\nIllegal Controller Number\n\n");
                   break;
              }
              printf("Please enter interleave factor [default - %d] - ",intlv);
              scanf("%d",&intlv);
              if (intlv > IMAX+1)
                   intlv = IMAX;
              printf("\n\nWARNING: All data residing on drive will be destroyed by the format -\n\n");
              printf("Are you sure you want to format controller #%d  (y/n)? - ",cnum);
              getline(&instg[0],SSIZE,YES);
              if (instg[0] == 'y' || instg[0] == 'Y') {
                   cmmd = 3;
                   printf("\n\nFORMATTING Controller #%d [interleave = %d]...",cnum,intlv);
                   hdop();
              }
              break;

         /* PERFORM READ-MULTIPLE-SECTOR OPERATION.             */

         case 5:
              mflag = 0;
              while(mflag == 0) {
                   scintf(SCLUP,STROW,STCOL,ENDROW,ENDCOL,sattr,SCCLR);
                   scintf(SETCURSOR,STROW+2,STCOL);
                   printf("\n\t\t\tREAD MULTIPLE SECTORS\n\n");
                   printf("\t\t1 - Change controller number [0-%d] - %d\n",CMAX - 1, cnum);
                   printf("\t\t2 - Change logical sector number - %ld\n",csect);
                   printf("\t\t3 - Change sector count - %ld\n",seccnt);
                   printf("\t\t4 - Perform operation\n");
                   printf("\t\t9 - Return to main menu\n");
                   printf("\n\nEnter desired operation - ");
                   scanf("%d",&ocmmd);
                   putch('\n');
                   switch(ocmmd) {
                   case 1:
                        printf("Controller number [0-%d] - ",CMAX - 1);
                        scanf("%d",&ocmmd);
                        if (ocmmd >= 0 && ocmmd < CMAX)
                             cnum = ocmmd;
                        printf("\n\n");
                        break;
                   case 2:
                        printf("Logical sector number - ");
                        scanf("%ld",&csect);
                        break;
                   case 3:
                        printf("Sector count - ");
                        scanf("%ld",&seccnt);
                        break;
                   case 4:
                        printf("\n\nDepress any key to abort test\n\n");
                        printf("READING SECTORS...");
                        cmmd = 0;
                        ccnt = 1;
                        i = 0;
                        while (seccnt != 0) {
                             i = hdop();
                             if (i != 0)
                                  break;
                             if ((ocmmd = kctest()) == 1) {
                                  printf("\n\nOperation terminated\n");
                                  break;
                             }
                             csect++;
                             seccnt--;
                        }
                        if (seccnt == 0 && i == 0) {
                             printf("\n\nOperation successfully completed\n");
                             seccnt = 1;
                        }
                        break;
                   case 9:          /* Exit Read-Multiple-Sector Operation. */
                        mflag = 1;
                        break;
                   default:
                        break;
                   }
              }
              break;

         /* PERFORM WRITE-MULTIPLE-SECTOR OPERATION.            */

         case 6:
              mflag = 0;
              while(mflag == 0) {
                   scintf(SCLUP,STROW,STCOL,ENDROW,ENDCOL,sattr,SCCLR);
                   scintf(SETCURSOR,STROW+2,STCOL);
                   printf("\n\t\t\tWRITE MULTIPLE SECTORS\n\n");
                   printf("\tData written to sectors is the data from the previous\n");
                   printf("\t    Read Sector command or Modify Sector command.\n\n");
                   printf("\t\t1 - Change controller number [0-%d] - %d\n",CMAX - 1, cnum);
                   printf("\t\t2 - Change logical sector number - %ld\n",csect);
                   printf("\t\t3 - Change sector count - %ld\n",seccnt);
                   printf("\t\t4 - Clear data buffer to zero\n");
                   printf("\t\t5 - Perform operation\n");
                   printf("\t\t9 - Return to main menu\n");
                   printf("\n\nEnter desired operation - ");
                   scanf("%d",&ocmmd);
                   putch('\n');
                   switch(ocmmd) {
                   case 1:
                        printf("Controller number [0-%d] - ",CMAX - 1);
                        scanf("%d",&ocmmd);
                        if (ocmmd >= 0 && ocmmd < CMAX)
                             cnum = ocmmd;
                        printf("\n\n");
                        break;
                   case 2:
                        printf("Logical sector number - ");
                        scanf("%ld",&csect);
                        break;
                   case 3:
                        printf("Sector count - ");
                        scanf("%ld",&seccnt);
                        break;
                   case 4:
                        for (i = 0; i < SSIZE; i++)
                             dbuf[i] = 0;
                        break;
                   case 5:
                        printf("Are you sure you want to do this (y/n)? - ");
                        getline(&instg[0],SSIZE,YES);
                        if (instg[0] == 'y' || instg[0] == 'Y') {
                             printf("\n\nDepress any key to abort test\n\n");
                             printf("WRITING SECTORS...");
                             cmmd = 1;
                             ccnt = 1;
                             i = 0;
                             while (seccnt != 0) {
                                  i = hdop();
                                  if (i != 0)
                                       break;
                                  if ((ocmmd = kctest()) == 1) {
                                       printf("\n\nOperation terminated\n");
                                       break;
                                  }
                                  csect++;
                                  seccnt--;
                             }
                             if (seccnt == 0 && i == 0) {
                                  printf("\n\nOperation successfully completed\n");
                                  seccnt = 1;
                             }
                        }
                        break;
                   case 9:         /* Exit Write-Multiple-Sector Operation. */
                        mflag = 1;
                        break;
                   default:
                        break;
                   }
              }
              break;

         /* PERFORM DOS PREPARATION OPERATION.                  */

         case 7:
              mflag = 0;
              while(mflag == 0) {
                   scintf(SCLUP,STROW,STCOL,ENDROW,ENDCOL,sattr,SCCLR);
                   scintf(SETCURSOR,STROW+2,STCOL);
                   printf("\n\t\t\tDOS INITIALIZATION\n\n");
                   printf("\t\t 1 - Controller number [0-%d] - %d\n",CMAX - 1, cnum);
                   printf("\t\t 2 - Maximum number of cylinders - %d\n",cmax);
                   printf("\t\t 3 - Maximum number of heads - %d\n",hmax);
                   printf("\t\t 4 - Starting reduced write current cyl - %d\n",srwcc);
                   printf("\t\t 5 - Starting write precompensation cyl - %d\n",swpcc);
                   printf("\t\t 6 - Maximum ECC data burst length - %d\n",eccmax);
                   printf("\t\t 7 - DCB Control value (options) - %d\n",dcbcv);
                   printf("\t\t 8 - Number of sectors per track - %d\n",sectrk);
                   printf("\t\t 9 - Bytes per sector - %d\n",bytsec);
                   printf("\t\t10 - Number of sectors per cluster - %d\n",secclt);
                   printf("\t\t11 - Number of File Allocation Tables - %d\n",fatnum);
                   printf("\t\t12 - Number of reserved sectors - %ld\n",ressct);
                   printf("\t\t13 - Number of root directory entries - %d\n",rootnm);
                   printf("\t\t14 - Number of hidden sectors - %ld\n",hdnsct);
                   printf("\t\t15 - Number of sectors in logical image - %ld\n",imgsz);
                   printf("\t\t16 - Number of sectors per FAT - %d\n",sctfat);
                   printf("\t\t19 - Perform operation\n");
                   printf("\t\t20 - Return to main menu\n");
                   printf("\n\nEnter desired operation - ");
                   scanf("%d",&ocmmd);
                   putch('\n');
                   switch(ocmmd) {
                   case 1:
                        printf("Controller number [0-%d] - ",CMAX - 1);
                        scanf("%d",&ocmmd);
                        if (ocmmd >= 0 && ocmmd < CMAX)
                             cnum = ocmmd;
                        printf("\n\n");
                        break;
                   case 2:
                        printf("Maximum number of cylinders - ");
                        scanf("%d",&cmax);
                        break;
                   case 3:
                        printf("Maximum number of heads [0-15] - ");
                        scanf("%d",&hmax);
                        break;
                   case 4:
                        printf("Starting reduced write current cylinder - ");
                        scanf("%d",&srwcc);
                        break;
                   case 5:
                        printf("Starting write precompensation cylinder - ");
                        scanf("%d",&swpcc);
                        break;
                   case 6:
                        printf("Maximum ECC data burst length - ");
                        scanf("%d",&eccmax);
                        break;
                   case 7:
                        printf("DCB Control value - ");
                        scanf("%d",&dcbcv);
                        break;
                   case 8:
                        printf("Number of sectors per track - ");
                        scanf("%d",&sectrk);
                        break;
                   case 9:
                        printf("Bytes per sector - ");
                        scanf("%d",&bytsec);
                        break;
                   case 10:
                        printf("Number of sectors per cluster - ");
                        scanf("%d",&secclt);
                        break;
                   case 11:
                        printf("Number of File Allocation Tables - ");
                        scanf("%d",&fatnum);
                        break;
                   case 12:
                        printf("Number of reserved sectors - ");
                        scanf("%ld",&ressct);
                        break;
                   case 13:
                        printf("Number of root directory entries - ");
                        scanf("%d",&rootnm);
                        break;
                   case 14:
                        printf("Number of hidden sectors - ");
                        scanf("%ld",&hdnsct);
                        break;
                   case 15:
                        printf("Number of sectors in logical image - ");
                        scanf("%ld",&imgsz);
                        break;
                   case 16:
                        printf("Number of sectors per FAT - ");
                        scanf("%d",&sctfat);
                        break;
                   case 19:
                        printf("\nWarning: DOS Initialization requires writing a new boot sector and a File\n");
                        printf("\t Allocation Table which will result in the loss of some or all files.\n\n");
                        printf("Are you sure you want to do this (y/n)? - ");
                        getline(&instg[0],SSIZE,YES);
                        putch('\n');
                        if (instg[0] == 'y' || instg[0] == 'Y') {
                             for (i = 0; i < SSIZE; i++)
                                  dbuf[i] = 0;
                             oem = "TPLC 1.2";
                             for (i = 3; i < 11; i++, oem++)
                                  dbuf[i] = *oem;
                             dbuf[11] = bytsec;
                             dbuf[12] = bytsec >> 8;
                             dbuf[13] = secclt;
                             dbuf[14] = ressct;
                             dbuf[15] = ressct >> 8;
                             dbuf[16] = fatnum;
                             dbuf[17] = rootnm;
                             dbuf[18] = rootnm >> 8;
                             dbuf[19] = imgsz;
                             dbuf[20] = imgsz >> 8;
                             dbuf[21] = 248;
                             dbuf[22] = sctfat;
                             dbuf[23] = sctfat >> 8;
                             dbuf[24] = sectrk;
                             dbuf[25] = sectrk >> 8;
                             dbuf[26] = hmax;
                             dbuf[27] = hmax >> 8;
                             dbuf[28] = hdnsct;
                             dbuf[29] = hdnsct >> 8;
                             dbuf[44] = cmax;   
                             dbuf[45] = cmax >> 8;   
                             dbuf[46] = intlv;  
                             dbuf[47] = intlv >> 8;  
                             dbuf[48] = srwcc;
                             dbuf[49] = srwcc >> 8;
                             dbuf[50] = swpcc; 
                             dbuf[51] = swpcc >> 8; 
                             dbuf[52] = eccmax;
                             dbuf[53] = eccmax >> 8;
                             dbuf[54] = dcbcv;
                             dbuf[55] = dcbcv >> 8;
                             csect = 0;
                             cmmd = 1;
                             ccnt = 1;          
                             i = hdop();
                             if (i != 0) {
                                  printf("\nDOS Initialization incomplete\n\n");
                                  printf("Depress any key to continue\n");
                                  getch();
                                  break;
                             }
                             for (i = 0; i < SSIZE; i++)
                                  dbuf[i] = 0;
                             dbuf[0] = 248;
                             dbuf[1] = 255;
                             dbuf[2] = 255;
                             csect = 1;
                             i = hdop();
                             if (i != 0) {
                                  printf("\nDOS Initialization incomplete\n\n");
                                  printf("Depress any key to continue\n");
                                  getch();
                             }
                        }
                        break;
                   case 20:             /* Exit DOS Preparation Operation. */
                        mflag = 1;
                        break;
                   default:
                        break;
                   }
              }
              break;

         /* EXIT UTILITY.       */

         case 9:
              exflg = 1;
              break;
         default:
              break;
         }
    }
}

/* ---- INTERFACE TO SASI HARD DISK DRIVER.

This function will set up the variables, call the disk driver, and
on return will perform any necessary error handling.

*/

static int recflg = 0;  /* Recursion flag between hdop and dperr to
                           terminate infinite nesting when dperr calls
                           hdop to reissue drive characters after an
                           error occurred.
                        */

int ecode;              /* Result code from SASI driver.        */
int retry;              /* Retry counter.                       */

hdop()
{

int dhaddr;             /* Dummy MSB of long pointer for small model.   */
int ctype = 0;


    switch(cmmd) {

    case 0:                     /* Perform read sector operation.       */
         if (recflg == 0)
              retry = 3;
         while (retry !=0) {
              ecode = hddrv(ctype,cmmd,cnum,ccnt,csect,&dbuf[0],dhaddr);
              dperr();
              if (ecode == 0)
                   retry = 1;
              retry--;
         }
         break;

    case 1:                     /* Perform write sector operation.      */
         if (recflg == 0)
              retry = 3;
         while (retry !=0) {
              ecode = hddrv(ctype,cmmd,cnum,ccnt,csect,&dbuf[0],dhaddr);
              dperr();
              if (ecode == 0)
                   retry = 1;
              retry--;
         }
         break;

    case 2:                     /* Issue drive characteristics operation. */
         if (recflg == 0)
              retry = 1;
         ecode = hddrv(ctype,cmmd,cnum,ccnt,csect,&dbuf[0],dhaddr,intlv,cmax,hmax,srwcc,swpcc,eccmax,dcbcv,sectrk);
         dperr();
         break;

    case 3:                     /* Perform format drive operation.      */
         if (recflg == 0)
              retry = 1;
         ecode = hddrv(ctype,cmmd,cnum,ccnt,csect,&dbuf[0],dhaddr,intlv);
         dperr();
         break;
    default:
         break;
   }
   return(ecode);
}

/* ---- DISPLAY ERROR CODE.             */

dperr()

{

int c;
int cmdtemp;
int etemp;

    if (recflg != 0)                    /* If called by itself, return  */
         return;                        /*  immediately.                */

    if (ecode != 0) {                   /* If an error occurred then    */
         etemp = ecode;                 /*  issue a reset to the SASI   */
         cmdtemp = cmmd;                /*  controller, and attempt to  */
         cmmd = 2;                      /*  reissue the drive char-     */
         recflg = 1;                    /*  acteristics.  If after      */
         irsasi();                      /*  retries then abort the      */
         hdop();                        /*  operation and notify the    */
         ecode = etemp;                 /*  operator of the problem.    */
         cmmd = cmdtemp;
         recflg = 0;
         if (retry == 1) {
              printf("\nAn error occurred - %x\n\n",ecode);
              printf("Depress any key to continue\n\n");
              c = getch();
         }
   }
}

/* ---- DUMMY DRIVER (uncomment for debug).
hddrv()
{
return(185);
}
*/
/* ---- DISPLAY DATA BLOCK.

This function will display a block of data which is assummed
to be a multiple of 256 bytes.  It will display the block in
the following format.

SECTOR: 5253

0000   41 42 43 44 45 46 47 48  49 4A 4B 4C 4D 4E 4F 50  ABCDEFGH IJLKMNOP
0010   20 10 03 FE E2 97 30 31  32 33 34 35 36 37 38 39   .....01 23456789

*/

dspc(dsize,oaddr,sval,dptr,dhold)
int dsize;      /* Number of bytes to display; multiple of DSSIZE.      */
int oaddr;      /* Starting offset address to be displayed on each
                   data line.                                           */
long sval;      /* Sector number to be displayed.                       */
char dptr[];    /* Pointer to data to be displayed.                     */
int dhold;      /* Command code for display:
                   NO (0) =  Do not ask operator if more data is to be
                             displayed and no blank lines between
                             "SECTOR: XXXX" and sector data.
                   YES (1) = Ask operator, after displaying DSSIZE
                             number of bytes, if more data is to be
                             displayed and put two blank lines between
                             "SECTOR: XXXX" and sector data.            */

{

int bcnt = 0;
int i = 0;
int t;
int hcnt;
int lpcnt;
int lncnt;
int kc;


    while (dsize > 0) {
         if (bcnt != 0) {
              printf("\nDisplay more (y/n)? - ");
              kc = getch();
              if (kc == 'n' || kc == 'N') {
                   dhold = NO;
                   goto done;
              }
         }
         if (dhold == YES) {
              putch('\n');
              putch('\n');
         }
         printf("SECTOR: %ld\n",sval);
         if (dhold == YES)
              putch('\n');
         lncnt = 256/16;
         while (lncnt > 0) {
              pnum(bcnt+(oaddr/DSSIZE));        /* Display offset address. */
              pnum(i);
              putch(' ');
              putch(' ');
              t = i;
              lpcnt = 2;
              while (lpcnt > 0) {       /* Display 16 bytes as hexidecimal */
                   hcnt = 8;            /*  characters.                    */
                   while (hcnt > 0) {
                        pnum(dptr[t]);
                        putch(' ');
                        t++;
                        hcnt--;
                   }
                   putch(' ');
                   lpcnt--;
              }
              putch(' ');
              hcnt = 16;                /* Display 16 bytes as ASCII chars. */
              while (hcnt > 0) {        
                   if (dptr[i] >= ' ' && dptr[i] < 128)
                        putch(dptr[i]);
                   else
                        putch('.');
                   i++;
                   hcnt--;
              }
              putch('\n');
              lncnt--;                /* Loop for next line.    */
         }
         bcnt++;                      /* Loop for next block (DSSIZE bytes). */
         dsize = dsize - DSSIZE;
    }
done:
    if (dhold == YES) {
         printf("\nDepress any key to continue\n\n");
         kc = getch();
    }
}

/* ----- CONVERT AND DISPLAY BYTE IN HEXIDECIMAL.

This function will convert an 8-bit value into two ASCII hexidecimal
values and display them at the current cursor position.  When this
function returns the cursor is located at the position just after
the least significant nibble of the converted byte.

*/

pnum(dval)
int dval;

{
int dv;

    dv = (dval >> 4) & 15;
    if (dv < 10)
         putch(dv + '0');
    else
         putch(dv+'A'-10);
    dv = dval & 15;
    if (dv < 10)
         putch(dv+'0');
    else
         putch(dv+'A'-10);
}

/* ----- DISPLAY MODIFY-SECTOR COMMAND PROMPTS.

This function displays command prompts used in the Modify Sector
operation.  It places this information near the bottom of the
screen, just under the display of sector data.

*/

dwpmpt()
{
    scintf(SCLUP,(DSSIZE/16)+2,STCOL,ENDROW,ENDCOL,sattr,SCCLR);
    scintf(SETCURSOR,(DSSIZE/16)+2,STCOL);
    printf("\nUse cursor control keys to edit data, enter:");
    printf(" \"W\" to write data, \"S\" to\nenter ASCII string,");
    printf(" \"V\" to fill data buffer with fill value, PgDn to\n");
    printf("edit next section of buffer, PgUp to edit previous");
    printf(" section of buffer,\n");
    printf("\"X\" to exit editing without writing.\n");
}

/* ----- GET LINE FROM CONSOLE INPUT.

This function will retrieve a string of characters from the console which
is terminated by a carriage return.  It deviates from the normal getline
function in that it properly handles backspaces.

*/

getline(s, lim, dsch)
char s[];
int lim;
int dsch;

{

int c, i;

    i = 0;
    while (--lim > 0 && (c = getch()) != CR) {
         if (c == BS) {
              if (i != 0) {
                   --i;
                   ++lim;
                   putch(BS);
                   putch(' ');
              }
         }
         else
              s[i++] = c;
         if (dsch == NO)
              c = '.';
         putch(c);
    }
    if (c == CR)
         s[i++] = c;
    s[i] = '\0';
    return(i);

}

/* Copyright 1985,86 by Turning Point Logic Corporation.  Placed in the
    Public Domain for personal, non-commercial use only.
*/
