/*                                                                           */
 /*                                                                           */
 /*                                                                           */
-/* (C) 2001-2004 Ullrich von Bassewitz                                       */
-/*               Römerstrasse 52                                             */
+/* (C) 2001-2009 Ullrich von Bassewitz                                       */
+/*               Roemerstrasse 52                                            */
 /*               D-70794 Filderstadt                                         */
 /* EMail:        uz@cc65.org                                                 */
 /*                                                                           */
 typedef unsigned (*OptFunc) (StackOpData* D);
 typedef struct OptFuncDesc OptFuncDesc;
 struct OptFuncDesc {
-    const char*     Name;   /* Name of the replaced runtime function */
-    OptFunc         Func;   /* Function pointer */
-    STOP_FLAGS      Flags;  /* Flags */
+    const char*         Name;           /* Name of the replaced runtime function */
+    OptFunc             Func;           /* Function pointer */
+    STOP_FLAGS          Flags;          /* Flags */
+};
+
+/* LoadData flags set by DirectOp */
+#define LD_DIRECT       0x01            /* Direct op may be used */
+#define LD_RELOAD_Y     0x02            /* Reload index register Y */
+#define LD_REMOVE       0x04            /* Load may be removed */
+
+/* Structure that tells us how to load the lhs values */
+typedef struct LoadData LoadData;
+struct LoadData {
+    unsigned char       Flags;          /* Tells us how to load */
+    unsigned char       Offs;           /* Stack offset if data is on stack */
 };
 
 /* Structure that holds the needed data */
     CodeEntry*          OpEntry;        /* Pointer to entry with op */
     CodeEntry*          NextEntry;      /* Entry after the op */
 
+    /* Stack offsets if the lhs is loaded from stack */
+    LoadData            AData;
+    LoadData            XData;
+
+
     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 set by DirectOp */
-#define OP_LO_DIRECT    0x01            /* Direct op may be used for lo byte */
-#define OP_LO_RELOAD_Y  0x02            /* Reload index register Y for lo byte */
-#define OP_HI_DIRECT    0x04            /* Direct op may be used for hi byte */
-#define OP_HI_RELOAD_Y  0x08            /* Reload index register Y for hi byte */
-
 
 
 /*****************************************************************************/
 
 
 
-static unsigned CheckOneDirectOp (CodeEntry* E, unsigned Direct, unsigned Reload)
+static void CheckOneDirectOp (CodeEntry* E, LoadData* L, unsigned char Offs)
 /* 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
 {
     /* Check the load entry */
     if (E) {
-        if (E->AM == AM65_IMM || E->AM == AM65_ZP || E->AM == AM65_ABS) {
+        /* Must check the call first since addressing mode is ABS, so second
+         * "if" will catch otherwise.
+         */
+        if (CE_IsCallTo (E, "ldaxysp")) {
+            /* Same as single loads from stack. Since we must distinguish
+             * between A and X here, the necessary offset is passed to the
+             * function as a parameter.
+             */
+            L->Offs = (unsigned char) E->RI->In.RegY - Offs;
+            L->Flags |= (LD_DIRECT | LD_RELOAD_Y);
+        } else if (E->AM == AM65_IMM || E->AM == AM65_ZP || E->AM == AM65_ABS) {
             /* These insns are all ok and replaceable */
-            return Direct;
+            L->Flags |= LD_DIRECT;
         } else if (E->AM == AM65_ZP_INDY &&
                    RegValIsKnown (E->RI->In.RegY) &&
                    strcmp (E->Arg, "sp") == 0) {
              * these locations may change between the push and the actual
              * operation.
              */
-            return Reload;
+            L->Offs  = (unsigned char) E->RI->In.RegY;
+            L->Flags |= (LD_DIRECT | LD_RELOAD_Y);
         }
     }
-
-    /* Nothing found */
-    return 0;
 }
 
 
  */
 {
     /* Check flags for A and X load instructions */
-    D->Flags |= CheckOneDirectOp (D->LoadAEntry, OP_LO_DIRECT, OP_LO_RELOAD_Y);
-    D->Flags |= CheckOneDirectOp (D->LoadXEntry, OP_HI_DIRECT, OP_HI_RELOAD_Y);
+    CheckOneDirectOp (D->LoadAEntry, &D->AData, 1);
+    CheckOneDirectOp (D->LoadXEntry, &D->XData, 0);
 }
 
 
     /* 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->Flags & OP_HI_DIRECT) == 0) {
+    if ((D->XData.Flags & LD_DIRECT) == 0) {
         X = NewCodeEntry (OP65_STX, AM65_ZP, D->ZPHi, 0, D->PushEntry->LI);
         InsertEntry (D, X, D->PushIndex+1);
     }
-    if ((D->Flags & OP_LO_DIRECT) == 0) {
+    if ((D->AData.Flags & LD_DIRECT) == 0) {
        X = NewCodeEntry (OP65_STA, AM65_ZP, D->ZPLo, 0, D->PushEntry->LI);
                InsertEntry (D, X, D->PushIndex+1);
     }
 {
     CodeEntry* X;
 
-    if ((D->Flags & OP_LO_DIRECT) != 0) {
+    if ((D->AData.Flags & LD_DIRECT) != 0) {
                /* Op with a variable location. If the location is on the stack, we
          * need to reload the Y register.
          */
-        CodeEntry* LoadA = D->LoadAEntry;
-        if ((D->Flags & OP_LO_RELOAD_Y) != 0) {
-            const char* Arg = MakeHexArg (LoadA->RI->In.RegY);
+        if ((D->AData.Flags & LD_RELOAD_Y) == 0) {
+
+            /* opc ... */
+            CodeEntry* LoadA = D->LoadAEntry;
+            X = NewCodeEntry (OPC, LoadA->AM, LoadA->Arg, 0, D->OpEntry->LI);
+            InsertEntry (D, X, D->IP++);
+
+        } else {
+
+            /* ldy #offs */
+            const char* Arg = MakeHexArg (D->AData.Offs);
             X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, D->OpEntry->LI);
             InsertEntry (D, X, D->IP++);
+
+            /* opc (sp),y */
+            X = NewCodeEntry (OPC, AM65_ZP_INDY, "sp", 0, D->OpEntry->LI);
+            InsertEntry (D, X, D->IP++);
+
         }
-       X = NewCodeEntry (OPC, LoadA->AM, LoadA->Arg, 0, D->OpEntry->LI);
+
+        /* In both cases, we can remove the load */
+        D->AData.Flags |= LD_REMOVE;
+
     } else {
+
        /* Op with temp storage */
        X = NewCodeEntry (OPC, AM65_ZP, D->ZPLo, 0, D->OpEntry->LI);
+        InsertEntry (D, X, D->IP++);
+
     }
-    InsertEntry (D, X, D->IP++);
 }
 
 
     X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, D->OpEntry->LI);
     InsertEntry (D, X, D->IP++);
 
-    if ((D->Flags & OP_HI_DIRECT) != 0) {
-        CodeEntry* LoadX = D->LoadXEntry;
-        if ((D->Flags & OP_HI_RELOAD_Y) != 0) {
+    if ((D->XData.Flags & LD_DIRECT) != 0) {
+
+        if ((D->XData.Flags & LD_RELOAD_Y) == 0) {
+
+            /* opc xxx */
+            CodeEntry* LoadX = D->LoadXEntry;
+           X = NewCodeEntry (OPC, LoadX->AM, LoadX->Arg, 0, D->OpEntry->LI);
+            InsertEntry (D, X, D->IP++);
+
+        } else {
+
             /* ldy #const */
-            const char* Arg = MakeHexArg (LoadX->RI->In.RegY);
+            const char* Arg = MakeHexArg (D->XData.Offs);
             X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, D->OpEntry->LI);
             InsertEntry (D, X, D->IP++);
+
+            /* opc (sp),y */
+            X = NewCodeEntry (OPC, AM65_ZP_INDY, "sp", 0, D->OpEntry->LI);
+            InsertEntry (D, X, D->IP++);
         }
-        /* opc xxx */
-       X = NewCodeEntry (OPC, LoadX->AM, LoadX->Arg, 0, D->OpEntry->LI);
-        InsertEntry (D, X, D->IP++);
+
+        /* In both cases, we can remove the load */
+        D->XData.Flags |= LD_REMOVE;
+
     } else {
         /* opc zphi */
         X = NewCodeEntry (OPC, AM65_ZP, D->ZPHi, 0, D->OpEntry->LI);
 
 
 
-static void RemovePushAndOp (StackOpData* D)
-/* Remove the call to pushax and the call to the operator subroutine */
+static void RemoveRemainders (StackOpData* D)
+/* Remove the code that is unnecessary after translation of the sequence */
 {
+    /* Remove the push and the operator routine */
     DelEntry (D, D->OpIndex);
     DelEntry (D, D->PushIndex);
+
+    /* Remove the register loads before the push. Beware: There may only be
+     * one!
+     */
+    if (D->LoadAIndex >= 0 && D->LoadAIndex == D->LoadXIndex) {
+        /* Common load routine */
+        if ((D->AData.Flags & D->XData.Flags) & LD_REMOVE) {
+            /* Both say: remove */
+            DelEntry (D, D->LoadAIndex);
+        }
+    } else if (D->LoadAIndex >= 0 && (D->AData.Flags & LD_REMOVE)) {
+        DelEntry (D, D->LoadAIndex);
+    } else if (D->LoadXIndex >= 0 && (D->XData.Flags & LD_REMOVE)) {
+        DelEntry (D, D->LoadXIndex);
+    }
 }
 
 
     }
 
     /* Remove the push and the call to the __bzero function */
-    RemovePushAndOp (D);
+    RemoveRemainders (D);
 
     /* We changed the sequence */
     return 1;
     InsertEntry (D, X, D->OpIndex+1);
 
     /* Remove the push and the call to the staspidx function */
-    RemovePushAndOp (D);
+    RemoveRemainders (D);
 
     /* We changed the sequence */
     return 1;
     InsertEntry (D, X, D->OpIndex+5);
 
     /* Remove the push and the call to the staxspidx function */
-    RemovePushAndOp (D);
+    RemoveRemainders (D);
 
     /* We changed the sequence */
     return 1;
                 X = NewCodeEntry (OP65_LDX, AM65_ZP, D->ZPHi, 0, D->OpEntry->LI);
             }
             InsertEntry (D, X, D->IP++);
+
+            /* bcc label */
             L = CS_GenLabel (D->Code, D->NextEntry);
             X = NewCodeEntry (OP65_BCC, AM65_BRA, L->Name, L, D->OpEntry->LI);
             InsertEntry (D, X, D->IP++);
+
+            /* inx */
             X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, D->OpEntry->LI);
             InsertEntry (D, X, D->IP++);
         } else {
     }
 
     /* Remove the push and the call to the tosaddax function */
-    RemovePushAndOp (D);
+    RemoveRemainders (D);
 
     /* We changed the sequence */
     return 1;
     }
 
     /* Remove the push and the call to the tosandax function */
-    RemovePushAndOp (D);
+    RemoveRemainders (D);
 
     /* We changed the sequence */
     return 1;
     }
 
     /* Remove the push and the call to the tosorax function */
-    RemovePushAndOp (D);
+    RemoveRemainders (D);
 
     /* We changed the sequence */
     return 1;
     AddOpHigh (D, OP65_SBC);
 
     /* Remove the push and the call to the tosaddax function */
-    RemovePushAndOp (D);
+    RemoveRemainders (D);
 
     /* We changed the sequence */
     return 1;
     }
 
     /* Remove the push and the call to the tosandax function */
-    RemovePushAndOp (D);
+    RemoveRemainders (D);
 
     /* We changed the sequence */
     return 1;
 static void ResetStackOpData (StackOpData* Data)
 /* Reset the given data structure */
 {
-    Data->Flags      = 0;
-    Data->OptFunc    = 0;
+    Data->AData.Flags   = 0;
+    Data->XData.Flags   = 0;
+    Data->OptFunc       = 0;
 
-    Data->LoadAIndex = -1;
-    Data->LoadXIndex = -1;
-    Data->LoadYIndex = -1;
-    Data->PushIndex  = -1;
-    Data->OpIndex    = -1;
+    Data->LoadAIndex    = -1;
+    Data->LoadXIndex    = -1;
+    Data->LoadYIndex    = -1;
+    Data->PushIndex     = -1;
+    Data->OpIndex       = -1;
 
-    Data->LoadAEntry = 0;
-    Data->LoadXEntry = 0;
+    Data->LoadAEntry    = 0;
+    Data->LoadXEntry    = 0;
 
-    Data->UsedRegs   = REG_NONE;
+    Data->UsedRegs      = REG_NONE;
 }
 
 
                         case OP65_TYA: Data.LoadAIndex = Data.LoadYIndex; break;
                         default:                                          break;
                     }
+                } else if (CE_IsCallTo (E, "ldaxysp")) {
+                    /* Both registers set */
+                    Data.LoadAIndex = I;
+                    Data.LoadXIndex = I;
                 } else {
                     if (E->Chg & REG_A) {
                         Data.LoadAIndex = -1;