Revision fd205ce62fe00587fdef69712d42d41d7a1bfc70 authored by Mike Stewart on 01 September 2016, 02:44:47 UTC, committed by Mike Stewart on 01 September 2016, 02:44:47 UTC
* Made T6RUPT occur on emission of a ZOUT by DINC
* Switched to AddSP16() for incrementing in DINC, to fix its numerical
  behavior
* Forbade regular CPU instructions from triggering T6RUPT
* Restricted TIME6 to only increment when enabled
* Disabled TIME6 upon triggering of T6RUPT
1 parent d042fd0
Raw File
yaYUL.c
/*
  Copyright 2003-2005,2009-2010,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:	yaYUL.c
  Purpose:	This is an assembler for Apollo Guidance Computer (AGC)
  		assembly language.  It is called yaYUL because the original
		assembler was called YUL, and this "yet another YUL".
  Contact:	Ron Burkey <info@sandroid.org>
  Website:	www.ibiblio.org/apollo/index.html
  Mode:		04/11/03 RSB.	Began.
  		07/03/04 RSB.	Now writes the binary file, but does not
				yet compute the bugger words.  This is
				principally useful as a temporary measure
				to allow me to assemble Validation.s, 
				which doesn't use the bugger words.
		07/04/04 RSB	Now returns non-zero if there are fatal
				errors in assembly.
		07/07/04 RSB	Added the predefined symbol "$7".
		07/26/04 RSB	Added --force.
		07/30/04 RSB	Added terminating bugger words to banks.
		08/12/04 RSB	Added NVER.
		05/14/05 RSB	Corrected website reference.
		07/27/05 JMS	(04/30/05) Write symbol table to binary file 
				with --g flag.
		07/28/05 RSB	Made --g the default.  Still accepts the
				--g switch, but it doesn't do anything.
		07/28/05 JMS    Added support for writing SymbolLines_to to symbol
		                table file.
		03/17/09 RSB	Make sure there's no .bin file produced on error.
		06/06/09 RSB	Corrected the address offsets printed in the 
				bugger word table.  (Was printing addresses like
				33,1777 rather than 33,3777.)
		06/27/09 RSB	Added some stuff for HtmlOut.  Don't know yet if
				it will actually go anywhere, or if I'm just
				messing around.	
		07/25/09 RSB	Began adding the --block1 feature.  Since there's
				mostly source-level compatibility, the way 
				I'm *trying* to do this is to just basically
				do a normal assembly but to substitute different
				binary codes at the final step and to limit
				the memory size differently.  I'm sure I'll
				have to add additional tweaks as I go along.
		02/20/10 RSB	Added --unpound-page.
		08/18/16 RSB    Various stuff related to --block1.
		08/21/16 RSB    Now outputs the correct number of banks for --block1.
		08/23/16 RSB	Corrected the address offsets used for block 1 in the
		                bugger-word table at the end of the listing.  Also,
		                for block 2, yaYUL automatically adds the two
		                extra pre-bugger-word address indicators, but in
		                block 1 these appear explicitly in the code, so
		                if yaYUL were to do it they would appear twice,
		                and the bugger words would be wrong as well.
*/

#include "yaYUL.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>

//#define VERSION(x) #x

//-------------------------------------------------------------------------
// Some global data.

int Force = 0;
char *InputFilename = NULL, *OutputFilename = NULL;
//FILE *InputFile = NULL;
FILE *OutputFile = NULL;
static int Hardware  = 0;

#if 0
static Address_t RegA = REG(00);
static Address_t RegL = REG(01);
static Address_t RegQ = REG(02);
#endif
static Address_t RegEB = REG(03);
static Address_t RegFB = REG(04);
static Address_t RegZ = REG(05);
static Address_t RegBB = REG(06);
static Address_t RegZeroes = REG (07);
#if 0
static Address_t RegARUPT = REG(010);
static Address_t RegLRUPT = REG(011);
static Address_t RegQRUPT = REG(012);
// Registers 013 and 014 are reserved.
static Address_t RegZRUPT = REG(015);
static Address_t RegBBRUPT = REG(016);
#endif
static Address_t RegBRUPT = REG(017);
#if 0
static Address_t RegCYR = REG(020);
static Address_t RegSR = REG(021);
static Address_t RegCYL = REG(022);
static Address_t RegEDOP = REG(023);
#endif

//-------------------------------------------------------------------------
// The following two utility functions are used in computing the bank
// checksums, and were simply copied from Oct2Bin.c.

// Convert an AGC-format signed integer to native format.
static int
AgcToNative (uint16_t n)
{
  int i;
  i = n;
  if (0 != (n & 040000))
    i = -(077777 & ~i);
  return (i);
}

// This function takes two signed integers in AGC format, adds them, and returns
// the sum (also in AGC format).  If there's overflow or underflow, the 
// carry is added in also.  This is done because that's the goofy way the
// AGC checksum is created.
int
Add (int n1, int n2)
{
  int i1, i2, Sum;
  // Convert from AGC 1's-complement format to the native integer format of this CPU.
  i1 = AgcToNative (n1);
  i2 = AgcToNative (n2);
  // Add 'em up.
  Sum = i1 + i2;
  // Account for carry or underflow.
  if (Sum > 16383)
    {
      Sum -= 16384;
      Sum++;
    }
  else if (Sum < -16383)
    {
      Sum += 16384;
      Sum--;
    }  
  // The following condition can't occur, but I'll check for it anyway.
  //if (Sum > 16383 || Sum < -16383)
  //  printf ("Arithmetic overflow.\n");
  // Convert back to 1's-complement and return.
  if (Sum >= 0)
    return (Sum);
  return (077777 & ~(-Sum)); 
}

//-------------------------------------------------------------------------
// The main program.

int 
main (int argc, char *argv[])
{
  int MaxPasses = 10;
  int RetVal = 1, i, j, k, LastUnresolved, Fatals = 0, Warnings = 0;
  extern int UnpoundPage;
  
  // JMS: OutputSymbols = 1 to output a symbol table to SymbolFile.
  // RSB: Jordan made this an option, but I think it should be the default.
  int OutputSymbols = 1;	// 0;
  char *SymbolFile = NULL;

  // Parse the command-line options.
  for (i = 1; i < argc; i++)
    {
      if (!strcmp (argv[i], "--help") || !strcmp (argv[i], "/?"))
        goto Done;
      else if (1 == sscanf (argv[i], "--max-passes=%d", &j))
        MaxPasses = j;	
      else if (!strcmp (argv[i], "--force"))
        Force = 1;	
      else if (!strcmp (argv[i], "--g"))
        OutputSymbols = 1;
      else if (!strcmp (argv[i], "--html"))
        Html = 1;
      else if (!strcmp (argv[i], "--unpound-page"))
        UnpoundPage = 1;
      else if (!strcmp (argv[i], "--block1"))
        Block1 = 1;
      else if (!strcmp (argv[i], "--hardware"))
        Hardware = 1;
      else if (*argv[i] == '-' || *argv[i] == '/')
        {
	  printf ("Unknown switch \"%s\".\n", argv[i]);
	  goto Done;
	}	
      else if (InputFilename == NULL)
        {
	  InputFilename = argv[i];
	  OutputFilename = (char *) malloc (5 + strlen (InputFilename));
	  if (OutputFilename == NULL)
	    {
	      printf ("Out of memory (1).\n");
	      goto Done;
	    }
	  sprintf (OutputFilename, "%s.bin", InputFilename);
	  //InputFile = fopen (InputFilename, "r");
	  //if (InputFile == NULL)
	  //  {
	  //    printf ("Input file does not exist.\n");
	  //    goto Done;
	  //  }
	  OutputFile = fopen (OutputFilename, "wb");
	  if (OutputFile == NULL)
	    {
	      printf ("Cannot create output file.\n");
	      goto Done;
	    }

	}
      else
        {
	  printf ("Two input files defined.\n");
	  goto Done;
	}	
    }

  printf ("Apollo Guidance Computer (AGC) assembler, version " NVER
          ", built " __DATE__ ", Block %d\n", (Block1 ? 1 : 2));
  printf ("(c)2003-2005,2009-2010,2016 Ronald S. Burkey\n");
  printf ("Refer to http://www.ibiblio.org/apollo/index.html for more information.\n");

  if (InputFilename == NULL || OutputFile == NULL)
    goto Done;  
  if (Html)
    {
      if (HtmlCreate (InputFilename))
	goto Done;
    }
    
  // Perform a preliminary pass, whose sole purpose is to identify
  // all symbols defined in the program.
  SymbolPass (InputFilename);
  // Also, define all register names.
  // ... Later:  It turns out that the Luminary or Colossus source code
  // defines any registers it needs, so this step isn't required.
  // I use the following symbols, which I don't allow the source to define.
  AddSymbol ("$3");
  AddSymbol ("$4");
  AddSymbol ("$5");
  AddSymbol ("$6");
  AddSymbol ("$7");
  AddSymbol ("$17");
  if (Block1)
    {
      AddSymbol ("$16");
      AddSymbol ("$25");
      AddSymbol ("$5777");
  }

  // Sort the symbol table, or else we won't be able to locate the 
  // symbols later.
  Fatals += SortSymbols ();  

  // Assign the registers their proper addresses.
  if (!Block1)
    {
      EditSymbol ("$3", &RegEB);
      EditSymbol ("$4", &RegFB);
      EditSymbol ("$5", &RegZ);
      EditSymbol ("$6", &RegBB);
      EditSymbol ("$7", &RegZeroes);
      EditSymbol ("$17", &RegBRUPT);
    }
  else
    {
      Address_t Location3 = REG (03);
      Address_t Location4 = REG (04);
      Address_t Location5 = REG (05);
      Address_t Location6 = REG (06);
      Address_t Location7 = REG (07);
      Address_t Location16 = REG (016);
      Address_t Location17 = REG (017);
      Address_t Location25 = REG (025);
      Address_t Location5777 = FIXEDADD (05777);
      EditSymbol ("$3", &Location3);
      EditSymbol ("$4", &Location4);
      EditSymbol ("$5", &Location5);
      EditSymbol ("$6", &Location6);
      EditSymbol ("$7", &Location7);
      EditSymbol ("$16", &Location16);
      EditSymbol ("$17", &Location17);
      EditSymbol ("$25", &Location25);
      EditSymbol ("$5777", &Location5777);
    }
    
  // Perform all compiler passes. What happens is that we keep 
  // running passes until all defined symbols have known values.  
  // Then we do one final pass to actually generate object code.
  // At the end of each pass we do a check, and if some symbols 
  // are still not resolved, we bump LAST_PASS upward.  I'm sure
  // there's a more mathematically sophisticated way to do this,
  // but it's not worth the effort to figure it out.
  
  LastUnresolved = UnresolvedSymbols ();
  for (i = 1; i <= MaxPasses; i++)
    {
      printf ("Pass #%d\n", i);
      j = Pass (0, InputFilename, OutputFile, &Fatals, &Warnings);
      k = UnresolvedSymbols ();
      if (j == -1)	
	{
	  printf ("Unrecoverable error.\n");
	  break;
	} 
      if (k == 0 || k >= LastUnresolved)
        {
	  printf ("Pass #%d\n", i + 1);
	  Pass (1, InputFilename, OutputFile, &Fatals, &Warnings);
	  break;
	}
      LastUnresolved = k;
      //PrintSymbols ();	
    }  
    
  // Print the symbol table.
  printf ("\n\n");
  PrintBankCounts ();
  printf ("\n\n");
  PrintSymbols (); 
  printf ("\nUnresolved symbols:  %d\n", UnresolvedSymbols ());
  printf ("Fatal errors:  %d\n", Fatals);
  printf ("Warnings:  %d\n", Warnings); 
  if (HtmlOut != NULL)
    {
      fprintf (HtmlOut, "\n");
      fprintf (HtmlOut, "</pre>\n<h1>Assembly Status</h1>\n<pre>\n");
      fprintf (HtmlOut, "Unresolved symbols:  %d\n", UnresolvedSymbols ());
      fprintf (HtmlOut, "Fatal errors:  %d\n", Fatals);
      fprintf (HtmlOut, "Warnings:  %d\n", Warnings); 
      fprintf (HtmlOut, "\n");
      fprintf (HtmlOut, "</pre>\n<h1>Bugger Words</h1>\n<pre>\n");
    }

  // JMS: 07.28
  // We sort the lines by increasing physical address so we can look them
  // up later.
  SortLines (SORT_YUL);

  // JMS: Print the symbol table to a binary file if we want to
  if (OutputSymbols)
    {
      SymbolFile = (char *) malloc (8 + strlen (InputFilename));
      if (SymbolFile == NULL)
	{
	  printf ("Out of memory (2).\n");
	  goto Done;
	}
      sprintf (SymbolFile, "%s.symtab", InputFilename);
      WriteSymbolsToFile (SymbolFile);
    }

  // Output the executable object code.
  if (Fatals == 0 || Force)
    {
      int BankRaw, Bank, Offset, Value;
      uint16_t Bugger, GuessBugger;

      printf ("\n");
      for (BankRaw = (Block1 ? 1 : 0); BankRaw < (Block1 ? 035 : 044); BankRaw++)
        {
	  // Compute the actual bank number.
	  Bank = BankRaw;
	  if (Bank < 4 && !Hardware && !Block1)	// flip-flop 0,1 with 2,3 when not building for hardware targets
	    Bank ^= 2;
	  // Add bugger info to the bank.
	  if (Block1)
	    {
	      if (Bank == 0)
	        Offset = 02000;
	      else if (Bank == 1)
	        Offset = 04000;
	      else
	        Offset = 06000;
	    }
	  else
	    {
              if (Bank == 2)
                Offset = 04000;
              else if (Bank == 3)
                Offset = 06000;
              else
                Offset = 02000;
	    }
	  Value = GetBankCount (Bank);
	  if (!Block1) {
            if (Value < 01776)
              {
                ObjectCode[Bank][Value] = Value + Offset;
                Value++;
              }
            if (Value < 01777)
              {
                ObjectCode[Bank][Value] = Value + Offset;
                Value++;
              }
	  }
	  if (Value < 02000 && Value != 0)
	    {
	      for (Bugger = Offset = 0; Offset < Value; Offset++)
		Bugger = Add (Bugger, ObjectCode[Bank][Offset]);
	      if (0 == (040000 & Bugger))
	        GuessBugger = Add (Bank, 077777 & ~Bugger);
	      else
	        GuessBugger = Add (077777 & ~Bank, 077777 & ~Bugger);
	      ObjectCode[Bank][Value] = GuessBugger;
	      printf ("Bugger word %05o at %02o,%04o.\n", GuessBugger, Bank,
	    		  (Block1 ? 06000 : 02000) + Value);
	      if (HtmlOut != NULL)
	        fprintf (HtmlOut, "Bugger word %05o at %02o,%04o.\n", GuessBugger,
	        		Bank, (Block1 ? 06000 : 02000) + Value);
	    } 
	  // Output the binary data.  
	  for (Offset = 0; Offset < 02000; Offset++)
	    {
	      Value = (ObjectCode[Bank][Offset] << 1); 

	      if (Hardware)
	        {
	          // Calculate the odd parity of the word using Brian Kernigan's bit-counting method
	          uint16_t p = 1;
	          uint16_t n = Value;

	          while (n)
	            {
	              n &= (n-1);
	              p = !p;
	            }

	          Value |= p;
	        }

	      fputc (Value >> 8, OutputFile);
	      fputc (Value, OutputFile);
	    }  
	}
    }
    
  // All done!  
  RetVal = 0;
Done:  
  //if (InputFile != NULL)
  //  fclose (InputFile);
  if (OutputFile != NULL)
    fclose (OutputFile); 
  HtmlClose ();
  if (RetVal)
    {
      printf ("USAGE:\n"
	      "\tyaYUL [OPTIONS] InputFile\n"
	      "The output (binary executable) always has the same name\n"
	      "as the assembly-language input file, except that .bin is \n"
	      "appended to it.  (E.g., Luminary.agc->Luminary.agc.bin.)\n"
	      "No relocatable object-file output format is provided,\n"
	      "because the original development team had no such\n"
	      "capability, and so there is no linker.\n\n"
	      "The assembly listing, including symbol table and any error\n"
	      "messages appear on the standard output.\n\n"
	      "OPTIONS:\n");
      printf ("--help or /?     Display this message.\n"); 
      printf ("--max-passes=n   By default, the assembler makes at most\n"
              "                 %d passes trying to resolve addresses.\n"
	      "                 This switch changes that value.\n", MaxPasses);
      printf ("--force          Force creation of core-rope image. (By\n"
              "                 default, the core-rope is not created if\n"
	      "                 there were fatal errors during assembly.\n"); 
      //printf ("--g              Output the binary symbol table to the file\n"
      //        "                 InputFile.symtab\n");
      printf ("--html           Causes an HTML file to be created, which is \n"
      	      "                 the same as the output listing except that it\n"
	      "                 if a lot more convenient to use. It has syntax\n"
	      "                 highlighting and hyperlinks from where each\n"
	      "                 symbol is used back to where it was defined.\n"
	      "                 The top-level HTML file produced is named the\n"
	      "                 same as the input source file, except with .html\n"
	      "                 replacing .s (if applicable).  Separate HTML\n"
	      "                 files are produced for all source files included\n"
	      "                 with the $ directive, and links between the files\n"
	      "                 are provided.\n");
      printf ("--unpound-page   Bypass --html processing for \"## Page\".\n");
      printf ("--block1         Assembles Block 1 code.  The default is Block 2.\n");
      printf ("--hardware       Emit binary with hardware bank order, and\n"
              "                 enable parity bit calculation\n");

    }   
  if (RetVal || Fatals)
    remove (OutputFilename);
  if (RetVal == 0)
    return (Fatals);
  else    
    return (RetVal);
}

  

back to top