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