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