]> git.sur5r.net Git - cc65/blob - src/cc65/coptstop.c
Adjusted to the current multiline-comment style.
[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-2019, 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            ZPUsage;
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         D->Lhs.X.Flags |= LI_REMOVE;
770         D->Lhs.A.Flags |= LI_REMOVE;
771
772     } else if ((D->Rhs.A.Flags & (LI_DIRECT | LI_RELOAD_Y)) == LI_DIRECT &&
773                (D->Rhs.X.Flags & (LI_DIRECT | LI_RELOAD_Y)) == LI_DIRECT) {
774
775         CodeEntry* LoadX = D->Rhs.X.LoadEntry;
776         CodeEntry* LoadA = D->Rhs.A.LoadEntry;
777
778         D->IP = D->OpIndex+1;
779
780         /* cpx */
781         X = NewCodeEntry (OP65_CPX, LoadX->AM, LoadX->Arg, 0, D->OpEntry->LI);
782         InsertEntry (D, X, D->IP++);
783
784         /* bne L */
785         X = NewCodeEntry (OP65_BNE, AM65_BRA, L->Name, L, D->OpEntry->LI);
786         InsertEntry (D, X, D->IP++);
787
788         /* cmp */
789         X = NewCodeEntry (OP65_CMP, LoadA->AM, LoadA->Arg, 0, D->OpEntry->LI);
790         InsertEntry (D, X, D->IP++);
791
792         /* Rhs load entries can be removed */
793         D->Rhs.X.Flags |= LI_REMOVE;
794         D->Rhs.A.Flags |= LI_REMOVE;
795
796     } else if ((D->Rhs.A.Flags & LI_DIRECT) != 0 &&
797                (D->Rhs.X.Flags & LI_DIRECT) != 0) {
798
799         D->IP = D->OpIndex+1;
800
801         /* Add operand for low byte */
802         AddOpLow (D, OP65_CMP, &D->Rhs);
803
804         /* bne L */
805         X = NewCodeEntry (OP65_BNE, AM65_BRA, L->Name, L, D->OpEntry->LI);
806         InsertEntry (D, X, D->IP++);
807
808         /* Add operand for high byte */
809         AddOpHigh (D, OP65_CMP, &D->Rhs, 0);
810
811     } else {
812
813         /* Save lhs into zeropage, then compare */
814         AddStoreX (D);
815         AddStoreA (D);
816
817         D->IP = D->OpIndex+1;
818
819         /* cpx */
820         X = NewCodeEntry (OP65_CPX, AM65_ZP, D->ZPHi, 0, D->OpEntry->LI);
821         InsertEntry (D, X, D->IP++);
822
823         /* bne L */
824         X = NewCodeEntry (OP65_BNE, AM65_BRA, L->Name, L, D->OpEntry->LI);
825         InsertEntry (D, X, D->IP++);
826
827         /* cmp */
828         X = NewCodeEntry (OP65_CMP, AM65_ZP, D->ZPLo, 0, D->OpEntry->LI);
829         InsertEntry (D, X, D->IP++);
830
831     }
832
833     /* Remove the push and the call to the tosgeax function */
834     RemoveRemainders (D);
835
836     /* We changed the sequence */
837     return 1;
838 }
839
840
841
842 static unsigned Opt_tosshift (StackOpData* D, const char* Name)
843 /* Optimize shift sequences. */
844 {
845     CodeEntry*  X;
846
847     /* Store the value into the zeropage instead of pushing it */
848     ReplacePushByStore (D);
849
850     /* If the lhs is direct (but not stack relative), we can just reload the
851     ** data later.
852     */
853     if ((D->Lhs.A.Flags & (LI_DIRECT | LI_RELOAD_Y)) == LI_DIRECT &&
854         (D->Lhs.X.Flags & (LI_DIRECT | LI_RELOAD_Y)) == LI_DIRECT) {
855
856         CodeEntry* LoadX = D->Lhs.X.LoadEntry;
857         CodeEntry* LoadA = D->Lhs.A.LoadEntry;
858
859         /* Inline the shift */
860         D->IP = D->OpIndex+1;
861
862         /* tay */
863         X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, D->OpEntry->LI);
864         InsertEntry (D, X, D->IP++);
865
866         /* lda */
867         X = NewCodeEntry (OP65_LDA, LoadA->AM, LoadA->Arg, 0, D->OpEntry->LI);
868         InsertEntry (D, X, D->IP++);
869
870         /* ldx */
871         X = NewCodeEntry (OP65_LDX, LoadX->AM, LoadX->Arg, 0, D->OpEntry->LI);
872         InsertEntry (D, X, D->IP++);
873
874         /* Lhs load entries can be removed */
875         D->Lhs.X.Flags |= LI_REMOVE;
876         D->Lhs.A.Flags |= LI_REMOVE;
877
878     } else {
879
880         /* Save lhs into zeropage and reload later */
881         AddStoreX (D);
882         AddStoreA (D);
883
884         /* Be sure to setup IP after adding the stores, otherwise it will get
885         ** messed up.
886         */
887         D->IP = D->OpIndex+1;
888
889         /* tay */
890         X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, D->OpEntry->LI);
891         InsertEntry (D, X, D->IP++);
892
893         /* lda zp */
894         X = NewCodeEntry (OP65_LDA, AM65_ZP, D->ZPLo, 0, D->OpEntry->LI);
895         InsertEntry (D, X, D->IP++);
896
897         /* ldx zp+1 */
898         X = NewCodeEntry (OP65_LDX, AM65_ZP, D->ZPHi, 0, D->OpEntry->LI);
899         InsertEntry (D, X, D->IP++);
900
901     }
902
903     /* jsr shlaxy/aslaxy/whatever */
904     X = NewCodeEntry (OP65_JSR, AM65_ABS, Name, 0, D->OpEntry->LI);
905     InsertEntry (D, X, D->IP++);
906
907     /* Remove the push and the call to the shift function */
908     RemoveRemainders (D);
909
910     /* We changed the sequence */
911     return 1;
912 }
913
914
915
916 static unsigned Opt___bzero (StackOpData* D)
917 /* Optimize the __bzero sequence */
918 {
919     CodeEntry*  X;
920     const char* Arg;
921     CodeLabel*  L;
922
923     /* Check if we're using a register variable */
924     if (!IsRegVar (D)) {
925         /* Store the value into the zeropage instead of pushing it */
926         AddStoreX (D);
927         AddStoreA (D);
928     }
929
930     /* If the return value of __bzero is used, we have to add code to reload
931     ** a/x from the pointer variable.
932     */
933     if (RegAXUsed (D->Code, D->OpIndex+1)) {
934         X = NewCodeEntry (OP65_LDA, AM65_ZP, D->ZPLo, 0, D->OpEntry->LI);
935         InsertEntry (D, X, D->OpIndex+1);
936         X = NewCodeEntry (OP65_LDX, AM65_ZP, D->ZPHi, 0, D->OpEntry->LI);
937         InsertEntry (D, X, D->OpIndex+2);
938     }
939
940     /* X is always zero, A contains the size of the data area to zero.
941     ** Note: A may be zero, in which case the operation is null op.
942     */
943     if (D->OpEntry->RI->In.RegA != 0) {
944
945         /* lda #$00 */
946         X = NewCodeEntry (OP65_LDA, AM65_IMM, "$00", 0, D->OpEntry->LI);
947         InsertEntry (D, X, D->OpIndex+1);
948
949         /* The value of A is known */
950         if (D->OpEntry->RI->In.RegA <= 0x81) {
951
952             /* Loop using the sign bit */
953
954             /* ldy #count-1 */
955             Arg = MakeHexArg (D->OpEntry->RI->In.RegA - 1);
956             X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, D->OpEntry->LI);
957             InsertEntry (D, X, D->OpIndex+2);
958
959             /* L: sta (zp),y */
960             X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, D->ZPLo, 0, D->OpEntry->LI);
961             InsertEntry (D, X, D->OpIndex+3);
962             L = CS_GenLabel (D->Code, X);
963
964             /* dey */
965             X = NewCodeEntry (OP65_DEY, AM65_IMP, 0, 0, D->OpEntry->LI);
966             InsertEntry (D, X, D->OpIndex+4);
967
968             /* bpl L */
969             X = NewCodeEntry (OP65_BPL, AM65_BRA, L->Name, L, D->OpEntry->LI);
970             InsertEntry (D, X, D->OpIndex+5);
971
972         } else {
973
974             /* Loop using an explicit compare */
975
976             /* ldy #$00 */
977             X = NewCodeEntry (OP65_LDY, AM65_IMM, "$00", 0, D->OpEntry->LI);
978             InsertEntry (D, X, D->OpIndex+2);
979
980             /* L: sta (zp),y */
981             X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, D->ZPLo, 0, D->OpEntry->LI);
982             InsertEntry (D, X, D->OpIndex+3);
983             L = CS_GenLabel (D->Code, X);
984
985             /* iny */
986             X = NewCodeEntry (OP65_INY, AM65_IMP, 0, 0, D->OpEntry->LI);
987             InsertEntry (D, X, D->OpIndex+4);
988
989             /* cpy #count */
990             Arg = MakeHexArg (D->OpEntry->RI->In.RegA);
991             X = NewCodeEntry (OP65_CPY, AM65_IMM, Arg, 0, D->OpEntry->LI);
992             InsertEntry (D, X, D->OpIndex+5);
993
994             /* bne L */
995             X = NewCodeEntry (OP65_BNE, AM65_BRA, L->Name, L, D->OpEntry->LI);
996             InsertEntry (D, X, D->OpIndex+6);
997         }
998
999     }
1000
1001     /* Remove the push and the call to the __bzero function */
1002     RemoveRemainders (D);
1003
1004     /* We changed the sequence */
1005     return 1;
1006 }
1007
1008
1009
1010 static unsigned Opt_staspidx (StackOpData* D)
1011 /* Optimize the staspidx sequence */
1012 {
1013     CodeEntry* X;
1014
1015     /* Check if we're using a register variable */
1016     if (!IsRegVar (D)) {
1017         /* Store the value into the zeropage instead of pushing it */
1018         AddStoreX (D);
1019         AddStoreA (D);
1020     }
1021
1022     /* Replace the store subroutine call by a direct op */
1023     X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, D->ZPLo, 0, D->OpEntry->LI);
1024     InsertEntry (D, X, D->OpIndex+1);
1025
1026     /* Remove the push and the call to the staspidx function */
1027     RemoveRemainders (D);
1028
1029     /* We changed the sequence */
1030     return 1;
1031 }
1032
1033
1034
1035 static unsigned Opt_staxspidx (StackOpData* D)
1036 /* Optimize the staxspidx sequence */
1037 {
1038     CodeEntry* X;
1039
1040     /* Check if we're using a register variable */
1041     if (!IsRegVar (D)) {
1042         /* Store the value into the zeropage instead of pushing it */
1043         AddStoreX (D);
1044         AddStoreA (D);
1045     }
1046
1047     /* Inline the store */
1048
1049     /* sta (zp),y */
1050     X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, D->ZPLo, 0, D->OpEntry->LI);
1051     InsertEntry (D, X, D->OpIndex+1);
1052
1053     if (RegValIsKnown (D->OpEntry->RI->In.RegY)) {
1054         /* Value of Y is known */
1055         const char* Arg = MakeHexArg (D->OpEntry->RI->In.RegY + 1);
1056         X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, D->OpEntry->LI);
1057     } else {
1058         X = NewCodeEntry (OP65_INY, AM65_IMP, 0, 0, D->OpEntry->LI);
1059     }
1060     InsertEntry (D, X, D->OpIndex+2);
1061
1062     if (RegValIsKnown (D->OpEntry->RI->In.RegX)) {
1063         /* Value of X is known */
1064         const char* Arg = MakeHexArg (D->OpEntry->RI->In.RegX);
1065         X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, D->OpEntry->LI);
1066     } else {
1067         /* Value unknown */
1068         X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, D->OpEntry->LI);
1069     }
1070     InsertEntry (D, X, D->OpIndex+3);
1071
1072     /* sta (zp),y */
1073     X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, D->ZPLo, 0, D->OpEntry->LI);
1074     InsertEntry (D, X, D->OpIndex+4);
1075
1076     /* If we remove staxspidx, we must restore the Y register to what the
1077     ** function would return.
1078     */
1079     X = NewCodeEntry (OP65_LDY, AM65_IMM, "$00", 0, D->OpEntry->LI);
1080     InsertEntry (D, X, D->OpIndex+5);
1081
1082     /* Remove the push and the call to the staxspidx function */
1083     RemoveRemainders (D);
1084
1085     /* We changed the sequence */
1086     return 1;
1087 }
1088
1089
1090
1091 static unsigned Opt_tosaddax (StackOpData* D)
1092 /* Optimize the tosaddax sequence */
1093 {
1094     CodeEntry*  X;
1095     CodeEntry*  N;
1096
1097     /* We need the entry behind the add */
1098     CHECK (D->NextEntry != 0);
1099
1100     /* Check if the X register is known and zero when the add is done, and
1101     ** if the add is followed by
1102     **
1103     **  ldy     #$00
1104     **  jsr     ldauidx         ; or ldaidx
1105     **
1106     ** If this is true, the addition does actually add an offset to a pointer
1107     ** before it is dereferenced. Since both subroutines take an offset in Y,
1108     ** we can pass the offset (instead of #$00) and remove the addition
1109     ** alltogether.
1110     */
1111     if (D->OpEntry->RI->In.RegX == 0                            &&
1112         D->NextEntry->OPC == OP65_LDY                           &&
1113         CE_IsKnownImm (D->NextEntry, 0)                         &&
1114         !CE_HasLabel (D->NextEntry)                             &&
1115         (N = CS_GetNextEntry (D->Code, D->OpIndex + 1)) != 0    &&
1116         (CE_IsCallTo (N, "ldauidx")                     ||
1117          CE_IsCallTo (N, "ldaidx"))) {
1118
1119         int Signed = (strcmp (N->Arg, "ldaidx") == 0);
1120
1121         /* Store the value into the zeropage instead of pushing it */
1122         AddStoreX (D);
1123         AddStoreA (D);
1124
1125         /* Replace the ldy by a tay. Be sure to create the new entry before
1126         ** deleting the ldy, since we will reference the line info from this
1127         ** insn.
1128         */
1129         X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, D->NextEntry->LI);
1130         DelEntry (D, D->OpIndex + 1);
1131         InsertEntry (D, X, D->OpIndex + 1);
1132
1133         /* Replace the call to ldaidx/ldauidx. Since X is already zero, and
1134         ** the ptr is in the zero page location, we just need to load from
1135         ** the pointer, and fix X in case of ldaidx.
1136         */
1137         X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, D->ZPLo, 0, N->LI);
1138         DelEntry (D, D->OpIndex + 2);
1139         InsertEntry (D, X, D->OpIndex + 2);
1140         if (Signed) {
1141
1142             CodeLabel* L;
1143
1144             /* Add sign extension - N is unused now */
1145             N = CS_GetNextEntry (D->Code, D->OpIndex + 2);
1146             CHECK (N != 0);
1147             L = CS_GenLabel (D->Code, N);
1148
1149             X = NewCodeEntry (OP65_BPL, AM65_BRA, L->Name, L, X->LI);
1150             InsertEntry (D, X, D->OpIndex + 3);
1151
1152             X = NewCodeEntry (OP65_DEX, AM65_IMP, 0, 0, X->LI);
1153             InsertEntry (D, X, D->OpIndex + 4);
1154         }
1155
1156     } else {
1157
1158         /* Store the value into the zeropage instead of pushing it */
1159         ReplacePushByStore (D);
1160
1161         /* Inline the add */
1162         D->IP = D->OpIndex+1;
1163
1164         /* clc */
1165         X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, D->OpEntry->LI);
1166         InsertEntry (D, X, D->IP++);
1167
1168         /* Low byte */
1169         AddOpLow (D, OP65_ADC, &D->Lhs);
1170
1171         /* High byte */
1172         if (D->PushEntry->RI->In.RegX == 0) {
1173
1174             /* The high byte is the value in X plus the carry */
1175             CodeLabel* L = CS_GenLabel (D->Code, D->NextEntry);
1176
1177             /* bcc L */
1178             X = NewCodeEntry (OP65_BCC, AM65_BRA, L->Name, L, D->OpEntry->LI);
1179             InsertEntry (D, X, D->IP++);
1180
1181             /* inx */
1182             X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, D->OpEntry->LI);
1183             InsertEntry (D, X, D->IP++);
1184
1185         } else if (D->OpEntry->RI->In.RegX == 0                         &&
1186                    (RegValIsKnown (D->PushEntry->RI->In.RegX)   ||
1187                     (D->Lhs.X.Flags & LI_RELOAD_Y) == 0)) {
1188
1189             /* The high byte is that of the first operand plus carry */
1190             CodeLabel* L;
1191             if (RegValIsKnown (D->PushEntry->RI->In.RegX)) {
1192                 /* Value of first op high byte is known */
1193                 const char* Arg = MakeHexArg (D->PushEntry->RI->In.RegX);
1194                 X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, D->OpEntry->LI);
1195             } else {
1196                 /* Value of first op high byte is unknown. Load from ZP or
1197                 ** original storage.
1198                 */
1199                 if (D->Lhs.X.Flags & LI_DIRECT) {
1200                     CodeEntry* LoadX = D->Lhs.X.LoadEntry;
1201                     X = NewCodeEntry (OP65_LDX, LoadX->AM, LoadX->Arg, 0, D->OpEntry->LI);
1202                 } else {
1203                     X = NewCodeEntry (OP65_LDX, AM65_ZP, D->ZPHi, 0, D->OpEntry->LI);
1204                 }
1205             }
1206             InsertEntry (D, X, D->IP++);
1207
1208             /* bcc label */
1209             L = CS_GenLabel (D->Code, D->NextEntry);
1210             X = NewCodeEntry (OP65_BCC, AM65_BRA, L->Name, L, D->OpEntry->LI);
1211             InsertEntry (D, X, D->IP++);
1212
1213             /* inx */
1214             X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, D->OpEntry->LI);
1215             InsertEntry (D, X, D->IP++);
1216         } else {
1217             /* High byte is unknown */
1218             AddOpHigh (D, OP65_ADC, &D->Lhs, 1);
1219         }
1220     }
1221
1222     /* Remove the push and the call to the tosaddax function */
1223     RemoveRemainders (D);
1224
1225     /* We changed the sequence */
1226     return 1;
1227 }
1228
1229
1230
1231 static unsigned Opt_tosandax (StackOpData* D)
1232 /* Optimize the tosandax sequence */
1233 {
1234     /* Store the value into the zeropage instead of pushing it */
1235     ReplacePushByStore (D);
1236
1237     /* Inline the and, low byte */
1238     D->IP = D->OpIndex + 1;
1239     AddOpLow (D, OP65_AND, &D->Lhs);
1240
1241     /* High byte */
1242     AddOpHigh (D, OP65_AND, &D->Lhs, 1);
1243
1244     /* Remove the push and the call to the tosandax function */
1245     RemoveRemainders (D);
1246
1247     /* We changed the sequence */
1248     return 1;
1249 }
1250
1251
1252
1253 static unsigned Opt_tosaslax (StackOpData* D)
1254 /* Optimize the tosaslax sequence */
1255 {
1256     return Opt_tosshift (D, "aslaxy");
1257 }
1258
1259
1260
1261 static unsigned Opt_tosasrax (StackOpData* D)
1262 /* Optimize the tosasrax sequence */
1263 {
1264     return Opt_tosshift (D, "asraxy");
1265 }
1266
1267
1268
1269 static unsigned Opt_toseqax (StackOpData* D)
1270 /* Optimize the toseqax sequence */
1271 {
1272     return Opt_toseqax_tosneax (D, "booleq");
1273 }
1274
1275
1276
1277 static unsigned Opt_tosgeax (StackOpData* D)
1278 /* Optimize the tosgeax sequence */
1279 {
1280     CodeEntry*  X;
1281     CodeLabel* L;
1282
1283     /* Inline the sbc */
1284     D->IP = D->OpIndex+1;
1285
1286     /* Must be true because of OP_RHS_LOAD */
1287     CHECK ((D->Rhs.A.Flags & D->Rhs.X.Flags & LI_DIRECT) != 0);
1288
1289     /* Add code for low operand */
1290     AddOpLow (D, OP65_CMP, &D->Rhs);
1291
1292     /* Add code for high operand */
1293     AddOpHigh (D, OP65_SBC, &D->Rhs, 0);
1294
1295     /* eor #$80 */
1296     X = NewCodeEntry (OP65_EOR, AM65_IMM, "$80", 0, D->OpEntry->LI);
1297     InsertEntry (D, X, D->IP++);
1298
1299     /* asl a */
1300     X = NewCodeEntry (OP65_ASL, AM65_ACC, "a", 0, D->OpEntry->LI);
1301     InsertEntry (D, X, D->IP++);
1302     L = CS_GenLabel (D->Code, X);
1303
1304     /* Insert a bvs L before the eor insn */
1305     X = NewCodeEntry (OP65_BVS, AM65_BRA, L->Name, L, D->OpEntry->LI);
1306     InsertEntry (D, X, D->IP - 2);
1307     ++D->IP;
1308
1309     /* lda #$00 */
1310     X = NewCodeEntry (OP65_LDA, AM65_IMM, "$00", 0, D->OpEntry->LI);
1311     InsertEntry (D, X, D->IP++);
1312
1313     /* ldx #$00 */
1314     X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, D->OpEntry->LI);
1315     InsertEntry (D, X, D->IP++);
1316
1317     /* rol a */
1318     X = NewCodeEntry (OP65_ROL, AM65_ACC, "a", 0, D->OpEntry->LI);
1319     InsertEntry (D, X, D->IP++);
1320
1321     /* Remove the push and the call to the tosgeax function */
1322     RemoveRemainders (D);
1323
1324     /* We changed the sequence */
1325     return 1;
1326 }
1327
1328
1329
1330 static unsigned Opt_tosltax (StackOpData* D)
1331 /* Optimize the tosltax sequence */
1332 {
1333     CodeEntry*  X;
1334     CodeLabel* L;
1335
1336
1337     /* Inline the compare */
1338     D->IP = D->OpIndex+1;
1339
1340     /* Must be true because of OP_RHS_LOAD */
1341     CHECK ((D->Rhs.A.Flags & D->Rhs.X.Flags & LI_DIRECT) != 0);
1342
1343     /* Add code for low operand */
1344     AddOpLow (D, OP65_CMP, &D->Rhs);
1345
1346     /* Add code for high operand */
1347     AddOpHigh (D, OP65_SBC, &D->Rhs, 0);
1348
1349     /* eor #$80 */
1350     X = NewCodeEntry (OP65_EOR, AM65_IMM, "$80", 0, D->OpEntry->LI);
1351     InsertEntry (D, X, D->IP++);
1352
1353     /* asl a */
1354     X = NewCodeEntry (OP65_ASL, AM65_ACC, "a", 0, D->OpEntry->LI);
1355     InsertEntry (D, X, D->IP++);
1356     L = CS_GenLabel (D->Code, X);
1357
1358     /* Insert a bvc L before the eor insn */
1359     X = NewCodeEntry (OP65_BVC, AM65_BRA, L->Name, L, D->OpEntry->LI);
1360     InsertEntry (D, X, D->IP - 2);
1361     ++D->IP;
1362
1363     /* lda #$00 */
1364     X = NewCodeEntry (OP65_LDA, AM65_IMM, "$00", 0, D->OpEntry->LI);
1365     InsertEntry (D, X, D->IP++);
1366
1367     /* ldx #$00 */
1368     X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, D->OpEntry->LI);
1369     InsertEntry (D, X, D->IP++);
1370
1371     /* rol a */
1372     X = NewCodeEntry (OP65_ROL, AM65_ACC, "a", 0, D->OpEntry->LI);
1373     InsertEntry (D, X, D->IP++);
1374
1375     /* Remove the push and the call to the tosltax function */
1376     RemoveRemainders (D);
1377
1378     /* We changed the sequence */
1379     return 1;
1380 }
1381
1382
1383
1384 static unsigned Opt_tosneax (StackOpData* D)
1385 /* Optimize the tosneax sequence */
1386 {
1387     return Opt_toseqax_tosneax (D, "boolne");
1388 }
1389
1390
1391
1392 static unsigned Opt_tosorax (StackOpData* D)
1393 /* Optimize the tosorax sequence */
1394 {
1395     /* Store the value into the zeropage instead of pushing it */
1396     ReplacePushByStore (D);
1397
1398     /* Inline the or, low byte */
1399     D->IP = D->OpIndex + 1;
1400     AddOpLow (D, OP65_ORA, &D->Lhs);
1401
1402     /* High byte */
1403     AddOpHigh (D, OP65_ORA, &D->Lhs, 1);
1404
1405     /* Remove the push and the call to the tosorax function */
1406     RemoveRemainders (D);
1407
1408     /* We changed the sequence */
1409     return 1;
1410 }
1411
1412
1413
1414 static unsigned Opt_tosshlax (StackOpData* D)
1415 /* Optimize the tosshlax sequence */
1416 {
1417     return Opt_tosshift (D, "shlaxy");
1418 }
1419
1420
1421
1422 static unsigned Opt_tosshrax (StackOpData* D)
1423 /* Optimize the tosshrax sequence */
1424 {
1425     return Opt_tosshift (D, "shraxy");
1426 }
1427
1428
1429
1430 static unsigned Opt_tossubax (StackOpData* D)
1431 /* Optimize the tossubax sequence. Note: subtraction is not commutative! */
1432 {
1433     CodeEntry*  X;
1434
1435
1436     /* Inline the sbc */
1437     D->IP = D->OpIndex+1;
1438
1439     /* sec */
1440     X = NewCodeEntry (OP65_SEC, AM65_IMP, 0, 0, D->OpEntry->LI);
1441     InsertEntry (D, X, D->IP++);
1442
1443     /* Must be true because of OP_RHS_LOAD */
1444     CHECK ((D->Rhs.A.Flags & D->Rhs.X.Flags & LI_DIRECT) != 0);
1445
1446     /* Add code for low operand */
1447     AddOpLow (D, OP65_SBC, &D->Rhs);
1448
1449     /* Add code for high operand */
1450     AddOpHigh (D, OP65_SBC, &D->Rhs, 1);
1451
1452     /* Remove the push and the call to the tossubax function */
1453     RemoveRemainders (D);
1454
1455     /* We changed the sequence */
1456     return 1;
1457 }
1458
1459
1460
1461 static unsigned Opt_tosugeax (StackOpData* D)
1462 /* Optimize the tosugeax sequence */
1463 {
1464     CodeEntry*  X;
1465
1466
1467     /* Inline the sbc */
1468     D->IP = D->OpIndex+1;
1469
1470     /* Must be true because of OP_RHS_LOAD */
1471     CHECK ((D->Rhs.A.Flags & D->Rhs.X.Flags & LI_DIRECT) != 0);
1472
1473     /* Add code for low operand */
1474     AddOpLow (D, OP65_CMP, &D->Rhs);
1475
1476     /* Add code for high operand */
1477     AddOpHigh (D, OP65_SBC, &D->Rhs, 0);
1478
1479     /* lda #$00 */
1480     X = NewCodeEntry (OP65_LDA, AM65_IMM, "$00", 0, D->OpEntry->LI);
1481     InsertEntry (D, X, D->IP++);
1482
1483     /* ldx #$00 */
1484     X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, D->OpEntry->LI);
1485     InsertEntry (D, X, D->IP++);
1486
1487     /* rol a */
1488     X = NewCodeEntry (OP65_ROL, AM65_ACC, "a", 0, D->OpEntry->LI);
1489     InsertEntry (D, X, D->IP++);
1490
1491     /* Remove the push and the call to the tosugeax function */
1492     RemoveRemainders (D);
1493
1494     /* We changed the sequence */
1495     return 1;
1496 }
1497
1498
1499
1500 static unsigned Opt_tosugtax (StackOpData* D)
1501 /* Optimize the tosugtax sequence */
1502 {
1503     CodeEntry*  X;
1504
1505
1506     /* Inline the sbc */
1507     D->IP = D->OpIndex+1;
1508
1509     /* Must be true because of OP_RHS_LOAD */
1510     CHECK ((D->Rhs.A.Flags & D->Rhs.X.Flags & LI_DIRECT) != 0);
1511
1512     /* sec */
1513     X = NewCodeEntry (OP65_SEC, AM65_IMP, 0, 0, D->OpEntry->LI);
1514     InsertEntry (D, X, D->IP++);
1515
1516     /* Add code for low operand */
1517     AddOpLow (D, OP65_SBC, &D->Rhs);
1518
1519     /* We need the zero flag, so remember the immediate result */
1520     X = NewCodeEntry (OP65_STA, AM65_ZP, "tmp1", 0, D->OpEntry->LI);
1521     InsertEntry (D, X, D->IP++);
1522
1523     /* Add code for high operand */
1524     AddOpHigh (D, OP65_SBC, &D->Rhs, 0);
1525
1526     /* Set Z flag */
1527     X = NewCodeEntry (OP65_ORA, AM65_ZP, "tmp1", 0, D->OpEntry->LI);
1528     InsertEntry (D, X, D->IP++);
1529
1530     /* Transform to boolean */
1531     X = NewCodeEntry (OP65_JSR, AM65_ABS, "boolugt", 0, D->OpEntry->LI);
1532     InsertEntry (D, X, D->IP++);
1533
1534     /* Remove the push and the call to the operator function */
1535     RemoveRemainders (D);
1536
1537     /* We changed the sequence */
1538     return 1;
1539 }
1540
1541
1542
1543 static unsigned Opt_tosuleax (StackOpData* D)
1544 /* Optimize the tosuleax sequence */
1545 {
1546     CodeEntry*  X;
1547
1548
1549     /* Inline the sbc */
1550     D->IP = D->OpIndex+1;
1551
1552     /* Must be true because of OP_RHS_LOAD */
1553     CHECK ((D->Rhs.A.Flags & D->Rhs.X.Flags & LI_DIRECT) != 0);
1554
1555     /* sec */
1556     X = NewCodeEntry (OP65_SEC, AM65_IMP, 0, 0, D->OpEntry->LI);
1557     InsertEntry (D, X, D->IP++);
1558
1559     /* Add code for low operand */
1560     AddOpLow (D, OP65_SBC, &D->Rhs);
1561
1562     /* We need the zero flag, so remember the immediate result */
1563     X = NewCodeEntry (OP65_STA, AM65_ZP, "tmp1", 0, D->OpEntry->LI);
1564     InsertEntry (D, X, D->IP++);
1565
1566     /* Add code for high operand */
1567     AddOpHigh (D, OP65_SBC, &D->Rhs, 0);
1568
1569     /* Set Z flag */
1570     X = NewCodeEntry (OP65_ORA, AM65_ZP, "tmp1", 0, D->OpEntry->LI);
1571     InsertEntry (D, X, D->IP++);
1572
1573     /* Transform to boolean */
1574     X = NewCodeEntry (OP65_JSR, AM65_ABS, "boolule", 0, D->OpEntry->LI);
1575     InsertEntry (D, X, D->IP++);
1576
1577     /* Remove the push and the call to the operator function */
1578     RemoveRemainders (D);
1579
1580     /* We changed the sequence */
1581     return 1;
1582 }
1583
1584
1585
1586 static unsigned Opt_tosultax (StackOpData* D)
1587 /* Optimize the tosultax sequence */
1588 {
1589     CodeEntry*  X;
1590
1591
1592     /* Inline the sbc */
1593     D->IP = D->OpIndex+1;
1594
1595     /* Must be true because of OP_RHS_LOAD */
1596     CHECK ((D->Rhs.A.Flags & D->Rhs.X.Flags & LI_DIRECT) != 0);
1597
1598     /* Add code for low operand */
1599     AddOpLow (D, OP65_CMP, &D->Rhs);
1600
1601     /* Add code for high operand */
1602     AddOpHigh (D, OP65_SBC, &D->Rhs, 0);
1603
1604     /* Transform to boolean */
1605     X = NewCodeEntry (OP65_JSR, AM65_ABS, "boolult", 0, D->OpEntry->LI);
1606     InsertEntry (D, X, D->IP++);
1607
1608     /* Remove the push and the call to the operator function */
1609     RemoveRemainders (D);
1610
1611     /* We changed the sequence */
1612     return 1;
1613 }
1614
1615
1616
1617 static unsigned Opt_tosxorax (StackOpData* D)
1618 /* Optimize the tosxorax sequence */
1619 {
1620     CodeEntry*  X;
1621
1622
1623     /* Store the value into the zeropage instead of pushing it */
1624     ReplacePushByStore (D);
1625
1626     /* Inline the xor, low byte */
1627     D->IP = D->OpIndex + 1;
1628     AddOpLow (D, OP65_EOR, &D->Lhs);
1629
1630     /* High byte */
1631     if (RegValIsKnown (D->PushEntry->RI->In.RegX) &&
1632         RegValIsKnown (D->OpEntry->RI->In.RegX)) {
1633         /* Both values known, precalculate the result */
1634         const char* Arg = MakeHexArg (D->PushEntry->RI->In.RegX ^ D->OpEntry->RI->In.RegX);
1635         X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, D->OpEntry->LI);
1636         InsertEntry (D, X, D->IP++);
1637     } else if (D->PushEntry->RI->In.RegX != 0) {
1638         /* High byte is unknown */
1639         AddOpHigh (D, OP65_EOR, &D->Lhs, 1);
1640     }
1641
1642     /* Remove the push and the call to the tosandax function */
1643     RemoveRemainders (D);
1644
1645     /* We changed the sequence */
1646     return 1;
1647 }
1648
1649
1650
1651 /*****************************************************************************/
1652 /*                                   Code                                    */
1653 /*****************************************************************************/
1654
1655
1656
1657 static const OptFuncDesc FuncTable[] = {
1658     { "__bzero",    Opt___bzero,   REG_NONE, OP_X_ZERO | OP_A_KNOWN     },
1659     { "staspidx",   Opt_staspidx,  REG_NONE, OP_NONE                    },
1660     { "staxspidx",  Opt_staxspidx, REG_AX,   OP_NONE                    },
1661     { "tosaddax",   Opt_tosaddax,  REG_NONE, OP_NONE                    },
1662     { "tosandax",   Opt_tosandax,  REG_NONE, OP_NONE                    },
1663     { "tosaslax",   Opt_tosaslax,  REG_NONE, OP_NONE                    },
1664     { "tosasrax",   Opt_tosasrax,  REG_NONE, OP_NONE                    },
1665     { "toseqax",    Opt_toseqax,   REG_NONE, OP_NONE                    },
1666     { "tosgeax",    Opt_tosgeax,   REG_NONE, OP_RHS_LOAD_DIRECT         },
1667     { "tosltax",    Opt_tosltax,   REG_NONE, OP_RHS_LOAD_DIRECT         },
1668     { "tosneax",    Opt_tosneax,   REG_NONE, OP_NONE                    },
1669     { "tosorax",    Opt_tosorax,   REG_NONE, OP_NONE                    },
1670     { "tosshlax",   Opt_tosshlax,  REG_NONE, OP_NONE                    },
1671     { "tosshrax",   Opt_tosshrax,  REG_NONE, OP_NONE                    },
1672     { "tossubax",   Opt_tossubax,  REG_NONE, OP_RHS_LOAD_DIRECT         },
1673     { "tosugeax",   Opt_tosugeax,  REG_NONE, OP_RHS_LOAD_DIRECT         },
1674     { "tosugtax",   Opt_tosugtax,  REG_NONE, OP_RHS_LOAD_DIRECT         },
1675     { "tosuleax",   Opt_tosuleax,  REG_NONE, OP_RHS_LOAD_DIRECT         },
1676     { "tosultax",   Opt_tosultax,  REG_NONE, OP_RHS_LOAD_DIRECT         },
1677     { "tosxorax",   Opt_tosxorax,  REG_NONE, OP_NONE                    },
1678 };
1679 #define FUNC_COUNT (sizeof(FuncTable) / sizeof(FuncTable[0]))
1680
1681
1682
1683 static int CmpFunc (const void* Key, const void* Func)
1684 /* Compare function for bsearch */
1685 {
1686     return strcmp (Key, ((const OptFuncDesc*) Func)->Name);
1687 }
1688
1689
1690
1691 static const OptFuncDesc* FindFunc (const char* Name)
1692 /* Find the function with the given name. Return a pointer to the table entry
1693 ** or NULL if the function was not found.
1694 */
1695 {
1696     return bsearch (Name, FuncTable, FUNC_COUNT, sizeof(OptFuncDesc), CmpFunc);
1697 }
1698
1699
1700
1701 static int CmpHarmless (const void* Key, const void* Entry)
1702 /* Compare function for bsearch */
1703 {
1704     return strcmp (Key, *(const char**)Entry);
1705 }
1706
1707
1708
1709 static int HarmlessCall (const char* Name)
1710 /* Check if this is a call to a harmless subroutine that will not interrupt
1711 ** the pushax/op sequence when encountered.
1712 */
1713 {
1714     static const char* const Tab[] = {
1715         "aslax1",
1716         "aslax2",
1717         "aslax3",
1718         "aslax4",
1719         "aslaxy",
1720         "asrax1",
1721         "asrax2",
1722         "asrax3",
1723         "asrax4",
1724         "asraxy",
1725         "bnegax",
1726         "complax",
1727         "decax1",
1728         "decax2",
1729         "decax3",
1730         "decax4",
1731         "decax5",
1732         "decax6",
1733         "decax7",
1734         "decax8",
1735         "decaxy",
1736         "incax1",
1737         "incax2",
1738         "incax3",
1739         "incax4",
1740         "incax5",
1741         "incax6",
1742         "incax7",
1743         "incax8",
1744         "incaxy",
1745         "ldaxidx",
1746         "ldaxysp",
1747         "negax",
1748         "shlax1",
1749         "shlax2",
1750         "shlax3",
1751         "shlax4",
1752         "shlaxy",
1753         "shrax1",
1754         "shrax2",
1755         "shrax3",
1756         "shrax4",
1757         "shraxy",
1758     };
1759
1760     void* R = bsearch (Name,
1761                        Tab,
1762                        sizeof (Tab) / sizeof (Tab[0]),
1763                        sizeof (Tab[0]),
1764                        CmpHarmless);
1765     return (R != 0);
1766 }
1767
1768
1769
1770 static void ResetStackOpData (StackOpData* Data)
1771 /* Reset the given data structure */
1772 {
1773     Data->OptFunc       = 0;
1774     Data->ZPUsage       = REG_NONE;
1775
1776     ClearLoadInfo (&Data->Lhs);
1777     ClearLoadInfo (&Data->Rhs);
1778
1779     Data->PushIndex     = -1;
1780     Data->OpIndex       = -1;
1781 }
1782
1783
1784
1785 static int PreCondOk (StackOpData* D)
1786 /* Check if the preconditions for a call to the optimizer subfunction are
1787 ** satisfied. As a side effect, this function will also choose the zero page
1788 ** register to use.
1789 */
1790 {
1791     /* Check the flags */
1792     unsigned UnusedRegs = D->OptFunc->UnusedRegs;
1793     if (UnusedRegs != REG_NONE &&
1794         (GetRegInfo (D->Code, D->OpIndex+1, UnusedRegs) & UnusedRegs) != 0) {
1795         /* Cannot optimize */
1796         return 0;
1797     }
1798     if ((D->OptFunc->Flags & OP_A_KNOWN) != 0 &&
1799         RegValIsUnknown (D->OpEntry->RI->In.RegA)) {
1800         /* Cannot optimize */
1801         return 0;
1802     }
1803     if ((D->OptFunc->Flags & OP_X_ZERO) != 0 &&
1804         D->OpEntry->RI->In.RegX != 0) {
1805         /* Cannot optimize */
1806         return 0;
1807     }
1808     if ((D->OptFunc->Flags & OP_LHS_LOAD) != 0) {
1809         if (D->Lhs.A.LoadIndex < 0 || D->Lhs.X.LoadIndex < 0) {
1810             /* Cannot optimize */
1811             return 0;
1812         } else if ((D->OptFunc->Flags & OP_LHS_LOAD_DIRECT) != 0) {
1813             if ((D->Lhs.A.Flags & D->Lhs.X.Flags & LI_DIRECT) == 0) {
1814                 /* Cannot optimize */
1815                 return 0;
1816             }
1817         }
1818     }
1819     if ((D->OptFunc->Flags & OP_RHS_LOAD) != 0) {
1820         if (D->Rhs.A.LoadIndex < 0 || D->Rhs.X.LoadIndex < 0) {
1821             /* Cannot optimize */
1822             return 0;
1823         } else if ((D->OptFunc->Flags & OP_RHS_LOAD_DIRECT) != 0) {
1824             if ((D->Rhs.A.Flags & D->Rhs.X.Flags & LI_DIRECT) == 0) {
1825                 /* Cannot optimize */
1826                 return 0;
1827             }
1828         }
1829     }
1830     if ((D->Rhs.A.Flags | D->Rhs.X.Flags) & LI_DUP_LOAD) {
1831         /* Cannot optimize */
1832         return 0;
1833     }
1834
1835     /* Determine the zero page locations to use. We've tracked the used
1836     ** ZP locations, so try to find some for us that are unused.
1837     */
1838     if ((D->ZPUsage & REG_PTR1) == REG_NONE) {
1839         D->ZPLo = "ptr1";
1840         D->ZPHi = "ptr1+1";
1841     } else if ((D->ZPUsage & REG_SREG) == REG_NONE) {
1842         D->ZPLo = "sreg";
1843         D->ZPHi = "sreg+1";
1844     } else if ((D->ZPUsage & REG_PTR2) == REG_NONE) {
1845         D->ZPLo = "ptr2";
1846         D->ZPHi = "ptr2+1";
1847     } else {
1848         /* No registers available */
1849         return 0;
1850     }
1851
1852     /* Determine if we have a basic block */
1853     return CS_IsBasicBlock (D->Code, D->PushIndex, D->OpIndex);
1854 }
1855
1856
1857
1858 /*****************************************************************************/
1859 /*                                   Code                                    */
1860 /*****************************************************************************/
1861
1862
1863
1864 unsigned OptStackOps (CodeSeg* S)
1865 /* Optimize operations that take operands via the stack */
1866 {
1867     unsigned            Changes = 0;    /* Number of changes in one run */
1868     StackOpData         Data;
1869     int                 I;
1870     int                 OldEntryCount;  /* Old number of entries */
1871     unsigned            UsedRegs = 0;   /* Registers used */
1872     unsigned            ChangedRegs = 0;/* Registers changed */
1873
1874
1875     enum {
1876         Initialize,
1877         Search,
1878         FoundPush,
1879         FoundOp
1880     } State = Initialize;
1881
1882
1883     /* Remember the code segment in the info struct */
1884     Data.Code = S;
1885
1886     /* Look for a call to pushax followed by a call to some other function
1887     ** that takes it's first argument on the stack, and the second argument
1888     ** in the primary register.
1889     ** It depends on the code between the two if we can handle/transform the
1890     ** sequence, so check this code for the following list of things:
1891     **
1892     **  - the range must be a basic block (one entry, one exit)
1893     **  - there may not be accesses to local variables with unknown
1894     **    offsets (because we have to adjust these offsets).
1895     **  - no subroutine calls
1896     **  - no jump labels
1897     **
1898     ** Since we need a zero page register later, do also check the
1899     ** intermediate code for zero page use.
1900     */
1901     I = 0;
1902     while (I < (int)CS_GetEntryCount (S)) {
1903
1904         /* Get the next entry */
1905         CodeEntry* E = CS_GetEntry (S, I);
1906
1907         /* Actions depend on state */
1908         switch (State) {
1909
1910             case Initialize:
1911                 ResetStackOpData (&Data);
1912                 UsedRegs = ChangedRegs = REG_NONE;
1913                 State = Search;
1914                 /* FALLTHROUGH */
1915
1916             case Search:
1917                 /* While searching, track register load insns, so we can tell
1918                 ** what is in a register once pushax is encountered.
1919                 */
1920                 if (CE_HasLabel (E)) {
1921                     /* Currently we don't track across branches */
1922                     ClearLoadInfo (&Data.Lhs);
1923                 }
1924                 if (CE_IsCallTo (E, "pushax")) {
1925                     Data.PushIndex = I;
1926                     State = FoundPush;
1927                 } else {
1928                     /* Track load insns */
1929                     TrackLoads (&Data.Lhs, E, I);
1930                 }
1931                 break;
1932
1933             case FoundPush:
1934                 /* We' found a pushax before. Search for a stack op that may
1935                 ** follow and in the meantime, track zeropage usage and check
1936                 ** for code that will disable us from translating the sequence.
1937                 */
1938                 if (CE_HasLabel (E)) {
1939                     /* Currently we don't track across branches */
1940                     ClearLoadInfo (&Data.Rhs);
1941                 }
1942                 if (E->OPC == OP65_JSR) {
1943
1944                     /* Subroutine call: Check if this is one of the functions,
1945                     ** we're going to replace.
1946                     */
1947                     Data.OptFunc = FindFunc (E->Arg);
1948                     if (Data.OptFunc) {
1949                         /* Remember the op index and go on */
1950                         Data.OpIndex = I;
1951                         Data.OpEntry = E;
1952                         State = FoundOp;
1953                         break;
1954                     } else if (!HarmlessCall (E->Arg)) {
1955                         /* A call to an unkown subroutine: We need to start
1956                         ** over after the last pushax. Note: This will also
1957                         ** happen if we encounter a call to pushax!
1958                         */
1959                         I = Data.PushIndex;
1960                         State = Initialize;
1961                         break;
1962                     } else {
1963                         /* Track register usage */
1964                         Data.ZPUsage |= (E->Use | E->Chg);
1965                         TrackLoads (&Data.Rhs, E, I);
1966                     }
1967
1968                 } else if (E->Info & OF_STORE && (E->Chg & REG_ZP) == 0) {
1969
1970                     /* Too dangerous - there may be a change of a variable
1971                     ** within the sequence.
1972                     */
1973                     I = Data.PushIndex;
1974                     State = Initialize;
1975                     break;
1976
1977                 } else if ((E->Use & REG_SP) != 0                       &&
1978                            (E->AM != AM65_ZP_INDY               ||
1979                             RegValIsUnknown (E->RI->In.RegY)    ||
1980                             E->RI->In.RegY < 2)) {
1981
1982                     /* If we are using the stack, and we don't have "indirect Y"
1983                     ** addressing mode, or the value of Y is unknown, or less
1984                     ** than two, we cannot cope with this piece of code. Having
1985                     ** an unknown value of Y means that we cannot correct the
1986                     ** stack offset, while having an offset less than two means
1987                     ** that the code works with the value on stack which is to
1988                     ** be removed.
1989                     */
1990                     I = Data.PushIndex;
1991                     State = Initialize;
1992                     break;
1993
1994                 } else {
1995                     /* Other stuff: Track register usage */
1996                     Data.ZPUsage |= (E->Use | E->Chg);
1997                     TrackLoads (&Data.Rhs, E, I);
1998                 }
1999                 /* If the registers from the push (A/X) are used before they're
2000                 ** changed, we cannot change the sequence, because this would
2001                 ** with a high probability change the register contents.
2002                 */
2003                 UsedRegs |= E->Use;
2004                 if ((UsedRegs & ~ChangedRegs) & REG_AX) {
2005                     I = Data.PushIndex;
2006                     State = Initialize;
2007                     break;
2008                 }
2009                 ChangedRegs |= E->Chg;
2010                 break;
2011
2012             case FoundOp:
2013                 /* Track zero page location usage beyond this point */
2014                 Data.ZPUsage |= GetRegInfo (S, I, REG_SREG | REG_PTR1 | REG_PTR2);
2015
2016                 /* Finalize the load info */
2017                 FinalizeLoadInfo (&Data.Lhs, S);
2018                 FinalizeLoadInfo (&Data.Rhs, S);
2019
2020                 /* Check if the lhs loads from zeropage. If this is true, these
2021                 ** zero page locations have to be added to ZPUsage, because
2022                 ** they cannot be used for intermediate storage. In addition,
2023                 ** if one of these zero page locations is destroyed between
2024                 ** pushing the lhs and the actual operation, we cannot use the
2025                 ** original zero page locations for the final op, but must
2026                 ** use another ZP location to save them.
2027                 */
2028                 ChangedRegs &= REG_ZP;
2029                 if (Data.Lhs.A.LoadEntry && Data.Lhs.A.LoadEntry->AM == AM65_ZP) {
2030                     Data.ZPUsage |= Data.Lhs.A.LoadEntry->Use;
2031                     if ((Data.Lhs.A.LoadEntry->Use & ChangedRegs) != 0) {
2032                         Data.Lhs.A.Flags &= ~(LI_DIRECT | LI_RELOAD_Y);
2033                     }
2034                 }
2035                 if (Data.Lhs.X.LoadEntry && Data.Lhs.X.LoadEntry->AM == AM65_ZP) {
2036                     Data.ZPUsage |= Data.Lhs.X.LoadEntry->Use;
2037                     if ((Data.Lhs.X.LoadEntry->Use & ChangedRegs) != 0) {
2038                         Data.Lhs.X.Flags &= ~(LI_DIRECT | LI_RELOAD_Y);
2039                     }
2040                 }
2041
2042                 /* Check the preconditions. If they aren't ok, reset the insn
2043                 ** pointer to the pushax and start over. We will loose part of
2044                 ** load tracking but at least a/x has probably lost between
2045                 ** pushax and here and will be tracked again when restarting.
2046                 */
2047                 if (!PreCondOk (&Data)) {
2048                     I = Data.PushIndex;
2049                     State = Initialize;
2050                     break;
2051                 }
2052
2053                 /* Prepare the remainder of the data structure. */
2054                 Data.PrevEntry = CS_GetPrevEntry (S, Data.PushIndex);
2055                 Data.PushEntry = CS_GetEntry (S, Data.PushIndex);
2056                 Data.OpEntry   = CS_GetEntry (S, Data.OpIndex);
2057                 Data.NextEntry = CS_GetNextEntry (S, Data.OpIndex);
2058
2059                 /* Remember the current number of code lines */
2060                 OldEntryCount = CS_GetEntryCount (S);
2061
2062                 /* Adjust stack offsets to account for the upcoming removal */
2063                 AdjustStackOffset (&Data, 2);
2064
2065                 /* Regenerate register info, since AdjustStackOffset changed
2066                 ** the code
2067                 */
2068                 CS_GenRegInfo (S);
2069
2070                 /* Call the optimizer function */
2071                 Changes += Data.OptFunc->Func (&Data);
2072
2073                 /* Since the function may have added or deleted entries,
2074                 ** correct the index.
2075                 */
2076                 I += CS_GetEntryCount (S) - OldEntryCount;
2077
2078                 /* Regenerate register info */
2079                 CS_GenRegInfo (S);
2080
2081                 /* Done */
2082                 State = Initialize;
2083                 continue;
2084
2085         }
2086
2087         /* Next entry */
2088         ++I;
2089
2090     }
2091
2092     /* Return the number of changes made */
2093     return Changes;
2094 }