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