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