]> git.sur5r.net Git - cc65/blobdiff - src/cc65/coptstop.c
The check for illegal storage classes on globals was wrong
[cc65] / src / cc65 / coptstop.c
index 231552d2e564c70696a1512564af9648ddba6c05..5a512cb206c202348dae6d746da32fce0e384dda 100644 (file)
@@ -6,7 +6,7 @@
 /*                                                                           */
 /*                                                                           */
 /*                                                                           */
-/* (C) 2001      Ullrich von Bassewitz                                       */
+/* (C) 2001-2002 Ullrich von Bassewitz                                       */
 /*               Wacholderweg 14                                             */
 /*               D-70597 Stuttgart                                           */
 /* EMail:        uz@cc65.org                                                 */
 
 
 #include <stdlib.h>
-
-/* common */
-#include "xsprintf.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.
@@ -69,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.
             */
@@ -89,10 +126,8 @@ static unsigned AdjustStackOffset (CodeSeg* S, unsigned Start, unsigned Stop,
            } else {
 
                /* Insert a new load instruction before the stack access */
-               char Buf [16];
-               CodeEntry* X;
-               xsprintf (Buf, sizeof (Buf), "$%02X", E->RI->In.RegY - Offs);
-               X = NewCodeEntry (OP65_LDY, AM65_IMM, Buf, 0, E->LI);
+               const char* Arg = MakeHexArg (E->RI->In.RegY - Offs);
+               CodeEntry* X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
                CS_InsertEntry (S, X, I);
 
                /* One more inserted entries */
@@ -116,40 +151,252 @@ 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, D->ZPLo) != 0 && strcmp (E->Arg, D->ZPHi) != 0) {
+            /* Load indirect with known offset is also ok, provided that
+             * the zeropage location used is not the same as the one we're
+             * using for the temp storage.
+             */
+            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);
+
+        /* Use the given zero page loc */
+        ZPLo = D->ZPLo;
+    }
+
+    /* 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 */
+    RemovePushAndOp (D);
+
+    /* We changed the sequence */
+    return 1;
+}
+
+
+
+static unsigned Opt_staxspidx (StackOpData* D)
+/* Optimize the staxspidx sequence if possible */
+{
+    CodeEntry* X;
+    const char* ZPLo;
+
+    /* Check if we're using a register variable */
+    if ((ZPLo = IsRegVar (D)) == 0) {
 
-    /* Correct the index of the store and get a pointer to the entry */
-    Store += 2;
-    StoreEntry = CS_GetEntry (S, Store);
+        /* Store the value into the zeropage instead of pushing it */
+        ReplacePushByStore (D);
+
+        /* 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_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 (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, D->OpEntry->LI);
+    }
+    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;
@@ -157,78 +404,65 @@ static unsigned Opt_staspidx (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* N;
-    CodeEntry* X;
-    CodeEntry* PushEntry;
-    CodeEntry* AddEntry;
+    CodeEntry*  X;
+
 
     /* We need the entry behind the add */
-    CHECK ((N = CS_GetNextEntry (S, Add)) != 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 allows us to replace it, we may use this
+     * location for the op and must not save the value in the zero page
+     * location.
+     */
+    CheckDirectOp (D);
 
     /* 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);
-
-    /* Correct the index of the add and get a pointer to the entry */
-    Add += 2;
-    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);
-    X = NewCodeEntry (OP65_ADC, AM65_ZP, ZPLo, 0, AddEntry->LI);
-    CS_InsertEntry (S, X, Add+2);
-    if (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;
-       if (PushEntry->RI->In.RegX >= 0) {
-           /* Value of first op high byte is known */
-           char Buf [16];
-           xsprintf (Buf, sizeof (Buf), "$%02X", PushEntry->RI->In.RegX);
-           X = NewCodeEntry (OP65_LDX, AM65_IMM, Buf, 0, AddEntry->LI);
+    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 (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 (RegValIsKnown (D->PushEntry->RI->In.RegX)) {
+           /* Value of first op high byte is known */
+           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;
@@ -236,51 +470,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* X;
-    CodeEntry* PushEntry;
-    CodeEntry* AndEntry;
+    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 allows us to replace it, we may use this
+     * location for the op and must not save the value in the zero page
+     * location.
+     */
+    CheckDirectOp (D);
 
     /* 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);
-
-    /* Correct the index of the add and get a pointer to the entry */
-    And += 2;
-    AndEntry = CS_GetEntry (S, And);
-
-    /* Inline the and */
-    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) {
+    ReplacePushByStore (D);
+
+    /* Inline the and, low byte */
+    D->IP = D->OpIndex + 1;
+    AddOpLow (D, OP65_AND);
+
+    /* 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;
@@ -288,54 +508,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* X;
-    CodeEntry* PushEntry;
-    CodeEntry* OrEntry;
+    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 allows us to replace it, we may use this
+     * location for the op and must not save the value in the zero page
+     * location.
+     */
+    CheckDirectOp (D);
 
     /* 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);
-
-    /* Correct the index of the add and get a pointer to the entry */
-    Or += 2;
-    OrEntry = CS_GetEntry (S, Or);
-
-    /* Inline the or */
-    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) {
+    ReplacePushByStore (D);
+
+    /* Inline the or, low byte */
+    D->IP = D->OpIndex + 1;
+    AddOpLow (D, OP65_ORA);
+
+    /* High byte */
+    if (RegValIsKnown (D->PushEntry->RI->In.RegX) &&
+        RegValIsKnown (D->OpEntry->RI->In.RegX)) {
        /* Both values known, precalculate the result */
-       char Buf [16];
-       int Val = (PushEntry->RI->In.RegX | OrEntry->RI->In.RegX);
-       xsprintf (Buf, sizeof (Buf), "$%02X", Val);
-               X = NewCodeEntry (OP65_LDX, AM65_IMM, Buf, 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;
@@ -343,54 +548,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)
-/* Optimize the tosorax sequence if possible */
+static unsigned Opt_tosxorax (StackOpData* D)
+/* Optimize the tosxorax sequence if possible */
 {
-    CodeEntry* X;
-    CodeEntry* PushEntry;
-    CodeEntry* XorEntry;
+    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 allows us to replace it, we may use this
+     * location for the op and must not save the value in the zero page
+     * location.
+     */
+    CheckDirectOp (D);
 
     /* 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);
-
-    /* Correct the index of the add and get a pointer to the entry */
-    Xor += 2;
-    XorEntry = CS_GetEntry (S, Xor);
-
-    /* Inline the or */
-    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) {
+    ReplacePushByStore (D);
+
+    /* Inline the xor, low byte */
+    D->IP = D->OpIndex + 1;
+    AddOpLow (D, OP65_EOR);
+
+    /* High byte */
+    if (RegValIsKnown (D->PushEntry->RI->In.RegX) &&
+        RegValIsKnown (D->OpEntry->RI->In.RegX)) {
        /* Both values known, precalculate the result */
-       char Buf [16];
-       int Val = (PushEntry->RI->In.RegX ^ XorEntry->RI->In.RegX);
-       xsprintf (Buf, sizeof (Buf), "$%02X", Val);
-               X = NewCodeEntry (OP65_LDX, AM65_IMM, Buf, 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;
@@ -399,25 +589,33 @@ static unsigned Opt_tosxorax (CodeSeg* S, unsigned Push, unsigned Xor,
 
 
 /*****************************************************************************/
-/*                                  Code                                    */
+/*                                  Code                                    */
 /*****************************************************************************/
 
 
 
-typedef unsigned (*OptFunc) (CodeSeg* S, unsigned Push, unsigned Store,
-                            const char* ZPLo, const char* ZPHi);
+/* Flags for the functions */
+typedef enum {
+    STOP_NONE,             /* Nothing special */
+    STOP_A_UNUSED           /* Call only if a unused later */
+} STOP_FLAGS;
+
+
+typedef unsigned (*OptFunc) (StackOpData* D);
 typedef struct OptFuncDesc OptFuncDesc;
 struct OptFuncDesc {
-    const char*         Name;   /* Name of the replaced runtime function */
-    OptFunc             Func;   /* Function pointer */
+    const char*     Name;   /* Name of the replaced runtime function */
+    OptFunc         Func;   /* Function pointer */
+    STOP_FLAGS      Flags;  /* Flags */
 };
 
 static const OptFuncDesc FuncTable[] = {
-    { "staspidx",       Opt_staspidx    },
-    { "tosaddax",       Opt_tosaddax    },
-    { "tosandax",       Opt_tosandax    },
-    { "tosorax",        Opt_tosorax     },
-    { "tosxorax",       Opt_tosxorax    },
+    { "staspidx",   Opt_staspidx,  STOP_NONE },
+    { "staxspidx",  Opt_staxspidx, STOP_A_UNUSED },
+    { "tosaddax",   Opt_tosaddax,  STOP_NONE },
+    { "tosandax",   Opt_tosandax,  STOP_NONE },
+    { "tosorax",    Opt_tosorax,   STOP_NONE },
+    { "tosxorax",   Opt_tosxorax,  STOP_NONE },
 };
 #define FUNC_COUNT (sizeof(FuncTable) / sizeof(FuncTable[0]))
 
@@ -441,6 +639,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                                    */
 /*****************************************************************************/
@@ -450,11 +677,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);
@@ -465,8 +693,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
@@ -484,10 +711,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;
@@ -498,37 +731,62 @@ unsigned OptStackOps (CodeSeg* S)
                const OptFuncDesc* F = FindFunc (E->Arg);
                if (F) {
 
-                   /* Determine the register to use */
-                   const char* ZPLo;
-                   const char* ZPHi;
-                           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";
-                   } else if ((UsedRegs & REG_PTR1) == REG_NONE) {
-                       ZPLo = "ptr1";
-                       ZPHi = "ptr1+1";
-                   } else if ((UsedRegs & REG_PTR2) == REG_NONE) {
-                       ZPLo = "ptr2";
-                       ZPHi = "ptr2+1";
-                   } else {
-                       /* No registers available */
-                       ZPLo = 0;
-                       ZPHi = 0;
-                   }
-
-                   /* If we have a register, call the optimizer function */
-                   if (ZPLo && ZPHi) {
+                    StackOpData Data;
+                   int PreCondOk = 1;
+
+                   /* Check the flags */
+                   if (F->Flags & STOP_A_UNUSED) {
+                       /* a must be unused later */
+                       if (RegAUsed (S, I+1)) {
+                           /* Cannot optimize */
+                           PreCondOk = 0;
+                       }
+                   }
+
+                   /* Determine the zero page locations to use */
+                   if (PreCondOk) {
+                       UsedRegs |= GetRegInfo (S, I+1, REG_SREG | REG_PTR1 | REG_PTR2);
+                       if ((UsedRegs & REG_SREG) == REG_NONE) {
+                           /* SREG is available */
+                           Data.ZPLo = "sreg";
+                           Data.ZPHi = "sreg+1";
+                       } else if ((UsedRegs & REG_PTR1) == REG_NONE) {
+                           Data.ZPLo = "ptr1";
+                           Data.ZPHi = "ptr1+1";
+                       } else if ((UsedRegs & REG_PTR2) == REG_NONE) {
+                           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 */
@@ -538,19 +796,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;