https://github.com/virtualagc/virtualagc
Revision 078c79d8734a9ed2860303a7c1662004284fe853 authored by Ron Burkey on 07 August 2022, 15:04:04 UTC, committed by Ron Burkey on 07 August 2022, 15:04:04 UTC
assembly listings from yaASM and yaLEMAP. Added some debugging messages
to 'make install'.  Tweaked debugging messages that VirtualAGC embeds in
'simulate'.  Verified buildability in Mint 21, 20, 19, 17, and verified
buildability using clang in Mint 17.
1 parent 6bb1acc
Raw File
Tip revision: 078c79d8734a9ed2860303a7c1662004284fe853 authored by Ron Burkey on 07 August 2022, 15:04:04 UTC
Fixed a potential string-overflow bug in yaASM. Removed timestamps from
Tip revision: 078c79d
mapperBlock1.c
/*
 * Copyright 2016 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
 *
 *  Filename:     mapperBlock1.c
 *  Purpose:      This is a utility whose purpose, given the contents of a
 *                Block1 rope and a set of starting addresses, is to map
 *                out those memory areas containing the basic instructions.
 *                Data areas, interpretive areas, and unused areas are left
 *                unmapped.
 *  Contact:      Ron Burkey <info@sandroid.org>
 *  Website:      www.ibiblio.org/apollo
 *  Mode:         2015-08-27 RSB  Began.
 *
 *  This is really just a little private experiment at the moment.  What
 *  it's for is this:  We have the prospect of acquiring the rope contents
 *  of a Block 1 AGC program called CORONA, but without source code.  I
 *  would like to disassemble the rope to regenerate the source code.
 *  One of the first steps in such a process is to determine which areas
 *  of the rope are basic, interpreter, data, or unused.  This utility
 *  addresses just the first.
 *
 *  The idea is this there are only certain possible code-entry points
 *  in the program, namely the power-up starting address and the
 *  interrupt vectors.  From each one of those, if you could trace through
 *  every possible code path, then you could know the location of every
 *  basic instruction.  I believe those addresses to be:
 *
 *      Power-up entry: One of the interrupt-vectors?  (In Block 2, it
 *                      would be the first interrupt-vector, which would
 *                      be at 4000.  Here, 2030 looks like the most-likely
 *                      one, but I don't really understand it.)
 *      Interrupts:     2000, 2004, 2010, 2014, 2020, 2024, 2030
 *
 *  This program neither disassembles the rope nor simulates its execution,
 *  but it does decode *enough* of each instruction it encounters to
 *  determine if it is a jump (TC) or conditional jump (CCS), and recursively
 *  follows the addresses implied by those jumps.  It also has to track the
 *  INDEX instruction and the assignments to the BANKREG register (address 015)
 *  to know which banks are being referenced.  I don't know what the power-up
 *  value of BANKREG is (I hope it doesn't matter!) so I arbitrarily set it at 0
 *
 *  I think it's the INDEX register that's the real fly in the ointment here,
 *  because it can jump almost any place, on the of a dynamical choice rather
 *  than something known deducible from the binary, so I'll have to work out
 *  some way to handle that.
 *
 *  To a certain extent, this program can also *help* to map out data areas,
 *  since it could in principle track all addresses which are loaded from
 *  or stored to, and I may add that feature in later, if the primary function
 *  seems to work well.
 *
 *  The same procedure could be followed for Block 2, but it would be more
 *  complex.  However, there is no prospect I'm aware of for obtaining Block 2
 *  programs solely from a rope, so I have no intention of addressing Block 2
 *  in this utility.
 *
 *  In this program, all C variables associated with an address assume a flat
 *  address space from 0 to 02000*035-1, where
 *
 *      0-1777 is erasable.
 *      2000-3777 is fixed 2000-3777 or 01,6000-01,7777
 *      4000-5777 is fixed 4000-5777 or 02,6000-02,7777
 *      6000-7777 is fixed 03,6000-03,7777
 *      10000-11777 is fixed 04,6000-04,7777
 *      .
 *      .
 *      .
 *      70000-71777 is fixed 34,6000-34,7777
 *
 * Conversion from the flat space to the erasable/fixed/banked representation is
 * done on an as-needed basis.
 */

#include <stdio.h>
#include <string.h>

#define MAX_ENTRY_POINTS 16
// basicMap[] is really just a set of true/false indicators as to whether the
// location contained a basic instruction or not.  I hope that erasable
// (0-01777) can't have an instruction in it.
#define MEMORY_SIZE (02000 * 035)
int rope[MEMORY_SIZE] =
  { 0 };
int basicMap[MEMORY_SIZE] =
  { 0 };
int dataMap[MEMORY_SIZE] =
  { 0 };
int BANKREG = 0;
int INDEX = -1;
int PC = 02000;

int
initializeBasic(int address)
{
  if (address < 0 || address >= MEMORY_SIZE)
    {
      fprintf(stderr, "Address overflow.\n");
      return (1);
    }
  INDEX = -1;
  PC = address;
  return (0);
}

// A function to decode a single basic instruction.  If elaborated enough, I suppose
// it could eventually become the basis for a Block 1 CPU simulator, though
// I only need the bare beginnings of that right now.  Returns
//
//      0       Success
//      1       Address not in memory space
int
decodeBasic(void)
{
  int instruction, keepIndex = 0, retVal = 1;
  // Sanity check.
  if (PC < 0 || PC >= MEMORY_SIZE)
    {
      fprintf(stderr, "Address overflow.\n");
      goto done;
    }
  instruction = rope[PC++];
  if (PC != 04000 && PC % 02000 == 0)
    {
      fprintf(stderr, "End of bank %02o reached.\n", (PC - 1) / 02000);
    }
  if (instruction == 047777)
    {
      goto done;
    }
  switch (instruction & 070000)
    {
  case 000000: // TC instruction.
    break;
  case 010000: // CCS instruction.
    break;
  case 020000: // INDEX instruction.
    keepIndex = 1;

    break;
  case 030000: // XCH, CAF instruction.
    // XCH and CAF have the same opcode bits, but it's XCH if the operand is erasable
    // memory and it's CAF if the operand is fixed memory.
    break;
  case 040000: // CS, MP instructions.
    // I believe it's an MP if the preceding instruction is EXTEND (or EXTEND / INDEX something)
    // and CS otherwise.   EXTEND = INDEX OPOVF = 047777.
    // But I also see it a few times preceded by INDEX TEM4, and no EXTEND preceding that.
    // I have no idea what that means, unless it's a typo.
    break;
  case 050000: // TS, DV instruction.
    // I believe it's an DV if the preceding instruction is EXTEND (or EXTEND / INDEX something)
    // and TS otherwise.   EXTEND = INDEX OPOVF = 047777.
  case 060000: // AD, SU instruction.
    // I believe it's an SU if the preceding instruction is EXTEND (or EXTEND / INDEX something)
    // and AD otherwise.   EXTEND = INDEX OPOVF = 047777.
    break;
  case 070000: // MASK instruction.
    break;
    }

  retVal = 0;
  done: ;
  if (!keepIndex)
    INDEX = -1;
  return (retVal);
}

// A recursive function to map an area starting from an address.
int
mapEntryPoint(int address)
{
  while (1)
    {
      int instruction;

      // Error or exit conditions.
      if (address < 0 || address >= MEMORY_SIZE)
        {
          fprintf(stderr, "Address overflow.\n");
          return (1);
        }
      if (address < 02000)
        {
          fprintf(stderr, "Erasable encountered.\n");
          return (1);
        }
      if (basicMap[address]) // Already hit before.
        break;

      // Parse the instruction.
      instruction = rope[address++];

    }

  return (0);
}

int
main(int argc, char *argv[])
{
  int i, j, k, entryPoints[MAX_ENTRY_POINTS], numEntryPoints = 0;

  // Parse the command line.
  for (i = 1; i < argc; i++)
    {
      if (!strncmp(argv[i], "--rope=", 7))
        {
          FILE *fp;
          fp = fopen(&argv[i][7], "rb");
          if (fp == NULL)
            {
              fprintf(stderr, "File not found.\n");
              return (1);
            }
          for (j = 02000; j < MEMORY_SIZE; j++)
            {
              k = getc(fp);
              if (k == EOF)
                {
                  fprintf(stderr, "Unexpected end of file.\n");
                  return (1);
                }
              rope[i] = k << 7;
              k = getc(fp);
              if (k == EOF)
                {
                  fprintf(stderr, "Unexpected end of file.\n");
                  return (1);
                }
              rope[i] |= (k >> 1) & 0177;
            }
          fclose(fp);
        }
      else if (1 == sscanf(argv[i], "--entry=%o", &j))
        {
          if (numEntryPoints >= MAX_ENTRY_POINTS)
            {
              fprintf(stderr, "Too many entry points.\n");
              return (1);
            }
          entryPoints[numEntryPoints++] = j;
        }
      else if (!strcmp(argv[i], "--help"))
        {
          fprintf(stderr,
              "Usage:  mapperBlock1 --rope=Filename --entry=Octal [--entry=Octal ...]");
          return (0);
        }
      else
        {
          fprintf(stderr, "Unknown switch: \"%s\"\n", argv[i]);
          return (1);
        }
    }
  if (numEntryPoints == 0)
    {
      fprintf(stderr, "No entry points were specified.\n");
      return (1);
    }

  for (i = 0; i < numEntryPoints; i++)
    if (mapEntryPoint(entryPoints[i]))
      {
        fprintf(stderr, "Implementation error for entry point %o.\n",
            entryPoints[i]);
        return (1);
      }

  return (0);
}

back to top