]> git.sur5r.net Git - cc65/blobdiff - src/cc65/coptstop.c
Fixed a bug that caused problems locating the last parameter of a function
[cc65] / src / cc65 / coptstop.c
index 697a0b5630433b80f2eff7bd7b2caf6d62559ce2..4f83b8ef5b64de7d7965b165c56d494eb299d391 100644 (file)
 
 
 #include <stdlib.h>
+#include <ctype.h>
 
 /* cc65 */
 #include "codeent.h"
 #include "codeinfo.h"
-#include "codeopt.h"
-#include "error.h"
 #include "coptstop.h"
 
 
 
 /*****************************************************************************/
-/*                                 Helpers                                  */
+/*                                   Data                                    */
+/*****************************************************************************/
+
+
+
+/* Structure that holds the needed data */
+typedef struct StackOpData StackOpData;
+struct StackOpData {
+    CodeSeg*    Code;                   /* Pointer to code segment */
+    unsigned    Flags;                  /* Flags to remember things */
+    unsigned    PushIndex;              /* Index of call to pushax in codeseg */
+    unsigned    OpIndex;                /* Index of actual operation */
+    CodeEntry*  PrevEntry;              /* Entry before the call to pushax */
+    CodeEntry*  PushEntry;              /* Pointer to entry with call to pushax */
+    CodeEntry*  OpEntry;                /* Pointer to entry with op */
+    CodeEntry*  NextEntry;              /* Entry after the op */
+    const char* ZPLo;                   /* Lo byte of zero page loc to use */
+    const char* ZPHi;                   /* Hi byte of zero page loc to use */
+    unsigned    IP;                     /* Insertion point used by some routines */
+};
+
+/* Flags returned by DirectOp */
+#define OP_DIRECT       0x01            /* Direct op may be used */
+#define OP_RELOAD_Y     0x02            /* Must reload index register Y */
+
+
+
+/*****************************************************************************/
+/*                                         Helpers                                  */
 /*****************************************************************************/
 
 
 
 static unsigned AdjustStackOffset (CodeSeg* S, unsigned Start, unsigned Stop,
-                                  unsigned Offs)
+                                  unsigned Offs)
 /* Adjust the offset for all stack accesses in the range Start to Stop, both
  * inclusive. The function returns the number of instructions that have been
  * inserted.
@@ -66,14 +93,27 @@ static unsigned AdjustStackOffset (CodeSeg* S, unsigned Start, unsigned Stop,
 
        CodeEntry* E = CS_GetEntry (S, I);
 
-       if (E->Use & REG_SP) {
-
-           CodeEntry* P;
+        int NeedCorrection = 0;
+       if ((E->Use & REG_SP) != 0) {
 
            /* Check for 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 */
+            NeedCorrection = 1;
+
+        } else if (CE_IsCallTo (E, "ldaxysp")) {
+
+            /* We need to correct this one */
+            NeedCorrection = 1;
+
+        }
+
+        if (NeedCorrection) {
+
+           CodeEntry* P;
+
            /* Get the code entry before this one. If it's a LDY, adjust the
             * value.
             */
@@ -111,40 +151,212 @@ static unsigned AdjustStackOffset (CodeSeg* S, unsigned Start, unsigned Stop,
 
 
 
+static void InsertEntry (StackOpData* D, CodeEntry* E, unsigned 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 the indices if necessary */
+    if (D->PushEntry && Index <= D->PushIndex) {
+        ++D->PushIndex;
+    }
+    if (D->OpEntry && Index <= D->OpIndex) {
+        ++D->OpIndex;
+    }
+}
+
+
+
+static void DelEntry (StackOpData* D, unsigned 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 the 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;
+    }
+}
+
+
+
+static void CheckDirectOp (StackOpData* D)
+/* Check if the given entry is a lda instruction with an addressing mode
+ * that allows us to replace it by another operation (like ora). If so, we may
+ * use this location for the or and must not save the value in the zero
+ * page location.
+ */
+{
+    /* We need the entry before the push */
+    CodeEntry* E;
+    CHECK ((E = D->PrevEntry) != 0);
+
+    if (E->OPC == OP65_LDA) {
+        if (E->AM == AM65_IMM || E->AM == AM65_ZP || E->AM == AM65_ABS) {
+            /* These insns are all ok and replaceable */
+            D->Flags |= OP_DIRECT;
+        } else if (E->AM == AM65_ZP_INDY && RegValIsKnown (E->RI->In.RegY) &&
+                   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.
+             */
+            D->Flags |= (OP_DIRECT | OP_RELOAD_Y);
+        }
+    }
+}
+
+
+
+static 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).
+ */
+{
+    CodeEntry* X;
+
+    /* Store the value into the zeropage instead of pushing it */
+    X = NewCodeEntry (OP65_STX, AM65_ZP, D->ZPHi, 0, D->PushEntry->LI);
+    InsertEntry (D, X, D->PushIndex+1);
+    if ((D->Flags & OP_DIRECT) == 0) {
+       X = NewCodeEntry (OP65_STA, AM65_ZP, D->ZPLo, 0, D->PushEntry->LI);
+               InsertEntry (D, X, D->PushIndex+1);
+    }
+}
+
+
+
+static void AddOpLow (StackOpData* D, opc_t OPC)
+/* 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 ((D->Flags & OP_DIRECT) != 0) {
+               /* Op with a variable location. If the location is on the stack, we
+         * need to reload the Y register.
+         */
+        if ((D->Flags & OP_RELOAD_Y) != 0) {
+            const char* Arg = MakeHexArg (D->PrevEntry->RI->In.RegY);
+            X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, D->OpEntry->LI);
+            InsertEntry (D, X, D->IP++);
+        }
+       X = NewCodeEntry (OPC, D->PrevEntry->AM, D->PrevEntry->Arg, 0, D->OpEntry->LI);
+    } else {
+       /* Op with temp storage */
+       X = NewCodeEntry (OPC, AM65_ZP, D->ZPLo, 0, D->OpEntry->LI);
+    }
+    InsertEntry (D, X, D->IP++);
+}
+
+
+
+static void AddOpHigh (StackOpData* D, opc_t OPC)
+/* 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;
+
+    /* High byte is unknown */
+    X = NewCodeEntry (OP65_STA, AM65_ZP, D->ZPLo, 0, D->OpEntry->LI);
+    InsertEntry (D, X, D->IP++);
+    X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, D->OpEntry->LI);
+    InsertEntry (D, X, D->IP++);
+    X = NewCodeEntry (OPC, AM65_ZP, D->ZPHi, 0, D->OpEntry->LI);
+    InsertEntry (D, X, D->IP++);
+    X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, D->OpEntry->LI);
+    InsertEntry (D, X, D->IP++);
+    X = NewCodeEntry (OP65_LDA, AM65_ZP, D->ZPLo, 0, D->OpEntry->LI);
+    InsertEntry (D, X, D->IP++);
+}
+
+
+
+static void RemovePushAndOp (StackOpData* D)
+/* Remove the call to pushax and the call to the operator subroutine */
+{
+    DelEntry (D, D->OpIndex);
+    DelEntry (D, D->PushIndex);
+}
+
+
+
+static const char* IsRegVar (const StackOpData* D)
+/* If the value pushed is that of a register variable, return the name of the
+ * entry in the register bank. Otherwise return NULL.
+ */
+{
+    CodeEntry* P;
+
+    if (D->PushIndex >= 2                                &&
+        (P = D->PrevEntry) != 0                          &&
+        P->OPC == OP65_LDX                               &&
+        P->AM == AM65_ZP                                 &&
+        strncmp (P->Arg, "regbank+", 7) == 0             &&
+        isdigit (P->Arg[8])                              &&
+        (P = CS_GetEntry (D->Code, D->PushIndex-2)) != 0 &&
+        P->OPC == OP65_LDA                               &&
+        P->AM == AM65_ZP                                 &&
+        strncmp (P->Arg, "regbank+", 7) == 0             &&
+        isdigit (P->Arg[8])) {
+        /* Ok, it loads the register variable */
+        return P->Arg;
+    } else {
+        return 0;
+    }
+}
+
+
+
 /*****************************************************************************/
-/*                      Actual optimization functions                       */
+/*                      Actual optimization functions                       */
 /*****************************************************************************/
 
 
 
-static unsigned Opt_staspidx (CodeSeg* S, unsigned Push, unsigned Store,
-                             const char* ZPLo, const char* ZPHi)
+static unsigned Opt_staspidx (StackOpData* D)
 /* Optimize the staspidx sequence if possible */
 {
     CodeEntry* X;
-    CodeEntry* PushEntry;
-    CodeEntry* StoreEntry;
+    const char* ZPLo;
 
-    /* Get the push entry */
-    PushEntry = CS_GetEntry (S, Push);
+    /* Check if we're using a register variable */
+    if ((ZPLo = IsRegVar (D)) == 0) {
 
-    /* Store the value into the zeropage instead of pushing it */
-    X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, PushEntry->LI);
-    CS_InsertEntry (S, X, Push+1);
-    X = NewCodeEntry (OP65_STX, AM65_ZP, ZPHi, 0, PushEntry->LI);
-    CS_InsertEntry (S, X, Push+2);
+        /* Store the value into the zeropage instead of pushing it */
+        ReplacePushByStore (D);
 
-    /* Correct the index of the store and get a pointer to the entry */
-    Store += 2;
-    StoreEntry = CS_GetEntry (S, Store);
+        /* Use the given zero page loc */
+        ZPLo = D->ZPLo;
+    }
 
-    /* Inline the store */
-    X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, ZPLo, 0, StoreEntry->LI);
-    CS_InsertEntry (S, X, Store+1);
+    /* Replace the store subroutine call by a direct op */
+    X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, ZPLo, 0, D->OpEntry->LI);
+    InsertEntry (D, X, D->OpIndex+1);
 
     /* Remove the push and the call to the staspidx function */
-    CS_DelEntry (S, Store);
-    CS_DelEntry (S, Push);
+    RemovePushAndOp (D);
 
     /* We changed the sequence */
     return 1;
@@ -152,47 +364,41 @@ static unsigned Opt_staspidx (CodeSeg* S, unsigned Push, unsigned Store,
 
 
 
-static unsigned Opt_staxspidx (CodeSeg* S, unsigned Push, unsigned Store,
-                              const char* ZPLo, const char* ZPHi)
+static unsigned Opt_staxspidx (StackOpData* D)
 /* Optimize the staxspidx sequence if possible */
 {
     CodeEntry* X;
-    CodeEntry* PushEntry;
-    CodeEntry* StoreEntry;
+    const char* ZPLo;
 
-    /* Get the push entry */
-    PushEntry = CS_GetEntry (S, Push);
+    /* Check if we're using a register variable */
+    if ((ZPLo = IsRegVar (D)) == 0) {
 
-    /* Store the value into the zeropage instead of pushing it */
-    X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, PushEntry->LI);
-    CS_InsertEntry (S, X, Push+1);
-    X = NewCodeEntry (OP65_STX, AM65_ZP, ZPHi, 0, PushEntry->LI);
-    CS_InsertEntry (S, X, Push+2);
+        /* Store the value into the zeropage instead of pushing it */
+        ReplacePushByStore (D);
 
-    /* Correct the index of the store and get a pointer to the entry */
-    Store += 2;
-    StoreEntry = CS_GetEntry (S, Store);
+        /* Use the given zero page loc */
+        ZPLo = D->ZPLo;
+    }
 
     /* Inline the store */
-    X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, ZPLo, 0, StoreEntry->LI);
-    CS_InsertEntry (S, X, Store+1);
-    X = NewCodeEntry (OP65_INY, AM65_IMP, 0, 0, StoreEntry->LI);
-    CS_InsertEntry (S, X, Store+2);
-    if (StoreEntry->RI->In.RegX >= 0) {
+    X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, ZPLo, 0, D->OpEntry->LI);
+    InsertEntry (D, X, D->OpIndex+1);
+    X = NewCodeEntry (OP65_INY, AM65_IMP, 0, 0, D->OpEntry->LI);
+    InsertEntry (D, X, D->OpIndex+2);
+    if (RegValIsKnown (D->OpEntry->RI->In.RegX)) {
        /* Value of X is known */
-       const char* Arg = MakeHexArg (StoreEntry->RI->In.RegX);
-               X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, StoreEntry->LI);
+       const char* Arg = MakeHexArg (D->OpEntry->RI->In.RegX);
+               X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, D->OpEntry->LI);
     } else {
        /* Value unknown */
-       X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, StoreEntry->LI);
+       X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, D->OpEntry->LI);
     }
-    CS_InsertEntry (S, X, Store+3);
-    X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, ZPLo, 0, StoreEntry->LI);
-    CS_InsertEntry (S, X, Store+4);
+    InsertEntry (D, X, D->OpIndex+3);
+    X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, ZPLo, 0, D->OpEntry->LI);
+    InsertEntry (D, X, D->OpIndex+4);
 
     /* Remove the push and the call to the staspidx function */
-    CS_DelEntry (S, Store);
-    CS_DelEntry (S, Push);
+    RemovePushAndOp (D);
 
     /* We changed the sequence */
     return 1;
@@ -200,100 +406,65 @@ static unsigned Opt_staxspidx (CodeSeg* S, unsigned Push, unsigned Store,
 
 
 
-static unsigned Opt_tosaddax (CodeSeg* S, unsigned Push, unsigned Add,
-                             const char* ZPLo, const char* ZPHi)
+static unsigned Opt_tosaddax (StackOpData* D)
 /* Optimize the tosaddax sequence if possible */
 {
-    CodeEntry* P;
-    CodeEntry* N;
-    CodeEntry* X;
-    CodeEntry* PushEntry;
-    CodeEntry* AddEntry;
-    int        DirectAdd;
+    CodeEntry*  X;
 
 
     /* We need the entry behind the add */
-    CHECK ((N = CS_GetNextEntry (S, Add)) != 0);
-
-    /* And the entry before the push */
-    CHECK ((P = CS_GetPrevEntry (S, Push)) != 0);
+    CHECK (D->NextEntry != 0);
 
-    /* Get the push entry */
-    PushEntry = CS_GetEntry (S, Push);
-
-    /* Check the entry before the push, if it's a lda instruction with an
-     * addressing mode that does not use an additional index register. If
-     * so, we may use this location for the add and must not save the
-     * value in the zero page location.
+    /* Check the entry before the push. If it's a lda instruction with an
+     * addressing mode that allows us to replace it, we may use this
+     * location for the op and must not save the value in the zero page
+     * location.
      */
-    DirectAdd = (P->OPC == OP65_LDA &&
-                (P->AM == AM65_IMM || P->AM == AM65_ZP || P->AM == AM65_ABS));
+    CheckDirectOp (D);
 
     /* Store the value into the zeropage instead of pushing it */
-    X = NewCodeEntry (OP65_STX, AM65_ZP, ZPHi, 0, PushEntry->LI);
-    CS_InsertEntry (S, X, Push+1);
-    ++Add;      /* Correct the index */
-    if (!DirectAdd) {
-       X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, PushEntry->LI);
-       CS_InsertEntry (S, X, Push+1);
-       ++Add;  /* Correct the index */
-    }
-
-    /* Get a pointer to the add entry */
-    AddEntry = CS_GetEntry (S, Add);
+    ReplacePushByStore (D);
 
     /* Inline the add */
-    X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, AddEntry->LI);
-    CS_InsertEntry (S, X, Add+1);
-    if (DirectAdd) {
-       /* Add from variable location */
-       X = NewCodeEntry (OP65_ADC, P->AM, P->Arg, 0, AddEntry->LI);
-    } else {
-       /* Add from temp storage */
-       X = NewCodeEntry (OP65_ADC, AM65_ZP, ZPLo, 0, AddEntry->LI);
-    }
-    CS_InsertEntry (S, X, Add+2);
-    if (PushEntry->RI->In.RegX == 0) {
+    D->IP = D->OpIndex+1;
+    X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, D->OpEntry->LI);
+    InsertEntry (D, X, D->IP++);
+
+    /* Low byte */
+    AddOpLow (D, OP65_ADC);
+
+    /* High byte */
+    if (D->PushEntry->RI->In.RegX == 0) {
        /* The high byte is the value in X plus the carry */
-       CodeLabel* L = CS_GenLabel (S, N);
-       X = NewCodeEntry (OP65_BCC, AM65_BRA, L->Name, L, AddEntry->LI);
-       CS_InsertEntry (S, X, Add+3);
-       X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, AddEntry->LI);
-       CS_InsertEntry (S, X, Add+4);
-    } else if (AddEntry->RI->In.RegX == 0) {
-       /* The high byte is that of the first operand plus carry */
+       CodeLabel* L = CS_GenLabel (D->Code, D->NextEntry);
+       X = NewCodeEntry (OP65_BCC, AM65_BRA, L->Name, L, D->OpEntry->LI);
+       InsertEntry (D, X, D->IP++);
+       X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, D->OpEntry->LI);
+       InsertEntry (D, X, D->IP++);
+    } else if (D->OpEntry->RI->In.RegX == 0) {
+               /* The high byte is that of the first operand plus carry */
        CodeLabel* L;
-       if (PushEntry->RI->In.RegX >= 0) {
+       if (RegValIsKnown (D->PushEntry->RI->In.RegX)) {
            /* Value of first op high byte is known */
-           const char* Arg = MakeHexArg (PushEntry->RI->In.RegX);
-           X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, AddEntry->LI);
+           const char* Arg = MakeHexArg (D->PushEntry->RI->In.RegX);
+           X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, D->OpEntry->LI);
        } else {
            /* Value of first op high byte is unknown */
-           X = NewCodeEntry (OP65_LDX, AM65_ZP, ZPHi, 0, AddEntry->LI);
+           X = NewCodeEntry (OP65_LDX, AM65_ZP, D->ZPHi, 0, D->OpEntry->LI);
        }
-       CS_InsertEntry (S, X, Add+3);
-       L = CS_GenLabel (S, N);
-       X = NewCodeEntry (OP65_BCC, AM65_BRA, L->Name, L, AddEntry->LI);
-       CS_InsertEntry (S, X, Add+4);
-       X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, AddEntry->LI);
-       CS_InsertEntry (S, X, Add+5);
+       InsertEntry (D, X, D->IP++);
+       L = CS_GenLabel (D->Code, D->NextEntry);
+       X = NewCodeEntry (OP65_BCC, AM65_BRA, L->Name, L, D->OpEntry->LI);
+       InsertEntry (D, X, D->IP++);
+       X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, D->OpEntry->LI);
+       InsertEntry (D, X, D->IP++);
     } else {
        /* High byte is unknown */
-       X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, AddEntry->LI);
-       CS_InsertEntry (S, X, Add+3);
-       X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, AddEntry->LI);
-       CS_InsertEntry (S, X, Add+4);
-       X = NewCodeEntry (OP65_ADC, AM65_ZP, ZPHi, 0, AddEntry->LI);
-       CS_InsertEntry (S, X, Add+5);
-       X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, AddEntry->LI);
-       CS_InsertEntry (S, X, Add+6);
-       X = NewCodeEntry (OP65_LDA, AM65_ZP, ZPLo, 0, AddEntry->LI);
-       CS_InsertEntry (S, X, Add+7);
+        AddOpHigh (D, OP65_ADC);
     }
 
     /* Remove the push and the call to the tosaddax function */
-    CS_DelEntry (S, Add);
-    CS_DelEntry (S, Push);
+    RemovePushAndOp (D);
 
     /* We changed the sequence */
     return 1;
@@ -301,73 +472,37 @@ static unsigned Opt_tosaddax (CodeSeg* S, unsigned Push, unsigned Add,
 
 
 
-static unsigned Opt_tosandax (CodeSeg* S, unsigned Push, unsigned And,
-                             const char* ZPLo, const char* ZPHi)
+static unsigned Opt_tosandax (StackOpData* D)
 /* Optimize the tosandax sequence if possible */
 {
-    CodeEntry* P;
-    CodeEntry* X;
-    CodeEntry* PushEntry;
-    CodeEntry* AndEntry;
-    int        DirectAnd;
-
-    /* Get the entry before the push */
-    CHECK ((P = CS_GetPrevEntry (S, Push)) != 0);
+    CodeEntry*  X;
 
-    /* Get the push entry */
-    PushEntry = CS_GetEntry (S, Push);
-
-    /* Check the entry before the push, if it's a lda instruction with an
-     * addressing mode that does not use an additional index register. If
-     * so, we may use this location for the and and must not save the
-     * value in the zero page location.
+    /* Check the entry before the push. If it's a lda instruction with an
+     * addressing mode that allows us to replace it, we may use this
+     * location for the op and must not save the value in the zero page
+     * location.
      */
-    DirectAnd = (P->OPC == OP65_LDA &&
-                (P->AM == AM65_IMM || P->AM == AM65_ZP || P->AM == AM65_ABS));
+    CheckDirectOp (D);
 
     /* Store the value into the zeropage instead of pushing it */
-    X = NewCodeEntry (OP65_STX, AM65_ZP, ZPHi, 0, PushEntry->LI);
-    CS_InsertEntry (S, X, Push+1);
-    ++And;      /* Correct the index */
-    if (!DirectAnd) {
-       X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, PushEntry->LI);
-       CS_InsertEntry (S, X, Push+1);
-       ++And;  /* Correct the index */
-    }
+    ReplacePushByStore (D);
 
-    /* Get a pointer to the and entry */
-    AndEntry = CS_GetEntry (S, And);
+    /* Inline the and, low byte */
+    D->IP = D->OpIndex + 1;
+    AddOpLow (D, OP65_AND);
 
-    /* Inline the and */
-    if (DirectAnd) {
-       /* And with variable location */
-       X = NewCodeEntry (OP65_AND, P->AM, P->Arg, 0, AndEntry->LI);
-    } else {
-       /* And with temp storage */
-       X = NewCodeEntry (OP65_AND, AM65_ZP, ZPLo, 0, AndEntry->LI);
-    }
-    CS_InsertEntry (S, X, And+1);
-    if (PushEntry->RI->In.RegX == 0 || AndEntry->RI->In.RegX == 0) {
+    /* High byte */
+    if (D->PushEntry->RI->In.RegX == 0 || D->OpEntry->RI->In.RegX == 0) {
        /* The high byte is zero */
-               X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, AndEntry->LI);
-       CS_InsertEntry (S, X, And+2);
+               X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, D->OpEntry->LI);
+       InsertEntry (D, X, D->IP++);
     } else {
        /* High byte is unknown */
-               X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, AndEntry->LI);
-       CS_InsertEntry (S, X, And+2);
-       X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, AndEntry->LI);
-       CS_InsertEntry (S, X, And+3);
-       X = NewCodeEntry (OP65_AND, AM65_ZP, ZPHi, 0, AndEntry->LI);
-       CS_InsertEntry (S, X, And+4);
-       X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, AndEntry->LI);
-       CS_InsertEntry (S, X, And+5);
-       X = NewCodeEntry (OP65_LDA, AM65_ZP, ZPLo, 0, AndEntry->LI);
-       CS_InsertEntry (S, X, And+6);
+        AddOpHigh (D, OP65_AND);
     }
 
     /* Remove the push and the call to the tosandax function */
-    CS_DelEntry (S, And);
-    CS_DelEntry (S, Push);
+    RemovePushAndOp (D);
 
     /* We changed the sequence */
     return 1;
@@ -375,73 +510,39 @@ static unsigned Opt_tosandax (CodeSeg* S, unsigned Push, unsigned And,
 
 
 
-static unsigned Opt_tosorax (CodeSeg* S, unsigned Push, unsigned Or,
-                            const char* ZPLo, const char* ZPHi)
+static unsigned Opt_tosorax (StackOpData* D)
 /* Optimize the tosorax sequence if possible */
 {
-    CodeEntry* P;
-    CodeEntry* X;
-    CodeEntry* PushEntry;
-    CodeEntry* OrEntry;
-    int        DirectOr;
-
-    /* Get the entry before the push */
-    CHECK ((P = CS_GetPrevEntry (S, Push)) != 0);
+    CodeEntry*  X;
 
-    /* Get the push entry */
-    PushEntry = CS_GetEntry (S, Push);
-
-    /* Check the entry before the push, if it's a lda instruction with an
-     * addressing mode that does not use an additional index register. If
-     * so, we may use this location for the or and must not save the
-     * value in the zero page location.
+    /* Check the entry before the push. If it's a lda instruction with an
+     * addressing mode that allows us to replace it, we may use this
+     * location for the op and must not save the value in the zero page
+     * location.
      */
-    DirectOr = (P->OPC == OP65_LDA &&
-               (P->AM == AM65_IMM || P->AM == AM65_ZP || P->AM == AM65_ABS));
+    CheckDirectOp (D);
 
     /* Store the value into the zeropage instead of pushing it */
-    X = NewCodeEntry (OP65_STX, AM65_ZP, ZPHi, 0, PushEntry->LI);
-    CS_InsertEntry (S, X, Push+1);
-    ++Or;  /* Correct the index */
-    if (!DirectOr) {
-       X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, PushEntry->LI);
-       CS_InsertEntry (S, X, Push+1);
-       ++Or;  /* Correct the index */
-    }
+    ReplacePushByStore (D);
 
-    /* Get a pointer to the or entry */
-    OrEntry = CS_GetEntry (S, Or);
+    /* Inline the or, low byte */
+    D->IP = D->OpIndex + 1;
+    AddOpLow (D, OP65_ORA);
 
-    /* Inline the or */
-    if (DirectOr) {
-       /* Or with variable location */
-       X = NewCodeEntry (OP65_ORA, P->AM, P->Arg, 0, OrEntry->LI);
-    } else {
-       X = NewCodeEntry (OP65_ORA, AM65_ZP, ZPLo, 0, OrEntry->LI);
-    }
-    CS_InsertEntry (S, X, Or+1);
-    if (PushEntry->RI->In.RegX >= 0 && OrEntry->RI->In.RegX >= 0) {
+    /* High byte */
+    if (RegValIsKnown (D->PushEntry->RI->In.RegX) &&
+        RegValIsKnown (D->OpEntry->RI->In.RegX)) {
        /* Both values known, precalculate the result */
-       const char* Arg = MakeHexArg (PushEntry->RI->In.RegX | OrEntry->RI->In.RegX);
-               X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, OrEntry->LI);
-       CS_InsertEntry (S, X, Or+2);
-    } else if (PushEntry->RI->In.RegX != 0) {
+       const char* Arg = MakeHexArg (D->PushEntry->RI->In.RegX | D->OpEntry->RI->In.RegX);
+               X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, D->OpEntry->LI);
+       InsertEntry (D, X, D->IP++);
+    } else if (D->PushEntry->RI->In.RegX != 0) {
        /* High byte is unknown */
-               X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, OrEntry->LI);
-       CS_InsertEntry (S, X, Or+2);
-       X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, OrEntry->LI);
-       CS_InsertEntry (S, X, Or+3);
-               X = NewCodeEntry (OP65_ORA, AM65_ZP, ZPHi, 0, OrEntry->LI);
-       CS_InsertEntry (S, X, Or+4);
-       X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, OrEntry->LI);
-       CS_InsertEntry (S, X, Or+5);
-       X = NewCodeEntry (OP65_LDA, AM65_ZP, ZPLo, 0, OrEntry->LI);
-       CS_InsertEntry (S, X, Or+6);
+        AddOpHigh (D, OP65_ORA);
     }
 
-    /* Remove the push and the call to the tosandax function */
-    CS_DelEntry (S, Or);
-    CS_DelEntry (S, Push);
+    /* Remove the push and the call to the tosorax function */
+    RemovePushAndOp (D);
 
     /* We changed the sequence */
     return 1;
@@ -449,74 +550,39 @@ static unsigned Opt_tosorax (CodeSeg* S, unsigned Push, unsigned Or,
 
 
 
-static unsigned Opt_tosxorax (CodeSeg* S, unsigned Push, unsigned Xor,
-                             const char* ZPLo, const char* ZPHi)
+static unsigned Opt_tosxorax (StackOpData* D)
 /* Optimize the tosxorax sequence if possible */
 {
-    CodeEntry* P;
-    CodeEntry* X;
-    CodeEntry* PushEntry;
-    CodeEntry* XorEntry;
-    int        DirectXor;
-
-    /* Get the entry before the push */
-    CHECK ((P = CS_GetPrevEntry (S, Push)) != 0);
-
-    /* Get the push entry */
-    PushEntry = CS_GetEntry (S, Push);
+    CodeEntry*  X;
 
-    /* Check the entry before the push, if it's a lda instruction with an
-     * addressing mode that does not use an additional index register. If
-     * so, we may use this location for the xor and must not save the
-     * value in the zero page location.
+    /* Check the entry before the push. If it's a lda instruction with an
+     * addressing mode that allows us to replace it, we may use this
+     * location for the op and must not save the value in the zero page
+     * location.
      */
-    DirectXor = (P->OPC == OP65_LDA &&
-                (P->AM == AM65_IMM || P->AM == AM65_ZP || P->AM == AM65_ABS));
+    CheckDirectOp (D);
 
     /* Store the value into the zeropage instead of pushing it */
-    X = NewCodeEntry (OP65_STX, AM65_ZP, ZPHi, 0, PushEntry->LI);
-    CS_InsertEntry (S, X, Push+1);
-    ++Xor;  /* Correct the index */
-    if (!DirectXor) {
-       X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, PushEntry->LI);
-       CS_InsertEntry (S, X, Push+1);
-       ++Xor;  /* Correct the index */
-    }
+    ReplacePushByStore (D);
 
-    /* Get a pointer to the entry */
-    XorEntry = CS_GetEntry (S, Xor);
+    /* Inline the xor, low byte */
+    D->IP = D->OpIndex + 1;
+    AddOpLow (D, OP65_EOR);
 
-    /* Inline the xor */
-    if (DirectXor) {
-       /* Xor with variable location */
-       X = NewCodeEntry (OP65_EOR, P->AM, P->Arg, 0, XorEntry->LI);
-    } else {
-       /* Xor with temp storage */
-       X = NewCodeEntry (OP65_EOR, AM65_ZP, ZPLo, 0, XorEntry->LI);
-    }
-    CS_InsertEntry (S, X, Xor+1);
-    if (PushEntry->RI->In.RegX >= 0 && XorEntry->RI->In.RegX >= 0) {
+    /* High byte */
+    if (RegValIsKnown (D->PushEntry->RI->In.RegX) &&
+        RegValIsKnown (D->OpEntry->RI->In.RegX)) {
        /* Both values known, precalculate the result */
-       const char* Arg = MakeHexArg (PushEntry->RI->In.RegX ^ XorEntry->RI->In.RegX);
-               X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, XorEntry->LI);
-       CS_InsertEntry (S, X, Xor+2);
-    } else if (PushEntry->RI->In.RegX != 0) {
+       const char* Arg = MakeHexArg (D->PushEntry->RI->In.RegX ^ D->OpEntry->RI->In.RegX);
+               X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, D->OpEntry->LI);
+       InsertEntry (D, X, D->IP++);
+    } else if (D->PushEntry->RI->In.RegX != 0) {
        /* High byte is unknown */
-               X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, XorEntry->LI);
-       CS_InsertEntry (S, X, Xor+2);
-       X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, XorEntry->LI);
-       CS_InsertEntry (S, X, Xor+3);
-               X = NewCodeEntry (OP65_EOR, AM65_ZP, ZPHi, 0, XorEntry->LI);
-       CS_InsertEntry (S, X, Xor+4);
-       X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, XorEntry->LI);
-       CS_InsertEntry (S, X, Xor+5);
-       X = NewCodeEntry (OP65_LDA, AM65_ZP, ZPLo, 0, XorEntry->LI);
-       CS_InsertEntry (S, X, Xor+6);
+        AddOpHigh (D, OP65_EOR);
     }
 
     /* Remove the push and the call to the tosandax function */
-    CS_DelEntry (S, Xor);
-    CS_DelEntry (S, Push);
+    RemovePushAndOp (D);
 
     /* We changed the sequence */
     return 1;
@@ -532,13 +598,12 @@ static unsigned Opt_tosxorax (CodeSeg* S, unsigned Push, unsigned Xor,
 
 /* Flags for the functions */
 typedef enum {
-    STOP_NONE,             /* Nothing special */
+    STOP_NONE,             /* Nothing special */
     STOP_A_UNUSED           /* Call only if a unused later */
 } STOP_FLAGS;
 
 
-typedef unsigned (*OptFunc) (CodeSeg* S, unsigned Push, unsigned Store,
-                            const char* ZPLo, const char* ZPHi);
+typedef unsigned (*OptFunc) (StackOpData* D);
 typedef struct OptFuncDesc OptFuncDesc;
 struct OptFuncDesc {
     const char*     Name;   /* Name of the replaced runtime function */
@@ -576,6 +641,35 @@ static const OptFuncDesc* FindFunc (const char* Name)
 
 
 
+static int CmpHarmless (const void* Key, const void* Entry)
+/* Compare function for bsearch */
+{
+    return strcmp (Key, *(const char**)Entry);
+}
+
+
+
+static int HarmlessCall (const char* Name)
+/* Check if this is a call to a harmless subroutine that will not interrupt
+ * the pushax/op sequence when encountered.
+ */
+{
+    static const char* Tab[] = {
+        "ldaxidx",
+        "ldaxysp",
+        "negax",
+    };
+
+    void* R = bsearch (Name,
+                       Tab,
+                       sizeof (Tab) / sizeof (Tab[0]),
+                       sizeof (Tab[0]),
+                       CmpHarmless);
+    return (R != 0);
+}
+
+
+
 /*****************************************************************************/
 /*                                  Code                                    */
 /*****************************************************************************/
@@ -585,11 +679,12 @@ static const OptFuncDesc* FindFunc (const char* Name)
 unsigned OptStackOps (CodeSeg* S)
 /* Optimize operations that take operands via the stack */
 {
-    unsigned Changes = 0;     /* Number of changes in one run */
-    int      InSeq = 0;       /* Inside a sequence */
-    unsigned Push = 0;               /* Index of pushax */
-    unsigned UsedRegs = 0;    /* Zeropage registers used in sequence */
-    unsigned I;
+    unsigned    Changes = 0;    /* Number of changes in one run */
+    int         InSeq = 0;      /* Inside a sequence */
+    unsigned    Push = 0;      /* Index of pushax */
+    unsigned    UsedRegs = 0;   /* Zeropage registers used in sequence */
+    unsigned    I;
+
 
     /* Generate register info */
     CS_GenRegInfo (S);
@@ -600,8 +695,7 @@ unsigned OptStackOps (CodeSeg* S)
      * It depends on the code between the two if we can handle/transform the
      * sequence, so check this code for the following list of things:
      *
-     *  - there must not be a jump or conditional branch (this may
-     *    get relaxed later).
+     *  - the range must be a basic block (one entry, one exit)
      *  - there may not be accesses to local variables with unknown
      *    offsets (because we have to adjust these offsets).
      *  - no subroutine calls
@@ -619,10 +713,16 @@ unsigned OptStackOps (CodeSeg* S)
        /* Handling depends if we're inside a sequence or not */
        if (InSeq) {
 
-           if ((E->Info & OF_BRA) != 0                              ||
-               ((E->Use & REG_SP) != 0                         &&
-                (E->AM != AM65_ZP_INDY || E->RI->In.RegY < 0))      ||
-               CE_HasLabel (E)) {
+            /* If we are using the stack, and we don't have "indirect Y"
+             * 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 two means that the code works with
+             * the value on stack which is to be removed.
+             */
+                   if ((E->Use & REG_SP) != 0 &&
+               (E->AM != AM65_ZP_INDY || RegValIsUnknown (E->RI->In.RegY) ||
+                 E->RI->In.RegY < 2)) {
 
                /* All this stuff is not allowed in a sequence */
                InSeq = 0;
@@ -633,9 +733,8 @@ unsigned OptStackOps (CodeSeg* S)
                const OptFuncDesc* F = FindFunc (E->Arg);
                if (F) {
 
-                   const char* ZPLo = 0;
-                   const char* ZPHi = 0;
-                   int PreCondOk    = 1;
+                    StackOpData Data;
+                   int PreCondOk = 1;
 
                    /* Check the flags */
                    if (F->Flags & STOP_A_UNUSED) {
@@ -651,31 +750,45 @@ unsigned OptStackOps (CodeSeg* S)
                        UsedRegs |= GetRegInfo (S, I+1, REG_SREG | REG_PTR1 | REG_PTR2);
                        if ((UsedRegs & REG_SREG) == REG_NONE) {
                            /* SREG is available */
-                           ZPLo = "sreg";
-                           ZPHi = "sreg+1";
+                           Data.ZPLo = "sreg";
+                           Data.ZPHi = "sreg+1";
                        } else if ((UsedRegs & REG_PTR1) == REG_NONE) {
-                           ZPLo = "ptr1";
-                           ZPHi = "ptr1+1";
+                           Data.ZPLo = "ptr1";
+                           Data.ZPHi = "ptr1+1";
                        } else if ((UsedRegs & REG_PTR2) == REG_NONE) {
-                           ZPLo = "ptr2";
-                           ZPHi = "ptr2+1";
+                           Data.ZPLo = "ptr2";
+                           Data.ZPHi = "ptr2+1";
                        } else {
                            /* No registers available */
                            PreCondOk = 0;
                        }
                    }
 
+                    /* Determine if we have a basic block */
+                    if (PreCondOk) {
+                        PreCondOk = CS_IsBasicBlock (S, Push, I);
+                    }
+
                    /* If preconditions are ok, call the optimizer function */
                    if (PreCondOk) {
 
                        /* Adjust stack offsets */
-                       unsigned Op = I + AdjustStackOffset (S, Push, I, 2);
-
-                       /* Call the optimizer function */
-                       Changes += F->Func (S, Push, Op, ZPLo, ZPHi);
-
-                       /* Regenerate register info */
-                       CS_GenRegInfo (S);
+                       Data.OpIndex = I + AdjustStackOffset (S, Push, I, 2);
+
+                        /* Prepare the remainder of the data structure */
+                        Data.Code      = S;
+                        Data.Flags     = 0;
+                        Data.PushIndex = Push;
+                        Data.PrevEntry = CS_GetPrevEntry (S, Data.PushIndex);
+                        Data.PushEntry = CS_GetEntry (S, Data.PushIndex);
+                        Data.OpEntry   = E;
+                        Data.NextEntry = CS_GetNextEntry (S, Data.OpIndex);
+
+                               /* Call the optimizer function */
+                       Changes += F->Func (&Data);
+
+                       /* Regenerate register info */
+                       CS_GenRegInfo (S);
                    }
 
                    /* End of sequence */
@@ -685,19 +798,20 @@ unsigned OptStackOps (CodeSeg* S)
                    /* Restart the sequence */
                    Push     = I;
                    UsedRegs = REG_NONE;
-               } else {
+               } else if (HarmlessCall (E->Arg)) {
+                    /* Track zeropage register usage */
+                    UsedRegs |= (E->Use | E->Chg);
+                } else {
                    /* A call to an unkown subroutine ends the sequence */
                    InSeq = 0;
                }
 
            } else {
-
                /* Other stuff: Track zeropage register usage */
                UsedRegs |= (E->Use | E->Chg);
-
            }
 
-       } else if (CE_IsCall (E, "pushax")) {
+       } else if (CE_IsCallTo (E, "pushax")) {
 
            /* This starts a sequence */
            Push     = I;