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