/*
  Copyright 2003-2005 Ronald S. Burkey <info@sandroid.org>
  
  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, Ronald S. Burkey gives permission 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:	main.c
  Purpose:	A top-level program for running the AGC simulation\
  		in a PC environment. 
  Compiler:	GNU gcc.
  Contact:	Ron Burkey <info@sandroid.org>
  Reference:	http://www.ibiblio.org/apollo/index.html
  Mods:		04/05/03 RSB.	Began.
		10/20/03 RSB.	Added a fake times() function for WIN32.
		11/26/03 RSB.	Added the actual machine-cycle timing.  
				(Previously, it just ran as fast as it could.
				Now, the machine cycles are about 11.7 us.)
				Began adding a primitive debugging capability.
		11/28/03 RSB.	Added a bunch of new debugging stuff.
		11/30/03 RSB.	Added interactively helting AGC-program
				execution (whether in --debug mode or not).
		05/01/04 RSB	--debug-dsky mode was fixed by adding the 
				--debug mode.  Too bad I didn't test it.
		05/04/04 RSB	The disassembly of AD was reading ADD.
		05/05/04 RSB	In the debugger, added S and N as synonyms for
				STEP and NEXT.
		05/06/04 RSB	Now displays error for non-existent *.ini file.
				Now does a little bit to find *.bin and *.ini
				in the installation directory if not in the
				current directory.
		05/08/04 RSB	The disassembler now shows the opcodes for 
				the "implied address codes" --- i.e., things
				like SQUARE in place of "MP A".  Also, the
				instructions are now shown as indexed.  
				Corrected the starting cycle count used for
				timing in case the --resume file is used.
				The timing used in debugging was completely
				wrong, as it did not account for the time
				the debugger was paused for user input.
		05/09/04 RSB	Added the GETOCT command to the disassembler.
		05/10/04 RSB	Fixed bank-editing in --debug mode, hopefully.
		05/12/04 RSB	Added backtrace stuff.
		05/13/04 RSB	Corrected disassembly of superbanks.  Fixed 
				the --debug command EDIT, which I apparently
				broke yesterday.
		05/14/04 RSB	Added interrupt-related --debug commands.
		05/17/04 RSB	Added INTERRUPTS ENABLE and INTERRUPTS DISABLE.
				... Changed to MASKOFF 0 and MASKON 0.
				Also changed INTERRUPT to INTON, added INTOFF,
				MASKON, and MASKOFF.
		05/31/04 RSB	Debugger now shows actual bank numbers in addition
				to just the contents of the EB and FB registers.
				Also, the debugger now correctly decodes overflow
				and shows the accumulator as a 16-bit register.
				For the DUMP, EDIT, DELETE, and BREAK debugger
				commands, erasable and fixed bank numbers are 
				now taken from the EB or FB register if omitted.
				Socket info now shown in the debugger.
		06/02/04 RSB	In --debug, BREAK and DELETE didn't work 
				properly in and around superbanks (nor did
				the breakpoint itself work).
		06/08/04 RSB	Added primitive watchpoints.
		06/11/04 RSB	Altered the sys/times.h include in WIN32.
		06/30/04 RSB	Implemented PATTERN.
		07/01/04 RSB	Enlarged the number of allowed breakpoints
				dramatically (32 -> 256), in order to 
				account for the possibility of trapping
				upon executing a lot of different instruction
				types at once.  Also, accounted for a certain
				level of testing of FROMFILEs.  Fixed it so
				that COREDUMP and FROMFILE don't automatically
				convert filenames to upper case.  Added a 
				comment command ("#") to --debug mode.
		07/12/04 RSB	Q is now 16 bits.
		07/15/04 RSB	Added the LOG command to --debug mode.
		07/15/04 RSB	Data now aligned at bit 0 rather than bit 1.
		07/19/04 RSB	Changed the WIN32 version of times from clock()
				to GetTickCount().  Added the --interlace 
				option.  Adjusted the lengths of the debugging status
				messages to fit on the crippled Win32 command lines.
		07/20/04 RSB	Oops!  Apparently forgot to implement --port=N.
		08/01/04 RSB	The enormous CPU usage in --debug whilst waiting
				for a keystroke has been cut to almost nothing.
		08/09/04 RSB	Adapted to use a threaded model for keyboard input,
				so as to avoid the problem if getc() blocking on
				Win32.
		08/10/04 RSB	Split help screen from --debug mode into separate
				help topics.  This is partially just from good 
				sense, but also partially because when I just
				printf the whole menu, it's truncated after some
				(presumably fixed) number of characters, and I don't
				feel like figuring out that problem.
		02/27/05 RSB	Added the license exception, as required by
				the GPL, for linking to Orbiter SDK libraries.
		05/14/05 RSB	Corrected website references.
		05/31/05 RSB	Added --debug-deda mode.
		07/05/05 RSB	Added autosave and autorestore of erasable memory.
		07/13/05 RSB	Fixed a possible issue of using too much CPU time
				in Win32.
		
*/

//#define VERSION(x) #x

#include <stdio.h>
#include "agc_engine.h"
#include <string.h>
#include <unistd.h>
#ifdef WIN32
#include <windows.h>
#include <sys/time.h>
#define LB "\r\n"
#else
#include <time.h>
#include <sys/times.h>
#define LB ""
#endif
#include <ctype.h>
FILE *rfopen (const char *Filename, const char *mode);

// Here's a keyboard buffer
static char KeyboardBuffer[256];
char *nbfgets (char *Buffer, int Length);

// Some buffers for strings.
char s[129], sraw[129], s1[129], s2[129], s3[129], s4[129], s5[129];

// Time between checks for --debug keystrokes.
#define KEYSTROKE_CHECK (sysconf (_SC_CLK_TCK) / 4)

agc_t State;

#define MAX_BREAKPOINTS 256
#define PATTERN_SIZE 017777777
#define PAT "%08o"
typedef struct
{
  int Address12;		// A 12-bit address.
  // RegBB is like the normal BB register, except that we also insert the superbank
  // bit into it.  In other words, it's like BB bitwise-ORred with channel 7.
  // For PATTERNs, Address12 is the value we're watching for, and vRegBB is the
  // mask we apply before comparing.
  int vRegBB;
  int WatchValue;
  int WatchBreak;		// 0 for breakpoints, 1 for watchpoints.
}
Breakpoint_t;
static Breakpoint_t Breakpoints[MAX_BREAKPOINTS];
static int NumBreakpoints = 0;

//-----------------------------------------------------------------------------------
#ifdef WIN32
struct tms {
  clock_t tms_utime;  /* user time */
  clock_t tms_stime;  /* system time */
  clock_t tms_cutime; /* user time of children */
  clock_t tms_cstime; /* system time of children */
};
clock_t times (struct tms *p)
{
  return (GetTickCount ());
}
#define _SC_CLK_TCK (1000)
#define sysconf(x) (x)
#endif // WIN32

//-----------------------------------------------------------------------------------
// Get the value stored at an address, as specified by a Breakpoint_t.

int16_t GetWatch (agc_t * State, Breakpoint_t * bp)
{
  int Address12, vRegBB;
  Address12 = (bp->Address12 & 07777);
  vRegBB = (bp->vRegBB & 07777);
  if (Address12 <= 01377)	// Fixed erasable.
    return (State->Erasable[Address12 / 0400][Address12 & 0377]);
  else if (Address12 <= 01777)	// Switched erasable.
    return (State->Erasable[vRegBB & 07][Address12 & 0377]);
  else
    return (0);
}

//-----------------------------------------------------------------------------------
// We don't do a lot of checking here.  Too bad!  Maybe one day ....
// Even so, returns 0 on "success" and 1 on known error.

int
ParseCfg (agc_t *State, char *Filename)
{
  char s[129] = { 0 };
  int KeyCode, Channel, Value;
  char Logic;
  FILE *fin;
  fin = rfopen (Filename, "r");
  if (fin == NULL)
    return (1);
  while (NULL != fgets (s, sizeof (s) - 1, fin))
    {
      char *ss;
      for (ss = s; *ss; ss++)
        if (*ss == '\n' || *ss == '\r')
	  *ss = 0;
      if (4 ==
	  sscanf (s, "DEBUG %d %o %c %x", &KeyCode, &Channel, &Logic, &Value))
	{
	  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;
	  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 (0);
}

//-----------------------------------------------------------------------------------
// Gets the value at the instruction pointer.  The INDEX is automatically added,
// and the Extracode bit is used as the 16th bit.

static int
GetFromZ (agc_t * State)
{
  int CurrentZ, Bank, Value;
  CurrentZ = (State->Erasable[0][RegZ] & 07777);
  // Print the address.
  if (CurrentZ < 01400)
    {
      Bank = CurrentZ / 0400;
      Value = State->Erasable[Bank][CurrentZ & 0377];
    }
  else if (CurrentZ >= 04000)
    {
      Bank = 2 + (CurrentZ - 04000) / 02000;
      Value = State->Fixed[Bank][CurrentZ & 01777];
    }
  else if (CurrentZ < 02000)
    {
      Bank = (7 & State->Erasable[0][RegBB]);
      Value = State->Erasable[Bank][CurrentZ & 0377];
    }
  else
    {
      Bank = (31 & State->Erasable[0][RegBB]);
      if (0x18 == (Bank & 0x18) && (State->OutputChannel7 & 0100))
	Bank += 0x08;
      Value = State->Fixed[Bank][CurrentZ & 01777];
    }
  Value =
    OverflowCorrected (AddSP16
		       (SignExtend (Value), SignExtend (State->IndexValue)));
  Value = (Value & 077777);
  if (State->ExtraCode)
    Value |= 0100000;		// Extracode?
  if (State->IndexValue)
    Value |= 0200000;		// Indexed?
  if (0040000 == (0140000 & State->Erasable[0][RegA]))
    Value |= 0400000;		// Positive overflow?
  if (0100000 == (0140000 & State->Erasable[0][RegA]))
    Value |= 01000000;		// Negative overflow?
  if (0 != (0100000 & State->Erasable[0][RegA]))
    Value |= 02000000;		// Sign of Accumulator.
  if (0 != (0100000 & (State->Erasable[0][RegL] ^ State->Erasable[0][RegA])))
    Value |= 04000000;		// Signs of Accumulator and L disagree.
  if (State->InIsr)
    Value |= 010000000;		// Inside of an ISR?
  return (Value);
}

//-----------------------------------------------------------------------------------
// Displays a disassembly of the current address.

static void
Disassemble (agc_t * State)
{
  int CurrentZ, Bank, Value, Erasable = 0, Fixed = 0;
  CurrentZ = (State->Erasable[0][RegZ] & 07777);
  // Print the address.
  if (CurrentZ < 01400)
    {
      Erasable = 1;
      printf ("   %05o\t", CurrentZ);
      Bank = CurrentZ / 0400;
      Value = State->Erasable[Bank][CurrentZ & 0377];
    }
  else if (CurrentZ >= 04000)
    {
      Fixed = 1;
      printf ("   %05o\t", CurrentZ);
      Bank = 2 + (CurrentZ - 04000) / 02000;
      Value = State->Fixed[Bank][CurrentZ & 01777];
    }
  else if (CurrentZ < 02000)
    {
      Erasable = 1;
      Bank = 7 & State->Erasable[0][RegBB];
      printf ("E%o,%05o\t", Bank, 01400 + (CurrentZ & 0377));
      Value = State->Erasable[Bank][CurrentZ & 0377];
    }
  else
    {
      Fixed = 1;
      Bank = 037 & (State->Erasable[0][RegBB] >> 10);
      if (Bank >= 030 && State->OutputChannel7 & 0100)
	Bank += 010;
      printf ("%02o,%05o\t", Bank, 02000 + (CurrentZ & 01777));
      Value = State->Fixed[Bank][CurrentZ & 01777];
    }
  Value &= 077777;
  // Print the octal value stored at the address.  
  printf ("%06o\t\t", Value);
  // Account for the index value.
  if (State->IndexValue != 0)
    {
      // The following calculation doesn't overflow-correct the instruction,
      // whereas the agc_engine DOES overflow-correct.
      if (0 == (040000 & State->IndexValue))
	Value += (State->IndexValue & 077777);
      else
	Value -= (~State->IndexValue & 077777);
      printf ("w/i:\t");
    }
  // Disassemble the instruction at the address.
  if (State->ExtraCode == 0)
    {
      if (Value == 040000)
	printf ("COM\n");
      else if (Value == 020001)
	printf ("DDOUBL\n");
      else if (Value == 060000)
	printf ("DOUBLE\n");
      else if (Value == 052006)
	printf ("DTCB\n");
      else if (Value == 052005)
	printf ("DTCF\n");
      else if (Value == 6)
	printf ("EXTEND\n");
      else if (Value == 4)
	printf ("INHINT\n");
      else if (Fixed && Value == CurrentZ + 1 && Value != 10000)
	printf ("NOOP\n");
      else if (Erasable && Value == 030000)
	printf ("NOOP\n");
      else if (Value == 054000)
	printf ("OVSK\n");
      else if (Value == 3)
	printf ("RELINT\n");
      else if (Value == 050017)
	printf ("RESUME\n");
      else if (Value == 2)
	printf ("RETURN\n");
      else if (Value == 054005)
	printf ("TCAA\n");
      else if (Value == 1)
	printf ("XLQ\n");
      else if (Value == 0)
	printf ("XXALQ\n");
      else if (Value == 022007)
	printf ("ZL\n");
      else
	switch (Value & 0x7000)
	  {
	  case 0x0000:
	    printf ("TC\t%04o\n", Value & 0xFFF);
	    break;
	  case 0x1000:
	    if (0 == (Value & 0x0C00))
	      printf ("CCS\t%04o\n", Value & 0x3FF);
	    else
	      printf ("TCF\t%04o\n", Value & 0xFFF);
	    break;
	  case 0x2000:
	    switch (Value & 0x0C00)
	      {
	      case 0x000:
		printf ("DAS\t%04o\n", (Value - 1) & 0x3FF);
		break;
	      case 0x400:
		printf ("LXCH\t%04o\n", Value & 0x3FF);
		break;
	      case 0x800:
		printf ("INCR\t%04o\n", Value & 0x3FF);
		break;
	      case 0xC00:
		printf ("ADS\t%04o\n", Value & 0x3FF);
		break;
	      }
	    break;
	  case 0x3000:
	    printf ("CA\t%04o\n", Value & 0xFFF);
	    break;
	  case 0x4000:
	    printf ("CS\t%04o\n", Value & 0xFFF);
	    break;
	  case 0x5000:
	    switch (Value & 0x0C00)
	      {
	      case 0x000:
		printf ("INDEX\t%04o\n", Value & 0x3FF);
		break;
	      case 0x400:
		printf ("DXCH\t%04o\n", (Value - 1) & 0x3FF);
		break;
	      case 0x800:
		printf ("TS\t%04o\n", Value & 0x3FF);
		break;
	      case 0xC00:
		printf ("XCH\t%04o\n", Value & 0x3FF);
		break;
	      }
	    break;
	  case 0x6000:
	    printf ("AD\t%04o\n", Value & 0xFFF);
	    break;
	  case 0x7000:
	    printf ("MASK\t%04o\n", Value & 0xFFF);
	    break;
	  }
    }
  else
    {
      if (Value == 040001)
	printf ("DCOM\n");
      else if (Value == 050017)
	printf ("RESUME\n");
      else if (Value == 070000)
	printf ("SQUARE\n");
      else if (Value == 022007)
	printf ("ZQ\n");
      else
	switch (Value & 0x7000)
	  {
	  case 0x0000:
	    switch (Value & 0x0E00)
	      {
	      case 0x0000:
		printf ("READ\t%03o\n", Value & 0777);
		break;
	      case 0x0200:
		printf ("WRITE\t%03o\n", Value & 0777);
		break;
	      case 0x0400:
		printf ("RAND\t%03o\n", Value & 0777);
		break;
	      case 0x0600:
		printf ("WAND\t%03o\n", Value & 0777);
		break;
	      case 0x0800:
		printf ("ROR\t%03o\n", Value & 0777);
		break;
	      case 0x0A00:
		printf ("WOR\t%03o\n", Value & 0777);
		break;
	      case 0x0C00:
		printf ("RXOR\t%03o\n", Value & 0777);
		break;
	      case 0x0E00:
		printf ("EDRUPT\t%03o\n", Value & 0777);
		break;
	      }
	    break;
	  case 0x1000:
	    if (0 == (Value & 0x0C00))
	      printf ("DV\t%04o\n", Value & 0x3FF);
	    else
	      printf ("BZF\t%04o\n", Value & 0xFFF);
	    break;
	  case 0x2000:
	    switch (Value & 0x0C00)
	      {
	      case 0x000:
		printf ("MSU\t%04o\n", Value & 0x3FF);
		break;
	      case 0x400:
		printf ("QXCH\t%04o\n", Value & 0x3FF);
		break;
	      case 0x800:
		printf ("AUG\t%04o\n", Value & 0x3FF);
		break;
	      case 0xC00:
		printf ("DIM\t%04o\n", Value & 0x3FF);
		break;
	      }
	    break;
	  case 0x3000:
	    printf ("DCA\t%04o\n", (Value - 1) & 0xFFF);
	    break;
	  case 0x4000:
	    printf ("DCS\t%04o\n", (Value - 1) & 0xFFF);
	    break;
	  case 0x5000:
	    printf ("INDEX\t%04o\n", Value & 0xFFF);
	    break;
	  case 0x6000:
	    if (0 == (Value & 0x0C00))
	      printf ("SU\t%04o\n", Value & 0x3FF);
	    else
	      printf ("BZMF\t%04o\n", Value & 0xFFF);
	    break;
	  case 0x7000:
	    printf ("MP\t%04o\n", Value & 0xFFF);
	    break;
	  }
    }
}

//-----------------------------------------------------------------------------------
// Dump a memory range to the screen.  The memory range is specified by strings of 
// the following 6 types:
//      DUMP %o E%o,%o          N words of switched erasable
//      DUMP %o %o,%o           N words of common fixed
//      DUMP %o %o              N words of unswitched erasable or fixed-fixed.
//      DUMP %o C%o             N words from i/o channels.
//      DUMP E%o,%o             1 word of switched erasable
//      DUMP %o,%o              1 word of common fixed
//      DUMP %o                 1 word of unswitched erasable or fixed-fixed.
//      DUMP C%o                1 word from i/o channels.
//      DUMP                    Repeats last dump.

static void
DumpMemory (agc_t * State, char *s)
{
  static int TypeLast = 0, iLast = 1, jLast = 0, kLast = 0;
  // i=N, j=banknum, k=offset.
  int i, j, k, Count = 0;
  if (!strcmp (s, "DUMP"))
    {
      i = iLast;
      j = jLast;
      k = kLast;
      if (TypeLast == 0)
	goto Type0;
      if (TypeLast == 1)
	goto Type1;
      if (TypeLast == 2)
	goto Type2;
      if (TypeLast == 3)
	goto Type3;
      iLast = jLast = kLast = 0;
      goto Type0;
    }
  else if (3 == sscanf (s, "DUMP%o E%o,%o", &i, &j, &k)
	   || ((i = 1), 2 == sscanf (s, "DUMP E%o,%o", &j, &k)))
    {
    Erasable:
    Type0:
      TypeLast = 0;
      iLast = i;
      jLast = j;
      kLast = k;
      if (j < 0 || j > 7)
	{
	  printf ("Illegal erasable bank number (%o).  Must be 0-7.\n", j);
	  return;
	}
      if (k < 01400 || k >= 02000)
	{
	  printf ("Illegal offset (%o).  Must be 1400-1777.\n", k);
	  return;
	}
      if (k + i > 02000)
	{
	  printf ("Range overflows bank.  Truncated to fit in the bank.\n");
	  i = 02000 - k;
	}
      if (i <= 0)
	{
	  printf ("Requested address range is empty.\n");
	  return;
	}
      for (; i > 0; i--, k++, Count++)
	{
	  if (Count == 0 || 0 == (k & 7))
	    printf ("E%o,%06o: ", j, k);
	  printf (" %05o", 0177777 & State->Erasable[j][k - 01400]);
	  if (7 == (7 & k))
	    printf ("\n");
	}
      if (0 != (7 & k))
	printf ("\n");
    }
  else if (3 == sscanf (s, "DUMP%o%o,%o", &i, &j, &k)
	   || ((i = 1), 2 == sscanf (s, "DUMP%o,%o", &j, &k)))
    {
    Fixed:
    Type1:
      TypeLast = 1;
      iLast = i;
      jLast = j;
      kLast = k;
      if (j < 0 || j > 043)
	{
	  printf ("Illegal common-fixed bank number (%o).  Must be 0-43.\n",
		  j);
	  return;
	}
      if (k < 02000 || k >= 04000)
	{
	  printf ("Illegal offset (%o).  Must be 2000-3777.\n", k);
	  return;
	}
      if (k + i > 04000)
	{
	  printf ("Range overflows bank.  Truncated to fit in the bank.\n");
	  i = 04000 - k;
	}
      if (i <= 0)
	{
	  printf ("Requested address range is empty.\n");
	  return;
	}
      for (; i > 0; i--, k++, Count++)
	{
	  if (Count == 0 || 0 == (k & 7))
	    printf ("%02o,%05o: ", j, k);
	  printf (" %06o", 0177777 & State->Fixed[j][k - 02000]);
	  if (7 == (7 & k))
	    printf ("\n");
	}
      if (0 != (7 & k))
	printf ("\n");
    }
  else if (2 == sscanf (s, "DUMP%o C%o", &i, &k)
	   || ((i = 1), 1 == sscanf (s, "DUMP C%o", &k)))
    {
    Type3:
      TypeLast = 3;
      iLast = i;
      jLast = j;
      kLast = k;
      if (k >= 0 && k <= 0777)
	{
	  if (k + i > 01000)
	    {
	      printf
		("Range overflows defined i/o channels.  Range truncated to fit.\n");
	      i = 01000 - k;
	    }
	  if (i <= 0)
	    {
	      printf ("Requested channel range is empty.\n");
	      return;
	    }
	  for (; i > 0; i--, k++, Count++)
	    {
	      if (Count == 0 || 0 == (k & 7))
		printf ("C%05o: ", k);
	      printf (" %06o", 0177777 & ReadIO (State, k));
	      if (7 == (7 & k))
		printf ("\n");
	    }
	  if (0 != (7 & k))
	    printf ("\n");
	}
      else
	{
	  printf ("I/O channel range not recognized.  Perhaps 000-0777?\n");
	  return;
	}
    }
  else if (2 == sscanf (s, "DUMP%o%o", &i, &k)
	   || ((i = 1), 1 == sscanf (s, "DUMP%o", &k)))
    {
    Type2:
      TypeLast = 2;
      iLast = i;
      jLast = j;
      kLast = k;
      if (k >= 0 && k <= 01377)
	{
	  // Note that I depend on a trick here, which is knowing that
	  // State.Erasable[0] butts up end-to-end with State.Erasable[1],
	  // which butts up end-to-end with State.Erasable[2].
	  // If this assumption isn't right, then dumps that span the 
	  // boundary at addresses 0400 or 01000 won't work right.
	  if (k + i > 01400)
	    {
	      printf
		("Range overflows bank.  Truncated to fit in the bank.\n");
	      i = 01400 - k;
	    }
	  if (i <= 0)
	    {
	      printf ("Requested address range is empty.\n");
	      return;
	    }
	  for (; i > 0; i--, k++, Count++)
	    {
	      if (Count == 0 || 0 == (k & 7))
		printf ("%05o: ", k);
	      printf (" %06o", 0177777 & State->Erasable[0][k]);
	      if (7 == (7 & k))
		printf ("\n");
	    }
	  if (0 != (7 & k))
	    printf ("\n");
	}
      else if (k >= 04000 && k <= 07777)
	{
	  // Note that I depend on a trick here, which is knowing that
	  // State.Fixed[3] butts up end-to-end with State.Fixed[2].
	  // If this assumption isn't right, then dumps that span the 
	  // boundary at address 06000 won't work right.
	  if (k + i > 010000)
	    {
	      printf
		("Range overflows bank.  Truncated to fit in the bank.\n");
	      i = 010000 - k;
	    }
	  if (i <= 0)
	    {
	      printf ("Requested address range is empty.\n");
	      return;
	    }
	  for (; i > 0; i--, k++, Count++)
	    {
	      if (Count == 0 || 0 == (k & 7))
		printf ("%05o: ", k);
	      printf (" %06o", 0177777 & State->Fixed[2][k - 04000]);
	      if (7 == (7 & k))
		printf ("\n");
	    }
	  if (0 != (7 & k))
	    printf ("\n");
	}
      else if (k >= 01400 && k <= 01777)
	{
	  j = (7 & (State->Erasable[0][RegEB] >> 8));
	  goto Erasable;
	}
      else if (k >= 02000 && k <= 03777)
	{
	  j = (037 & (State->Erasable[0][RegFB] >> 10));
	  if (j >= 030 && 0 != (0100 & State->OutputChannel7))
	    j += 010;
	  goto Fixed;
	}
      else
	{
	  printf
	    ("Memory range not recognized.  Perhaps 0000-01377 or 04000-07777?\n");
	  return;
	}
    }
  else
    {
      printf
	("The format of this command is \"DUMP N A\", where N is the number of words\n");
      printf ("and A is the starting address.  Examples of A are:\n");
      printf ("\t1234\t\tUnswitched erasable memory, 0-1377 octal.\n");
      printf ("\t4567\t\tCommon fixed memory, 4000-7777 octal.\n");
      printf
	("\tE4,1456\t\tSwitched erasable memory, banks E0-E7, offset 1400-1777 octal.\n");
      printf
	("\t12,2345\t\tFixed fixed memory, banks 0-43, offset 2000-3777 octal.\n");
      printf ("\tI123\t\tI/O channel, 000-777.\n");
    }
}

//-----------------------------------------------------------------------------------
// Add a breakpoint.

static void
AddBreakpoint (agc_t * State, char *s)
{
  int j, k, i, vRegBB;
  if (2 == sscanf (s, "BREAK E%o,%o", &j, &k))
    {
    Erasable:
      if (j < 0 || j > 7)
	{
	  printf ("Illegal erasable bank number (%o).  Must be 0-7.\n", j);
	  return;
	}
      if (k < 01400 || k >= 02000)
	{
	  printf ("Illegal offset (%o).  Must be 1400-1777.\n", k);
	  return;
	}
      vRegBB = j;
      if (j < 3)
	{
	  printf
	    ("Note that this breakpoint will NOT be hit on reaching the corresponding location\n");
	  printf
	    ("in unswitched-erasable.  Make a separate breakpoint for that, if desired.\n");
	}
    }
  else if (2 == sscanf (s, "BREAK %o,%o", &j, &k))
    {
    Fixed:
      if (j < 0 || j > 043)
	{
	  printf ("Illegal common-fixed bank number (%o).  Must be 0-43.\n",
		  j);
	  return;
	}
      if (k < 02000 || k >= 04000)
	{
	  printf ("Illegal offset (%o).  Must be 2000-3777.\n", k);
	  return;
	}
      vRegBB = j << 10;
      if (j > 037)
	{
	  vRegBB = ((j - 010) << 10);
	  vRegBB |= 0100;
	}
      if (j == 2 || j == 3)
	{
	  printf
	    ("Note that this breakpoint will NOT be hit on reaching the corresponding location\n");
	  printf
	    ("in fixed-fixed memory.  Make a separate breakpoint for that, if desired.\n");
	}
    }
  else if (1 == sscanf (s, "BREAK %o", &k))
    {
      if (k >= 0 && k <= 01377)
	{
	  printf
	    ("Note that this breakpoint will NOT be hit on reaching the corresponding location\n");
	  printf
	    ("in switched-erasable banks E0-E2.  Make a separate breakpoint for that, if desired.\n");
	}
      else if (k >= 04000 && k <= 07777)
	{
	  printf
	    ("Note that this breakpoint will NOT be hit on reaching the corresponding location\n");
	  printf
	    ("in common-fixed banks 02 or 03.  Make a separate breakpoint for that, if desired.\n");
	}
      else if (k >= 01400 && k <= 01777)
	{
	  j = (7 & (State->Erasable[0][RegEB] >> 8));
	  goto Erasable;
	}
      else if (k >= 02000 && k <= 03777)
	{
	  j = (037 & (State->Erasable[0][RegFB] >> 10));
	  if (j >= 030 && 0 != (0100 & State->OutputChannel7))
	    j += 010;
	  goto Fixed;
	}
      else
	{
	  printf
	    ("Memory range not recognized.  Perhaps 0000-01377 or 04000-07777?\n");
	  return;
	}
      vRegBB = 0;
    }
  else
    {
      printf
	("The format of this command is \"BREAK A\", where A is an address.  Examples of A are:\n");
      printf ("\t1234\t\tUnswitched erasable memory, 0-1377 octal.\n");
      printf ("\t4567\t\tCommon fixed memory, 4000-7777 octal.\n");
      printf
	("\tE4,1456\t\tSwitched erasable memory, banks E0-E7, offset 1400-1777 octal.\n");
      printf
	("\t12,2345\t\tFixed fixed memory, banks 0-43, offset 2000-3777 octal.\n");
      return;
    }
  for (i = 0; i < NumBreakpoints; i++)
    if (Breakpoints[i].Address12 == k && Breakpoints[i].vRegBB == vRegBB &&
	Breakpoints[i].WatchBreak == 0)
      {
	printf ("This breakpoint already exists.\n");
	return;
      }
  if (NumBreakpoints < MAX_BREAKPOINTS)
    {
      Breakpoints[NumBreakpoints].Address12 = k;
      Breakpoints[NumBreakpoints].vRegBB = vRegBB;
      Breakpoints[NumBreakpoints].WatchBreak = 0;
      NumBreakpoints++;
      printf ("Breakpoint added.\n");
    }
  else
    printf ("The maximum number of breakpoints is already defined.\n");
}

//-----------------------------------------------------------------------------------
// Add a watchpoint.

static void
AddWatchpoint (agc_t * State, char *s)
{
  int j, k, i, vRegBB;
  if (2 == sscanf (s, "WATCH E%o,%o", &j, &k))
    {
    Erasable:
      if (j < 0 || j > 7)
	{
	  printf ("Illegal erasable bank number (%o).  Must be 0-7.\n", j);
	  return;
	}
      if (k < 01400 || k >= 02000)
	{
	  printf ("Illegal offset (%o).  Must be 1400-1777.\n", k);
	  return;
	}
      vRegBB = j;
      if (j < 3)
	{
	  printf
	    ("Note that this watchpoint will be hit on reaching the corresponding location\n");
	  printf ("in unswitched-erasable.\n");
	}
    }
  else if (1 == sscanf (s, "WATCH %o", &k))
    {
      if (k >= 0 && k <= 01377)
	{
	  printf
	    ("Note that this watchpoint will be hit on reaching the corresponding location\n");
	  printf ("in switched-erasable banks E0-E2.\n");
	}
      else if (k >= 04000 && k <= 07777)
	goto Illegal;
      else if (k >= 01400 && k <= 01777)
	{
	  j = (7 & (State->Erasable[0][RegEB] >> 8));
	  goto Erasable;
	}
      else if (k >= 02000 && k <= 03777)
	goto Illegal;
      else
	{
	Illegal:
	  printf ("Memory range not recognized or illegal.\n");
	  return;
	}
      vRegBB = 0;
    }
  else
    {
      printf
	("The format of this command is \"WATCH A\", where A is an address in erasable memory.\n"
	 "Examples of A are:\n");
      printf ("\t1234\t\tUnswitched erasable memory, 0-1377 octal.\n");
      printf
	("\tE4,1456\t\tSwitched erasable memory, banks E0-E7, offset 1400-1777 octal.\n");
      return;
    }
  for (i = 0; i < NumBreakpoints; i++)
    if (Breakpoints[i].Address12 == k && Breakpoints[i].vRegBB == vRegBB &&
	Breakpoints[i].WatchBreak == 1)
      {
	printf ("This watchpoint already exists.\n");
	return;
      }
  if (NumBreakpoints < MAX_BREAKPOINTS)
    {
      Breakpoints[NumBreakpoints].Address12 = k;
      Breakpoints[NumBreakpoints].vRegBB = vRegBB;
      Breakpoints[NumBreakpoints].WatchBreak = 1;
      Breakpoints[NumBreakpoints].WatchValue =
	GetWatch (State, &Breakpoints[NumBreakpoints]);
      NumBreakpoints++;
      printf ("Watchpoint added.\n");
    }
  else
    printf ("The maximum number of watchpoints is already defined.\n");
}

//----------------------------------------------------------------------------------------------
// Delete a breakpoint

static void
DeleteBreakpoint (agc_t * State, char *s)
{
  int j, k, i, vRegBB;
  if (!strcmp (s, "DELETE"))
    {
      printf ("All breakpoints/watchpoints deleted.\n");
      NumBreakpoints = 0;
      return;
    }
  else if (2 == sscanf (s, "DELETE E%o,%o", &j, &k))
    {
    Erasable:
      if (j < 0 || j > 7)
	{
	  printf ("Illegal erasable bank number (%o).  Must be 0-7.\n", j);
	  return;
	}
      if (k < 01400 || k >= 02000)
	{
	  printf ("Illegal offset (%o).  Must be 1400-1777.\n", k);
	  return;
	}
      vRegBB = j;
      if (j < 3)
	{
	  printf
	    ("Note that if there is a break/watch at the corresponding location\n");
	  printf
	    ("in unswitched-erasable memory, this will not delete it.  Do that separately.\n");
	}
    }
  else if (2 == sscanf (s, "DELETE %o,%o", &j, &k))
    {
    Fixed:
      if (j < 0 || j > 043)
	{
	  printf ("Illegal common-fixed bank number (%o).  Must be 0-43.\n",
		  j);
	  return;
	}
      if (k < 02000 || k >= 04000)
	{
	  printf ("Illegal offset (%o).  Must be 2000-3777.\n", k);
	  return;
	}
      vRegBB = j << 10;
      if (j > 037)
	{
	  vRegBB = ((j - 010) << 10);
	  vRegBB |= 0100;
	}
      if (j == 2 || j == 3)
	{
	  printf
	    ("Note that if there is a break/watch at the corresponding location\n");
	  printf
	    ("in fixed-fixed memory, this will not delete it.  Do that separately.\n");
	}
    }
  else if (1 == sscanf (s, "DELETE %o", &k))
    {
      if (k >= 0 && k <= 01377)
	{
	  printf
	    ("Note that if there is a break/watch at the corresponding location\n");
	  printf
	    ("in switched-erasable memory, this will not delete it.  Do that separately.\n");
	}
      else if (k >= 04000 && k <= 07777)
	{
	  printf
	    ("Note that if there is a break/watch at the corresponding location\n");
	  printf
	    ("in common-fixed memory, this will not delete it.  Do that separately.\n");
	}
      else if (k >= 01400 && k <= 01777)
	{
	  j = (7 & (State->Erasable[0][RegEB] >> 8));
	  goto Erasable;
	}
      else if (k >= 02000 && k <= 03777)
	{
	  j = (037 & (State->Erasable[0][RegFB] >> 10));
	  if (j >= 030 && 0 != (0100 & State->OutputChannel7))
	    j += 010;
	  goto Fixed;
	}
      else
	{
	  printf
	    ("Memory range not recognized.  Perhaps 0000-01377 or 04000-07777?\n");
	  return;
	}
      vRegBB = 0;
    }
  else
    {
      printf
	("The format of this command is \"DELETE A\", where A is an address.  Examples of A are:\n");
      printf ("\t1234\t\tUnswitched erasable memory, 0-1377 octal.\n");
      printf ("\t4567\t\tCommon fixed memory, 4000-7777 octal.\n");
      printf
	("\tE4,1456\t\tSwitched erasable memory, banks E0-E7, offset 1400-1777 octal.\n");
      printf
	("\t12,2345\t\tFixed fixed memory, banks 0-43, offset 2000-3777 octal.\n");
      return;
    }
  for (i = 0; i < NumBreakpoints; i++)
    if (Breakpoints[i].Address12 == k && Breakpoints[i].vRegBB == vRegBB)
      {
	if (Breakpoints[i].WatchBreak)
	  printf ("Watchpoint deleted.\n");
	else
	  printf ("Breakpoint deleted.\n");
	NumBreakpoints--;
	for (j = i; j < NumBreakpoints; j++)
	  Breakpoints[j] = Breakpoints[j + 1];
	i--;
      }
}

//----------------------------------------------------------------------------------------------
// Change the value stored at a memory location or i/o channel.

static void
EditMemory (agc_t * State, char *s)
{
  int j, k, Value;
  if (3 == sscanf (s, "EDIT E%o,%o%o", &j, &k, &Value))
    {
    Erasable:
      if (j < 0 || j > 7)
	{
	  printf ("Illegal erasable bank number (%o).  Must be 0-7.\n", j);
	  return;
	}
      if (k < 01400 || k >= 02000)
	{
	  printf ("Illegal offset (%o).  Must be 1400-1777.\n", k);
	  return;
	}
      State->Erasable[j][k & 0377] = Value;
    }
  else if (3 == sscanf (s, "EDIT%o,%o%o", &j, &k, &Value))
    {
    Fixed:
      if (j < 0 || j > 043)
	{
	  printf ("Illegal common-fixed bank number (%o).  Must be 0-43.\n",
		  j);
	  return;
	}
      if (k < 02000 || k >= 04000)
	{
	  printf ("Illegal offset (%o).  Must be 2000-3777.\n", k);
	  return;
	}
      State->Fixed[j][k & 01777] = Value;
    }
  else if (2 == sscanf (s, "EDIT%o%o", &k, &Value))
    {
      if (k >= 0 && k <= 01377)
	{
	  State->Erasable[0][k] = Value;
	  // Must handle EB, FB, and BB registers specially.
	  if (k == RegEB)
	    {
	      State->Erasable[0][RegEB] &= 03400;
	      State->Erasable[0][RegBB] = (State->Erasable[0][RegBB] & ~7) |
		(State->Erasable[0][RegEB] >> 8);
	    }
	  else if (k == RegFB)
	    {
	      State->Erasable[0][RegFB] &= 076000;
	      State->Erasable[0][RegBB] =
		(State->Erasable[0][RegBB] & ~076000) | State->
		Erasable[0][RegFB];
	    }
	  else if (k == RegBB)
	    {
	      State->Erasable[0][RegFB] = 076000 & State->Erasable[0][RegBB];
	      State->Erasable[0][RegEB] =
		03400 & (State->Erasable[0][RegBB] << 8);
	    }
	}
      else if (k >= 04000 && k <= 07777)
	State->Fixed[2][k - 04000] = Value;
      else if (k >= 01400 && k <= 01777)
	{
	  j = (7 & (State->Erasable[0][RegEB] >> 8));
	  goto Erasable;
	}
      else if (k >= 02000 && k <= 03777)
	{
	  j = (037 & (State->Erasable[0][RegFB] >> 10));
	  if (j >= 030 && 0 != (0100 & State->OutputChannel7))
	    j += 010;
	  goto Fixed;
	}
      else
	{
	  printf
	    ("Memory range not recognized.  Perhaps 0000-01377 or 04000-07777?\n");
	  return;
	}
    }
  else if (2 == sscanf (s, "EDIT C%o%o", &k, &Value))
    {
      if (k >= 0 && k <= 0777)
	CpuWriteIO (State, k, Value);
      else
	{
	  printf ("I/O channel range not recognized.  Perhaps 000-0777?\n");
	  return;
	}
    }
  else
    {
      printf
	("The format of this command is \"EDIT A V\", where A is an address\n");
      printf ("and V is a 15-bit octal value.  Examples of A are:\n");
      printf ("\t1234\t\tUnswitched erasable memory, 0-1377 octal.\n");
      printf ("\t4567\t\tCommon fixed memory, 4000-7777 octal.\n");
      printf
	("\tE4,1456\t\tSwitched erasable memory, banks E0-E7, offset 1400-1777 octal.\n");
      printf
	("\t12,2345\t\tFixed fixed memory, banks 0-43, offset 2000-3777 octal.\n");
      printf ("\tI123\t\tI/O channel 000-777 octal.\n");
    }
}

//-----------------------------------------------------------------------------------
// My substitute for fgets, for use when stdin is unblocked.

#define MAX_FROMFILES 11
static FILE *FromFiles[MAX_FROMFILES];
static int NumFromFiles = 1;
//static char OutBuf[16384];

void
rfgets (agc_t *State, char *Buffer, int MaxSize, FILE * fp)
{
  int c, Count = 0;
  char *s;
  //static int FirstTime = 1;
  MaxSize--;
  while (1)
    {
      // While waiting for character input, continue to look for client connects
      // and disconnects.
      while ((fp != stdin && EOF == (c = fgetc (fp))) ||
            (fp == stdin && Buffer != (s = nbfgets (Buffer, MaxSize))))
	{
	  // If we have redirected console input, and the file of source data is
	  // exhausted, then reattach the console.
	  if (NumFromFiles > 1 && fp == FromFiles[NumFromFiles - 1])
	    {
	      NumFromFiles--;
	      printf ("Keystroke source-file closed.\n");
	      if (NumFromFiles == 1)
		printf ("The keyboard has been reattached.\n> ");
	      fclose (fp);
	      fp = FromFiles[NumFromFiles - 1];
	    }
	  else
	    {
#ifdef WIN32	    
	      Sleep (10);
#else
	      struct timespec req, rem;
	      req.tv_sec = 0;
	      req.tv_nsec = 10000000;
	      nanosleep (&req, &rem);
#endif // WIN32
	    }  
	  //if (FirstTime)
	  //  {
	  //    FirstTime = 0;
	  //    printf ("Non-blocking getchar.\n");
	  //  }
	  ChannelRoutine (State);
	}
      if (fp == stdin && s != NULL)
        return;
      Buffer[Count] = c;
      if (c == '\n' || Count >= MaxSize)
        {
	  Buffer[Count] = 0;
	  return;
	}
      Count++;
    }
}

//-----------------------------------------------------------------------------------

int
main (int argc, char *argv[])
{
  FILE *LogFile = NULL;
  int LogCount = 0, LogLast = -1;
  char *RomImage = NULL, *CoreDump = NULL;
  int PatternValue, PatternMask;
  int i, j;
  struct tms DummyTime;
  clock_t NextCoreDump, DumpInterval;

  //int k, n;
  //int16_t *WordPtr;
  clock_t RealTime, RealTimeOffset, LastRealTime, NextKeycheck;
  unsigned long long CycleCount, DesiredCycles;

  //setvbuf (stdout, OutBuf, _IOLBF, sizeof (OutBuf));
  FromFiles[0] = stdin;
  NextKeycheck = times (&DummyTime);
  DumpInterval = 10 * sysconf (_SC_CLK_TCK);

  printf ("Apollo Guidance Computer simulation, ver. " NVER ", built "
	  __DATE__ " " __TIME__ "\n" "(c)2003-2005 Ronald S. Burkey.\n");
  printf ("Refer to http://www.ibiblio.org/apollo/index.html for additional information.\n");

  // Parse the command-line parameters.
  for (i = 1; i < argc; i++)
    {
      if (!strcmp (argv[i], "--help") || !strcmp (argv[i], "/?"))
	break;
      else if (!strncmp (argv[i], "--core=", 7))
	RomImage = &argv[i][7];
      else if (!strncmp (argv[i], "--resume=", 9))
	CoreDump = &argv[i][9];
      else if (1 == sscanf (argv[i], "--port=%d", &j))
        Portnum = j;
      else if (1 == sscanf (argv[i], "--dump-time=%d", &j))
        DumpInterval = j * sysconf (_SC_CLK_TCK);
      else if (!strcmp (argv[i], "--debug-dsky"))
	DebugDsky = 1;
      else if (!strcmp (argv[i], "--debug-deda"))
	DebugDeda = 1;
      else if (!strncmp (argv[i], "--cfg=", 6))
	{
	  if (ParseCfg (&State, &argv[i][6]))
	    {
	      RomImage = NULL;
	      printf
		("\n*** The switch %s refers to an unknown file. ***\n\n",
		 argv[i]);
	      break;
	    }
	}
      else if (!strcmp (argv[i], "--debug"))
	SingleStepCounter = 0;
      else if (1 == sscanf (argv[i], "--interlace=%d", &j))
        SocketInterlaceReload = j;
    }
  if (argc == 1 || i < argc || (RomImage == NULL && !DebugDsky))
    {
      printf ("USAGE:\n"
	      "\tyaAGC --core=filename [OPTIONS]\n\n"
	      "The core filename is a binary image of the program,\n"
	      "constants, and so forth, that are supposed to be in\n"
	      "the AGC\'s core-rope memory.  Such a file can be\n"
	      "created from AGC assembly language using the yaYUL\n"
	      "assembler (if and when I write it) or from from an ASCII\n"
	      "represention of the binary image using the Oct2Bin\n"
	      "program.\n\n"
	      "OPTIONS:.\n"
	      "--port=n      Change the server port number (default=19697).\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"
	      "--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"
	      "--debug       Halt before executing first instruction and enter\n"
	      "              Debugging mode.  The debugging mode is very primitive,\n"
	      "              but does allow you to set breakpoints in the AGC code,\n"
	      "              single-step AGC programs, examine or modify memory,\n"
	      "              and so forth.\n"
	      "--resume=file Use indicated file (previously created from the --debug\n"
	      "              mode\'s COREDUMP command) to initialize erasable memory,\n"
	      "              i/o channels, and hidden CPU state variables.  In\n"
	      "              effect, this allows you to take a snapshot of the AGC\n"
	      "              program and to resume its execution from that point,\n"
	      "              rather than always starting the AGC program with power-\n"
	      "              up defaults. The power-up default is to reload erasable\n"
	      "              memory with its contents at the prior power-down (from\n"
	      "              the file LM.core or CM.core), but not to restore the \n"
	      "              other state variables mentioned.  If you want a\n"
	      "              completely clean system, erase LM.core or CM.core.\n"
	      "              Alternately, if you want the state of the system to\n"
	      "              be identical to the state at last power-down, use \n"
	      "              --resume=LM.core or --resume=CM.core.\n"
	      "--interlace=N By default, yaAGC tries to read the socket interface\n"
	      "              by which it communicates with yaDSKY and other\n"
	      "              components between each %dth CPU instruction (N=%d).\n"
	      "              In theory, a value of N=1 provides the intention of \n"
	      "              obtaining maximum responsiveness, but on some slower\n"
	      "              computers, however, the overhead from these socket-\n"
	      "              interrogations actually slows the system down\n"
	      "              substantially.  The default value of N is intended\n"
	      "              to be a reasonable compromise that should work well\n"
	      "              on most computers.  With the --interlace option, you can\n"
	      "              fine-tune the system by setting the socket-checks\n"
	      "              to occur every N-th CPU instruction instead.\n"
	      "--dump-time=N By default, yaAGC saves the contents of erasable memory\n"
	      "              to disk (in the file LM.core or CM.core) every 10 seconds\n"
	      "              so that when yaAGC is next run it will (sort of)\n"
	      "              retained the contents of erasable memory.  You can change\n"
	      "              that interval to N seconds with this switch.\n"
	      "Note that the core-rope image should be exactly 36 banks\n"
	      "(36x1024=36864 words, or 73728 bytes) in size.  Other sizes\n"
	      "may be accepted, but it is unclear what (if any) utility such\n"
	      "core-rope images 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", SocketInterlaceReload, SocketInterlaceReload);
      return (1);
    }

  // Initialize the simulation.
  if (!DebugDsky)
    {
      if (CoreDump == NULL)
        {
	  if (CmOrLm)
            i = agc_engine_init (&State, RomImage, "CM.core", 0);
	  else
            i = agc_engine_init (&State, RomImage, "LM.core", 0);
	}
      else
        i = agc_engine_init (&State, RomImage, CoreDump, 1);
      switch (i)
	{
	case 0:
	  break;
	case 1:
	  printf ("Specified core-rope image file not found.\n");
	  break;
	case 2:
	  printf ("Core-rope image larger than core memory.\n");
	  break;
	case 3:
	  printf ("Core-rope image file must have even size.\n");
	  break;
	case 5:
	  printf ("Core-rope image file read error.\n");
	  break;
	default:
	  printf ("Initialization implementation error.\n");
	  break;
	}
      if (i != 0)
	return (1);
    }

  // Other stuff may need to go here, to initialize the DSKY simulation and
  // the hardware-backend simulation.  At present, though, I'm not sure what
  // that stuff may be.


  // Run the simulated CPU.  Expecting to ACCURATELY cycle the simulation every 
  // 11.7 microseconds within Linux (or Win32) is a bit too much, I think.
  // (Not that it's really critical, as long as it looks right from the
  // user's standpoint.)  So I do a trick:  I just execute the simulation
  // often enough to keep up with real-time on the average.  AGC time is
  // measured as the number of machine cycles divided by AGC_PER_SECOND, 
  // while real-time is measured using the times() function.  What this mains
  // is that AGC_PER_SECOND AGC cycles are executed every CLK_TCK clock ticks.  
  // The timing is thus rather rough-and-ready (i.e., I'm sure it can be improved 
  // a lot).  It's good enough for me, for NOW, but I'd be happy to take suggestions 
  // for how to improve it in a reasonably portable way.
  RealTimeOffset = times (&DummyTime);	// The starting time of the program.
  NextCoreDump = RealTimeOffset + DumpInterval;
  CycleCount = State.CycleCounter * sysconf (_SC_CLK_TCK);	// Number of AGC cycles so far.
  RealTimeOffset -= (CycleCount + AGC_PER_SECOND / 2) / AGC_PER_SECOND;
  LastRealTime = ~0UL;
  while (1)
    {
      int Break;
      RealTime = times (&DummyTime);
      if (RealTime != LastRealTime)
	{
	  // Make a routine core dump.
	  if (RealTime >= NextCoreDump)
	    {
	      if (CmOrLm)
	        MakeCoreDump (&State, "CM.core");
              else
	        MakeCoreDump (&State, "LM.core");
	      NextCoreDump = RealTime + DumpInterval;
	    }
	  // Need to recalculate the number of AGC cycles we're supposed to
	  // have executed.  Notice the trick of multiplying both CycleCount
	  // and DesiredCycles by CLK_TCK, to avoid a long long division by CLK_TCK.
	  // This not only reduces overhead, but actually makes the calculation
	  // more exact.  A bit tricky to understand at first glance, though.
	  LastRealTime = RealTime;
	  //DesiredCycles = ((RealTime - RealTimeOffset) * AGC_PER_SECOND) / CLK_TCK;
	  DesiredCycles = (RealTime - RealTimeOffset) * AGC_PER_SECOND;
	}
      else
        {
#ifdef WIN32
	  Sleep (10);	    
#else // WIN32
	  struct timespec req, rem;
	  req.tv_sec = 0;
	  req.tv_nsec = 10000000;
	  nanosleep (&req, &rem);
#endif // WIN32
	}
      while (CycleCount < DesiredCycles)
	{
	  int CurrentZ, CurrentBB;

	  // If we're in --debug-dsky mode, we don't want to do all of the 
	  // --debug stuff, since we're not actually executing AGC code.
	  if (DebugDsky)
	    {
	      CycleCount += sysconf (_SC_CLK_TCK);
	      agc_engine (&State);
	      continue;
	    }

	ShowDisassembly:
	  CurrentZ = State.Erasable[0][RegZ];
	  CurrentBB =
	    (State.Erasable[0][RegBB] & 076007) | (State.
						   InputChannel[7] & 0100);
	  if (State.PendFlag)
	    Break = 0;
	  else
	    {
	      if (SingleStepCounter == 0)
		{
		  Break = 1;
		  printf ("Stepped.\n");
		}
	      else
		{
		  if (SingleStepCounter > 0)
		    SingleStepCounter--;
		  for (Break = i = 0; i < NumBreakpoints; i++)
		    if (Breakpoints[i].WatchBreak == 2)
		      {
			// Pattern!
			int Value;
			Value = GetFromZ (&State);
			if (Breakpoints[i].Address12 ==
			    (Value & Breakpoints[i].vRegBB))
			  {
			    printf ("Hit pattern, Value=" PAT " Mask=" PAT
				    ".\n", Breakpoints[i].Address12,
				    Breakpoints[i].vRegBB);
			    Break = 1;
			    break;
			  }
		      }
		    else if (Breakpoints[i].WatchBreak == 0)
		      {
			int Address12, vRegBB;
			int CurrentFB, vCurrentFB;
			Address12 = Breakpoints[i].Address12;
			if (Address12 != CurrentZ)
			  continue;
			if (Address12 < 01400)
			  {
			    printf ("Hit breakpoint at %05o.\n", Address12);
			    Break = 1;
			    break;
			  }
			if (Address12 >= 04000)
			  {
			    printf ("Hit breakpoint at %05o.\n", Address12);
			    Break = 1;
			    break;
			  }
			vRegBB = Breakpoints[i].vRegBB;
			if (Address12 >= 01400 && Address12 < 02000 &&
			    (vRegBB & 7) == (CurrentBB & 7))
			  {
			    printf ("Hit breakpoint at E%o,%05o.\n",
				    CurrentBB & 7, Address12);
			    Break = 1;
			    break;
			  }
			CurrentFB = (CurrentBB >> 10) & 037;
			if (CurrentFB >= 030 && (CurrentBB & 0100))
			  CurrentFB += 010;
			vCurrentFB = (vRegBB >> 10) & 037;
			if (vCurrentFB >= 030 && (vRegBB & 0100))
			  vCurrentFB += 010;
			if (Address12 >= 02000 && Address12 < 04000 &&
			    CurrentFB == vCurrentFB)
			  {
			    int Bank;
			    Bank = (CurrentBB >> 10) & 037;
			    if (0 != (CurrentBB & 0100) && Bank >= 030)
			      Bank += 010;
			    printf ("Hit breakpoint at %02o,%05o.\n",
				    Bank, Address12);
			    Break = 1;
			    break;
			  }
		      }
		    else if (Breakpoints[i].WatchBreak == 1 &&
			     Breakpoints[i].WatchValue != GetWatch (&State,
								    &Breakpoints
								    [i]))
		      {
			int Address12, vRegBB, Before, After;
			Address12 = Breakpoints[i].Address12;
			Before = (Breakpoints[i].WatchValue & 077777);
			After = (GetWatch (&State, &Breakpoints[i]) & 077777);
			if (Address12 < 01400)
			  {
			    printf ("Hit watchpoint at %05o, %06o -> %06o.\n",
				    Address12, Before, After);
			    Breakpoints[i].WatchValue =
			      GetWatch (&State, &Breakpoints[i]);
			    Break = 1;
			    break;
			  }
			vRegBB = Breakpoints[i].vRegBB;
			if (Address12 >= 01400 && Address12 < 02000 &&
			    (vRegBB & 7) == (CurrentBB & 7))
			  {
			    printf
			      ("Hit watchpoint at E%o,%05o, %06o -> %06o.\n",
			       CurrentBB & 7, Address12, Before, After);
			    Breakpoints[i].WatchValue =
			      GetWatch (&State, &Breakpoints[i]);
			    Break = 1;
			    break;
			  }
		      }
		}
	    }
	  if (!Break)
	    {
	      if (RealTime >= NextKeycheck)
	        {
		  NextKeycheck = RealTime + KEYSTROKE_CHECK;
		  while (KeyboardBuffer == nbfgets (KeyboardBuffer, sizeof (KeyboardBuffer)))
		    {
		      //printf ("*** \"%s\" ***\n", KeyboardBuffer);
		      Break = 1;
		    }
		}
	    }
	  if (Break)
	    {
	      extern int DebuggerInterruptMasks[11];
	      char *ss, OverflowChar, OverflowCharQ;
	      SingleStepCounter = -1;
	      for (i = j = 0; i < NumServers; i++)
		if (Clients[i].Socket != -1)
		  j++;
	      if (j > 0)
		{
		  int i;
		  printf ("Client sockets (%d/%d):", j, NumServers);
		  for (i = 0; i < NumServers; i++)
		    if (Clients[i].Socket != -1)
		      printf (" %d", Clients[i].Socket);
		  printf ("\n");
		}
	      printf
		("ACC=%06o \tL=%06o \tQ=%06o \tEB=%06o(E%o)\t" LB "FB=%06o(%02o)\tZ=%06o \tBB=%06o\n",
		 0177777 & State.Erasable[0][RegA],
		 0177777 & State.Erasable[0][RegL],
		 0177777 & State.Erasable[0][RegQ],
		 0177777 & State.Erasable[0][RegEB],
		 07 & (State.Erasable[0][RegEB] >> 8),
		 0177777 & State.Erasable[0][RegFB],
		 037 & (State.Erasable[0][RegFB] >> 10),
		 0177777 & State.Erasable[0][RegZ],
		 0177777 & State.Erasable[0][RegBB]);
	      printf
		("ARUPT=%06o\tLRUPT=%06o\tQRUPT=%06o\tZRUPT=%06o\t" LB "BBRUPT=%06o\t"
		 "BRUPT=%05o\tCHAN07=%05o\n",
		 0177777 & State.Erasable[0][RegARUPT],
		 0177777 & State.Erasable[0][RegLRUPT],
		 0177777 & State.Erasable[0][RegQRUPT],
		 0177777 & State.Erasable[0][RegZRUPT],
		 0177777 & State.Erasable[0][RegBBRUPT],
		 0177777 & State.Erasable[0][RegBRUPT],
		 0177777 & State.InputChannel[7]);
	      //if (State.PendFlag)
	      //  ss = "\tWORKING";
	      //else
	      //ss = " ";
	      printf
		("CYR=%06o\tSR=%06o\tCYL=%06o\tEDOP=%06o",
		 0177777 & State.Erasable[0][RegCYR],
		 0177777 & State.Erasable[0][RegSR],
		 0177777 & State.Erasable[0][RegCYL],
		 0177777 & State.Erasable[0][RegEDOP]);
#ifndef WIN32
	      printf
		("\t\t\tRealtime=%lu/%lu",
		 RealTime - RealTimeOffset, sysconf (_SC_CLK_TCK));
#endif		 
	      printf ("\n");
	      if (0040000 == (0140000 & State.Erasable[0][RegA]))
		OverflowChar = '+';
	      else if (0100000 == (0140000 & State.Erasable[0][RegA]))
		OverflowChar = '-';
	      else
		OverflowChar = '0';
	      if (0040000 == (0140000 & State.Erasable[0][RegQ]))
		OverflowCharQ = '+';
	      else if (0100000 == (0140000 & State.Erasable[0][RegQ]))
		OverflowCharQ = '-';
	      else
		OverflowCharQ = '0';
	      printf
		("Index=%05o\tExtend=%d\tInterrupts=%d\tOverflowAQ=%c%c\t" LB 
		 "ISR=%d\t\tCycles=%llu/85333",
		 077777 & State.IndexValue,
		 State.ExtraCode,
		 State.AllowInterrupt, OverflowChar, OverflowCharQ, State.InIsr, State.CycleCounter);
#ifdef WIN32
              if (State.CycleCounter < 10)
	        printf ("\t");
              if (State.CycleCounter < 1000000000)
	        printf ("\t");
	      printf
		("\tRealtime=%lu/%d",
		 RealTime - RealTimeOffset, sysconf (_SC_CLK_TCK));
#endif
	      printf ("\n");
	      Disassemble (&State);
	      while (1)
		{
		  if (NumFromFiles == 1)
		    printf ("> ");
		  s[sizeof (s) - 1] = 0;
		  rfgets (&State, s, sizeof (s) - 1, FromFiles[NumFromFiles - 1]);
		  // Normalize the strings by getting rid of leading, trailing
		  // or duplicated spaces.
		  i = sscanf (s, "%s%s%s%s%s", s1, s2, s3, s4, s5);
		  if (i == 1)
		    strcpy (s, s1);
		  else if (i == 2)
		    sprintf (s, "%s %s", s1, s2);
		  else if (i == 3)
		    sprintf (s, "%s %s %s", s1, s2, s3);
		  else if (i == 4)
		    sprintf (s, "%s %s %s %s", s1, s2, s3, s4);
		  else if (i == 5)
		    sprintf (s, "%s %s %s %s %s", s1, s2, s3, s4, s5);
		  else 
		    s[0] = 0;
		  //printf ("Command is \"%s\"\n", s);  
		  if (s[0] == '#')
		    continue;
		  if (s[0] == 0)
		    continue;
		  strcpy (sraw, s);
		  RealTimeOffset +=
		    ((RealTime = times (&DummyTime)) - LastRealTime);
		  LastRealTime = RealTime;
		  for (ss = s; *ss; *ss = toupper (*ss), ss++);
		    //if (*ss == '\n')
		    //  *ss = 0;
		  //for (ss = sraw; *ss; ss++)
		  //  if (*ss == '\n')
		  //    *ss = 0;
		  if (!strcmp (s, "HELP"))
		    {
		      printf ("\n"
			      "Unless otherwise specified, all numbers are in octal notation.\n"
			      "Addresses are a single octal number (like 130 or 4655) when\n"
			      "referring to unswitched-erasable or fixed-fixed memory, are\n"
			      "octal pairs (like 23,2345) when referring to common-fixed memory,\n"
			      "or are octal pairs prefixed with E (like E5,1456) when referring\n"
			      "to switched-erasable memory.  Common-fixed memory banks in super-\n"
			      "bank 1 are referred to as banks 40, 41, 42, and 43 (octal).  When\n"
			      "referring to i/o channels rather than to memory, we prefix the \n"
			      "channel number by \'C\' (like C7).\n"
			      "\n"
			      "For information on specific topics, use one of the following:\n"
			      "\thelp backtrace\n"
			      "\thelp backtraces\n"
			      "\thelp break\n"
			      "\thelp breakpoints\n"
			      "\thelp cont\n"
			      "\thelp coredump\n"
			      "\thelp delete\n"
			      "\thelp dump\n"
			      "\thelp edit\n"
			      "\thelp fromfile\n"
			      "\thelp getoct\n"
			      "\thelp interrupts\n"
			      "\thelp intoff\n"
			      "\thelp inton\n"
			      "\thelp log\n"
			      "\thelp maskoff\n"
			      "\thelp maskon\n"
			      "\thelp pattern\n"
			      "\thelp quit\n"
			      "\thelp step\n"
			      "\thelp watch\n" "\n");
		      continue;
		    }
		  else if (!strcmp (s, "HELP BACKTRACE"))
		    {
		      printf ("\n"
		              "backtrace N\n"
			      "\tJump to a given backtrace point, as identified by the\n"
			      "\tindex numbers shown in the \'backtraces\' command.\n"
			      "\tIndex 0 is the most recent backtrace point, index 1\n"
			      "\tthe next-most-recent, and so on.  This restores all\n"
			      "\terasable memory and i/o channels.  However, any\n"
			      "\tperipherals (such as a DSKY) will not necessarily\n"
			      "\treturn to their previous states.\n" "\n");
		      continue;
		    }
		  else if (!strcmp (s, "HELP BACKTRACES"))
		    {
		      printf ("\n"
			      "backtraces\n"
			      "\tDisplays the most recent backtrace points.\n" "\n");
		      continue;
		    }
		  else if (!strcmp (s, "HELP BREAK"))
		    {
		      printf ("\n"
		      	      "break A\n"
			      "\tSet a breakpoint at address A.  If an address requiring\n"
			      "\ta bank number is used, but the bank number is omitted,\n"
			      "\tthe bank number is taken from the EB or FB register.\n" "\n");
		      continue;
		    }
		  else if (!strcmp (s, "HELP BREAKPOINTS"))
		    {
		      printf ("\n"
			      "breakpoints\n"
			      "\tList the defined breakpoints, watchpoints, and patterns.\n" "\n");
		      continue;
		    }
		  else if (!strcmp (s, "HELP CONT"))
		    {
		      printf ("\n"
			      "cont\n"
			      "\tContinue execution.  The program will continue executing\n"
			      "\tuntil a breakpoint is reached or, in Linux or (for versions\n"
			      "\t20040810 or later) Win32, until you hit the carriage-return\n"
			      "\tkey.\n" "\n");
		      continue;
		    }
		  else if (!strcmp (s, "HELP COREDUMP"))
		    {
		      printf ("\n"
			      "coredump F\n"
			      "\tWrite a core-dump file to disk, using the filename F.\n"
			      "\tThe core-dump file can later be reloaded by running\n"
			      "\twith the --resume switch, causing the AGC program\n"
			      "\tto continue from exactly this point rather than from\n"
			      "\tthe beginning.\n" "\n");
		      continue;
		    }
		  else if (!strcmp (s, "HELP DELETE"))
		    {
		      printf ("\n"
		              "delete [A]\n"
			      "\tDelete the breakpoint or watchpoint at address A.  If \n"
			      "\tA is omitted, all breakpoints and watchpoints are deleted.\n"
			      "\tIf an address requiring a bank number is used, but the bank\n"
			      "\tnumber is omitted,the bank number is taken from the EB or FB\n"
			      "\tregister.\n"
			      "\n"
			      "delete V M\n"
			      "\tDelete the pattern with value V and mask M.\n" "\n");
		      continue;
		    }
		  else if (!strcmp (s, "HELP DUMP"))
		    {
		      printf ("\n"
			      "dump [N] A\n"
			      "\tDump values of N memory or i/o channel locations, from\n"
			      "\taddress A.  If N is omitted, it defaults to 1.  If an address\n"
			      "\trequiring a bank number is used, but the bank number is\n"
			      "\tomitted, the bank number is taken from the EB or FB register.\n"
			      "\n"
			      "dump\n"
			      "\tSimply repeat the last DUMP performed.\n" "\n");
		      continue;
		    }
		  else if (!strcmp (s, "HELP EDIT"))
		    {
		      printf ("\n"
		              "edit A V\n"
			      "\tChange value of memory location or i/o channel A to V.\n"
			      "\tIf an address requiring a bank number is used, but the \n"
			      "\tbank number is omitted, the bank number is taken from the\n"
			      "\tEB or FB register.\n" "\n");
		      continue;
		    }
		  else if (!strcmp (s, "HELP FROMFILE"))
		    {
		      printf ("\n"
			      "fromfile F\n"
			      "\tStart taking debugger commands from a file with filename F\n"
			      "\trather than from the keyboard.  When the file is exhausted,\n"
			      "\tkeyboard control will be resumed.  The use I envisage for\n"
			      "\tthis is to set up a bunch of breakpoints or other initialization.\n"
			      "\tFROMFILE commands can be nested up to 10 levels.\n" "\n");
		      continue;
		    }
		  else if (!strcmp (s, "HELP GETOCT"))
		    {
		      printf ("\n"
		              "The GETOCT command is used in two different ways, depending\n"
			      "on the nature of the expression it operates on.\n"
			      "\n"
			      "getoct Expression\n"
			      "\tConverts a numeric expression into the octal format\n"
			      "\tused by the AGC processor.  The expression types are\n"
			      "\tlimited to the following:\n"
			      "\t\tHexOrDecimal * HexOrDecimal\n"
			      "\t\tHexOrDecimal / HexOrDecimal\n"
			      "\tIn these expressions, spaces are significant.  By\n"
			      "\t\"HexOrDecimal\" is meant a hexadecimal number with\n"
			      "\tleading \"0x\", such as 0xF000, or else a decimal\n"
			      "\tnumber, which may including + or - signs, decimal\n"
			      "\tpoint, and exponential, such as -1.5E-3 or 16.\n"
			      "\n"
		              "getoct Expression\n"
			      "\tConverts a pair of octal numbers representing a single\n"
			      "\tnumeric value in the native format used by the AGC\n"
			      "\tprocessor to a more recognisable format.  The expression\n"
			      "\ttypes are limited to the following:\n"
			      "\t\tOctal Octal\n"
			      "\t\tOctal Octal * En * Bn\n"
			      "\tIn these expressions, spaces are significant.\n"
			      "\tBy \"Octal\" is meant any octal number, of 5 digits\n"
			      "\tor less, with or without a leading 0.  By \"En\" is \n"
			      "\tmeant something like E5 or E-3.  By \"Bn\" is meant\n"
			      "\tsomething like B5 or B-3. In both cases, the \"n\" part\n"
			      "\tof En or Bn is a decimal integer.  Notice that although\n"
			      "\ta large number of decimal places may be printed, the \n"
			      "\toriginal octal number only has (at most!) 28 significant\n"
			      "\tbits, and thus there are actually at most 9 significant\n"
			      "\tdecimal digits.\n" "\n");
		      continue;
		    }
		  else if (!strcmp (s, "HELP INTERRUPTS"))
		    {
		      printf ("\n"
			      "interrupts\n"
			      "\tDisplay the active interrupt-service requests.\n" "\n");
		      continue;
		    }
		  else if (!strcmp (s, "HELP INTOFF"))
		    {
		      printf ("\n"
			      "intoff N\n"
			      "\tClear interrupt-request N (1, 2, ..., 10).\n" "\n");
		      continue;
		    }
		  else if (!strcmp (s, "HELP INTON"))
		    {
		      printf ("\n"
			      "inton N\n"
			      "\tSet interrupt-request N (1, 2, ..., 10).\n" "\n");
		      continue;
		    }
		  else if (!strcmp (s, "HELP LOG"))
		    {
		      printf ("\n"
			      "log N\n"
			      "\tLog next N instructions to the file yaAGC.log.  The file\n"
			      "\tformat is very simple, and is only intended to be used\n"
			      "\tfor regression testing.\n" "\n");
		      continue;
		    }
		  else if (!strcmp (s, "HELP MASKOFF"))
		    {
		      printf ("\n"
			      "maskoff N\n"
			      "\tReset a mask within the debugger to re-allow interrupt\n"
			      "\tN (1, ..., 10).  If N=0, all interrupts are unmasked.\n" 
			      "\tIn other words, this command undoes what the MASKON\n"
			      "\tcommand does.\n" "\n");
		      continue;
		    }
		  else if (!strcmp (s, "HELP MASKON"))
		    {
		      printf ("\n"
			      "maskon N\n"
			      "\tSet a mask within the debugger to disallow interrupt\n"
			      "\tN (1, ..., 10).  If N=0, all interrupts are masked.\n" "\n");
		      continue;
		    }
		  else if (!strcmp (s, "HELP PATTERN"))
		    {
		      printf ("\n"
		              "pattern V M\n"
			      "\tSet a pattern with value V and mask M.  A \"pattern\"\n"
			      "\tis like a breakpoint, except that instead of halting\n"
			      "\tupon reaching a certain address, the program-halt occurs\n"
			      "\tupon reaching a certain value (V) at the program counter.\n"
			      "\tThe instruction stored at the program counter is logically\n"
			      "\tbitwise ANDed with the mask M before comparing it to V.\n"
			      "\tThis can be used (for example) to select a given instruction\n"
			      "\ttype.  V and M are both in octal.  Note that V and M are\n"
			      "\teach 32 bits, and the extra bits represent additional \n"
			      "\tconditions beyond the bare instruction code stored at the\n"
			      "\tprogram counter.  These bits are:\n"
			      "\t\t16th\tExtracode bit\n"
			      "\t\t17th\tThe INDEX is non-zero\n"
			      "\t\t18th\tAccumulator has + overflow\n"
			      "\t\t19th\tAccumulator has - overflow\n"
			      "\t\t20th\tSign of Accumulator is -\n"
			      "\t\t21st\tSigns of Accumulator and L mismatch\n"
			      "\t\t22nd\tWithin an interrupt\n"
			      "\t\tother\tZero\n" "\n");
		      continue;
		    }
		  else if (!strcmp (s, "HELP QUIT"))
		    {
		      printf ("\n"
		              "quit (or exit)\n"
			      "\tEnd the program.\n" "\n");
		      continue;
		    }
		  else if (!strcmp (s, "HELP STEP"))
		    { 
		      printf ("\n"
			      "step [N] (or next [N])\n"
			      "\tStep through N instructions.  If omitted, N defaults to 1.\n"
			      "\tYou can also use just the first letter, as shorthand.\n" "\n");
		      continue;
		    }
		  else if (!strcmp (s, "HELP WATCH"))
		    {
		      printf ("\n"
			      "watch A\n"
			      "\tHalt execution when a value is written to the address A.\n"
			      "\tThe break occurs AFTER the value is changed, but the\n"
			      "\t\"before\" and \"after\" values stored at the address\n"
			      "\tare displayed after execution stops.  The address\n"
			      "\tobviously has to be in erasable memory or i/o-channel memory.\n"
			      "\tNote that the value stored at the address has to CHANGE to\n"
			      "\ttrigger the break.\n" "\n");
		      continue;
		    }
		  else if (1 == sscanf (s, "LOG%d", &i))
		    {
		      if (LogFile != NULL)
			printf ("Logging is already in progress.\n");
		      else
			{
			  LogFile = fopen ("yaAGC.log", "w");
			  if (LogFile == NULL)
			    printf ("The log file cannot be created.\n");
			  else if (i < 1)
			    {
			      printf ("Log count out of range.\n");
			      fclose (LogFile);
			      LogFile = NULL;
			    }
			  else
			    LogCount = i;
			}
		    }
		  else if (!strncmp (s, "FROMFILE ", 9))
		    {
		      if (NumFromFiles < MAX_FROMFILES)
			{
			  FromFiles[NumFromFiles] = fopen (&sraw[9], "r");
			  if (FromFiles[NumFromFiles] == NULL)
			    printf ("Cannot open keystroke file \"%s\".\n",
				    &sraw[9]);
			  else
			    {
			      NumFromFiles++;
			      printf ("Now taking keystrokes from \"%s\".\n",
				      &sraw[9]);
			    }
			}
		      else
			printf
			  ("Too many nested FROMFILE commands, discarding \"%s\".\n",
			   &sraw[9]);
		    }
		  else if (1 == sscanf (s, "STEP%o", &i)
			   || 1 == sscanf (s, "NEXT%o", &i))
		    {
		      if (i >= 1)
		        SingleStepCounter = i - 1;
		      else
		        printf ("The step-count must be 1 or greater.\n");
		      break;
		    }
		  else if (!strcmp (s, "STEP") || !strcmp (s, "NEXT") ||
			   !strcmp (s, "S") || !strcmp (s, "N"))
		    {
		      SingleStepCounter = 0;
		      break;
		    }
		  else if (!strcmp (s, "QUIT") || !strcmp (s, "EXIT"))
		    return (0);
		  else if (!strncmp (s, "GETOCT", 6))
		    {
		      // The following function is taken from CheckDec.c in the
		      // Luminary131 directory.
		      extern void CheckDec (char *s);
		      CheckDec (&s[6]);
		    }
		  else if (!strncmp (s, "DUMP", 4))
		    DumpMemory (&State, s);
		  else if (!strncmp (s, "BREAK ", 6))
		    AddBreakpoint (&State, s);
		  else if (!strncmp (s, "WATCH ", 6))
		    AddWatchpoint (&State, s);
		  else if (2 == sscanf (s, "DELETE%o%o", &i, &j))
		    {
		      PatternValue = (i & PATTERN_SIZE);
		      PatternMask = (j & PATTERN_SIZE);
		      for (i = 0; i < NumBreakpoints; i++)
			if (Breakpoints[i].WatchBreak == 2 &&
			    Breakpoints[i].Address12 == PatternValue &&
			    Breakpoints[i].vRegBB == PatternMask)
			  {
			    printf ("Pattern " PAT "," PAT
				    " has been deleted.\n", PatternValue,
				    PatternMask);
			    NumBreakpoints--;
			    for (; i < NumBreakpoints; i++)
			      Breakpoints[i] = Breakpoints[i + 1];
			    i = -1;
			    break;
			  }
		      if (i != -1)
			printf ("Pattern not found.\n");
		    }
		  else if (!strncmp (s, "DELETE", 6))
		    DeleteBreakpoint (&State, s);
		  else if (2 == sscanf (s, "PATTERN%o%o", &i, &j))
		    {
		      PatternValue = (i & PATTERN_SIZE);
		      PatternMask = (j & PATTERN_SIZE);
		      // First, see if the pattern has already been defined.
		      for (i = 0; i < NumBreakpoints; i++)
			if (Breakpoints[i].WatchBreak == 2 &&
			    Breakpoints[i].Address12 == PatternValue &&
			    Breakpoints[i].vRegBB == PatternMask)
			  {
			    printf
			      ("This pattern has already been defined.\n");
			    break;
			  }
		      if (i == NumBreakpoints)
			{
			  if (NumBreakpoints >= MAX_BREAKPOINTS)
			    printf
			      ("The maximum number of breakpoints/watchpoints/"
			       "patterns has already been reached.\n");
			  else
			    {
			      printf ("Setting pattern " PAT "," PAT ".\n",
				      PatternValue, PatternMask);
			      Breakpoints[NumBreakpoints].WatchBreak = 2;
			      Breakpoints[NumBreakpoints].Address12 =
				PatternValue;
			      Breakpoints[NumBreakpoints].vRegBB =
				PatternMask;
			      NumBreakpoints++;
			    }
			}
		    }
		  else if (!strncmp (s, "EDIT ", 5))
		    {
		      State.PendFlag = SingleStepCounter = 0;
		      Break = 1;
		      EditMemory (&State, s);
		      goto ShowDisassembly;
		    }
		  else if (!strcmp (s, "CONT"))
		    break;
		  else if (!strncmp (s, "COREDUMP ", 9))
		    MakeCoreDump (&State, &sraw[9]);
		  else if (!strcmp (s, "INTERRUPTS"))
		    {
		      if (!DebuggerInterruptMasks[0])
			printf ("The debugger is blocking all interrupts.\n");
		      else
			printf ("The debugger is allowing interrupts.\n");
		      printf ("Debugger interrupt mask:");
		      for (i = 1; i <= NUM_INTERRUPT_TYPES; i++)
			printf (" %d", !DebuggerInterruptMasks[i]);
		      printf ("\n");
		      printf ("Interrupt-request flags:");
		      for (i = 1; i <= NUM_INTERRUPT_TYPES; i++)
			printf (" %d", State.InterruptRequests[i]);
		      printf ("\n");
		      if (State.InIsr)
			printf ("In ISR #%d.\n", State.InterruptRequests[0]);
		      else
			printf ("Last ISR: #%d.\n",
				State.InterruptRequests[0]);
		    }
		  else if (1 == sscanf (s, "INTON%d", &i))
		    {
		      if (i < 1 || i > NUM_INTERRUPT_TYPES)
			printf ("Only interrupt types 1 to %d are used.\n",
				NUM_INTERRUPT_TYPES);
		      else if (State.InterruptRequests[i])
			printf ("Interrupt %d already requested.\n", i);
		      else
			{
			  printf ("Interrupt %d request-flag set.\n", i);
			  State.InterruptRequests[i] = 1;
			}
		    }
		  else if (1 == sscanf (s, "INTOFF%d", &i))
		    {
		      if (i < 1 || i > NUM_INTERRUPT_TYPES)
			printf ("Only interrupt types 1 to %d are used.\n",
				NUM_INTERRUPT_TYPES);
		      else if (!State.InterruptRequests[i])
			printf ("Interrupt %d not requested.\n", i);
		      else
			{
			  printf ("Interrupt %d request-flag cleared.\n", i);
			  State.InterruptRequests[i] = 0;
			}
		    }
		  else if (1 == sscanf (s, "MASKON%d", &i))
		    {
		      if (i < 0 || i > NUM_INTERRUPT_TYPES)
			printf ("Only interrupt types 0 to %d are used.\n",
				NUM_INTERRUPT_TYPES);
		      else
			DebuggerInterruptMasks[i] = 0;
		    }
		  else if (1 == sscanf (s, "MASKOFF%d", &i))
		    {
		      if (i < 0 || i > NUM_INTERRUPT_TYPES)
			printf ("Only interrupt types 0 to %d are used.\n",
				NUM_INTERRUPT_TYPES);
		      else
			DebuggerInterruptMasks[i] = 1;
		    }
		  else if (!strcmp (s, "BACKTRACES"))
		    BacktraceDisplay (&State);
		  else if (1 == sscanf (s, "BACKTRACE%d", &i))
		    {
		      int j;
		      if (0 != (j = BacktraceRestore (&State, i)))
			printf ("Error %d restoring backtrace point #%d.\n",
				j, i);
		      else
			{
			  printf ("Backtrace point #%d restored.\n", i);
			  for (j = 0; j < NumBreakpoints; j++)
			    if (Breakpoints[j].WatchBreak == 1)
			      Breakpoints[j].WatchValue =
				GetWatch (&State, &Breakpoints[j]);
			}
		      State.PendFlag = SingleStepCounter = 0;
		      Break = 1;
		      CycleCount = sysconf (_SC_CLK_TCK) * State.CycleCounter;
		      goto ShowDisassembly;
		    }
		  else if (!strcmp (s, "BREAKPOINTS"))
		    {
		      if (NumBreakpoints == 0)
			printf ("No breakpoint(s) are currently set.\n");
		      for (i = 0; i < NumBreakpoints; i++)
			{
			  int Address12, vRegBB;
			  if (Breakpoints[i].WatchBreak == 2)
			    {
			      printf ("Pattern #%d is value " PAT
				      " masked with " PAT, i + 1,
				      Breakpoints[i].Address12,
				      Breakpoints[i].vRegBB);
			    }
			  else
			    {
			      if (Breakpoints[i].WatchBreak)
				printf ("Watchpoint #%d is at address ",
					i + 1);
			      else
				printf ("Breakpoint #%d is at address ",
					i + 1);
			      Address12 = Breakpoints[i].Address12;
			      vRegBB = Breakpoints[i].vRegBB;
			      if (Address12 < 01400 || Address12 >= 04000)
				printf ("%05o", Address12);
			      else if (Address12 >= 01400
				       && Address12 < 02000)
				printf ("E%o,%05o", vRegBB & 7, Address12);
			      else if (Address12 >= 02000
				       && Address12 < 04000)
				{
				  int Bank;
				  Bank = (vRegBB >> 10) & 037;
				  if (0 != (vRegBB & 0100) && Bank >= 030)
				    Bank += 010;
				  printf ("%02o,%05o", Bank, Address12);
				}
			    }
			  printf ("\n");
			}
		    }
		  else
		    printf ("Illegal command.\n");
		}
	    }
	  if (LogFile != NULL)
	    {
	      int Bank, Address, NewLast;
	      Bank =
		077777 &
		(((State.Erasable[0][RegBB]) | State.OutputChannel7));
	      Address = 077777 & (State.Erasable[0][RegZ]);
	      NewLast = (Bank << 15) | Address;
	      if (NewLast != LogLast)
		{
		  LogLast = NewLast;
		  fprintf (LogFile, "%05o %05o\n", Bank, Address);
		  LogCount--;
		  if (LogCount <= 0)
		    {
		      fclose (LogFile);
		      LogFile = NULL;
		      printf ("Logging completed.\n");
		    }
		}
	    }
	  //CycleCount++;   
	  agc_engine (&State);
	  CycleCount += sysconf (_SC_CLK_TCK);
	}
    }

  return (0);
}

#if 0
//----------------------------------------------------------------------------------------------
// The following is a nice little template that can be used for easily building functions that
// parse a debugger command line of the form "COMMAND ADDRESS".
static void
AddCOMMAND (agc_t * State, char *s)
{
  int j, k;
  if (2 == sscanf (s, "COMMAND E%o,%o", &j, &k))
    {
      if (j < 0 || j > 7)
	{
	  printf ("Illegal erasable bank number (%o).  Must be 0-7.\n", j);
	  return;
	}
      if (k < 01400 || k >= 02000)
	{
	  printf ("Illegal offset (%o).  Must be 1400-1777.\n", k);
	  return;
	}

    }
  else if (2 == sscanf (s, "COMMAND %o,%o", &j, &k))
    {
      if (j < 0 || j > 043)
	{
	  printf ("Illegal common-fixed bank number (%o).  Must be 0-43.\n",
		  j);
	  return;
	}
      if (k < 02000 || k >= 04000)
	{
	  printf ("Illegal offset (%o).  Must be 2000-3777.\n", k);
	  return;
	}

    }
  else if (1 == sscanf (s, "COMMAND %o", &k))
    {
      if (k >= 0 && k <= 01377)
	{

	}
      else if (k >= 04000 && k <= 07777)
	{

	}
      else
	{
	  printf
	    ("Memory range not recognized.  Perhaps 0000-01377 or 04000-07777?\n");
	  return;
	}
    }
  else
    {
      printf
	("The format of this command is \"COMMAND A\", where A is an address.  Examples of A are:\n");
      printf ("\t1234\t\tUnswitched erasable memory, 0-1377 octal.\n");
      printf ("\t4567\t\tCommon fixed memory, 4000-7777 octal.\n");
      printf
	("\tE4,1456\t\tSwitched erasable memory, banks E0-E7, offset 1400-1777 octal.\n");
      printf
	("\t12,2345\t\tFixed fixed memory, banks 0-43, offset 2000-3777 octal.\n");
    }
}
#endif // 0
