/***************************************************************************

  Copyright Lattice Semiconductor, Inc. (1992)


***************************************************************************/
/***************************************************************************

   Program: gasm.c

   Purpose:
   To assemble a source file for one of the ipsGDS devices into a JEDEC file.

   Usage:
   gasm  sourcefile [JEDEC_filename]

   Usage is also available by calling the progam without any parameters, or
   with less than two parameters.

   Revision History:

$Log: gasm.c_v $
# Revision 1.1  1993/06/28  17:23:39  Mike_Lottridge
# Initial revision
#

****************************************************************************/

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <dos.h>


#define FALSE 0
#define TRUE 1
#define NEG 0
#define POS 1

#define MAX_FUSES 219
#define MAX_LINE_LENGTH 256
#define TITLE_LENGTH 1024
char *PROGRAM_NAME = "GASM";

typedef enum IO_TYPE { INPUT, OUT_INV, OUT_NONINV, OUT_0, OUT_1, UNUSED, NOT_IO};
typedef enum LINE_TYPE { TITLE, DEVICE, COMMENT, PIN_DEF, UES_ASCII, UES_HEX, UES_BINARY, LINE_ERROR};
typedef enum DEVICE_TYPE {ISPGDS22, ISPGDS18, ISPGDS14, ERROR};

typedef struct {
      enum IO_TYPE io_type;
      int input;
      char bank;  /* bank and cell number always relates to ispGDS22 */
      int  cell;
      }PIN_TYPE;

/* Assign bank letters to each pin. Array index = pin #. 'pin 0' not used */
char ispgds22_bank[] = "0AAA0AA0AAA0AAABBB0BB0BBB0BBB";

char ispgds18_bank[] = "0AAA0A0AA0AAABBB0B0BB0BBB";

char ispgds14_bank[] = "0AAA00A0AAABBB00B0BBB";


/* Assign IO cell numbers to each pin. Array index = pin #. 'pin 0' not used */
int ispgds22_cell[] =
{
-1,  0, 1, 2, -1, 3, 4, -1, 5, 6, 7, -1, 8, 9, 10,
    10, 9, 8, -1, 7, 6, -1, 5, 4, 3, -1, 2, 1, 0
};
int ispgds18_cell[] =
{
-1,  0, 1, 2, -1, 3, -1, 6, 7, -1, 8, 9, 10,
    10, 9, 8, -1, 7, -1, 4, 3, -1, 2, 1, 0
};
int ispgds14_cell[] =
{
-1,  0, 1, 2, -1, -1, 7, -1, 8, 9, 10,
    10, 9, 8, -1, -1, 3, -1, 2, 1, 0
};

PIN_TYPE pin[29]; /* 28 pins total, but 'pin 0' not used */
FILE *source_file, *outfile, *outfile2;
char *source_filename;
char *root_filename;

int fuse[ MAX_FUSES ];
int ues[32];
char title[ TITLE_LENGTH ];
enum DEVICE_TYPE device;
int line_number; /* line number of source file */

/*  function prototypes */
void main(int argc, char *argv[]);
void read_source( void );
void init(void);
enum LINE_TYPE get_line_type(char *line);
void get_title(char *line);
void get_device(char *line);
void get_pin_def(char *line);
void get_ues_ascii( char *line);
// void get_ues_hex( char *line);
// void get_ues_binary( char *line);
void line_error(int line_number, const char *errstr);
void quit(const char *errstr);
int equal( char *str1, const char *str2);
void assemble(void);
void write_jedec( FILE *jed_file);
void calc_fuse_checksum( char *fuse_checksum);
void write_doc( FILE *doc_file);


/*************************************************************************
  MAIN
**************************************************************************/

void main(int argc, char *argv[])
{
int i;
char *temp_filename, *temp_filename2;

printf(" %s: ispGDS Assembler \n", PROGRAM_NAME);
printf("     copyright (c)1993 Lattice Semiconductor\n");
printf("     Version 1.01 \n ");

if ( argc < 2 )
   {
   printf("\nUsage: %s sourcefile  [JEDEC_file] ", PROGRAM_NAME);
   exit(0);
   }

/* store the filename for later use */
source_filename = (char *)calloc( strlen(argv[1]) +1, sizeof(char) );
strcpy(source_filename, argv[1]);

root_filename =(char *)calloc( strlen(source_filename) +5, sizeof(char) );
for(i=0;i<strlen(source_filename);i++)
{
  if(source_filename[i] == '.')
    break;
  root_filename[i] = source_filename[i];
}

/* read the source file */
source_file = fopen(argv[1],"r");
if (source_file == NULL)
   {
   perror("\n ERROR OPENING SOURCE FILE: \n");
   exit(0);
   }

printf("\nReading Source file\n");
read_source();

if(fclose(source_file) == EOF)
  perror("\n Error Closing Source File: \n");


/* generate the proper fuse settings */
assemble();


/* Write out the JEDEC file */
if(argc>2)
  outfile = fopen(argv[2],"w");
else
{
  temp_filename = strdup(root_filename);
  outfile = fopen( strcat(temp_filename, ".JED"), "w");
}
if(outfile == NULL)
   {
   perror("ERROR OPENING OUTPUT FILE: ");
   exit(1);
   }

write_jedec(outfile);

if(ferror(outfile) != 0)
{
 printf("Error writing JEDEC file \n");
 clearerr(outfile);
 printf("Error cleared\n");
}
else
  printf("\nJEDEC file successfully written to disk\n");

if(fclose(outfile) == EOF)
  perror("\n Error Closing JEDEC File: \n");


/* Write out the Documentation file */
if(argc>3)
  outfile2 = fopen(argv[3],"w");
else
{
  temp_filename2 = strdup(root_filename);
  outfile2 = fopen( strcat(temp_filename2, ".DOC"), "w");
}
if(outfile2 == NULL)
   {
   perror("ERROR OPENING OUTPUT FILE: ");
   exit(1);
   }

write_doc(outfile2);

if(ferror(outfile2) != 0)
{
 printf("Error writing DOC file \n");
 clearerr(outfile2);
 printf("Error cleared\n");
}
else
  printf("\nDOC file successfully written to disk\n");


if(fclose(outfile2) == EOF)
  perror("\n Error Closing DOC File: \n");


exit(0);
} /* main */

/*****************************************************************
read_source()

reads the source file and stores all the data in data structures.

******************************************************************/

void read_source()
{
char line[MAX_LINE_LENGTH];
enum LINE_TYPE linetype;

/* intialize the global data structures */
init();

/* read each line of the source file and process according to type */

while( fgets(line, MAX_LINE_LENGTH, source_file) != NULL )
{
  line_number++; /* initialized in init() */
  linetype = get_line_type(line);

  switch (linetype)
   {
    case COMMENT:        break; /* ignore comments */

    case TITLE:          get_title(line);
                         break;

    case DEVICE:         get_device(line);
                         break;

    case PIN_DEF:        get_pin_def(line);
                         break;

   case UES_ASCII:       get_ues_ascii(line);
                         break;

//   case UES_HEX:         get_ues_hex(line);
//                         break;

//   case UES_BINARY:      get_ues_binary(line);
//                         break;

   case LINE_ERROR:      line_error(line_number, "Syntax Error");
                         break; 
   }

} /* while */

} /* read_source() */

/***********************************************************************
init()

Initialize Global Data Structures

************************************************************************/
void init()
{
int i;

   for(i=0; i < 29; i++)
   {
     pin[i].io_type = UNUSED; /* default for unused pins */
     pin[i].input = 0;
   }

   for(i=0; i < 121; i++)    /* unused array bits are set to 1 */
      fuse[i] = 1;
   for(; i < MAX_FUSES; i++) /* arch and UES default to 0 */
      fuse[i] = 0;


   for(i=0;i<TITLE_LENGTH;i++)
      title[i] = NULL;

   for(i=0;i<32;i++)
      ues[i] = 0;

   line_number = 0;
   device = ERROR;


} /* init() */


/**********************************************************************
enum LINE_TYPE get_line_type(char *line)

Takes a line from the source file, checks the front portion of the
line, looking for PIN, UES_xxx, TITLE, or DEVICE keywords.

************************************************************************/
enum LINE_TYPE get_line_type(char *line)
{
char token[20];
int i, j, len;

  for(i=0;i<20;i++) token[i] = NULL; /* initialize token string */
  len = strlen(line);

  for(i=0; i<len; i++)
  {
    if(line[i] != ' ') break; /* get past any spaces at the front of the line */
  }
  if( i==len) return(COMMENT); /* blank line, ignore, like comment */
  if( line[i] == '\n') return(COMMENT); /* The line is blank */

  if( line[i] == '"') return(COMMENT); /* The whole line is a comment */

  j=0;
  while( isalpha(line[i])|| (line[i]=='_') )
  {
    token[j] = toupper(line[i]); /* make it all upper case */
    i++; j++;
  }

  if(equal(token, "TITLE"))      return(TITLE);
  if(equal(token, "DEVICE"))     return(DEVICE);
  if(equal(token, "PIN"))        return(PIN_DEF);
  if(equal(token, "UES_ASCII"))  return(UES_ASCII);
  if(equal(token, "UES_HEX"))    return(UES_HEX);
  if(equal(token, "UES_BINARY")) return(UES_BINARY);
  /* default */
  return(LINE_ERROR);

}  /* get_line_type */

/**********************************************************************
get_title( char *line)

Takes a line from the source file, beginning with the keyword TITLE,
extracts the text betweeen single quotes, and stores in the global
structure 

***********************************************************************/

void get_title( char *line)
{
int i,j,len;

  len = strlen(line);
  for( i=0; i<len; i++)
  {
     if( line[i] == 39)  /* find the ' to start the TITLE block */
       break;
   }
  /* if no single quote on line with Title, exit with error message */
  if( (i==len) || (line[i]=='\n') )
     line_error(line_number,"Could not find single quote to start Title section");

  /* if quote found, store Title block in a buffer */
  i++;
  for( j=0; j < TITLE_LENGTH; j++)
  {
    if( line[i] == 39) /* if single quote found */
       break;
    else
    {
       title[j] = line[i];
       i++;
       if(title[j] == '\n')
       {
         if( fgets(line, MAX_LINE_LENGTH, source_file) == NULL )
           line_error(line_number,"Can't find ' to end TITLE block");
         line_number++; /* if newline, increment line_number count*/
         i = 0;         /* starting a new line */
       }
    }
   }

} /* get_title */

/**********************************************************************
get_device( char *line)

Takes a line from the source file, beginning with the keyword DEVICE,
and extracts the device type, storing it in the device variable. This
routine also stores the bank letter and IO cell numbers in the pin[]
structure.

***********************************************************************/

void get_device( char *line)
{
int i,j,len;
char dev[256];

  len = strlen(line);
  for( i=0; i<len; i++)
  {
     if( line[i] == '=')  /* find the =  */
     break;
  }
  /* if no equal sign on line DEVICE, exit with error message */
  if(i==len)
  {
    printf("\n Error on Line %d: Could not find equal sign for DEVICE statement\n", line_number);
    exit(1);
  }

  /* if = found, get the device type */
  i++;
  for(j=0 ; i < len; i++)
  {
    if( isalnum(line[i]) )
    {
      dev[j] = toupper(line[i]);
      j++;
    }
  }
  dev[j] = NULL; /* terminate the string */

  if( equal( dev, "ISPGDS22") ) device = ISPGDS22;
  else if( equal( dev, "ISPGDS18") ) device = ISPGDS18;
  else if( equal( dev, "ISPGDS14") ) device = ISPGDS14;
  else
     quit(" \nInvalid device type, or syntax error in DEVICE definition statement\n");

  /* Store bank and io cell info for each pin, specific to device type */
  if(device == ISPGDS22)
  {
     for(i=1;i<29;i++)
     {
       pin[i].bank = ispgds22_bank[i];
       pin[i].cell = ispgds22_cell[i];
     }
  }
  if(device == ISPGDS18)
  {
     for(i=1;i<25;i++)
     {
       pin[i].bank = ispgds18_bank[i];
       pin[i].cell = ispgds18_cell[i];
     }
  }
  if(device == ISPGDS14)
  {
     for(i=1;i<21;i++)
     {
       pin[i].bank = ispgds14_bank[i];
       pin[i].cell = ispgds14_cell[i];
     }
  }

} /* get_device */

/**********************************************************************
get_pin_def( char *line)

Takes a line from the source file, beginning with the keyword PIN,
and stores the pin assignments

***********************************************************************/

void get_pin_def( char *line)
{
int i,j,k,len,left_side;
int outpin, inpin;
int outpin_polarity = POS;
int found_equal_sign = FALSE;
char  tempnum[20]; /* leave plenty of room for overruns, just in case */
char temp_alpha[20];
char input_type;
int found_inpin = FALSE;

  for(i=0;i<20;i++)
  {
     tempnum[i] = 0; /* initialize array */
     temp_alpha[i] = 0;
   }

  len = strlen(line);

/* the presence of the PIN keyword has already been established, so */
/* only look for the numbers and negations */
  for(j=0, i=0; i<len; i++)
  {
     if( line[i] == '=')  /* only read the left side of the equation */
     {
       found_equal_sign = TRUE;
       break;
     }
     if( line[i] == '!') /* get the polarity of the left side */
       outpin_polarity = NEG;
     if( isdigit(line[i]) )
     {
       tempnum[j] = line[i];
       j++;
     }
  }
  if(found_equal_sign != TRUE)
    line_error(line_number," Syntax error in PIN definition \n");

  tempnum[j] = NULL; /* null terminate the tempnum string */
  outpin = atoi(tempnum);
  if( !valid_pin(outpin) )
  {
    printf("ERROR on line %d: Pin %d is not a valid I/O pin number", line_number, outpin);
    exit(1);
   }

  for(j=0, k=0; i<len; i++) /* keep the i value to point to right side of equation */
  {
     if( line[i] == '!') /* check for illegal polarity definition on right side */
        line_error(line_number,"Negation not allowed on right side of equation ");
     if( isdigit(line[i]) )
     {
       found_inpin = TRUE;
       tempnum[j] = line[i];
       j++;
     }
    if(isalpha(line[i]) )
    {
      temp_alpha[k] = toupper(line[i]);
      k++;
    }
  }

  tempnum[j] = NULL; /* null terminate the tempnum string */
  inpin = atoi(tempnum);
  if( found_inpin)
  {
    if( !valid_pin(inpin) )
    {
       printf("ERROR on line %d: Pin %d is not a valid I/O pin number", line_number, inpin);
       exit(1);
    }
  }
  input_type = temp_alpha[0]; /* only the first letter is actually used */

  /* check if pin previously defined, and define output type */
  if( pin[outpin].io_type != UNUSED)
  {
    printf("\nERROR on Line %d: PIN %d already been defined\n", line_number, outpin);
    exit(1);
  }
  else if( input_type == 'P')      /* right side is PIN definition */
  {
    if( !((pin[inpin].io_type == INPUT) || (pin[inpin].io_type == UNUSED)) )
    {
      printf("\nERROR on Line %d: Pin %d has already been assigned as an output\n", line_number, inpin);
      exit(1);
    }

    pin[outpin].input = inpin; /* assign input pin to connect to */
    pin[inpin].io_type = INPUT;

    if(outpin_polarity == NEG)
        pin[outpin].io_type = OUT_INV;
    else
        pin[outpin].io_type = OUT_NONINV;
  }
  else if(input_type == 'L')              /* says LOW or LO */
     pin[outpin].io_type = OUT_0;
  else if(input_type == 'H')              /* says HIGH or HI */
     pin[outpin].io_type = OUT_1;
  else
  {
     printf("\nSyntax Error on Line %d\n", line_number);
     exit(1);
   }

} /* get_pin_def */

/**********************************************************************
get_ues_ascii( char *line)

Takes a line from the source file, beginning with the keyword UES_ASCII,
and extracts the UES string, storing it in the global variable. 

***********************************************************************/
void get_ues_ascii( char *line)
{
int i,j,len;
char tmp;
char ues_temp[MAX_LINE_LENGTH];

  for( i=0;i<MAX_LINE_LENGTH;i++)
     ues_temp[i] = 0;

  len = strlen(line);
  for( i=0; i<len; i++)
  {
     if( line[i] == '=')  /* find the =  */
     break;
  }
  /* if no equal sign on line , exit with error message */
  if(i==len)
  {
    printf("\n Error on Line %d: Could not find equal sign\n", line_number);
    exit(1);
  }

  /* if = found, get the UES  */
  for(j=0 ; i < len; i++)
  {
    if( isalnum(line[i]) )
    {
      ues_temp[j] = toupper(line[i]);
      j++;
    }
  }
  for(i=0;i<4;i++)
  {
     tmp = ues_temp[i];
     for(j=0;j<8;j++)
     {
       if( tmp & 0x80 ) /* high bit set */
          ues[(i*8)+j] = 1;
       else
          ues[(i*8)+j] = 0;
       tmp = tmp << 1;
     }
  }

} /* get_ues_ascii */


/*********************************************************************
int valid_pin(int pin)

Checks that the pin number specified is a valid I/O pin for the device
specified

***********************************************************************/
int valid_pin(int pin)
{
  if(device == ERROR)
  {
     printf("\nERROR: Device not specified. DEVICE statement needed before equations\n");
     exit(1);
  }


  if(device == ISPGDS22)
  {
     if( (pin >28) || (pin <1) )
        return(FALSE);

     switch(pin)
     {
        case  4:        return(FALSE);
        case  7:        return(FALSE);
        case 11:        return(FALSE);
        case 18:        return(FALSE);
        case 21:        return(FALSE);
        case 25:        return(FALSE);
        default:        return(TRUE);
      }
  }
  else if(device == ISPGDS18)
  {
     if( (pin >24) || (pin <1) )
        return(FALSE);

     switch(pin)
     {
        case  4:        return(FALSE);
        case  6:        return(FALSE);
        case  9:        return(FALSE);
        case 16:        return(FALSE);
        case 18:        return(FALSE);
        case 21:        return(FALSE);
        default:        return(TRUE);
      }
  }
  else if(device == ISPGDS14)
  {
    if( (pin >20) || (pin <1) )
        return(FALSE);

     switch(pin)
     {
        case  4:        return(FALSE);
        case  5:        return(FALSE);
        case 7:         return(FALSE);
        case 14:        return(FALSE);
        case 15:        return(FALSE);
        case 17:        return(FALSE);
        default:        return(TRUE);
      }
   }
   else
   {
     printf("\nERROR: Device not specified. Device must be specified before equations. \n");
     exit(1);
   }


return(FALSE); /* shouldn't be reached */

} /* valid_pin */

/*********************************************************************
void error_check()

Checks that the specified design can fit into the device.
- each input/output pair must be in different banks

***********************************************************************/
void error_check()
{
int i;

  if(device == ISPGDS22)
  {
     for(i=1;i<29;i++)
     {
        if(pin[i].input != 0) /* input pin assigned to this output */
        {
           if( ispgds22_bank[i] == ispgds22_bank[ pin[i].input ])
           {
              printf("\nERROR: Output Pin %d and input pin %d must be in different banks\n",i,pin[i].input);
              exit(1);
            }
         }
      }
    }
  else if(device == ISPGDS18)
  {
     for(i=1;i<25;i++)
     {
        if(pin[i].input != 0) /* input pin assigned to this output */
        {
           if( ispgds18_bank[i] == ispgds18_bank[ pin[i].input ])
           {
              printf("\nERROR: Output Pin %d and input pin %d must be in different banks\n",i,pin[i].input);
              exit(1);
            }
         }
     }
  }

  else if(device == ISPGDS14)
  {
     for(i=1;i<21;i++)
     {
        if(pin[i].input != 0) /* input pin assigned to this output */
        {
           if( ispgds14_bank[i] == ispgds14_bank[ pin[i].input ])
           {
              printf("\nERROR: Output Pin %d and input pin %d must be in different banks\n",i,pin[i].input);
              exit(1);
            }
         }
      }
   }
   else quit("\n Device not specified\n");

} /* error_check () */  


/*********************************************************************
void line_error(int line_number, const char *errstr)

prints out an error message, with a line number, and exits

***********************************************************************/

void line_error(int line_number, const char *errstr)
{

printf("\n ERROR on Line %d: %s \n", line_number, errstr);
exit(1);

} /* line_error() */


/*********************************************************************
void quit(const char *errstr)

prints out an error message and terminates program


***********************************************************************/

void quit(const char *errstr)
{

#ifdef DEBUG0
show_error("in quit() ");
#endif

printf("\n ERROR: %s \n", errstr);
fcloseall();
exit(1);

} /* quit() */


/*********************************************************************
int equal( char *str1, const char *str2)

compare two strings, and returns 1 if equal, or 0 if not;

***********************************************************************/

int equal( char *str1, const char *str2)
{

if( !strcmp( str1, str2) ) return(1);
else return(0);
}



/********************************************************************/
/********************************************************************/
/*                         OUTPUT ROUTINES                          */
/********************************************************************/
/********************************************************************/

/*********************************************************************
void assemble()

Fills the fuse[] array with the proper fuse settings, which can
then be used to generate the JEDEC map

***********************************************************************/
void assemble(void)
{
int i, arch_bit[3];
int fuse_num;
int number_of_pins;

/* fuse[] was initialized to all zeros in the init() function */

 if(device==ISPGDS22)
    number_of_pins = 28;
 if(device==ISPGDS18)
    number_of_pins = 24;
 if(device==ISPGDS14)
    number_of_pins = 20;

 for(i=1;i<number_of_pins+1;i++)
 {

    if(pin[i].input != 0) /* connection in array needed */
    {
       if(pin[i].bank == 'A')
          fuse_num = 10 + (pin[i].cell * 11) - pin[ (pin[i].input) ].cell;
       if(pin[i].bank == 'B')
          fuse_num = (10 - pin[i].cell) + pin[ (pin[i].input) ].cell * 11;
       fuse[ fuse_num ] = 0;
    }

/* set ARCH bits */
    switch(pin[i].io_type)
    {
       case INPUT:          arch_bit[0]=1;
                            arch_bit[1]=0;
                            arch_bit[2]=1;
                            break;

       case OUT_NONINV:     arch_bit[0]=0;
                            arch_bit[1]=1;
                            arch_bit[2]=1;
                            break;

       case OUT_INV:        arch_bit[0]=0;
                            arch_bit[1]=0;
                            arch_bit[2]=1;
                            break;

       case OUT_1:          arch_bit[0]=0;
                            arch_bit[1]=1;
                            arch_bit[2]=0;
                            break;

       case OUT_0:          arch_bit[0]=0;
                            arch_bit[1]=0;
                            arch_bit[2]=0;
                            break;

       default:             arch_bit[0]=1;
                            arch_bit[1]=1;
                            arch_bit[2]=1;
                            break;
    } /* switch */

    if(pin[i].bank == 'A')
    {
       fuse[(174-pin[i].cell)] = arch_bit[0];
       fuse[(196-pin[i].cell)] = arch_bit[1];
       fuse[(218-pin[i].cell)] = arch_bit[2];
    }
    else if(pin[i].bank == 'B')
    {
       fuse[(153+pin[i].cell)] = arch_bit[0];
       fuse[(175+pin[i].cell)] = arch_bit[1];
       fuse[(197+pin[i].cell)] = arch_bit[2];
    }

 }

/* Set UES bits */
   fuse_num = 121;
   for(i=0;i<32;i++, fuse_num++)
      fuse[fuse_num] = ues[i];

}

/*********************************************************************
void write_jedec()

***********************************************************************/

void write_jedec(FILE *jed_file)
{
char fuse_checksum[5];
char devstr[10], datestr[20];
int len, i, j, qp;
struct date d;


  switch(device)
  {
     case ISPGDS22:      qp = 28;
                         strcpy(devstr, "ispGDS22");
                         break;
     case ISPGDS18:      qp = 24;
                         strcpy(devstr, "ispGDS18");
                         break;
     case ISPGDS14:      qp = 20;
                         strcpy(devstr, "ispGDS14");
                         break;
  }


  getdate(&d);
  sprintf(datestr,"%d/%d/%d", d.da_mon, d.da_day, d.da_year);

  for(i=0;i<5;i++)
     fuse_checksum[i] = NULL;
  calc_fuse_checksum( (char *) fuse_checksum );

  fputc(2,jed_file); /* STX: start of transmission char */
  fprintf(jed_file,"\nJEDEC file for %s , created on %s \n",devstr,datestr);
  fprintf(jed_file,"Created by GASM v1.0\n\n");

  len = strlen(title);
  for(i=0;i<len;i++)
  {
     fputc(title[i], jed_file);
  }
  fprintf(jed_file, "\n*\n");
  fprintf(jed_file, "F0*\n");

  fprintf(jed_file, "QP%d* QF219*\n", qp);

  fprintf(jed_file, "L0000\n");
  for(i=0;i<11;i++)
  {
     for(j=0;j<11;j++)
     {
        if( fuse[ (i*11 + j)] == 0)
          fputc('0',jed_file);
        else
          fputc('1',jed_file);
     }

     if(i!=10) fputc('\n',jed_file);

  }
  fprintf(jed_file, "*\nL0121\n");
  for(i=0;i<32;i++)
  {
     if( fuse[ i + 121] == 0)
         fputc('0',jed_file);
      else
         fputc('1',jed_file);
      }

  fprintf(jed_file, "*\nL0153\n");
  for(i=0;i<22;i++)
  {
     if( fuse[ (i + 153)] == 0)
       fputc('0',jed_file);
     else
       fputc('1',jed_file);
   }

  fprintf(jed_file, "*\nL0175\n");
  for(i=0;i<22;i++)
  {
     if( fuse[ (i + 175)] == 0)
       fputc('0',jed_file);
     else
       fputc('1',jed_file);
   }

  fprintf(jed_file, "*\nL0197\n");
  for(i=0;i<22;i++)
  {
     if( fuse[ (i + 197)] == 0)
       fputc('0',jed_file);
     else
       fputc('1',jed_file);
   }

fprintf(jed_file, "*\n");

fprintf(jed_file, "C%s*\n",fuse_checksum);
fputc(3, jed_file); /* ETX: end of transmission char */
fprintf(jed_file, "0000\n");

} /* write_jedec */


/*********************************************************************
void calc_fuse_checksum()

calculate the 16-bit fuse checksum, and store in the passed array
***********************************************************************/
void calc_fuse_checksum( char *fuse_checksum)
{
int i;
int checksum = 0;

  for(i=0;i<MAX_FUSES;i++)
  {
    if(fuse[i] == 1)
      checksum = checksum + ( 0x0001 << (i % 8) );
  }

  /* print checksum in hex to string to be returned */
   sprintf(fuse_checksum, "%X", checksum);
   printf("\nFuse Checksum = %X\n",checksum);

} /* calc_fuse_checksum */

/*********************************************************************
void write_doc()

***********************************************************************/

void write_doc(FILE *doc_file)
{

int maxpins;
int i;
char devstr[10];

  for(i=0;i<10;i++)
    devstr[i]=NULL;

  switch(device)
  {
     case ISPGDS22:      maxpins = 28;
                         strcpy(devstr, "ispGDS22");
                         break;
     case ISPGDS18:      maxpins = 24;
                         strcpy(devstr, "ispGDS18");
                         break;
     case ISPGDS14:      maxpins = 20;
                         strcpy(devstr, "ispGDS14");
                         break;
  }

fprintf(doc_file,"Document file from GASM\nSource File: %s\n\n",source_filename);
fprintf(doc_file,"Device: %s\n\n",devstr);

fprintf(doc_file,"EQUATIONS\n");
for(i=1; i<=maxpins; i++)
{
  if(pin[i].io_type == OUT_0)
     fprintf(doc_file," PIN %d = LOW \n", i);
  else if(pin[i].io_type == OUT_1)
     fprintf(doc_file," PIN %d = HIGH \n", i);
  else if(pin[i].io_type == OUT_INV)
     fprintf(doc_file,"!PIN %d = PIN %d \n", i,pin[i].input);
  else if(pin[i].io_type == OUT_NONINV)
     fprintf(doc_file," PIN %d = PIN %d \n", i,pin[i].input);
}


/* *************
fprintf(doc_file,"PIN DEFINITIONS\N");
for(i=1; i<maxpins; i++)
{
  if(pin[i].io_type == NOT_IO)
     fprintf(doc_file," PIN %d is not an I/O pin \n", i);
  else if(pin[i].io_type == UNUSED)
     fprintf(doc_file," PIN %d is an unused I/O pin \n", i);
  else if(pin[i].io_type == INPUT)
     fprintf(doc_file," PIN %d is an input pin \n", i);
  else if(pin[i].io_type == OUT_0)
     fprintf(doc_file," PIN %d is an output pin, fixed to a TTL low \n", i);
  else if(pin[i].io_type == OUT_1)
     fprintf(doc_file," PIN %d is an output pin, fixed to a TTL high \n", i);
  else if(pin[i].io_type == OUT_INV)
     fprintf(doc_file," PIN %d is an output pin, with an inverted output \n", i);
  else if(pin[i].io_type == OUT_NONINV)
     fprintf(doc_file," PIN %d is an output pin \n", i);
}
************** */


} /* write_doc */

