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