Raw File
AGCmain.cpp
/****************************************************************************
 * AGC4 (Apollo Guidance Computer) BLOCK I Simulator
 *
 * AUTHOR: John Pultorak
 * DATE: 07/29/02
 * FILE: AGCmain.cpp
 *
 * VERSIONS:
 * 1.0 - initial version.
 * 1.1 - fixed minor bugs; passed automated test and checkout programs:
 * teco1.asm, teco2.asm, and teco3.asm to test basic instructions,
 * extended instructions, and editing registers.
 * 1.2 - decomposed architecture into subsystems; fixed minor bug in DSKY
 * keyboard logic (not tested in current teco*.asm suite).
 * Implemented scaler pulses F17, F13, F10. Tied scaler output to
 * involuntary counters and interrupts. Implemented counter overflow
 * logic and tied it to interrupts and other counters. Added simple
 * set/clear breakpoint. Fixed a bug in bank addressing.
 * 1.3 - fixed bugs in the DSKY. Added 14-bit effective address (CADR) to the
 * simulator display output. Inhibited interrupts when the operator
 * single-steps the AGC.
 * 1.4 - performance enhancements. Recoded the control pulse execution code
 * for better simulator performance. Also changed the main loop so it
 * polls the keyboard and system clock less often for better performance.
 * 1.5 - reversed the addresses of TIME1 and TIME2 so TIME2 occurs first.
 * This is the way its done in Block II so that a common routine (READLO)
 * can be used to read the double word for AGC time.
 * 1.6 - added indicators for 'CHECK FAIL' and 'KEY RELS'. Mapped them to OUT1,
 * bits 5 and 7. Added a function to display the current location in
 * the source code list file using the current CADR.
 * 1.7 - increased length of 'examine' function display. Any changes in DSKY now
 * force the simulator to update the display immediately. Added a 'watch'
 * function that looks for changes in a memory location and halts the
 * AGC. Added the 'UPTL', 'COMP', and "PROG ALM" lights to the DSKY.
 * 1.8 - started reorganizing the simulator in preparation for H/W logic design.
 * Eliminated slow (1Hz) clock capability. Removed BUS REQUEST feature.
 * Eliminated SWRST switch.
 * 1.9 - eliminated the inclusive 'OR' of the output for all registers onto the
 * R/W bus. The real AGC OR'ed all register output onto the bus; normally
 * only one register was enabled at a time, but for some functions several
 * were simultaneously enabled to take advantage of the 'OR' function (i.e.:
 * for the MASK instruction). The updated logic will use tristate outputs
 * to the bus except for the few places where the 'OR' function is actually
 * needed. Moved the parity bit out of the G register into a 1-bit G15
 * register. This was done for convenience because the parity bit in G
 * is set independently from the rest of the register.
 * 1.10 - moved the G15 parity register from MBF to the PAR subsystem. Merged SBFWG
 * and SBEWG pulses into a single SBWG pulse. Deleted the CLG pulse for MBF
 * (not needed). Separated the ALU read pulses from all others so they can
 * be executed last to implement the ALU inclusive OR functions. Implemented
 * separate read and write busses, linked through the ALU. Implemented test
 * parity (TP) signal in PAR; added parity alarm (PALM) FF to latch PARITY
 * ALARM indicator in PAR.
 * 1.11 - consolidated address testing signals and moved them to ADR. Moved memory
 * read/write functions from MBF to MEM. Merged EMM and FMM subsystems into
 * MEM. Fixed a bad logic bug in writeMemory() that was causing the load of
 * the fixed memory to overwrite array boundaries and clobber the CPM table.
 * Added a memory bus (MEM_DATA_BUS, MEM_PARITY_BUS).
 * 1.12 - reduced the number of involuntary counters (CTR) from 20 to 8. Eliminated
 * the SHINC subsequence. Changed the (CTR) sequence and priority registers into
 * a single synchronization register clocked by WPCTR. Eliminated the fifth
 * interrupt (UPRUPT; INT). Eliminated (OUT) the signal to read from output
 * register 0 (the DSKY register), since it was not used and did not provide
 * any useful function, anyway. Deleted register OUT0 (OUT) which shadowed
 * the addressed DSKY register and did not provide any useful function.
 * Eliminated the unused logic that sets the parity bit in OUT2 for downlink
 * telemetry.
 * 1.13 - reorganized the CPM control pulses into CPM-A, CPM-B, and CPM-C groups.
 * Added the SDV1, SMP1, and SRSM3 control pulses to CPM-A to indicate when
 * those subsequences are active; these signals are input to CPM-C. Moved the
 * ISD function into CPM-A. Fixed a minor bug causing subsequence RSM3 to be
 * displayed as RSM0. Added GENRST to clear most registers during STBY.
 * 1.14 - Moved CLISQ to TP1 to fix a problem in the hardware AGC. CLISQ was clearing
 * SNI on CLK2 at TP12, but the TPG was advancing on CLK1 which occurs after
 * CLK2, so the TPG state machine was not seeing SNI and was not moving to
 * the correct state. In this software simulation, everything advances on
 * the same pulse, so it wasn't a problem to clear SNI on TP12. Added a
 * switch to enable/disable the scaler.
 * 1.15 - Reenabled interrupts during stepping (by removing MON::RUN) signals from
 * CPM-A and CPM-C logic). Interrupts can be prevented by disabling the scaler.
 * Fixed a problem with INHINT1; it is supposed to prevent an interrupt
 * between instructions if there's an overflow. It was supposed to be cleared
 * on TP12 after SNI (after a new instruction), but was being cleared on TP12
 * after every subsequence.
 * 1.16 - Changed CPM-A to load and use EPROM tables for the control pulse matrix. The
 * EPROM tables are negative logic (0=asserted), but this simulator expects
 * positive logic, so each word is bit-flipped when the EPROM tables load
 * during simulator initialization.
 * SOURCES:
 * Mostly based on information from "Logical Description for the Apollo Guidance
 * Computer (AGC4)", Albert Hopkins, Ramon Alonso, and Hugh Blair-Smith, R-393,
 * MIT Instrumentation Laboratory, 1963.
 *
 * PORTABILITY:
 * Compiled with Microsoft Visual C++ 6.0 standard edition. Should be fairly
 * portable, except for some Microsoft-specific I/O and timer calls in this file.
 *
 * NOTE: set tabs to 4 spaces to keep columns formatted correctly.
 *
 *****************************************************************************
 */
#include <conio.h>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#include <ctype.h>
#include "reg.h"
#include "TPG.h"
#include "MON.h"
#include "SCL.h"
#include "SEQ.h"
#include "INP.h"
#include "OUT.h"
#include "BUS.h"
#include "DSP.h"
#include "ADR.h"
#include "PAR.h"
#include "MBF.h"
#include "MEM.h"
#include "CTR.h"
#include "INT.h"
#include "KBD.h"
#include "CRG.h"
#include "ALU.h"
#include "CPM.h"
#include "ISD.h"
#include "CLK.h"
extern bool dskyChanged;
//-----------------------------------------------------------------------
// CONTROL LOGIC
void
genAGCStates()
{
  // 1) Decode the current instruction subsequence (glbl_subseq).
  // SEQ::glbl_subseq = CPM::instructionSubsequenceDecoder();
  // 2) Build a list of control pulses for this state.
  CPM::controlPulseMatrix();
  // 3) Execute the control pulses for this state. In the real AGC, these occur
  // simultaneously. Since we can't achieve that here, we break it down into the
  // following steps:
  // Most operations involve data transfers--usually reading data from
  // a register onto a bus and then writing that data into another register. To
  // approximate this, we first iterate through all registers to perform
  // the 'read' operation--this transfers data from register to bus.
  // Then we again iterate through the registers to do 'write' operations,
  // which move data from the bus back into the register.
  BUS::glbl_READ_BUS = 0; // clear bus; necessary because words are logical
  // OR'ed onto the bus.
  MEM::MEM_DATA_BUS = 0; // clear data lines: memory bits 15-1
  MEM::MEM_PARITY_BUS = 0; // parity line: memory bit 16
  // Now start executing the pulses:
  // First, read register outputs onto the bus or anywhere else.
  int i;
  for (i = 0; i < MAXPULSES && SEQ::glbl_cp[i] != NO_PULSE; i++)
    {
      CLK::doexecR(SEQ::glbl_cp[i]);
    }
  // Next, execute ALU read pulses. See comments in ALU .C file
  ALU::glbl_BUS = 0;
  for (i = 0; i < MAXPULSES && SEQ::glbl_cp[i] != NO_PULSE; i++)
    {
      CLK::doexecR_ALU(SEQ::glbl_cp[i]);
    }
  BUS::glbl_WRITE_BUS = BUS::glbl_READ_BUS; // in case nothing is logically OR'ed below;
  for (i = 0; i < MAXPULSES && SEQ::glbl_cp[i] != NO_PULSE; i++)
    {
      CLK::doexecR_ALU_OR(SEQ::glbl_cp[i]);
    }
  // Now, write the bus and any other signals into the register inputs.
  for (i = 0; i < MAXPULSES && SEQ::glbl_cp[i] != NO_PULSE; i++)
    {
      CLK::doexecW(SEQ::glbl_cp[i]);
    }
  // Always execute these pulses.
  SCL::doexecWP_SCL();
  SCL::doexecWP_F17();
  SCL::doexecWP_F13();
  SCL::doexecWP_F10();
  TPG::doexecWP_TPG();
}
//-----------------------------------------------------------------------
// SIMULATION LOGIC
// contains prefix for source filename; i.e.: the portion
// of the filename before .obj or .lst
char filename[80];
char*
getCommand(char* prompt)
{
  static char s[80];
  char* sp = s;
  cout << prompt;
  cout.flush();
  char key;
  while ((key = _getch()) != 13)
    {
      if (isprint(key))
        {
          cout << key;
          cout.flush();
          *sp = key;
          sp++;
        }
      else if (key == 8 && sp != s)
        {
          cout << key << " " << key;
          cout.flush();
          sp--;
        }
    }
  *sp = '\0';
  return s;
}
bool breakpointEnab = false;
unsigned breakpoint = 0;
void
toggleBreakpoint()
{
  if (!breakpointEnab)
    {
      char b[80];
      strcpy(b, getCommand("Set breakpoint: -- enter 14-bit CADR (octal): "));
      cout << endl;
      breakpoint = strtol(b, 0, 8);
      breakpointEnab = true;
    }
  else
    {
      cout << "Clearing breakpoint." << endl;
      breakpointEnab = false;
    }
}
bool watchEnab = false;
unsigned watchAddr = 0;
unsigned oldWatchValue = 0;
void
toggleWatch()
{
  if (!watchEnab)
    {
      char b[80];
      strcpy(b, getCommand("Set watch: -- enter 14-bit CADR (octal): "));
      cout << endl;
      watchAddr = strtol(b, 0, 8);
      watchEnab = true;
      oldWatchValue = MEM::readMemory(watchAddr);
      char buf[100];
      sprintf(buf, "%06o: %06o", watchAddr, oldWatchValue);
      cout << buf << endl;
    }
  else
    {
      cout << "Clearing watch." << endl;
      watchEnab = false;
    }
}
void
incrCntr()
{
  char cntrname[80];
  strcpy(cntrname, getCommand("Increment counter: -- enter pcell (0-19): "));
  cout << endl;
  int pc = atoi(cntrname);
  CTR::pcUp[pc] = 1;
}
void
decrCntr()
{
  char cntrname[80];
  strcpy(cntrname, getCommand("Decrement counter: -- enter pcell (0-19): "));
  cout << endl;
  int pc = atoi(cntrname);
  CTR::pcDn[pc] = 1;
}
void
interrupt()
{
  char iname[80];
  strcpy(iname, getCommand("Interrupt: -- enter priority (1-5): "));
  cout << endl;
  int i = atoi(iname) - 1;
  INT::rupt[i] = 1;
}
#ifdef NOTDEF
// Load AGC memory from the specified file object file
void loadMemory()
  {
    strcpy(filename, getCommand("Load Memory -- enter filename: "));
    cout << endl;
    // Add the .obj extension.
    char fname[80];
    strcpy(fname, filename);
    strcat(fname, ".obj");
    FILE* fp = fopen(fname, "r");
    if(!fp)
      {
        perror("fopen failed:");
        cout << "*** ERROR: Can't load memory for file: " << fname << endl;
        return;
      }
    unsigned addr;
    unsigned data;
    while(fscanf(fp, "%o %o", &addr, &data) != EOF)
      {
        MEM::writeMemory(addr, data);
      }
    fclose(fp);
    cout << "Memory loaded." << endl;
  }
#endif
static int loadBuf[0xffff + 1]; // tempory buffer for assembling H,L memory data
void
loadEPROM(char* fileName, bool highBytes)
{
  cout << "Reading EPROM: " << fileName << endl;
  // Open the EPROM file.
  FILE* ifp = fopen(fileName, "r");
  if (!ifp)
    {
      perror("fopen failed for source file");
      exit(-1);
    }
  const int addressBytes = 3; // 24-bit address range
  const int sumCheckBytes = 1;
  char buf[4096]; // buffer holds a single S-Record
  while (fgets(buf, 4096, ifp))
    {
      // process a record
      if (buf[0] != 'S')
        {
          cout << "Error reading start of EPROM record for: " << fileName
              << endl;
          exit(-1);
        }
      char tmp[256];
      strncpy(tmp, &buf[2], 2);
      tmp[2] = '\0';
      int totalByteCount = strtol(tmp, 0, 16);
      int mySumCheck = totalByteCount & 0xff;
      strncpy(tmp, &buf[4], 6);
      tmp[addressBytes * 2] = '\0';
      int address = strtol(tmp, 0, 16);
      mySumCheck = (mySumCheck + ((address & 0xff0000) >> 16)) % 256;
      mySumCheck = (mySumCheck + ((address & 0x00ff00) >> 8)) % 256;
      mySumCheck = (mySumCheck + ((address & 0x0000ff))) % 256;
      //cout << hex << totalByteCount << ", " << address << dec << endl;
      int dataBytes = totalByteCount - addressBytes - sumCheckBytes;
      int i = (addressBytes + 2) * 2; // index to 1st databyte char.
      for (int j = 0; j < dataBytes; j++)
        {
          // get a data byte
          strncpy(tmp, &buf[i], 2);
          tmp[2] = '\0';
          int data = strtol(tmp, 0, 16);
          //cout << hex << data << dec << endl;
          mySumCheck = (mySumCheck + data) % 256;
          if (highBytes)
            {
              loadBuf[address] = loadBuf[address] | ((data << 8) & 0xff00);
            }
          else
            {
              loadBuf[address] = loadBuf[address] | (data & 0xff);
            }
          address++;
          i += 2; // bump to next databyte char
        }
      strncpy(tmp, &buf[i], 2);
      tmp[2] = '\0';
      int sumCheck = strtol(tmp, 0, 16);
      if (sumCheck != ((~mySumCheck) & 0xff))
        {
          cout << "sumCheck failed; file: " << fileName << ", address: " << hex
              << address << ", sumCheck: " << sumCheck << ", mySumCheck: "
              << mySumCheck << dec << endl;
          exit(-1);
        }
    }
  fclose(ifp);
  cout << "Memory loaded." << endl;
}
// Load AGC memory from the specified EPROM files
void
loadMemory()
{
  strcpy(filename, getCommand("Load Memory -- enter filename: "));
  cout << endl;
  char fname[80];
  // Add the _H.hex extension.
  strcpy(fname, filename);
  strcat(fname, "_H.hex");
  loadEPROM(fname, true);
  // Add the _L.hex extension.
  strcpy(fname, filename);
  strcat(fname, "_L.hex");
  loadEPROM(fname, false);
  //*******************************************************************
  // EPROM is now in loadBuf; move it to AGC memory.
  // AGC fixed memory only uses NUMFBANK banks.
  for (int address = 1024; address < 1024 * (NUMFBANK + 1); address++)
    {
      // Don't load address region 0-1023; that region is allocated
      // to eraseable memory.
      //cout << "loading CADR=" << hex << address << endl;
      MEM::writeMemory(address, loadBuf[address]);
    }
//*******************************************************************
}
// Write the entire contents of fixed and
// eraseable memory to the specified file.
// Does not write the registers
void
saveMemory(char* filename)
{
  FILE* fp = fopen(filename, "w");
  if (!fp)
    {
      perror("*** ERROR: fopen failed:");
      exit(-1);
    }
  char buf[100];
  for (unsigned addr = 020; addr <= 031777; addr++)
    {
      sprintf(buf, "%06o %06o\n", addr, MEM::readMemory(addr));
      fputs(buf, fp);
    }
  fclose(fp);
}
void
examineMemory()
{
  char theAddress[20];
  strcpy(theAddress, getCommand("Examine Memory -- enter address (octal): "));
  cout << endl;
  unsigned address = strtol(theAddress, 0, 8);
  char buf[100];
  for (unsigned i = address; i < address + 23; i++)
    {
      sprintf(buf, "%06o: %06o", i, MEM::readMemory(i));
      cout << buf << endl;
    }
}
// Returns true if time (s) elapsed since last time it returned true; does not block
// search for "Time Management"
bool
checkElapsedTime(time_t s)
{
  if (!s)
    return true;
  static clock_t start = clock();
  clock_t finish = clock();
  double duration = (double) (finish - start) / CLOCKS_PER_SEC;
  if (duration >= s)
    {
      start = finish;
      return true;
    }
  return false;
}
// Blocks until time (s) has elapsed.
void
delay(time_t s)
{
  if (!s)
    return;
  clock_t start = clock();
  clock_t finish = 0;
  double duration = 0;
  do
    {
      finish = clock();
    }
  while ((duration = (double) (finish - start) / CLOCKS_PER_SEC) < s);
}
void
updateAGCDisplay()
{
  static bool displayTimeout = false;
  static int clockCounter = 0;
  if (checkElapsedTime(2))
    displayTimeout = true;
  if (MON::FCLK)
    {
      if (MON::RUN)
        {
          // update every 2 seconds at the start of a new instruction
          if (displayTimeout || dskyChanged)
            {
              clockCounter++;
              if ((TPG::register_SG.read() == TP12
                  && SEQ::register_SNI.read() == 1)
                  || (TPG::register_SG.read() == STBY) || clockCounter > 500
                  || dskyChanged)
                {
                  MON::displayAGC();
                  displayTimeout = false;
                  clockCounter = 0;
                  dskyChanged = false;
                }
            }
        }
      else
        {
          static bool displayOnce = false;
          if (TPG::register_SG.read() == WAIT)
            {
              if (displayOnce == false)
                {
                  MON::displayAGC();
                  displayOnce = true;
                  clockCounter = 0;
                }
            }
          else
            {
              displayOnce = false;
            }
        }
    }
  else
    MON::displayAGC(); // When the clock is manual or slow, always update.
}
void
showMenu()
{
  cout << "AGC4 EMULATOR MENU:" << endl;
  cout << " 'r' = RUN: toggle RUN/HALT switch upward to the RUN position."
      << endl;
}
const int startCol = 0; // columns are numbered 0-n
const int colLen = 5; // number of chars in column
const int maxLines = 23; // # of total lines to display
const int noffset = 10; // # of lines prior to, and including, selected line
const int maxLineLen = 79;
void
showSourceCode()
{
// Add the .lst extension.
  char fname[80];
  strcpy(fname, filename);
  strcat(fname, ".lst");
// Open the file containing the source code listing.
  FILE* fp = fopen(fname, "r");
  if (!fp)
    {
      perror("fopen failed:");
      cout << "*** ERROR: Can't load source list file: " << fname << endl;
      return;
    }
  cout << endl;
// Get the address of the source code line to display.
// The address we want is the current effective address is the
// S and bank registers.
  char CADR[colLen + 1];
  sprintf(CADR, "%05o", ADR::getEffectiveAddress());
  int op = 0; // offset index
  long foffset[noffset];
  for (int i = 0; i < noffset; i++)
    foffset[i] = 0;
  bool foundit = false;
  int lineCount = 0;
  char s[256];
  char valString[20];
  char out[256];
  while (!feof(fp))
    {
      if (!foundit)
        {
          foffset[op] = ftell(fp);
          op = (op + 1) % noffset;
        }
// Read a line of the source code list file.
      if (fgets(s, 256, fp))
        {
// Get the address (CADR) from the line.
          strncpy(valString, s + startCol, colLen);
          valString[colLen] = '\0';
// 'foundit' is true after we have found the desired line.
          if (foundit)
            {
              if (strcmp(valString, CADR) == 0)
                cout << ">";
              else
                cout << " ";
// truncate line so it fits in 80 col display
              strncpy(out, s, maxLineLen);
              out[maxLineLen] = '\0';
              cout << out;
              lineCount++;
              if (lineCount >= maxLines)
                break;
            }
          else
            {
              if (strcmp(valString, CADR) == 0)
                {
// Reposition the file pointer back several lines so
// we can see the code that preceeds the desired
// line, too.
                  foundit = true;
                  fseek(fp, foffset[op], 0);
                }
            }
        }
    }
  fclose(fp);
}
void
main(int argc, char* argv[])
{
  CPM::readEPROM("CPM1_8.hex", CPM::EPROM1_8);
  CPM::readEPROM("CPM9_16.hex", CPM::EPROM9_16);
  CPM::readEPROM("CPM17_24.hex", CPM::EPROM17_24);
  CPM::readEPROM("CPM25_32.hex", CPM::EPROM25_32);
  CPM::readEPROM("CPM33_40.hex", CPM::EPROM33_40);
  CPM::readEPROM("CPM41_48.hex", CPM::EPROM41_48);
  CPM::readEPROM("CPM49_56.hex", CPM::EPROM49_56);
  bool singleClock = false;
  genAGCStates();
  MON::displayAGC();
  while (1)
    {
      // NOTE: assumes that the display is always pointing to the start of
      // a new line at the top of this loop!
      // Clock the AGC, but between clocks, poll the keyboard
      // for front-panel input by the user. This uses a Microsoft function;
      // substitute some other non-blocking function to access the keyboard
      // if you're porting this to a different platform.
      cout << "> ";
      cout.flush(); // display prompt
      while (!_kbhit())
        {
          if (MON::FCLK || singleClock)
            {
              // This is a performance enhancement. If the AGC is running,
              // don't check the keyboard or simulator display every
              // simulation cycle, because that slows the simulator
              // down too much.
              int genStateCntr = 100;
              do
                {
                  CLK::clkAGC();
                  singleClock = false;
                  genAGCStates();
                  genStateCntr--;
                  // Needs more work. It doesn't always stop at the
                  // right location and sometimes stops at the
                  // instruction afterwards, too.
                  if (breakpointEnab
                      && breakpoint == ADR::getEffectiveAddress())
                    {
                      MON::RUN = 0;
                    }
                  // Halt right after instr that changes a watched
                  // memory location.
                  if (watchEnab)
                    {
                      unsigned newWatchValue = MEM::readMemory(watchAddr);
                      if (newWatchValue != oldWatchValue)
                        {
                          MON::RUN = 0;
                        }
                      oldWatchValue = newWatchValue;
                    }
                }
              while (MON::FCLK && MON::RUN && genStateCntr > 0);
              updateAGCDisplay();
            }
          // for convenience, clear the single step switch on TP1; in the
          // hardware AGC, this happens when the switch is released
          if (MON::STEP && TPG::register_SG.read() == TP1)
            MON::STEP = 0;
        }
      char key = _getch();
      // Keyboard controls for front-panel:
      switch (key)
        {
      // AGC controls
      // simulator controls
      case 'q':
        cout << "QUIT..." << endl;
        exit(0);
      case 'm':
        showMenu();
        break;
      case 'd':
        genAGCStates();
        MON::displayAGC();
        break; // update display
      case 'l':
        loadMemory();
        break;
      case 'e':
        examineMemory();
        break;
      case 'f':
        showSourceCode();
        break;
      case ']':
        incrCntr();
        //genAGCStates();
        //displayAGC(EVERY_CYCLE);
        break;
      case '[':
        decrCntr();
        //genAGCStates();
        //displayAGC(EVERY_CYCLE);
        break;
      case 'i':
        interrupt();
        //genAGCStates();
        //displayAGC(EVERY_CYCLE);
        break;
      case 'z':
        //SCL::F17 = (SCL::F17 + 1) % 2;
        genAGCStates();
        MON::displayAGC();
        break;
      case 'x':
        //SCL::F13 = (SCL::F13 + 1) % 2;
        genAGCStates();
        MON::displayAGC();
        break;
      case 'c':
        MON::SCL_ENAB = (MON::SCL_ENAB + 1) % 2;
        genAGCStates();
        MON::displayAGC();
        break;
      case 'r':
        MON::RUN = (MON::RUN + 1) % 2;
        genAGCStates();
        if (!MON::FCLK)
          MON::displayAGC();
        break;
      case 's':
        MON::STEP = (MON::STEP + 1) % 2;
        genAGCStates();
        if (!MON::FCLK)
          MON::displayAGC();
        break;
      case 'a':
        MON::SA = (MON::SA + 1) % 2;
        genAGCStates();
        MON::displayAGC();
        break;
      case 'n':
        MON::INST = (MON::INST + 1) % 2;
        genAGCStates();
        MON::displayAGC();
        break;
      case 'p':
        MON::PURST = (MON::PURST + 1) % 2;
        genAGCStates();
        MON::displayAGC();
        break;
      case 'b':
        toggleBreakpoint();
        break;
      case 'y':
        toggleWatch();
        break;
      case ';':
        // Clear ALARM indicators
        PAR::CLR_PALM(); // Asynchronously clear PARITY FAIL
        MON::displayAGC();
        break;
      // DSKY:
      case '0':
        KBD::keypress(KEYIN_0);
        break;
      case '1':
        KBD::keypress(KEYIN_1);
        break;
      case '2':
        KBD::keypress(KEYIN_2);
        break;
      case '3':
        KBD::keypress(KEYIN_3);
        break;
      case '4':
        KBD::keypress(KEYIN_4);
        break;
      case '5':
        KBD::keypress(KEYIN_5);
        break;
      case '6':
        KBD::keypress(KEYIN_6);
        break;
      case '7':
        KBD::keypress(KEYIN_7);
        break;
      case '8':
        KBD::keypress(KEYIN_8);
        break;
      case '9':
        KBD::keypress(KEYIN_9);
        break;
      case '+':
        KBD::keypress(KEYIN_PLUS);
        break;
      case '-':
        KBD::keypress(KEYIN_MINUS);
        break;
      case '.':
        KBD::keypress(KEYIN_CLEAR);
        break;
      case '/':
        KBD::keypress(KEYIN_VERB);
        break;
      case '*':
        KBD::keypress(KEYIN_NOUN);
        break;
      case 'g':
        KBD::keypress(KEYIN_KEY_RELEASE);
        break;
      case 'h':
        KBD::keypress(KEYIN_ERROR_RESET);
        break;
      case 'j':
        KBD::keypress(KEYIN_ENTER);
        break;
      case '\0': // must be a function key
        key = _getch();
        switch (key)
          {
        case 0x3b: // F1: single clock pulse (when system clock off)
          singleClock = true;
          break;
        case 0x3c: // F2: manual clock (FCLK=0)
          MON::FCLK = 0;
          genAGCStates();
          MON::displayAGC();
          break;
        case 0x3e: // F4: fast clock (FCLK=1)
          MON::FCLK = 1;
          genAGCStates();
          MON::displayAGC();
          break;
        default:
          cout << "function key: " << key << "=" << hex << (int) key << dec
              << endl;
          }
        break;
      //default: cout << "??" << endl;
      default:
        cout << key << "=" << hex << (int) key << dec << endl;
        }
    }
}
back to top