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