/*
**            Copyright © 1993 Christer Gessler
**
**  This program is only copyrighted to maintain its status as free
**  software.
**  
**
**  This program is free software; you can redistribute it and/or modify
**  it under the terms of the GNU General Public License as published by
**  the Free Software Foundation; either version 2, or (at your option)
**  any later version.
**
**  This program is distributed in the hope that it will be useful,
**  but WITHOUT ANY WARRANTY; without even the implied warranty of
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**  GNU General Public License for more details.
**
**  You should have received a copy of the GNU General Public License
**  along with this program; if not, write to the Free Software
**  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

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

struct Pin {
  struct Pin *Connection;
  int        HostGate;
  long       Value;
};

struct Point {
  struct Pin *Source;
  struct Pin **Targets;
  long       Value;
};

struct Special {
  int  Number;
  long Type;
  union Shared {
    char *Table;
    long Value;
  } Data;
};

struct GateType {             /*  gate definition  */
  char           Name[10];
  short          Inputs, Outputs;
  short          Specials;
  char           **InputNames, **OutputNames;
  struct Special *SpecialTypes;
  char           *Table;  
};

struct Gate {                 /*  databank for a gate in use  */
  int        GateType;
  short      Inputs, Outputs;
  struct Pin *InputPins, *OutputPins;
};

void command_line_interface (void);      /*  interactive mode                 */
void print_help (void);                  /*  prints help for commands         */
char **parse_line (char *line);          /*  split line into words            */
int  load_input_file (char *filename);   /*  loads inputdata for circuit      */
int  load_gates (char *filename);        /*  load gate definitions            */
int  load_source (char *filename);       /*  load circuit definition          */
int  interpret_source (char *filename, char **loaded_files);  /*  parse circuit source file        */
int  setup_connection (int gate[2][4], int line); /* define gates and connections */
int  test_circuit (void);                /*  test loaded circuit              */
int  get_gate_type (char *gate_name);    /*  get type number of a gate        */
int  get_pin_number (int *gatedata, char *pinname);  /*  get number of a pin  */
int  run_circuit (void);                 /*  runs the circuit                 */
void circuit_error (char *err_string, int gate_num); /*  print errormessage   */
void source_error (char *err_string, int line_num);  /*  print errormessage   */
int  check_for_file_error (FILE *file);  /*  get type of file error           */
void free_outputdata (char*** outputdata);  /*  free outputdata arrays        */
void free_inputdata (void);              /*  initializes input data array     */
void free_circuit (void);                /*  initializes circuit structures   */
void quit (char *error_message, int return_code);  /*  exit program           */

#define VERSION  "1.0"       /*  current program version  */
#define FILE_ERROR  -1        /*  a return code  */
#define MEM_ERROR   -2        /*  a return code  */
#define TAB  '\t'
#define MAX_LINE_LENGTH  81  /*  maximum length of the lines in the source  */
#define NUMBER_OF_ELEMENTARY_GATES  7
#define HIGH    1
#define LOW     0
#define MEMORY  10
#define TOGGLE  11
#define SET          1           /*  a special input type  */
#define TRIGGER_N_F  2           /*  a special input type  */
#define TRIGGER_P_F  3           /*  a special input type  */
#define OUTPUT_MODE  0x1         /*  output mode bit  */

#ifdef AMIGA
char versionstring[] = "\0$VER: Logic Evaluator "VERSION"  29/6 1993";
#endif


struct GateType Elementary_Gates[] = {
  {  "NOT", 1, 1, 0, NULL, NULL, NULL, NULL  },
  {  "AND", -1, 1, 0, NULL, NULL, NULL, NULL  },
  {  "OR", -1, 1, 0, NULL, NULL, NULL, NULL  },
  {  "XOR", 2, 1, 0, NULL, NULL, NULL, NULL  },
  {  "NAND", -1, 1, 0, NULL, NULL, NULL, NULL  },
  {  "NOR", -1, 1, 0, NULL, NULL, NULL, NULL  },
  {  "XNOR", 2, 1, 0, NULL, NULL, NULL, NULL  }
}, *Expansion_Gates = NULL;

int NumberOfGatesUsed = 0, NumberOfExpansionGates = 0;
int NumberOfInputs = 0, NumberOfOutputs = 0;
int NumberOfPhases = 0, NumberOfTests = 0;
unsigned long Mode = 0;
struct Gate *CircuitGates = NULL;
char **InputData = NULL;


void main (int argc, char *argv[])
{
  int m, n, arg, pos = 0, errs;
  char num_buffer[12], usage[] =
    "Usage: LogEv [-Options] [<sourcefile>] [Gates <gatefile> <gatefile> ...]\n";

  if (argc > 1) {
    if (argv[1][0] == '?')
        quit(usage, 0);
    else {
      for (arg = 1; arg < argc; arg++)
	{
	  if (argv[arg][0] == '-')
	    for (n = 1; argv[arg][n]; n++)
	      switch (argv[arg][n])
		{
		case '?':
		case 'h':
		case 'H':
		  quit(usage, 0);
		  break;
		case 'o':
		case 'O':
		  switch (argv[arg][++n])
		    {
		    case 't':
		    case 'T':
		      Mode |= OUTPUT_MODE;
		      break;
		    case 'c':
		    case 'C':
		      Mode &= ~OUTPUT_MODE;
		      break;
		    default:
		      quit ("Unknown output mode option.\n", 20);
		      break;
		    }
		  break;
		case 't':
		case 'T':
		  for (m = ++n; isdigit (argv[arg][m]) && (m - n < 11); m++)
		    num_buffer[m - n] = argv[arg][m];
		  num_buffer[m - n] = '\0';
		  NumberOfTests = atoi (num_buffer);
		  if (NumberOfTests < 1) {
		    NumberOfTests = 0;
		    printf ("Illegal specification of number of testruns.\n");
		  }
		  n = m - 1;
		  break;
		default:
		  quit ("Unknown option.\n", 20);
		  break;
		}
	  else {
	    if (! stricmp (argv[arg], "gates")) {
	      while (++arg < argc) {
		errs = load_gates (argv[arg]);
		if (errs)
		  if (errs == FILE_ERROR)
		    printf ("Error: Couldn't open gate file %s.\n", argv[arg]);
		  else
		    printf ("There were %d error(s) in gatefile %s.\n", errs,
			    argv[arg]);
	      }
	    }
	    else if (! pos)
	      pos = arg;
	    else
	      quit ("Bad args.\n", 20);
	  }
	}
      if (pos) {
	errs = load_source (argv[pos]);
	if (errs)
	  if (errs == FILE_ERROR)
	    quit ("Couldn't open source file.\n",20);
	  else if (errs == MEM_ERROR) {
	    printf ("Error: Out of memory.\n");
            free_circuit ();
	  }
	  else {
	    printf ("There were %d error(s) in the source.\n", errs);
	    free_circuit ();
	  }
      }
      command_line_interface ();
    }
  }
  else
    command_line_interface ();

  printf ("This line should never be executed.\n");
}

void command_line_interface (void)
{
  int n, argcount, errs;
  char command_line[MAX_LINE_LENGTH + 1], **argument;

  while (1)
    {
      printf ("(type '?' for help) > ");  /*  prompt  */

      errs = 0;
      n = -1;
      do {
	command_line[++n] = getchar ();
      } while (command_line[n] != '\n' && n != MAX_LINE_LENGTH);
      command_line[n] = '\0';

      argument = parse_line (command_line);
      for (n = 0; n < MAX_LINE_LENGTH / 2; n++)
	if (! argument[n])
	  break;
      argcount = n - 1;

      if (argcount > -1) {
	if (! stricmp (argument[0], "?"))
	  print_help ();

	else if (! stricmp (argument[0], "call")) {
	  if (argcount > 0) {
	    n = system (&command_line[argument[1] - argument[0]]);
	    printf ("program exited with returncode %d\n", n);
	  }
	  else
	    printf ("You must supply a filename.\n");
	}	      

	else if (! stricmp (argument[0], "clear")) {
	  free_inputdata ();
	  free_circuit ();
	}

	else if (! stricmp (argument[0], "gates")) {
	  errs = load_gates (argument[1]);
	  if (errs)
	    if (errs == FILE_ERROR)
	      printf ("Error: Couldn't open gate file.\n");
/*	    else if (errs == MEM_ERROR)
	      printf ("Error: Out of memory.\n");  */
	    else
	      printf ("There were %d error(s) in the gate file.\n", errs);
	}

	else if (! stricmp (argument[0], "load")) {
	  if (argcount == 2) {
	    if (stricmp (argument[2], "append"))
	      free_circuit ();
	  }
	  else
	    free_circuit ();

          errs = load_source (argument[1]);
          if (errs) {
	    if (errs == FILE_ERROR)
	      printf ("Error: Couldn't open source file.\n");
	    else if (errs == MEM_ERROR)
	      printf ("Error: Out of memory.\n");
	    else
	      printf ("There were %d error(s) in the source.\n", errs);
	    free_circuit ();
	  }
	}

	else if (! stricmp (argument[0], "mode")) {
	  if (argcount > 0)
	    if (! stricmp (argument[1], "table"))
	      Mode |= OUTPUT_MODE;
	    else if (! stricmp (argument[1], "curves"))
	      Mode &= ~OUTPUT_MODE;
	    else
	      printf ("Unknown mode.\n");
	  else
	    printf ("No mode specified.\n");
	}

	else if (! stricmp (argument[0], "run")) {
	  if (! NumberOfOutputs)
            printf ("There are no outputs defined!\n");
	  else if (NumberOfInputs) {
	    if (argcount > 0)
	      errs = load_input_file (argument[1]);
	    if (errs)
	      if (errs == FILE_ERROR)
		printf ("Error: Couldn't open input file.\n");
	      else if (errs == MEM_ERROR)
		printf ("Error: Out of memory.\n");
	      else
		printf ("There were %d error(s) in the run file.\n", errs);
	    else if (InputData)
	      errs = run_circuit ();
	    else
	      printf ("Error: There is no inputdata loaded.\n");
	  }
	  else {
	    printf ("Warning: No inputs in circuit.\n");
	    errs = run_circuit ();
	  }
	}

	else if (! stricmp (argument[0], "quit")) {
	  printf ("Really quit? (Y/n) ");
	  n = -1;
	  do {
	    command_line[++n] = getchar ();
	  } while (command_line[n] != '\n' && n != 81);
	  command_line[n] = '\0';
	  if (command_line[0] != 'n' && command_line[0] != 'N')
	    quit (NULL, 0);
	}

	else
	  printf ("Unknown command: %s\n", argument[0]);
      }
    }

}

void print_help (void)
{
  int n;
  char *strings[] = {
    "\n",
    "Arguments in brackets [] are optional.\n",
    "?                          Prints this helptext.\n",
    "CALL <program>             Runs an external program.\n",
    "CLEAR                      Clears the loaded circuit and inputdata.\n",
    "GATES <filename>           Loads external gate definitions.\n",
    "LOAD <filename> [APPEND]   Loads a new circuit.  If APPEND i specified the\n",
    "                           circuit allready in memory (if any) is added.\n",
    "MODE <MODETYPE>            Sets a mode for the program.\n",
    "RUN [<runfile>]            Runs the circuit with inputdata from runfile.\n",
    "QUIT                       Quits the program.\n",
    "\n"
  };

  printf ("\nLogic Evaluator version %s, © Christer Gessler 1993.\n", VERSION);
  printf ("This program is distributed under the GNU license.\n");

  for (n = 0; n < 12; n++)
    printf (strings[n]);
}

char **parse_line (char *line)
{
  int n = 0, arg = 0, space = 1;
  static char arg_line[MAX_LINE_LENGTH + 1], **args;

  if (args)
    memset (args, '\0', (MAX_LINE_LENGTH / 2) * sizeof (char *));
  else {
    args = (char **) calloc (MAX_LINE_LENGTH / 2, sizeof (char *));
    if (! args)
      quit ("Out of memory.\n", 20);
  }
  strcpy (arg_line, line);
  for (; n < MAX_LINE_LENGTH; n++)
    {
      if (line[n] == ';' || line[n] == '\n' || line[n] == '\0') {
	arg_line[n] = '\0';
	break;
      }
      if (line[n] == ' ' || line[n] == TAB) {
	arg_line[n] = '\0';
	space = 1;
	continue;
      }
      if (space) {
	args[arg++] = &arg_line[n];
	space = 0;
      }
    }
  return (args);
}

int load_input_file (char *filename)
{
  FILE *filep;
  char *bp, line[MAX_LINE_LENGTH + 1], **word;
  int line_number = 0, errors = 0, inputnum, phasenum = -1, tablemode = 0, n;

  memset (line, '\0', MAX_LINE_LENGTH + 1);
  NumberOfPhases = 0;
  free_inputdata ();

  filep = fopen (filename, "r");
  if (! filep)
    return (FILE_ERROR);

  do {
    bp = fgets (line, MAX_LINE_LENGTH, filep);
    if (! bp) {
      if (check_for_file_error (filep)) {
	fclose (filep);
	free_inputdata ();
	return (errors);
      }
    }
    else {
      word = parse_line (line);      
      line_number++;
      if (! word[0])
	continue;

      if (word[0][0] == '#') {
	if (! stricmp (&word[0][1], "table"))
	  tablemode = 1;

	else if (! stricmp (&word[0][1], "phases")) {
	  NumberOfPhases = atoi (word[1]);
	  if (NumberOfPhases < 1) {
	    printf ("%s %s\n", word[0], word[1]);
	    source_error ("Illegal number of phases.", line_number);
	    errors++;
	    NumberOfPhases = 0;
	  }
	  else {
	    if (InputData) {
	      source_error ("Number of phases allready defined.", line_number);
	      errors++;
	    }
	    else {
	      InputData = (char **) calloc (NumberOfPhases + 1, sizeof (char *));
	      if (! InputData)
		return (MEM_ERROR);
	    }
	  }
	}
      }

      else if (isalpha (word[0][0])) {
	if (tablemode) {
	  source_error ("Table syntax error.", line_number);
	  errors++;
	}
	else if (! stricmp (word[0], "phase"))
	  if (InputData && ++phasenum < NumberOfPhases) {
	    InputData[phasenum] = malloc (NumberOfInputs);
            if (! InputData[phasenum]) {
	      free_inputdata ();
              return (MEM_ERROR);
	    }
	    memset (InputData[phasenum], -1, NumberOfInputs);
	  }
	  else {
            source_error ("Not that many phases defined.", line_number);
            errors++;
	    phasenum--;
          }
      }

      else if (isdigit (word[0][0]))
	{
	  if (tablemode) {
	    if (InputData && ++phasenum < NumberOfPhases) {
	      InputData[phasenum] = malloc (NumberOfInputs);
	      if (! InputData[phasenum]) {
		free_inputdata ();
		return (MEM_ERROR);
	      }
	      for (n = 0; n < NumberOfInputs; n++) {
		if (word[n]) {
		  if (! strcmp (word[n], "0"))
		    InputData[phasenum][n] = (char) LOW;
		  else if (! strcmp (word[n], "1"))
		    InputData[phasenum][n] = (char) HIGH;
		  else {
		    source_error ("Table syntax error.", line_number);
		    errors++;
		  }
		}
		else {
		  source_error ("Not enough columns in table.", line_number);
		  errors++;
		}
	      }
	    }
	    else {
	      source_error ("Not that many phases defined.", line_number);
	      errors++;
	      phasenum--;
	    }
	  }
	  else {
	    if (phasenum < 0) {
	      source_error ("No phase initiated yet.", line_number);
	      errors++;
	    }
	    else {
	      inputnum = atoi (word[0]) - 1;
	      if (inputnum > NumberOfInputs) {
		source_error ("Input number not present in circuit.", line_number);
		errors++;
	      }
	      else if (inputnum < 0) {
		source_error ("Input number not correct.", line_number);
		errors++;
	      }
	      else {
		if (! stricmp (word[1], "high"))
		  InputData[phasenum][inputnum] = (char) HIGH;
		else if (! stricmp (word[1], "1"))
		  InputData[phasenum][inputnum] = (char) HIGH;
		
		else if (! stricmp (word[1], "low"))
		  InputData[phasenum][inputnum] = (char) LOW;
		else if (! stricmp (word[1], "0"))
		  InputData[phasenum][inputnum] = (char) LOW;

		else {
		  source_error ("State must be either HIGH or LOW.", line_number);
		  errors++;
		}
	      }
	    }
	  }
	}

    }
  } while (! feof (filep));

  if (++phasenum < NumberOfPhases) {
    printf ("Warning: There are more phases defined than used.\n");
    NumberOfPhases = phasenum;
  }

  if (fclose (filep))
    check_for_file_error (filep);

  if (errors)
    free_inputdata ();

  return (errors);
}

int load_gates (char *filename)
{
  FILE *filep;
  int n, gate = -1, spec = 0, errors = 0, table = 0, tableline = 0, val, fin = -1;
  char *bp, line[MAX_LINE_LENGTH + 1], **word;

  filep = fopen (filename, "r");
  if (! filep)
    return (FILE_ERROR);

  do {
    bp = fgets (line, MAX_LINE_LENGTH, filep);
    if (! bp) {
      if (check_for_file_error (filep)) {
        fclose (filep);
        return (errors);
      }
    }
    else {
      word = parse_line (line);
      if (! word[0])
	continue;

      if (word[0][0] == '#') {
	if (! stricmp (word[0], "#gate")) {
	  if (table) {
	    printf ("Gatefile error: The table for gate %s was not completed.\n", 
		   Expansion_Gates[gate].Name);
	    errors++;
	  }
	  gate++;
	  NumberOfExpansionGates++;
	  Expansion_Gates = (struct GateType *) realloc (Expansion_Gates,
			    NumberOfExpansionGates * sizeof (struct GateType));
	  if (! Expansion_Gates)
            quit ("Out of memory.\n", 20);
	  memset (&Expansion_Gates[gate], '\0', sizeof (struct GateType));
	  spec = 0;  table = 0;
	}
      }
      else if (! NumberOfExpansionGates) {
	printf ("Gatefile error: You must declare the gate first.\n");
	errors++;
      }
      else if (table) {
	for (n = 0; n < Expansion_Gates[gate].Outputs; n++) {
	  if (! stricmp (word[n], "0"))
	    val = LOW;
	  else if (! stricmp (word[n], "1"))
	    val = HIGH;
	  else if (! stricmp (word[n], "memory"))
	    val = MEMORY;
	  else if (! stricmp (word[n], "toggle"))
	    val = TOGGLE;
	  else {
	    printf ("Gatefile error: Illegal value in table in gate %s.\n",
		    Expansion_Gates[gate].Name);
	    errors++;
	    continue;
	  }
	  Expansion_Gates[gate].Table[tableline*Expansion_Gates[gate].Outputs + n] =
	    val;
	}
	if (++tableline == table) {
	  printf ("Name: %s\n", Expansion_Gates[gate].Name);
	  printf ("Inputs: %d\n", Expansion_Gates[gate].Inputs);
	  printf ("Outputs: %d\n", Expansion_Gates[gate].Outputs);
	  printf ("Specials: %d\n\n", Expansion_Gates[gate].Specials);
	  table = 0;
	  fin++;
	}
	continue;
      }
      else
	if (! stricmp (word[0], "name:")) {
	  strncpy (Expansion_Gates[gate].Name, word[1], 9);
	  Expansion_Gates[gate].Name[9] = '\0';
	}
	else if (! stricmp (word[0], "inputs:")) {
	  Expansion_Gates[gate].Inputs = atoi (word[1]);
	  if (Expansion_Gates[gate].Inputs <= 0)
	    printf ("Gatefile warning: Should the %s gate really have %d inputs?\n",
		    Expansion_Gates[gate].Name, Expansion_Gates[gate].Inputs);
	  Expansion_Gates[gate].InputNames = calloc (Expansion_Gates[gate].Inputs,
						     sizeof (char *));
	  if (! Expansion_Gates[gate].InputNames)
	    quit ("Out of memory.\n", 20);
	}
	else if (! stricmp (word[0], "outputs:")) {
	  Expansion_Gates[gate].Outputs = atoi (word[1]);
	  if (Expansion_Gates[gate].Outputs <= 0)
	    printf("Gatefile warning: Should the %s gate really have %d outputs?\n",
		    Expansion_Gates[gate].Name, Expansion_Gates[gate].Outputs);
	  Expansion_Gates[gate].OutputNames = calloc (Expansion_Gates[gate].Outputs,
						      sizeof (char *));
	  if (! Expansion_Gates[gate].OutputNames)
            quit ("Out of memory.\n", 20);
	}
	else if (! stricmp (word[0], "inputnames:")) {
	  for (n = 0; n < Expansion_Gates[gate].Inputs; n++) {
	    Expansion_Gates[gate].InputNames[n] = calloc (strlen (word[n+1])+1, 1);
	    if (! Expansion_Gates[gate].InputNames[n])
	      quit ("Out of memory.\n", 20);
	    strcpy (Expansion_Gates[gate].InputNames[n], word[n+1]);
	  }
	}
	else if (! stricmp (word[0], "outputnames:")) {
	  for (n = 0; n < Expansion_Gates[gate].Outputs; n++) {
	    Expansion_Gates[gate].OutputNames[n] = calloc (strlen (word[n+1])+1, 1);
            if (! Expansion_Gates[gate].OutputNames[n])
              quit ("Out of memory.\n", 20);
	    strcpy (Expansion_Gates[gate].OutputNames[n], word[n+1]);
	  }
	}
	else if (! stricmp (word[0], "specials:")) {
	  if (Expansion_Gates[gate].SpecialTypes) {
	    printf ("Gatefile error: Number of special pins allready defined in gate %d.\n", Expansion_Gates[gate].Name);
	    errors++;
	  }
	  else {
	    Expansion_Gates[gate].Specials = atoi (word[1]);
	    if (Expansion_Gates[gate].Specials < 0)
	      printf ("Gatefile error: Should the %s gate really have %d special inputs?\n",
		      Expansion_Gates[gate].Name, Expansion_Gates[gate].Specials);
	    Expansion_Gates[gate].SpecialTypes = calloc(Expansion_Gates[gate].
						 Specials, sizeof (struct Special));
	    if (! Expansion_Gates[gate].SpecialTypes)
              quit ("Out of memory.\n", 20);
	  }
	}
	else if (! stricmp (word[0], "specialtype:")) {
	  if (spec >= Expansion_Gates[gate].Specials) {
	    printf ("Gatefile error: More special inputs defined than declared in gate %s.\n", Expansion_Gates[gate].Name);
	    errors++;
	  }
	  else {
	    for (n = 0; n < Expansion_Gates[gate].Inputs; n++)
	      if (! stricmp (Expansion_Gates[gate].InputNames[n], word[1])) {
		Expansion_Gates[gate].SpecialTypes[spec].Number = n + 1;
		break;
	      }
	    if (! Expansion_Gates[gate].SpecialTypes[spec].Number) {
	      printf ("Gatefile error: Special input \"%s\" not found in gate %s.\n", word[1], Expansion_Gates[gate].Name);
	      errors++;
	    }
	    else {
	      if (! stricmp (word[2], "trigger"))
		if (! stricmp (word[3], "nf")) {
		  Expansion_Gates[gate].SpecialTypes[spec].Type = TRIGGER_N_F;
		  Expansion_Gates[gate].SpecialTypes[spec].Data.Value = LOW;
		}
		else if (! stricmp (word[3], "pf")) {
		  Expansion_Gates[gate].SpecialTypes[spec].Type = TRIGGER_P_F;
		  Expansion_Gates[gate].SpecialTypes[spec].Data.Value = HIGH;
		}
		else {
		  printf ("Gatefile error: Trigger type unknown in gate %s.\n",
			  Expansion_Gates[gate].Name);
		  errors++;
		}
	      else if (! stricmp (word[2], "set")) {
		Expansion_Gates[gate].SpecialTypes[spec].Type = SET;
		Expansion_Gates[gate].SpecialTypes[spec].Data.Table = calloc
		  (Expansion_Gates[gate].Outputs, sizeof (char));
		if (! Expansion_Gates[gate].SpecialTypes[spec].Data.Table)
		  quit ("Out of memory.\n", 20);
		for (n = 0; n < Expansion_Gates[gate].Outputs; n++)
		  if (! stricmp (word[3 + n], "0"))
		    Expansion_Gates[gate].SpecialTypes[spec].Data.Table[n] = LOW;
		  else if (! stricmp (word[3 + n], "1"))
		    Expansion_Gates[gate].SpecialTypes[spec].Data.Table[n] = HIGH;
		  else {
		    printf ("Gatefile error: Illegal value in specials table in gate %s.\n", Expansion_Gates[gate].Name);
		    errors++;
		  }
	      }
	      else {
		printf ("Gatefile error: Illegal special function in gate %s.\n",
			Expansion_Gates[gate].Name);
		errors++;
	      }
	      spec++;
	    }
	  }
	}
	else if (! stricmp (word[0], "table:")) {
	  tableline = Expansion_Gates[gate].Inputs - Expansion_Gates[gate].Specials;
	  for (table = 1, n = 0; n < tableline; n++)
	    table *= 2;
	  Expansion_Gates[gate].Table = calloc (table * Expansion_Gates[gate].
						Outputs, sizeof (char));
	  if (! Expansion_Gates[gate].Table)
	    quit ("Out of memory.\n", 20);
	  tableline = 0;
	}
    }
  } while (! feof (filep));

  if (fclose (filep))
    check_for_file_error (filep);

  if (fin != gate) {
    printf ("Gatefile error: There were %d uncomplete definition(s) in the file.\n",
	    gate - fin);
    errors += gate - fin;
  }

  return (errors);
}

int load_source (char *filename)
{
  int errors;

  errors = interpret_source (filename, NULL);

  if (errors >= 0) {
    printf ("Total number of gates in circuit: %d\n", NumberOfGatesUsed);
    errors += test_circuit ();
  }

  return (errors);
}

int interpret_source (char *filename, char **loaded_files)
{
  FILE *filep;
  int m, n, state = 0, skip_line, line_number = 0, errors = 0;
  int gate[2][4];
  char *bp, line[MAX_LINE_LENGTH + 1], name[MAX_LINE_LENGTH + 1], **word;

                 /*  to make sure the string is null-terminated  */
  memset (line, '\0', MAX_LINE_LENGTH + 1);

  if (! loaded_files) {
    loaded_files = calloc (2, sizeof (char *));
    if (! loaded_files)
      return (MEM_ERROR);
    loaded_files[0] = malloc (strlen (filename) + 1);
    if (! loaded_files[0])
      return (MEM_ERROR);
    strcpy (loaded_files[0], filename);
  }
  else {
    n = 0;
    while (loaded_files[n])
      n++;
    loaded_files[n] = malloc (strlen (filename) + 1);
    if (! loaded_files[n])
      return (MEM_ERROR);
    strcpy (loaded_files[n], filename);
    loaded_files = realloc (loaded_files, (++n + 1) * sizeof (char *));
    if (! loaded_files)
      return (MEM_ERROR);
    loaded_files[n] = NULL;
  }

  filep = fopen (filename, "r");
  if (! filep)
    return (FILE_ERROR);

  do {
    bp = fgets (line, MAX_LINE_LENGTH, filep);
    if (! bp) {
      if (check_for_file_error (filep)) {
        fclose (filep);
        return (errors);
      }
    }
    else {
      line_number++;
      skip_line = (n = 0);
      word = parse_line (line);
      if (! word[0])
	continue;

      if (word[0][0] == '#') {
	if (! stricmp (word[0], "#include"))
	  if (word[1]) {
	    while (loaded_files[n])
	      if (! stricmp (loaded_files[n++], word[1]))
		skip_line = 1;

	    if (! skip_line) {
	      m = interpret_source (word[1], loaded_files);
	      if (m)
		if (m == FILE_ERROR) {
		  source_error ("Couldn't open included source file.", line_number);
		  errors++;
		}
		else if (m == MEM_ERROR) {
		  if (fclose (filep))
		    check_for_file_error (filep);
		  return (MEM_ERROR);
		}
		else {
		  printf ("Error in line %d: There were %d error(s) in included source file.\n", line_number, m);
		  errors += m;
		}
	    }
	    else
	      printf ("Warning in line %d: File allready included.\n", line_number);
	  }
	  else {
	    source_error ("A filename must be supplied.", line_number);
	    errors++;
	  }

	continue;
      }
      while (line[n] != ';' && line[n] != '\n' && line[n] != '\0')
	{
	  if (line[n] == ' ' || line[n] == TAB)
	    n++;
	  else
	    switch (++state)
	      {
	      case 1:        /*  get name of first gate  */
		m = 0;
		if (! isalpha (line[n])) {
		  source_error ("Line must start with a gate name.", line_number);
		  errors++;  skip_line = 1;
		  break;
		}
		do {
		  name[m++] = line[n++];
		} while (isalpha (line[n]));
		name[m] = '\0';
		if (-1 == (gate[0][0] = get_gate_type (name))) {
		  source_error ("First gate unknown.", line_number);
		  errors++;  skip_line = 1;
		}
		if (gate[0][0] == -4 || gate[0][0] == -5) {
		  gate[0][1] = 1;
		  gate[0][2] = -1;
		  state += 2;
		}
		break;
	      case 2:        /*  get number of first gate  */
		if (line[n] != '.') {
                  source_error ("First gate isn't numbered correctly.",
                                line_number);
		  errors++;  skip_line = 1;
		  break;
		}
		m = 0;
		while (isdigit (line[++n]))
		  name[m++] = line[n];
		name[m] = '\0';
		gate[0][1] = atoi (name);
		if (gate[0][1] < 1) {
                  source_error ("First gate isn't numbered correctly.",
                                line_number);
		  errors++;  skip_line = 1;
		  break;
		}
		if (gate[0][0] < -1) {  /*  special gate type without pins  */
		  if (gate[0][0] < -2)
		    gate[0][2] = -1;
		  else
		    gate[0][2] = 1;
		  state++;
		}
		break;
	      case 3:        /*  get pin of first gate  */
                if (line[n] != '(') {
                  source_error ("First gate doesn't have a pin.", line_number);
                  errors++;  skip_line = 1;
                  break;
                }
		m = 0;
		while (line[++n] != ')')
		  name[m++] = line[n];
		if (! m) {
		  source_error ("First gate doesn't have a pin.", line_number);
		  errors++;  skip_line = 1;
		  break;
		}
		name[m] = '\0';
		if (! (gate[0][2] = get_pin_number (gate[0], name))) {
                  source_error ("Illegal pin name for first gate.", line_number);
                  errors++;  skip_line = 1;
                  break;
		}
		n++;
		break;
	      case 4:        /*  check for interconnectionmark (a hyphen)  */
		if (line[n] != '-') {
		  source_error ("No interconnectionmark found.", line_number);
		  errors++;  skip_line = 1;
		  break;
		}
		n++;
		break;
	      case 5:        /*  get name of second gate  */
                m = 0;
                if (! isalpha (line[n])) {
                  source_error ("Line must have a second gate.", line_number);
                  errors++;  skip_line = 1;
                  break;
                }
                do {
                  name[m++] = line[n++];
                } while (isalpha (line[n]));
                name[m] = '\0';
                if (-1 == (gate[1][0] = get_gate_type (name))) {
                  source_error ("Second gate unknown.", line_number);
                  errors++;  skip_line = 1;
                }
                break;
	      case 6:        /*  get number of second gate  */
                if (line[n] != '.') {
                  source_error ("Second gate isn't numbered correctly.",
				line_number);
                  errors++;  skip_line = 1;
                  break;
                }
                m = 0;
                while (isdigit (line[++n]))
                  name[m++] = line[n];
                name[m] = '\0';
                gate[1][1] = atoi (name);
                if (gate[1][1] < 1) {
                  source_error ("Second gate isn't numbered correctly.",
                                line_number);
                  errors++;  skip_line = 1;
                  break;
                }
                if (gate[1][0] < -1) {  /*  special gate type without pins  */
		  if (gate[1][0] < -2)
		    gate[1][2] = -1;
		  else
		    gate[1][2] = 1;
                  state++;
		}
		break;
	      case 7:        /*  get pin of second gate  */
                if (line[n] != '(') {
                  source_error ("Second gate doesn't have a pin.", line_number);
                  errors++;  skip_line = 1;
                  break;
                }
                m = 0;
                while (line[++n] != ')')
                  name[m++] = line[n];
                if (! m) {
                  source_error ("Second gate doesn't have a pin.", line_number);
                  errors++;  skip_line = 1;
                  break;
                }
                name[m] = '\0';
		if (! (gate[1][2] = get_pin_number (gate[1], name))) {
                  source_error ("Illegal pin name for second gate.", line_number);
                  errors++;  skip_line = 1;
                  break;
		}
                n++;
		break;
	      default:       /*  too much information on line  */
		n++;
		source_error ("Only one statement allowed per line.", line_number);
		errors++;  skip_line = 1;
		break;
	      }
	  if (skip_line)
	    break;
	}
      if (! skip_line)
	if (state > 0 && state < 7) {
	  source_error ("Incomplete statement.", line_number);
	  errors++;
	}
	else if (state == 7) {
	  state = setup_connection (gate, line_number);
	  if (state < 0) {
	    if (fclose (filep))
	      check_for_file_error (filep);
	    return (state);
	  }
	}
      state = 0;
    }
  }  while (! feof (filep));

  if (fclose (filep))
    check_for_file_error (filep);

  return (errors);
}

int setup_connection (int gate[2][4], int line)
{
  int m, n, errors = 0, inputgate, outputgate;
  struct Gate *gatepointer[2];
  struct Pin  *pin_in, *pin_out;

  if ((gate[0][2] < gate[1][2] ? gate[0][2] : gate[1][2]) > 0 ||
      gate[0][2] == gate[1][2]) {
    source_error ("Illegal connection.", line);
    errors++;
  }
  else {
    gatepointer[0] = NULL;
    gatepointer[1] = NULL;
    /*  gate one:  */
    if (! CircuitGates) {
      CircuitGates = (struct Gate *) calloc (1, sizeof (struct Gate));
      if (! CircuitGates)
	return (MEM_ERROR);
      CircuitGates[0].GateType = gate[0][0];
      gate[0][3] = 0;
      gatepointer[0] = &CircuitGates[0];
      NumberOfGatesUsed++;
      if (gate[0][0] == -3)
	NumberOfInputs++;
      else if (gate[0][0] == -2)
	NumberOfOutputs++;
    }
    else {
      for (m = 0, n = 0; n < NumberOfGatesUsed; n++) {
	if (gate[0][0] == CircuitGates[n].GateType) {
	  m++;
	  if (m == gate[0][1]) {
	    gatepointer[0] = &CircuitGates[n];
	    gate[0][3] = n;
	  }
	}
      }
      if (! gatepointer[0]) {  /*  if gate not previously referenced  */
	n = gate[0][1] - m;
	CircuitGates = (struct Gate *) realloc ((char *) CircuitGates,
		       (n + NumberOfGatesUsed) * sizeof (struct Gate));
	if (! CircuitGates)
	  return (MEM_ERROR);
	for (m = NumberOfGatesUsed; m < NumberOfGatesUsed + n; m++) {
	  if (gate[0][0] == -3)
	    NumberOfInputs++;
	  else if (gate[0][0] == -2)
	    NumberOfOutputs++;
	  CircuitGates[m].GateType = gate[0][0];
	  CircuitGates[m].Inputs = 0;
	  CircuitGates[m].Outputs = 0;
	  CircuitGates[m].InputPins = NULL;
	  CircuitGates[m].OutputPins = NULL;
	}
	NumberOfGatesUsed = m;
	gate[0][3] = --m;
	gatepointer[0] = &CircuitGates[m];
      }
    }
    /*  gate two:  */
    for (m = 0, n = 0; n < NumberOfGatesUsed; n++) {
      if (gate[1][0] == CircuitGates[n].GateType) {
	m++;
	if (m == gate[1][1]) {
	  gatepointer[1] = &CircuitGates[n];
	  gate[1][3] = n;
	}
      }
    }
    if (! gatepointer[1]) {  /*  if gate not previously referenced  */
      n = gate[1][1] - m;
      CircuitGates = (struct Gate *) realloc ((char *) CircuitGates,
		     (n + NumberOfGatesUsed) * sizeof (struct Gate));
      for (m = NumberOfGatesUsed; m < NumberOfGatesUsed + n; m++) {
	if (gate[1][0] == -3)
	  NumberOfInputs++;
	else if (gate[1][0] == -2)
	  NumberOfOutputs++;
	CircuitGates[m].GateType = gate[1][0];
	CircuitGates[m].Inputs = 0;
	CircuitGates[m].Outputs = 0;
	CircuitGates[m].InputPins = NULL;
	CircuitGates[m].OutputPins = NULL;
      }
      NumberOfGatesUsed = m;
      gate[1][3] = --m;
      gatepointer[1] = &CircuitGates[m];
    }
    gatepointer[0] = &CircuitGates[gate[0][3]];
    gatepointer[1] = &CircuitGates[gate[1][3]];
    
    /*  setup pins and connection  */
    inputgate = gate[0][2] > gate[1][2] ? 0 : 1;
    outputgate = 1 - inputgate;
    gate[outputgate][2] *= -1;  /*  make positive  */
    
    if (gate[outputgate][0] < NUMBER_OF_ELEMENTARY_GATES) {
      if (! gatepointer[outputgate] -> OutputPins) {
	gatepointer[outputgate] -> OutputPins = (struct Pin *) calloc
	  (2, sizeof (struct Pin));
	if (! gatepointer[outputgate] -> OutputPins)
	  return (MEM_ERROR);
      }
      gatepointer[outputgate] -> Outputs = 1;
      pin_out = &(gatepointer[outputgate] -> OutputPins)[1];
    }
    else {
      if (! gatepointer[outputgate] -> OutputPins) {
        gatepointer[outputgate] -> OutputPins = (struct Pin *) calloc
          (n = Expansion_Gates[gate[outputgate][0] - NUMBER_OF_ELEMENTARY_GATES].
	   Outputs + 1, sizeof (struct Pin));
	if (! gatepointer[outputgate] -> OutputPins)
	  return (MEM_ERROR);
	gatepointer[outputgate] -> Outputs = --n;
      }
      pin_out = &(gatepointer[outputgate] -> OutputPins)[gate[outputgate][2]];
    }
    
    if (gatepointer[inputgate] -> Inputs >= gate[inputgate][2])
      if ((gatepointer[inputgate] -> InputPins)[gate[inputgate][2]].Connection) {
	source_error ("Input pin allready connected.", line);
	errors++;
      }
    if (gate[inputgate][0] < NUMBER_OF_ELEMENTARY_GATES) {
      if (gate[inputgate][2] > gatepointer[inputgate] -> Inputs) {
	/*  allocate pins  */
	gatepointer[inputgate] -> InputPins = (struct Pin *) realloc
	  ((char *) gatepointer[inputgate] -> InputPins,
	   (gate[inputgate][2] + 1) * sizeof (struct Pin));
	if (! gatepointer[inputgate] -> InputPins)
	  return (MEM_ERROR);
	/*  update connections to the new memory location  */
	pin_in = gatepointer[inputgate] -> InputPins;
	for (n = 1; n <= gatepointer[inputgate] -> Inputs; n++)
	  if (pin_in[n].Connection)
	    pin_in[n].Connection -> Connection = &pin_in[n];
	/*  initialize new pins here  */
	n = gatepointer[inputgate] -> Inputs;
	for (; ++n <= gate[inputgate][2];) {
	  pin_in[n].HostGate = 0;
	  pin_in[n].Value = 0;
	  pin_in[n].Connection = NULL;
	}
	gatepointer[inputgate] -> Inputs = gate[inputgate][2];
      }
      pin_in = &(gatepointer[inputgate] -> InputPins)[gate[inputgate][2]];
    }
    else {
      if (! gatepointer[inputgate] -> InputPins) {
        gatepointer[inputgate] -> InputPins = (struct Pin *) calloc
          (n = Expansion_Gates[gate[inputgate][0] - NUMBER_OF_ELEMENTARY_GATES].
           Inputs + 1, sizeof (struct Pin));
	if (! gatepointer[inputgate] -> InputPins)
	  return (MEM_ERROR);
        gatepointer[inputgate] -> Inputs = --n;
      }
      pin_in = &(gatepointer[inputgate] -> InputPins)[gate[inputgate][2]];
    }
    
    pin_out -> HostGate = gate[outputgate][3];
    pin_out -> Value = 0;
    pin_out -> Connection = pin_in;
    
    pin_in -> HostGate = gate[inputgate][3];
    pin_in -> Value = 0;
    pin_in -> Connection = pin_out;
  }
  return (errors);
}

int test_circuit (void)
{
  int m, n, type, errors = 0;

  for (n = 0; n < NumberOfGatesUsed; n++)
    {
      if ((type = CircuitGates[n].GateType) < 0)
	;
      else
	{
	  if (CircuitGates[n].Inputs == 0) {
	    circuit_error ("No inputs defined.", n);
	    errors++;
	  }
	  else {
	    if (type < NUMBER_OF_ELEMENTARY_GATES) {
	      if (Elementary_Gates[type].Inputs != -1 &&
		  CircuitGates[n].Inputs > Elementary_Gates[type].Inputs) {
		circuit_error ("Too many inputs.", n);
		errors++;
	      }
	    }
	    else {
	      type -= NUMBER_OF_ELEMENTARY_GATES;
              if (Expansion_Gates[type].Inputs != -1 &&
                  CircuitGates[n].Inputs > Expansion_Gates[type].Inputs) {
                circuit_error ("Too many inputs.", n);
                errors++;
              }
	    }

	    for (m = 1; m <= CircuitGates[n].Inputs; m++) {
	      if (! CircuitGates[n].InputPins[m].Connection) {
		circuit_error ("Input pin not conected.", n);
		errors++;
	      }
	      if (! CircuitGates[n].InputPins[m].HostGate == n) {
		circuit_error ("Pin not correctly assigned to gate internally.",n);
		errors++;
	      }
	    }
	  }
	  if (CircuitGates[n].Outputs == 0) {
	    circuit_error ("No outputs.", n);
	    errors++;
	  }
	}
    }

  return (errors);
}

int get_gate_type (char *gate_name)
{
  int n;

  for (n = 0; n < NUMBER_OF_ELEMENTARY_GATES; n++)
    if (! stricmp (Elementary_Gates[n].Name, gate_name))
      return (n);

  for (n = 0; n < NumberOfExpansionGates; n++)
    if (! stricmp (Expansion_Gates[n].Name, gate_name))
      return (n + NUMBER_OF_ELEMENTARY_GATES);

  if (! stricmp (gate_name, "Output"))
    return (-2);
  else if (! stricmp (gate_name, "Input"))
    return (-3);
  else if (! stricmp (gate_name, "High"))
    return (-4);
  else if (! stricmp (gate_name, "Low"))
    return (-5);
  else if (! stricmp (gate_name, "Clock"))
    return (-6);
  else
    return (-1);
}

int  get_pin_number (int *gatedata, char *pinname)
  /*  returns negative value for outputpin, positive for inputpin  */
{
  int n, gate;

  if (gatedata[0] < NUMBER_OF_ELEMENTARY_GATES)
    if (toupper (pinname[0]) == 'I')
      return (atoi (&pinname[1]));
    else if (toupper (pinname[0]) == 'O')
      return (-1);
    else
      return (0);
  else if (Expansion_Gates) {
    gate = gatedata[0] - NUMBER_OF_ELEMENTARY_GATES;
    for (n = 0; n < Expansion_Gates[gate].Inputs; n++)
      if (! stricmp (pinname, Expansion_Gates[gate].InputNames[n]))
	return (n + 1);
    for (n = 0; n < Expansion_Gates[gate].Outputs; n++)
      if (! stricmp (pinname, Expansion_Gates[gate].OutputNames[n]))
	return (-(n + 1));
    return (0);
  }
  else {
    printf ("There are no expansion gates defined.\n");
    return (0);
  }
}

int run_circuit (void)
{
  int  k, m, n, o, p, tests, type, pin, **inputlist = NULL, temp, trig;
  int outrows, currow = 0, curpos = 0;
  long value;
  char ***outputdata = NULL;

  if (! NumberOfInputs)
    NumberOfPhases = 1;

  /*  initialization of output strings   */
  if (Mode & OUTPUT_MODE) {
    outputdata = (char ***) calloc (2, sizeof (char **));    
    if (! outputdata)
      return (MEM_ERROR);
    outputdata[0] = (char **) calloc (NumberOfPhases + 1, sizeof (char *));
    if (! outputdata[0]) {
      free (outputdata);
      return (MEM_ERROR);
    }
    for (n = 0; n < NumberOfPhases; n++) {
      outputdata[0][n] = calloc (NumberOfOutputs + 1, 2 * sizeof (char));
      if (! outputdata[0][n]) {
	free_outputdata (outputdata);
	return (MEM_ERROR);
      }
    }
  }
  else {
    outrows = (NumberOfPhases - 1) / 50 + 1;
    outputdata = (char ***) calloc (outrows + 1, sizeof (char **));
    if (! outputdata)
      return (MEM_ERROR);
    for (m = 0; m < outrows; m++) {
      outputdata[m] = (char **) calloc (NumberOfOutputs + 2, sizeof (char *));
      if (! outputdata[m]) {
        free_outputdata (outputdata);
	return (MEM_ERROR);
      }
      for (n = 0; n <= NumberOfOutputs; n++) {
	outputdata[m][n] = calloc (NumberOfPhases + 1, sizeof (char));
	if (! outputdata[m][n]) {
	  free_outputdata (outputdata);
	  return (MEM_ERROR);
	}
      }
    }
  }

  if (NumberOfTests)
    tests = NumberOfTests;
  else
    tests = NumberOfGatesUsed * 2;

  /*  setting pins in circuit to default values  */
  for (m = 0; m < NumberOfGatesUsed; m++)
    switch (CircuitGates[m].GateType)
      {
      case -4:
	CircuitGates[m].OutputPins[1].Value = HIGH;
	break;
      case -5:
	CircuitGates[m].OutputPins[1].Value = LOW;
	break;
      default:
	if (CircuitGates[m].GateType > -1)
	  for (n = 1; n <= CircuitGates[m].Outputs; n++)
	    CircuitGates[m].OutputPins[n].Value = 0;
	break;
      }

  /*  setting up a list of expansiongate inputs to speed the test up  */
  if (NumberOfExpansionGates) {
    inputlist = (int **) calloc (NumberOfExpansionGates + 1, sizeof (int *));
    if (! inputlist) {
      free_outputdata (outputdata);
      return (MEM_ERROR);
    }
    for (m = 0; m < NumberOfExpansionGates; m++)
      if (temp = Expansion_Gates[m].Inputs - Expansion_Gates[m].Specials) {
	inputlist[m] = (int *) calloc (temp, sizeof (int));
	if (! inputlist[m]) {
	  m = 0;
	  while (inputlist[m])
	    free (inputlist[m++]);
	  free (inputlist);
	  free_outputdata (outputdata);
	  return (MEM_ERROR);
	}
	for (p = 0, n = 1; n <= Expansion_Gates[m].Inputs; n++) {
	  for (o = 0; o < Expansion_Gates[m].Specials; o++)
	    if (Expansion_Gates[m].SpecialTypes[o].Number == n)
	      break;
	  if (o == Expansion_Gates[m].Specials)
	    inputlist[m][p++] = n;
	}
      }
  }

  for (p = 0; p < NumberOfPhases; p++) {
    for (n = -1, m = 0; m < NumberOfGatesUsed; m++)
      if (CircuitGates[m].GateType == -3)
	if (InputData[p][++n] != -1)
	  CircuitGates[m].OutputPins[1].Value = InputData[p][n];

    for (o = 0; o < tests; o++)
      for (m = 0; m < NumberOfGatesUsed; m++)
	if (CircuitGates[m].GateType > 0) {
	  for (n = 1; n <= CircuitGates[m].Inputs; n++)
	    if (CircuitGates[m].InputPins[n].Connection)
	      CircuitGates[m].InputPins[n].Value =
		CircuitGates[m].InputPins[n].Connection -> Value;

	  switch (CircuitGates[m].GateType)
	    {
	    case 0:  /*  NOT  */
	      CircuitGates[m].OutputPins[1].Value = 
		! CircuitGates[m].InputPins[1].Value;
	      break;
	    case 1:  /*  AND   */
	      value = CircuitGates[m].InputPins[1].Value;
	      for (n = 2; n <= CircuitGates[m].Inputs; n++)
		value &= CircuitGates[m].InputPins[n].Value;
              CircuitGates[m].OutputPins[1].Value = value;
	      break;
	    case 2:  /*  OR    */
              value = CircuitGates[m].InputPins[1].Value;
              for (n = 2; n <= CircuitGates[m].Inputs; n++)
                value |= CircuitGates[m].InputPins[n].Value;
              CircuitGates[m].OutputPins[1].Value = value;
	      break;
	    case 3:  /*  XOR   */
	      CircuitGates[m].OutputPins[1].Value =
		              CircuitGates[m].InputPins[1].Value !=
		              CircuitGates[m].InputPins[2].Value;
	      break;
	    case 4:  /*  NAND  */
              value = CircuitGates[m].InputPins[1].Value;
              for (n = 2; n <= CircuitGates[m].Inputs; n++)
                value &= CircuitGates[m].InputPins[n].Value;
              CircuitGates[m].OutputPins[1].Value = ! value;
	      break;
	    case 5:  /*  NOR   */
              value = CircuitGates[m].InputPins[1].Value;
              for (n = 2; n <= CircuitGates[m].Inputs; n++)
                value |= CircuitGates[m].InputPins[n].Value;
              CircuitGates[m].OutputPins[1].Value = ! value;
	      break;
	    case 6:  /*  XNOR  */
              CircuitGates[m].OutputPins[1].Value =
                             CircuitGates[m].InputPins[1].Value ==
			     CircuitGates[m].InputPins[2].Value;
	      break;
	    default:  /*  not one of the elementary gates  */
	      type = CircuitGates[m].GateType - NUMBER_OF_ELEMENTARY_GATES;
	      if (Expansion_Gates[type].Specials) {
		trig = 1;
		for (n = 0; n < Expansion_Gates[type].Specials; n++) {
		  pin = Expansion_Gates[type].SpecialTypes[n].Number;
		  switch (Expansion_Gates[type].SpecialTypes[n].Type)
		    {
		    case SET:
		      if (CircuitGates[m].InputPins[pin].Value == HIGH) {
			trig = 0;
			for (k = 0; k < Expansion_Gates[type].Outputs; k++) {
			  temp= Expansion_Gates[type].SpecialTypes[n].Data.Table[k];
			  CircuitGates[m].OutputPins[k + 1].Value = temp;
			}
		      }
		      break;
		    case TRIGGER_N_F:
		      trig = 0;
		      if (CircuitGates[m].InputPins[pin].Value == LOW &&
			  Expansion_Gates[type].SpecialTypes[n].Data.Value == HIGH)
			trig = 1;
		      Expansion_Gates[type].SpecialTypes[n].Data.Value =
			CircuitGates[m].InputPins[pin].Value;
		      break;
		    case TRIGGER_P_F:
		      trig = 0;
                      if (CircuitGates[m].InputPins[pin].Value == HIGH &&
                          Expansion_Gates[type].SpecialTypes[n].Data.Value == LOW)
			trig = 1;
                      Expansion_Gates[type].SpecialTypes[n].Data.Value =
                        CircuitGates[m].InputPins[pin].Value;
		      break;
		    }
		}

		if (trig) {
		  temp=Expansion_Gates[type].Inputs-Expansion_Gates[type].Specials;
		  if (temp) {
		    for (value = 0, n = 0; n < temp; n++)
		      value |= CircuitGates[m].InputPins[inputlist[type][n]].Value
			<< temp - 1 - n;
		    for (n = 0; n < Expansion_Gates[type].Outputs; n++) {
		      temp = Expansion_Gates[type].Table[value * Expansion_Gates
							 [type].Outputs + n];
		      switch (temp)
			{
			case HIGH:
			  CircuitGates[m].OutputPins[n + 1].Value = HIGH;
			  break;
			case LOW:
			  CircuitGates[m].OutputPins[n + 1].Value = LOW;
			  break;
			case MEMORY:
			  break;
			case TOGGLE:
			  CircuitGates[m].OutputPins[n + 1].Value =
			    ! CircuitGates[m].OutputPins[n + 1].Value;
			  break;
			}
		    }
		  }
		}
	      }
	      else {
		for (value = 0, n = 1; n <= Expansion_Gates[type].Inputs; n++)
		  value |= CircuitGates[m].InputPins[n].Value
		    << Expansion_Gates[type].Inputs - n;
		for (n = 0; n < Expansion_Gates[type].Outputs; n++) {
		  temp = Expansion_Gates[type].Table[value * Expansion_Gates
						     [type].Outputs + n];
		  switch (temp)
		    {
		    case HIGH:
		      CircuitGates[m].OutputPins[n + 1].Value = HIGH;
		      break;
		    case LOW:
		      CircuitGates[m].OutputPins[n + 1].Value = LOW;
		      break;
		    case MEMORY:
		      break;
		    case TOGGLE:
		      CircuitGates[m].OutputPins[n + 1].Value =
			! CircuitGates[m].OutputPins[n + 1].Value;
		      break;
		    }
		}
	      }
	      break;
	    }
	}

    if (Mode & OUTPUT_MODE) {
      for (n = 0, m = 0; m < NumberOfGatesUsed; m++)
        if (CircuitGates[m].GateType == -2) {
          CircuitGates[m].InputPins[1].Value =
            CircuitGates[m].InputPins[1].Connection -> Value;
          if (CircuitGates[m].InputPins[1].Value == 1)
            outputdata[0][p][n++] = '1';
          else
            outputdata[0][p][n++] = '0';
	  outputdata[0][p][n++] = ' ';
        }
    }
    else {
      for (n = 0, m = 0; m < NumberOfGatesUsed; m++)
	if (CircuitGates[m].GateType == -2) {
	  n++;
	  CircuitGates[m].InputPins[1].Value = 
	    CircuitGates[m].InputPins[1].Connection -> Value;
	  if (CircuitGates[m].InputPins[1].Value == 1)
	    outputdata[currow][n][curpos] = '1';
	  else
	    outputdata[currow][n][curpos] = '_';
	}
      if (++curpos > 49) {
	curpos = 0;
	currow++;
      }
    }
  }

  if (inputlist) {
    m = 0;
    while (inputlist[m])
      free (inputlist[m++]);
    free (inputlist);
  }

  if (Mode & OUTPUT_MODE) {
    printf ("\nNumber of outputs: %d\n\n", NumberOfOutputs);
    for (n = 0; n < NumberOfPhases; n++)
      printf ("Phase %6d:   %s\n", n + 1, outputdata[0][n]);
    printf ("\n");
  }
  else
    for (m = 0; m < outrows; m++) {
      printf ("\nPhase (/10): %6d---------|---------|---------|---------|---------|\n", m * 50);
      for (n = 1; n <= NumberOfOutputs; n++)
	printf ("\nOutput %4d:       %s\n", n, outputdata[m][n]);
      printf ("\n");
    }
  free_outputdata (outputdata);

  return (0);
}

void circuit_error (char *err_string, int gate_num)
{
  printf ("Error in gate %d: %s\n", gate_num + 1, err_string);
}

void source_error (char *err_string, int line_num)
{
  printf ("Error in line %d: %s\n", line_num, err_string);
}

int check_for_file_error (FILE *file)
{
  if (feof (file))
    return (0);

  perror ("A file error occurred");
  return (1);
}

void free_outputdata (char ***outputdata)
{
  int m = 0, n;

  if (outputdata) {
    while (outputdata[m]) {
      n = 0;
      while (outputdata[m][n])
	free (outputdata[m][n++]);
      free (outputdata[m++]);
    }
    free (outputdata);
  }
}

void free_inputdata (void)
{
  int n = -1;

  if (InputData) {
    while (InputData[++n]) {
      free (InputData[n]);
    }
    free (InputData);
    InputData = NULL;
  }
}

void free_circuit (void)
{
  if (CircuitGates)
    free ((char *) CircuitGates);
  CircuitGates = NULL;
  NumberOfGatesUsed = 0;
  NumberOfInputs = 0;
  NumberOfOutputs = 0;
}

void quit (char *error_message, int return_code)
{
  if (error_message)
    printf (error_message);

  free_inputdata ();
  free_circuit ();

  exit (return_code);
}
