Revision e9fdba28bc24263664b236ea2fb026326c582d33 authored by Ron Burkey on 24 December 2017, 21:04:22 UTC, committed by Ron Burkey on 24 December 2017, 21:04:22 UTC
1 parent d7b4237
Raw File
ParseGeneral.c
/*
  Copyright 2003-2004,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:	ParseGeneral.c
  Purpose:	Assembles most opcodes opcode.
  Mode:		04/18/03 RSB	Began.
  		04/26/03 RSB	Distilled from the essences of 
				ParseTC.c et al.
		07/05/04 RSB	Added KeepExtend.
		07/21/04 RSB	The parse-behavior modification-flag definitions
				were poor, in that the overlap between QC and PC
				flags causes some address-range tests to fail.
				Added CAE, CAF.  
		07/22/04 RSB	Oops!  Broke every instruction with QC1, QC2, or 
				QC3.
		07/27/04 RSB	Moved STCALL, STODL, STORE, and STOVL over to
				ParseST.c.
		2012-09-25 JL   Handle arguments like "DUMMYJOB + 2", i.e.
		                Mod1=+, Mod2=2.
                2016-08-18 RSB  Tweaks related to Block1.
                2016-12-18 MAS  Weakened erroneous EXTEND checks when dealing
                                with explicit TC 6's, which can show up as
                                targets for an INDEX and so don't actually extend.
                2017-01-29 MAS  Added some special logic for handling Raython-
                                style literal operands.
                2017-06-28 MAS  Added BLK2's special  handling of EDRUPT.
 */

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

int KeepExtend = 0;

//------------------------------------------------------------------------
// A sort of generalized parser that works for most instruction types.
// "Normally", Flags==0. However, various fancier alternatives can be
// set by ORing together various of the constants defined above.
// The Opcode is always something of the form 0n0000 (in octal), where
// n=0,1,...,7.
int ParseGeneral(ParseInput_t *InRecord, ParseOutput_t *OutRecord, int Opcode, int Flags)
{
    ParseOutput_t dummy;
    int Value, i;
    Address_t K;
    char args[32];

    args[0] = '\0';

    if (InRecord->Mod1)
        strcpy(args, InRecord->Mod1);

    // Handle arguments like "DUMMYJOB + 2", i.e. Mod1=+, Mod2=2.
    if (InRecord->Mod2)
        strcat(args, InRecord->Mod2);

    IncPc(&InRecord->ProgramCounter, 1, &OutRecord->ProgramCounter);

    if (!OutRecord->ProgramCounter.Invalid && OutRecord->ProgramCounter.Overflow) {
        strcpy(OutRecord->ErrorMessage, "Next code may overflow storage.");
        OutRecord->Warning = 1;
    }

    OutRecord->EBank = InRecord->EBank;
    OutRecord->SBank = InRecord->SBank;

    // Set the default binary word.
    OutRecord->NumWords = 1;

    if (Block1)
      Flags = Flags & ~(PC1|PC2|PC3|PC4|PC5|PC6|PC7|QC1|QC2|QC3|QCNOT0);

    if (Flags & PC1)
        Opcode |= 01000;
    else if (Flags & PC2)
        Opcode |= 02000;
    else if (Flags & PC3)
        Opcode |= 03000;
    else if (Flags & PC4)
        Opcode |= 04000;
    else if (Flags & PC5)
        Opcode |= 05000;
    else if (Flags & PC6)
        Opcode |= 06000;
    else if (Flags & PC7)
        Opcode |= 07000;
    else if (Flags & QC1)
        Opcode |= 02000;
    else if (Flags & QC2)
        Opcode |= 04000;
    else if (Flags & QC3)
        Opcode |= 06000;

    OutRecord->Words[0] = Opcode;

    if (Flags & QCNOT0)
        OutRecord->Words[0] |= 06000;

    // Do some sanity checking.
    if (!Block1) {
      if (InRecord->Extend &&  !(Flags & EXTENDED) && !InRecord->IndexValid) {
          if (InRecord->Extend != 2) {
             // Bomb out if the extend came from an EXTEND, but not from an explicit TC 6
             strcpy(OutRecord->ErrorMessage, "Illegally preceded by EXTEND.");
             OutRecord->Fatal = 1;
          }
          OutRecord->Extend = 0;
      } else if (!InRecord->Extend && (Flags & EXTENDED)) {
          strcpy(OutRecord->ErrorMessage, "Required EXTEND is missing.");
          OutRecord->Fatal = 1;
          OutRecord->Extend = 0;
      }
    }

    if (InRecord->IndexValid) {
        OutRecord->IndexValid = 0;
    }

    i = GetOctOrDec(InRecord->Operand, &Value);
    if (!i && (Flags & ENUMBER) && *InRecord->Operand != '+' && *InRecord->Operand != '-') {
        int Offset;

        if (Raytheon && Value >= 02000 && Value < 04000) {
            // For Raytheon builds, shift common-fixed literals into the expected range
            Value += 010000 + InRecord->ProgramCounter.FB * 02000;
        }
        PseudoToStruct(Value, &K);

        if (!GetOctOrDec(args, &Offset))
            OpcodeOffset = Offset;

        goto DoIt;
    }

    if (!i) {
        if (*InRecord->Operand == '+' || *InRecord->Operand == '-')
            IncPc(&InRecord->ProgramCounter, Value, &K);
        else
            PseudoToStruct(Value, &K);

        if (*args) {
            i = GetOctOrDec(args, &Value);
            if (!i)
                OpcodeOffset = Value;
        }
        DoIt:
        if (K.Invalid) {
            strcpy(OutRecord->ErrorMessage, "Destination address not resolved.");
            OutRecord->Fatal = 1;
        } else if (K.Overflow) {
            strcpy(OutRecord->ErrorMessage, "Destination address out of range.");
            OutRecord->Fatal = 1;
        } else if (!K.Address) {
            // There are a lot of cases in which an actual numerical constant
            // is used where an address would be expected.  As a first cut at
            // addressing this, we suppose that numbers in the range 0-03777
            // may be converted straightforwardly to unswitched erasable.
            if (K.Constant)
                goto AddressFound;
            strcpy(OutRecord->ErrorMessage, "Destination not an address.");
            OutRecord->Fatal = 1;
        } else {
            AddressFound:
            // Here I had originally intended to add a check that the bank
            // numbers were compatible.  This turns out not to be generally
            // possible at assembly time, unless a bunch of analysis is
            // added to understand program flow.  However, certain checks,
            // particularly having to do with quarter-codes, can be done.
            // ... Later:  Not true for erasable banks, since the EBANK=
            // pseudo-op tells the assembler what bank is expected.  Also,
            // some constants need to be translated to erasable.
            if (K.Constant) {
                PseudoToSegmented(K.Value, &dummy);
                K = dummy.ProgramCounter;
            }

            if (Flags & KPLUS1)
                IncPc(&K, 1, &K);

            i = K.SReg;
            if (!InRecord->IndexValid) {
                // If we've been preceded by an INDEX instruction, none of the following
                // checks are valid, since the instruction may be changed to essentially
                // anything else prior to execution.
                if ((Flags & FIXED) && !K.Fixed) {
                    i &= ~07000;
                    strcpy(OutRecord->ErrorMessage, "The address is not in fixed memory.");
                    OutRecord->Fatal = 1;
                } else if ((Flags & ERASABLE) && !K.Erasable) {
                    i &= ~07600;
                    strcpy(OutRecord->ErrorMessage, "The address is not in erasable memory.");
                    OutRecord->Fatal = 1;
                }
                if (Flags & (PC0 | PC1 | PC2 | PC3 | PC4 | PC5 | PC6 | PC7)) {
                    if ((i & 07000) != 0 && (i & 07000) != (Opcode & 07000)) {
                        i &= ~07000;
                        strcpy(OutRecord->ErrorMessage, "Operand out of range.");
                        OutRecord->Fatal = 1;
                    }
                } else if (Flags & (QC0 | QC1 | QC2 | QC3)) {
                    if ((i & 06000) != 0 && (i & 06000) != (Opcode & 06000)) {
                        i &= ~06000;
                        sprintf(OutRecord->ErrorMessage, "Operand (0%o) out of range.", i);
                        OutRecord->Fatal = 1;
                    }
                } else if (Flags & QCNOT0) {
                    if (0 == (K.SReg & 06000)) {
                        i |= 06000;
                        strcpy(OutRecord->ErrorMessage, "Operand out of range.");
                        OutRecord->Fatal = 1;
                    }
                }
            }
            OutRecord->Words[0] = Opcode | i;
        }
    } else {
        // The operand is NOT a number.  Presumably, it's a symbol.
        i = FetchSymbolPlusOffset(&InRecord->ProgramCounter, InRecord->Operand, args, &K);
        if (!i)
            goto DoIt;

        sprintf(OutRecord->ErrorMessage, "Symbol \"%s\" undefined or offset bad", InRecord->Operand);
        OutRecord->Fatal = 1;
    }

    // Handle the special case `TC 6' setting the Extend bit.
    if (KeepExtend) {
        KeepExtend = 0;
        OutRecord->Extend = InRecord->Extend;
    } else {
        OutRecord->Extend = 0;
        if (Opcode == 000000 && !(Flags & EXTENDED) && !K.Invalid && (K.Constant || (K.Erasable && K.Unbanked)) && K.SReg == 06) {
            if (strlen(InRecord->Alias) == 0) {
                // This was an explicit TC 6 rather than an EXTEND. This can show up (as in Retread, for example),
                // as something that gets indexed into something other than an EXTEND, and so should be allowed through.
                // Setting Extend = 2 makes the erroneous Extend check non-fatal.
                OutRecord->Extend = 2;
            } else {
                OutRecord->Extend = 1;
            }
        } else {
            OutRecord->Extend = 0;
        }
    }
    OutRecord->IndexValid = 0;
    return (0);
}

//------------------------------------------------------------------------
// Various little parsers based on ParseGeneral.

int ParseAD(ParseInput_t *InRecord, ParseOutput_t *OutRecord)
{
    return (ParseGeneral(InRecord, OutRecord, 060000, ENUMBER));
}

int ParseADS(ParseInput_t *InRecord, ParseOutput_t *OutRecord)
{
    return (ParseGeneral(InRecord, OutRecord, 020000, QC3));
}

int ParseAUG(ParseInput_t *InRecord, ParseOutput_t *OutRecord)
{
    return (ParseGeneral(InRecord, OutRecord, 020000, EXTENDED | QC2));
}

int ParseBZMF(ParseInput_t *InRecord, ParseOutput_t *OutRecord)
{
    return (ParseGeneral(InRecord, OutRecord, 060000, EXTENDED | QCNOT0));
}

int ParseBZF(ParseInput_t *InRecord, ParseOutput_t *OutRecord)
{
    return (ParseGeneral(InRecord, OutRecord, 010000, EXTENDED | QCNOT0));
}

int ParseCA(ParseInput_t *InRecord, ParseOutput_t *OutRecord)
{
    return (ParseGeneral(InRecord, OutRecord, 030000, ENUMBER));
}

int ParseCAE(ParseInput_t *InRecord, ParseOutput_t *OutRecord)
{
    return (ParseGeneral(InRecord, OutRecord, 030000, ERASABLE | ENUMBER));
}

int ParseCAF(ParseInput_t *InRecord, ParseOutput_t *OutRecord)
{
    return (ParseGeneral(InRecord, OutRecord, 030000, FIXED | ENUMBER));
}

int ParseCCS(ParseInput_t *InRecord, ParseOutput_t *OutRecord)
{
    return (ParseGeneral(InRecord, OutRecord, 010000, QC0 | ENUMBER));
}

int ParseCS(ParseInput_t *InRecord, ParseOutput_t *OutRecord)
{
    return (ParseGeneral(InRecord, OutRecord, 040000, ENUMBER));
}

int ParseDAS(ParseInput_t *InRecord, ParseOutput_t *OutRecord)
{
    return (ParseGeneral(InRecord, OutRecord, 020000, QC0 | KPLUS1));
}

int ParseDCA(ParseInput_t *InRecord, ParseOutput_t *OutRecord)
{
    return (ParseGeneral(InRecord, OutRecord, 030000, EXTENDED | KPLUS1 | ENUMBER));
}

int ParseDCS(ParseInput_t *InRecord, ParseOutput_t *OutRecord)
{
    return (ParseGeneral(InRecord, OutRecord, 040000, EXTENDED | KPLUS1 | ENUMBER));
}

int ParseDIM(ParseInput_t *InRecord, ParseOutput_t *OutRecord)
{
    return (ParseGeneral(InRecord, OutRecord, 020000, EXTENDED | QC3));
}

int ParseDNCHAN(ParseInput_t *InRecord, ParseOutput_t *OutRecord)
{
    return (ParseGeneral(InRecord, OutRecord, 034000, PC0 | ENUMBER));
}

int ParseDV(ParseInput_t *InRecord, ParseOutput_t *OutRecord)
{
    return (ParseGeneral(InRecord, OutRecord, (Block1 ? 050000 : 010000), EXTENDED | QC0));
}

int ParseDXCH(ParseInput_t *InRecord, ParseOutput_t *OutRecord)
{
    return (ParseGeneral(InRecord, OutRecord, 050000, QC1 | KPLUS1 | ENUMBER));
}

int ParseEDRUPT(ParseInput_t *InRecord, ParseOutput_t *OutRecord)
{
    int result;

    if (blk2)
    {
        // For BLK2, EDRUPT was implied-address and always assembled
        // to 07000.
        if (*InRecord->Operand != 0)
        {
            strcpy(OutRecord->ErrorMessage,
                "Extra fields are present.");
            OutRecord->Warning = 1;
        }
        InRecord->Operand = "0";
    }

    result = (ParseGeneral(InRecord, OutRecord, 000000, EXTENDED | PC7));

    if (blk2)
    {
        InRecord->Operand = "";
    }

    return result;
}

int ParseLXCH(ParseInput_t *InRecord, ParseOutput_t *OutRecord)
{
    return (ParseGeneral(InRecord, OutRecord, 020000, QC1 | ENUMBER));
}

int ParseINCR(ParseInput_t *InRecord, ParseOutput_t *OutRecord)
{
    return (ParseGeneral(InRecord, OutRecord, 020000, QC2));
}

int ParseMASK(ParseInput_t *InRecord, ParseOutput_t *OutRecord)
{
    return (ParseGeneral(InRecord, OutRecord, 070000, 0));
}

int ParseMP(ParseInput_t *InRecord, ParseOutput_t *OutRecord)
{
    return (ParseGeneral(InRecord, OutRecord, (Block1 ? 040000 : 070000), EXTENDED));
}

int ParseMSU(ParseInput_t *InRecord, ParseOutput_t *OutRecord)
{
    return (ParseGeneral(InRecord, OutRecord, 020000, EXTENDED | QC0 | ENUMBER));
}

int ParseQXCH(ParseInput_t *InRecord, ParseOutput_t *OutRecord)
{
    return (ParseGeneral(InRecord, OutRecord, 020000, EXTENDED | QC1 | ENUMBER));
}

int ParseRAND(ParseInput_t *InRecord, ParseOutput_t *OutRecord)
{
    return (ParseGeneral(InRecord, OutRecord, 000000, EXTENDED | PC2 | ENUMBER));
}

int ParseREAD(ParseInput_t *InRecord, ParseOutput_t *OutRecord)
{
    return (ParseGeneral(InRecord, OutRecord, 000000, EXTENDED | PC0 | ENUMBER));
}

int ParseROR(ParseInput_t *InRecord, ParseOutput_t *OutRecord)
{
    return (ParseGeneral(InRecord, OutRecord, 000000, EXTENDED | PC4 | ENUMBER));
}

int ParseRXOR(ParseInput_t *InRecord, ParseOutput_t *OutRecord)
{
    return (ParseGeneral(InRecord, OutRecord, 000000, EXTENDED | PC6 | ENUMBER));
}

int ParseSU(ParseInput_t *InRecord, ParseOutput_t *OutRecord)
{
    return (ParseGeneral(InRecord, OutRecord, 060000, EXTENDED | QC0 | ENUMBER));
}

int ParseTC(ParseInput_t *InRecord, ParseOutput_t *OutRecord)
{
    return (ParseGeneral(InRecord, OutRecord, 000000, ENUMBER));
}

int ParseTCF(ParseInput_t *InRecord, ParseOutput_t *OutRecord)
{
    if (*InRecord->Operand == 0) {
        /* Handle a TCF with no operand, which translates to TCF +0, i.e. NOOP. */
        InRecord->Operand = "+0";
    }
    return (ParseGeneral(InRecord, OutRecord, 010000, QCNOT0 | ENUMBER));
}

int ParseTS(ParseInput_t *InRecord, ParseOutput_t *OutRecord)
{
    return (ParseGeneral(InRecord, OutRecord, 050000, QC2 | ENUMBER));
}

int ParseWAND(ParseInput_t *InRecord, ParseOutput_t *OutRecord)
{
    return (ParseGeneral(InRecord, OutRecord, 000000, EXTENDED | PC3 | ENUMBER));
}

int ParseWOR(ParseInput_t *InRecord, ParseOutput_t *OutRecord)
{
    return (ParseGeneral(InRecord, OutRecord, 000000, EXTENDED | PC5 | ENUMBER));
}

int ParseWRITE(ParseInput_t *InRecord, ParseOutput_t *OutRecord)
{
    return (ParseGeneral(InRecord, OutRecord, 000000, EXTENDED | PC1 | ENUMBER));
}

int ParseXCH(ParseInput_t *InRecord, ParseOutput_t *OutRecord)
{
    return (ParseGeneral(InRecord, OutRecord, (Block1 ? 030000 : 050000), QC3 | ENUMBER));
}

back to top