/*****************************************************************************/
/*                                                                           */
/*                               codeoptutil.c                               */
/*                                                                           */
/*           Optimize operations that take operands via the stack            */
/*                                                                           */
/*                                                                           */
/*                                                                           */
/* (C) 2001      Ullrich von Bassewitz                                       */
/*               Wacholderweg 14                                             */
/*               D-70597 Stuttgart                                           */
/* EMail:        uz@cc65.org                                                 */
/*                                                                           */
/*                                                                           */
/* This software is provided 'as-is', without any expressed or implied       */
/* warranty.  In no event will the authors be held liable for any damages    */
/* arising from the use of this software.                                    */
/*                                                                           */
/* Permission is granted to anyone to use this software for any purpose,     */
/* including commercial applications, and to alter it and redistribute it    */
/* freely, subject to the following restrictions:                            */
/*                                                                           */
/* 1. The origin of this software must not be misrepresented; you must not   */
/*    claim that you wrote the original software. If you use this software   */
/*    in a product, an acknowledgment in the product documentation would be  */
/*    appreciated but is not required.                                       */
/* 2. Altered source versions must be plainly marked as such, and must not   */
/*    be misrepresented as being the original software.                      */
/* 3. This notice may not be removed or altered from any source              */
/*    distribution.                                                          */
/*                                                                           */
/*****************************************************************************/



#include <stdlib.h>

/* common */
#include "chartype.h"
#include "xmalloc.h"

/* cc65 */
#include "codeinfo.h"
#include "codeoptutil.h"
#include "error.h"



/*****************************************************************************/
/*                            Load tracking code                             */
/*****************************************************************************/



void ClearLoadRegInfo (LoadRegInfo* LRI)
/* Clear a LoadRegInfo struct */
{
    LRI->Flags      = LI_NONE;
    LRI->LoadIndex  = -1;
    LRI->LoadEntry  = 0;
    LRI->LoadYIndex = -1;
    LRI->LoadYEntry = 0;
    LRI->ChgIndex   = -1;
    LRI->ChgEntry   = 0;
    LRI->Offs       = 0;
}



void CopyLoadRegInfo (LoadRegInfo* To, LoadRegInfo* From)
/* Copy a LoadRegInfo struct */
{
    To->Flags      = From->Flags;
    To->LoadIndex  = From->LoadIndex;
    To->LoadEntry  = From->LoadEntry;
    To->LoadYIndex = From->LoadYIndex;
    To->LoadYEntry = From->LoadYEntry;
    To->ChgIndex   = From->ChgIndex;
    To->ChgEntry   = From->ChgEntry;
    To->Offs       = From->Offs;
}



void FinalizeLoadRegInfo (LoadRegInfo* LRI, CodeSeg* S)
/* Prepare a LoadRegInfo struct for use */
{
    /* Get the entries */
    if (LRI->LoadIndex >= 0) {
        LRI->LoadEntry = CS_GetEntry (S, LRI->LoadIndex);
    } else {
        LRI->LoadEntry = 0;
    }
    if (LRI->LoadYIndex >= 0) {
        LRI->LoadYEntry = CS_GetEntry (S, LRI->LoadYIndex);
    } else {
        LRI->LoadYEntry = 0;
    }
    if (LRI->ChgIndex >= 0) {
        LRI->ChgEntry = CS_GetEntry (S, LRI->ChgIndex);
    } else {
        LRI->ChgEntry = 0;
    }

    /* Load from src not modified before op can be treated as direct */
    if ((LRI->Flags & (LI_SRC_CHG | LI_Y_SRC_CHG)) == 0 &&
        (LRI->Flags & (LI_CHECK_ARG | LI_CHECK_Y)) != 0) {
        LRI->Flags |= LI_DIRECT;
        if ((LRI->Flags & LI_CHECK_Y) != 0) {
            LRI->Flags |= LI_RELOAD_Y;
        }
    }
    /* We cannot ldy src,y or reload unknown Y */
    if ((LRI->Flags & (LI_CHECK_Y | LI_RELOAD_Y)) == (LI_CHECK_Y | LI_RELOAD_Y) &&
        (LRI->LoadYEntry == 0 ||
         (LRI->LoadYEntry->Use & REG_Y) == REG_Y)) {
        LRI->Flags &= ~LI_DIRECT;
    }
}



void AdjustLoadRegInfo (LoadRegInfo* LRI, int Index, int Change)
/* Adjust a load register info struct after deleting or inserting an entry
** with a given index
*/
{
    CHECK (abs (Change) == 1);
    if (Change < 0) {
        /* Deletion */
        if (Index < LRI->LoadIndex) {
            --LRI->LoadIndex;
        } else if (Index == LRI->LoadIndex) {
            /* Has been removed */
            LRI->LoadIndex = -1;
            LRI->LoadEntry = 0;
        }
        if (Index < LRI->LoadYIndex) {
            --LRI->LoadIndex;
        } else if (Index == LRI->LoadYIndex) {
            /* Has been removed */
            LRI->LoadYIndex = -1;
            LRI->LoadYEntry = 0;
        }
        if (Index < LRI->ChgIndex) {
            --LRI->ChgIndex;
        } else if (Index == LRI->ChgIndex) {
            /* Has been removed */
            LRI->ChgIndex = -1;
            LRI->ChgEntry = 0;
        }
    } else {
        /* Insertion */
        if (Index <= LRI->LoadIndex) {
            ++LRI->LoadIndex;
        }
        if (Index <= LRI->LoadYIndex) {
            ++LRI->LoadYIndex;
        }
        if (Index <= LRI->ChgIndex) {
            ++LRI->ChgIndex;
        }
    }
}



void ClearLoadInfo (LoadInfo* LI)
/* Clear a LoadInfo struct */
{
    ClearLoadRegInfo (&LI->A);
    ClearLoadRegInfo (&LI->X);
    ClearLoadRegInfo (&LI->Y);
}



void CopyLoadInfo (LoadInfo* To, LoadInfo* From)
/* Copy a LoadInfo struct */
{
    CopyLoadRegInfo (&To->A, &From->A);
    CopyLoadRegInfo (&To->X, &From->X);
    CopyLoadRegInfo (&To->Y, &From->Y);
}



void FinalizeLoadInfo (LoadInfo* LI, CodeSeg* S)
/* Prepare a LoadInfo struct for use */
{
    /* Get the entries */
    FinalizeLoadRegInfo (&LI->A, S);
    FinalizeLoadRegInfo (&LI->X, S);
    FinalizeLoadRegInfo (&LI->Y, S);
}



void AdjustLoadInfo (LoadInfo* LI, int Index, int Change)
/* Adjust a load info struct after deleting entry with a given index */
{
    AdjustLoadRegInfo (&LI->A, Index, Change);
    AdjustLoadRegInfo (&LI->X, Index, Change);
    AdjustLoadRegInfo (&LI->Y, Index, Change);
}



RegInfo* GetLastChangedRegInfo (StackOpData* D, LoadRegInfo* Reg)
/* Get RegInfo of the last insn entry that changed the reg */
{
    CodeEntry* E;

    if (Reg->ChgIndex >= 0 && (E = CS_GetEntry (D->Code, Reg->ChgIndex)) != 0) {
        return E->RI;
    }

    return 0;
}



static int Affected (LoadRegInfo* LRI, const CodeEntry* E)
/* Check if the result of the same loading code as in LRI may be changed by E.
** If any part of the arg is used, it could be unsafe to add such a store before E.
** If any part of the arg is changed, it could be unsafe to add such a load after E.
*/
{
    fncls_t         fncls;
    unsigned int    Use;
    unsigned int    Chg;
    unsigned int    UseToCheck = 0;
    unsigned int    ChgToCheck = 0;
    const ZPInfo*   ZI  = 0;
    unsigned        Res = 0;
    CodeEntry*      AE  = 0;
    CodeEntry*      YE  = 0;

    if ((LRI->Flags & (LI_CHECK_ARG | LI_CHECK_Y | LI_RELOAD_Y)) == 0) {
        /* Nothing to check */
        return 0;
    }

    if (E->AM == AM65_ACC || E->AM == AM65_BRA || E->AM == AM65_IMM || E->AM == AM65_IMP) {
        goto L_Result;
    }
    CHECK ((LRI->Flags & LI_CHECK_ARG) == 0 || LRI->LoadIndex < 0 || LRI->LoadEntry != 0);
    CHECK ((LRI->Flags & (LI_CHECK_Y | LI_RELOAD_Y)) == 0 || LRI->LoadYIndex < 0 || LRI->LoadYEntry != 0);

    if ((LRI->Flags & LI_CHECK_ARG) != 0) {
        AE = LRI->LoadEntry;
        if (AE != 0) {
            /* We ignore processor flags for loading args.
            ** Further more, Reg A can't be used as the index.
            */
            UseToCheck |= AE->Use & ~REG_A & REG_ALL;
            ChgToCheck |= AE->Chg & ~REG_A & REG_ALL;

            /* Check if the argument has been parsed successfully */
            if (!CE_IsArgStrParsed (AE)) {
                /* Bail out and play it safe*/
                goto L_Affected;
            }
            /* We have to manually set up the use/chg flags for builtin functions */
            ZI = GetZPInfo (AE->ArgBase);
            if (ZI != 0) {
                UseToCheck |= ZI->ByteUse;
                ChgToCheck |= ZI->ByteUse;
            }
        } else {
            /* We don't know what regs could have been used for the src.
            ** So we just assume all.
            */
            UseToCheck |= ~REG_A & REG_ALL;
            ChgToCheck |= ~REG_A & REG_ALL;
        }
    }

    if ((LRI->Flags & LI_CHECK_Y) != 0) {
        YE = LRI->LoadYEntry;
        if (YE != 0) {
            UseToCheck |= YE->Use;

            /* Check if the argument has been parsed successfully */
            if (!CE_IsArgStrParsed (YE)) {
                /* Bail out and play it safe*/
                goto L_Affected;
            }
            /* We have to manually set up the use/chg flags for builtin functions */
            ZI = GetZPInfo (YE->ArgBase);
            if (ZI != 0) {
                UseToCheck |= ZI->ByteUse;
                ChgToCheck |= ZI->ByteUse;
            }
        } else {
            /* We don't know what regs could have been used by Y.
            ** So we just assume all.
            */
            UseToCheck |= ~REG_A & REG_ALL;
            ChgToCheck |= ~REG_A & REG_ALL;
        }
    }

    if (E->OPC == OP65_JSR) {
        /* Try to know about the function */
        fncls = GetFuncInfo (E->Arg, &Use, &Chg);
        if (fncls == FNCLS_BUILTIN) {
            /* Builtin functions are usually harmless */
            if ((ChgToCheck & Use & REG_ALL) != 0) {
                Res |= LI_SRC_USE;
            }
            if ((UseToCheck & Chg & REG_ALL) != 0) {
                Res |= LI_SRC_CHG;
            }
            goto L_Result;
        }
        /* Otherwise play it safe */
        goto L_Affected;

    } else {
        if ((E->Info & (OF_READ | OF_WRITE)) != 0) {

            /* Check if the argument has been parsed successfully */
            if (!CE_IsArgStrParsed (E)) {
                /* Bail out and play it safe*/
                goto L_Affected;
            }

            /* These opc may operate on memory locations. In some cases we can
            ** be sure that the src is unaffected as E doesn't overlap with it.
            ** However, if we don't know what memory locations could have been
            ** used for the src, we just assume all.
            */
            if (E->AM == AM65_ABS       ||
                E->AM == AM65_ZP        ||
                (E->AM == AM65_ZP_INDY && strcmp (E->ArgBase, "sp") == 0)
                ) {
                if ((LRI->Flags & LI_CHECK_ARG) != 0) {
                    if (AE == 0                             ||
                        (AE->AM != AM65_ABS &&
                         AE->AM != AM65_ZP  &&
                         (AE->AM != AM65_ZP_INDY ||
                          strcmp (AE->ArgBase, "sp") != 0)) ||
                         (AE->ArgOff == E->ArgOff &&
                          strcmp (AE->ArgBase, E->ArgBase) == 0)) {

                        if ((E->Info & OF_READ) != 0) {
                            /* Used */
                            Res |= LI_SRC_USE;
                        }
                        if ((E->Info & OF_WRITE) != 0) {
                            /* Changed */
                            Res |= LI_SRC_CHG;
                        }
                    }
                }

                if ((LRI->Flags & LI_CHECK_Y) != 0) {
                    /* If we don't know what memory location could have been
                    ** used by Y, we just assume all.
                    */
                    if (YE == 0 ||
                        (YE->ArgOff == E->ArgOff && strcmp (YE->ArgBase, E->ArgBase) == 0)) {

                        if ((E->Info & OF_READ) != 0) {
                            /* Used */
                            Res |= LI_Y_SRC_USE;
                        }
                        if ((E->Info & OF_WRITE) != 0) {
                            /* Changed */
                            Res |= LI_Y_SRC_CHG;
                        }
                    }
                }

                /* Otherwise unaffected */
                goto L_Result;
            }
            /* We could've check further for more cases where the load target
            ** isn't modified, but for now let's save the trouble and just play
            ** it safe.
            */
            goto L_Affected;
        }
    }

L_Affected:
    if ((E->Info & OF_READ) != 0) {
        /* Used */
        Res |= LI_SRC_USE;
        if ((LRI->Flags & LI_CHECK_Y) != 0) {
            Res |= LI_Y_SRC_USE;
        }
    }
    if ((E->Info & OF_WRITE) != 0) {
        /* Changed */
        Res |= LI_SRC_CHG;
        if ((LRI->Flags & LI_CHECK_Y) != 0) {
            Res |= LI_Y_SRC_CHG;
        }
    }

L_Result:
    if ((LRI->Flags & LI_RELOAD_Y) != 0 &&
        (E->Use & REG_Y) != 0) {
        Res |= LI_Y_USE;
    }
    if ((LRI->Flags & LI_CHECK_Y) != 0 &&
        (E->Chg & REG_Y) != 0) {
        Res |= LI_Y_CHG;
    }

    return Res;
}



static void HonourUseAndChg (LoadRegInfo* LRI, unsigned Reg, const CodeEntry* E, int I)
/* Honour use and change flags for an instruction */
{
    if ((E->Chg & Reg) != 0) {
        /* This changes the content of the reg */
        ClearLoadRegInfo (LRI);
        LRI->ChgIndex = I;
        LRI->Flags = 0;
    } else {
        LRI->Flags |= Affected (LRI, E);
    }
}



void PrepairLoadRegInfoForArgCheck (CodeSeg* S, LoadRegInfo* LRI, CodeEntry* E)
/* Set the load src flags and remember to check for load src change if necessary.
** Note: this doesn't assume reloading Y.
*/
{
    if (E->AM == AM65_IMM) {
        /* These insns are all ok and replaceable */
        LRI->Flags |= LI_DIRECT;
    } else if (E->AM == AM65_ZP || E->AM == AM65_ABS) {
        /* These insns are replaceable only if they are not modified later */
        LRI->Flags |= LI_CHECK_ARG;
    } else if (E->AM == AM65_ZPY || E->AM == AM65_ABSY) {
        /* These insns are replaceable only if they are not modified later */
        LRI->Flags |= LI_CHECK_ARG | LI_CHECK_Y;
    } else if ((E->AM == AM65_ZP_INDY) &&
                strcmp (E->Arg, "sp") == 0) {
        /* A load from the stack with known offset is also ok, but in this
        ** case we must reload the index register later. Please note that
        ** a load indirect via other zero page locations is not ok, since
        ** these locations may change between the push and the actual
        ** operation.
        */
        LRI->Flags |= LI_CHECK_ARG | LI_CHECK_Y;
    }

    /* If the load offset has a known value, we can just remember and reload
    ** it into the index register later.
    */
    if ((LRI->Flags & LI_CHECK_Y) != 0) {
        if (RegValIsKnown (E->RI->In.RegY)) {
            LRI->Offs = (unsigned char)E->RI->In.RegY;
            LRI->Flags &= ~LI_CHECK_Y;
            LRI->Flags |= LI_RELOAD_Y;
        }
    }

    /* Watch for any change of the load target */
    if ((LRI->Flags & LI_CHECK_ARG) != 0) {
        LRI->LoadIndex = CS_GetEntryIndex (S, E);
        LRI->LoadEntry = E;
    }

    /* We need to check if the src of Y is changed */
    if (LRI->LoadYIndex >= 0) {
        LRI->LoadYEntry = CS_GetEntry (S, LRI->LoadYIndex);
    } else {
        LRI->LoadYEntry = 0;
    }
}



void SetIfOperandSrcAffected (LoadInfo* LLI, CodeEntry* E)
/* Check and flag operand src that may be affected */
{
    LLI->A.Flags |= Affected (&LLI->A, E);
    LLI->X.Flags |= Affected (&LLI->X, E);
    LLI->Y.Flags |= Affected (&LLI->Y, E);
}



void SetIfOperandLoadUnremovable (LoadInfo* LI, unsigned Used)
/* Check and flag operand load that may be unremovable */
{
    /* Disallow removing the loads if the registers are used */
    if ((Used & REG_A) != 0) {
        LI->A.Flags |= LI_DONT_REMOVE;
    }
    if ((Used & REG_X) != 0) {
        LI->X.Flags |= LI_DONT_REMOVE;
    }
    if ((Used & REG_Y) != 0) {
        LI->Y.Flags |= LI_DONT_REMOVE;
    }
}



unsigned int TrackLoads (LoadInfo* LI, CodeSeg* S, int I)
/* Track loads for a code entry.
** Return used registers.
*/
{
    unsigned Used;
    CodeEntry* E = CS_GetEntry (S, I);
    CHECK (E != 0);

    /* By default */
    Used = E->Use;

    /* Whether we had a load or xfer op before or not, the newly loaded value
    ** will be the real one used for the pushax/op unless it's overwritten,
    ** so we can just reset the flags about it in such cases.
    */
    if (E->Info & OF_LOAD) {

        LoadRegInfo* LRI = 0;

        /* Determine, which register was loaded */
        if (E->Chg & REG_A) {
            LRI = &LI->A;
        } else if (E->Chg & REG_X) {
            LRI = &LI->X;
        } else if (E->Chg & REG_Y) {
            LRI = &LI->Y;
        }
        CHECK (LRI != 0);

        /* Remember the load */
        LRI->LoadIndex  = I;
        LRI->ChgIndex   = I;
        LRI->LoadYIndex = -1;

        /* Set load flags */
        LRI->Flags = LI_LOAD_INSN;
        if (E->AM == AM65_IMM) {
            /* These insns are all ok and replaceable */
            LRI->Flags |= LI_DIRECT;
        } else if (E->AM == AM65_ZP || E->AM == AM65_ABS) {
            /* These insns are replaceable only if they are not modified later */
            LRI->Flags |= LI_CHECK_ARG;
        } else if (E->AM == AM65_ZPY || E->AM == AM65_ABSY) {
            /* These insns are replaceable only if they are not modified later */
            LRI->Flags |= LI_CHECK_ARG | LI_CHECK_Y;
        } else if (E->AM == AM65_ZP_INDY &&
                   strcmp (E->Arg, "sp") == 0) {
            /* A load from the stack with known offset is also ok, but in this
            ** case we must reload the index register later. Please note that
            ** a load indirect via other zero page locations is not ok, since
            ** these locations may change between the push and the actual
            ** operation.
            */
            LRI->Flags |= LI_CHECK_ARG | LI_CHECK_Y | LI_SP;

            /* Reg Y can be regarded as unused if this load is removed */
            Used &= ~REG_Y;
            if (LRI == &LI->A) {
                LI->Y.Flags |= LI_USED_BY_A;
            } else {
                LI->Y.Flags |= LI_USED_BY_X;
            }
        }

        /* If the load offset has a known value, we can just remember and reload
        ** it into the index register later.
        */
        if ((LRI->Flags & LI_CHECK_Y) != 0) {
            if (RegValIsKnown (E->RI->In.RegY)) {
                LRI->Offs = (unsigned char)E->RI->In.RegY;
                LRI->Flags &= ~LI_CHECK_Y;
                LRI->Flags |= LI_RELOAD_Y;
            } else {
                /* We need to check if the src of Y is changed */
                LRI->LoadYIndex = LI->Y.LoadIndex;
            }
        }

        /* Watch for any change of the load target */
        if ((LRI->Flags & LI_CHECK_ARG) != 0) {
            LRI->LoadEntry = CS_GetEntry (S, I);
        }

        if (LRI->LoadYIndex >= 0) {
            LRI->LoadYEntry = CS_GetEntry (S, LRI->LoadYIndex);
        } else {
            LRI->LoadYEntry = 0;
        }

    } else if (E->Info & OF_XFR) {

        /* Determine source and target of the transfer and handle the TSX insn */
        LoadRegInfo* Src;
        LoadRegInfo* Tgt;
        switch (E->OPC) {
            case OP65_TAX:
                Src = &LI->A;
                Tgt = &LI->X;
                Used &= ~REG_A;
                Src->Flags |= LI_USED_BY_X;
                break;
            case OP65_TAY:
                Src = &LI->A;
                Tgt = &LI->Y;
                Used &= ~REG_A;
                Src->Flags |= LI_USED_BY_Y;
                break;
            case OP65_TXA:
                Src = &LI->X;
                Tgt = &LI->A;
                Used &= ~REG_X;
                Src->Flags |= LI_USED_BY_A;
                break;
            case OP65_TYA:
                Src = &LI->Y;
                Tgt = &LI->A;
                Used &= ~REG_Y;
                Src->Flags |= LI_USED_BY_A;
                break;
            case OP65_TSX:
                ClearLoadRegInfo (&LI->X);
                return Used;
            case OP65_TXS:
                return Used;
            default:            Internal ("Unknown XFR insn in TrackLoads");
        }

        /* Transfer the data */
        Tgt->LoadIndex  = Src->LoadIndex;
        Tgt->LoadEntry  = Src->LoadEntry;
        Tgt->LoadYIndex = Src->LoadYIndex;
        Tgt->LoadYEntry = Src->LoadYEntry;
        Tgt->ChgIndex   = I;
        Tgt->Offs       = Src->Offs;
        Tgt->Flags      = Src->Flags;

    } else if (CE_IsCallTo (E, "ldaxysp") && RegValIsKnown (E->RI->In.RegY)) {

        /* Both registers set, Y changed */
        LI->A.LoadIndex = I;
        LI->A.ChgIndex  = I;
        LI->A.Flags     = (LI_LOAD_INSN | LI_DIRECT | LI_RELOAD_Y | LI_SP);
        LI->A.Offs      = (unsigned char) E->RI->In.RegY - 1;

        LI->X.LoadIndex = I;
        LI->X.ChgIndex  = I;
        LI->X.Flags     = (LI_LOAD_INSN | LI_DIRECT | LI_RELOAD_Y | LI_SP);
        LI->X.Offs      = (unsigned char) E->RI->In.RegY;

        /* Reg Y can be regarded as unused if this load is removed */
        Used &= ~REG_Y;
        LI->Y.Flags |= LI_USED_BY_A | LI_USED_BY_X;

    } else {
        HonourUseAndChg (&LI->A, REG_A, E, I);
        HonourUseAndChg (&LI->X, REG_X, E, I);
        HonourUseAndChg (&LI->Y, REG_Y, E, I);
    }

    return Used;
}



void SetDontRemoveEntryFlag (LoadRegInfo* LRI)
/* Flag the entry as non-removable according to register flags */
{
    if (LRI->Flags & LI_DONT_REMOVE) {
        if (LRI->LoadEntry != 0) {
            LRI->LoadEntry->Flags |= CEF_DONT_REMOVE;

            /* If the load requires Y, then Y shouldn't be removed either */
            if (LRI->LoadYEntry != 0) {
                LRI->LoadYEntry->Flags |= CEF_DONT_REMOVE;
            }
        }
    }
}



void ResetDontRemoveEntryFlag (LoadRegInfo* LRI)
/* Unflag the entry as non-removable according to register flags */
{
    if (LRI->LoadEntry != 0) {
        LRI->LoadEntry->Flags &= ~CEF_DONT_REMOVE;
    }

    if (LRI->LoadYEntry != 0) {
        LRI->LoadYEntry->Flags &= ~CEF_DONT_REMOVE;
    }

    if (LRI->ChgEntry != 0) {
        LRI->ChgEntry->Flags &= ~CEF_DONT_REMOVE;
    }
}



void SetDontRemoveEntryFlags (StackOpData* D)
/* Flag the entries as non-removable according to register flags */
{
    SetDontRemoveEntryFlag (&D->Lhs.A);
    SetDontRemoveEntryFlag (&D->Lhs.X);
    SetDontRemoveEntryFlag (&D->Lhs.Y);
    SetDontRemoveEntryFlag (&D->Rhs.A);
    SetDontRemoveEntryFlag (&D->Rhs.X);
    SetDontRemoveEntryFlag (&D->Rhs.Y);
    SetDontRemoveEntryFlag (&D->Rv.A);
    SetDontRemoveEntryFlag (&D->Rv.X);
    SetDontRemoveEntryFlag (&D->Rv.Y);
}



void ResetDontRemoveEntryFlags (StackOpData* D)
/* Unflag the entries as non-removable according to register flags */
{
    ResetDontRemoveEntryFlag (&D->Lhs.A);
    ResetDontRemoveEntryFlag (&D->Lhs.X);
    ResetDontRemoveEntryFlag (&D->Lhs.Y);
    ResetDontRemoveEntryFlag (&D->Rhs.A);
    ResetDontRemoveEntryFlag (&D->Rhs.X);
    ResetDontRemoveEntryFlag (&D->Rhs.Y);
    ResetDontRemoveEntryFlag (&D->Rv.A);
    ResetDontRemoveEntryFlag (&D->Rv.X);
    ResetDontRemoveEntryFlag (&D->Rv.Y);
}



void ResetStackOpData (StackOpData* Data)
/* Reset the given data structure */
{
    Data->OptFunc       = 0;
    Data->ZPUsage       = REG_NONE;
    Data->ZPChanged     = REG_NONE;
    Data->UsedRegs      = REG_NONE;
    Data->RhsMultiChg   = 0;

    ClearLoadInfo (&Data->Lhs);
    ClearLoadInfo (&Data->Rhs);
    ClearLoadInfo (&Data->Rv);

    Data->PushIndex     = -1;
    Data->OpIndex       = -1;
}



/*****************************************************************************/
/*                                  Helpers                                  */
/*****************************************************************************/



void InsertEntry (StackOpData* D, CodeEntry* E, int Index)
/* Insert a new entry. Depending on Index, D->PushIndex and D->OpIndex will
** be adjusted by this function.
*/
{
    /* Insert the entry into the code segment */
    CS_InsertEntry (D->Code, E, Index);

    /* Adjust register loads if necessary */
    AdjustLoadInfo (&D->Lhs, Index, 1);
    AdjustLoadInfo (&D->Rhs, Index, 1);

    /* Adjust the indices if necessary */
    if (D->PushEntry && Index <= D->PushIndex) {
        ++D->PushIndex;
    }
    if (D->OpEntry && Index <= D->OpIndex) {
        ++D->OpIndex;
    }
}



void DelEntry (StackOpData* D, int Index)
/* Delete an entry. Depending on Index, D->PushIndex and D->OpIndex will be
** adjusted by this function, and PushEntry/OpEntry may get invalidated.
*/
{
    /* Delete the entry from the code segment */
    CS_DelEntry (D->Code, Index);

    /* Adjust register loads if necessary */
    AdjustLoadInfo (&D->Lhs, Index, -1);
    AdjustLoadInfo (&D->Rhs, Index, -1);

    /* Adjust the other indices if necessary */
    if (Index < D->PushIndex) {
        --D->PushIndex;
    } else if (Index == D->PushIndex) {
        D->PushEntry = 0;
    }
    if (Index < D->OpIndex) {
        --D->OpIndex;
    } else if (Index == D->OpIndex) {
        D->OpEntry = 0;
    }
}



void AdjustStackOffset (StackOpData* D, unsigned Offs)
/* Adjust the offset for all stack accesses in the range PushIndex to OpIndex.
** OpIndex is adjusted according to the insertions.
*/
{
    /* Walk over all entries */
    int I = D->PushIndex + 1;
    while (I < D->OpIndex) {

        CodeEntry* E = CS_GetEntry (D->Code, I);

        /* Check against some things that should not happen */
        CHECK ((E->Use & SLV_TOP) != SLV_TOP);

        /* Check if this entry does a stack access, and if so, if it's a plain
        ** load from stack, since this is needed later.
        */
        int Correction = 0;
        if ((E->Use & SLV_IND) == SLV_IND) {

            if (E->OPC != OP65_JSR) {
                /* Check against some things that should not happen */
                CHECK (E->AM == AM65_ZP_INDY && E->RI->In.RegY >= (short) Offs);
                CHECK (strcmp (E->Arg, "sp") == 0);

                /* We need to correct this one */
                Correction = 2;

            } else {
                /* We need to correct this one */
                Correction = 1;
            }

        }

        if (Correction) {
            /* Get the code entry before this one. If it's a LDY, adjust the
            ** value.
            */
            CodeEntry* P = CS_GetPrevEntry (D->Code, I);
            if (P && P->OPC == OP65_LDY && CE_IsConstImm (P) && !CE_HasLabel (E)) {
                /* The Y load is just before the stack access, adjust it */
                CE_SetNumArg (P, P->Num - Offs);
            } else {
                /* Insert a new load instruction before the stack access */
                const char* Arg = MakeHexArg (E->RI->In.RegY - Offs);
                CodeEntry* X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
                InsertEntry (D, X, I++);
            }

            /* If we need the value of Y later, be sure to reload it */
            unsigned R = REG_Y | (E->Chg & ~REG_A);
            R = GetRegInfo (D->Code, I + 1, R) & R;
            if ((R & REG_Y) != 0) {
                const char* Arg = MakeHexArg (E->RI->In.RegY);
                if ((R & PSTATE_ZN) != 0 && (R & ~(REG_Y | PSTATE_ZN)) == 0) {
                    CodeEntry* N;
                    if ((N = CS_GetNextEntry (D->Code, I)) != 0 &&
                        ((N->Info & OF_ZBRA) != 0) && N->JumpTo != 0) {
                        /* The Y register is used but the load instruction loads A
                        ** and is followed by a branch that evaluates the zero flag.
                        ** This means that we cannot just insert the load insn
                        ** for the Y register at this place, because it would
                        ** destroy the Z flag. Instead place load insns at the
                        ** target of the branch and after it.
                        ** Note: There is a chance that this code won't work. The
                        ** jump may be a backwards jump (in which case the stack
                        ** offset has already been adjusted) or there may be other
                        ** instructions between the load and the conditional jump.
                        ** Currently the compiler does not generate such code, but
                        ** it is possible to force the optimizer into something
                        ** invalid by use of inline assembler.
                        ** Note: In reality, this route is never taken as all
                        ** callers of this function will just give up with
                        ** optimization whenever they detect a branch.
                        */

                        /* Add load insn after the branch */
                        CodeEntry* X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
                        InsertEntry (D, X, I+2);

                        /* Add load insn before branch target */
                        CodeEntry* Y = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
                        int J = CS_GetEntryIndex (D->Code, N->JumpTo->Owner);
                        CHECK (J > I);      /* Must not happen */
                        InsertEntry (D, Y, J);

                        /* Move the label to the new insn */
                        CodeLabel* L = CS_GenLabel (D->Code, Y);
                        CS_MoveLabelRef (D->Code, N, L);

                        /* Skip the next two instructions in the next round */
                        I += 2;
                    } else {
                        /* This could be suboptimal but it will always work (unless stack overflows) */
                        CodeEntry* X = NewCodeEntry (OP65_PHP, AM65_IMP, 0, 0, E->LI);
                        InsertEntry (D, X, I+1);
                        X = NewCodeEntry (OP65_LDY, AM65_IMM, 0, 0, E->LI);
                        InsertEntry (D, X, I+2);
                        X = NewCodeEntry (OP65_PLP, AM65_IMP, 0, 0, E->LI);
                        InsertEntry (D, X, I+3);
                        /* Skip the three inserted instructions in the next round */
                        I += 3;
                    }
                } else {
                    CodeEntry* X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
                    InsertEntry (D, X, I+1);
                    /* Skip this instruction in the next round */
                    ++I;
                }
            }
        }

        /* Next entry */
        ++I;
    }

    /* If we have rhs load insns that load from stack, we'll have to adjust
    ** the offsets for these also.
    */
    if ((D->Rhs.A.Flags & (LI_RELOAD_Y | LI_SP | LI_CHECK_Y)) == (LI_RELOAD_Y | LI_SP)) {
        D->Rhs.A.Offs -= Offs;
    }
    if ((D->Rhs.X.Flags & (LI_RELOAD_Y | LI_SP | LI_CHECK_Y)) == (LI_RELOAD_Y | LI_SP)) {
        D->Rhs.X.Offs -= Offs;
    }
}



int IsRegVar (StackOpData* D)
/* If the value pushed is that of a zeropage variable that is unchanged until Op,
** replace ZPLo and ZPHi in the given StackOpData struct by the variable and return true.
** Otherwise leave D untouched and return false.
*/
{
    CodeEntry*  LoadA = D->Lhs.A.LoadEntry;
    CodeEntry*  LoadX = D->Lhs.X.LoadEntry;
    unsigned    Len;

    /* Must be unchanged till Op */
    if ((D->Lhs.A.Flags & (LI_DIRECT | LI_RELOAD_Y)) != LI_DIRECT ||
        (D->Lhs.X.Flags & (LI_DIRECT | LI_RELOAD_Y)) != LI_DIRECT) {
        return 0;
    }

    /* Must have both load insns */
    if (LoadA == 0 || LoadX == 0) {
        return 0;
    }

    /* Must be loads from zp */
    if (LoadA->AM != AM65_ZP || LoadX->AM != AM65_ZP) {
        return 0;
    }

    /* Must be the same zp loc with high byte in X */
    Len = strlen (LoadA->Arg);
    if (strncmp (LoadA->Arg, LoadX->Arg, Len) != 0      ||
        strcmp (LoadX->Arg + Len, "+1") != 0) {
        return 0;
    }

    /* Use the zero page location directly */
    D->ZPLo = LoadA->Arg;
    D->ZPHi = LoadX->Arg;
    return 1;
}



void AddStoreLhsA (StackOpData* D)
/* Add a store to zero page after the push insn */
{
    CodeEntry* X = NewCodeEntry (OP65_STA, AM65_ZP, D->ZPLo, 0, D->PushEntry->LI);
    InsertEntry (D, X, D->PushIndex+1);
}



void AddStoreLhsX (StackOpData* D)
/* Add a store to zero page after the push insn */
{
    CodeEntry* X = NewCodeEntry (OP65_STX, AM65_ZP, D->ZPHi, 0, D->PushEntry->LI);
    InsertEntry (D, X, D->PushIndex+1);
}



void ReplacePushByStore (StackOpData* D)
/* Replace the call to the push subroutine by a store into the zero page
** location (actually, the push is not replaced, because we need it for
** later, but the name is still ok since the push will get removed at the
** end of each routine).
*/
{
    /* Store the value into the zeropage instead of pushing it. Check high
    ** byte first so that the store is later in A/X order.
    */
    if ((D->Lhs.X.Flags & LI_DIRECT) == 0) {
        AddStoreLhsX (D);
    }
    if ((D->Lhs.A.Flags & LI_DIRECT) == 0) {
        AddStoreLhsA (D);
    }
}



void AddOpLow (StackOpData* D, opc_t OPC, LoadInfo* LI)
/* Add an op for the low byte of an operator. This function honours the
** OP_DIRECT and OP_RELOAD_Y flags and generates the necessary instructions.
** All code is inserted at the current insertion point.
*/
{
    CodeEntry* X;

    if ((LI->A.Flags & LI_DIRECT) != 0) {
        /* Op with a variable location. If the location is on the stack, we
        ** need to reload the Y register.
        */
        if ((LI->A.Flags & LI_RELOAD_Y) == 0) {

            /* opc ... */
            CodeEntry* LoadA = LI->A.LoadEntry;
            X = NewCodeEntry (OPC, LoadA->AM, LoadA->Arg, 0, D->OpEntry->LI);
            InsertEntry (D, X, D->IP++);

        } else {

            if ((LI->A.Flags & LI_CHECK_Y) == 0) {
                /* ldy #offs */
                X = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (LI->A.Offs), 0, D->OpEntry->LI);
            } else {
                /* ldy src */
                X = NewCodeEntry (OP65_LDY, LI->A.LoadYEntry->AM, LI->A.LoadYEntry->Arg, 0, D->OpEntry->LI);
            }
            InsertEntry (D, X, D->IP++);

            if (LI->A.LoadEntry->OPC == OP65_JSR) {
                /* opc (sp),y */
                X = NewCodeEntry (OPC, AM65_ZP_INDY, "sp", 0, D->OpEntry->LI);
            } else {
                /* opc src,y */
                X = NewCodeEntry (OPC, LI->A.LoadEntry->AM, LI->A.LoadEntry->Arg, 0, D->OpEntry->LI);
            }
            InsertEntry (D, X, D->IP++);

        }

        /* In both cases, we can remove the load */
        LI->A.Flags |= LI_REMOVE;

    } else {

        /* Op with temp storage */
        X = NewCodeEntry (OPC, AM65_ZP, D->ZPLo, 0, D->OpEntry->LI);
        InsertEntry (D, X, D->IP++);

    }
}



void AddOpHigh (StackOpData* D, opc_t OPC, LoadInfo* LI, int KeepResult)
/* Add an op for the high byte of an operator. Special cases (constant values
** or similar) have to be checked separately, the function covers only the
** generic case. Code is inserted at the insertion point.
*/
{
    CodeEntry* X;

    if (KeepResult) {
        /* pha */
        X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, D->OpEntry->LI);
        InsertEntry (D, X, D->IP++);
    }

    /* txa */
    X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, D->OpEntry->LI);
    InsertEntry (D, X, D->IP++);

    if ((LI->X.Flags & LI_DIRECT) != 0) {

        if ((LI->X.Flags & LI_RELOAD_Y) == 0) {

            /* opc xxx */
            CodeEntry* LoadX = LI->X.LoadEntry;
            X = NewCodeEntry (OPC, LoadX->AM, LoadX->Arg, 0, D->OpEntry->LI);
            InsertEntry (D, X, D->IP++);

        } else {

            if ((LI->X.Flags & LI_CHECK_Y) == 0) {
                /* ldy #const */
                X = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (LI->X.Offs), 0, D->OpEntry->LI);
            } else {
                /* ldy src */
                X = NewCodeEntry (OP65_LDY, LI->X.LoadYEntry->AM, LI->X.LoadYEntry->Arg, 0, D->OpEntry->LI);
            }
            InsertEntry (D, X, D->IP++);

            if (LI->X.LoadEntry->OPC == OP65_JSR) {
                /* opc (sp),y */
                X = NewCodeEntry (OPC, AM65_ZP_INDY, "sp", 0, D->OpEntry->LI);
            } else {
                /* opc src,y */
                X = NewCodeEntry (OPC, LI->X.LoadEntry->AM, LI->X.LoadEntry->Arg, 0, D->OpEntry->LI);
            }
            InsertEntry (D, X, D->IP++);
        }

        /* In both cases, we can remove the load */
        LI->X.Flags |= LI_REMOVE;

    } else {
        /* opc zphi */
        X = NewCodeEntry (OPC, AM65_ZP, D->ZPHi, 0, D->OpEntry->LI);
        InsertEntry (D, X, D->IP++);
    }

    if (KeepResult) {
        /* tax */
        X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, D->OpEntry->LI);
        InsertEntry (D, X, D->IP++);

        /* pla */
        X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, D->OpEntry->LI);
        InsertEntry (D, X, D->IP++);
    }
}



void RemoveRegLoads (StackOpData* D, LoadInfo* LI)
/* Remove register load insns */
{
    if ((LI->A.Flags & LI_REMOVE) == LI_REMOVE) {
        if (LI->A.LoadIndex >= 0 &&
            (LI->A.LoadEntry->Flags & CEF_DONT_REMOVE) == 0) {
            DelEntry (D, LI->A.LoadIndex);
            LI->A.LoadEntry = 0;
        }
        if (LI->A.LoadYIndex >= 0 &&
            (LI->A.LoadYEntry->Flags & CEF_DONT_REMOVE) == 0) {
            DelEntry (D, LI->A.LoadYIndex);
        }
        if (LI->A.ChgIndex >= 0 &&
            (LI->A.ChgEntry->Flags & CEF_DONT_REMOVE) == 0) {
            DelEntry (D, LI->A.ChgIndex);
        }
    }

    if (LI->A.LoadEntry != 0                &&
        (LI->A.Flags & LI_RELOAD_Y) != 0    &&
        LI->A.LoadYIndex >= 0) {
        /* If an entry is using Y and not removed, then its Y load mustn't be removed */
        LI->A.LoadYEntry->Flags |= CEF_DONT_REMOVE;
    }

    if ((LI->X.Flags & LI_REMOVE) == LI_REMOVE) {
        if (LI->X.LoadIndex >= 0 &&
            (LI->X.LoadEntry->Flags & CEF_DONT_REMOVE) == 0) {
            DelEntry (D, LI->X.LoadIndex);
            LI->X.LoadEntry = 0;
        }
        if (LI->X.LoadYIndex >= 0 &&
            (LI->X.LoadYEntry->Flags & CEF_DONT_REMOVE) == 0) {
            DelEntry (D, LI->X.LoadYIndex);
        }
        if (LI->X.ChgIndex >= 0 &&
            (LI->X.ChgEntry->Flags & CEF_DONT_REMOVE) == 0) {
            DelEntry (D, LI->X.ChgIndex);
        }
    }

    if (LI->X.LoadEntry != 0                &&
        (LI->X.Flags & LI_RELOAD_Y) != 0    &&
        LI->X.LoadYIndex >= 0) {
        /* If an entry is using Y and not removed, then its Y load mustn't be removed */
        LI->X.LoadYEntry->Flags |= CEF_DONT_REMOVE;
    }
}



void RemoveRemainders (StackOpData* D)
/* Remove the code that is unnecessary after translation of the sequence */
{
    /* Remove the register loads for lhs and rhs if nothing prevents that */
    RemoveRegLoads (D, &D->Lhs);
    RemoveRegLoads (D, &D->Rhs);

    /* Remove the push and the operator routine */
    DelEntry (D, D->OpIndex);
    DelEntry (D, D->PushIndex);
}



static int CmpHarmless (const void* Key, const void* Entry)
/* Compare function for bsearch */
{
    return strcmp (Key, *(const char**)Entry);
}



int HarmlessCall (const CodeEntry* E, int PushedBytes)
/* Check if this is a call to a harmless subroutine that will not interrupt
** the pushax/op sequence when encountered.
*/
{
    unsigned Use = 0, Chg = 0;
    if (GetFuncInfo (E->Arg, &Use, &Chg) == FNCLS_BUILTIN) {
        if ((Chg & REG_SP) != 0) {
            return 0;
        }
        if ((Use & REG_SP) != 0                     &&
            ((Use & (SLV_IND | SLV_TOP)) != SLV_IND ||
             RegValIsUnknown (E->RI->In.RegY)       ||
             E->RI->In.RegY < PushedBytes)) {
            /* If we are using the stack, and we don't have "indirect"
            ** addressing mode, or the value of Y is unknown, or less
            ** than two, we cannot cope with this piece of code. Having
            ** an unknown value of Y means that we cannot correct the
            ** stack offset, while having an offset less than PushedBytes
            ** means that the code works with the value on stack which
            ** is to be removed.
            */
            return 0;
        }
        return 1;
    } else {
        static const char* const Tab[] = {
            "_abs",
        };

        void* R = bsearch (E->Arg,
                            Tab,
                            sizeof (Tab) / sizeof (Tab[0]),
                            sizeof (Tab[0]),
                            CmpHarmless);
        return (R != 0);
    }
}



/*****************************************************************************/
/*                            Load tracking code                             */
/*****************************************************************************/



/*****************************************************************************/
/*                                  Helpers                                  */
/*****************************************************************************/



const char* GetZPName (unsigned ZPLoc)
/* Get the name strings of certain known ZP Regs */
{
    if ((ZPLoc & REG_TMP1) != 0) {
        return "tmp1";
    }
    if ((ZPLoc & REG_PTR1_LO) != 0) {
        return "ptr1";
    }
    if ((ZPLoc & REG_PTR1_HI) != 0) {
        return "ptr1+1";
    }
    if ((ZPLoc & REG_PTR2_LO) != 0) {
        return "ptr2";
    }
    if ((ZPLoc & REG_PTR2_HI) != 0) {
        return "ptr2+1";
    }
    if ((ZPLoc & REG_SREG_LO) != 0) {
        return "sreg";
    }
    if ((ZPLoc & REG_SREG_HI) != 0) {
        return "sreg+1";
    }
    if ((ZPLoc & REG_SAVE_LO) != 0) {
        return "save";
    }
    if ((ZPLoc & REG_SAVE_HI) != 0) {
        return "save+1";
    }
    if ((ZPLoc & REG_SP_LO) != 0) {
        return "sp";
    }
    if ((ZPLoc & REG_SP_HI) != 0) {
        return "sp+1";
    }

    return 0;
}

unsigned FindAvailableBackupLoc (BackupInfo* B, unsigned Type)
/* Find a ZP loc for storing the backup and fill in the info.
** The allowed types are specified with the Type parameter.
** For convenience, all types are aloowed if none is specified.
** Return the type of the found loc.
*/
{
    unsigned SizeType = Type & BU_SIZE_MASK;
    Type &= BU_TYPE_MASK;
    if (Type == 0) {
        Type = BU_TYPE_MASK;
    }

    if (SizeType == BU_B8 && (Type & BU_REG) != 0 && (B->ZPUsage & REG_Y) == 0) {
        /* Use the Y Reg only */
        B->Type = BU_REG | SizeType;
        B->Where = REG_Y;
        B->ZPUsage |= REG_Y;
        return B->Type;
    }

    if (SizeType == BU_B8 && (Type & BU_ZP) != 0) {
        /* For now we only check for tmp1 and sreg */
        if ((B->ZPUsage & REG_TMP1) == 0) {
            B->Type = BU_ZP | BU_B8;
            B->Where = REG_TMP1;
            B->ZPUsage |= REG_TMP1;
            return B->Type;
        }
        if ((B->ZPUsage & REG_SREG_LO) == 0) {
            B->Type = BU_ZP | BU_B8;
            B->Where = REG_SREG_LO;
            B->ZPUsage |= REG_SREG_LO;
            return B->Type;
        }
        if ((B->ZPUsage & REG_SREG_HI) == 0) {
            B->Type = BU_ZP | BU_B8;
            B->Where = REG_SREG_HI;
            B->ZPUsage |= REG_SREG_HI;
            return B->Type;
        }
    }

    if (SizeType == BU_B16 && (Type & BU_ZP) != 0) {
        /* For now we only check for ptr1, sreg and ptr2 */
        if ((B->ZPUsage & REG_PTR1) == 0) {
            B->Type = BU_ZP | BU_B16;
            B->Where = REG_PTR1;
            B->ZPUsage |= REG_PTR1;
            return B->Type;
        }
        if ((B->ZPUsage & REG_SREG) == 0) {
            B->Type = BU_ZP | BU_B16;
            B->Where = REG_SREG;
            B->ZPUsage |= REG_SREG;
            return B->Type;
        }
        if ((B->ZPUsage & REG_PTR2) == 0) {
            B->Type = BU_ZP | BU_B16;
            B->Where = REG_PTR2;
            B->ZPUsage |= REG_PTR2;
            return B->Type;
        }
    }

    if (SizeType == BU_B24 && (Type & BU_ZP) != 0) {
        /* For now we only check for certain combinations of
        ** tmp1 + (ptr1, sreg or ptr2).
        */
        if ((B->ZPUsage & (REG_TMP1 | REG_PTR1)) == 0) {
            B->Type = BU_ZP | BU_B24;
            B->Where = REG_TMP1 | REG_PTR1;
            B->ZPUsage |= REG_TMP1 | REG_PTR1;
            return B->Type;
        }
        if ((B->ZPUsage & (REG_TMP1 | REG_SREG)) == 0) {
            B->Type = BU_ZP | BU_B24;
            B->Where = REG_TMP1 | REG_SREG;
            B->ZPUsage |= REG_TMP1 | REG_SREG;
            return B->Type;
        }
        if ((B->ZPUsage & (REG_TMP1 | REG_PTR2)) == 0) {
            B->Type = BU_ZP | BU_B24;
            B->Where = REG_TMP1 | REG_PTR2;
            B->ZPUsage |= REG_TMP1 | REG_PTR2;
            return B->Type;
        }
    }

    if (SizeType < BU_B32 && (Type & BU_SP6502) != 0) {
        /* Even for BU_B24, we just push/pop all 3 of AXY */
        B->Type = BU_SP6502 | BU_B16;
        B->Where = 0;
        return B->Type;
    }

    if (SizeType != BU_B24 && SizeType <= BU_B32 && (Type & BU_SP) != 0) {
        /* We may also use pusha/popa, pushax/popax and pusheax/popeax */
        B->Type = BU_SP | SizeType;
        B->Where = 0;
        return B->Type;
    }

    /* No available */
    return BU_UNKNOWN;
}



void AdjustEntryIndices (Collection* Indices, int Index, int Change)
/* Adjust a load register info struct after deleting or inserting successive
** entries with a given index.
*/
{
    int I;
    int* IndexPtr;

    if (Change > 0) {
        /* Insertion */
        for (I = 0; I < (int)CollCount (Indices); ++I) {
            IndexPtr = CollAtUnchecked (Indices, I);
            if (Index <= *IndexPtr) {
                *IndexPtr += Change;
            }
        }
    } else if (Change < 0) {
        /* Deletion */
        for (I = 0; I < (int)CollCount (Indices); ++I) {
            IndexPtr = CollAtUnchecked (Indices, I);
            if (Index <= *IndexPtr + Change) {
                *IndexPtr += Change;
            } else if (Index <= *IndexPtr) {
                /* Has been removed */
                *IndexPtr = -1;
                /*CollDelete (Indices, I);*/
                --I;
            }
        }
    }
}



void DelEntryIdx (CodeSeg* S, int Idx, Collection* Indices)
/* Delete an entry and adjust Indices if necessary */
{
    CS_DelEntry (S, Idx);
    AdjustEntryIndices (Indices, Idx, -1);
}



void DelEntriesIdx (CodeSeg* S, int Idx, int Count, Collection* Indices)
/* Delete entries and adjust Indices if necessary */
{
    CS_DelEntries (S, Idx, Count);
    AdjustEntryIndices (Indices, Idx, -Count);
}



void RemoveFlaggedRegLoads (CodeSeg* S, LoadRegInfo* LRI, Collection* Indices)
/* Remove flagged register load insns */
{
    if ((LRI->Flags & LI_REMOVE) == LI_REMOVE) {
        if (LRI->LoadIndex >= 0 &&
            (LRI->LoadEntry->Flags & CEF_DONT_REMOVE) == 0) {
            DelEntryIdx (S, LRI->LoadIndex, Indices);
            LRI->LoadEntry = 0;
        }
        if (LRI->LoadYIndex >= 0 &&
            (LRI->LoadYEntry->Flags & CEF_DONT_REMOVE) == 0) {
            DelEntryIdx (S, LRI->LoadYIndex, Indices);
        }
        if (LRI->ChgIndex >= 0 &&
            (LRI->ChgEntry->Flags & CEF_DONT_REMOVE) == 0) {
            DelEntryIdx (S, LRI->ChgIndex, Indices);
        }
    }

    if (LRI->LoadEntry != 0 &&
        (LRI->Flags & LI_RELOAD_Y) != 0 &&
        LRI->LoadYIndex >= 0) {
        /* If an entry is using Y and not removed, then its Y load mustn't be removed */
        LRI->LoadYEntry->Flags |= CEF_DONT_REMOVE;
    }
}


void RemoveFlaggedLoads (CodeSeg* S, LoadInfo* LI, Collection* Indices)
/* Remove flagged load insns */
{
    RemoveFlaggedRegLoads (S, &LI->A, Indices);
    RemoveFlaggedRegLoads (S, &LI->X, Indices);
    RemoveFlaggedRegLoads (S, &LI->Y, Indices);
}



static int BackupAAt (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices, int After)
/* Backup the content of A Before or After the specified index Idx depending on the After param */
{
    CodeEntry* E;
    CodeEntry* X;
    int OldIdx;

    /* Adjust the insertion point if necessary */
    if (After) {
        ++Idx;
    }
    OldIdx = Idx;

    /* Cannot insert after the last insn */
    CHECK ((unsigned)Idx < CollCount (&S->Entries));

    /* Get the entry at Idx */
    E = CS_GetEntry (S, Idx);

    if (E->RI != 0 && RegValIsKnown (E->RI->In.RegA)) {
        /* Just memorize the value */
        B->Type = BU_IMM | BU_B8;
        B->Imm = E->RI->In.RegA;

    } else {
        FindAvailableBackupLoc (B, BU_B8);
        switch (B->Type & BU_TYPE_MASK) {
        case BU_REG:
            if ((B->Where & REG_X) != 0) {
                X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, E->LI);
                CS_InsertEntry (S, X, Idx++);
                break;
            } else if ((B->Where & REG_Y) != 0) {
                X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, E->LI);
                CS_InsertEntry (S, X, Idx++);
                break;
            }
            break;

        case BU_ZP:
            X = NewCodeEntry (OP65_STA, AM65_ZP, GetZPName (B->Where), 0, E->LI);
            CS_InsertEntry (S, X, Idx++);
           break;

        case BU_SP6502:
            X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, E->LI);
            CS_InsertEntry (S, X, Idx++);
            break;

        case BU_SP:
            if ((B->ZPUsage & REG_Y) == 0) {
                X = NewCodeEntry (OP65_JSR, AM65_ABS, "pusha", 0, E->LI);
                CS_InsertEntry (S, X, Idx++);
                break;
            }

            /* Unable to do backup */
            return 0;

        default:
            /* Unable to do backup */
            return 0;
        }
    }

    /* Adjust all indices at once */
    AdjustEntryIndices (Indices, OldIdx, Idx - OldIdx);

    /* Move labels if it was an insertion before Idx */
    if (!After) {
        CS_MoveLabels (S, E, CS_GetEntry (S, OldIdx));
    }

    /* Done */
    return 1;
}



static int BackupXAt (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices, int After)
/* Backup the content of X before or after the specified index Idx depending on the param After */
{
    CodeEntry* E;
    CodeEntry* X;
    int OldIdx;

    /* Adjust the insertion point if necessary */
    if (After) {
        ++Idx;
    }
    OldIdx = Idx;

    /* Cannot insert after the last insn */
    CHECK ((unsigned)Idx < CollCount (&S->Entries));

    /* Get the entry at Idx */
    E = CS_GetEntry (S, Idx);

    if (E->RI != 0 && RegValIsKnown (E->RI->In.RegX)) {
        /* Just memorize the value */
        B->Type = BU_IMM | BU_B8;
        B->Imm = E->RI->In.RegX;

    } else {
        FindAvailableBackupLoc (B, BU_B8);
        switch (B->Type & BU_TYPE_MASK) {
        case BU_ZP:
            X = NewCodeEntry (OP65_STX, AM65_ZP, GetZPName(B->Where), 0, E->LI);
            CS_InsertEntry (S, X, Idx++);
           break;

        case BU_SP6502:
            if ((B->ZPUsage & REG_A) == 0) {
                X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, E->LI);
                CS_InsertEntry (S, X, Idx++);
                X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, E->LI);
                CS_InsertEntry (S, X, Idx++);
                break;
            }

            /* Unable to do backup */
            return 0;

        case BU_SP:
            if ((B->ZPUsage & REG_A) == 0) {
                X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, E->LI);
                CS_InsertEntry (S, X, Idx++);
                X = NewCodeEntry (OP65_JSR, AM65_ABS, "pusha", 0, E->LI);
                CS_InsertEntry (S, X, Idx++);
                break;
            }

            /* Unable to do backup */
            return 0;

        case BU_REG:
            /* Fallthrough */
        default:

            /* Unable to do backup */
            return 0;
        }
    }

    /* Adjust all indices at once */
    AdjustEntryIndices (Indices, OldIdx, Idx - OldIdx);

    /* Move labels if it was an insertion before Idx */
    if (!After) {
        CS_MoveLabels (S, E, CS_GetEntry (S, OldIdx));
    }

    /* Done */
    return 1;
}



static int BackupYAt (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices, int After)
/* Backup the content of Y before or after the specified index Idx depending on the param After */
{
    CodeEntry* E;
    CodeEntry* X;
    int OldIdx;

    /* Adjust the insertion point if necessary */
    if (After) {
        ++Idx;
    }
    OldIdx = Idx;

    /* Cannot insert after the last insn */
    CHECK ((unsigned)Idx < CollCount (&S->Entries));

    /* Get the entry at Idx */
    E = CS_GetEntry (S, Idx);

    if (E->RI != 0 && RegValIsKnown (E->RI->In.RegY)) {
        /* Just memorize the value */
        B->Type = BU_IMM | BU_B8;
        B->Imm = E->RI->In.RegY;

    } else {
        FindAvailableBackupLoc (B, BU_B8);
        switch (B->Type & BU_TYPE_MASK) {
        case BU_ZP:
            X = NewCodeEntry (OP65_STY, AM65_ZP, GetZPName(B->Where), 0, E->LI);
            CS_InsertEntry (S, X, Idx++);
           break;

        case BU_SP6502:
            if ((B->ZPUsage & REG_A) == 0) {
                X = NewCodeEntry (OP65_TYA, AM65_IMP, 0, 0, E->LI);
                CS_InsertEntry (S, X, Idx++);
                X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, E->LI);
                CS_InsertEntry (S, X, Idx++);
                break;
            }

            /* Unable to do backup */
            return 0;

        case BU_SP:
            if ((B->ZPUsage & REG_A) == 0) {
                X = NewCodeEntry (OP65_TYA, AM65_IMP, 0, 0, E->LI);
                CS_InsertEntry (S, X, Idx++);
                X = NewCodeEntry (OP65_JSR, AM65_ABS, "pusha", 0, E->LI);
                CS_InsertEntry (S, X, Idx++);
                break;
            }

            /* Unable to do backup */
            return 0;

        case BU_REG:
            /* Fallthrough */
        default:

            /* Unable to do backup */
            return 0;
        }
    }

    /* Adjust all indices at once */
    AdjustEntryIndices (Indices, OldIdx, Idx - OldIdx);

    /* Move labels if it was an insertion before Idx */
    if (!After) {
        CS_MoveLabels (S, E, CS_GetEntry (S, OldIdx));
    }

    /* Done */
    return 1;
}



static int BackupAXAt (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices, int After)
/* Backup the content of AX Before or After the specified index Idx depending on the After param */
{
    CodeEntry* E;
    CodeEntry* X;
    int OldIdx;
    StrBuf Arg;

    SB_Init (&Arg);

    /* Adjust the insertion point if necessary */
    if (After) {
        ++Idx;
    }
    OldIdx = Idx;

    /* Cannot insert after the last insn */
    CHECK ((unsigned)Idx < CollCount (&S->Entries));

    /* Get the entry at Idx */
    E = CS_GetEntry (S, Idx);

    if (E->RI != 0 && RegValIsKnown (E->RI->In.RegA) && RegValIsKnown (E->RI->In.RegX)) {
        /* Just memorize the value */
        B->Type = BU_IMM | BU_B16;
        B->Imm = E->RI->In.RegA | (E->RI->In.RegX << 8);

    } else {
        FindAvailableBackupLoc (B, BU_B16);
        switch (B->Type & BU_TYPE_MASK) {
        case BU_ZP:
            SB_AppendStr (&Arg, GetZPName (B->Where));
            SB_Terminate (&Arg);
            X = NewCodeEntry (OP65_STA, AM65_ZP, SB_GetConstBuf (&Arg), 0, E->LI);
            CS_InsertEntry (S, X, Idx++);
            SB_AppendStr (&Arg, "+1");
            SB_Terminate (&Arg);
            X = NewCodeEntry (OP65_STX, AM65_ZP, SB_GetConstBuf (&Arg), 0, E->LI);
            CS_InsertEntry (S, X, Idx++);
            break;

        case BU_SP6502:
            if ((B->ZPUsage & REG_A) == 0) {
                X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, E->LI);
                CS_InsertEntry (S, X, Idx++);
                X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, E->LI);
                CS_InsertEntry (S, X, Idx++);
                X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, E->LI);
                CS_InsertEntry (S, X, Idx++);
                break;
            }

            /* Unable to do backup */
            return 0;

        case BU_SP:
            if ((B->ZPUsage & REG_Y) == 0) {
                X = NewCodeEntry (OP65_JSR, AM65_ABS, "pushax", 0, E->LI);
                CS_InsertEntry (S, X, Idx++);
                break;
            }

            /* Unable to do backup */
            return 0;

        case BU_REG:
            /* Fallthrough */

        default:
            /* Unable to do backup */
            return 0;
        }
    }

    /* Adjust all indices at once */
    AdjustEntryIndices (Indices, OldIdx, Idx - OldIdx);

    /* Move labels if it was an insertion before Idx */
    if (!After) {
        CS_MoveLabels (S, E, CS_GetEntry (S, OldIdx));
    }

    SB_Done (&Arg);

    /* Done */
    return 1;
}



static int BackupAXYAt (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices, int After)
/* Backup the content of AXY before or after the specified index Idx depending on the param After.
** This doesn't allow separating the backup of Y from that of AX for now.
*/
{
    CodeEntry* E;
    CodeEntry* X;
    int OldIdx;
    StrBuf Arg;

    SB_Init (&Arg);

    /* Adjust the insertion point if necessary */
    if (After) {
        ++Idx;
    }
    OldIdx = Idx;

    /* Cannot insert after the last insn */
    CHECK ((unsigned)Idx < CollCount (&S->Entries));

    /* Get the entry at Idx */
    E = CS_GetEntry (S, Idx);

    if (E->RI != 0 &&
        RegValIsKnown (E->RI->In.RegA) &&
        RegValIsKnown (E->RI->In.RegX) &&
        RegValIsKnown (E->RI->In.RegY)) {
        /* Just memorize the value */
        B->Type = BU_IMM | BU_B24;
        B->Imm = E->RI->In.RegA | (E->RI->In.RegX << 8) | (E->RI->In.RegY << 16);

    } else {
        FindAvailableBackupLoc (B, BU_B24);
        switch (B->Type & BU_TYPE_MASK) {
        case BU_ZP:
            CHECK ((B->Where & REG_TMP1) != 0);
            SB_AppendStr (&Arg, GetZPName (B->Where & ~REG_TMP1));
            SB_Terminate (&Arg);
            X = NewCodeEntry (OP65_STA, AM65_ZP, SB_GetConstBuf (&Arg), 0, E->LI);
            CS_InsertEntry (S, X, Idx++);
            SB_AppendStr (&Arg, "+1");
            SB_Terminate (&Arg);
            X = NewCodeEntry (OP65_STX, AM65_ZP, SB_GetConstBuf (&Arg), 0, E->LI);
            CS_InsertEntry (S, X, Idx++);
            X = NewCodeEntry (OP65_STY, AM65_ZP, GetZPName (B->Where & REG_TMP1), 0, E->LI);
            CS_InsertEntry(S, X, Idx++);
            break;

        case BU_SP6502:
            if ((B->ZPUsage & REG_A) == 0) {
                X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, E->LI);
                CS_InsertEntry (S, X, Idx++);
                X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, E->LI);
                CS_InsertEntry (S, X, Idx++);
                X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, E->LI);
                CS_InsertEntry (S, X, Idx++);
                X = NewCodeEntry (OP65_TYA, AM65_IMP, 0, 0, E->LI);
                CS_InsertEntry (S, X, Idx++);
                X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, E->LI);
                CS_InsertEntry (S, X, Idx++);
                break;
            }

            /* Unable to do backup */
            return 0;

        case BU_SP:
            if ((B->ZPUsage & REG_AY) == 0) {
                X = NewCodeEntry (OP65_JSR, AM65_ABS, "pushax", 0, E->LI);
                CS_InsertEntry (S, X, Idx++);
                X = NewCodeEntry (OP65_TYA, AM65_IMP, 0, 0, E->LI);
                CS_InsertEntry (S, X, Idx++);
                X = NewCodeEntry (OP65_JSR, AM65_ABS, "pusha", 0, E->LI);
                CS_InsertEntry (S, X, Idx++);
                break;
            }

            /* Unable to do backup */
            return 0;

        case BU_REG:
            /* Fallthrough */

        default:
            /* Unable to do backup */
            return 0;
        }
    }

    /* Adjust all indices at once */
    AdjustEntryIndices (Indices, OldIdx, Idx - OldIdx);

    /* Move labels if it was an insertion before Idx */
    if (!After) {
        CS_MoveLabels (S, E, CS_GetEntry (S, OldIdx));
    }

    SB_Done (&Arg);

    /* Done */
    return 1;
}



int BackupABefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices)
/* Backup the content of A Before the specified index Idx */
{
    return BackupAAt (S, B, Idx, Indices, 0);
}



int BackupXBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices)
/* Backup the content of X before the specified index Idx */
{
    return BackupXAt (S, B, Idx, Indices, 0);
}



int BackupYBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices)
/* Backup the content of Y before the specified index Idx */
{
    return BackupYAt (S, B, Idx, Indices, 0);
}



int BackupAXBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices)
/* Backup the content of AX before the specified index Idx */
{
    return BackupAXAt (S, B, Idx, Indices, 0);
}



int BackupAXYBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices)
/* Backup the content of AXY before the specified index Idx.
** This doesn't allow separating the backup of Y from that of AX for now.
*/
{
    return BackupAXYAt (S, B, Idx, Indices, 0);
}



int BackupAAfter (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices)
/* Backup the content of A after the specified index Idx */
{
    return BackupAAt (S, B, Idx, Indices, 1);
}



int BackupXAfter (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices)
/* Backup the content of X after the specified index Idx */
{
    return BackupXAt (S, B, Idx, Indices, 1);
}



int BackupYAfter (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices)
/* Backup the content of Y after the specified index Idx */
{
    return BackupYAt (S, B, Idx, Indices, 1);
}



int BackupAXAfter (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices)
/* Backup the content of AX after the specified index Idx */
{
    return BackupAXAt (S, B, Idx, Indices, 1);
}



int BackupAXYAfter (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices)
/* Backup the content of AXY after the specified index Idx.
** This doesn't allow separating the backup of Y from that of AX for now.
*/
{
    return BackupAXYAt (S, B, Idx, Indices, 1);
}



int RestoreABefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices)
/* Restore the content of Y before the specified index Idx */
{
    CodeEntry* E;
    CodeEntry* X;
    int OldIdx = Idx;

    /* Get the entry at Idx */
    E = CS_GetEntry (S, Idx);

    switch (B->Type & BU_TYPE_MASK) {
    case BU_IMM:
        /* Just use the memorized value */
        X = NewCodeEntry (OP65_LDA, AM65_IMM, MakeHexArg (B->Imm & 0xFF), 0, E->LI);
        CS_InsertEntry (S, X, Idx++);
        break;

    case BU_REG:
        if ((B->Where & REG_X) != 0) {
            X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, E->LI);
            CS_InsertEntry (S, X, Idx++);
        } else if ((B->Where & REG_Y) != 0) {
            X = NewCodeEntry (OP65_TYA, AM65_IMP, 0, 0, E->LI);
            CS_InsertEntry (S, X, Idx++);
        }
        break;

    case BU_ZP:
        X = NewCodeEntry (OP65_LDA, AM65_ZP, GetZPName (B->Where), 0, E->LI);
        CS_InsertEntry (S, X, Idx++);
        break;

    case BU_SP6502:
        X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, E->LI);
        CS_InsertEntry (S, X, Idx++);
        break;

    case BU_SP:
        if ((B->ZPUsage & REG_Y) == 0) {
            X = NewCodeEntry (OP65_JSR, AM65_ABS, "popa", 0, E->LI);
            CS_InsertEntry (S, X, Idx++);
            break;
        }

        /* Unable to restore */
        return 0;

    default:
        /* Unable to restore */
        return 0;
    }

    /* Adjust all indices at once */
    AdjustEntryIndices (Indices, OldIdx, Idx - OldIdx);

    /* Done */
    return 1;
}



int RestoreXBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices)
/* Restore the content of X before the specified index Idx */
{
    CodeEntry* E;
    CodeEntry* X;
    int OldIdx = Idx;

    /* Get the entry at Idx */
    E = CS_GetEntry (S, Idx);

    switch (B->Type & BU_TYPE_MASK) {
    case BU_IMM:
        /* Just use the memorized value */
        X = NewCodeEntry (OP65_LDX, AM65_IMM, MakeHexArg (B->Imm & 0xFF), 0, E->LI);
        CS_InsertEntry (S, X, Idx++);
        break;

    case BU_REG:
        if ((B->Where & REG_A) != 0) {
            X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, E->LI);
            CS_InsertEntry (S, X, Idx++);
        } else if ((B->Where & REG_Y) != 0) {
            if ((B->ZPUsage & REG_A) == 0) {
                X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, E->LI);
                CS_InsertEntry (S, X, Idx++);
            }
            X = NewCodeEntry (OP65_TYA, AM65_IMP, 0, 0, E->LI);
            CS_InsertEntry (S, X, Idx++);
            X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, E->LI);
            CS_InsertEntry (S, X, Idx++);
            if ((B->ZPUsage & REG_A) == 0) {
                X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, E->LI);
                CS_InsertEntry (S, X, Idx++);
            }
        }
        break;

    case BU_ZP:
        X = NewCodeEntry (OP65_LDX, AM65_ZP, GetZPName (B->Where), 0, E->LI);
        CS_InsertEntry (S, X, Idx++);
        break;

    case BU_SP6502:
        if ((B->ZPUsage & REG_A) == 0) {
            X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, E->LI);
            CS_InsertEntry (S, X, Idx++);
            X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, E->LI);
            CS_InsertEntry (S, X, Idx++);
            break;
        }

        /* Unable to restore */
        return 0;

    case BU_SP:
        if ((B->ZPUsage & REG_A) == 0) {
            X = NewCodeEntry (OP65_JSR, AM65_ABS, "popa", 0, E->LI);
            CS_InsertEntry (S, X, Idx++);
            X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, E->LI);
            CS_InsertEntry (S, X, Idx++);
            break;
        }

        /* Unable to restore */
        return 0;

    default:
        /* Unable to restore */
        return 0;
    }

    /* Adjust all indices at once */
    AdjustEntryIndices (Indices, OldIdx, Idx - OldIdx);

    /* Done */
    return 1;
}



int RestoreYBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices)
/* Restore the content of Y before the specified index Idx */
{
    CodeEntry* E;
    CodeEntry* X;
    int OldIdx = Idx;

    /* Get the entry at Idx */
    E = CS_GetEntry (S, Idx);

    switch (B->Type & BU_TYPE_MASK) {
    case BU_IMM:
        /* Just use the memorized value */
        X = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (B->Imm & 0xFF), 0, E->LI);
        CS_InsertEntry (S, X, Idx++);
        break;

    case BU_REG:
        if ((B->Where & REG_A) != 0) {
            X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, E->LI);
            CS_InsertEntry (S, X, Idx++);
        } else if ((B->Where & REG_X) != 0) {
            if ((B->ZPUsage & REG_A) == 0) {
                X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, E->LI);
                CS_InsertEntry (S, X, Idx++);
            }
            X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, E->LI);
            CS_InsertEntry (S, X, Idx++);
            X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, E->LI);
            CS_InsertEntry (S, X, Idx++);
            if ((B->ZPUsage & REG_A) == 0) {
                X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, E->LI);
                CS_InsertEntry (S, X, Idx++);
            }
        }
        break;

    case BU_ZP:
        X = NewCodeEntry (OP65_LDY, AM65_ZP, GetZPName (B->Where), 0, E->LI);
        CS_InsertEntry (S, X, Idx++);
        break;

    case BU_SP6502:
        if ((B->ZPUsage & REG_A) == 0) {
            X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, E->LI);
            CS_InsertEntry (S, X, Idx++);
            X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, E->LI);
            CS_InsertEntry (S, X, Idx++);
            break;
        }

        /* Unable to restore */
        return 0;

    case BU_SP:
        if ((B->ZPUsage & REG_A) == 0) {
            X = NewCodeEntry (OP65_JSR, AM65_ABS, "popa", 0, E->LI);
            CS_InsertEntry (S, X, Idx++);
            X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, E->LI);
            CS_InsertEntry (S, X, Idx++);
            break;
        }

        /* Unable to restore */
        return 0;

    default:
        /* Unable to restore */
        return 0;
    }

    /* Adjust all indices at once */
    AdjustEntryIndices (Indices, OldIdx, Idx - OldIdx);

    /* Done */
    return 1;
}



int RestoreAXBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices)
/* Restore the content of AX before the specified index Idx */
{
    CodeEntry* E;
    CodeEntry* X;
    StrBuf Arg;
    int OldIdx = Idx;

    SB_Init (&Arg);

    /* Get the entry at Idx */
    E = CS_GetEntry (S, Idx);

    switch (B->Type & BU_TYPE_MASK) {
    case BU_REG:
        /* Just use the memorized value */
        X = NewCodeEntry (OP65_LDA, AM65_IMM, MakeHexArg (B->Imm & 0xFF), 0, E->LI);
        CS_InsertEntry (S, X, Idx++);
        X = NewCodeEntry (OP65_LDX, AM65_IMM, MakeHexArg ((B->Imm >> 8) & 0xFF), 0, E->LI);
        CS_InsertEntry (S, X, Idx++);
        break;

    case BU_ZP:
        SB_AppendStr (&Arg, GetZPName (B->Where));
        SB_Terminate (&Arg);
        X = NewCodeEntry (OP65_LDA, AM65_ZP, SB_GetConstBuf (&Arg), 0, E->LI);
        CS_InsertEntry (S, X, Idx++);
        SB_AppendStr (&Arg, "+1");
        SB_Terminate (&Arg);
        X = NewCodeEntry (OP65_LDX, AM65_ZP, SB_GetConstBuf (&Arg), 0, E->LI);
        CS_InsertEntry (S, X, Idx++);
        break;

    case BU_SP6502:
        if ((B->ZPUsage & REG_A) == 0) {
            X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, E->LI);
            CS_InsertEntry (S, X, Idx++);
            X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, E->LI);
            CS_InsertEntry (S, X, Idx++);
            X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, E->LI);
            CS_InsertEntry (S, X, Idx++);
            break;
        }

        /* Unable to restore */
        return 0;

    case BU_SP:
        if ((B->ZPUsage & REG_Y) == 0) {
            X = NewCodeEntry (OP65_JSR, AM65_ABS, "popax", 0, E->LI);
            CS_InsertEntry (S, X, Idx++);
            break;
        }

        /* Unable to restore */
        return 0;

    default:
        /* Unable to restore */
        return 0;
    }

    /* Adjust all indices at once */
    AdjustEntryIndices (Indices, OldIdx, Idx - OldIdx);

    SB_Done (&Arg);

    return 1;
}



int RestoreAXYBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices)
/* Restore the content of AXY before the specified index Idx.
** This only allows restore from compacted AXY backup for now.
*/
{
    CodeEntry* E;
    CodeEntry* X;
    int OldIdx = Idx;
    StrBuf Arg;

    SB_Init (&Arg);

    /* Get the entry at Idx */
    E = CS_GetEntry (S, Idx);

    switch (B->Type & BU_TYPE_MASK) {
    case BU_IMM:
        /* Just use memorized value */
        X = NewCodeEntry (OP65_LDA, AM65_IMM, MakeHexArg (B->Imm & 0xFF), 0, E->LI);
        CS_InsertEntry (S, X, Idx++);
        X = NewCodeEntry (OP65_LDX, AM65_IMM, MakeHexArg ((B->Imm >> 8) & 0xFF), 0, E->LI);
        CS_InsertEntry (S, X, Idx++);
        X = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg ((B->Imm >> 16) & 0xFF), 0, E->LI);
        CS_InsertEntry (S, X, Idx++);
        break;

    case BU_ZP:
        CHECK ((B->Where & REG_TMP1) != 0);
        SB_AppendStr (&Arg, GetZPName (B->Where & ~REG_TMP1));
        SB_Terminate (&Arg);
        X = NewCodeEntry (OP65_LDA, AM65_ZP, SB_GetConstBuf (&Arg), 0, E->LI);
        CS_InsertEntry (S, X, Idx++);
        SB_AppendStr (&Arg, "+1");
        SB_Terminate (&Arg);
        X = NewCodeEntry (OP65_LDX, AM65_ZP, SB_GetConstBuf (&Arg), 0, E->LI);
        CS_InsertEntry (S, X, Idx++);
        X = NewCodeEntry (OP65_LDY, AM65_ZP, GetZPName (B->Where & REG_TMP1), 0, E->LI);
        CS_InsertEntry (S, X, Idx++);
        break;

    case BU_SP6502:
        X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, E->LI);
        CS_InsertEntry (S, X, Idx++);
        X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, E->LI);
        CS_InsertEntry (S, X, Idx++);
        X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, E->LI);
        CS_InsertEntry (S, X, Idx++);
        X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, E->LI);
        CS_InsertEntry (S, X, Idx++);
        X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, E->LI);
        CS_InsertEntry (S, X, Idx++);
        break;

    case BU_SP:
        X = NewCodeEntry (OP65_JSR, AM65_ABS, "popa", 0, E->LI);
        CS_InsertEntry (S, X, Idx++);
        X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, E->LI);
        CS_InsertEntry (S, X, Idx++);
        X = NewCodeEntry (OP65_JSR, AM65_ABS, "popax", 0, E->LI);
        CS_InsertEntry (S, X, Idx++);
        break;

    default:
        /* Unable to restorep */
        return 0;
    }

    /* Adjust all indices at once */
    AdjustEntryIndices (Indices, OldIdx, Idx - OldIdx);

    SB_Done (&Arg);

    /* Done */
    return 1;
}



int BackupArgAfter (CodeSeg* S, BackupInfo* B, int Idx, const CodeEntry* E, Collection* Indices)
/* Backup the content of the opc arg of the entry E after the specified index Idx.
** Reg A/Y will be used to transfer the content from a memory location to another
** regardless of whether it is in use.
*/
{
    CodeEntry* X;
    int OldIdx = Idx;
    unsigned ArgSize;
    unsigned Use, Chg;
    StrBuf SrcArg;
    StrBuf DstArg;

    SB_Init (&SrcArg);
    SB_Init (&DstArg);

    /* We only recognize opc with an arg for now, as well as a special case for ldaxysp */
    if ((E->OPC != OP65_JSR || strcmp (E->Arg, "ldaxysp") == 0) &&
        E->AM != AM65_BRA) {
        /* Get size of the arg */
        if ((E->Info & OF_LBRA) != 0 || strcmp (E->Arg, "ldaxysp") == 0) {
            ArgSize = BU_B16;
        } else {
            ArgSize = BU_B8;
        }

        if (E->AM == AM65_IMM && CE_HasNumArg (E)) {
            /* Just memorize the value */
            B->Type = BU_IMM | ArgSize;
            B->Imm = E->Num;

            /* Adjust all indices at once */
            AdjustEntryIndices (Indices, OldIdx + 1, Idx - OldIdx);

            /* Done */
            return 1;

        }

        if (E->Size != 1 && E->AM != AM65_IMP) {

            /* We only recognize opc with an arg for now */
            FindAvailableBackupLoc (B, ArgSize);
            switch (B->Type & BU_TYPE_MASK) {
            case BU_ZP:
                X = NewCodeEntry (OP65_LDA, E->AM, E->Arg, 0, E->LI);
                CS_InsertEntry (S, X, ++Idx);
                SB_AppendStr (&DstArg, GetZPName (B->Where));
                SB_Terminate (&DstArg);
                X = NewCodeEntry (OP65_STA, AM65_ZP, SB_GetConstBuf (&DstArg), 0, E->LI);
                CS_InsertEntry (S, X, ++Idx);
                if (ArgSize == BU_B16) {
                    SB_AppendStr (&SrcArg, E->Arg);
                    SB_AppendStr (&SrcArg, "+1");
                    SB_Terminate (&SrcArg);
                    X = NewCodeEntry (OP65_LDA, E->AM, SB_GetConstBuf (&SrcArg), 0, E->LI);
                    CS_InsertEntry (S, X, ++Idx);
                    SB_AppendStr (&DstArg, "+1");
                    SB_Terminate (&DstArg);
                    X = NewCodeEntry (OP65_STA, AM65_ZP, SB_GetConstBuf (&DstArg), 0, E->LI);
                    CS_InsertEntry (S, X, ++Idx);
                }
                break;

            case BU_REG:
                CHECK (ArgSize == BU_B8 && B->Where == REG_Y);
                if (E->AM == AM65_ZP || E->AM == AM65_ABS) {
                    X = NewCodeEntry (OP65_LDY, E->AM, E->Arg, 0, E->LI);
                    CS_InsertEntry (S, X, ++Idx);
                } else {
                    X = NewCodeEntry (OP65_LDA, E->AM, E->Arg, 0, E->LI);
                    CS_InsertEntry (S, X, ++Idx);
                    X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, E->LI);
                    CS_InsertEntry (S, X, ++Idx);
                }
                break;

            case BU_SP6502:
                X = NewCodeEntry (OP65_LDA, E->AM, E->Arg, 0, E->LI);
                CS_InsertEntry (S, X, ++Idx);
                X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, E->LI);
                CS_InsertEntry (S, X, ++Idx);
                if (ArgSize == BU_B16) {
                    SB_AppendStr (&SrcArg, E->Arg);
                    SB_AppendStr (&SrcArg, "+1");
                    SB_Terminate (&SrcArg);
                    X = NewCodeEntry (OP65_LDA, E->AM, SB_GetConstBuf (&SrcArg), 0, E->LI);
                    CS_InsertEntry (S, X, ++Idx);
                    X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, E->LI);
                    CS_InsertEntry (S, X, ++Idx);
                }
                break;

            case BU_SP:
                X = NewCodeEntry (OP65_LDA, E->AM, E->Arg, 0, E->LI);
                CS_InsertEntry (S, X, ++Idx);
                if (ArgSize != BU_B16) {
                    X = NewCodeEntry (OP65_JSR, AM65_ABS, "pusha", 0, E->LI);
                    CS_InsertEntry (S, X, ++Idx);
                } else {
                    SB_AppendStr (&SrcArg, E->Arg);
                    SB_AppendStr (&SrcArg, "+1");
                    SB_Terminate (&SrcArg);
                    if ((B->ZPUsage & REG_X) == 0) {
                        if (E->AM == AM65_ZP) {
                            X = NewCodeEntry (OP65_LDX, E->AM, SB_GetConstBuf (&SrcArg), 0, E->LI);
                            CS_InsertEntry (S, X, ++Idx);
                            X = NewCodeEntry (OP65_JSR, AM65_ABS, "pushax", 0, E->LI);
                            CS_InsertEntry (S, X, ++Idx);
                        } else {
                            X = NewCodeEntry (OP65_LDA, E->AM, SB_GetConstBuf (&SrcArg), 0, E->LI);
                            CS_InsertEntry (S, X, ++Idx);
                            X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, E->LI);
                            CS_InsertEntry (S, X, ++Idx);
                            X = NewCodeEntry (OP65_JSR, AM65_ABS, "pushax", 0, E->LI);
                            CS_InsertEntry (S, X, ++Idx);
                        }
                    } else {
                        X = NewCodeEntry (OP65_JSR, AM65_ABS, "pusha", 0, E->LI);
                        CS_InsertEntry (S, X, ++Idx);
                        X = NewCodeEntry (OP65_LDA, AM65_ZP, SB_GetConstBuf (&DstArg), 0, E->LI);
                        CS_InsertEntry (S, X, ++Idx);
                        X = NewCodeEntry (OP65_JSR, AM65_ABS, "pusha", 0, E->LI);
                        CS_InsertEntry (S, X, ++Idx);
                    }
                }
                break;
            }

            /* Adjust all indices at once */
            AdjustEntryIndices (Indices, OldIdx + 1, Idx - OldIdx);

            /* Done */
            return 1;
        }
    } else if (E->OPC == OP65_JSR) {
        /* For function calls we load their arguments instead */
        GetFuncInfo (E->Arg, &Use, &Chg);
        if ((Use & ~REG_AXY) == 0) {
            if (Use == REG_A) {
                ArgSize = BU_B8;
                return BackupAAfter (S, B, Idx, Indices);
            } else if (Use == REG_AX) {
                ArgSize = BU_B16;
                return BackupAXAfter (S, B, Idx, Indices);
            } else if (Use == REG_AXY) {
                /* This is actually a 16-bit word plus a 8-bit byte */
                ArgSize = BU_B24;
                return BackupAXYAfter (S, B, Idx, Indices);
            }

            /* We don't recognize other usage patterns for now */
        }
    }

    SB_Done (&SrcArg);
    SB_Done (&DstArg);

    /* Unable to do backup */
    return 0;
}

static int LoadAAt (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices, int After)
/* Reload into A the same arg according to LoadRegInfo before or after Idx
** depending on the After param.
*/
{
    CodeEntry* E;
    CodeEntry* O;       /* Old entry at Idx */
    CodeEntry* X;
    int Success = 0;
    int OldIdx;
    unsigned Use, Chg;

    /* Adjust the insertion point if necessary */
    if (After) {
        ++Idx;
    }
    OldIdx = Idx;

    E = LRI->LoadEntry;
    CHECK (E != 0);

    O = CS_GetEntry (S, OldIdx);

    /* We only recognize opc with an arg for now, as well as a special case for ldaxysp */
    if ((E->OPC != OP65_JSR || strcmp (E->Arg, "ldaxysp") == 0) &&
        E->AM != AM65_BRA && E->AM != AM65_IMP) {
        if (E->Size != 1 && E->AM != AM65_IMP) {

            /* FIXME: The load flags only reflect the situation by the time it reaches the range end */
            if ((LRI->Flags & (LI_DIRECT | LI_CHECK_ARG | LI_CHECK_Y)) != 0) {
                if ((LRI->Flags & LI_RELOAD_Y) != 0) {
                    if ((LRI->Flags & LI_CHECK_Y) == 0) {
                        X = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (LRI->Offs), 0, E->LI);
                    } else {
                        X = NewCodeEntry (OP65_LDY, LRI->LoadYEntry->AM, LRI->LoadYEntry->Arg, 0, E->LI);
                    }
                    CS_InsertEntry (S, X, Idx++);
                }
                X = NewCodeEntry (OP65_LDA, E->AM, E->Arg, 0, E->LI);
                CS_InsertEntry (S, X, Idx++);

                Success = 1;
            }
        }
    } else if (E->OPC == OP65_JSR) {

        /* For other function calls we load their arguments instead */
        GetFuncInfo (E->Arg, &Use, &Chg);
        if ((Use & ~REG_AXY) == 0) {
            if (Use == REG_X) {
                X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, E->LI);
                CS_InsertEntry (S, X, Idx++);
            } else if (Use == REG_Y) {
                X = NewCodeEntry (OP65_TYA, AM65_IMP, 0, 0, E->LI);
                CS_InsertEntry (S, X, Idx++);
            } else if (Use == REG_A) {
                /* nothing to do */
            } else {
                /* We don't recognize other usage patterns for now */
                return 0;
            }

            Success = 1;
        }
    }

    if (Success) {
        /* Adjust all indices at once */
        AdjustEntryIndices (Indices, OldIdx, Idx - OldIdx);

        /* Move labels if it was an insertion before Idx */
        CS_MoveLabels (S, O, CS_GetEntry (S, OldIdx));

        /* Done */
        return 1;
    }

    /* Unable to load */
    return 0;
}



static int LoadXAt (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices, int After)
/* Reload into X the same arg according to LoadRegInfo before or after Idx
** depending on the After param.
*/
{
    CodeEntry* E;
    CodeEntry* O;       /* Old entry at Idx */
    CodeEntry* X;
    int Success = 0;
    int OldIdx;
    unsigned Use, Chg;

    /* Adjust the insertion point if necessary */
    if (After) {
        ++Idx;
    }
    OldIdx = Idx;

    E = LRI->LoadEntry;
    CHECK (E != 0);

    O = CS_GetEntry (S, OldIdx);

    /* We only recognize opc with an arg for now, as well as a special case for ldaxysp */
    if ((E->OPC != OP65_JSR || strcmp (E->Arg, "ldaxysp") == 0) &&
        E->AM != AM65_BRA && E->AM != AM65_IMP) {
        if (E->Size != 1 && E->AM != AM65_IMP) {

            /* FIXME: The load flags only reflect the situation by the time it reaches the range end */
            if ((LRI->Flags & (LI_DIRECT | LI_CHECK_ARG | LI_CHECK_Y)) != 0) {
                if ((LRI->Flags & LI_RELOAD_Y) != 0) {
                    if ((LRI->Flags & LI_CHECK_Y) == 0) {
                        X = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (LRI->Offs), 0, E->LI);
                    } else {
                        X = NewCodeEntry (OP65_LDY, LRI->LoadYEntry->AM, LRI->LoadYEntry->Arg, 0, E->LI);
                    }
                    CS_InsertEntry (S, X, Idx++);

                    /* ldx does support AM65_ZPY and AM65_ABSY */
                    if (E->AM == AM65_ZPY || E->AM == AM65_ABSY) {
                        X = NewCodeEntry (OP65_LDX, E->AM, E->Arg, 0, E->LI);
                    } else {
                        X = NewCodeEntry (OP65_LDA, E->AM, E->Arg, 0, E->LI);
                        CS_InsertEntry (S, X, Idx++);
                        X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, E->LI);
                    }
                    CS_InsertEntry (S, X, Idx++);
                } else {
                    X = NewCodeEntry (OP65_LDX, E->AM, E->Arg, 0, E->LI);
                    CS_InsertEntry (S, X, Idx++);
                }

                Success = 1;
            }
        }
    } else if (E->OPC == OP65_JSR) {
        /* For function calls we load their arguments instead */
        GetFuncInfo (E->Arg, &Use, &Chg);
        if ((Use & ~REG_AXY) == 0) {
            if (Use == REG_A) {
                X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, E->LI);
                CS_InsertEntry (S, X, Idx++);
            } else if (Use == REG_Y) {
                X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, E->LI);
                CS_InsertEntry (S, X, Idx++);
                X = NewCodeEntry (OP65_TYA, AM65_IMP, 0, 0, E->LI);
                CS_InsertEntry (S, X, Idx++);
                X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, E->LI);
                CS_InsertEntry (S, X, Idx++);
                X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, E->LI);
                CS_InsertEntry (S, X, Idx++);
            } else if (Use == REG_X) {
                /* nothing to do */
            } else {
                /* We don't recognize other usage patterns for now */
                return 0;
            }

            Success = 1;
        }
    }

    if (Success) {
        /* Adjust all indices at once */
        AdjustEntryIndices (Indices, OldIdx, Idx - OldIdx);

        /* Move labels if it was an insertion before Idx */
        CS_MoveLabels (S, O, CS_GetEntry (S, OldIdx));

        /* Done */
        return 1;
    }

    /* Unable to load */
    return 0;
}



static int LoadYAt (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices, int After)
/* Reload into Y the same arg according to LoadRegInfo before or after Idx
** depending on the After param.
*/
{
    CodeEntry* E;
    CodeEntry* O;       /* Old entry at Idx */
    CodeEntry* X;
    int Success = 0;
    int OldIdx;
    unsigned Use, Chg;

    /* Adjust the insertion point if necessary */
    if (After) {
        ++Idx;
    }
    OldIdx = Idx;

    E = LRI->LoadEntry;
    CHECK (E != 0);

    O = CS_GetEntry (S, OldIdx);

    /* We only recognize opc with an arg for now, as well as a special case for ldaxysp */
    if ((E->OPC != OP65_JSR || strcmp (E->Arg, "ldaxysp") == 0) &&
        E->AM != AM65_BRA && E->AM != AM65_IMP) {
        if (E->Size != 1 && E->AM != AM65_IMP) {

            /* FIXME: The load flags only reflect the situation by the time it reaches the range end */
            if ((LRI->Flags & (LI_DIRECT | LI_CHECK_ARG | LI_CHECK_Y)) != 0) {
                if ((LRI->Flags & LI_RELOAD_Y) != 0) {
                    if ((LRI->Flags & LI_CHECK_Y) == 0) {
                        X = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (LRI->Offs), 0, E->LI);
                    } else {
                        X = NewCodeEntry (OP65_LDY, LRI->LoadYEntry->AM, LRI->LoadYEntry->Arg, 0, E->LI);
                    }
                    CS_InsertEntry (S, X, Idx++);
                    X = NewCodeEntry (OP65_LDA, E->AM, E->Arg, 0, E->LI);
                    CS_InsertEntry (S, X, Idx++);
                    X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, E->LI);
                    CS_InsertEntry (S, X, Idx++);
                } else {
                    X = NewCodeEntry (OP65_LDY, E->AM, E->Arg, 0, E->LI);
                    CS_InsertEntry (S, X, Idx++);
                }

                Success = 1;
            }
        }
    } else if (E->OPC == OP65_JSR) {
        /* For function calls we load their arguments instead */
        GetFuncInfo (E->Arg, &Use, &Chg);
        if ((Use & ~REG_AXY) == 0) {
            if (Use == REG_A) {
                X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, E->LI);
                CS_InsertEntry (S, X, Idx++);
            } else if (Use == REG_X) {
                X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, E->LI);
                CS_InsertEntry (S, X, Idx++);
                X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, E->LI);
                CS_InsertEntry (S, X, Idx++);
                X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, E->LI);
                CS_InsertEntry (S, X, Idx++);
                X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, E->LI);
                CS_InsertEntry (S, X, Idx++);
            } else if (Use == REG_Y) {
                /* nothing to do */
            } else {
                /* We don't recognize other usage patterns for now */
                return 0;
            }

            Success = 1;
        }
    }

    if (Success) {
        /* Adjust all indices at once */
        AdjustEntryIndices (Indices, OldIdx, Idx - OldIdx);

        /* Move labels if it was an insertion before Idx */
        CS_MoveLabels (S, O, CS_GetEntry (S, OldIdx));

        /* Done */
        return 1;
    }

    /* Unable to load */
    return 0;
}



int LoadABefore (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices)
/* Reload into A the same arg according to LoadRegInfo at Idx */
{
    return LoadAAt (S, Idx, LRI, Indices, 0);
}



int LoadXBefore (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices)
/* Reload into X the same arg according to LoadRegInfo at Idx */
{
    return LoadXAt (S, Idx, LRI, Indices, 0);
}



int LoadYBefore (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices)
/* Reload into Y the same arg according to LoadRegInfo at Idx */
{
    return LoadYAt (S, Idx, LRI, Indices, 0);
}



int LoadAAfter (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices)
/* Reload into A the same arg according to LoadRegInfo after Idx */
{
    return LoadAAt (S, Idx, LRI, Indices, 1);
}



int LoadXAfter (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices)
/* Reload into X the same arg according to LoadRegInfo after Idx */
{
    return LoadXAt (S, Idx, LRI, Indices, 1);
}



int LoadYAfter (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices)
/* Reload into Y the same arg according to LoadRegInfo after Idx */
{
    return LoadYAt (S, Idx, LRI, Indices, 1);
}



unsigned GetRegAccessedInOpenRange (CodeSeg* S, int First, int Last)
/* Get what ZPs, registers or processor states are used or changed in the range
** (First, Last).
** The code block must be basic without any jump backwards.
*/
{
    CodeEntry* X;
    unsigned ZPAccessed = 0;

    CHECK (Last <= (int)CollCount (&S->Entries));

    while (++First < Last) {
        X = CS_GetEntry (S, First);
        ZPAccessed |= X->Use | X->Chg;
    }

    return ZPAccessed;
}



unsigned GetRegUsageInOpenRange (CodeSeg* S, int First, int Last, unsigned* Use, unsigned* Chg)
/* Get what ZPs, registers or processor states are used or changed in the range
** (First, Last) in output parameters Use and Chg.
** Return what ZP regs are used before changed in this range.
** The code block must be basic without any jump backwards.
*/
{
    CodeEntry* X;
    unsigned U = 0;
    unsigned C = 0;

    CHECK (Last <= (int)CollCount (&S->Entries));

    /* Clear the output flags first */
    if (Use != 0) {
        *Use = 0;
    }
    if (Chg != 0) {
        *Chg = 0;
    }

    while (++First < Last) {
        X = CS_GetEntry (S, First);
        if (Use != 0) {
            *Use |= X->Use;
        }
        if (Chg != 0) {
            *Chg |= X->Chg;
        }
        /* Used before changed */
        U |= ~C & X->Use;
        C |= X->Chg;
    }

    return U;
}



int IsArgSameInOpenRange (CodeSeg* S, int First, int Last, CodeEntry* E)
/* Check if the loading the opc arg gives the same result everywhere between (First, Last).
** The code block in the range must be basic without any jump backwards.
** Note: this always checks Y if any of the LI_CHECK_Y / LI_RELOAD_Y flags is set.
*/
{
    LoadRegInfo LRI;
    CodeEntry*  X;
    unsigned CheckedFlags = LI_SRC_CHG;

    CHECK (Last <= (int)CollCount (&S->Entries));

    /* TODO: We'll currently give up finding the src of Y */
    ClearLoadRegInfo (&LRI);
    PrepairLoadRegInfoForArgCheck (S, &LRI, E);

    /* TODO: We don't currently check for all cases */
    if ((LRI.Flags & (LI_DIRECT | LI_CHECK_ARG | LI_CHECK_Y | LI_RELOAD_Y)) == 0) {
        /* Just bail out as if the src would change right away */
        return 0;
    }

    /* If there's no need to check */
    if ((LRI.Flags & (LI_CHECK_ARG | LI_CHECK_Y | LI_RELOAD_Y)) == 0) {
        return 1;
    }

    /* This always checks Y */
    if ((LRI.Flags & (LI_CHECK_Y | LI_RELOAD_Y)) != 0) {
        LRI.Flags |= LI_CHECK_Y;
        LRI.Flags &= ~LI_RELOAD_Y;
        CheckedFlags |= LI_Y_CHG;
    }

    while (++First < Last) {
        X = CS_GetEntry (S, First);
        if ((Affected (&LRI, X) & CheckedFlags) != 0) {
            return 0;
        }
    }

    /* No change found */
    return 1;
}



int FindArgFirstChangeInOpenRange (CodeSeg* S, int First, int Last, CodeEntry* E)
/* Find the first possible spot where the loaded arg of E might be changed in
** the range (First, Last). The code block in the range must be basic without
** any jump backwards.
** Return the index of the found entry, or Last if not found.
** Note: changes of Y are always ignored even if the LI_RELOAD_Y flag is not set.
*/
{
    LoadRegInfo LRI;
    CodeEntry* X;
    unsigned CheckedFlags = LI_SRC_CHG;

    CHECK (Last <= (int)CollCount (&S->Entries));

    /* TODO: We'll currently give up finding the src of Y */
    ClearLoadRegInfo (&LRI);
    PrepairLoadRegInfoForArgCheck (S, &LRI, E);

    /* TODO: We don't currently check for all cases */
    if ((LRI.Flags & (LI_DIRECT | LI_CHECK_ARG | LI_CHECK_Y)) == 0) {
        /* Just bail out as if the src would change right away */
        return First + 1;
    }

    /* If there's no need to check */
    if ((LRI.Flags & (LI_CHECK_ARG | LI_CHECK_Y)) == 0) {
        return Last;
    }

    while (++First < Last) {
        X = CS_GetEntry (S, First);
        if ((Affected (&LRI, X) & CheckedFlags) != 0) {
            return First;
        }
    }

    /* Not found */
    return Last;
}



int FindArgLastUsageInOpenRange (CodeSeg* S, int First, int Last, CodeEntry* E, int ReloadY)
/* Find the last index where the arg of E might be used or changed in the range (First, Last).
** ReloadY indicates whether Y is supposed to be reloaded.
** The code block in the range must be basic without any jump backwards.
** Return the index of the found entry, or First if not found.
*/
{
    LoadRegInfo LRI;
    CodeEntry* X;
    unsigned CheckedFlags = LI_SRC_USE | LI_SRC_CHG;
    int Found = First;

    CHECK (Last <= (int)CollCount (&S->Entries));

    /* TODO: We'll currently give up finding the src of Y */
    ClearLoadRegInfo (&LRI);
    PrepairLoadRegInfoForArgCheck (S, &LRI, E);

    /* Whether Y is to be reloaded */
    if (ReloadY) {
        /* Always reload Y */
        if ((LRI.Flags & LI_CHECK_Y) != 0) {
            LRI.Flags |= LI_RELOAD_Y;
        }
    } else if ((LRI.Flags & LI_RELOAD_Y) != 0) {
        /* Always check Y */
        LRI.Flags |= LI_CHECK_Y;
        LRI.Flags &= ~LI_RELOAD_Y;
    }

    /* TODO: We don't currently check for all cases */
    if ((LRI.Flags & (LI_DIRECT | LI_CHECK_ARG | LI_CHECK_Y | LI_RELOAD_Y)) == 0) {
        /* Just bail out as if the src would change everywhere */
        return First < Last ? Last - 1 : First;
    }

    if ((LRI.Flags & LI_CHECK_Y) != 0) {
        CheckedFlags |= LI_Y_SRC_USE | LI_Y_SRC_CHG;
    }

    if ((LRI.Flags & LI_RELOAD_Y) != 0) {
        CheckedFlags |= LI_Y_USE;
    } else if ((LRI.Flags & LI_CHECK_Y) != 0) {
        CheckedFlags |= LI_Y_CHG;
    }

    while (++First < Last) {
        X = CS_GetEntry (S, First);
        if ((Affected (&LRI, X) & CheckedFlags) != 0) {
            Found = First;
        }
    }

    /* Result */
    return Found;
}



int FindRegFirstChangeInOpenRange (CodeSeg* S, int First, int Last, unsigned what)
/* Find the first possible spot where the queried ZPs, registers and/or processor
** states might be changed in the range (First, Last). The code block in the
** range must be basic without any jump backwards.
** Return the index of the found entry, or Last if not found.
*/
{
    CodeEntry* X;

    CHECK (Last <= (int)CollCount (&S->Entries));

    while (++First < Last) {
        X = CS_GetEntry (S, First);
        if ((X->Chg & what) != 0) {
            return First;
        }
    }

    /* Not found */
    return Last;
}



int FindRegFirstUseInOpenRange (CodeSeg* S, int First, int Last, unsigned what)
/* Find the first possible spot where the queried ZPs, registers and/or processor
** states might be used in the range (First, Last). The code block in the range
** must be basic without any jump backwards.
** Return the index of the found entry, or Last if not found.
*/
{
    CodeEntry* X;

    CHECK (Last <= (int)CollCount (&S->Entries));

    while (++First < Last) {
        X = CS_GetEntry (S, First);
        if ((X->Use & what) != 0) {
            return First;
        }
    }

    /* Not found */
    return Last;
}



int FindRegLastChangeInOpenRange (CodeSeg* S, int First, int Last, unsigned what)
/* Find the last possible spot where the queried ZPs, registers and/or processor
** states might be changed in the range (First, Last). The code block in the
** range must be basic without any jump backwards.
** Return the index of the found entry, or First if not found.
*/
{
    CodeEntry* X;
    int Found = First;

    CHECK (Last <= (int)CollCount (&S->Entries));

    while (++First < Last) {
        X = CS_GetEntry (S, First);
        if ((X->Chg & what) != 0) {
            Found = First;
        }
    }

    return Found;
}



int FindRegLastUseInOpenRange (CodeSeg* S, int First, int Last, unsigned what)
/* Find the last possible spot where the queried ZPs, registers and/or processor
** states might be used in the range (First, Last). The code block in the range
** must be basic without any jump backwards.
** Return the index of the found entry, or First if not found.
*/
{
    CodeEntry* X;
    int Found = First;

    CHECK (Last <= (int)CollCount (&S->Entries));

    while (++First < Last) {
        X = CS_GetEntry (S, First);
        if ((X->Use & what) != 0) {
            Found = First;
        }
    }

    return Found;
}
