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