Raw File
runOneInstruction.c
/*
 * Copyright 2019,2020 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:    runOneInstruction.c
 * Purpose:     Emulates one instruction for yaLVDC.c, using the global state
 * 		structure. Also provides various related utility functions that
 * 		I think might be useful for an LVDC debugger.
 * Compiler:    GNU gcc.
 * Reference:   http://www.ibibio.org/apollo
 * Mods:        2019-09-20 RSB  Began.
 *              2020-06-05 RSB  Fixes for various bugs discovered in running
 *                              self-test procedures from the PAST program:
 *                              1)  SHL instructions were producing results
 *                                  >26 bits in ACC, causing subsequent TMI
 *                                  or TNZ instructions to fail.
 *                              2)  A side effect of PRS sourced from memory
 *                                  is supposed to be that the data goes into
 *                                  ACC.
 *                              3)  Assigned "spare" CIO 175 as an input port
 *                                  rather than an output port.
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "yaLVDC.h"

// Different fields of an LVDC/PTC data word.
#define signMask        0200000000
#define dataWordMask    0377777777

//#define DEBUG_A_LOT

//////////////////////////////////////////////////////////////////////////////

// Parse the fields of a HOP constant.  Return 0 on success, 1 on failure.
// The input hopConstant is in the form it resides in data memory ... i.e.,
// left-aligned at bit 27,  though the constant itself is 26 bits.  Returns
// 0 on success, 1 on failure, the latter of which can occur if the input
// hopConstant isn't formatted correctly.
int
parseHopConstant(int hopConstant, hopStructure_t *hopStructure)
{
  int retVal = 1;

  if ((!ptc && (hopConstant & 0100000) != 0)
      || (ptc && (hopConstant & 03300003) != 0))
    {
      //pushErrorMessage("Corrupted HOP constant", NULL);
      goto done;
    }
  hopStructure->im = ((hopConstant >> 25) & 1) | ((hopConstant << 1) & 6);
  hopStructure->is = (hopConstant >> 2) & 017;
  hopStructure->s = (hopConstant >> 6) & 1;
  hopStructure->loc = (hopConstant >> 7) & 0377;
  hopStructure->dupdn = (hopConstant >> 16) & 1;
  hopStructure->dm = (hopConstant >> 17) & 7;
  hopStructure->ds = (hopConstant >> 20) & 017;
  hopStructure->dupin = (hopConstant >> 24) & 1;

  retVal = 0;
  done: ;
  return (retVal);
}

// Inverse operation of parseHopConstant():  Namely, form a HOP constant from
// a hop structure.  Returns 0 on success and 1 on failure.
int
formHopConstant(hopStructure_t *hopStructure, int *hopConstant)
{
  int retVal = 1;

  if (hopStructure->im > 7)
    goto done;
  *hopConstant = ((hopStructure->im >> 1) & 03)
      | ((hopStructure->im & 1) << 25);
  if (hopStructure->is > 017)
    goto done;
  *hopConstant |= hopStructure->is << 2;
  if (hopStructure->s > 1)
    goto done;
  *hopConstant |= hopStructure->s << 6;
  if (hopStructure->loc > 0377)
    goto done;
  *hopConstant |= hopStructure->loc << 7;
  if (hopStructure->dupdn > 1)
    goto done;
  *hopConstant |= hopStructure->dupdn << 16;
  if (hopStructure->dm > 7)
    goto done;
  *hopConstant |= hopStructure->dm << 17;
  if (hopStructure->ds > 017)
    goto done;
  *hopConstant |= hopStructure->ds << 20;
  if (hopStructure->dupin > 1)
    goto done;
  *hopConstant |= hopStructure->dupin << 24;

  retVal = 0;
  done: ;
  return (retVal);
}

//////////////////////////////////////////////////////////////////////////////
// Fetch a data word from core memory.  Because it is "data", we first try to
// fetch it from data memory, and only fall back to fetching it from
// instruction memory if that fails.  (Recall that in our internal structures
// we pretend there's a separate "instruction memory" and "data memory" space,
// even though physically the same.)  If that happens, we set a flag
// (dataFromInstructinMemory) so that the calling code can deal with it if
// appropriate, but it's not treated as an error.  If location is treated as
// empty in both data and instruction memory, however, it is treated as an
// error.
//
// Note that address 0775 is treated as a special case, since the data comes
// from the CPU's PQ register rather than from memory.

int inhibitFetchMessages = 0;
int
fetchData(int module, int residual, int sector, int loc, int *data,
    int32_t *dataFromInstructionMemory)
{
  int retVal = 1;

  *dataFromInstructionMemory = 0;

  if (residual)
    {
      if (!ptc && loc == 0375)
        {
          *data = state.pq;
          return (0);
        }
      sector = 017;
      if (ptc)
        module = 0;
    }

  *data = state.core[module][sector][2][loc];
  if (*data == -1)
    {
      int16_t fetch1, fetch0;
      fetch1 = state.core[module][sector][1][loc];
      fetch0 = state.core[module][sector][0][loc];
      // Note that fetching data from a completely-empty location
      // may be an error (or perhaps not?), but that fetching from a
      // partially-empty location is not, since the LVDC/PTC code
      // is self-modifying, and may need to do that to modify the
      // code.  But regardless, we can't actually treat it as an
      // error when operating with the PTC panel, because it's
      // very disruptive to suddenly drop into the native debugger.
      if (fetch0 == -1 && fetch1 == -1)
        {
          if (!inhibitFetchMessages)
            printf("Fetching data from empty location %o-%02o-%03o\n", module,
                sector, loc);
          if (!ptc)
            {
              runStepN = 0;
              goto done;
            }
          *data = 0;
        }
      else
        {
          if (fetch0 == -1)
            fetch0 = 0;
          if (fetch1 == -1)
            fetch1 = 0;
          *data = (fetch1 << 13) + fetch0;
          *dataFromInstructionMemory = 1;
        }
    }

#ifdef DEBUG_A_LOT
  printf("Fetched %09o from %o-%02o-%03o\n", *data, module, sector, loc);
#endif

  retVal = 0;
  done: ;
  return (retVal);
}

// And similarly, write a word to data memory.  If there's already something
// in that address of "instruction memory" (which is physically the same space),
// get rid of what's in instruction and set a flag (dataOverwritesInstructionMemory)
// to tell the calling software that's what happened (in case it's interested
// in that info).
//
// Note that addresses 0775, 0776, and 0777 are treated as special cases.  For
// 0775, the data destination is the CPU's PQ register rather than memory.
// For 0776 or 0777, the data from the function argument is overridden and
// state.hopSaver is used instead if appropriate.  This happens if the
// preceding instruction was a HOP, and thus state.hopSaver holds the
// return address of the HOP.
int
storeData(int module, int residual, int sector, int loc, int data,
    int32_t *dataOverwritesInstructionMemory)
{
  int retVal = 1;

  *dataOverwritesInstructionMemory = 0;

  if (residual)
    {
      if (loc == 0375)
        {
          state.pq = data;
          return (0);
        }
      else if (loc == 0376 || loc == 0377)
        data = state.hopSaver;
      if (ptc)
        module = 0;
      sector = 017;
    }
  state.core[module][sector][2][loc] = data;
  if (state.core[module][sector][1][loc] != -1
      || state.core[module][sector][0][loc] != -1)
    {
      state.core[module][sector][1][loc] = -1;
      state.core[module][sector][0][loc] = -1;
      *dataOverwritesInstructionMemory = 1;
    }

#ifdef DEBUG_A_LOT
  printf("Stored  %09o to %o-%02o-%03o\n", data, module, sector, loc);
#endif

  retVal = 0;
  //done: ;
  return (retVal);
}

// Fetch the instruction itself.  First try getting it from instruction
// memory; if that fails, get it from data memory and set a flag
// (instructionFromDataMemory) for optional use by the calling code.
int
fetchInstruction(int module, int sector, int syllable, int loc,
    uint16_t *instruction, int *instructionFromDataMemory)
{
  int retVal = 1, fetchedData;
  *instructionFromDataMemory = 0;
  fetchedData = state.core[module][sector][syllable][loc];
  if (fetchedData == -1)
    {
      fetchedData = state.core[module][sector][2][loc];
      if (fetchedData == -1)
        {
          if (!inhibitFetchMessages)
            printf("Cannot fetch instruction from empty data address\n");
          goto done;
        }
      *instructionFromDataMemory = 1;
      if (syllable == 1)
        fetchedData = fetchedData >> 13;
      fetchedData &= 017777;
    }

  *instruction = fetchedData;
  retVal = 0;
  done: ;
  return (retVal);
}

// Sign-extend an LVDC/PTC data word to a native signed integer, for computations.
int
convertDataWordToNative(int dataWord)
{
  if ((signMask & dataWord) != 0)
    {
      // This method assumes that the target computer running the
      // LVDC/PTC emulator uses 2's-complement arithmetic.
      dataWord |= ~dataWordMask;
    }
  return dataWord;
}
// Truncate a native signed integer to an LVDC/PTC data word.
int
convertNativeToDataWord(int integer)
{
  // This method assumes that the target computer running the
  // LVDC/PTC emulator uses 2's-complement arithmetic.
  return (integer & dataWordMask);
}

void
checkForInterrupts(void)
{
  // Check if an interrupt has been triggered.
  if (!state.masterInterruptLatch && !state.inhibitInterruptsOneCycle)
    {
      if (ptc)
        {
          int i, intLatch, intInhibit;
          intLatch = state.cio[0154];
          intInhibit = state.interruptInhibitLatches;
          for (i = 0; i < 16;
              i++, intLatch = intLatch << 1, intInhibit = intInhibit >> 1)
            if ((intLatch & 0200000000) != 0 && (intInhibit & 1) == 0)
              break;
          if (i < 16)
            {
              state.masterInterruptLatch = 1;
              state.hop = state.core[0][017][2][i];
              //printf("Interrupt %d.\n", i + 1);
            }
        }
      else // LVDC
        {
          if (state.pio[0137] != 0)
            {
              hopStructure_t hs;
              state.masterInterruptLatch = 1;
              parseHopConstant(state.hop, &hs);
              state.hop = state.core[hs.im][017][2][0];
            }
        }
    }

}

//////////////////////////////////////////////////////////////////////////////
// The top-level function in this file.  Emulates a single instruction.

// The return value is 0 on success, non-zero on failure.  The cyclesUsed
// argument is used to report the number of computer cycles actually used while
// emulating the instruction, which is usually 1.
int instructionFromDataMemory = 0;
int dataFromInstructionMemory = 0;
int dataOverwritesInstructionMemory = 0;
int
runOneInstruction(int *cyclesUsed)
{
  int retVal = 1;
  int cycleCount = 1, nextLOC, nextS, isHOP = 0;
  int64_t dummy;  // For multiplication intermediate result.
  hopStructure_t hopStructure, rawHopStructure, rawestHopStructure;
  uint16_t instruction, operand9;
  uint8_t op, operand, residual, a8, a9;
  int32_t fetchedFromMemory;
  //int modOperand = 0;

  // If we changes any of state.pio[], state.cio[], or state.prs, then
  // the following state.xxxChange variables will be set accordingly to
  // indicate to external code that some action is needed before emulating
  // the next CPU instruction.
  state.pioChange = -1;
  state.cioChange = -1;
  state.prsChange = -1;
  state.lastHop = -1;
  state.riLastHOP = state.hop;
  if (state.busyCountPlotter)
    {
      state.busyCountPlotter--;
      if (!state.busyCountPlotter)
        state.bbPlotter = 0;
    }
  if (state.busyCountPrinter /* && (state.cio[0210] & 035) == 0*/)
    {
      state.busyCountPrinter--;
      if (!state.busyCountPrinter)
        {
          state.bbPrinter = 0;
        };
    }
  if (state.busyCountCarriagePrinter && (state.cio[0210] & 4) == 0)
    {
      state.busyCountCarriagePrinter--;
      if (!state.busyCountCarriagePrinter)
        {
          state.cio210CarrBusy = 0;
        };
    }
  if (state.busyCountTypewriter && (state.cio[0210] & 5) == 0)
    {
      state.busyCountTypewriter--;
      if (!state.busyCountTypewriter)
        {
          if (state.caseChange)
            {
              //printf("CASE %09o %09o %09o ", state.cio[0154], state.currentCaseInterrupt, state.currentTypewriterInterrupt);
              state.caseChange = 0;
              //state.cio[0154] &= ~state.currentCaseInterrupt;
              state.cio[0154] |= state.currentTypewriterInterrupt;
              //printf("-> %09o\n", state.cio[0154]);
              state.busyCountTypewriter = CASE_CHANGE_BUSY_CYCLES;
              dPrintoutsTypewriter("R1I CASE CHANGE");
            }
          else
            {
              state.bbTypewriter = 0;
              dPrintoutsTypewriter("R1I READY");
            }
        }
    }

  state.ai3Shifter = (state.ai3Shifter << 1) & 0377777777;

  // The following relates to how to report back a PRS instruction check-parity
  // bit on the interrupt latch, via a CIO 154.  I'm not sure how this should
  // interact with the regular CIO or PIO instructions for altering the interrupt
  // latch, nor if the data should be available indefinitely.
  if (state.prsParityDelayCount < 3)
    state.prsParityDelayCount++;

  // Set global variables providing background info on the emulation.
  state.inhibitInterruptsOneCycle = 0;
  dataFromInstructionMemory = 0;
  instructionFromDataMemory = 0;

  reenterForEXM: ;
  if (state.pendingEXM.pending)
    {
      state.pendingEXM.pending = 0;
      state.hop = state.pendingEXM.pendingHop;
      if (parseHopConstant(state.hop, &hopStructure))
        {
          printf("Cannot interpret current instruction address (HOP=%09o)\n",
              state.hop);
          runStepN = 0;
          goto done;
        }
      if (parseHopConstant(state.pendingEXM.nextHop, &rawHopStructure))
        {
          printf("Cannot interpret next instruction address (HOP=%09o)\n",
              state.pendingEXM.nextHop);
          runStepN = 0;
          goto done;
        }
      nextLOC = rawHopStructure.loc;
      nextS = rawHopStructure.s;
      instruction = state.pendingEXM.pendingInstruction;
    }
  else
    {
      // Find current instruction address and data-sector environment, by parsing
      // the HOP register to find its various fields.
      if (parseHopConstant(state.hop, &hopStructure))
        {
          //pushErrorMessage("Cannot interpret current instruction address", NULL);
          printf("Cannot interpret current instruction address (HOP=%09o)\n",
              state.hop);
          runStepN = 0;
          goto done;
        }
      memcpy(&rawHopStructure, &hopStructure, sizeof(hopStructure_t));
      // What would the next instruction location be in the normal course of events?
      // Only the LOC field of the HOP constant is involved, but we'll track the S
      // field as well, to facilitate working with TRA, TNZ, and TMI later.
      nextLOC = hopStructure.loc;
      nextS = hopStructure.s;
      if (hopStructure.loc != 0377)
        nextLOC += 1;

      if (fetchInstruction(hopStructure.im, hopStructure.is, hopStructure.s,
          hopStructure.loc, &instruction, &instructionFromDataMemory))
        goto done;
    }
  memcpy(&rawestHopStructure, &rawHopStructure, sizeof(hopStructure_t));
  state.riLastInstruction = instruction;

  // Parse instruction into fields.
  state.lastInstruction = instruction;
  op = instruction & 017;
  a9 = (instruction >> 4) & 1;
  operand = (instruction >> 5) & 0377;
  a8 = (operand >> 7) & 1;
  residual = a9;
  operand9 = operand | (a9 << 8);
  // The following has something to do with EXM, but doesn't work as-is,
  // since it messes up almost all operands.  Fix later.
  //operand = (operand & ~3) | modOperand;
  //modOperand = 0;

  // Execute the instruction.
  if (op == 000)
    {
      // HOP
      isHOP = 1;
      state.inhibitInterruptsOneCycle = 1;
      if (fetchData(hopStructure.dm, residual, hopStructure.ds, operand,
          &fetchedFromMemory, &dataFromInstructionMemory))
        {
          //pushErrorMessage("HOP to empty location", NULL);
          printf("HOP to empty location\n");
          runStepN = 0;
          goto done;
        }
#ifdef DEBUG_A_LOT
      printf("HOP %o-%02o-%o-%02o %09o\n", hopStructure.dm, residual, hopStructure.ds, operand, fetchedFromMemory);
#endif
      state.lastHop = state.hop;
      state.hop = fetchedFromMemory;
    }
  else if (!ptc && (op == 001 || op == 005))
    {
      // MPY or MPH
      // The actual LVDC had a pretty complex behavior with this instruction,
      // in that the full 26-bit result would become available 4 cycles later,
      // but after 2 cycles you could fetch the less-significant word of the
      // result from PQ.  At least at first, I'm not going to implement it that
      // way, and I'll just provide the full result in PQ immediately.  I don't
      // see a problem with doing that.
      if (fetchData(hopStructure.dm, residual, hopStructure.ds, operand,
          &fetchedFromMemory, &dataFromInstructionMemory))
        goto done;
      // The LVDC uses only 24 bits of the multiplicand and multitplier, so the
      // 2 least significant bits are discarded.  (Assumes native 2's-complement.)
      dummy = (convertDataWordToNative(fetchedFromMemory) & ~3)
          * (convertDataWordToNative(state.acc) & ~3);
      state.pq = convertNativeToDataWord(dummy / (1 << 25));
      if (op == 005)
        cycleCount = 5;
    }
  else if (ptc && op == 001)
    {
      // PRS
      // Within runOneInstruction(), we don't actually perform any actions on
      // the basis of this instruction, other than to access the variable
      // state.prs, and to set the variable state.prsChange to 1 to indicate
      // that state.prs has been written to.  The calling code must
      // interrogate these values in between instructions and take whatever
      // larger action is required.
      int data, location;
      if (residual == 0 || (residual == 1 && operand <= 0373))
        {
          if (fetchData(hopStructure.dm, residual, hopStructure.ds, operand,
              &fetchedFromMemory, &dataFromInstructionMemory))
            goto done;
          data = fetchedFromMemory;
          state.acc = data;
        }
      else if (residual == 1 && operand == 0374)
        {
          // I don't really know what a "group mark word" is supposed to be,
          // but in researching the BA8421 encoding, I found that the character
          // 077 in BA421 _may_ be a group mark.
          data = 077;
        }
      else if (residual == 1 && operand == 0375)
        data = state.acc;
      else
        goto done;
      location = operand | (residual << 8);
      state.prs[location] = data;
      state.prsChange = location;
      state.cio264Buffer = state.cio[0264];
      state.lastWasPrinter = 1;
    }
  else if (ptc && op == 005)
    {
      // CIO
      // Within runOneInstruction(), we don't actually perform any actions on
      // the basis of this instruction, other than to access the array
      // state.cio[], and to set the variable state.cioChange to indicate
      // which array element (if any) has been written to.  The calling code must
      // interrogate these values in between instructions and take whatever
      // larger action is required.

      // The following is based on Figure 2-11 in the PTC documentation.  CIO 175
      // does not appear at all in the documentation, but the PAST program calls it
      // and 155, 161, 165, and 171 "spares".  It "tests" these spares by executing
      // them and then by testing that CIO 175 has put 0 into ACC.  On the basis of
      // this rather slim data, I infer that 175 is a spare input port while the others
      // are spare outputs.
      if (operand9 == 0154 || operand9 == 0214 || operand9 == 0220
          || operand9 == 0175)
        {
          // The "gate" is needed only for reading PROG REG A, in which the lowest
          // 9 bits are gated by bit written out on CIO 210 to the discrete outputs.
          // When 1, those bits cause the corresponding bits on CIO 210 to be read
          // back as 1, but when 0 gates in other signals on CIO 210, including the
          // PRINTER/PLOTTER/TYPEWRITER BUSY bits.
          state.acc = state.cio[operand9];
          if (operand9 == 0214)
            {
              state.acc |= state.progRegA17_22 | state.bbPrinter
                  | state.bbPlotter | state.bbTypewriter;
              if ((state.cio[0210] & 8) != 0)
                state.acc |= 1;
              if ((state.cio[0210] & 16) != 0)
                state.acc |= 1;
              dPrintoutsTypewriter("RI CIO 214");
            }
          else if (operand9 == 0154)
            {
              if (state.lastWasPrinter)
                state.acc |= state.cio210CarrBusy;
              else
                state.acc |= state.cio210CarrBusy & ~0000400000;
              if (state.prsParityDelayCount > 0
                  && state.prsParityDelayCount < 4)
                {
                  state.acc = (state.acc & ~0002000000)
                      | state.prsDelayedParity[state.prsParityDelayCount];
                }
              // Has parity-check bit been set for CIO 264 since last PRS?
              // This is purely ad hoc ... i.e., empirically determined by looking
              // at the results of self-tests using PAST source code. It is
              // undoubtedly bogus.
              if ((state.cio264Buffer & 02) != 0 && (state.cio[0210] & 4) == 0)
                {
                  if (printerOctalMode)
                    state.acc |= 0000400000 | ((state.cio264Buffer & 3) << 24);
                  else
                    state.acc |= 0100000000;
                  //state.cio264Buffer &= ~2;
                }
            }
        }
      else
        {
          state.cioChange = operand9;
          // Note that for many CIO operations, it is the operation itself which
          // is significant, and not the specific info in ACC.  In other words,
          // the instruction above is often sufficient, and the instruction below
          // is often irrelevant.  However, there's no harm in the instruction
          // below, so we can keep it in all cases.
          state.cio[operand9] = state.acc;
        }
      if (operand == 0001)
        state.acc = state.ai3Shifter;
    }
  else if (op == 002)
    {
      // SUB
      if (fetchData(hopStructure.dm, residual, hopStructure.ds, operand,
          &fetchedFromMemory, &dataFromInstructionMemory))
        goto done;
      state.acc = convertNativeToDataWord(
          convertDataWordToNative(state.acc)
              - convertDataWordToNative(fetchedFromMemory));
    }
  else if (!ptc && op == 003)
    {
      // DIV
      // We start by shifting the dividend as far left as possible, to insure
      // that the quotient will have as many significant bits as possible.
      // We'll shift the quotient down afterward to account for this.  The
      // LVDC words are 26 bits, and we're using 64 bits for the intermediate
      // values.
      dummy = convertDataWordToNative(state.acc) * (1 << 28); // Scale ACC to 64 bits.
      if (fetchData(hopStructure.dm, residual, hopStructure.ds, operand,
          &fetchedFromMemory, &dataFromInstructionMemory))
        goto done;
      dummy /= convertDataWordToNative(fetchedFromMemory);
      // The quotient will now be 2**28 too big, so must rescale it.  Also, the
      // LVDC only has a 24-bit quotient, so the two least-significant bits
      // are discarded.  (Assumes native 2's-complement arithmetic.)
      state.pq = convertNativeToDataWord((dummy / (1 << 28)) & ~3); // Undo scaling.
    }
  else if (op == 004)
    {
      // TNZ
      if (state.acc != 0)
        {
          state.inhibitInterruptsOneCycle = 1;
          nextLOC = operand;
          nextS = residual;
          state.lastHop = state.hop;
        }
    }
  else if (op == 006)
    {
      // AND
      if (fetchData(hopStructure.dm, residual, hopStructure.ds, operand,
          &fetchedFromMemory, &dataFromInstructionMemory))
        goto done;
      state.acc &= fetchedFromMemory;
    }
  else if (op == 007)
    {
      // ADD
      if (fetchData(hopStructure.dm, residual, hopStructure.ds, operand,
          &fetchedFromMemory, &dataFromInstructionMemory))
        {
          printf("Failed\n");
          goto done;
        }
      state.acc = convertNativeToDataWord(
          convertDataWordToNative(state.acc)
              + convertDataWordToNative(fetchedFromMemory));
    }
  else if (op == 010)
    {
      // TRA
      state.inhibitInterruptsOneCycle = 1;
      nextLOC = operand;
      nextS = residual;
      state.lastHop = state.hop;
    }
  else if ((!ptc && op == 011) || (ptc && op == 015))
    {
      // XOR
      if (fetchData(hopStructure.dm, residual, hopStructure.ds, operand,
          &fetchedFromMemory, &dataFromInstructionMemory))
        goto done;
      state.acc = state.acc ^ fetchedFromMemory;
    }
  else if (op == 012)
    {
      // PIO
      // Within runOneInstruction(), we don't actually perform any actions on
      // the basis of this instruction, other than to access the array
      // state.pio[], and to set the variable state.pioChange to indicate
      // which array element (if any) has been written to.  The calling code must
      // interrogate these values in between instructions and take whatever
      // larger action is required.

      // Determine direction of data flow.  If the least-significant 2 bits of
      // the operand9 are 11, then the data flows into the accumulator.
      // Otherwise, the accumulator or memory is the source of the data.
      if ((operand9 & 3) == 3)
        state.acc = state.pio[operand9];
      else
        {
          state.pioChange = operand9;
          if (a8) // Source is the accumulator
            state.pio[operand9] = state.acc;
          else // Source is memory.
            {
              if (fetchData(hopStructure.dm, residual, hopStructure.ds, operand,
                  &fetchedFromMemory, &dataFromInstructionMemory))
                fetchedFromMemory = 0;
              state.pio[operand9] = fetchedFromMemory;
            }
        }
    }
  else if (op == 013)
    {
      // STO
      storeData(hopStructure.dm, residual, hopStructure.ds, operand, state.acc,
          &dataOverwritesInstructionMemory);
    }
  else if (op == 014)
    {
      // TMI
      // Since the accumulator is only 26 bits, it won't fill the int32_t
      // it's stored in, and hence we can't just check if it's negative.
      // However, it's stored in 2's-complement form, and if we assume that the
      // CPU running the emulator is also 2's-complement (which has been a good
      // assumption for at least the last 40 years), we can simply check its
      // most-significant (26th) bit.
      if ((state.acc & signMask) != 0)
        {
          state.inhibitInterruptsOneCycle = 1;
          nextLOC = operand;
          nextS = residual;
          state.lastHop = state.hop;
        }
    }
  else if ((!ptc && op == 015) || (ptc && op == 003))
    {
      // RSU
      if (fetchData(hopStructure.dm, residual, hopStructure.ds, operand,
          &fetchedFromMemory, &dataFromInstructionMemory))
        goto done;
      state.acc = convertNativeToDataWord(
          convertDataWordToNative(fetchedFromMemory)
              - convertDataWordToNative(state.acc));
    }
  else if (ptc && op == 016 && a8 == 1)
    {
      // CDS
      rawHopStructure.dm = (operand >> 4) & 1;
      rawHopStructure.ds = operand & 017;
#ifdef DEBUG_A_LOT
      printf("CDS %o,%02o\n", rawHopStructure.dm, rawHopStructure.ds);
#endif
    }
  else if (!ptc && op == 016 && a8 == 0 && a9 == 0)
    {
      // CDS
      rawHopStructure.dm = (operand >> 1) & 07;
      rawHopStructure.ds = (operand >> 4) & 017;
      rawHopStructure.dupdn = operand & 1;
    }
  else if (ptc && op == 016 && a8 == 0)
    {
      int direction = operand & 0100;
      int sign = state.acc & signMask;
#ifdef DEBUG_A_LOT
      printf("Shift %03o\n", operand);
#endif
      if (direction)
        {
          switch (operand & 077)
            {
          case 001:
            state.acc = sign | (state.acc >> 1);
            break;
          case 002:
            state.acc = sign | (sign >> 1) | (state.acc >> 2);
            break;
          case 004:
            state.acc = sign | (sign >> 1) | (sign >> 2) | (state.acc >> 3);
            break;
          case 010:
            state.acc = sign | (sign >> 1) | (sign >> 2) | (sign >> 3)
                | (state.acc >> 4);
            break;
          case 020:
            state.acc = sign | (sign >> 1) | (sign >> 2) | (sign >> 3)
                | (sign >> 4) | (state.acc >> 5);
            break;
          case 040:
            state.acc = sign | (sign >> 1) | (sign >> 2) | (sign >> 3)
                | (sign >> 4) | (sign >> 5) | (state.acc >> 6);
            break;
          default:
            //pushErrorMessage("Illegal SHF instruction", NULL);
            printf("Illegal SHF instruction\n");
            goto done;
            }
        }
      else
        {
          switch (operand & 077)
            {
          case 001:
            state.acc = state.acc << 1;
            break;
          case 002:
            state.acc = state.acc << 2;
            break;
          case 004:
            state.acc = state.acc << 3;
            break;
          case 010:
            state.acc = state.acc << 4;
            break;
          case 020:
            state.acc = state.acc << 5;
            break;
          case 040:
            state.acc = state.acc << 6;
            break;
          default:
            //pushErrorMessage("Illegal SHF instruction", NULL);
            printf("Illegal SHF instruction\n");
            goto done;
            }
          state.acc = state.acc & 0377777777;
        }
    }
  else if (!ptc && op == 016 && a8 == 0 && a9 == 1)
    {
      // SHF
      int sign = state.acc & signMask;
      printf("Shift %03o\n", operand);
      switch (operand)
        {
      case 000:
        state.acc = 0;
        break;
      case 001:
        state.acc = sign | (state.acc >> 1);
        break;
      case 002:
        state.acc = sign | (sign >> 1) | (state.acc >> 2);
        break;
      case 020:
        state.acc = state.acc << 1;
        break;
      case 040:
        state.acc = state.acc << 2;
        break;
      default:
        //pushErrorMessage("Illegal SHF instruction", NULL);
        printf("Illegal SHF instruction\n");
        goto done;
        }
      state.acc = state.acc & 0377777777;
    }
  else if (!ptc && op == 016 && a8 == 1 && a9 == 1)
    {
      // EXM
      const uint8_t locs[] =
        { 0200, 0240, 0300, 0340 };
      const uint8_t secs[] =
        { 004, 014, 005, 015, 006, 016, 007, 017 };
      int syllable, modBits, dsIndex;
      uint8_t loc;
      hopStructure_t pendingHop;
      modBits = operand & 017;
      syllable = (operand >> 4) & 1;
      loc = locs[(operand >> 5) & 3];
      if (fetchInstruction(hopStructure.dm, 017, syllable, locs[loc],
          &instruction, &instructionFromDataMemory))
        goto done;
      dsIndex = (instruction >> 4) & 7;
      instruction = (instruction & ~023) | modBits;
      state.pendingEXM.pendingInstruction = instruction;
      rawHopStructure.loc = nextLOC;
      rawHopStructure.s = nextS;
      if (formHopConstant(&rawHopStructure, &state.pendingEXM.nextHop))
        goto done;
      pendingHop.im = hopStructure.dm;
      pendingHop.is = 017;
      pendingHop.s = syllable;
      pendingHop.loc = locs[loc];
      pendingHop.dm = hopStructure.dm;
      pendingHop.ds = secs[dsIndex];
      pendingHop.dupdn = hopStructure.dupdn;
      pendingHop.dupin = hopStructure.dupin;
      if (formHopConstant(&pendingHop, &state.pendingEXM.pendingHop))
        goto done;
      state.pendingEXM.pending = 1;
      // We're just going to jump back up to the start of this function
      // to executed the modified instruction.  Because all the info
      // for doing this is in the state.pendingEXM structure, we could
      // instead exit the function normally and count on the parent code
      // to just call runOneInstruction() again later.  The problem is
      // that I'm not sure how all that affects stuff the parent code is
      // doing (such as the debugger interface), so for right now I'm
      // taking the simpler route of not returning until after the modified
      // instruction is executed.  But if all the details were worked out,
      // I think exiting the function normally would be better.
      cycleCount++;
      state.inhibitInterruptsOneCycle = 1;
      goto reenterForEXM;
    }
  else if (op == 017)
    {
      // CLA
      if (fetchData(hopStructure.dm, residual, hopStructure.ds, operand,
          &fetchedFromMemory, &dataFromInstructionMemory))
        goto done;
      state.acc = fetchedFromMemory;
    }
  else
    {
      //pushErrorMessage("Implementation error", NULL);
      printf("Implementation error\n");
      runStepN = 0;
      goto done;
    }

  rawHopStructure.loc = nextLOC;
  rawHopStructure.s = nextS;
  if (!isHOP)
    {
      if (formHopConstant(&rawHopStructure, &state.hop))
        goto done;
    }
  rawestHopStructure.loc++;
  if (formHopConstant(&rawestHopStructure, &state.hopSaver))
    state.hopSaver = -1;

  *cyclesUsed = cycleCount;
  retVal = 0;
  done: ;
  return (retVal);
}
back to top