]> git.sur5r.net Git - cc65/blob - src/cc65/codeopt.c
Fixed internal compiler error on invalid input code.
[cc65] / src / cc65 / codeopt.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                 codeopt.c                                 */
4 /*                                                                           */
5 /*                           Optimizer subroutines                           */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 2001-2006 Ullrich von Bassewitz                                       */
10 /*               Römerstraße 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 #include <string.h>
38
39 /* common */
40 #include "abend.h"
41 #include "chartype.h"
42 #include "cpu.h"
43 #include "print.h"
44 #include "xmalloc.h"
45
46 /* cc65 */
47 #include "asmlabel.h"
48 #include "codeent.h"
49 #include "codeinfo.h"
50 #include "coptadd.h"
51 #include "coptc02.h"
52 #include "coptcmp.h"
53 #include "coptind.h"
54 #include "coptneg.h"
55 #include "coptpush.h"
56 #include "coptsize.h"
57 #include "coptstop.h"
58 #include "coptstore.h"
59 #include "coptsub.h"
60 #include "copttest.h"
61 #include "error.h"
62 #include "global.h"
63 #include "codeopt.h"
64
65
66
67 /*****************************************************************************/
68 /*                                     Data                                  */
69 /*****************************************************************************/
70
71
72
73 /* Shift types */
74 enum {
75     SHIFT_NONE,
76     SHIFT_ASR_1,
77     SHIFT_ASL_1,
78     SHIFT_LSR_1,
79     SHIFT_LSL_1
80 };
81
82
83
84 /*****************************************************************************/
85 /*                              Optimize shifts                              */
86 /*****************************************************************************/
87
88
89
90 static unsigned OptShift1 (CodeSeg* S)
91 /* A call to the shlaxN routine may get replaced by one or more asl insns
92  * if the value of X is not used later.
93  */
94 {
95     unsigned Changes = 0;
96
97     /* Walk over the entries */
98     unsigned I = 0;
99     while (I < CS_GetEntryCount (S)) {
100
101         /* Get next entry */
102         CodeEntry* E = CS_GetEntry (S, I);
103
104         /* Check for the sequence */
105         if (E->OPC == OP65_JSR                       &&
106             (strncmp (E->Arg, "shlax", 5) == 0 ||
107              strncmp (E->Arg, "aslax", 5) == 0)      &&
108             strlen (E->Arg) == 6                     &&
109             IsDigit (E->Arg[5])                      &&
110             !RegXUsed (S, I+1)) {
111
112             /* Insert shift insns */
113             unsigned Count = E->Arg[5] - '0';
114             while (Count--) {
115                 CodeEntry* X = NewCodeEntry (OP65_ASL, AM65_ACC, "a", 0, E->LI);
116                 CS_InsertEntry (S, X, I+1);
117             }
118
119             /* Delete the call to shlax */
120             CS_DelEntry (S, I);
121
122             /* Remember, we had changes */
123             ++Changes;
124
125         }
126
127         /* Next entry */
128         ++I;
129
130     }
131
132     /* Return the number of changes made */
133     return Changes;
134 }
135
136
137
138 static unsigned OptShift2 (CodeSeg* S)
139 /* A call to the shraxN routine may get replaced by one or more lsr insns
140  * if the value of X is zero.
141  */
142 {
143     unsigned Changes = 0;
144     unsigned I;
145
146     /* Generate register info */
147     CS_GenRegInfo (S);
148
149     /* Walk over the entries */
150     I = 0;
151     while (I < CS_GetEntryCount (S)) {
152
153         /* Get next entry */
154         CodeEntry* E = CS_GetEntry (S, I);
155
156         /* Check for the sequence */
157         if (E->OPC == OP65_JSR                       &&
158             strncmp (E->Arg, "shrax", 5) == 0        &&
159             strlen (E->Arg) == 6                     &&
160             IsDigit (E->Arg[5])                      &&
161             E->RI->In.RegX == 0) {
162
163             /* Insert shift insns */
164             unsigned Count = E->Arg[5] - '0';
165             while (Count--) {
166                 CodeEntry* X = NewCodeEntry (OP65_LSR, AM65_ACC, "a", 0, E->LI);
167                 CS_InsertEntry (S, X, I+1);
168             }
169
170             /* Delete the call to shlax */
171             CS_DelEntry (S, I);
172
173             /* Remember, we had changes */
174             ++Changes;
175
176         }
177
178         /* Next entry */
179         ++I;
180
181     }
182
183     /* Free the register info */
184     CS_FreeRegInfo (S);
185
186     /* Return the number of changes made */
187     return Changes;
188 }
189
190
191
192 static unsigned GetShiftType (const char* Sub)
193 /* Helper function for OptShift3 */
194 {
195     if (*Sub == 'a') {
196         if (strcmp (Sub+1, "slax1") == 0) {
197             return SHIFT_ASL_1;
198         } else if (strcmp (Sub+1, "srax1") == 0) {
199             return SHIFT_ASR_1;
200         }
201     } else if (*Sub == 's') {
202         if (strcmp (Sub+1, "hlax1") == 0) {
203             return SHIFT_LSL_1;
204         } else if (strcmp (Sub+1, "hrax1") == 0) {
205             return SHIFT_LSR_1;
206         }
207     }
208     return SHIFT_NONE;
209 }
210
211
212
213 static unsigned OptShift3 (CodeSeg* S)
214 /* Search for the sequence
215  *
216  *      lda     xxx
217  *      ldx     yyy
218  *      jsr     aslax1/asrax1/shlax1/shrax1
219  *      sta     aaa
220  *      stx     bbb
221  *
222  * and replace it by
223  *
224  *      lda     xxx
225  *      asl     a
226  *      sta     aaa
227  *      lda     yyy
228  *      rol     a
229  *      sta     bbb
230  *
231  * or similar, provided that a/x is not used later
232  */
233 {
234     unsigned Changes = 0;
235
236     /* Walk over the entries */
237     unsigned I = 0;
238     while (I < CS_GetEntryCount (S)) {
239
240         unsigned ShiftType;
241         CodeEntry* L[5];
242
243         /* Get next entry */
244         L[0] = CS_GetEntry (S, I);
245
246         /* Check for the sequence */
247         if (L[0]->OPC == OP65_LDA                               &&
248             (L[0]->AM == AM65_ABS || L[0]->AM == AM65_ZP)       &&
249             CS_GetEntries (S, L+1, I+1, 4)                      &&
250             !CS_RangeHasLabel (S, I+1, 4)                       &&
251             L[1]->OPC == OP65_LDX                               &&
252             (L[1]->AM == AM65_ABS || L[1]->AM == AM65_ZP)       &&
253             L[2]->OPC == OP65_JSR                               &&
254             (ShiftType = GetShiftType (L[2]->Arg)) != SHIFT_NONE&&
255             L[3]->OPC == OP65_STA                               &&
256             (L[3]->AM == AM65_ABS || L[3]->AM == AM65_ZP)       &&
257             L[4]->OPC == OP65_STX                               &&
258             (L[4]->AM == AM65_ABS || L[4]->AM == AM65_ZP)       &&
259             !RegAXUsed (S, I+5)) {
260
261             CodeEntry* X;
262
263             /* Handle the four shift types differently */
264             switch (ShiftType) {
265
266                 case SHIFT_ASR_1:
267                     X = NewCodeEntry (OP65_LDA, L[1]->AM, L[1]->Arg, 0, L[1]->LI);
268                     CS_InsertEntry (S, X, I+5);
269                     X = NewCodeEntry (OP65_CMP, AM65_IMM, "$80", 0, L[2]->LI);
270                     CS_InsertEntry (S, X, I+6);
271                     X = NewCodeEntry (OP65_ROR, AM65_ACC, "a", 0, L[2]->LI);
272                     CS_InsertEntry (S, X, I+7);
273                     X = NewCodeEntry (OP65_STA, L[4]->AM, L[4]->Arg, 0, L[4]->LI);
274                     CS_InsertEntry (S, X, I+8);
275                     X = NewCodeEntry (OP65_LDA, L[0]->AM, L[0]->Arg, 0, L[0]->LI);
276                     CS_InsertEntry (S, X, I+9);
277                     X = NewCodeEntry (OP65_ROR, AM65_ACC, "a", 0, L[2]->LI);
278                     CS_InsertEntry (S, X, I+10);
279                     X = NewCodeEntry (OP65_STA, L[3]->AM, L[3]->Arg, 0, L[3]->LI);
280                     CS_InsertEntry (S, X, I+11);
281                     CS_DelEntries (S, I, 5);
282                     break;
283
284                 case SHIFT_LSR_1:
285                     X = NewCodeEntry (OP65_LDA, L[1]->AM, L[1]->Arg, 0, L[1]->LI);
286                     CS_InsertEntry (S, X, I+5);
287                     X = NewCodeEntry (OP65_LSR, AM65_ACC, "a", 0, L[2]->LI);
288                     CS_InsertEntry (S, X, I+6);
289                     X = NewCodeEntry (OP65_STA, L[4]->AM, L[4]->Arg, 0, L[4]->LI);
290                     CS_InsertEntry (S, X, I+7);
291                     X = NewCodeEntry (OP65_LDA, L[0]->AM, L[0]->Arg, 0, L[0]->LI);
292                     CS_InsertEntry (S, X, I+8);
293                     X = NewCodeEntry (OP65_ROR, AM65_ACC, "a", 0, L[2]->LI);
294                     CS_InsertEntry (S, X, I+9);
295                     X = NewCodeEntry (OP65_STA, L[3]->AM, L[3]->Arg, 0, L[3]->LI);
296                     CS_InsertEntry (S, X, I+10);
297                     CS_DelEntries (S, I, 5);
298                     break;
299
300                 case SHIFT_LSL_1:
301                 case SHIFT_ASL_1:
302                     /* These two are identical */
303                     X = NewCodeEntry (OP65_ASL, AM65_ACC, "a", 0, L[2]->LI);
304                     CS_InsertEntry (S, X, I+1);
305                     X = NewCodeEntry (OP65_STA, L[3]->AM, L[3]->Arg, 0, L[3]->LI);
306                     CS_InsertEntry (S, X, I+2);
307                     X = NewCodeEntry (OP65_LDA, L[1]->AM, L[1]->Arg, 0, L[1]->LI);
308                     CS_InsertEntry (S, X, I+3);
309                     X = NewCodeEntry (OP65_ROL, AM65_ACC, "a", 0, L[2]->LI);
310                     CS_InsertEntry (S, X, I+4);
311                     X = NewCodeEntry (OP65_STA, L[4]->AM, L[4]->Arg, 0, L[4]->LI);
312                     CS_InsertEntry (S, X, I+5);
313                     CS_DelEntries (S, I+6, 4);
314                     break;
315
316             }
317
318             /* Remember, we had changes */
319             ++Changes;
320
321         }
322
323         /* Next entry */
324         ++I;
325
326     }
327
328     /* Return the number of changes made */
329     return Changes;
330 }
331
332
333
334 /*****************************************************************************/
335 /*                              Optimize loads                               */
336 /*****************************************************************************/
337
338
339
340 static unsigned OptLoad1 (CodeSeg* S)
341 /* Search for a call to ldaxysp where X is not used later and replace it by
342  * a load of just the A register.
343  */
344 {
345     unsigned I;
346     unsigned Changes = 0;
347
348     /* Generate register info */
349     CS_GenRegInfo (S);
350
351     /* Walk over the entries */
352     I = 0;
353     while (I < CS_GetEntryCount (S)) {
354
355         CodeEntry* E;
356
357         /* Get next entry */
358         E = CS_GetEntry (S, I);
359
360         /* Check for the sequence */
361         if (CE_IsCallTo (E, "ldaxysp")          &&
362             RegValIsKnown (E->RI->In.RegY)      &&
363             !RegXUsed (S, I+1)) {
364
365             CodeEntry* X;
366
367             /* Reload the Y register */
368             const char* Arg = MakeHexArg (E->RI->In.RegY - 1);
369             X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
370             CS_InsertEntry (S, X, I+1);
371
372             /* Load from stack */
373             X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, E->LI);
374             CS_InsertEntry (S, X, I+2);
375
376             /* Now remove the call to the subroutine */
377             CS_DelEntry (S, I);
378
379             /* Remember, we had changes */
380             ++Changes;
381
382         }
383
384         /* Next entry */
385         ++I;
386
387     }
388
389     /* Free the register info */
390     CS_FreeRegInfo (S);
391
392     /* Return the number of changes made */
393     return Changes;
394 }
395
396
397
398 /*****************************************************************************/
399 /*                     Optimize stores through pointers                      */
400 /*****************************************************************************/
401
402
403
404 static unsigned OptPtrStore1Sub (CodeSeg* S, unsigned I, CodeEntry** const L)
405 /* Check if this is one of the allowed suboperation for OptPtrStore1 */
406 {
407     /* Check for a label attached to the entry */
408     if (CE_HasLabel (L[0])) {
409         return 0;
410     }
411
412     /* Check for single insn sub ops */
413     if (L[0]->OPC == OP65_AND                                           ||
414         L[0]->OPC == OP65_EOR                                           ||
415         L[0]->OPC == OP65_ORA                                           ||
416         (L[0]->OPC == OP65_JSR && strncmp (L[0]->Arg, "shlax", 5) == 0) ||
417         (L[0]->OPC == OP65_JSR && strncmp (L[0]->Arg, "shrax", 5) == 0)) {
418
419         /* One insn */
420         return 1;
421
422     } else if (L[0]->OPC == OP65_CLC                      &&
423                (L[1] = CS_GetNextEntry (S, I)) != 0       &&
424                L[1]->OPC == OP65_ADC                      &&
425                !CE_HasLabel (L[1])) {
426         return 2;
427     } else if (L[0]->OPC == OP65_SEC                      &&
428                (L[1] = CS_GetNextEntry (S, I)) != 0       &&
429                L[1]->OPC == OP65_SBC                      &&
430                !CE_HasLabel (L[1])) {
431         return 2;
432     }
433
434
435
436     /* Not found */
437     return 0;
438 }
439
440
441
442 static unsigned OptPtrStore1 (CodeSeg* S)
443 /* Search for the sequence:
444  *
445  *      jsr     pushax
446  *      ldy     xxx
447  *      jsr     ldauidx
448  *      subop
449  *      ldy     yyy
450  *      jsr     staspidx
451  *
452  * and replace it by:
453  *
454  *      sta     ptr1
455  *      stx     ptr1+1
456  *      ldy     xxx
457  *      ldx     #$00
458  *      lda     (ptr1),y
459  *      subop
460  *      ldy     yyy
461  *      sta     (ptr1),y
462  *
463  * In case a/x is loaded from the register bank before the pushax, we can even
464  * use the register bank instead of ptr1.
465  */
466 /*
467  *      jsr     pushax
468  *      ldy     xxx
469  *      jsr     ldauidx
470  *      ldx     #$00
471  *      lda     (zp),y
472  *      subop
473  *      ldy     yyy
474  *      sta     (zp),y
475  *      jsr     staspidx
476  */
477 {
478     unsigned Changes = 0;
479
480     /* Walk over the entries */
481     unsigned I = 0;
482     while (I < CS_GetEntryCount (S)) {
483
484         unsigned K;
485         CodeEntry* L[10];
486
487         /* Get next entry */
488         L[0] = CS_GetEntry (S, I);
489
490         /* Check for the sequence */
491         if (CE_IsCallTo (L[0], "pushax")            &&
492             CS_GetEntries (S, L+1, I+1, 3)          &&
493             L[1]->OPC == OP65_LDY                   &&
494             CE_IsConstImm (L[1])                    &&
495             !CE_HasLabel (L[1])                     &&
496             CE_IsCallTo (L[2], "ldauidx")           &&
497             !CE_HasLabel (L[2])                     &&
498             (K = OptPtrStore1Sub (S, I+3, L+3)) > 0 &&
499             CS_GetEntries (S, L+3+K, I+3+K, 2)      &&
500             L[3+K]->OPC == OP65_LDY                 &&
501             CE_IsConstImm (L[3+K])                  &&
502             !CE_HasLabel (L[3+K])                   &&
503             CE_IsCallTo (L[4+K], "staspidx")        &&
504             !CE_HasLabel (L[4+K])) {
505
506
507             const char* RegBank = 0;
508             const char* ZPLoc   = "ptr1";
509             CodeEntry* X;
510
511
512             /* Get the preceeding two instructions and check them. We check
513              * for:
514              *          lda     regbank+n
515              *          ldx     regbank+n+1
516              */
517             if (I > 1) {
518                 CodeEntry* P[2];
519                 P[0] = CS_GetEntry (S, I-2);
520                 P[1] = CS_GetEntry (S, I-1);
521                 if (P[0]->OPC == OP65_LDA &&
522                     P[0]->AM  == AM65_ZP  &&
523                     P[1]->OPC == OP65_LDX &&
524                     P[1]->AM  == AM65_ZP  &&
525                     !CE_HasLabel (P[1])   &&
526                     strncmp (P[0]->Arg, "regbank+", 8) == 0) {
527
528                     unsigned Len = strlen (P[0]->Arg);
529
530                     if (strncmp (P[0]->Arg, P[1]->Arg, Len) == 0 &&
531                         P[1]->Arg[Len+0] == '+'                  &&
532                         P[1]->Arg[Len+1] == '1'                  &&
533                         P[1]->Arg[Len+2] == '\0') {
534
535                         /* Ok, found. Use the name of the register bank */
536                         RegBank = ZPLoc = P[0]->Arg;
537                     }
538                 }
539             }
540
541             /* Insert the load via the zp pointer */
542             X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[3]->LI);
543             CS_InsertEntry (S, X, I+3);
544             X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, ZPLoc, 0, L[2]->LI);
545             CS_InsertEntry (S, X, I+4);
546
547             /* Insert the store through the zp pointer */
548             X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, ZPLoc, 0, L[3]->LI);
549             CS_InsertEntry (S, X, I+6+K);
550
551             /* Delete the old code */
552             CS_DelEntry (S, I+7+K);     /* jsr spaspidx */
553             CS_DelEntry (S, I+2);       /* jsr ldauidx */
554
555             /* Create and insert the stores into the zp pointer if needed */
556             if (RegBank == 0) {
557                 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
558                 CS_InsertEntry (S, X, I+1);
559                 X = NewCodeEntry (OP65_STX, AM65_ZP, "ptr1+1", 0, L[0]->LI);
560                 CS_InsertEntry (S, X, I+2);
561             }
562
563             /* Delete more old code. Do it here to keep a label attached to
564              * entry I in place.
565              */
566             CS_DelEntry (S, I);         /* jsr pushax */
567
568             /* Remember, we had changes */
569             ++Changes;
570
571         }
572
573         /* Next entry */
574         ++I;
575
576     }
577
578     /* Return the number of changes made */
579     return Changes;
580 }
581
582
583
584 static unsigned OptPtrStore2 (CodeSeg* S)
585 /* Search for the sequence:
586  *
587  *      lda     #<(label+0)
588  *      ldx     #>(label+0)
589  *      clc
590  *      adc     xxx
591  *      bcc     L
592  *      inx
593  * L:   jsr     pushax
594  *      ldx     #$00
595  *      lda     yyy
596  *      ldy     #$00
597  *      jsr     staspidx
598  *
599  * and replace it by:
600  *
601  *      ldy     xxx
602  *      ldx     #$00
603  *      lda     yyy
604  *      sta     label,y
605  */
606 {
607     unsigned Changes = 0;
608
609     /* Walk over the entries */
610     unsigned I = 0;
611     while (I < CS_GetEntryCount (S)) {
612
613         CodeEntry* L[11];
614         unsigned Len;
615
616         /* Get next entry */
617         L[0] = CS_GetEntry (S, I);
618
619         /* Check for the sequence */
620         if (L[0]->OPC == OP65_LDA                            &&
621             L[0]->AM == AM65_IMM                             &&
622             CS_GetEntries (S, L+1, I+1, 10)                  &&
623             L[1]->OPC == OP65_LDX                            &&
624             L[1]->AM == AM65_IMM                             &&
625             L[2]->OPC == OP65_CLC                            &&
626             L[3]->OPC == OP65_ADC                            &&
627             (L[3]->AM == AM65_ABS || L[3]->AM == AM65_ZP)    &&
628             (L[4]->OPC == OP65_BCC || L[4]->OPC == OP65_JCC) &&
629             L[4]->JumpTo != 0                                &&
630             L[4]->JumpTo->Owner == L[6]                      &&
631             L[5]->OPC == OP65_INX                            &&
632             CE_IsCallTo (L[6], "pushax")                     &&
633             L[7]->OPC == OP65_LDX                            &&
634             L[8]->OPC == OP65_LDA                            &&
635             L[9]->OPC == OP65_LDY                            &&
636             CE_IsKnownImm (L[9], 0)                          &&
637             CE_IsCallTo (L[10], "staspidx")                  &&
638             !CS_RangeHasLabel (S, I+1, 5)                    &&
639             !CS_RangeHasLabel (S, I+7, 4)                    &&
640             /* Check the label last because this is quite costly */
641             (Len = strlen (L[0]->Arg)) > 3                   &&
642             L[0]->Arg[0] == '<'                              &&
643             L[0]->Arg[1] == '('                              &&
644             strlen (L[1]->Arg) == Len                        &&
645             L[1]->Arg[0] == '>'                              &&
646             memcmp (L[0]->Arg+1, L[1]->Arg+1, Len-1) == 0) {
647
648             CodeEntry* X;
649             char* Label;
650
651             /* We will create all the new stuff behind the current one so
652              * we keep the line references.
653              */
654             X = NewCodeEntry (OP65_LDY, L[3]->AM, L[3]->Arg, 0, L[0]->LI);
655             CS_InsertEntry (S, X, I+11);
656
657             X = NewCodeEntry (OP65_LDX, L[7]->AM, L[7]->Arg, 0, L[7]->LI);
658             CS_InsertEntry (S, X, I+12);
659
660             X = NewCodeEntry (OP65_LDA, L[8]->AM, L[8]->Arg, 0, L[8]->LI);
661             CS_InsertEntry (S, X, I+13);
662
663             Label = memcpy (xmalloc (Len-2), L[0]->Arg+2, Len-3);
664             Label[Len-3] = '\0';
665             X = NewCodeEntry (OP65_STA, AM65_ABSY, Label, 0, L[10]->LI);
666             CS_InsertEntry (S, X, I+14);
667             xfree (Label);
668
669             /* Remove the old code */
670             CS_DelEntries (S, I, 11);
671
672             /* Remember, we had changes */
673             ++Changes;
674
675         }
676
677         /* Next entry */
678         ++I;
679
680     }
681
682     /* Return the number of changes made */
683     return Changes;
684 }
685
686
687
688 /*****************************************************************************/
689 /*                      Optimize loads through pointers                      */
690 /*****************************************************************************/
691
692
693
694 static unsigned OptPtrLoad1 (CodeSeg* S)
695 /* Search for the sequence:
696  *
697  *      clc
698  *      adc     xxx
699  *      tay
700  *      txa
701  *      adc     yyy
702  *      tax
703  *      tya
704  *      ldy
705  *      jsr     ldauidx
706  *
707  * and replace it by:
708  *
709  *      clc
710  *      adc     xxx
711  *      sta     ptr1
712  *      txa
713  *      adc     yyy
714  *      sta     ptr1+1
715  *      ldy
716  *      ldx     #$00
717  *      lda     (ptr1),y
718  */
719 {
720     unsigned Changes = 0;
721
722     /* Walk over the entries */
723     unsigned I = 0;
724     while (I < CS_GetEntryCount (S)) {
725
726         CodeEntry* L[9];
727
728         /* Get next entry */
729         L[0] = CS_GetEntry (S, I);
730
731         /* Check for the sequence */
732         if (L[0]->OPC == OP65_CLC               &&
733             CS_GetEntries (S, L+1, I+1, 8)      &&
734             L[1]->OPC == OP65_ADC               &&
735             L[2]->OPC == OP65_TAY               &&
736             L[3]->OPC == OP65_TXA               &&
737             L[4]->OPC == OP65_ADC               &&
738             L[5]->OPC == OP65_TAX               &&
739             L[6]->OPC == OP65_TYA               &&
740             L[7]->OPC == OP65_LDY               &&
741             CE_IsCallTo (L[8], "ldauidx")       &&
742             !CS_RangeHasLabel (S, I+1, 8)) {
743
744             CodeEntry* X;
745             CodeEntry* P;
746
747             /* Track the insertion point */
748             unsigned IP = I+2;
749
750             /* sta ptr1 */
751             X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[2]->LI);
752             CS_InsertEntry (S, X, IP++);
753
754             /* If the instruction before the clc is a ldx, replace the
755              * txa by an lda with the same location of the ldx. Otherwise
756              * transfer the value in X to A.
757              */
758             if ((P = CS_GetPrevEntry (S, I)) != 0 &&
759                 P->OPC == OP65_LDX                &&
760                 !CE_HasLabel (P)) {
761                 X = NewCodeEntry (OP65_LDA, P->AM, P->Arg, 0, P->LI);
762             } else {
763                 X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, L[3]->LI);
764             }
765             CS_InsertEntry (S, X, IP++);
766
767             /* adc yyy */
768             X = NewCodeEntry (OP65_ADC, L[4]->AM, L[4]->Arg, 0, L[4]->LI);
769             CS_InsertEntry (S, X, IP++);
770
771             /* sta ptr1+1 */
772             X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1+1", 0, L[5]->LI);
773             CS_InsertEntry (S, X, IP++);
774
775             /* ldy ... */
776             X = NewCodeEntry (OP65_LDY, L[7]->AM, L[7]->Arg, 0, L[7]->LI);
777             CS_InsertEntry (S, X, IP++);
778
779             /* ldx #$00 */
780             X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[8]->LI);
781             CS_InsertEntry (S, X, IP++);
782
783             /* lda (ptr1),y */
784             X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "ptr1", 0, L[8]->LI);
785             CS_InsertEntry (S, X, IP++);
786
787             /* Remove the old instructions */
788             CS_DelEntries (S, IP, 7);
789
790             /* Remember, we had changes */
791             ++Changes;
792
793         }
794
795         /* Next entry */
796         ++I;
797
798     }
799
800     /* Return the number of changes made */
801     return Changes;
802 }
803
804
805
806 static unsigned OptPtrLoad2 (CodeSeg* S)
807 /* Search for the sequence:
808  *
809  *      adc     xxx
810  *      pha
811  *      txa
812  *      iny
813  *      adc     yyy
814  *      tax
815  *      pla
816  *      ldy
817  *      jsr     ldauidx
818  *
819  * and replace it by:
820  *
821  *      adc     xxx
822  *      sta     ptr1
823  *      txa
824  *      iny
825  *      adc     yyy
826  *      sta     ptr1+1
827  *      ldy
828  *      ldx     #$00
829  *      lda     (ptr1),y
830  */
831 {
832     unsigned Changes = 0;
833
834     /* Walk over the entries */
835     unsigned I = 0;
836     while (I < CS_GetEntryCount (S)) {
837
838         CodeEntry* L[9];
839
840         /* Get next entry */
841         L[0] = CS_GetEntry (S, I);
842
843         /* Check for the sequence */
844         if (L[0]->OPC == OP65_ADC               &&
845             CS_GetEntries (S, L+1, I+1, 8)      &&
846             L[1]->OPC == OP65_PHA               &&
847             L[2]->OPC == OP65_TXA               &&
848             L[3]->OPC == OP65_INY               &&
849             L[4]->OPC == OP65_ADC               &&
850             L[5]->OPC == OP65_TAX               &&
851             L[6]->OPC == OP65_PLA               &&
852             L[7]->OPC == OP65_LDY               &&
853             CE_IsCallTo (L[8], "ldauidx")       &&
854             !CS_RangeHasLabel (S, I+1, 8)) {
855
856             CodeEntry* X;
857
858             /* Store the low byte and remove the PHA instead */
859             X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
860             CS_InsertEntry (S, X, I+1);
861
862             /* Store the high byte */
863             X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1+1", 0, L[4]->LI);
864             CS_InsertEntry (S, X, I+6);
865
866             /* Load high and low byte */
867             X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[6]->LI);
868             CS_InsertEntry (S, X, I+10);
869             X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "ptr1", 0, L[6]->LI);
870             CS_InsertEntry (S, X, I+11);
871
872             /* Delete the old code */
873             CS_DelEntry (S, I+12);      /* jsr ldauidx */
874             CS_DelEntry (S, I+8);       /* pla */
875             CS_DelEntry (S, I+7);       /* tax */
876             CS_DelEntry (S, I+2);       /* pha */
877
878             /* Remember, we had changes */
879             ++Changes;
880
881         }
882
883         /* Next entry */
884         ++I;
885
886     }
887
888     /* Return the number of changes made */
889     return Changes;
890 }
891
892
893
894 static unsigned OptPtrLoad3 (CodeSeg* S)
895 /* Search for the sequence:
896  *
897  *      lda     #<(label+0)
898  *      ldx     #>(label+0)
899  *      clc
900  *      adc     xxx
901  *      bcc     L
902  *      inx
903  * L:   ldy     #$00
904  *      jsr     ldauidx
905  *
906  * and replace it by:
907  *
908  *      ldy     xxx
909  *      ldx     #$00
910  *      lda     label,y
911  */
912 {
913     unsigned Changes = 0;
914
915     /* Walk over the entries */
916     unsigned I = 0;
917     while (I < CS_GetEntryCount (S)) {
918
919         CodeEntry* L[8];
920         unsigned Len;
921
922         /* Get next entry */
923         L[0] = CS_GetEntry (S, I);
924
925         /* Check for the sequence */
926         if (L[0]->OPC == OP65_LDA                            &&
927             L[0]->AM == AM65_IMM                             &&
928             CS_GetEntries (S, L+1, I+1, 7)                   &&
929             L[1]->OPC == OP65_LDX                            &&
930             L[1]->AM == AM65_IMM                             &&
931             L[2]->OPC == OP65_CLC                            &&
932             L[3]->OPC == OP65_ADC                            &&
933             (L[3]->AM == AM65_ABS || L[3]->AM == AM65_ZP)    &&
934             (L[4]->OPC == OP65_BCC || L[4]->OPC == OP65_JCC) &&
935             L[4]->JumpTo != 0                                &&
936             L[4]->JumpTo->Owner == L[6]                      &&
937             L[5]->OPC == OP65_INX                            &&
938             L[6]->OPC == OP65_LDY                            &&
939             CE_IsKnownImm (L[6], 0)                          &&
940             CE_IsCallTo (L[7], "ldauidx")                    &&
941             !CS_RangeHasLabel (S, I+1, 5)                    &&
942             !CE_HasLabel (L[7])                              &&
943             /* Check the label last because this is quite costly */
944             (Len = strlen (L[0]->Arg)) > 3                   &&
945             L[0]->Arg[0] == '<'                              &&
946             L[0]->Arg[1] == '('                              &&
947             strlen (L[1]->Arg) == Len                        &&
948             L[1]->Arg[0] == '>'                              &&
949             memcmp (L[0]->Arg+1, L[1]->Arg+1, Len-1) == 0) {
950
951             CodeEntry* X;
952             char* Label;
953
954             /* We will create all the new stuff behind the current one so
955              * we keep the line references.
956              */
957             X = NewCodeEntry (OP65_LDY, L[3]->AM, L[3]->Arg, 0, L[0]->LI);
958             CS_InsertEntry (S, X, I+8);
959
960             X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[0]->LI);
961             CS_InsertEntry (S, X, I+9);
962
963             Label = memcpy (xmalloc (Len-2), L[0]->Arg+2, Len-3);
964             Label[Len-3] = '\0';
965             X = NewCodeEntry (OP65_LDA, AM65_ABSY, Label, 0, L[0]->LI);
966             CS_InsertEntry (S, X, I+10);
967             xfree (Label);
968
969             /* Remove the old code */
970             CS_DelEntries (S, I, 8);
971
972             /* Remember, we had changes */
973             ++Changes;
974
975         }
976
977         /* Next entry */
978         ++I;
979
980     }
981
982     /* Return the number of changes made */
983     return Changes;
984 }
985
986
987
988 static unsigned OptPtrLoad4 (CodeSeg* S)
989 /* Search for the sequence:
990  *
991  *      lda     #<(label+0)
992  *      ldx     #>(label+0)
993  *      ldy     #$xx
994  *      clc
995  *      adc     (sp),y
996  *      bcc     L
997  *      inx
998  * L:   ldy     #$00
999  *      jsr     ldauidx
1000  *
1001  * and replace it by:
1002  *
1003  *      ldy     #$xx
1004  *      lda     (sp),y
1005  *      tay
1006  *      ldx     #$00
1007  *      lda     label,y
1008  */
1009 {
1010     unsigned Changes = 0;
1011
1012     /* Walk over the entries */
1013     unsigned I = 0;
1014     while (I < CS_GetEntryCount (S)) {
1015
1016         CodeEntry* L[9];
1017         unsigned Len;
1018
1019         /* Get next entry */
1020         L[0] = CS_GetEntry (S, I);
1021
1022         /* Check for the sequence */
1023         if (L[0]->OPC == OP65_LDA                            &&
1024             L[0]->AM == AM65_IMM                             &&
1025             CS_GetEntries (S, L+1, I+1, 8)                   &&
1026             L[1]->OPC == OP65_LDX                            &&
1027             L[1]->AM == AM65_IMM                             &&
1028             !CE_HasLabel (L[1])                              &&
1029             L[2]->OPC == OP65_LDY                            &&
1030             CE_IsConstImm (L[2])                             &&
1031             !CE_HasLabel (L[2])                              &&
1032             L[3]->OPC == OP65_CLC                            &&
1033             !CE_HasLabel (L[3])                              &&
1034             L[4]->OPC == OP65_ADC                            &&
1035             L[4]->AM == AM65_ZP_INDY                         &&
1036             !CE_HasLabel (L[4])                              &&
1037             (L[5]->OPC == OP65_BCC || L[5]->OPC == OP65_JCC) &&
1038             L[5]->JumpTo != 0                                &&
1039             L[5]->JumpTo->Owner == L[7]                      &&
1040             !CE_HasLabel (L[5])                              &&
1041             L[6]->OPC == OP65_INX                            &&
1042             !CE_HasLabel (L[6])                              &&
1043             L[7]->OPC == OP65_LDY                            &&
1044             CE_IsKnownImm (L[7], 0)                          &&
1045             CE_IsCallTo (L[8], "ldauidx")                    &&
1046             !CE_HasLabel (L[8])                              &&
1047             /* Check the label last because this is quite costly */
1048             (Len = strlen (L[0]->Arg)) > 3                   &&
1049             L[0]->Arg[0] == '<'                              &&
1050             L[0]->Arg[1] == '('                              &&
1051             strlen (L[1]->Arg) == Len                        &&
1052             L[1]->Arg[0] == '>'                              &&
1053             memcmp (L[0]->Arg+1, L[1]->Arg+1, Len-1) == 0) {
1054
1055             CodeEntry* X;
1056             char* Label;
1057
1058             /* Add the lda */
1059             X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, L[4]->Arg, 0, L[0]->LI);
1060             CS_InsertEntry (S, X, I+3);
1061
1062             /* Add the tay */
1063             X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, L[0]->LI);
1064             CS_InsertEntry (S, X, I+4);
1065
1066             /* Add the ldx */
1067             X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[0]->LI);
1068             CS_InsertEntry (S, X, I+5);
1069
1070             /* Add the lda */
1071             Label = memcpy (xmalloc (Len-2), L[0]->Arg+2, Len-3);
1072             Label[Len-3] = '\0';
1073             X = NewCodeEntry (OP65_LDA, AM65_ABSY, Label, 0, L[0]->LI);
1074             CS_InsertEntry (S, X, I+6);
1075             xfree (Label);
1076
1077             /* Remove the old code */
1078             CS_DelEntries (S, I, 2);
1079             CS_DelEntries (S, I+5, 6);
1080
1081             /* Remember, we had changes */
1082             ++Changes;
1083
1084         }
1085
1086         /* Next entry */
1087         ++I;
1088
1089     }
1090
1091     /* Return the number of changes made */
1092     return Changes;
1093 }
1094
1095
1096
1097 static unsigned OptPtrLoad5 (CodeSeg* S)
1098 /* Search for the sequence:
1099  *
1100  *      clc
1101  *      adc     xxx
1102  *      bcc     L
1103  *      inx
1104  * L:   ldy     #$00
1105  *      jsr     ldauidx
1106  *
1107  * and replace it by:
1108  *
1109  *      ldy     xxx
1110  *      sta     ptr1
1111  *      stx     ptr1+1
1112  *      ldx     #$00
1113  *      lda     (ptr1),y
1114  */
1115 {
1116     unsigned Changes = 0;
1117
1118     /* Walk over the entries */
1119     unsigned I = 0;
1120     while (I < CS_GetEntryCount (S)) {
1121
1122         CodeEntry* L[6];
1123
1124         /* Get next entry */
1125         L[0] = CS_GetEntry (S, I);
1126
1127         /* Check for the sequence */
1128         if (L[0]->OPC == OP65_CLC                            &&
1129             CS_GetEntries (S, L+1, I+1, 5)                   &&
1130             L[1]->OPC == OP65_ADC                            &&
1131             (L[1]->AM == AM65_ABS || L[1]->AM == AM65_ZP || L[1]->AM == AM65_IMM)    &&
1132             (L[2]->OPC == OP65_BCC || L[2]->OPC == OP65_JCC) &&
1133             L[2]->JumpTo != 0                                &&
1134             L[2]->JumpTo->Owner == L[4]                      &&
1135             L[3]->OPC == OP65_INX                            &&
1136             L[4]->OPC == OP65_LDY                            &&
1137             CE_IsKnownImm (L[4], 0)                          &&
1138             CE_IsCallTo (L[5], "ldauidx")                    &&
1139             !CS_RangeHasLabel (S, I+1, 3)                    &&
1140             !CE_HasLabel (L[5])) {
1141
1142             CodeEntry* X;
1143
1144             /* We will create all the new stuff behind the current one so
1145              * we keep the line references.
1146              */
1147             X = NewCodeEntry (OP65_LDY, L[1]->AM, L[1]->Arg, 0, L[0]->LI);
1148             CS_InsertEntry (S, X, I+6);
1149
1150             /* sta ptr1 */
1151             X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
1152             CS_InsertEntry (S, X, I+7);
1153
1154             /* stx ptr1+1 */
1155             X = NewCodeEntry (OP65_STX, AM65_ZP, "ptr1+1", 0, L[0]->LI);
1156             CS_InsertEntry (S, X, I+8);
1157
1158             /* ldx #$00 */
1159             X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[0]->LI);
1160             CS_InsertEntry (S, X, I+9);
1161
1162             X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "ptr1", 0, L[0]->LI);
1163             CS_InsertEntry (S, X, I+10);
1164
1165             /* Remove the old code */
1166             CS_DelEntries (S, I, 6);
1167
1168             /* Remember, we had changes */
1169             ++Changes;
1170
1171         }
1172
1173         /* Next entry */
1174         ++I;
1175
1176     }
1177
1178     /* Return the number of changes made */
1179     return Changes;
1180 }
1181
1182
1183
1184 static unsigned OptPtrLoad6 (CodeSeg* S)
1185 /* Search for the sequence:
1186  *
1187  *      lda     regbank+n
1188  *      ldx     regbank+n+1
1189  *      sta     regsave
1190  *      stx     regsave+1
1191  *      clc
1192  *      adc     #$01
1193  *      bcc     L0005
1194  *      inx
1195  * L:   sta     regbank+n
1196  *      stx     regbank+n+1
1197  *      lda     regsave
1198  *      ldx     regsave+1
1199  *      ldy     #$00
1200  *      jsr     ldauidx
1201  *
1202  * and replace it by:
1203  *
1204  *      ldy     #$00
1205  *      ldx     #$00
1206  *      lda     (regbank+n),y
1207  *      inc     regbank+n
1208  *      bne     L1
1209  *      inc     regbank+n+1
1210  * L1:  tay                     <- only if flags are used
1211  *
1212  * This function must execute before OptPtrLoad7!
1213  *
1214  */
1215 {
1216     unsigned Changes = 0;
1217
1218     /* Walk over the entries */
1219     unsigned I = 0;
1220     while (I < CS_GetEntryCount (S)) {
1221
1222         CodeEntry* L[15];
1223         unsigned Len;
1224
1225         /* Get next entry */
1226         L[0] = CS_GetEntry (S, I);
1227
1228         /* Check for the sequence */
1229         if (L[0]->OPC == OP65_LDA                               &&
1230             L[0]->AM == AM65_ZP                                 &&
1231             strncmp (L[0]->Arg, "regbank+", 8) == 0             &&
1232             (Len = strlen (L[0]->Arg)) > 0                      &&
1233             CS_GetEntries (S, L+1, I+1, 14)                     &&
1234             !CS_RangeHasLabel (S, I+1, 7)                       &&
1235             !CS_RangeHasLabel (S, I+9, 5)                       &&
1236             L[1]->OPC == OP65_LDX                               &&
1237             L[1]->AM == AM65_ZP                                 &&
1238             strncmp (L[1]->Arg, L[0]->Arg, Len) == 0            &&
1239             strcmp (L[1]->Arg+Len, "+1") == 0                   &&
1240             L[2]->OPC == OP65_STA                               &&
1241             L[2]->AM == AM65_ZP                                 &&
1242             strcmp (L[2]->Arg, "regsave") == 0                  &&
1243             L[3]->OPC == OP65_STX                               &&
1244             L[3]->AM == AM65_ZP                                 &&
1245             strcmp (L[3]->Arg, "regsave+1") == 0                &&
1246             L[4]->OPC == OP65_CLC                               &&
1247             L[5]->OPC == OP65_ADC                               &&
1248             CE_IsKnownImm (L[5], 1)                             &&
1249             L[6]->OPC == OP65_BCC                               &&
1250             L[6]->JumpTo != 0                                   &&
1251             L[6]->JumpTo->Owner == L[8]                         &&
1252             L[7]->OPC == OP65_INX                               &&
1253             L[8]->OPC == OP65_STA                               &&
1254             L[8]->AM == AM65_ZP                                 &&
1255             strcmp (L[8]->Arg, L[0]->Arg) == 0                  &&
1256             L[9]->OPC == OP65_STX                               &&
1257             L[9]->AM == AM65_ZP                                 &&
1258             strcmp (L[9]->Arg, L[1]->Arg) == 0                  &&
1259             L[10]->OPC == OP65_LDA                              &&
1260             L[10]->AM == AM65_ZP                                &&
1261             strcmp (L[10]->Arg, "regsave") == 0                 &&
1262             L[11]->OPC == OP65_LDX                              &&
1263             L[11]->AM == AM65_ZP                                &&
1264             strcmp (L[11]->Arg, "regsave+1") == 0               &&
1265             L[12]->OPC == OP65_LDY                              &&
1266             CE_IsConstImm (L[12])                               &&
1267             CE_IsCallTo (L[13], "ldauidx")) {
1268
1269             CodeEntry* X;
1270             CodeLabel* Label;
1271
1272             /* Check if the instruction following the sequence uses the flags
1273              * set by the load. If so, insert a test of the value in the
1274              * accumulator.
1275              */
1276             if (CE_UseLoadFlags (L[14])) {
1277                 X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, L[13]->LI);
1278                 CS_InsertEntry (S, X, I+14);
1279             }
1280
1281             /* Attach a label to L[14]. This may be either the just inserted
1282              * instruction, or the one following the sequence.
1283              */
1284             Label = CS_GenLabel (S, L[14]);
1285
1286             /* ldy #$xx */
1287             X = NewCodeEntry (OP65_LDY, AM65_IMM, L[12]->Arg, 0, L[12]->LI);
1288             CS_InsertEntry (S, X, I+14);
1289
1290             /* ldx #$xx */
1291             X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[13]->LI);
1292             CS_InsertEntry (S, X, I+15);
1293
1294             /* lda (regbank+n),y */
1295             X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, L[0]->Arg, 0, L[13]->LI);
1296             CS_InsertEntry (S, X, I+16);
1297
1298             /* inc regbank+n */
1299             X = NewCodeEntry (OP65_INC, AM65_ZP, L[0]->Arg, 0, L[5]->LI);
1300             CS_InsertEntry (S, X, I+17);
1301
1302             /* bne ... */
1303             X = NewCodeEntry (OP65_BNE, AM65_BRA, Label->Name, Label, L[6]->LI);
1304             CS_InsertEntry (S, X, I+18);
1305
1306             /* inc regbank+n+1 */
1307             X = NewCodeEntry (OP65_INC, AM65_ZP, L[1]->Arg, 0, L[7]->LI);
1308             CS_InsertEntry (S, X, I+19);
1309
1310             /* Delete the old code */
1311             CS_DelEntries (S, I, 14);
1312
1313             /* Remember, we had changes */
1314             ++Changes;
1315
1316         }
1317
1318         /* Next entry */
1319         ++I;
1320
1321     }
1322
1323     /* Return the number of changes made */
1324     return Changes;
1325 }
1326
1327
1328
1329 static unsigned OptPtrLoad7 (CodeSeg* S)
1330 /* Search for the sequence:
1331  *
1332  *      lda     zp
1333  *      ldx     zp+1
1334  *      ldy     xx
1335  *      jsr     ldauidx
1336  *
1337  * and replace it by:
1338  *
1339  *      ldy     xx
1340  *      ldx     #$00
1341  *      lda     (zp),y
1342  */
1343 {
1344     unsigned Changes = 0;
1345
1346     /* Walk over the entries */
1347     unsigned I = 0;
1348     while (I < CS_GetEntryCount (S)) {
1349
1350         CodeEntry* L[4];
1351         unsigned Len;
1352
1353         /* Get next entry */
1354         L[0] = CS_GetEntry (S, I);
1355
1356         /* Check for the sequence */
1357         if (L[0]->OPC == OP65_LDA && L[0]->AM == AM65_ZP        &&
1358             CS_GetEntries (S, L+1, I+1, 3)                      &&
1359             !CS_RangeHasLabel (S, I+1, 3)                       &&
1360             L[1]->OPC == OP65_LDX && L[1]->AM == AM65_ZP        &&
1361             (Len = strlen (L[0]->Arg)) > 0                      &&
1362             strncmp (L[0]->Arg, L[1]->Arg, Len) == 0            &&
1363             strcmp (L[1]->Arg + Len, "+1") == 0                 &&
1364             L[2]->OPC == OP65_LDY                               &&
1365             CE_IsCallTo (L[3], "ldauidx")) {
1366
1367             CodeEntry* X;
1368
1369             /* ldx #$00 */
1370             X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[3]->LI);
1371             CS_InsertEntry (S, X, I+3);
1372
1373             /* lda (zp),y */
1374             X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, L[0]->Arg, 0, L[3]->LI);
1375             CS_InsertEntry (S, X, I+4);
1376
1377             /* Remove the old code */
1378             CS_DelEntry (S, I+5);
1379             CS_DelEntries (S, I, 2);
1380
1381             /* Remember, we had changes */
1382             ++Changes;
1383
1384         }
1385
1386         /* Next entry */
1387         ++I;
1388
1389     }
1390
1391     /* Return the number of changes made */
1392     return Changes;
1393 }
1394
1395
1396
1397 static unsigned OptPtrLoad8 (CodeSeg* S)
1398 /* Search for the sequence:
1399  *
1400  *      lda     zp
1401  *      ldx     zp+1
1402  *      (anything that doesn't change a/x)
1403  *      ldy     xx
1404  *      jsr     ldauidx
1405  *
1406  * and replace it by:
1407  *
1408  *      lda     zp
1409  *      ldx     zp+1
1410  *      (anything that doesn't change a/x)
1411  *      ldy     xx
1412  *      ldx     #$00
1413  *      lda     (zp),y
1414  *
1415  * Must execute before OptPtrLoad10!
1416  */
1417 {
1418     unsigned Changes = 0;
1419     unsigned I;
1420
1421     /* Generate register info */
1422     CS_GenRegInfo (S);
1423
1424     /* Walk over the entries */
1425     I = 0;
1426     while (I < CS_GetEntryCount (S)) {
1427
1428         CodeEntry* L[5];
1429         unsigned Len;
1430
1431         /* Get next entry */
1432         L[0] = CS_GetEntry (S, I);
1433
1434         /* Check for the sequence */
1435         if (L[0]->OPC == OP65_LDA && L[0]->AM == AM65_ZP        &&
1436             CS_GetEntries (S, L+1, I+1, 4)                      &&
1437             !CS_RangeHasLabel (S, I+1, 4)                       &&
1438             L[1]->OPC == OP65_LDX && L[1]->AM == AM65_ZP        &&
1439             (Len = strlen (L[0]->Arg)) > 0                      &&
1440             strncmp (L[0]->Arg, L[1]->Arg, Len) == 0            &&
1441             strcmp (L[1]->Arg + Len, "+1") == 0                 &&
1442             (L[2]->Chg & REG_AX) == 0                           &&
1443             L[3]->OPC == OP65_LDY                               &&
1444             CE_IsCallTo (L[4], "ldauidx")) {
1445
1446             CodeEntry* X;
1447
1448             /* ldx #$00 */
1449             X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[3]->LI);
1450             CS_InsertEntry (S, X, I+5);
1451
1452             /* lda (zp),y */
1453             X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, L[0]->Arg, 0, L[3]->LI);
1454             CS_InsertEntry (S, X, I+6);
1455
1456             /* Remove the old code */
1457             CS_DelEntry (S, I+4);
1458
1459             /* Remember, we had changes */
1460             ++Changes;
1461
1462         }
1463
1464         /* Next entry */
1465         ++I;
1466
1467     }
1468
1469     /* Free the register info */
1470     CS_FreeRegInfo (S);
1471
1472     /* Return the number of changes made */
1473     return Changes;
1474 }
1475
1476
1477
1478 static unsigned OptPtrLoad9 (CodeSeg* S)
1479 /* Search for the sequence:
1480  *
1481  *      lda     zp
1482  *      ldx     zp+1
1483  *      ldy     xx
1484  *      jsr     ldaxidx
1485  *
1486  * and replace it by:
1487  *
1488  *      ldy     xx
1489  *      lda     (zp),y
1490  *      tax
1491  *      dey
1492  *      lda     (zp),y
1493  */
1494 {
1495     unsigned Changes = 0;
1496
1497     /* Walk over the entries */
1498     unsigned I = 0;
1499     while (I < CS_GetEntryCount (S)) {
1500
1501         CodeEntry* L[4];
1502         unsigned Len;
1503
1504         /* Get next entry */
1505         L[0] = CS_GetEntry (S, I);
1506
1507         /* Check for the sequence */
1508         if (L[0]->OPC == OP65_LDA && L[0]->AM == AM65_ZP        &&
1509             CS_GetEntries (S, L+1, I+1, 3)                      &&
1510             !CS_RangeHasLabel (S, I+1, 3)                       &&
1511             L[1]->OPC == OP65_LDX && L[1]->AM == AM65_ZP        &&
1512             (Len = strlen (L[0]->Arg)) > 0                      &&
1513             strncmp (L[0]->Arg, L[1]->Arg, Len) == 0            &&
1514             strcmp (L[1]->Arg + Len, "+1") == 0                 &&
1515             L[2]->OPC == OP65_LDY                               &&
1516             CE_IsCallTo (L[3], "ldaxidx")) {
1517
1518             CodeEntry* X;
1519
1520             /* lda (zp),y */
1521             X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, L[0]->Arg, 0, L[3]->LI);
1522             CS_InsertEntry (S, X, I+4);
1523
1524             /* tax */
1525             X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, L[3]->LI);
1526             CS_InsertEntry (S, X, I+5);
1527
1528             /* dey */
1529             X = NewCodeEntry (OP65_DEY, AM65_IMP, 0, 0, L[3]->LI);
1530             CS_InsertEntry (S, X, I+6);
1531
1532             /* lda (zp),y */
1533             X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, L[0]->Arg, 0, L[3]->LI);
1534             CS_InsertEntry (S, X, I+7);
1535
1536             /* Remove the old code */
1537             CS_DelEntry (S, I+3);
1538             CS_DelEntries (S, I, 2);
1539
1540             /* Remember, we had changes */
1541             ++Changes;
1542
1543         }
1544
1545         /* Next entry */
1546         ++I;
1547
1548     }
1549
1550     /* Return the number of changes made */
1551     return Changes;
1552 }
1553
1554
1555
1556 static unsigned OptPtrLoad10 (CodeSeg* S)
1557 /* Search for the sequence
1558  *
1559  *      ldy     ...
1560  *      jsr     ldauidx
1561  *
1562  * and replace it by:
1563  *
1564  *      ldy     ...
1565  *      stx     ptr1+1
1566  *      sta     ptr1
1567  *      ldx     #$00
1568  *      lda     (ptr1),y
1569  *
1570  * This step must be executed *after* OptPtrLoad1!
1571  */
1572 {
1573     unsigned Changes = 0;
1574
1575     /* Walk over the entries */
1576     unsigned I = 0;
1577     while (I < CS_GetEntryCount (S)) {
1578
1579         CodeEntry* L[2];
1580
1581         /* Get next entry */
1582         L[0] = CS_GetEntry (S, I);
1583
1584         /* Check for the sequence */
1585         if (L[0]->OPC == OP65_LDY               &&
1586             CS_GetEntries (S, L+1, I+1, 1)      &&
1587             CE_IsCallTo (L[1], "ldauidx")       &&
1588             !CE_HasLabel (L[1])) {
1589
1590             CodeEntry* X;
1591
1592             /* Store the high byte */
1593             X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
1594             CS_InsertEntry (S, X, I+1);
1595
1596             /* Store the low byte */
1597             X = NewCodeEntry (OP65_STX, AM65_ZP, "ptr1+1", 0, L[0]->LI);
1598             CS_InsertEntry (S, X, I+2);
1599
1600             /* Delete the call to ldauidx */
1601             CS_DelEntry (S, I+3);
1602
1603             /* Load the high and low byte */
1604             X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[0]->LI);
1605             CS_InsertEntry (S, X, I+3);
1606             X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "ptr1", 0, L[0]->LI);
1607             CS_InsertEntry (S, X, I+4);
1608
1609             /* Remember, we had changes */
1610             ++Changes;
1611
1612         }
1613
1614         /* Next entry */
1615         ++I;
1616
1617     }
1618
1619     /* Return the number of changes made */
1620     return Changes;
1621 }
1622
1623
1624
1625 /*****************************************************************************/
1626 /*                            Decouple operations                            */
1627 /*****************************************************************************/
1628
1629
1630
1631 static unsigned OptDecouple (CodeSeg* S)
1632 /* Decouple operations, that is, do the following replacements:
1633  *
1634  *   dex        -> ldx #imm
1635  *   inx        -> ldx #imm
1636  *   dey        -> ldy #imm
1637  *   iny        -> ldy #imm
1638  *   tax        -> ldx #imm
1639  *   txa        -> lda #imm
1640  *   tay        -> ldy #imm
1641  *   tya        -> lda #imm
1642  *   lda zp     -> lda #imm
1643  *   ldx zp     -> ldx #imm
1644  *   ldy zp     -> ldy #imm
1645  *
1646  * Provided that the register values are known of course.
1647  */
1648 {
1649     unsigned Changes = 0;
1650     unsigned I;
1651
1652     /* Generate register info for the following step */
1653     CS_GenRegInfo (S);
1654
1655     /* Walk over the entries */
1656     I = 0;
1657     while (I < CS_GetEntryCount (S)) {
1658
1659         const char* Arg;
1660
1661         /* Get next entry and it's input register values */
1662         CodeEntry* E = CS_GetEntry (S, I);
1663         const RegContents* In = &E->RI->In;
1664
1665         /* Assume we have no replacement */
1666         CodeEntry* X = 0;
1667
1668         /* Check the instruction */
1669         switch (E->OPC) {
1670
1671             case OP65_DEA:
1672                 if (RegValIsKnown (In->RegA)) {
1673                     Arg = MakeHexArg ((In->RegA - 1) & 0xFF);
1674                     X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI);
1675                 }
1676                 break;
1677
1678             case OP65_DEX:
1679                 if (RegValIsKnown (In->RegX)) {
1680                     Arg = MakeHexArg ((In->RegX - 1) & 0xFF);
1681                     X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI);
1682                 }
1683                 break;
1684
1685             case OP65_DEY:
1686                 if (RegValIsKnown (In->RegY)) {
1687                     Arg = MakeHexArg ((In->RegY - 1) & 0xFF);
1688                     X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
1689                 }
1690                 break;
1691
1692             case OP65_INA:
1693                 if (RegValIsKnown (In->RegA)) {
1694                     Arg = MakeHexArg ((In->RegA + 1) & 0xFF);
1695                     X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI);
1696                 }
1697                 break;
1698
1699             case OP65_INX:
1700                 if (RegValIsKnown (In->RegX)) {
1701                     Arg = MakeHexArg ((In->RegX + 1) & 0xFF);
1702                     X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI);
1703                 }
1704                 break;
1705
1706             case OP65_INY:
1707                 if (RegValIsKnown (In->RegY)) {
1708                     Arg = MakeHexArg ((In->RegY + 1) & 0xFF);
1709                     X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
1710                 }
1711                 break;
1712
1713             case OP65_LDA:
1714                 if (E->AM == AM65_ZP) {
1715                     switch (GetKnownReg (E->Use & REG_ZP, In)) {
1716                         case REG_TMP1:
1717                             Arg = MakeHexArg (In->Tmp1);
1718                             X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI);
1719                             break;
1720
1721                         case REG_PTR1_LO:
1722                             Arg = MakeHexArg (In->Ptr1Lo);
1723                             X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI);
1724                             break;
1725
1726                         case REG_PTR1_HI:
1727                             Arg = MakeHexArg (In->Ptr1Hi);
1728                             X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI);
1729                             break;
1730
1731                         case REG_SREG_LO:
1732                             Arg = MakeHexArg (In->SRegLo);
1733                             X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI);
1734                             break;
1735
1736                         case REG_SREG_HI:
1737                             Arg = MakeHexArg (In->SRegHi);
1738                             X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI);
1739                             break;
1740                     }
1741                 }
1742                 break;
1743
1744             case OP65_LDX:
1745                 if (E->AM == AM65_ZP) {
1746                     switch (GetKnownReg (E->Use & REG_ZP, In)) {
1747                         case REG_TMP1:
1748                             Arg = MakeHexArg (In->Tmp1);
1749                             X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI);
1750                             break;
1751
1752                         case REG_PTR1_LO:
1753                             Arg = MakeHexArg (In->Ptr1Lo);
1754                             X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI);
1755                             break;
1756
1757                         case REG_PTR1_HI:
1758                             Arg = MakeHexArg (In->Ptr1Hi);
1759                             X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI);
1760                             break;
1761
1762                         case REG_SREG_LO:
1763                             Arg = MakeHexArg (In->SRegLo);
1764                             X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI);
1765                             break;
1766
1767                         case REG_SREG_HI:
1768                             Arg = MakeHexArg (In->SRegHi);
1769                             X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI);
1770                             break;
1771                     }
1772                 }
1773                 break;
1774
1775             case OP65_LDY:
1776                 if (E->AM == AM65_ZP) {
1777                     switch (GetKnownReg (E->Use, In)) {
1778                         case REG_TMP1:
1779                             Arg = MakeHexArg (In->Tmp1);
1780                             X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
1781                             break;
1782
1783                         case REG_PTR1_LO:
1784                             Arg = MakeHexArg (In->Ptr1Lo);
1785                             X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
1786                             break;
1787
1788                         case REG_PTR1_HI:
1789                             Arg = MakeHexArg (In->Ptr1Hi);
1790                             X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
1791                             break;
1792
1793                         case REG_SREG_LO:
1794                             Arg = MakeHexArg (In->SRegLo);
1795                             X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
1796                             break;
1797
1798                         case REG_SREG_HI:
1799                             Arg = MakeHexArg (In->SRegHi);
1800                             X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
1801                             break;
1802                     }
1803                 }
1804                 break;
1805
1806             case OP65_TAX:
1807                 if (E->RI->In.RegA >= 0) {
1808                     Arg = MakeHexArg (In->RegA);
1809                     X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI);
1810                 }
1811                 break;
1812
1813             case OP65_TAY:
1814                 if (E->RI->In.RegA >= 0) {
1815                     Arg = MakeHexArg (In->RegA);
1816                     X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
1817                 }
1818                 break;
1819
1820             case OP65_TXA:
1821                 if (E->RI->In.RegX >= 0) {
1822                     Arg = MakeHexArg (In->RegX);
1823                     X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI);
1824                 }
1825                 break;
1826
1827             case OP65_TYA:
1828                 if (E->RI->In.RegY >= 0) {
1829                     Arg = MakeHexArg (In->RegY);
1830                     X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI);
1831                 }
1832                 break;
1833
1834             default:
1835                 /* Avoid gcc warnings */
1836                 break;
1837
1838         }
1839
1840         /* Insert the replacement if we have one */
1841         if (X) {
1842             CS_InsertEntry (S, X, I+1);
1843             CS_DelEntry (S, I);
1844             ++Changes;
1845         }
1846
1847         /* Next entry */
1848         ++I;
1849
1850     }
1851
1852     /* Free register info */
1853     CS_FreeRegInfo (S);
1854
1855     /* Return the number of changes made */
1856     return Changes;
1857 }
1858
1859
1860
1861 /*****************************************************************************/
1862 /*                              struct OptFunc                               */
1863 /*****************************************************************************/
1864
1865
1866
1867 typedef struct OptFunc OptFunc;
1868 struct OptFunc {
1869     unsigned       (*Func) (CodeSeg*);  /* Optimizer function */
1870     const char*    Name;                /* Name of the function/group */
1871     unsigned       CodeSizeFactor;      /* Code size factor for this opt func */
1872     unsigned long  TotalRuns;           /* Total number of runs */
1873     unsigned long  LastRuns;            /* Last number of runs */
1874     unsigned long  TotalChanges;        /* Total number of changes */
1875     unsigned long  LastChanges;         /* Last number of changes */
1876     char           Disabled;            /* True if function disabled */
1877 };
1878
1879
1880
1881 /*****************************************************************************/
1882 /*                                   Code                                    */
1883 /*****************************************************************************/
1884
1885
1886
1887 /* A list of all the function descriptions */
1888 static OptFunc DOpt65C02BitOps  = { Opt65C02BitOps,  "Opt65C02BitOps",   66, 0, 0, 0, 0, 0 };
1889 static OptFunc DOpt65C02Ind     = { Opt65C02Ind,     "Opt65C02Ind",     100, 0, 0, 0, 0, 0 };
1890 static OptFunc DOpt65C02Stores  = { Opt65C02Stores,  "Opt65C02Stores",  100, 0, 0, 0, 0, 0 };
1891 static OptFunc DOptAdd1         = { OptAdd1,         "OptAdd1",         125, 0, 0, 0, 0, 0 };
1892 static OptFunc DOptAdd2         = { OptAdd2,         "OptAdd2",         200, 0, 0, 0, 0, 0 };
1893 static OptFunc DOptAdd3         = { OptAdd3,         "OptAdd3",          65, 0, 0, 0, 0, 0 };
1894 static OptFunc DOptAdd4         = { OptAdd4,         "OptAdd4",          90, 0, 0, 0, 0, 0 };
1895 static OptFunc DOptAdd5         = { OptAdd5,         "OptAdd5",         100, 0, 0, 0, 0, 0 };
1896 static OptFunc DOptAdd6         = { OptAdd6,         "OptAdd6",          40, 0, 0, 0, 0, 0 };
1897 static OptFunc DOptBoolTrans    = { OptBoolTrans,    "OptBoolTrans",    100, 0, 0, 0, 0, 0 };
1898 static OptFunc DOptBranchDist   = { OptBranchDist,   "OptBranchDist",     0, 0, 0, 0, 0, 0 };
1899 static OptFunc DOptCmp1         = { OptCmp1,         "OptCmp1",          42, 0, 0, 0, 0, 0 };
1900 static OptFunc DOptCmp2         = { OptCmp2,         "OptCmp2",          85, 0, 0, 0, 0, 0 };
1901 static OptFunc DOptCmp3         = { OptCmp3,         "OptCmp3",          75, 0, 0, 0, 0, 0 };
1902 static OptFunc DOptCmp4         = { OptCmp4,         "OptCmp4",          75, 0, 0, 0, 0, 0 };
1903 static OptFunc DOptCmp5         = { OptCmp5,         "OptCmp5",         100, 0, 0, 0, 0, 0 };
1904 static OptFunc DOptCmp6         = { OptCmp6,         "OptCmp6",         100, 0, 0, 0, 0, 0 };
1905 static OptFunc DOptCmp7         = { OptCmp7,         "OptCmp7",          85, 0, 0, 0, 0, 0 };
1906 static OptFunc DOptCmp8         = { OptCmp8,         "OptCmp8",          50, 0, 0, 0, 0, 0 };
1907 static OptFunc DOptCondBranches = { OptCondBranches, "OptCondBranches",  80, 0, 0, 0, 0, 0 };
1908 static OptFunc DOptDeadCode     = { OptDeadCode,     "OptDeadCode",     100, 0, 0, 0, 0, 0 };
1909 static OptFunc DOptDeadJumps    = { OptDeadJumps,    "OptDeadJumps",    100, 0, 0, 0, 0, 0 };
1910 static OptFunc DOptDecouple     = { OptDecouple,     "OptDecouple",     100, 0, 0, 0, 0, 0 };
1911 static OptFunc DOptDupLoads     = { OptDupLoads,     "OptDupLoads",       0, 0, 0, 0, 0, 0 };
1912 static OptFunc DOptJumpCascades = { OptJumpCascades, "OptJumpCascades", 100, 0, 0, 0, 0, 0 };
1913 static OptFunc DOptJumpTarget   = { OptJumpTarget,   "OptJumpTarget",   100, 0, 0, 0, 0, 0 };
1914 static OptFunc DOptLoad1        = { OptLoad1,        "OptLoad1",        100, 0, 0, 0, 0, 0 };
1915 static OptFunc DOptRTS          = { OptRTS,          "OptRTS",          100, 0, 0, 0, 0, 0 };
1916 static OptFunc DOptRTSJumps1    = { OptRTSJumps1,    "OptRTSJumps1",    100, 0, 0, 0, 0, 0 };
1917 static OptFunc DOptRTSJumps2    = { OptRTSJumps2,    "OptRTSJumps2",    100, 0, 0, 0, 0, 0 };
1918 static OptFunc DOptNegA1        = { OptNegA1,        "OptNegA1",        100, 0, 0, 0, 0, 0 };
1919 static OptFunc DOptNegA2        = { OptNegA2,        "OptNegA2",        100, 0, 0, 0, 0, 0 };
1920 static OptFunc DOptNegAX1       = { OptNegAX1,       "OptNegAX1",       100, 0, 0, 0, 0, 0 };
1921 static OptFunc DOptNegAX2       = { OptNegAX2,       "OptNegAX2",       100, 0, 0, 0, 0, 0 };
1922 static OptFunc DOptNegAX3       = { OptNegAX3,       "OptNegAX3",       100, 0, 0, 0, 0, 0 };
1923 static OptFunc DOptNegAX4       = { OptNegAX4,       "OptNegAX4",       100, 0, 0, 0, 0, 0 };
1924 static OptFunc DOptPrecalc      = { OptPrecalc,      "OptPrecalc",      100, 0, 0, 0, 0, 0 };
1925 static OptFunc DOptPtrLoad1     = { OptPtrLoad1,     "OptPtrLoad1",     100, 0, 0, 0, 0, 0 };
1926 static OptFunc DOptPtrLoad2     = { OptPtrLoad2,     "OptPtrLoad2",     100, 0, 0, 0, 0, 0 };
1927 static OptFunc DOptPtrLoad3     = { OptPtrLoad3,     "OptPtrLoad3",     100, 0, 0, 0, 0, 0 };
1928 static OptFunc DOptPtrLoad4     = { OptPtrLoad4,     "OptPtrLoad4",     100, 0, 0, 0, 0, 0 };
1929 static OptFunc DOptPtrLoad5     = { OptPtrLoad5,     "OptPtrLoad5",      92, 0, 0, 0, 0, 0 };
1930 static OptFunc DOptPtrLoad6     = { OptPtrLoad6,     "OptPtrLoad6",      50, 0, 0, 0, 0, 0 };
1931 static OptFunc DOptPtrLoad7     = { OptPtrLoad7,     "OptPtrLoad7",      65, 0, 0, 0, 0, 0 };
1932 static OptFunc DOptPtrLoad8     = { OptPtrLoad8,     "OptPtrLoad8",     108, 0, 0, 0, 0, 0 };
1933 static OptFunc DOptPtrLoad9     = { OptPtrLoad9,     "OptPtrLoad9",      86, 0, 0, 0, 0, 0 };
1934 static OptFunc DOptPtrLoad10    = { OptPtrLoad10,    "OptPtrLoad10",    100, 0, 0, 0, 0, 0 };
1935 static OptFunc DOptPtrStore1    = { OptPtrStore1,    "OptPtrStore1",    100, 0, 0, 0, 0, 0 };
1936 static OptFunc DOptPtrStore2    = { OptPtrStore2,    "OptPtrStore2",     40, 0, 0, 0, 0, 0 };
1937 static OptFunc DOptPush1        = { OptPush1,        "OptPush1",         65, 0, 0, 0, 0, 0 };
1938 static OptFunc DOptPush2        = { OptPush2,        "OptPush2",         50, 0, 0, 0, 0, 0 };
1939 static OptFunc DOptPushPop      = { OptPushPop,      "OptPushPop",        0, 0, 0, 0, 0, 0 };
1940 static OptFunc DOptShift1       = { OptShift1,       "OptShift1",       100, 0, 0, 0, 0, 0 };
1941 static OptFunc DOptShift2       = { OptShift2,       "OptShift2",       100, 0, 0, 0, 0, 0 };
1942 static OptFunc DOptShift3       = { OptShift3,       "OptShift3",       110, 0, 0, 0, 0, 0 };
1943 static OptFunc DOptSize1        = { OptSize1,        "OptSize1",        100, 0, 0, 0, 0, 0 };
1944 static OptFunc DOptSize2        = { OptSize2,        "OptSize2",        100, 0, 0, 0, 0, 0 };
1945 static OptFunc DOptStackOps     = { OptStackOps,     "OptStackOps",     100, 0, 0, 0, 0, 0 };
1946 static OptFunc DOptStore1       = { OptStore1,       "OptStore1",        70, 0, 0, 0, 0, 0 };
1947 static OptFunc DOptStore2       = { OptStore2,       "OptStore2",       220, 0, 0, 0, 0, 0 };
1948 static OptFunc DOptStore3       = { OptStore3,       "OptStore3",       120, 0, 0, 0, 0, 0 };
1949 static OptFunc DOptStore4       = { OptStore4,       "OptStore4",        50, 0, 0, 0, 0, 0 };
1950 static OptFunc DOptStore5       = { OptStore5,       "OptStore5",       100, 0, 0, 0, 0, 0 };
1951 static OptFunc DOptStoreLoad    = { OptStoreLoad,    "OptStoreLoad",      0, 0, 0, 0, 0, 0 };
1952 static OptFunc DOptSub1         = { OptSub1,         "OptSub1",         100, 0, 0, 0, 0, 0 };
1953 static OptFunc DOptSub2         = { OptSub2,         "OptSub2",         100, 0, 0, 0, 0, 0 };
1954 static OptFunc DOptSub3         = { OptSub3,         "OptSub3",         100, 0, 0, 0, 0, 0 };
1955 static OptFunc DOptTest1        = { OptTest1,        "OptTest1",        100, 0, 0, 0, 0, 0 };
1956 static OptFunc DOptTransfers1   = { OptTransfers1,   "OptTransfers1",     0, 0, 0, 0, 0, 0 };
1957 static OptFunc DOptTransfers2   = { OptTransfers2,   "OptTransfers2",    60, 0, 0, 0, 0, 0 };
1958 static OptFunc DOptUnusedLoads  = { OptUnusedLoads,  "OptUnusedLoads",    0, 0, 0, 0, 0, 0 };
1959 static OptFunc DOptUnusedStores = { OptUnusedStores, "OptUnusedStores",   0, 0, 0, 0, 0, 0 };
1960
1961
1962 /* Table containing all the steps in alphabetical order */
1963 static OptFunc* OptFuncs[] = {
1964     &DOpt65C02BitOps,
1965     &DOpt65C02Ind,
1966     &DOpt65C02Stores,
1967     &DOptAdd1,
1968     &DOptAdd2,
1969     &DOptAdd3,
1970     &DOptAdd4,
1971     &DOptAdd5,
1972     &DOptAdd6,
1973     &DOptBoolTrans,
1974     &DOptBranchDist,
1975     &DOptCmp1,
1976     &DOptCmp2,
1977     &DOptCmp3,
1978     &DOptCmp4,
1979     &DOptCmp5,
1980     &DOptCmp6,
1981     &DOptCmp7,
1982     &DOptCmp8,
1983     &DOptCondBranches,
1984     &DOptDeadCode,
1985     &DOptDeadJumps,
1986     &DOptDecouple,
1987     &DOptDupLoads,
1988     &DOptJumpCascades,
1989     &DOptJumpTarget,
1990     &DOptLoad1,
1991     &DOptNegA1,
1992     &DOptNegA2,
1993     &DOptNegAX1,
1994     &DOptNegAX2,
1995     &DOptNegAX3,
1996     &DOptNegAX4,
1997     &DOptPrecalc,
1998     &DOptPtrLoad1,
1999     &DOptPtrLoad10,
2000     &DOptPtrLoad2,
2001     &DOptPtrLoad3,
2002     &DOptPtrLoad4,
2003     &DOptPtrLoad5,
2004     &DOptPtrLoad6,
2005     &DOptPtrLoad7,
2006     &DOptPtrLoad8,
2007     &DOptPtrLoad9,
2008     &DOptPtrStore1,
2009     &DOptPtrStore2,
2010     &DOptPush1,
2011     &DOptPush2,
2012     &DOptPushPop,
2013     &DOptRTS,
2014     &DOptRTSJumps1,
2015     &DOptRTSJumps2,
2016     &DOptShift1,
2017     &DOptShift2,
2018     &DOptShift3,
2019     &DOptSize1,
2020     &DOptSize2,
2021     &DOptStackOps,
2022     &DOptStore1,
2023     &DOptStore2,
2024     &DOptStore3,
2025     &DOptStore4,
2026     &DOptStore5,
2027     &DOptStoreLoad,
2028     &DOptSub1,
2029     &DOptSub2,
2030     &DOptSub3,
2031     &DOptTest1,
2032     &DOptTransfers1,
2033     &DOptTransfers2,
2034     &DOptUnusedLoads,
2035     &DOptUnusedStores,
2036 };
2037 #define OPTFUNC_COUNT  (sizeof(OptFuncs) / sizeof(OptFuncs[0]))
2038
2039
2040
2041 static int CmpOptStep (const void* Key, const void* Func)
2042 /* Compare function for bsearch */
2043 {
2044     return strcmp (Key, (*(const OptFunc**)Func)->Name);
2045 }
2046
2047
2048
2049 static OptFunc* FindOptFunc (const char* Name)
2050 /* Find an optimizer step by name in the table and return a pointer. Return
2051  * NULL if no such step is found.
2052  */
2053 {
2054     /* Search for the function in the list */
2055     OptFunc** O = bsearch (Name, OptFuncs, OPTFUNC_COUNT, sizeof (OptFuncs[0]), CmpOptStep);
2056     return O? *O : 0;
2057 }
2058
2059
2060
2061 static OptFunc* GetOptFunc (const char* Name)
2062 /* Find an optimizer step by name in the table and return a pointer. Print an
2063  * error and call AbEnd if not found.
2064  */
2065 {
2066     /* Search for the function in the list */
2067     OptFunc* F = FindOptFunc (Name);
2068     if (F == 0) {
2069         /* Not found */
2070         AbEnd ("Optimization step `%s' not found", Name);
2071     }
2072     return F;
2073 }
2074
2075
2076
2077 void DisableOpt (const char* Name)
2078 /* Disable the optimization with the given name */
2079 {
2080     if (strcmp (Name, "any") == 0) {
2081         unsigned I;
2082         for (I = 0; I < OPTFUNC_COUNT; ++I) {
2083             OptFuncs[I]->Disabled = 1;
2084         }
2085     } else {
2086         GetOptFunc(Name)->Disabled = 1;
2087     }
2088 }
2089
2090
2091
2092 void EnableOpt (const char* Name)
2093 /* Enable the optimization with the given name */
2094 {
2095     if (strcmp (Name, "any") == 0) {
2096         unsigned I;
2097         for (I = 0; I < OPTFUNC_COUNT; ++I) {
2098             OptFuncs[I]->Disabled = 0;
2099         }
2100     } else {
2101         GetOptFunc(Name)->Disabled = 0;
2102     }
2103 }
2104
2105
2106
2107 void ListOptSteps (FILE* F)
2108 /* List all optimization steps */
2109 {
2110     unsigned I;
2111     for (I = 0; I < OPTFUNC_COUNT; ++I) {
2112         fprintf (F, "%s\n", OptFuncs[I]->Name);
2113     }
2114 }
2115
2116
2117
2118 static void ReadOptStats (const char* Name)
2119 /* Read the optimizer statistics file */
2120 {
2121     char Buf [256];
2122     unsigned Lines;
2123
2124     /* Try to open the file */
2125     FILE* F = fopen (Name, "r");
2126     if (F == 0) {
2127         /* Ignore the error */
2128         return;
2129     }
2130
2131     /* Read and parse the lines */
2132     Lines = 0;
2133     while (fgets (Buf, sizeof (Buf), F) != 0) {
2134
2135         char* B;
2136         unsigned Len;
2137         OptFunc* Func;
2138
2139         /* Fields */
2140         char Name[32];
2141         unsigned long  TotalRuns;
2142         unsigned long  TotalChanges;
2143
2144         /* Count lines */
2145         ++Lines;
2146
2147         /* Remove trailing white space including the line terminator */
2148         B = Buf;
2149         Len = strlen (B);
2150         while (Len > 0 && IsSpace (B[Len-1])) {
2151             --Len;
2152         }
2153         B[Len] = '\0';
2154
2155         /* Remove leading whitespace */
2156         while (IsSpace (*B)) {
2157             ++B;
2158         }
2159
2160         /* Check for empty and comment lines */
2161         if (*B == '\0' || *B == ';' || *B == '#') {
2162             continue;
2163         }
2164
2165         /* Parse the line */
2166         if (sscanf (B, "%31s %lu %*u %lu %*u", Name, &TotalRuns, &TotalChanges) != 3) {
2167             /* Syntax error */
2168             continue;
2169         }
2170
2171         /* Search for the optimizer step. */
2172         Func = FindOptFunc (Name);
2173         if (Func == 0) {
2174             /* Not found */
2175             continue;
2176         }
2177
2178         /* Found the step, set the fields */
2179         Func->TotalRuns    = TotalRuns;
2180         Func->TotalChanges = TotalChanges;
2181
2182     }
2183
2184     /* Close the file, ignore errors here. */
2185     fclose (F);
2186 }
2187
2188
2189
2190 static void WriteOptStats (const char* Name)
2191 /* Write the optimizer statistics file */
2192 {
2193     unsigned I;
2194
2195     /* Try to open the file */
2196     FILE* F = fopen (Name, "w");
2197     if (F == 0) {
2198         /* Ignore the error */
2199         return;
2200     }
2201
2202     /* Write a header */
2203     fprintf (F,
2204              "; Optimizer               Total      Last       Total      Last\n"
2205              ";   Step                  Runs       Runs        Chg       Chg\n");
2206
2207
2208     /* Write the data */
2209     for (I = 0; I < OPTFUNC_COUNT; ++I) {
2210         const OptFunc* O = OptFuncs[I];
2211         fprintf (F,
2212                  "%-20s %10lu %10lu %10lu %10lu\n",
2213                  O->Name,
2214                  O->TotalRuns,
2215                  O->LastRuns,
2216                  O->TotalChanges,
2217                  O->LastChanges);
2218     }
2219
2220     /* Close the file, ignore errors here. */
2221     fclose (F);
2222 }
2223
2224
2225
2226 static unsigned RunOptFunc (CodeSeg* S, OptFunc* F, unsigned Max)
2227 /* Run one optimizer function Max times or until there are no more changes */
2228 {
2229     unsigned Changes, C;
2230
2231     /* Don't run the function if it is disabled or if it is prohibited by the
2232      * code size factor
2233      */
2234     if (F->Disabled || F->CodeSizeFactor > S->CodeSizeFactor) {
2235         return 0;
2236     }
2237
2238     /* Run this until there are no more changes */
2239     Changes = 0;
2240     do {
2241
2242         /* Run the function */
2243         C = F->Func (S);
2244         Changes += C;
2245
2246         /* Do statistics */
2247         ++F->TotalRuns;
2248         ++F->LastRuns;
2249         F->TotalChanges += C;
2250         F->LastChanges  += C;
2251
2252     } while (--Max && C > 0);
2253
2254     /* Return the number of changes */
2255     return Changes;
2256 }
2257
2258
2259
2260 static unsigned RunOptGroup1 (CodeSeg* S)
2261 /* Run the first group of optimization steps. These steps translate known
2262  * patterns emitted by the code generator into more optimal patterns. Order
2263  * of the steps is important, because some of the steps done earlier cover
2264  * the same patterns as later steps as subpatterns.
2265  */
2266 {
2267     unsigned Changes = 0;
2268
2269     Changes += RunOptFunc (S, &DOptPtrStore1, 1);
2270     Changes += RunOptFunc (S, &DOptPtrStore2, 1);
2271     Changes += RunOptFunc (S, &DOptAdd3, 1);    /* Before OptPtrLoad5! */
2272     Changes += RunOptFunc (S, &DOptPtrLoad1, 1);
2273     Changes += RunOptFunc (S, &DOptPtrLoad2, 1);
2274     Changes += RunOptFunc (S, &DOptPtrLoad3, 1);
2275     Changes += RunOptFunc (S, &DOptPtrLoad4, 1);
2276     Changes += RunOptFunc (S, &DOptPtrLoad5, 1);
2277     Changes += RunOptFunc (S, &DOptPtrLoad6, 1);
2278     Changes += RunOptFunc (S, &DOptPtrLoad7, 1);
2279     Changes += RunOptFunc (S, &DOptPtrLoad8, 1);
2280     Changes += RunOptFunc (S, &DOptPtrLoad9, 1);
2281     Changes += RunOptFunc (S, &DOptNegAX1, 1);
2282     Changes += RunOptFunc (S, &DOptNegAX2, 1);
2283     Changes += RunOptFunc (S, &DOptNegAX3, 1);
2284     Changes += RunOptFunc (S, &DOptNegAX4, 1);
2285     Changes += RunOptFunc (S, &DOptAdd1, 1);
2286     Changes += RunOptFunc (S, &DOptAdd2, 1);
2287     Changes += RunOptFunc (S, &DOptAdd4, 1);
2288     Changes += RunOptFunc (S, &DOptStore4, 1);
2289     Changes += RunOptFunc (S, &DOptStore5, 1);
2290     Changes += RunOptFunc (S, &DOptShift1, 1);
2291     Changes += RunOptFunc (S, &DOptShift2, 1);
2292     Changes += RunOptFunc (S, &DOptShift3, 1);
2293     Changes += RunOptFunc (S, &DOptStore1, 1);
2294     Changes += RunOptFunc (S, &DOptStore2, 5);
2295     Changes += RunOptFunc (S, &DOptStore3, 5);
2296
2297     /* Return the number of changes */
2298     return Changes;
2299 }
2300
2301
2302
2303 static unsigned RunOptGroup2 (CodeSeg* S)
2304 /* Run one group of optimization steps. This step involves just decoupling
2305  * instructions by replacing them by instructions that do not depend on
2306  * previous instructions. This makes it easier to find instructions that
2307  * aren't used.
2308  */
2309 {
2310     unsigned Changes = 0;
2311
2312     Changes += RunOptFunc (S, &DOptDecouple, 1);
2313
2314     /* Return the number of changes */
2315     return Changes;
2316 }
2317
2318
2319
2320 static unsigned RunOptGroup3 (CodeSeg* S)
2321 /* Run one group of optimization steps. These steps depend on each other,
2322  * that means that one step may allow another step to do additional work,
2323  * so we will repeat the steps as long as we see any changes.
2324  */
2325 {
2326     unsigned Changes, C;
2327
2328     Changes = 0;
2329     do {
2330         C = 0;
2331
2332         C += RunOptFunc (S, &DOptPtrLoad10, 1);
2333         C += RunOptFunc (S, &DOptNegA1, 1);
2334         C += RunOptFunc (S, &DOptNegA2, 1);
2335         C += RunOptFunc (S, &DOptSub1, 1);
2336         C += RunOptFunc (S, &DOptSub2, 1);
2337         C += RunOptFunc (S, &DOptSub3, 1);
2338         C += RunOptFunc (S, &DOptAdd5, 1);
2339         C += RunOptFunc (S, &DOptAdd6, 1);
2340         C += RunOptFunc (S, &DOptStackOps, 1);
2341         C += RunOptFunc (S, &DOptJumpCascades, 1);
2342         C += RunOptFunc (S, &DOptDeadJumps, 1);
2343         C += RunOptFunc (S, &DOptRTS, 1);
2344         C += RunOptFunc (S, &DOptDeadCode, 1);
2345         C += RunOptFunc (S, &DOptJumpTarget, 1);
2346         C += RunOptFunc (S, &DOptCondBranches, 1);
2347         C += RunOptFunc (S, &DOptRTSJumps1, 1);
2348         C += RunOptFunc (S, &DOptBoolTrans, 1);
2349         C += RunOptFunc (S, &DOptCmp1, 1);
2350         C += RunOptFunc (S, &DOptCmp2, 1);
2351         C += RunOptFunc (S, &DOptCmp3, 1);
2352         C += RunOptFunc (S, &DOptCmp4, 1);
2353         C += RunOptFunc (S, &DOptCmp5, 1);
2354         C += RunOptFunc (S, &DOptCmp6, 1);
2355         C += RunOptFunc (S, &DOptCmp7, 1);
2356         C += RunOptFunc (S, &DOptCmp8, 1);
2357         C += RunOptFunc (S, &DOptTest1, 1);
2358         C += RunOptFunc (S, &DOptLoad1, 1);
2359         C += RunOptFunc (S, &DOptUnusedLoads, 1);
2360         C += RunOptFunc (S, &DOptUnusedStores, 1);
2361         C += RunOptFunc (S, &DOptDupLoads, 1);
2362         C += RunOptFunc (S, &DOptStoreLoad, 1);
2363         C += RunOptFunc (S, &DOptTransfers1, 1);
2364         C += RunOptFunc (S, &DOptStore1, 1);
2365         C += RunOptFunc (S, &DOptStore5, 1);
2366         C += RunOptFunc (S, &DOptPushPop, 1);
2367         C += RunOptFunc (S, &DOptPrecalc, 1);
2368
2369         Changes += C;
2370
2371     } while (C);
2372
2373     /* Return the number of changes */
2374     return Changes;
2375 }
2376
2377
2378
2379 static unsigned RunOptGroup4 (CodeSeg* S)
2380 /* 65C02 specific optimizations. */
2381 {
2382     unsigned Changes = 0;
2383
2384     if (CPUIsets[CPU] & CPU_ISET_65SC02) {
2385         Changes += RunOptFunc (S, &DOpt65C02BitOps, 1);
2386         Changes += RunOptFunc (S, &DOpt65C02Ind, 1);
2387         Changes += RunOptFunc (S, &DOpt65C02Stores, 1);
2388         if (Changes) {
2389             /* The 65C02 replacement codes do often make the use of a register
2390              * value unnecessary, so if we have changes, run another load
2391              * removal pass.
2392              */
2393             Changes += RunOptFunc (S, &DOptUnusedLoads, 1);
2394         }
2395     }
2396
2397     /* Return the number of changes */
2398     return Changes;
2399 }
2400
2401
2402
2403 static unsigned RunOptGroup5 (CodeSeg* S)
2404 /* Run another round of pattern replacements. These are done late, since there
2405  * may be better replacements before.
2406  */
2407 {
2408     unsigned Changes = 0;
2409
2410     Changes += RunOptFunc (S, &DOptPush1, 1);
2411     Changes += RunOptFunc (S, &DOptPush2, 1);
2412     Changes += RunOptFunc (S, &DOptUnusedLoads, 1);
2413     Changes += RunOptFunc (S, &DOptTransfers2, 1);
2414
2415     /* Return the number of changes */
2416     return Changes;
2417 }
2418
2419
2420
2421 static unsigned RunOptGroup6 (CodeSeg* S)
2422 /* The last group of optimization steps. Adjust branches, do size optimizations.
2423  */
2424 {
2425     unsigned Changes = 0;
2426     unsigned C;
2427
2428     if (S->CodeSizeFactor <= 100) {
2429         /* Optimize for size, that is replace operations by shorter ones, even
2430          * if this does hinder further optimizations (no problem since we're
2431          * done soon).
2432          */
2433         C = RunOptFunc (S, &DOptSize1, 1);
2434         if (C) {
2435             Changes += C;
2436             /* Run some optimization passes again, since the size optimizations
2437              * may have opened new oportunities.
2438              */
2439             Changes += RunOptFunc (S, &DOptUnusedLoads, 1);
2440             Changes += RunOptFunc (S, &DOptJumpTarget, 5);
2441             Changes += RunOptFunc (S, &DOptStore5, 1);
2442         }
2443     }
2444     C = RunOptFunc (S, &DOptSize2, 1);
2445     if (C) {
2446         Changes += C;
2447         /* Run some optimization passes again, since the size optimizations
2448          * may have opened new oportunities.
2449          */
2450         Changes += RunOptFunc (S, &DOptUnusedLoads, 1);
2451         Changes += RunOptFunc (S, &DOptJumpTarget, 5);
2452         Changes += RunOptFunc (S, &DOptStore5, 1);
2453     }
2454
2455     /* Adjust branch distances */
2456     Changes += RunOptFunc (S, &DOptBranchDist, 3);
2457
2458     /* Replace conditional branches to RTS. If we had changes, we must run dead
2459      * code elimination again, since the change may have introduced dead code.
2460      */
2461     C = RunOptFunc (S, &DOptRTSJumps2, 1);
2462     Changes += C;
2463     if (C) {
2464         Changes += RunOptFunc (S, &DOptDeadCode, 1);
2465     }
2466
2467     /* Return the number of changes */
2468     return Changes;
2469 }
2470
2471
2472
2473 void RunOpt (CodeSeg* S)
2474 /* Run the optimizer */
2475 {
2476     const char* StatFileName;
2477
2478     /* If we shouldn't run the optimizer, bail out */
2479     if (!S->Optimize) {
2480         return;
2481     }
2482
2483     /* Check if we are requested to write optimizer statistics */
2484     StatFileName = getenv ("CC65_OPTSTATS");
2485     if (StatFileName) {
2486         ReadOptStats (StatFileName);
2487     }
2488
2489     /* Print the name of the function we are working on */
2490     if (S->Func) {
2491         Print (stdout, 1, "Running optimizer for function `%s'\n", S->Func->Name);
2492     } else {
2493         Print (stdout, 1, "Running optimizer for global code segment\n");
2494     }
2495
2496     /* Run groups of optimizations */
2497     RunOptGroup1 (S);
2498     RunOptGroup2 (S);
2499     RunOptGroup3 (S);
2500     RunOptGroup4 (S);
2501     RunOptGroup5 (S);
2502     RunOptGroup6 (S);
2503
2504     /* Write statistics */
2505     if (StatFileName) {
2506         WriteOptStats (StatFileName);
2507     }
2508 }
2509
2510
2511