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