]> git.sur5r.net Git - cc65/blob - src/cc65/coptstop.c
Added more code to improve compares.
[cc65] / src / cc65 / coptstop.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                 coptstop.c                                */
4 /*                                                                           */
5 /*           Optimize operations that take operands via the stack            */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 2001-2009 Ullrich von Bassewitz                                       */
10 /*               Roemerstrasse 52                                            */
11 /*               D-70794 Filderstadt                                         */
12 /* EMail:        uz@cc65.org                                                 */
13 /*                                                                           */
14 /*                                                                           */
15 /* This software is provided 'as-is', without any expressed or implied       */
16 /* warranty.  In no event will the authors be held liable for any damages    */
17 /* arising from the use of this software.                                    */
18 /*                                                                           */
19 /* Permission is granted to anyone to use this software for any purpose,     */
20 /* including commercial applications, and to alter it and redistribute it    */
21 /* freely, subject to the following restrictions:                            */
22 /*                                                                           */
23 /* 1. The origin of this software must not be misrepresented; you must not   */
24 /*    claim that you wrote the original software. If you use this software   */
25 /*    in a product, an acknowledgment in the product documentation would be  */
26 /*    appreciated but is not required.                                       */
27 /* 2. Altered source versions must be plainly marked as such, and must not   */
28 /*    be misrepresented as being the original software.                      */
29 /* 3. This notice may not be removed or altered from any source              */
30 /*    distribution.                                                          */
31 /*                                                                           */
32 /*****************************************************************************/
33
34
35
36 #include <stdlib.h>
37
38 /* common */
39 #include "chartype.h"
40
41 /* cc65 */
42 #include "codeent.h"
43 #include "codeinfo.h"
44 #include "coptstop.h"
45
46
47
48 /*****************************************************************************/
49 /*                            Load tracking data                             */
50 /*****************************************************************************/
51
52
53
54 /* LoadRegInfo flags set by DirectOp */
55 typedef enum {
56   LI_NONE               = 0x00,
57   LI_DIRECT             = 0x01,         /* Direct op may be used */
58   LI_RELOAD_Y           = 0x02,         /* Reload index register Y */
59   LI_REMOVE             = 0x04,         /* Load may be removed */
60 } LI_FLAGS;
61
62 /* Structure that tells us how to load the lhs values */
63 typedef struct LoadRegInfo LoadRegInfo;
64 struct LoadRegInfo {
65     int                 LoadIndex;      /* Index of load insn, -1 if invalid */
66     CodeEntry*          LoadEntry;      /* The actual entry, 0 if invalid */
67     LI_FLAGS            Flags;          /* Tells us how to load */
68     unsigned char       Offs;           /* Stack offset if data is on stack */
69 };
70
71 /* Now combined for both registers */
72 typedef struct LoadInfo LoadInfo;
73 struct LoadInfo {
74     LoadRegInfo         A;              /* Info for A register */
75     LoadRegInfo         X;              /* Info for X register */
76     LoadRegInfo         Y;              /* Info for Y register */
77 };
78
79
80
81 /*****************************************************************************/
82 /*                                   Data                                    */
83 /*****************************************************************************/
84
85
86
87 /* Flags for the functions */
88 typedef enum {
89     OP_NONE             = 0x00,         /* Nothing special */
90     OP_A_KNOWN          = 0x01,         /* Value of A must be known */
91     OP_X_ZERO           = 0x02,         /* X must be zero */
92     OP_LHS_LOAD         = 0x04,         /* Must have load insns for LHS */
93     OP_LHS_LOAD_DIRECT  = 0x0C,         /* Must have direct load insn for LHS */
94     OP_RHS_LOAD         = 0x10,         /* Must have load insns for RHS */
95     OP_RHS_LOAD_DIRECT  = 0x30,         /* Must have direct load insn for RHS */
96 } OP_FLAGS;
97
98 /* Structure forward decl */
99 typedef struct StackOpData StackOpData;
100
101 /* Structure that describes an optimizer subfunction for a specific op */
102 typedef unsigned (*OptFunc) (StackOpData* D);
103 typedef struct OptFuncDesc OptFuncDesc;
104 struct OptFuncDesc {
105     const char*         Name;           /* Name of the replaced runtime function */
106     OptFunc             Func;           /* Function pointer */
107     unsigned            UnusedRegs;     /* Regs that must not be used later */
108     OP_FLAGS            Flags;          /* Flags */
109 };
110
111 /* Structure that holds the needed data */
112 struct StackOpData {
113     CodeSeg*            Code;           /* Pointer to code segment */
114     unsigned            Flags;          /* Flags to remember things */
115
116     /* Pointer to optimizer subfunction description */
117     const OptFuncDesc*  OptFunc;
118
119     /* ZP register usage inside the sequence */
120     unsigned            UsedRegs;
121
122     /* Register load information for lhs and rhs */
123     LoadInfo            Lhs;
124     LoadInfo            Rhs;
125
126     /* Several indices of insns in the code segment */
127     int                 PushIndex;      /* Index of call to pushax in codeseg */
128     int                 OpIndex;        /* Index of actual operation */
129
130     /* Pointers to insns in the code segment */
131     CodeEntry*          PrevEntry;      /* Entry before the call to pushax */
132     CodeEntry*          PushEntry;      /* Pointer to entry with call to pushax */
133     CodeEntry*          OpEntry;        /* Pointer to entry with op */
134     CodeEntry*          NextEntry;      /* Entry after the op */
135
136     const char*         ZPLo;           /* Lo byte of zero page loc to use */
137     const char*         ZPHi;           /* Hi byte of zero page loc to use */
138     unsigned            IP;             /* Insertion point used by some routines */
139 };
140
141
142
143 /*****************************************************************************/
144 /*                            Load tracking code                             */
145 /*****************************************************************************/
146
147
148
149 static void ClearLoadRegInfo (LoadRegInfo* RI)
150 /* Clear a LoadRegInfo struct */
151 {
152     RI->LoadIndex = -1;
153 }
154
155
156
157 static void FinalizeLoadRegInfo (LoadRegInfo* RI, CodeSeg* S)
158 /* Prepare a LoadRegInfo struct for use */
159 {
160     /* Get the entry */
161     if (RI->LoadIndex >= 0) {
162         RI->LoadEntry = CS_GetEntry (S, RI->LoadIndex);
163     } else {
164         RI->LoadEntry = 0;
165     }
166     RI->Flags = LI_NONE;
167 }
168
169
170
171 static void ClearLoadInfo (LoadInfo* LI)
172 /* Clear a LoadInfo struct */
173 {
174     ClearLoadRegInfo (&LI->A);
175     ClearLoadRegInfo (&LI->X);
176     ClearLoadRegInfo (&LI->Y);
177 }
178
179
180
181 static void AdjustLoadRegInfo (LoadRegInfo* RI, int DelIndex, int Change)
182 /* Adjust a load register info struct after deleting or inserting an entry
183  * with a given index
184  */
185 {
186     CHECK (abs (Change) == 1);
187     if (Change < 0) {
188         /* Deletion */
189         if (DelIndex < RI->LoadIndex) {
190             --RI->LoadIndex;
191         } else if (DelIndex == RI->LoadIndex) {
192             /* Has been removed */
193             RI->LoadIndex = -1;
194             RI->LoadEntry = 0;
195         }
196     } else {
197         /* Insertion */
198         if (DelIndex <= RI->LoadIndex) {
199             ++RI->LoadIndex;
200         }
201     }
202 }
203
204
205
206 static void FinalizeLoadInfo (LoadInfo* LI, CodeSeg* S)
207 /* Prepare a LoadInfo struct for use */
208 {
209     /* Get the entries */
210     FinalizeLoadRegInfo (&LI->A, S);
211     FinalizeLoadRegInfo (&LI->X, S);
212     FinalizeLoadRegInfo (&LI->Y, S);
213 }
214
215
216
217 static void AdjustLoadInfo (LoadInfo* LI, int DelIndex, int Change)
218 /* Adjust a load info struct after deleting entry with a given index */
219 {
220     AdjustLoadRegInfo (&LI->A, DelIndex, Change);
221     AdjustLoadRegInfo (&LI->X, DelIndex, Change);
222     AdjustLoadRegInfo (&LI->Y, DelIndex, Change);
223 }
224
225
226
227 static void TrackLoads (LoadInfo* LI, CodeEntry* E, int I)
228 /* Track loads for a code entry */
229 {
230     if (E->Info & OF_LOAD) {
231         if (E->Chg & REG_A) {
232             LI->A.LoadIndex = I;
233         }
234         if (E->Chg & REG_X) {
235             LI->X.LoadIndex = I;
236         }
237         if (E->Chg & REG_Y) {
238             LI->Y.LoadIndex = I;
239         }
240     } else if (E->Info & OF_XFR) {
241         switch (E->OPC) {
242             case OP65_TAX: LI->X.LoadIndex = LI->A.LoadIndex; break;
243             case OP65_TAY: LI->Y.LoadIndex = LI->A.LoadIndex; break;
244             case OP65_TXA: LI->A.LoadIndex = LI->X.LoadIndex; break;
245             case OP65_TYA: LI->A.LoadIndex = LI->Y.LoadIndex; break;
246             default:                                          break;
247         }
248     } else if (CE_IsCallTo (E, "ldaxysp")) {
249         /* Both registers set, Y changed */
250         LI->A.LoadIndex = I;
251         LI->X.LoadIndex = I;
252         LI->Y.LoadIndex = -1;
253     } else {
254         if (E->Chg & REG_A) {
255             LI->A.LoadIndex = -1;
256         }
257         if (E->Chg & REG_X) {
258             LI->X.LoadIndex = -1;
259         }
260         if (E->Chg & REG_Y) {
261             LI->Y.LoadIndex = -1;
262         }
263     }
264 }
265
266
267
268 /*****************************************************************************/
269 /*                                  Helpers                                  */
270 /*****************************************************************************/
271
272
273
274 static void AdjustStackOffset (StackOpData* D, unsigned Offs)
275 /* Adjust the offset for all stack accesses in the range PushIndex to OpIndex.
276  * OpIndex is adjusted according to the insertions.
277  */
278 {
279     /* Walk over all entries */
280     int I = D->PushIndex + 1;
281     while (I < D->OpIndex) {
282
283         CodeEntry* E = CS_GetEntry (D->Code, I);
284
285         int NeedCorrection = 0;
286         if ((E->Use & REG_SP) != 0) {
287
288             /* Check for some things that should not happen */
289             CHECK (E->AM == AM65_ZP_INDY || E->RI->In.RegY >= (short) Offs);
290             CHECK (strcmp (E->Arg, "sp") == 0);
291
292             /* We need to correct this one */
293             NeedCorrection = 1;
294
295         } else if (CE_IsCallTo (E, "ldaxysp")) {
296
297             /* We need to correct this one */
298             NeedCorrection = 1;
299
300         }
301
302         if (NeedCorrection) {
303
304             /* Get the code entry before this one. If it's a LDY, adjust the
305              * value.
306              */
307             CodeEntry* P = CS_GetPrevEntry (D->Code, I);
308             if (P && P->OPC == OP65_LDY && CE_IsConstImm (P)) {
309
310                 /* The Y load is just before the stack access, adjust it */
311                 CE_SetNumArg (P, P->Num - Offs);
312
313             } else {
314
315                 /* Insert a new load instruction before the stack access */
316                 const char* Arg = MakeHexArg (E->RI->In.RegY - Offs);
317                 CodeEntry* X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
318                 CS_InsertEntry (D->Code, X, I++);
319
320                 /* One more inserted entries */
321                 ++D->OpIndex;
322
323             }
324
325             /* If we need the value of Y later, be sure to reload it */
326             if (RegYUsed (D->Code, I+1)) {
327                 const char* Arg = MakeHexArg (E->RI->In.RegY);
328                 CodeEntry* X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
329                 CS_InsertEntry (D->Code, X, I+1);
330
331                 /* One more inserted entries */
332                 ++D->OpIndex;
333
334                 /* Skip this instruction in the next round */
335                 ++I;
336             }
337         }
338
339         /* Next entry */
340         ++I;
341     }
342 }
343
344
345
346 static void InsertEntry (StackOpData* D, CodeEntry* E, int Index)
347 /* Insert a new entry. Depending on Index, D->PushIndex and D->OpIndex will
348  * be adjusted by this function.
349  */
350 {
351     /* Insert the entry into the code segment */
352     CS_InsertEntry (D->Code, E, Index);
353
354     /* Adjust register loads if necessary */
355     AdjustLoadInfo (&D->Lhs, Index, 1);
356     AdjustLoadInfo (&D->Rhs, Index, 1);
357
358     /* Adjust the indices if necessary */
359     if (D->PushEntry && Index <= D->PushIndex) {
360         ++D->PushIndex;
361     }
362     if (D->OpEntry && Index <= D->OpIndex) {
363         ++D->OpIndex;
364     }
365 }
366
367
368
369 static void DelEntry (StackOpData* D, int Index)
370 /* Delete an entry. Depending on Index, D->PushIndex and D->OpIndex will be
371  * adjusted by this function, and PushEntry/OpEntry may get invalidated.
372  */
373 {
374     /* Delete the entry from the code segment */
375     CS_DelEntry (D->Code, Index);
376
377     /* Adjust register loads if necessary */
378     AdjustLoadInfo (&D->Lhs, Index, -1);
379     AdjustLoadInfo (&D->Rhs, Index, -1);
380
381     /* Adjust the other indices if necessary */
382     if (Index < D->PushIndex) {
383         --D->PushIndex;
384     } else if (Index == D->PushIndex) {
385         D->PushEntry = 0;
386     }
387     if (Index < D->OpIndex) {
388         --D->OpIndex;
389     } else if (Index == D->OpIndex) {
390         D->OpEntry = 0;
391     }
392 }
393
394
395
396 static void CheckOneDirectOp (LoadRegInfo* LI, unsigned char Offs)
397 /* Check if the given entry is a lda instruction with an addressing mode
398  * that allows us to replace it by another operation (like ora). If so, we may
399  * use this location for the or and must not save the value in the zero
400  * page location.
401  */
402 {
403     /* Get the load entry */
404     CodeEntry* E = LI->LoadEntry;
405     if (E == 0) {
406         /* No load insn */
407         return;
408     }
409
410     /* Check the load entry */
411     if (E) {
412         /* Must check the call first since addressing mode is ABS, so second
413          * "if" will catch otherwise.
414          */
415         if (CE_IsCallTo (E, "ldaxysp")) {
416             /* Same as single loads from stack. Since we must distinguish
417              * between A and X here, the necessary offset is passed to the
418              * function as a parameter.
419              */
420             LI->Offs = (unsigned char) E->RI->In.RegY - Offs;
421             LI->Flags |= (LI_DIRECT | LI_RELOAD_Y);
422         } else if (E->AM == AM65_IMM || E->AM == AM65_ZP || E->AM == AM65_ABS) {
423             /* These insns are all ok and replaceable */
424             LI->Flags |= LI_DIRECT;
425         } else if (E->AM == AM65_ZP_INDY &&
426                    RegValIsKnown (E->RI->In.RegY) &&
427                    strcmp (E->Arg, "sp") == 0) {
428             /* A load from the stack with known offset is also ok, but in this
429              * case we must reload the index register later. Please note that
430              * a load indirect via other zero page locations is not ok, since
431              * these locations may change between the push and the actual
432              * operation.
433              */
434             LI->Offs  = (unsigned char) E->RI->In.RegY;
435             LI->Flags |= (LI_DIRECT | LI_RELOAD_Y);
436         }
437     }
438 }
439
440
441
442 static void CheckDirectOp (StackOpData* D)
443 /* Check if the given entry is a lda instruction with an addressing mode
444  * that allows us to replace it by another operation (like ora). If so, we may
445  * use this location for the or and must not save the value in the zero
446  * page location.
447  */
448 {
449     /* Check flags for all load instructions */
450     CheckOneDirectOp (&D->Lhs.A, 1);
451     CheckOneDirectOp (&D->Lhs.X, 0);
452     CheckOneDirectOp (&D->Rhs.A, 1);
453     CheckOneDirectOp (&D->Rhs.X, 0);
454 }
455
456
457
458 static void AddStoreA (StackOpData* D)
459 /* Add a store to zero page after the push insn */
460 {
461     CodeEntry* X = NewCodeEntry (OP65_STA, AM65_ZP, D->ZPLo, 0, D->PushEntry->LI);
462     InsertEntry (D, X, D->PushIndex+1);
463 }
464
465
466
467 static void AddStoreX (StackOpData* D)
468 /* Add a store to zero page after the push insn */
469 {
470     CodeEntry* X = NewCodeEntry (OP65_STX, AM65_ZP, D->ZPHi, 0, D->PushEntry->LI);
471     InsertEntry (D, X, D->PushIndex+1);
472 }
473
474
475
476 static void ReplacePushByStore (StackOpData* D)
477 /* Replace the call to the push subroutine by a store into the zero page
478  * location (actually, the push is not replaced, because we need it for
479  * later, but the name is still ok since the push will get removed at the
480  * end of each routine).
481  */
482 {
483     /* Store the value into the zeropage instead of pushing it. Check high
484      * byte first so that the store is later in A/X order.
485      */
486     if ((D->Lhs.X.Flags & LI_DIRECT) == 0) {
487         AddStoreX (D);
488     }
489     if ((D->Lhs.A.Flags & LI_DIRECT) == 0) {
490         AddStoreA (D);
491     }
492 }
493
494
495
496 static void AddOpLow (StackOpData* D, opc_t OPC)
497 /* Add an op for the low byte of an operator. This function honours the
498  * OP_DIRECT and OP_RELOAD_Y flags and generates the necessary instructions.
499  * All code is inserted at the current insertion point.
500  */
501 {
502     CodeEntry* X;
503
504     if ((D->Lhs.A.Flags & LI_DIRECT) != 0) {
505         /* Op with a variable location. If the location is on the stack, we
506          * need to reload the Y register.
507          */
508         if ((D->Lhs.A.Flags & LI_RELOAD_Y) == 0) {
509
510             /* opc ... */
511             CodeEntry* LoadA = D->Lhs.A.LoadEntry;
512             X = NewCodeEntry (OPC, LoadA->AM, LoadA->Arg, 0, D->OpEntry->LI);
513             InsertEntry (D, X, D->IP++);
514
515         } else {
516
517             /* ldy #offs */
518             const char* Arg = MakeHexArg (D->Lhs.A.Offs);
519             X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, D->OpEntry->LI);
520             InsertEntry (D, X, D->IP++);
521
522             /* opc (sp),y */
523             X = NewCodeEntry (OPC, AM65_ZP_INDY, "sp", 0, D->OpEntry->LI);
524             InsertEntry (D, X, D->IP++);
525
526         }
527
528         /* In both cases, we can remove the load */
529         D->Lhs.A.Flags |= LI_REMOVE;
530
531     } else {
532
533         /* Op with temp storage */
534         X = NewCodeEntry (OPC, AM65_ZP, D->ZPLo, 0, D->OpEntry->LI);
535         InsertEntry (D, X, D->IP++);
536
537     }
538 }
539
540
541
542 static void AddOpHigh (StackOpData* D, opc_t OPC)
543 /* Add an op for the high byte of an operator. Special cases (constant values
544  * or similar) have to be checked separately, the function covers only the
545  * generic case. Code is inserted at the insertion point.
546  */
547 {
548     CodeEntry* X;
549
550     /* pha */
551     X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, D->OpEntry->LI);
552     InsertEntry (D, X, D->IP++);
553
554     /* txa */
555     X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, D->OpEntry->LI);
556     InsertEntry (D, X, D->IP++);
557
558     if ((D->Lhs.X.Flags & LI_DIRECT) != 0) {
559
560         if ((D->Lhs.X.Flags & LI_RELOAD_Y) == 0) {
561
562             /* opc xxx */
563             CodeEntry* LoadX = D->Lhs.X.LoadEntry;
564             X = NewCodeEntry (OPC, LoadX->AM, LoadX->Arg, 0, D->OpEntry->LI);
565             InsertEntry (D, X, D->IP++);
566
567         } else {
568
569             /* ldy #const */
570             const char* Arg = MakeHexArg (D->Lhs.X.Offs);
571             X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, D->OpEntry->LI);
572             InsertEntry (D, X, D->IP++);
573
574             /* opc (sp),y */
575             X = NewCodeEntry (OPC, AM65_ZP_INDY, "sp", 0, D->OpEntry->LI);
576             InsertEntry (D, X, D->IP++);
577         }
578
579         /* In both cases, we can remove the load */
580         D->Lhs.X.Flags |= LI_REMOVE;
581
582     } else {
583         /* opc zphi */
584         X = NewCodeEntry (OPC, AM65_ZP, D->ZPHi, 0, D->OpEntry->LI);
585         InsertEntry (D, X, D->IP++);
586     }
587
588     /* tax */
589     X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, D->OpEntry->LI);
590     InsertEntry (D, X, D->IP++);
591
592     /* pla */
593     X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, D->OpEntry->LI);
594     InsertEntry (D, X, D->IP++);
595 }
596
597
598
599 static void RemoveRegLoads (StackOpData* D, LoadInfo* LI)
600 /* Remove register load insns */
601 {
602     /* Both registers may be loaded with one insn, but DelEntry will in this
603      * case clear the other one.
604      */
605     if (LI->A.LoadIndex >= 0 && (LI->A.Flags & LI_REMOVE)) {
606         DelEntry (D, LI->A.LoadIndex);
607     }
608     if (LI->X.LoadIndex >= 0 && (LI->X.Flags & LI_REMOVE)) {
609         DelEntry (D, LI->X.LoadIndex);
610     }
611 }
612
613
614
615 static void RemoveRemainders (StackOpData* D)
616 /* Remove the code that is unnecessary after translation of the sequence */
617 {
618     /* Remove the register loads for lhs and rhs */
619     RemoveRegLoads (D, &D->Lhs);
620     RemoveRegLoads (D, &D->Rhs);
621
622     /* Remove the push and the operator routine */
623     DelEntry (D, D->OpIndex);
624     DelEntry (D, D->PushIndex);
625 }
626
627
628
629 static int IsRegVar (StackOpData* D)
630 /* If the value pushed is that of a zeropage variable, replace ZPLo and ZPHi
631  * in the given StackOpData struct by the variable and return true. Otherwise
632  * leave D untouched and return false.
633  */
634 {
635     CodeEntry*  LoadA = D->Lhs.A.LoadEntry;
636     CodeEntry*  LoadX = D->Lhs.X.LoadEntry;
637     unsigned    Len;
638
639     /* Must have both load insns */
640     if (LoadA == 0 || LoadX == 0) {
641         return 0;
642     }
643
644     /* Must be loads from zp */
645     if (LoadA->AM != AM65_ZP || LoadX->AM != AM65_ZP) {
646         return 0;
647     }
648
649     /* Must be the same zp loc with high byte in X */
650     Len = strlen (LoadA->Arg);
651     if (strncmp (LoadA->Arg, LoadX->Arg, Len) != 0      ||
652         strcmp (LoadX->Arg + Len, "+1") != 0) {
653         return 0;
654     }
655
656     /* Use the zero page location directly */
657     D->ZPLo = LoadA->Arg;
658     D->ZPHi = LoadX->Arg;
659     return 1;
660 }
661
662
663
664 /*****************************************************************************/
665 /*                       Actual optimization functions                       */
666 /*****************************************************************************/
667
668
669
670 static unsigned Opt_toseqax_tosneax (StackOpData* D, const char* BoolTransformer)
671 /* Optimize the toseqax and tosneax sequences. */
672 {
673     CodeEntry*  X;
674     CodeLabel* L;
675
676     /* Create a call to the boolean transformer function and a label for this
677      * insn. This is needed for all variants. Other insns are inserted *before*
678      * the call.
679      */
680     X = NewCodeEntry (OP65_JSR, AM65_ABS, BoolTransformer, 0, D->OpEntry->LI);
681     InsertEntry (D, X, D->OpIndex + 1);
682     L = CS_GenLabel (D->Code, X);
683
684     /* If the lhs is direct (but not stack relative), encode compares with lhs
685      * effectively reverting the order (which doesn't matter for ==).
686      */
687     if ((D->Lhs.A.Flags & (LI_DIRECT | LI_RELOAD_Y)) == LI_DIRECT &&
688         (D->Lhs.X.Flags & (LI_DIRECT | LI_RELOAD_Y)) == LI_DIRECT) {
689
690         CodeEntry* LoadX = D->Lhs.X.LoadEntry;
691         CodeEntry* LoadA = D->Lhs.A.LoadEntry;
692
693         D->IP = D->OpIndex+1;
694
695         /* cpx */
696         X = NewCodeEntry (OP65_CPX, LoadX->AM, LoadX->Arg, 0, D->OpEntry->LI);
697         InsertEntry (D, X, D->IP++);
698
699         /* bne L */
700         X = NewCodeEntry (OP65_BNE, AM65_BRA, L->Name, L, D->OpEntry->LI);
701         InsertEntry (D, X, D->IP++);
702
703         /* cmp */
704         X = NewCodeEntry (OP65_CMP, LoadA->AM, LoadA->Arg, 0, D->OpEntry->LI);
705         InsertEntry (D, X, D->IP++);
706
707         /* Lhs load entries can be removed */
708         D->Lhs.X.Flags |= LI_REMOVE;
709         D->Lhs.A.Flags |= LI_REMOVE;
710
711     } else if ((D->Rhs.A.Flags & (LI_DIRECT | LI_RELOAD_Y)) == LI_DIRECT &&
712                (D->Rhs.X.Flags & (LI_DIRECT | LI_RELOAD_Y)) == LI_DIRECT) {
713
714         CodeEntry* LoadX = D->Rhs.X.LoadEntry;
715         CodeEntry* LoadA = D->Rhs.A.LoadEntry;
716
717         D->IP = D->OpIndex+1;
718
719         /* cpx */
720         X = NewCodeEntry (OP65_CPX, LoadX->AM, LoadX->Arg, 0, D->OpEntry->LI);
721         InsertEntry (D, X, D->IP++);
722
723         /* bne L */
724         X = NewCodeEntry (OP65_BNE, AM65_BRA, L->Name, L, D->OpEntry->LI);
725         InsertEntry (D, X, D->IP++);
726
727         /* cmp */
728         X = NewCodeEntry (OP65_CMP, LoadA->AM, LoadA->Arg, 0, D->OpEntry->LI);
729         InsertEntry (D, X, D->IP++);
730
731         /* Rhs load entries can be removed */
732         D->Rhs.X.Flags |= LI_REMOVE;
733         D->Rhs.A.Flags |= LI_REMOVE;
734
735     } else if ((D->Rhs.A.Flags & LI_DIRECT) != 0 &&
736                (D->Rhs.X.Flags & LI_DIRECT) != 0) {
737
738         D->IP = D->OpIndex+1;
739
740         /* If the location is on the stack, we need to reload the Y register. */
741         if ((D->Rhs.A.Flags & LI_RELOAD_Y) == 0) {
742
743             /* cmp ... */
744             CodeEntry* LoadA = D->Rhs.A.LoadEntry;
745             X = NewCodeEntry (OP65_CMP, LoadA->AM, LoadA->Arg, 0, D->OpEntry->LI);
746             InsertEntry (D, X, D->IP++);
747
748         } else {
749
750             /* ldy #offs */
751             const char* Arg = MakeHexArg (D->Rhs.A.Offs);
752             X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, D->OpEntry->LI);
753             InsertEntry (D, X, D->IP++);
754
755             /* cmp (sp),y */
756             X = NewCodeEntry (OP65_CMP, AM65_ZP_INDY, "sp", 0, D->OpEntry->LI);
757             InsertEntry (D, X, D->IP++);
758         }
759
760         /* In both cases, we can remove the load */
761         D->Rhs.A.Flags |= LI_REMOVE;
762
763         /* bne L */
764         X = NewCodeEntry (OP65_BNE, AM65_BRA, L->Name, L, D->OpEntry->LI);
765         InsertEntry (D, X, D->IP++);
766
767         /* txa */
768         X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, D->OpEntry->LI);
769         InsertEntry (D, X, D->IP++);
770
771         /* If the location is on the stack, we need to reload the Y register. */
772         if ((D->Rhs.X.Flags & LI_RELOAD_Y) == 0) {
773
774             /* cmp ... */
775             CodeEntry* LoadX = D->Rhs.X.LoadEntry;
776             X = NewCodeEntry (OP65_CMP, LoadX->AM, LoadX->Arg, 0, D->OpEntry->LI);
777             InsertEntry (D, X, D->IP++);
778
779         } else {
780
781             /* ldy #offs */
782             const char* Arg = MakeHexArg (D->Rhs.X.Offs);
783             X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, D->OpEntry->LI);
784             InsertEntry (D, X, D->IP++);
785
786             /* cmp (sp),y */
787             X = NewCodeEntry (OP65_CMP, AM65_ZP_INDY, "sp", 0, D->OpEntry->LI);
788             InsertEntry (D, X, D->IP++);
789         }
790
791         /* In both cases, we can remove the load */
792         D->Rhs.X.Flags |= LI_REMOVE;
793
794     } else {
795
796         /* Save lhs into zeropage, then compare */
797         AddStoreX (D);
798         AddStoreA (D);
799
800         D->IP = D->OpIndex+1;
801
802         /* cpx */
803         X = NewCodeEntry (OP65_CPX, AM65_ZP, D->ZPHi, 0, D->OpEntry->LI);
804         InsertEntry (D, X, D->IP++);
805
806         /* bne L */
807         X = NewCodeEntry (OP65_BNE, AM65_BRA, L->Name, L, D->OpEntry->LI);
808         InsertEntry (D, X, D->IP++);
809
810         /* cmp */
811         X = NewCodeEntry (OP65_CMP, AM65_ZP, D->ZPLo, 0, D->OpEntry->LI);
812         InsertEntry (D, X, D->IP++);
813
814     }
815
816     /* Remove the push and the call to the tosgeax function */
817     RemoveRemainders (D);
818
819     /* We changed the sequence */
820     return 1;
821 }
822
823
824
825 static unsigned Opt___bzero (StackOpData* D)
826 /* Optimize the __bzero sequence */
827 {
828     CodeEntry*  X;
829     const char* Arg;
830     CodeLabel*  L;
831
832     /* Check if we're using a register variable */
833     if (!IsRegVar (D)) {
834         /* Store the value into the zeropage instead of pushing it */
835         AddStoreX (D);
836         AddStoreA (D);
837     }
838
839     /* If the return value of __bzero is used, we have to add code to reload
840      * a/x from the pointer variable.
841      */
842     if (RegAXUsed (D->Code, D->OpIndex+1)) {
843         X = NewCodeEntry (OP65_LDA, AM65_ZP, D->ZPLo, 0, D->OpEntry->LI);
844         InsertEntry (D, X, D->OpIndex+1);
845         X = NewCodeEntry (OP65_LDX, AM65_ZP, D->ZPHi, 0, D->OpEntry->LI);
846         InsertEntry (D, X, D->OpIndex+2);
847     }
848
849     /* X is always zero, A contains the size of the data area to zero.
850      * Note: A may be zero, in which case the operation is null op.
851      */
852     if (D->OpEntry->RI->In.RegA != 0) {
853
854         /* lda #$00 */
855         X = NewCodeEntry (OP65_LDA, AM65_IMM, "$00", 0, D->OpEntry->LI);
856         InsertEntry (D, X, D->OpIndex+1);
857
858         /* The value of A is known */
859         if (D->OpEntry->RI->In.RegA <= 0x81) {
860
861             /* Loop using the sign bit */
862
863             /* ldy #count-1 */
864             Arg = MakeHexArg (D->OpEntry->RI->In.RegA - 1);
865             X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, D->OpEntry->LI);
866             InsertEntry (D, X, D->OpIndex+2);
867
868             /* L: sta (zp),y */
869             X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, D->ZPLo, 0, D->OpEntry->LI);
870             InsertEntry (D, X, D->OpIndex+3);
871             L = CS_GenLabel (D->Code, X);
872
873             /* dey */
874             X = NewCodeEntry (OP65_DEY, AM65_IMP, 0, 0, D->OpEntry->LI);
875             InsertEntry (D, X, D->OpIndex+4);
876
877             /* bpl L */
878             X = NewCodeEntry (OP65_BPL, AM65_BRA, L->Name, L, D->OpEntry->LI);
879             InsertEntry (D, X, D->OpIndex+5);
880
881         } else {
882
883             /* Loop using an explicit compare */
884
885             /* ldy #$00 */
886             X = NewCodeEntry (OP65_LDY, AM65_IMM, "$00", 0, D->OpEntry->LI);
887             InsertEntry (D, X, D->OpIndex+2);
888
889             /* L: sta (zp),y */
890             X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, D->ZPLo, 0, D->OpEntry->LI);
891             InsertEntry (D, X, D->OpIndex+3);
892             L = CS_GenLabel (D->Code, X);
893
894             /* iny */
895             X = NewCodeEntry (OP65_INY, AM65_IMP, 0, 0, D->OpEntry->LI);
896             InsertEntry (D, X, D->OpIndex+4);
897
898             /* cpy #count */
899             Arg = MakeHexArg (D->OpEntry->RI->In.RegA);
900             X = NewCodeEntry (OP65_CPY, AM65_IMM, Arg, 0, D->OpEntry->LI);
901             InsertEntry (D, X, D->OpIndex+5);
902
903             /* bne L */
904             X = NewCodeEntry (OP65_BNE, AM65_BRA, L->Name, L, D->OpEntry->LI);
905             InsertEntry (D, X, D->OpIndex+6);
906         }
907
908     }
909
910     /* Remove the push and the call to the __bzero function */
911     RemoveRemainders (D);
912
913     /* We changed the sequence */
914     return 1;
915 }
916
917
918
919 static unsigned Opt_staspidx (StackOpData* D)
920 /* Optimize the staspidx sequence */
921 {
922     CodeEntry* X;
923
924     /* Check if we're using a register variable */
925     if (!IsRegVar (D)) {
926         /* Store the value into the zeropage instead of pushing it */
927         AddStoreX (D);
928         AddStoreA (D);
929     }
930
931     /* Replace the store subroutine call by a direct op */
932     X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, D->ZPLo, 0, D->OpEntry->LI);
933     InsertEntry (D, X, D->OpIndex+1);
934
935     /* Remove the push and the call to the staspidx function */
936     RemoveRemainders (D);
937
938     /* We changed the sequence */
939     return 1;
940 }
941
942
943
944 static unsigned Opt_staxspidx (StackOpData* D)
945 /* Optimize the staxspidx sequence */
946 {
947     CodeEntry* X;
948
949     /* Check if we're using a register variable */
950     if (!IsRegVar (D)) {
951         /* Store the value into the zeropage instead of pushing it */
952         AddStoreX (D);
953         AddStoreA (D);
954     }
955
956     /* Inline the store */
957
958     /* sta (zp),y */
959     X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, D->ZPLo, 0, D->OpEntry->LI);
960     InsertEntry (D, X, D->OpIndex+1);
961
962     if (RegValIsKnown (D->OpEntry->RI->In.RegY)) {
963         /* Value of Y is known */
964         const char* Arg = MakeHexArg (D->OpEntry->RI->In.RegY + 1);
965         X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, D->OpEntry->LI);
966     } else {
967         X = NewCodeEntry (OP65_INY, AM65_IMP, 0, 0, D->OpEntry->LI);
968     }
969     InsertEntry (D, X, D->OpIndex+2);
970
971     if (RegValIsKnown (D->OpEntry->RI->In.RegX)) {
972         /* Value of X is known */
973         const char* Arg = MakeHexArg (D->OpEntry->RI->In.RegX);
974         X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, D->OpEntry->LI);
975     } else {
976         /* Value unknown */
977         X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, D->OpEntry->LI);
978     }
979     InsertEntry (D, X, D->OpIndex+3);
980
981     /* sta (zp),y */
982     X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, D->ZPLo, 0, D->OpEntry->LI);
983     InsertEntry (D, X, D->OpIndex+4);
984
985     /* If we remove staxspidx, we must restore the Y register to what the
986      * function would return.
987      */
988     X = NewCodeEntry (OP65_LDY, AM65_IMM, "$00", 0, D->OpEntry->LI);
989     InsertEntry (D, X, D->OpIndex+5);
990
991     /* Remove the push and the call to the staxspidx function */
992     RemoveRemainders (D);
993
994     /* We changed the sequence */
995     return 1;
996 }
997
998
999
1000 static unsigned Opt_tosaddax (StackOpData* D)
1001 /* Optimize the tosaddax sequence */
1002 {
1003     CodeEntry*  X;
1004     CodeEntry*  N;
1005
1006     /* We need the entry behind the add */
1007     CHECK (D->NextEntry != 0);
1008
1009     /* Check if the X register is known and zero when the add is done, and
1010      * if the add is followed by
1011      *
1012      *  ldy     #$00
1013      *  jsr     ldauidx         ; or ldaidx
1014      *
1015      * If this is true, the addition does actually add an offset to a pointer
1016      * before it is dereferenced. Since both subroutines take an offset in Y,
1017      * we can pass the offset (instead of #$00) and remove the addition
1018      * alltogether.
1019      */
1020     if (D->OpEntry->RI->In.RegX == 0                            &&
1021         D->NextEntry->OPC == OP65_LDY                           &&
1022         CE_IsKnownImm (D->NextEntry, 0)                         &&
1023         !CE_HasLabel (D->NextEntry)                             &&
1024         (N = CS_GetNextEntry (D->Code, D->OpIndex + 1)) != 0    &&
1025         (CE_IsCallTo (N, "ldauidx")                     ||
1026          CE_IsCallTo (N, "ldaidx"))) {
1027
1028         int Signed = (strcmp (N->Arg, "ldaidx") == 0);
1029
1030         /* Store the value into the zeropage instead of pushing it */
1031         AddStoreX (D);
1032         AddStoreA (D);
1033
1034         /* Replace the ldy by a tay. Be sure to create the new entry before
1035          * deleting the ldy, since we will reference the line info from this
1036          * insn.
1037          */
1038         X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, D->NextEntry->LI);
1039         DelEntry (D, D->OpIndex + 1);
1040         InsertEntry (D, X, D->OpIndex + 1);
1041
1042         /* Replace the call to ldaidx/ldauidx. Since X is already zero, and
1043          * the ptr is in the zero page location, we just need to load from
1044          * the pointer, and fix X in case of ldaidx.
1045          */
1046         X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, D->ZPLo, 0, N->LI);
1047         DelEntry (D, D->OpIndex + 2);
1048         InsertEntry (D, X, D->OpIndex + 2);
1049         if (Signed) {
1050
1051             CodeLabel* L;
1052
1053             /* Add sign extension - N is unused now */
1054             N = CS_GetNextEntry (D->Code, D->OpIndex + 2);
1055             CHECK (N != 0);
1056             L = CS_GenLabel (D->Code, N);
1057
1058             X = NewCodeEntry (OP65_BPL, AM65_BRA, L->Name, L, X->LI);
1059             InsertEntry (D, X, D->OpIndex + 3);
1060
1061             X = NewCodeEntry (OP65_DEX, AM65_IMP, 0, 0, X->LI);
1062             InsertEntry (D, X, D->OpIndex + 4);
1063         }
1064
1065     } else {
1066
1067         /* Store the value into the zeropage instead of pushing it */
1068         ReplacePushByStore (D);
1069
1070         /* Inline the add */
1071         D->IP = D->OpIndex+1;
1072
1073         /* clc */
1074         X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, D->OpEntry->LI);
1075         InsertEntry (D, X, D->IP++);
1076
1077         /* Low byte */
1078         AddOpLow (D, OP65_ADC);
1079
1080         /* High byte */
1081         if (D->PushEntry->RI->In.RegX == 0) {
1082             /* The high byte is the value in X plus the carry */
1083             CodeLabel* L = CS_GenLabel (D->Code, D->NextEntry);
1084             X = NewCodeEntry (OP65_BCC, AM65_BRA, L->Name, L, D->OpEntry->LI);
1085             InsertEntry (D, X, D->IP++);
1086             X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, D->OpEntry->LI);
1087             InsertEntry (D, X, D->IP++);
1088         } else if (D->OpEntry->RI->In.RegX == 0) {
1089             /* The high byte is that of the first operand plus carry */
1090             CodeLabel* L;
1091             if (RegValIsKnown (D->PushEntry->RI->In.RegX)) {
1092                 /* Value of first op high byte is known */
1093                 const char* Arg = MakeHexArg (D->PushEntry->RI->In.RegX);
1094                 X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, D->OpEntry->LI);
1095             } else {
1096                 /* Value of first op high byte is unknown */
1097                 X = NewCodeEntry (OP65_LDX, AM65_ZP, D->ZPHi, 0, D->OpEntry->LI);
1098             }
1099             InsertEntry (D, X, D->IP++);
1100
1101             /* bcc label */
1102             L = CS_GenLabel (D->Code, D->NextEntry);
1103             X = NewCodeEntry (OP65_BCC, AM65_BRA, L->Name, L, D->OpEntry->LI);
1104             InsertEntry (D, X, D->IP++);
1105
1106             /* inx */
1107             X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, D->OpEntry->LI);
1108             InsertEntry (D, X, D->IP++);
1109         } else {
1110             /* High byte is unknown */
1111             AddOpHigh (D, OP65_ADC);
1112         }
1113     }
1114
1115     /* Remove the push and the call to the tosaddax function */
1116     RemoveRemainders (D);
1117
1118     /* We changed the sequence */
1119     return 1;
1120 }
1121
1122
1123
1124 static unsigned Opt_tosandax (StackOpData* D)
1125 /* Optimize the tosandax sequence */
1126 {
1127     CodeEntry*  X;
1128
1129     /* Store the value into the zeropage instead of pushing it */
1130     ReplacePushByStore (D);
1131
1132     /* Inline the and, low byte */
1133     D->IP = D->OpIndex + 1;
1134     AddOpLow (D, OP65_AND);
1135
1136     /* High byte */
1137     if (D->PushEntry->RI->In.RegX == 0 || D->OpEntry->RI->In.RegX == 0) {
1138         /* The high byte is zero */
1139         X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, D->OpEntry->LI);
1140         InsertEntry (D, X, D->IP++);
1141     } else {
1142         /* High byte is unknown */
1143         AddOpHigh (D, OP65_AND);
1144     }
1145
1146     /* Remove the push and the call to the tosandax function */
1147     RemoveRemainders (D);
1148
1149     /* We changed the sequence */
1150     return 1;
1151 }
1152
1153
1154
1155 static unsigned Opt_toseqax (StackOpData* D)
1156 /* Optimize the toseqax sequence */
1157 {
1158     return Opt_toseqax_tosneax (D, "booleq");
1159 }
1160
1161
1162
1163 static unsigned Opt_tosgeax (StackOpData* D)
1164 /* Optimize the tosgeax sequence */
1165 {
1166     CodeEntry*  X;
1167     CodeLabel* L;
1168
1169     /* Inline the sbc */
1170     D->IP = D->OpIndex+1;
1171
1172     /* Must be true because of OP_RHS_LOAD */
1173     CHECK ((D->Rhs.A.Flags & LI_DIRECT) != 0);
1174
1175     /* If the location is on the stack, we need to reload the Y register. */
1176     if ((D->Rhs.A.Flags & LI_RELOAD_Y) == 0) {
1177
1178         /* cmp ... */
1179         CodeEntry* LoadA = D->Rhs.A.LoadEntry;
1180         X = NewCodeEntry (OP65_CMP, LoadA->AM, LoadA->Arg, 0, D->OpEntry->LI);
1181         InsertEntry (D, X, D->IP++);
1182
1183     } else {
1184
1185         /* ldy #offs */
1186         const char* Arg = MakeHexArg (D->Rhs.A.Offs);
1187         X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, D->OpEntry->LI);
1188         InsertEntry (D, X, D->IP++);
1189
1190         /* cmp (sp),y */
1191         X = NewCodeEntry (OP65_CMP, AM65_ZP_INDY, "sp", 0, D->OpEntry->LI);
1192         InsertEntry (D, X, D->IP++);
1193     }
1194
1195     /* In both cases, we can remove the load */
1196     D->Rhs.A.Flags |= LI_REMOVE;
1197
1198     /* txa */
1199     X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, D->OpEntry->LI);
1200     InsertEntry (D, X, D->IP++);
1201
1202     /* Must be true because of OP_RHS_LOAD */
1203     CHECK ((D->Rhs.X.Flags & LI_DIRECT) != 0);
1204
1205     /* If the location is on the stack, we need to reload the Y register. */
1206     if ((D->Rhs.X.Flags & LI_RELOAD_Y) == 0) {
1207
1208         /* sbc ... */
1209         CodeEntry* LoadX = D->Rhs.X.LoadEntry;
1210         X = NewCodeEntry (OP65_SBC, LoadX->AM, LoadX->Arg, 0, D->OpEntry->LI);
1211         InsertEntry (D, X, D->IP++);
1212
1213     } else {
1214
1215         /* ldy #offs */
1216         const char* Arg = MakeHexArg (D->Rhs.X.Offs);
1217         X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, D->OpEntry->LI);
1218         InsertEntry (D, X, D->IP++);
1219
1220         /* sbc (sp),y */
1221         X = NewCodeEntry (OP65_SBC, AM65_ZP_INDY, "sp", 0, D->OpEntry->LI);
1222         InsertEntry (D, X, D->IP++);
1223     }
1224
1225     /* In both cases, we can remove the load */
1226     D->Rhs.X.Flags |= LI_REMOVE;
1227
1228     /* eor #$80 */
1229     X = NewCodeEntry (OP65_EOR, AM65_IMM, "$80", 0, D->OpEntry->LI);
1230     InsertEntry (D, X, D->IP++);
1231
1232     /* asl a */
1233     X = NewCodeEntry (OP65_ASL, AM65_ACC, "a", 0, D->OpEntry->LI);
1234     InsertEntry (D, X, D->IP++);
1235     L = CS_GenLabel (D->Code, X);
1236
1237     /* Insert a bvs L before the eor insn */
1238     X = NewCodeEntry (OP65_BVS, AM65_BRA, L->Name, L, D->OpEntry->LI);
1239     InsertEntry (D, X, D->IP - 2);
1240     ++D->IP;
1241
1242     /* lda #$00 */
1243     X = NewCodeEntry (OP65_LDA, AM65_IMM, "$00", 0, D->OpEntry->LI);
1244     InsertEntry (D, X, D->IP++);
1245
1246     /* ldx #$00 */
1247     X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, D->OpEntry->LI);
1248     InsertEntry (D, X, D->IP++);
1249
1250     /* rol a */
1251     X = NewCodeEntry (OP65_ROL, AM65_ACC, "a", 0, D->OpEntry->LI);
1252     InsertEntry (D, X, D->IP++);
1253
1254     /* Remove the push and the call to the tosgeax function */
1255     RemoveRemainders (D);
1256
1257     /* We changed the sequence */
1258     return 1;
1259 }
1260
1261
1262
1263 static unsigned Opt_tosltax (StackOpData* D)
1264 /* Optimize the tosltax sequence */
1265 {
1266     CodeEntry*  X;
1267     CodeLabel* L;
1268
1269
1270     /* Inline the sbc */
1271     D->IP = D->OpIndex+1;
1272
1273     /* Must be true because of OP_RHS_LOAD */
1274     CHECK ((D->Rhs.A.Flags & LI_DIRECT) != 0);
1275
1276     /* If the location is on the stack, we need to reload the Y register. */
1277     if ((D->Rhs.A.Flags & LI_RELOAD_Y) == 0) {
1278
1279         /* cmp ... */
1280         CodeEntry* LoadA = D->Rhs.A.LoadEntry;
1281         X = NewCodeEntry (OP65_CMP, LoadA->AM, LoadA->Arg, 0, D->OpEntry->LI);
1282         InsertEntry (D, X, D->IP++);
1283
1284     } else {
1285
1286         /* ldy #offs */
1287         const char* Arg = MakeHexArg (D->Rhs.A.Offs);
1288         X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, D->OpEntry->LI);
1289         InsertEntry (D, X, D->IP++);
1290
1291         /* cmp (sp),y */
1292         X = NewCodeEntry (OP65_CMP, AM65_ZP_INDY, "sp", 0, D->OpEntry->LI);
1293         InsertEntry (D, X, D->IP++);
1294     }
1295
1296     /* In both cases, we can remove the load */
1297     D->Rhs.A.Flags |= LI_REMOVE;
1298
1299     /* txa */
1300     X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, D->OpEntry->LI);
1301     InsertEntry (D, X, D->IP++);
1302
1303     /* Must be true because of OP_RHS_LOAD */
1304     CHECK ((D->Rhs.X.Flags & LI_DIRECT) != 0);
1305
1306     /* If the location is on the stack, we need to reload the Y register. */
1307     if ((D->Rhs.X.Flags & LI_RELOAD_Y) == 0) {
1308
1309         /* sbc ... */
1310         CodeEntry* LoadX = D->Rhs.X.LoadEntry;
1311         X = NewCodeEntry (OP65_SBC, LoadX->AM, LoadX->Arg, 0, D->OpEntry->LI);
1312         InsertEntry (D, X, D->IP++);
1313
1314     } else {
1315
1316         /* ldy #offs */
1317         const char* Arg = MakeHexArg (D->Rhs.X.Offs);
1318         X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, D->OpEntry->LI);
1319         InsertEntry (D, X, D->IP++);
1320
1321         /* sbc (sp),y */
1322         X = NewCodeEntry (OP65_SBC, AM65_ZP_INDY, "sp", 0, D->OpEntry->LI);
1323         InsertEntry (D, X, D->IP++);
1324     }
1325
1326     /* In both cases, we can remove the load */
1327     D->Rhs.X.Flags |= LI_REMOVE;
1328
1329     /* eor #$80 */
1330     X = NewCodeEntry (OP65_EOR, AM65_IMM, "$80", 0, D->OpEntry->LI);
1331     InsertEntry (D, X, D->IP++);
1332
1333     /* asl a */
1334     X = NewCodeEntry (OP65_ASL, AM65_ACC, "a", 0, D->OpEntry->LI);
1335     InsertEntry (D, X, D->IP++);
1336     L = CS_GenLabel (D->Code, X);
1337
1338     /* Insert a bvc L before the eor insn */
1339     X = NewCodeEntry (OP65_BVC, AM65_BRA, L->Name, L, D->OpEntry->LI);
1340     InsertEntry (D, X, D->IP - 2);
1341     ++D->IP;
1342
1343     /* lda #$00 */
1344     X = NewCodeEntry (OP65_LDA, AM65_IMM, "$00", 0, D->OpEntry->LI);
1345     InsertEntry (D, X, D->IP++);
1346
1347     /* ldx #$00 */
1348     X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, D->OpEntry->LI);
1349     InsertEntry (D, X, D->IP++);
1350
1351     /* rol a */
1352     X = NewCodeEntry (OP65_ROL, AM65_ACC, "a", 0, D->OpEntry->LI);
1353     InsertEntry (D, X, D->IP++);
1354
1355     /* Remove the push and the call to the tosltax function */
1356     RemoveRemainders (D);
1357
1358     /* We changed the sequence */
1359     return 1;
1360 }
1361
1362
1363
1364 static unsigned Opt_tosneax (StackOpData* D)
1365 /* Optimize the tosneax sequence */
1366 {
1367     return Opt_toseqax_tosneax (D, "boolne");
1368 }
1369
1370
1371
1372 static unsigned Opt_tosorax (StackOpData* D)
1373 /* Optimize the tosorax sequence */
1374 {
1375     CodeEntry*  X;
1376
1377     /* Store the value into the zeropage instead of pushing it */
1378     ReplacePushByStore (D);
1379
1380     /* Inline the or, low byte */
1381     D->IP = D->OpIndex + 1;
1382     AddOpLow (D, OP65_ORA);
1383
1384     /* High byte */
1385     if (RegValIsKnown (D->PushEntry->RI->In.RegX) &&
1386         RegValIsKnown (D->OpEntry->RI->In.RegX)) {
1387         /* Both values known, precalculate the result */
1388         unsigned char Result = D->PushEntry->RI->In.RegX | D->OpEntry->RI->In.RegX;
1389         const char* Arg = MakeHexArg (Result);
1390         X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, D->OpEntry->LI);
1391         InsertEntry (D, X, D->IP++);
1392     } else if (D->PushEntry->RI->In.RegX != 0) {
1393         /* High byte is unknown */
1394         AddOpHigh (D, OP65_ORA);
1395     }
1396
1397     /* Remove the push and the call to the tosorax function */
1398     RemoveRemainders (D);
1399
1400     /* We changed the sequence */
1401     return 1;
1402 }
1403
1404
1405
1406 static unsigned Opt_tossubax (StackOpData* D)
1407 /* Optimize the tossubax sequence. Note: subtraction is not commutative! */
1408 {
1409     CodeEntry*  X;
1410
1411
1412     /* Inline the sbc */
1413     D->IP = D->OpIndex+1;
1414
1415     /* sec */
1416     X = NewCodeEntry (OP65_SEC, AM65_IMP, 0, 0, D->OpEntry->LI);
1417     InsertEntry (D, X, D->IP++);
1418
1419     /* Must be true because of OP_RHS_LOAD */
1420     CHECK ((D->Rhs.A.Flags & LI_DIRECT) != 0);
1421
1422     /* If the location is on the stack, we need to reload the Y register. */
1423     if ((D->Rhs.A.Flags & LI_RELOAD_Y) == 0) {
1424
1425         /* sbc ... */
1426         CodeEntry* LoadA = D->Rhs.A.LoadEntry;
1427         X = NewCodeEntry (OP65_SBC, LoadA->AM, LoadA->Arg, 0, D->OpEntry->LI);
1428         InsertEntry (D, X, D->IP++);
1429
1430     } else {
1431
1432         /* ldy #offs */
1433         const char* Arg = MakeHexArg (D->Rhs.A.Offs);
1434         X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, D->OpEntry->LI);
1435         InsertEntry (D, X, D->IP++);
1436
1437         /* sbc (sp),y */
1438         X = NewCodeEntry (OP65_SBC, AM65_ZP_INDY, "sp", 0, D->OpEntry->LI);
1439         InsertEntry (D, X, D->IP++);
1440     }
1441
1442     /* In both cases, we can remove the load */
1443     D->Rhs.A.Flags |= LI_REMOVE;
1444
1445     /* pha */
1446     X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, D->OpEntry->LI);
1447     InsertEntry (D, X, D->IP++);
1448
1449     /* txa */
1450     X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, D->OpEntry->LI);
1451     InsertEntry (D, X, D->IP++);
1452
1453     /* Must be true because of OP_RHS_LOAD */
1454     CHECK ((D->Rhs.X.Flags & LI_DIRECT) != 0);
1455
1456     /* If the location is on the stack, we need to reload the Y register. */
1457     if ((D->Rhs.X.Flags & LI_RELOAD_Y) == 0) {
1458
1459         /* sbc ... */
1460         CodeEntry* LoadX = D->Rhs.X.LoadEntry;
1461         X = NewCodeEntry (OP65_SBC, LoadX->AM, LoadX->Arg, 0, D->OpEntry->LI);
1462         InsertEntry (D, X, D->IP++);
1463
1464     } else {
1465
1466         /* ldy #offs */
1467         const char* Arg = MakeHexArg (D->Rhs.X.Offs);
1468         X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, D->OpEntry->LI);
1469         InsertEntry (D, X, D->IP++);
1470
1471         /* sbc (sp),y */
1472         X = NewCodeEntry (OP65_SBC, AM65_ZP_INDY, "sp", 0, D->OpEntry->LI);
1473         InsertEntry (D, X, D->IP++);
1474     }
1475
1476     /* In both cases, we can remove the load */
1477     D->Rhs.X.Flags |= LI_REMOVE;
1478
1479     /* tax */
1480     X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, D->OpEntry->LI);
1481     InsertEntry (D, X, D->IP++);
1482
1483     /* pla */
1484     X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, D->OpEntry->LI);
1485     InsertEntry (D, X, D->IP++);
1486
1487     /* Remove the push and the call to the tossubax function */
1488     RemoveRemainders (D);
1489
1490     /* We changed the sequence */
1491     return 1;
1492 }
1493
1494
1495
1496 static unsigned Opt_tosugeax (StackOpData* D)
1497 /* Optimize the tosugeax sequence */
1498 {
1499     CodeEntry*  X;
1500
1501
1502     /* Inline the sbc */
1503     D->IP = D->OpIndex+1;
1504
1505     /* Must be true because of OP_RHS_LOAD */
1506     CHECK ((D->Rhs.A.Flags & LI_DIRECT) != 0);
1507
1508     /* If the location is on the stack, we need to reload the Y register. */
1509     if ((D->Rhs.A.Flags & LI_RELOAD_Y) == 0) {
1510
1511         /* cmp ... */
1512         CodeEntry* LoadA = D->Rhs.A.LoadEntry;
1513         X = NewCodeEntry (OP65_CMP, LoadA->AM, LoadA->Arg, 0, D->OpEntry->LI);
1514         InsertEntry (D, X, D->IP++);
1515
1516     } else {
1517
1518         /* ldy #offs */
1519         const char* Arg = MakeHexArg (D->Rhs.A.Offs);
1520         X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, D->OpEntry->LI);
1521         InsertEntry (D, X, D->IP++);
1522
1523         /* cmp (sp),y */
1524         X = NewCodeEntry (OP65_CMP, AM65_ZP_INDY, "sp", 0, D->OpEntry->LI);
1525         InsertEntry (D, X, D->IP++);
1526     }
1527
1528     /* In both cases, we can remove the load */
1529     D->Rhs.A.Flags |= LI_REMOVE;
1530
1531     /* txa */
1532     X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, D->OpEntry->LI);
1533     InsertEntry (D, X, D->IP++);
1534
1535     /* Must be true because of OP_RHS_LOAD */
1536     CHECK ((D->Rhs.X.Flags & LI_DIRECT) != 0);
1537
1538     /* If the location is on the stack, we need to reload the Y register. */
1539     if ((D->Rhs.X.Flags & LI_RELOAD_Y) == 0) {
1540
1541         /* sbc ... */
1542         CodeEntry* LoadX = D->Rhs.X.LoadEntry;
1543         X = NewCodeEntry (OP65_SBC, LoadX->AM, LoadX->Arg, 0, D->OpEntry->LI);
1544         InsertEntry (D, X, D->IP++);
1545
1546     } else {
1547
1548         /* ldy #offs */
1549         const char* Arg = MakeHexArg (D->Rhs.X.Offs);
1550         X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, D->OpEntry->LI);
1551         InsertEntry (D, X, D->IP++);
1552
1553         /* sbc (sp),y */
1554         X = NewCodeEntry (OP65_SBC, AM65_ZP_INDY, "sp", 0, D->OpEntry->LI);
1555         InsertEntry (D, X, D->IP++);
1556     }
1557
1558     /* In both cases, we can remove the load */
1559     D->Rhs.X.Flags |= LI_REMOVE;
1560
1561     /* lda #$00 */
1562     X = NewCodeEntry (OP65_LDA, AM65_IMM, "$00", 0, D->OpEntry->LI);
1563     InsertEntry (D, X, D->IP++);
1564
1565     /* ldx #$00 */
1566     X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, D->OpEntry->LI);
1567     InsertEntry (D, X, D->IP++);
1568
1569     /* rol a */
1570     X = NewCodeEntry (OP65_ROL, AM65_ACC, "a", 0, D->OpEntry->LI);
1571     InsertEntry (D, X, D->IP++);
1572
1573     /* Remove the push and the call to the tosugeax function */
1574     RemoveRemainders (D);
1575
1576     /* We changed the sequence */
1577     return 1;
1578 }
1579
1580
1581
1582 static unsigned Opt_tosxorax (StackOpData* D)
1583 /* Optimize the tosxorax sequence */
1584 {
1585     CodeEntry*  X;
1586
1587
1588     /* Store the value into the zeropage instead of pushing it */
1589     ReplacePushByStore (D);
1590
1591     /* Inline the xor, low byte */
1592     D->IP = D->OpIndex + 1;
1593     AddOpLow (D, OP65_EOR);
1594
1595     /* High byte */
1596     if (RegValIsKnown (D->PushEntry->RI->In.RegX) &&
1597         RegValIsKnown (D->OpEntry->RI->In.RegX)) {
1598         /* Both values known, precalculate the result */
1599         const char* Arg = MakeHexArg (D->PushEntry->RI->In.RegX ^ D->OpEntry->RI->In.RegX);
1600         X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, D->OpEntry->LI);
1601         InsertEntry (D, X, D->IP++);
1602     } else if (D->PushEntry->RI->In.RegX != 0) {
1603         /* High byte is unknown */
1604         AddOpHigh (D, OP65_EOR);
1605     }
1606
1607     /* Remove the push and the call to the tosandax function */
1608     RemoveRemainders (D);
1609
1610     /* We changed the sequence */
1611     return 1;
1612 }
1613
1614
1615
1616 /*****************************************************************************/
1617 /*                                   Code                                    */
1618 /*****************************************************************************/
1619
1620
1621
1622 static const OptFuncDesc FuncTable[] = {
1623     { "__bzero",    Opt___bzero,   REG_NONE, OP_X_ZERO | OP_A_KNOWN     },
1624     { "staspidx",   Opt_staspidx,  REG_NONE, OP_NONE                    },
1625     { "staxspidx",  Opt_staxspidx, REG_AX,   OP_NONE                    },
1626     { "tosaddax",   Opt_tosaddax,  REG_NONE, OP_NONE                    },
1627     { "tosandax",   Opt_tosandax,  REG_NONE, OP_NONE                    },
1628     { "toseqax",    Opt_toseqax,   REG_NONE, OP_NONE                    },
1629     { "tosgeax",    Opt_tosgeax,   REG_NONE, OP_RHS_LOAD_DIRECT         },
1630     { "tosltax",    Opt_tosltax,   REG_NONE, OP_RHS_LOAD_DIRECT         },
1631     { "tosneax",    Opt_tosneax,   REG_NONE, OP_NONE                    },
1632     { "tosorax",    Opt_tosorax,   REG_NONE, OP_NONE                    },
1633     { "tossubax",   Opt_tossubax,  REG_NONE, OP_RHS_LOAD_DIRECT         },
1634     { "tosugeax",   Opt_tosugeax,  REG_NONE, OP_RHS_LOAD_DIRECT         },
1635     { "tosxorax",   Opt_tosxorax,  REG_NONE, OP_NONE                    },
1636 };
1637 #define FUNC_COUNT (sizeof(FuncTable) / sizeof(FuncTable[0]))
1638
1639
1640
1641 static int CmpFunc (const void* Key, const void* Func)
1642 /* Compare function for bsearch */
1643 {
1644     return strcmp (Key, ((const OptFuncDesc*) Func)->Name);
1645 }
1646
1647
1648
1649 static const OptFuncDesc* FindFunc (const char* Name)
1650 /* Find the function with the given name. Return a pointer to the table entry
1651  * or NULL if the function was not found.
1652  */
1653 {
1654     return bsearch (Name, FuncTable, FUNC_COUNT, sizeof(OptFuncDesc), CmpFunc);
1655 }
1656
1657
1658
1659 static int CmpHarmless (const void* Key, const void* Entry)
1660 /* Compare function for bsearch */
1661 {
1662     return strcmp (Key, *(const char**)Entry);
1663 }
1664
1665
1666
1667 static int HarmlessCall (const char* Name)
1668 /* Check if this is a call to a harmless subroutine that will not interrupt
1669  * the pushax/op sequence when encountered.
1670  */
1671 {
1672     static const char* Tab[] = {
1673         "aslax1",
1674         "aslax2",
1675         "asrax1",
1676         "asrax2",
1677         "bnegax",
1678         "ldaxidx",
1679         "ldaxysp",
1680         "negax",
1681         "shlax1",
1682         "shlax2",
1683         "shrax1",
1684         "shrax2",
1685     };
1686
1687     void* R = bsearch (Name,
1688                        Tab,
1689                        sizeof (Tab) / sizeof (Tab[0]),
1690                        sizeof (Tab[0]),
1691                        CmpHarmless);
1692     return (R != 0);
1693 }
1694
1695
1696
1697 static void ResetStackOpData (StackOpData* Data)
1698 /* Reset the given data structure */
1699 {
1700     Data->OptFunc       = 0;
1701     Data->UsedRegs      = REG_NONE;
1702
1703     ClearLoadInfo (&Data->Lhs);
1704     ClearLoadInfo (&Data->Rhs);
1705
1706     Data->PushIndex     = -1;
1707     Data->OpIndex       = -1;
1708 }
1709
1710
1711
1712 static int PreCondOk (StackOpData* D)
1713 /* Check if the preconditions for a call to the optimizer subfunction are
1714  * satisfied. As a side effect, this function will also choose the zero page
1715  * register to use.
1716  */
1717 {
1718     /* Check the flags */
1719     unsigned UnusedRegs = D->OptFunc->UnusedRegs;
1720     if (UnusedRegs != REG_NONE &&
1721         (GetRegInfo (D->Code, D->OpIndex+1, UnusedRegs) & UnusedRegs) != 0) {
1722         /* Cannot optimize */
1723         return 0;
1724     }
1725     if ((D->OptFunc->Flags & OP_A_KNOWN) != 0 &&
1726         RegValIsUnknown (D->OpEntry->RI->In.RegA)) {
1727         /* Cannot optimize */
1728         return 0;
1729     }
1730     if ((D->OptFunc->Flags & OP_X_ZERO) != 0 &&
1731         D->OpEntry->RI->In.RegX != 0) {
1732         /* Cannot optimize */
1733         return 0;
1734     }
1735     if ((D->OptFunc->Flags & OP_LHS_LOAD) != 0) {
1736         if (D->Lhs.A.LoadIndex < 0 || D->Lhs.X.LoadIndex < 0) {
1737             /* Cannot optimize */
1738             return 0;
1739         } else if ((D->OptFunc->Flags & OP_LHS_LOAD_DIRECT) != 0) {
1740             if ((D->Lhs.A.Flags & D->Lhs.X.Flags & LI_DIRECT) == 0) {
1741                 /* Cannot optimize */
1742                 return 0;
1743             }
1744         }
1745     }
1746     if ((D->OptFunc->Flags & OP_RHS_LOAD) != 0) {
1747         if (D->Rhs.A.LoadIndex < 0 || D->Rhs.X.LoadIndex < 0) {
1748             /* Cannot optimize */
1749             return 0;
1750         } else if ((D->OptFunc->Flags & OP_RHS_LOAD_DIRECT) != 0) {
1751             if ((D->Rhs.A.Flags & D->Rhs.X.Flags & LI_DIRECT) == 0) {
1752                 /* Cannot optimize */
1753                 return 0;
1754             }
1755         }
1756     }
1757
1758     /* Determine the zero page locations to use */
1759     if ((D->UsedRegs & REG_PTR1) == REG_NONE) {
1760         D->ZPLo = "ptr1";
1761         D->ZPHi = "ptr1+1";
1762     } else if ((D->UsedRegs & REG_SREG) == REG_NONE) {
1763         D->ZPLo = "sreg";
1764         D->ZPHi = "sreg+1";
1765     } else if ((D->UsedRegs & REG_PTR2) == REG_NONE) {
1766         D->ZPLo = "ptr2";
1767         D->ZPHi = "ptr2+1";
1768     } else {
1769         /* No registers available */
1770         return 0;
1771     }
1772
1773     /* Determine if we have a basic block */
1774     return CS_IsBasicBlock (D->Code, D->PushIndex, D->OpIndex);
1775 }
1776
1777
1778
1779 /*****************************************************************************/
1780 /*                                   Code                                    */
1781 /*****************************************************************************/
1782
1783
1784
1785 unsigned OptStackOps (CodeSeg* S)
1786 /* Optimize operations that take operands via the stack */
1787 {
1788     unsigned            Changes = 0;    /* Number of changes in one run */
1789     StackOpData         Data;
1790     unsigned            I;
1791
1792     enum {
1793         Initialize,
1794         Search,
1795         FoundPush,
1796         FoundOp
1797     } State = Initialize;
1798
1799
1800     /* Generate register info */
1801     CS_GenRegInfo (S);
1802
1803     /* Remember the code segment in the info struct */
1804     Data.Code = S;
1805
1806     /* Look for a call to pushax followed by a call to some other function
1807      * that takes it's first argument on the stack, and the second argument
1808      * in the primary register.
1809      * It depends on the code between the two if we can handle/transform the
1810      * sequence, so check this code for the following list of things:
1811      *
1812      *  - the range must be a basic block (one entry, one exit)
1813      *  - there may not be accesses to local variables with unknown
1814      *    offsets (because we have to adjust these offsets).
1815      *  - no subroutine calls
1816      *  - no jump labels
1817      *
1818      * Since we need a zero page register later, do also check the
1819      * intermediate code for zero page use.
1820      */
1821     I = 0;
1822     while (I < CS_GetEntryCount (S)) {
1823
1824         /* Get the next entry */
1825         CodeEntry* E = CS_GetEntry (S, I);
1826
1827         /* Actions depend on state */
1828         switch (State) {
1829
1830             case Initialize:
1831                 ResetStackOpData (&Data);
1832                 State = Search;
1833                 /* FALLTHROUGH */
1834
1835             case Search:
1836                 /* While searching, track register load insns, so we can tell
1837                  * what is in a register once pushax is encountered.
1838                  */
1839                 if (CE_IsCallTo (E, "pushax")) {
1840                     Data.PushIndex = I;
1841                     State = FoundPush;
1842                 } else {
1843                     /* Track load insns */
1844                     TrackLoads (&Data.Lhs, E, I);
1845                 }
1846                 break;
1847
1848             case FoundPush:
1849                 /* We' found a pushax before. Search for a stack op that may
1850                  * follow and in the meantime, track zeropage usage and check
1851                  * for code that will disable us from translating the sequence.
1852                  */
1853                 if (E->OPC == OP65_JSR) {
1854
1855                     /* Subroutine call: Check if this is one of the functions,
1856                      * we're going to replace.
1857                      */
1858                     Data.OptFunc = FindFunc (E->Arg);
1859                     if (Data.OptFunc) {
1860                         /* Remember the op index and go on */
1861                         Data.OpIndex = I;
1862                         Data.OpEntry = E;
1863                         State = FoundOp;
1864                         break;
1865                     } else if (!HarmlessCall (E->Arg)) {
1866                         /* A call to an unkown subroutine: We need to start
1867                          * over after the last pushax. Note: This will also
1868                          * happen if we encounter a call to pushax!
1869                          */
1870                         I = Data.PushIndex;
1871                         State = Initialize;
1872                         break;
1873                     } else {
1874                         /* Track register usage */
1875                         Data.UsedRegs |= (E->Use | E->Chg);
1876                         TrackLoads (&Data.Rhs, E, I);
1877                     }
1878
1879                 } else if (E->Info & OF_STORE && (E->Chg & REG_ZP) == 0) {
1880
1881                     /* Too dangerous - there may be a change of a variable
1882                      * within the sequence.
1883                      */
1884                     I = Data.PushIndex;
1885                     State = Initialize;
1886                     break;
1887
1888                 } else if ((E->Use & REG_SP) != 0                       &&
1889                            (E->AM != AM65_ZP_INDY               ||
1890                             RegValIsUnknown (E->RI->In.RegY)    ||
1891                             E->RI->In.RegY < 2)) {
1892
1893                     /* If we are using the stack, and we don't have "indirect Y"
1894                      * addressing mode, or the value of Y is unknown, or less
1895                      * than two, we cannot cope with this piece of code. Having
1896                      * an unknown value of Y means that we cannot correct the
1897                      * stack offset, while having an offset less than two means
1898                      * that the code works with the value on stack which is to
1899                      * be removed.
1900                      */
1901                     I = Data.PushIndex;
1902                     State = Initialize;
1903                     break;
1904
1905                 } else {
1906                     /* Other stuff: Track register usage */
1907                     Data.UsedRegs |= (E->Use | E->Chg);
1908                     TrackLoads (&Data.Rhs, E, I);
1909                 }
1910                 break;
1911
1912             case FoundOp:
1913                 /* Track zero page location usage beyond this point */
1914                 Data.UsedRegs |= GetRegInfo (S, I, REG_SREG | REG_PTR1 | REG_PTR2);
1915
1916                 /* Finalize the load info */
1917                 FinalizeLoadInfo (&Data.Lhs, S);
1918                 FinalizeLoadInfo (&Data.Rhs, S);
1919
1920                 /* Set flags for direct operations */
1921                 CheckDirectOp (&Data);
1922
1923                 /* If the Lhs loads do load from zeropage, we have to include
1924                  * them into UsedRegs registers used. The Rhs loads have already
1925                  * been tracked.
1926                  */
1927                 if (Data.Lhs.A.LoadEntry && Data.Lhs.A.LoadEntry->AM == AM65_ZP) {
1928                     Data.UsedRegs |= Data.Lhs.A.LoadEntry->Use;
1929                 }
1930                 if (Data.Lhs.X.LoadEntry && Data.Lhs.X.LoadEntry->AM == AM65_ZP) {
1931                     Data.UsedRegs |= Data.Lhs.X.LoadEntry->Use;
1932                 }
1933
1934                 /* Check the preconditions. If they aren't ok, reset the insn
1935                  * pointer to the pushax and start over. We will loose part of
1936                  * load tracking but at least a/x has probably lost between
1937                  * pushax and here and will be tracked again when restarting.
1938                  */
1939                 if (!PreCondOk (&Data)) {
1940                     I = Data.PushIndex;
1941                     State = Initialize;
1942                     break;
1943                 }
1944
1945                 /* Adjust stack offsets to account for the upcoming removal */
1946                 AdjustStackOffset (&Data, 2);
1947
1948                 /* Regenerate register info, since AdjustStackOffset changed
1949                  * the code
1950                  */
1951                 CS_GenRegInfo (S);
1952
1953                 /* Prepare the remainder of the data structure. */
1954                 Data.PrevEntry = CS_GetPrevEntry (S, Data.PushIndex);
1955                 Data.PushEntry = CS_GetEntry (S, Data.PushIndex);
1956                 Data.OpEntry   = CS_GetEntry (S, Data.OpIndex);
1957                 Data.NextEntry = CS_GetNextEntry (S, Data.OpIndex);
1958
1959                 /* Call the optimizer function */
1960                 Changes += Data.OptFunc->Func (&Data);
1961
1962                 /* Regenerate register info */
1963                 CS_GenRegInfo (S);
1964
1965                 /* Done */
1966                 State = Initialize;
1967                 break;
1968
1969         }
1970
1971         /* Next entry */
1972         ++I;
1973
1974     }
1975
1976     /* Free the register info */
1977     CS_FreeRegInfo (S);
1978
1979     /* Return the number of changes made */
1980     return Changes;
1981 }
1982
1983
1984