Raw File
CPM.cpp
/****************************************************************************
 * CPM - CONTROL PULSE MATRIX subsystem
 *
 * AUTHOR: John Pultorak
 * DATE: 9/22/01
 * FILE: CPM.cpp
 *
 * NOTES: see header file.
 *
 *****************************************************************************
 */
#include "CPM.h"
#include "SEQ.h"
#include "MON.h"
#include "CTR.h"
#include "INT.h"
#include "ADR.h"
#include <stdlib.h>
#ifdef USE_NCURSES
#include <ncurses.h>
#else
#include <stdio.h>
#define printw printf
#define endwin()
#endif
const char* CPM::subseqString[] =
  { "TC0", "CCS0", "CCS1", "NDX0", "NDX1", "RSM3", "XCH0", "CS0", "TS0", "AD0",
      "MASK0", "MP0", "MP1", "MP3", "DV0", "DV1", "SU0", "RUPT1", "RUPT3",
      "STD2", "PINC0", "MINC0", "SHINC0", "NO_SEQ" };
subseq
CPM::instructionSubsequenceDecoder(int counter_subseq, int SQ_field,
    int STB_field)
{
  // Combinational logic decodes instruction and the stage count
  // to get the instruction subsequence.
  static subseq decode[16][4] =
    {
      { TC0, RUPT1, STD2, RUPT3 }, // 00
          { CCS0, CCS1, NO_SEQ, NO_SEQ }, // 01
          { NDX0, NDX1, NO_SEQ, RSM3 }, // 02
          { XCH0, NO_SEQ, STD2, NO_SEQ }, // 03
          { NO_SEQ, NO_SEQ, NO_SEQ, NO_SEQ }, // 04
          { NO_SEQ, NO_SEQ, NO_SEQ, NO_SEQ }, // 05
          { NO_SEQ, NO_SEQ, NO_SEQ, NO_SEQ }, // 06
          { NO_SEQ, NO_SEQ, NO_SEQ, NO_SEQ }, // 07
          { NO_SEQ, NO_SEQ, NO_SEQ, NO_SEQ }, // 10
          { MP0, MP1, NO_SEQ, MP3 }, // 11
          { DV0, DV1, STD2, NO_SEQ }, // 12
          { SU0, NO_SEQ, STD2, NO_SEQ }, // 13
          { CS0, NO_SEQ, STD2, NO_SEQ }, // 14
          { TS0, NO_SEQ, STD2, NO_SEQ }, // 15
          { AD0, NO_SEQ, STD2, NO_SEQ }, // 16
          { MASK0, NO_SEQ, STD2, NO_SEQ } // 17
    };
  if (counter_subseq == PINCSEL)
    return PINC0;
  else if (counter_subseq == MINCSEL)
    return MINC0;
  else
    return decode[SQ_field][STB_field];
}
void
CPM::clearControlPulses()
{
  for (unsigned i = 0; i < MAXPULSES; i++)
    SEQ::glbl_cp[i] = NO_PULSE;
}
void
CPM::assertj(cpType* pulse)
{
  int j = 0;
  for (unsigned i = 0; i < MAXPULSES && j < MAX_IPULSES && pulse[j] != NO_PULSE;
      i++)
    {
      if (SEQ::glbl_cp[i] == NO_PULSE)
        {
          SEQ::glbl_cp[i] = pulse[j];
          j++;
        }
    }
}
void
CPM::assertj(cpType pulse)
{
  for (unsigned i = 0; i < MAXPULSES; i++)
    {
      if (SEQ::glbl_cp[i] == NO_PULSE)
        {
          SEQ::glbl_cp[i] = pulse;
          break;
        }
    }
}
int CPM::EPROM1_8[];
int CPM::EPROM9_16[];
int CPM::EPROM17_24[];
int CPM::EPROM25_32[];
int CPM::EPROM33_40[];
int CPM::EPROM41_48[];
int CPM::EPROM49_56[];
void
CPM::readEPROM(const char* fileName, int* eprom)
{
  printw("Reading EPROM: %s\n", fileName);

  // Open the EPROM file.
  FILE* ifp = fopen(fileName, "r");
  if (!ifp)
    {
      perror("fopen failed for source file");
      endwin();
      exit(-1);
    }
  const int addressBytes = 3; // 24-bit address range
  const int sumCheckBytes = 1;
  char buf[4096];
  while (fgets(buf, 4096, ifp))
    {
      // process a record
      if (buf[0] != 'S')
        {
          printw("Error reading start of EPROM record for: %s\n", fileName);
          endwin();
          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;
      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);
          mySumCheck = (mySumCheck + data) % 256;
          // The H/W AGC needs negative logic in the EPROMS (0=asserted)
          // but this simulator needs positive logic, so we bit flip the word.
          //eprom[address] = data;
          eprom[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))
        {
          printw(
              "sumCheck failed; file: %s, address: %0X, sumCheck: %0o, mySumCheck: %0o\n",
              fileName, address, sumCheck, mySumCheck);
          exit(-1);
        }
    }
  fclose(ifp);
}
void
CPM::checkEPROM(int inval, int lowbit)
{
  for (int mask = 0x1; inval && mask != 0x100; mask = mask << 1)
    {
      if (inval & mask)
        assertj((cpType) lowbit);
      lowbit++;
    }
}
// perform the CPM-A EPROM function using the EPROM files
void
CPM::getControlPulses_EPROM(int address)
{
  checkEPROM(EPROM1_8[address], 1);
  checkEPROM(EPROM9_16[address], 9);
  checkEPROM(EPROM17_24[address], 17);
  checkEPROM(EPROM25_32[address], 25);
  checkEPROM(EPROM33_40[address], 33);
  checkEPROM(EPROM41_48[address], 41);
  checkEPROM(EPROM49_56[address], 49);
}
void
CPM::get_CPM_A(int address)
{
  // Use the EPROM tables to get the CPM-A control pulses documented
  // in R-393.
  getControlPulses_EPROM(address);
  // Now add some additional control pulses implied, but not documented
  // in R-393.
  if (SEQ::register_LOOPCTR.read() == 6)
    {
      assertj(ST2); // STA <- 2
      assertj(CLCTR); // CTR <- 0
    }
  //*****************************************************************
  // Now that the EPROM tables are used for CPM-A, this function is only
  // used to display the instruction subsequence in MON.
  SEQ::glbl_subseq = CPM::instructionSubsequenceDecoder(CTR::getSubseq(),
      SEQ::register_SQ.read(), SEQ::register_STB.read());
  //*****************************************************************
  // These were in CPM-C, where the rest of the control signal assertions
  // related to their use still are, but were moved here because WB and RB
  // are part of the R-393 sequence tables. Check CPM-C to see how these
  // assertions fit in (the former use is commented out there).
  switch (TPG::register_SG.read())
    {
  case PWRON:
    assertj(WB); // TC GOPROG copied to B (see CPM-C for related assertions)
    break;
  case TP12:
    if (SEQ::register_SNI.read() == 1)
      {
        if (!INT::IRQ())
          {
            // Normal instruction
            assertj(RB); // SQ <- B (see CPM-C for related assertions)
          }
      }
    break;
  default:
    ;
    }
}
void
CPM::controlPulseMatrix()
{
  // Combination logic decodes time pulse, subsequence, branch register, and
  // "select next instruction" latch to get control pulses associated with
  // those states.
  // Get rid of any old control pulses.
  clearControlPulses();
  //*******************************************************************************
  //*******************************************************************************
  // SUBSYSTEM A
  int SB2_field = 0;
  int SB1_field = 0;
  switch (CTR::getSubseq())
    {
  case PINCSEL:
    SB2_field = 0;
    SB1_field = 1;
    break;
  case MINCSEL:
    SB2_field = 1;
    SB1_field = 0;
    break;
  default:
    SB2_field = 0;
    SB1_field = 0;
    };
  int CPM_A_address = 0;
  CPM_A_address = (SB2_field << 13) | (SB1_field << 12)
      | (SEQ::register_SQ.read() << 8) | (SEQ::register_STB.read() << 6)
      | (TPG::register_SG.read() << 2) | (SEQ::register_BR1.read() << 1)
      | SEQ::register_BR2.read();
  //printw("CPM_A_address = %05o\n", CPM_A_address);
  // Construct address into CPM-A control pulse ROM:
  // Address bits (bit 1 is LSB)
  // 1: register BR2
  // 2: register BR1
  // 3-6: register SG (4)
  // 7,8: register STB (2)
  // 9-12: register SQ (4)
  // 13: STB_01 (from CTR: selects PINC, MINC, or none)
  // 14: STB_02 (from CTR: selects PINC, MINC, or none)
  get_CPM_A(CPM_A_address);
  //*******************************************************************************
  // SUBSYSTEM B
  // NOTE: WG, RSC, WSC are generated by SUBSYSTEM A. Those 3 signals are only used
  // by SUBSYSTEM B; not anywhere else.
  // CONSIDER MOVING TO ADR **********************8
  if (SEQ::isAsserted(WG))
    {
      switch (ADR::register_S.read())
        {
      case 020:
        assertj(W20);
        break;
      case 021:
        assertj(W21);
        break;
      case 022:
        assertj(W22);
        break;
      case 023:
        assertj(W23);
        break;
      default:
        if (ADR::GTR_17())
          assertj(WGn); // not a central register
        }
    }
  if (SEQ::isAsserted(RSC))
    {
      switch (ADR::register_S.read())
        {
      case 00:
        assertj(RA0);
        break;
      case 01:
        assertj(RA1);
        break;
      case 02:
        assertj(RA2);
        break;
      case 03:
        assertj(RA3);
        break;
      case 04:
        assertj(RA4);
        break;
      case 05:
        assertj(RA5);
        break;
      case 06:
        assertj(RA6);
        break;
      case 07:
        assertj(RA7);
        break;
      case 010:
        assertj(RA10);
        break;
      case 011:
        assertj(RA11);
        break;
      case 012:
        assertj(RA12);
        break;
      case 013:
        assertj(RA13);
        break;
      case 014:
        assertj(RA14);
        break;
      case 015:
        assertj(RBK);
        break;
      default:
        break; // 016, 017
        }
    }
  if (SEQ::isAsserted(WSC))
    switch (ADR::register_S.read())
      {
    case 00:
      assertj(WA0);
      break;
    case 01:
      assertj(WA1);
      break;
    case 02:
      assertj(WA2);
      break;
    case 03:
      assertj(WA3);
      break;
    case 010:
      assertj(WA10);
      break;
    case 011:
      assertj(WA11);
      break;
    case 012:
      assertj(WA12);
      break;
    case 013:
      assertj(WA13);
      break;
    case 014:
      assertj(WA14);
      break;
    case 015:
      assertj(WBK);
      break;
    default:
      break; // 016, 017
      }
  //*******************************************************************************
  //*******************************************************************************
  // SUBSYSTEM C
  switch (TPG::register_SG.read())
    {
  case STBY:
    assertj(GENRST);
    // inhibit all alarms
    // init "SQ" complex
    // clear branch registers
    // stage registers are not cleared; should they be?
    // zeroes are already gated onto bus when no read pulses are asserted.
    // to zero synchronous-clocked registers, assert write pulses here.
    // Level-triggered registers are zeroed by GENRST anded with CLK2.
    break;
  case PWRON:
    assertj(R2000);
//assertj(WB); // TC GOPROG copied to B (implemented in CPM-A)
    break;
  case TP1:
    // Moved this from TP12 to TP1 because CLISQ was getting cleared in the
    // hardware AGC before TPG was clocked; therefore TPG was not seeing the
    // SNI indication.
    assertj(CLISQ); // SNI <- 0
  case TP5:
    // EMEM must be available in G register by TP6
    if (ADR::GTR_17() && // not a central register
        !ADR::GTR_1777() && // not fixed memory
        !SEQ::isAsserted(SDV1) && // not a loop counter subseq
        !SEQ::isAsserted(SMP1))
      {
        assertj(SBWG);
      }
    if (ADR::EQU_17())
      assertj(INH); // INHINT (INDEX 017)
    if (ADR::EQU_16())
      assertj(CLINH); // RELINT (INDEX 016)
    break;
  case TP6:
    // FMEM must be available in G register by TP7
    if (ADR::GTR_1777() && // not eraseable memory
        !SEQ::isAsserted(SDV1) && // not a loop counter subseq
        !SEQ::isAsserted(SMP1))
      {
        assertj(SBWG);
      }
    break;
  case TP11:
    // G register written to memory beginning at TP11; Memory updates are in
    // G by TP10 for all normal and extracode instructions, but the PINC, MINC,
    // and SHINC sequences write to G in TP10 because they need to update the
    // parity bit.
    if (ADR::GTR_17() && // not a central register
        !ADR::GTR_1777() && // not fixed memory
        !SEQ::isAsserted(SDV1) && // not a loop counter subseq
        !SEQ::isAsserted(SMP1))
      {
        assertj(WE);
      }
    // Additional interrupts are inhibited during servicing of an interrupt;
    // Remove the inhibition when RESUME is executed (INDEX 025)
    if (SEQ::isAsserted(SRSM3))
      assertj(CLRP);
    break;
  case TP12:
    // DISABLE INPUT CHANGE TO PRIORITY COUNTER (reenable after TP1)
    // Check the priority counters; service any waiting inputs on the next
    // memory cycle.
    assertj(WPCTR);
    if (SEQ::register_SNI.read() == 1) // if SNI is set, get next instruction
      {
        if (INT::IRQ()) // if interrupt requested (see CPM-A for similar assertion)
          {
            // Interrupt: SQ <- 0 (the default RW bus state)
            assertj(RPT); // latch interrupt vector
            assertj(SETSTB); // STB <- 1
          }
        else
          {
            // Normal instruction
            //assertj(RB); // SQ <- B (implemented in CPM-A)
            assertj(CLSTB); // STB <- 0
          }
        assertj(WSQ);
        assertj(CLSTA); // STA <- 0
        // Remove inhibition of interrupts (if they were) AFTER the next instruction
        assertj(CLINH1); // INHINT1 <- 0
      }
    else if (CTR::getSubseq() == NOPSEL) // if previous sequence was not a counter
      {
        // get next sequence for same instruction.
        assertj(WSTB); // STB <- STA
        assertj(CLSTA); // STA <- 0
      }
    //assertj(CLISQ); // SNI <- 0 (moved to TP1)
    break;
  default:
    ;
    }
//*******************************************************************************
}
back to top