Revision b848d1bc915eb239b7ede8de755fca6f7edb63cb authored by Jim Lawton on 24 August 2016, 17:23:36 UTC, committed by Jim Lawton on 24 August 2016, 17:23:44 UTC
1 parent 7481b16
Raw File
agc_cli.c
/*
  Original Copyright 2003-2006,2009 Ronald S. Burkey <info@sandroid.org>
  Modified Copyright 2008,2016 Onno Hommes <ohommes@alumni.cmu.edu>
  
  This file is part of yaAGC.

  yaAGC 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 of the License, or
  (at your option) any later version.

  yaAGC 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 yaAGC; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

  In addition, as a special exception, permission is given to
  link the code of this program with the Orbiter SDK library (or with 
  modified versions of the Orbiter SDK library that use the same license as 
  the Orbiter SDK library), and distribute linked combinations including 
  the two. You must obey the GNU General Public License in all respects for 
  all of the code used other than the Orbiter SDK library. If you modify 
  this file, you may extend this exception to your version of the file, 
  but you are not obligated to do so. If you do not wish to do so, delete 
  this exception statement from your version. 
 
  Filename:	agc_cli.c
  Purpose:	This module implements the yaAGC command-line interface
  Contact:	Onno Hommes <ohommes@alumni.cmu.edu>
  Reference:	http://www.ibiblio.org/apollo
  Mods:         11/30/08 OH.	Began rework
                08/04/16 OH     Fixed the GPL statement and old user-id
 */

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "agc_cli.h"
#include "agc_engine.h"
#include "agc_symtab.h"

/* Some legacy vars for now */
int FullNameMode = 0;
int QuietMode = 0;

static char CduLog[] = "yaAGC.cdulog";

static Options_t Options;

/**
The command line options try to stay compatible with the early versions of
yaAGC for some time to enable a transition period but will not list them
in the usage for the command help to discourage its usage. Hence the option
--core is somewhat confusing because in the old builds it represents the
executable core-ropes image file but normally gdb uses this for the
core file. */
static void CliShowUsage(void)
{
	printf ("Usage:\n"
"\tyaAGC [options] exec-ropes-file [core-resume-file]\n\n"
"Options:\n\n"
"--exec=EXECFILE   Use EXECFILE as the exec-ropes-file\n"
"--fullname        Output information used by emacs-GDB interface.\n"
"--symbols=SYMFILE Read symbols from SYMFILE generated by yaYUL.\n"
"--quiet           Do not print version number on startup.\n"
"--cd=DIR          Change current directory to DIR.\n"
"--command=FILE    Execute AGC commands from FILE.\n"
"--directory=DIR   Search for source files in DIR.\n"
"--port=N          Change the server port number (default=19697).\n"
"--nodebug         Disables debugging and run just the simulation\n"
"--interlace=N     Read the socket interface every N CPU instructions.\n"
"--dump-time=N     Create core image every N seconds (default = 10).\n"
"--cdu-log         Used only for debugging. Creates the file yaAGC.cdulog\n"
"                  containing data related to the bandwidth-limiting of\n"
"                  CDU inputs PCDU and MCDU.\n"
"--debug-dsky      Rather than run the core program, go into DSKY-debug\n"
"                  mode. In this mode send pre-determined codes to\n"
"                  the DSKY upon receiving DSKY keypresses.\n"
"--debug-deda      This mode runs the core program as usual, but also\n"
"                  responds to messages from yaDEDA and generates fake\n"
"                  messages to yaDEDA for testing purposes.\n"
"--deda-quiet      Used with --debug-deda to eliminate outputs from yaAGC\n"
"                  to the DEDA.  In other words, lets yaAGC parse the\n"
"                  messages being received from the DEDA, but never to\n"
"                  send any.  That lets \"yaAGC --debug-deda --deda-quiet\"\n"
"                  to be used alongside yaAGS without conflict.\n"
"--cfg=file        The name of a configuration file.  Presently, the\n"
"                  configuration files is used only for --debug-dsky\n"
"                  mode.  It would typically be the same configuration\n"
"                  file as used for yaDSKY.\n\n"
"Note that the exec-ropes file should contain exactly 36 banks\n"
"(36x1024=36864 words, or 73728 bytes). Other sizes may be accepted,\n"
"but it is unclear what (if any) utility such core-rope images\n"
"would have. (In particular, if the core-rope\n"
"is supposed to be for actual Luminary or Colossus software, then\n"
"the checksums of the missing memory banks would be incorrect, and\n"
"so the built-in self-test would fail.)\n\n");
}

extern FILE *rfopen (const char *Filename, const char *mode);


/**
This function parses the specified configuration file.
It loads its contents not a lot of checking is done here.
It returns 0 on "success" and 1 on known error.
*/
int CliParseCfg (char *Filename)
{
	char s[129] = { 0 };
	int KeyCode, Channel, Value, Result = 1;
	char Logic;
	FILE *fin;

	fin = rfopen (Filename, "r");
	if (fin)
	{
		Result = 0;

		while (NULL != fgets (s, sizeof (s) - 1, fin))
		{
			char *ss;

			/* Find newline or form feed and replace with string termination */
			for (ss = s; *ss; ss++) if (*ss == '\n' || *ss == '\r') *ss = 0;

			/* Parse string */
			if (4 == sscanf(s,"DEBUG %d %o %c %x",&KeyCode,&Channel,&Logic,&Value))
			{
				/* Ensure valid values are porvided */
				if (Channel < 0 || Channel > 255) continue;
				if (Logic != '=' && Logic != '&' &&
				    Logic != '|' && Logic != '^') continue;
				if (Value != (Value & 0x7FFF)) continue;
				if (KeyCode < 0 || KeyCode > 31) continue;
				if (NumDebugRules >= MAX_DEBUG_RULES) break;

				/* Set the Debug Rules */
				DebugRules[NumDebugRules].KeyCode = KeyCode;
				DebugRules[NumDebugRules].Channel = Channel;
				DebugRules[NumDebugRules].Logic = Logic;
				DebugRules[NumDebugRules].Value = Value;
				NumDebugRules++;
			}
			else if (!strcmp (s, "LMSIM")) CmOrLm = 0;
			else if (!strcmp (s, "CMSIM")) CmOrLm = 1;
		}
		fclose (fin);
	}
	return (Result);
}

/**
This is a private function of the Cli module. It sets the
internal Options structure members to their default value.
The Option structure handle will be returned through the
commandline parse function. */
static void CliInitializeOptions(void)
{
	  Options.core = (char*)0;
	  Options.resume = (char*)0;
	  Options.cdu_log = (char*)0;
	  Options.symtab = (char*)0;
	  Options.directory = (char*)0;
	  Options.cd = (char*)0;
	  Options.cfg = (char*)0;
	  Options.fromfile = (char*)0;
	  Options.port  = 19697;
	  Options.dump_time = 10;
	  Options.debug_dsky = 0;
	  Options.debug_deda = 0;
	  Options.deda_quiet = 0;
	  Options.quiet = 0;
	  Options.fullname = 0;
	  Options.debug = 1;
	  Options.resumed = 0;
	  Options.interlace = 50;
	  Options.version = 0;
}
/**
This function takes a character string and checks the string for
known command line options. To support both single and double dash
options this function will normalize the double dash to s single dash just
by skipping the first dash. If the token is recognized the function
will return CLI_E_OK else it will return CLI_E_UNKOWNTOKEN.
\param *token The character string
\return The success of failure indication. */
static int CliProcessArgument(char* token)
{
	int result = CLI_E_OK;
	int j;

	/* Transform -- to just - for compatibility */
	if (!strncmp(token,"--",2)) token++;

	/* Parse the token */
	if (!strcmp (token, "-help") || !strcmp (token, "/?"))	result = 1;
	else if (!strncmp (token, "-nx", 3)) { /* Ignore for now */ }
	else if (!strncmp (token, "-args", 5)) { /* Ignore for now */ }
	else if (!strncmp (token, "-core=", 6))
	{
		/* If --core is used assume classic behavior is expected */
		Options.core = strdup(&token[6]);

		/* with classi behavior default is nodebug */
		Options.debug = 0;
	}
	else if (!strncmp (token, "-directory=", 11))Options.directory = strdup(&token[11]);
	else if (!strncmp (token, "-cd=", 4))Options.cd = strdup(&token[4]);
	else if (!strncmp (token, "-exec=", 6))Options.core = strdup(&token[6]);
	else if (!strncmp (token, "-resume=", 8))Options.resume = strdup(&token[8]);
	else if (1 == sscanf (token, "-port=%d", &j)) Options.port = j;
	else if (1 == sscanf (token, "-dump-time=%d", &j)) Options.dump_time = j;
	else if (!strcmp (token, "-debug-dsky")) Options.debug_dsky = 1;
	else if (!strcmp (token, "-debug-deda")) Options.debug_deda = 1;
	else if (!strcmp (token, "-deda-quiet")) Options.deda_quiet = 1;
	else if (!strcmp (token, "-cdu-log")) Options.cdu_log = CduLog;
	else if (!strncmp (token, "-cfg=", 5)) Options.cfg = strdup(&token[5]);
	else if (!strcmp (token, "-fullname")) Options.fullname = 1;
	else if (!strcmp (token, "-quiet"))Options.quiet = 1;
	else if (!strcmp (token, "-nodebug")) Options.debug = 0;
	else if (!strcmp (token, "-debug")) Options.debug = 1;
	else if (!strcmp (token, "-version")) Options.version = 6;
	else if (!strncmp (token, "-command=",9)) Options.fromfile = strdup(&token[9]);
	else if (!strncmp (token, "-interpreter=",13)) /* Ignore for now */;
	else if (!strncmp (token, "-symbols=", 9)) Options.symtab = strdup(&token[9]);
	else if (!strncmp (token, "-symtab=", 8)) Options.symtab = strdup(&token[8]);
	else if (1 == sscanf (token,"-interlace=%d", &j)) Options.interlace = j;
	else if (Options.core == (char*)0) Options.core = strdup(token);
	else if (Options.resume == (char*)0) Options.resume = strdup(token);
	else result = CLI_E_UNKOWNTOKEN;

	return (result);
}

/**
This function takes the command line argument count and argument array
as inputs and returns an Option structure if all parses correctly.
When errors are encountered the parser will return a NULL reference
\param argc The argument count
\param *argv The pointer to the argument array.
\return A handle to an Option structure. */
Options_t* CliParseArguments(int argc, char *argv[])
{
	Options_t* result = (Options_t*)0;
	int i;

	/* Set all the defaults in the option structure */
	CliInitializeOptions();

	/* Parse the command-line tokens */
	for (i = 1; i < argc; i++) if (CliProcessArgument(argv[i])) break;

	/* If there is an issue with the provided command line interface
	 * display the usage message. Otherwise proceed with the automatic
	 * values based on the core-ropes image name.
	 */
	if (argc == 1 || i < argc || (!Options.core && !Options.debug_dsky))
	{
		/* Check if only version info is requested */
		if (Options.version) result = &Options;
		else /* Show the usage message */
			CliShowUsage();
	}
	else
	{
		/* If a new working directory is specified then change to it
		 * immediately */
		if (Options.cd > 0)
		  {
		    if (chdir(Options.cd) < 0)
		      {
		        printf("\n*** Cannot change directories. ***\n]n");
		        return (NULL);
		      }
		  }

		/* Options are properly Parsed */
		result = &Options;

		/* Must have .bin extension to find the symbol table based on the
		 * core basename with the bin extension.
		 */
		if (strstr(Options.core,".bin"))
		{
			int FullPathLength = strlen(Options.core);

			/* If Debugging without symtab set default symtab */
			if (Options.debug && !Options.symtab)
			{
				Options.symtab = (char*)calloc(1,FullPathLength + 4);
				strcpy(Options.symtab,Options.core);
				strcpy(Options.symtab + strlen(Options.core)-3,"symtab");
			}
		}

		/* If a configuration file is specified load its contents */
		if (Options.cfg)
		{
			if (CliParseCfg (Options.cfg))
			{
			  result = (Options_t*)0;
			  printf("\n*** Unknown configuration file. ***\n\n");
			}
		}
	}

	return (result);
}
back to top