Raw File
/*
  Copyright 2003,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:     IncPc.c
  Purpose:      Increments (or decrements) an Address_t structure, with 
                detection of out-of range.  This is a more complicated
                thing than it seems, because of the variety of different
                types of memory bank.  (Also, since the Address_t 
                structure can represent a constant rather than an address.)
  History:      04/15/03 RSB    Began.
                08/18/16 RSB    Some cross-my-finger-and-hope tweaks for
                                --block1.
 */

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

//-------------------------------------------------------------------------
// Increment program counter by a certain amount. 
// Sets the Overflow flag in the Address_t structure.
void IncPc(Address_t *OldPc, int Increment, Address_t *NewPc)
{
    int i, j, Max, Min, BankIncrement;

    // I have no theoretical basis for how to treat the case of Increment
    // being very large (larger than a bank size), but there is a place
    // in the Luminary source code where decrements of about 12000 (octal)
    // are used.  The lower 12 bits seem to be applied to SREG, whereas
    // the upper 3 bits seem to be applied to the bank number.
    if (Increment >= 0) {
        BankIncrement = 7 & (Increment >> 12);
        Increment &= 07777;
    } else {
        Increment = -Increment;
        BankIncrement = -(7 & (Increment >> 12));
        Increment = -(07777 & Increment);
    }

    // Get a good starting point for the calculation.
    *NewPc = *OldPc;

    // If the value is invalid---not yet assigned---then adding to it makes
    // no sense.
    if (NewPc->Invalid)
        return;

    // Okay, add to the existing Value.  This is something that ALWAYS works.
    // (Only overflowing 32-bit arithmetic could make it fail, but since
    // all possible starting values and increments are 15-bit or less,
    // overflowing 32-bit arithmetic would be a real feat!
    //NewPc->Value += Increment;

    // Convert constants (which are presumably pseudo-addresses) to real
    // addresses.
    if (NewPc->Constant) {
        // ... except we do want to check that the constant is in range.
        //if (NewPc->Value < -16383 || NewPc->Value > 32767)
        //  NewPc->Overflow = 1;
        if (NewPc->Value < 0)
            return;
        else if (NewPc->Value < 01400) {
            NewPc->Constant = 0;
            NewPc->Address = 1;
            NewPc->Erasable = 1;
            NewPc->Fixed = 0;
            NewPc->Banked = 0;
            NewPc->Unbanked = 1;
            NewPc->EB = 0;
            NewPc->FB = 0;
#ifdef YAYUL_TRACE
            printf("*** IncPc (%d): clearing superbank...\n", __LINE__);
#endif
            NewPc->Super = 0;
            NewPc->SReg = NewPc->Value;
        } else
            return;
    }

    // Okey-smokey, the "address" is really an address. If it has previously
    // overflowed, there's no particular reason to continue.
    if (NewPc->Overflow)
        return;

    // Compute the new S-register value (in the absence of overflow).
    i = (j = NewPc->SReg) + Increment;
    NewPc->SReg = i;
    if (NewPc->Erasable && NewPc->Banked)
        NewPc->EB += BankIncrement;
    else if (NewPc->Fixed && NewPc->Banked)
        NewPc->FB += BankIncrement;

    // Okay, here's some workaround code for the weird construct
    // TC FixedMemoryLabel -LargeOffset
    // taking a location in fixed memory down to a location in erasable.
    // Don't blame me!
    if (NewPc->Fixed && BankIncrement != 0 && NewPc->FB == 0) {
        NewPc->Constant = 0;
        NewPc->Address = 1;
        NewPc->Erasable = 1;
        NewPc->Fixed = 0;
        NewPc->Banked = 0;
        NewPc->Unbanked = 1;
        NewPc->EB = 0;
        NewPc->FB = 0;
#ifdef YAYUL_TRACE
        //printf("--- IncPc (%d): clearing superbank...\n", __LINE__);
#endif
        NewPc->Super = 0;
    }  

    // Determine the appropriate SReg ranges for this memory region.
    // The address will be either banked or unbanked, and either in fixed
    // memory or erasable memory.  So, there are four possible combos.
    if (NewPc->Erasable) {
        if (NewPc->Unbanked) {
            Min = 0;
            Max = Block1 ? 01777 : 01377;
        } else if (!Block1 && NewPc->Banked) {
            Min = 01400;
            Max = 01777;
            NewPc->EB += BankIncrement;
        } else
            goto ImplementationError;
    } else if (NewPc->Fixed) {
        if (NewPc->Banked) {
            Min = Block1 ? 06000 : 02000;
            Max = Block1 ? 07777 : 03777;
            NewPc->FB += BankIncrement;
        } else if (NewPc->Unbanked) {
            Min = Block1 ? 02000 : 04000;
            Max = 07777;
        } else
            goto ImplementationError;
    } else
        goto ImplementationError;

    // Did the new S-register value go out of range for the memory region?
    if (i < Min) {
        NewPc->Overflow = 1;
        NewPc->SReg = Min;
    } else if (i > Max) {
        NewPc->Overflow = 1;
        NewPc->SReg = Max;
    }  

    // Back-convert to get a pseudo-address.
    if (NewPc->Unbanked)
        NewPc->Value = NewPc->SReg;
    else if (NewPc->Erasable) {
        NewPc->Value = (NewPc->SReg - 01400) + 0400 * NewPc->EB;
    }
    else if (NewPc->Fixed) {
        NewPc->Value = 010000 + (NewPc->SReg - 02000) + 02000 * NewPc->FB;
        if (NewPc->Super && NewPc->FB >= 030)
            NewPc->Value += 010 * 02000;
    } else
        goto ImplementationError;
    return;

    ImplementationError:
    // Can't occur, but I give it a good pinch if it does!
    NewPc->Invalid = 1;
}
back to top