Revision 4e5d304eb7cd5589b924ffb8b423b6f15511b35d authored by Ron Burkey on 20 October 2018, 17:47:00 UTC, committed by Ron Burkey on 20 October 2018, 17:47:00 UTC
the recently-added documents about YUL, was transcribed.  Because the
original program contained a deliberate error in YUL (as well as some
constructs that have unintentionally become errors in yaYUL), I've
provided it in two forms:  TRIVIUM (which matches the original scan,
to the extent feasible) and TRIVIUM-repaired (which has the deliberate
and unintentional errors fixed, but otherwise retains the identical
functionality of the original).
1 parent c6c292e
Raw File
binLEMAP.c
/*
  Copyright 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

  Filename:	binLEMAP.c
  Purpose:	Accepts octal data from an ASCII file in a format
  		that is natural for data taken from an AGS (LEMAP)
		assembly listing, and converts it into a binary
		format suitable for use with the yaAGS AGS CPU
		emulator.  The main purpose, aside from quickly
		preparing binaries from LEMAP assembly listings
		is to prepare binaries that can be used to verify
		yaLEMAP operation and AGS flight-program source code.
  Compiler:	GNU gcc.
  Reference:	http://www.ibibio.org/apollo
  Mode:		2005-01-10 RSB.	Began.
  		2005-01-19 RSB	Eliminate all appearances of the 
				character 'p' (which is sometimes
				added to fool iMac's text-to-speech
				into saying the numbers correctly).
  		2017-10-12 MAS	binLEMAP now can calculate up to 10
  		              	checksums per binsource instead of
  		              	only one. To make use of this, place
  		              	"CKSM 207-1004" (for example) on the
  		              	line preceding the embedded checksum
  		              	in the binsource.
  		2017-10-17 MAS	Added support for what I am calling
  		              	the "total" checksum of the listing,
  		              	which appears to be the 36-bit sum
  		              	of all words appearing in the listing,
  		              	but with the two 18-bit words making
  		              	up that sum swapped. The "total"
  		              	checksum is typically printed at the
  		              	very end of an AGS listing.

  The format of the input file is as follows:
  
  1.  Anything following a '#' char is discarded.
  2.  Blank lines (after comment removal) are discarded.
  3.  Non-blank lines have one of the following formats:
	ORG OctalNum
	OctalNum
	OctalNum OctalNum OctalNum
	
  The line-format containing 3 octal numbers produces a
  single output number that is produced by shifting and
  logical-ORring the 3 numbers as if the first number is an
  opcode, the second is an index bit, and the third is an
  address.
  
  The output file simply contains the binary values, 32
  bits for each location from 0 to 07777, in little-endian
  format.  Only the least-significant 18 bits are used, 
  and the upper 14 bits of each value are zero. The format
  is chosen to be easy for lazy users on little-endian
  CPUs like the Intel Pentium, but binLEMAT itself works
  on big-endian CPUs as well, and is platform-independent.

  The output file is always called binLEMAP.bin.
*/

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

#define MEMSIZE 010000
static int Memory[MEMSIZE], Valid[MEMSIZE];
static char s[1000], sd[1000];
static int CheckStarts[10];
static int CheckEnds[10];
static int CheckLocs[10];

int
main (void)
{
  int i, j, i1, i2, i3, RetVal = 0, Lines = 0;
  int Location = 0, Checksum = 0, NumChecksums = 0;
  long long l1 = 0, TotalChecksum = 0, TotalSum = 0;
  char *ss;
  FILE *fp;

  // Process the input file.
  while (NULL != fgets (s, sizeof (s) - 1, stdin))
    {
      Lines++;
      // Eliminate the newline.
      for (ss = s; *ss; ss++)
	if (*ss == '\n')
	  {
	    *ss = 0;
	    break;
	  }
	else
	  *ss = toupper (*ss);
      // Eliminate comments.
      ss = strstr (s, "#");
      if (ss != NULL)
	*ss = 0;
	
      // Turn 'p' in to a space.
      for (ss = s; *ss; ss++)
        if (*ss == 'p')
	  *ss = ' ';

      if (1 == sscanf (s, "ORG%o%s", &i, sd))
	Location = i;
      else if (2 == sscanf (s, "CKSM %o-%o%s", &i1, &i2, sd))
        {
          CheckStarts[NumChecksums] = i1;
          CheckEnds[NumChecksums] = i2;
          CheckLocs[NumChecksums] = Location;
          NumChecksums++;
        }
      else if (1 == sscanf (s, "TOTAL %llo%s", &l1, sd))
        {
          TotalChecksum = l1;
        }
      else
	{
	  i = sscanf (s, "%o%o%o%s", &i1, &i2, &i3, sd);
	  switch (i)
	    {
	    case -1:
	    case 0:
	      if (0 != (i = sscanf (s, "%s", sd)) && -1 != i)
		{
		  RetVal++;
		  fprintf (stderr, "%d: Unrecognized line: %s\n", Lines, s);
		}
	      break;
	    case 1:
	    BufferIt:
	      if (Location < 0 || Location >= MEMSIZE)
		{
		  RetVal++;
		  fprintf (stderr, "%d: Location Counter out of range.\n",
			   Lines);
		}
	      else
		{
		  if (i1 < 0 || i1 > 0777777)
		    {
		      RetVal++;
		      fprintf (stderr, "%d: Octal value %o out of range.\n",
			       Lines, i1);
		      i1 &= 0777777;
		    }
		  if (Valid[Location])
		    {
		      RetVal++;
		      fprintf (stderr, "%d: Overwriting address 0%04o.\n",
			       Lines, Location);
		    }
		  Memory[Location] = i1;
		  Valid[Location] = 1;
		  Location++;
		}
	      break;
	    case 2:
	      RetVal++;
	      fprintf (stderr, "%d: Wrong number of values.\n", Lines);
	      break;
	    case 3:
	      if (i1 < 0 || i1 > 077 || 0 != (i1 & 1))
		{
		  RetVal++;
		  fprintf (stderr, "%d: Illegal opcode %o.\n", Lines, i1);
		}
	      if (i2 != 0 && i2 != 1)
		{
		  RetVal++;
		  fprintf (stderr, "%d: Illegal index bit %o.\n", Lines, i2);
		}
	      if (i3 < 0 || i3 >= MEMSIZE)
		{
		  RetVal++;
		  fprintf (stderr, "%d: Illegal address %o.\n", Lines, i3);
		}
	      i1 = (((i1 | i2) << 12) | i3);
	      goto BufferIt;
	    default:
	      RetVal++;
	      fprintf (stderr, "%d: Unrecognized line: %s\n", Lines, s);
	      break;
	    }
	}
    }

  for (i = 0; i < NumChecksums; i++)
    {
      for (j = CheckStarts[i], Checksum = 0; j <= CheckEnds[i]; Checksum += Memory[j++]);
      Checksum = (0777777 & -Checksum);
      if (!Valid[CheckLocs[i]] || Checksum != Memory[CheckLocs[i]])
        {
            RetVal++;
            fprintf (stderr, "Checksum mismatch, computed=%o, embedded=%o.\n",
                     Checksum, Memory[CheckLocs[i]]);
        }
    }

  if (TotalChecksum != 0)
    {
      // Calculate the overall checksum for the entire program. This
      // is the sum of all the words, which is then word-swapped.
      for (i = 0; i < MEMSIZE; i++)
        TotalSum += Memory[i];
      TotalSum = (TotalSum >> 18) | ((TotalSum & 0777777) << 18);
      if (TotalSum != TotalChecksum)
        {
          RetVal++;
          fprintf (stderr, "Total checksum mismatch, computed=%llo, provided=%llo\n",
                   TotalSum, TotalChecksum);
        }
    }

  // Output the binary
  fp = fopen ("binLEMAP.bin", "wb");
  if (fp == NULL)
    {
      RetVal++;
      fprintf (stderr, "Cannot create output file.\n");
    }
  else
    {
      for (i = 0; i < MEMSIZE; i++)
	{
	  fputc (0xFF & Memory[i], fp);
	  fputc (0xFF & (Memory[i] >> 8), fp);
	  fputc (0x03 & (Memory[i] >> 16), fp);
	  fputc (0, fp);
	}
      fclose (fp);
    }

  // All done!
  if (RetVal == 0)
    fprintf (stderr, "Successful!\n");
  else
    fprintf (stderr, "%d total errors.\n", RetVal);
  return (RetVal);

}
back to top