]> git.sur5r.net Git - cc65/blob - src/cc65/codeopt.c
bc33902279e5dd117b15effe9816fbe02db9dde0
[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
556             /* Create and insert the stores into the zp pointer if needed */
557             if (RegBank == 0) {
558                 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
559                 CS_InsertEntry (S, X, I+1);
560                 X = NewCodeEntry (OP65_STX, AM65_ZP, "ptr1+1", 0, L[0]->LI);
561                 CS_InsertEntry (S, X, I+2);
562             }
563
564             /* Delete more old code. Do it here to keep a label attached to
565              * entry I in place.
566              */
567             CS_DelEntry (S, I);         /* jsr pushax */
568
569             /* Remember, we had changes */
570             ++Changes;
571
572         }
573
574         /* Next entry */
575         ++I;
576
577     }
578
579     /* Return the number of changes made */
580     return Changes;
581 }
582
583
584
585 static unsigned OptPtrStore2 (CodeSeg* S)
586 /* Search for the sequence:
587  *
588  *      lda     #<(label+0)
589  *      ldx     #>(label+0)
590  *      clc
591  *      adc     xxx
592  *      bcc     L
593  *      inx
594  * L:   jsr     pushax
595  *      ldx     #$00
596  *      lda     yyy
597  *      ldy     #$00
598  *      jsr     staspidx
599  *
600  * and replace it by:
601  *
602  *      ldy     xxx
603  *      ldx     #$00
604  *      lda     yyy
605  *      sta     label,y
606  */
607 {
608     unsigned Changes = 0;
609
610     /* Walk over the entries */
611     unsigned I = 0;
612     while (I < CS_GetEntryCount (S)) {
613
614         CodeEntry* L[11];
615         unsigned Len;
616
617         /* Get next entry */
618         L[0] = CS_GetEntry (S, I);
619
620         /* Check for the sequence */
621         if (L[0]->OPC == OP65_LDA                            &&
622             L[0]->AM == AM65_IMM                             &&
623             CS_GetEntries (S, L+1, I+1, 10)                  &&
624             L[1]->OPC == OP65_LDX                            &&
625             L[1]->AM == AM65_IMM                             &&
626             L[2]->OPC == OP65_CLC                            &&
627             L[3]->OPC == OP65_ADC                            &&
628             (L[3]->AM == AM65_ABS || L[3]->AM == AM65_ZP)    &&
629             (L[4]->OPC == OP65_BCC || L[4]->OPC == OP65_JCC) &&
630             L[4]->JumpTo != 0                                &&
631             L[4]->JumpTo->Owner == L[6]                      &&
632             L[5]->OPC == OP65_INX                            &&
633             CE_IsCallTo (L[6], "pushax")                     &&
634             L[7]->OPC == OP65_LDX                            &&
635             L[8]->OPC == OP65_LDA                            &&
636             L[9]->OPC == OP65_LDY                            &&
637             CE_KnownImm (L[9])                               &&
638             L[9]->Num == 0                                   &&
639             CE_IsCallTo (L[10], "staspidx")                  &&
640             !CS_RangeHasLabel (S, I+1, 5)                    &&
641             !CS_RangeHasLabel (S, I+7, 4)                    &&
642             /* Check the label last because this is quite costly */
643             (Len = strlen (L[0]->Arg)) > 3                   &&
644             L[0]->Arg[0] == '<'                              &&
645             L[0]->Arg[1] == '('                              &&
646             strlen (L[1]->Arg) == Len                        &&
647             L[1]->Arg[0] == '>'                              &&
648             memcmp (L[0]->Arg+1, L[1]->Arg+1, Len-1) == 0) {
649
650             CodeEntry* X;
651             char* Label;
652
653             /* We will create all the new stuff behind the current one so
654              * we keep the line references.
655              */
656             X = NewCodeEntry (OP65_LDY, L[3]->AM, L[3]->Arg, 0, L[0]->LI);
657             CS_InsertEntry (S, X, I+11);
658
659             X = NewCodeEntry (OP65_LDX, L[7]->AM, L[7]->Arg, 0, L[7]->LI);
660             CS_InsertEntry (S, X, I+12);
661
662             X = NewCodeEntry (OP65_LDA, L[8]->AM, L[8]->Arg, 0, L[8]->LI);
663             CS_InsertEntry (S, X, I+13);
664
665             Label = memcpy (xmalloc (Len-2), L[0]->Arg+2, Len-3);
666             Label[Len-3] = '\0';
667             X = NewCodeEntry (OP65_STA, AM65_ABSY, Label, 0, L[10]->LI);
668             CS_InsertEntry (S, X, I+14);
669             xfree (Label);
670
671             /* Remove the old code */
672             CS_DelEntries (S, I, 11);
673
674             /* Remember, we had changes */
675             ++Changes;
676
677         }
678
679         /* Next entry */
680         ++I;
681
682     }
683
684     /* Return the number of changes made */
685     return Changes;
686 }
687
688
689
690 /*****************************************************************************/
691 /*                      Optimize loads through pointers                      */
692 /*****************************************************************************/
693
694
695
696 static unsigned OptPtrLoad1 (CodeSeg* S)
697 /* Search for the sequence:
698  *
699  *      clc
700  *      adc     xxx
701  *      tay
702  *      txa
703  *      adc     yyy
704  *      tax
705  *      tya
706  *      ldy
707  *      jsr     ldauidx
708  *
709  * and replace it by:
710  *
711  *      clc
712  *      adc     xxx
713  *      sta     ptr1
714  *      txa
715  *      adc     yyy
716  *      sta     ptr1+1
717  *      ldy
718  *      ldx     #$00
719  *      lda     (ptr1),y
720  */
721 {
722     unsigned Changes = 0;
723
724     /* Walk over the entries */
725     unsigned I = 0;
726     while (I < CS_GetEntryCount (S)) {
727
728         CodeEntry* L[9];
729
730         /* Get next entry */
731         L[0] = CS_GetEntry (S, I);
732
733         /* Check for the sequence */
734         if (L[0]->OPC == OP65_CLC               &&
735             CS_GetEntries (S, L+1, I+1, 8)      &&
736             L[1]->OPC == OP65_ADC               &&
737             L[2]->OPC == OP65_TAY               &&
738             L[3]->OPC == OP65_TXA               &&
739             L[4]->OPC == OP65_ADC               &&
740             L[5]->OPC == OP65_TAX               &&
741             L[6]->OPC == OP65_TYA               &&
742             L[7]->OPC == OP65_LDY               &&
743             CE_IsCallTo (L[8], "ldauidx")       &&
744             !CS_RangeHasLabel (S, I+1, 8)) {
745
746             CodeEntry* X;
747             CodeEntry* P;
748
749             /* Track the insertion point */
750             unsigned IP = I+2;
751
752             /* sta ptr1 */
753             X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[2]->LI);
754             CS_InsertEntry (S, X, IP++);
755
756             /* If the instruction before the clc is a ldx, replace the
757              * txa by an lda with the same location of the ldx. Otherwise
758              * transfer the value in X to A.
759              */
760             if ((P = CS_GetPrevEntry (S, I)) != 0 &&
761                 P->OPC == OP65_LDX                &&
762                 !CE_HasLabel (P)) {
763                 X = NewCodeEntry (OP65_LDA, P->AM, P->Arg, 0, P->LI);
764             } else {
765                 X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, L[3]->LI);
766             }
767             CS_InsertEntry (S, X, IP++);
768
769             /* adc yyy */
770             X = NewCodeEntry (OP65_ADC, L[4]->AM, L[4]->Arg, 0, L[4]->LI);
771             CS_InsertEntry (S, X, IP++);
772
773             /* sta ptr1+1 */
774             X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1+1", 0, L[5]->LI);
775             CS_InsertEntry (S, X, IP++);
776
777             /* ldy ... */
778             X = NewCodeEntry (OP65_LDY, L[7]->AM, L[7]->Arg, 0, L[7]->LI);
779             CS_InsertEntry (S, X, IP++);
780
781             /* ldx #$00 */
782             X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[8]->LI);
783             CS_InsertEntry (S, X, IP++);
784
785             /* lda (ptr1),y */
786             X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "ptr1", 0, L[8]->LI);
787             CS_InsertEntry (S, X, IP++);
788
789             /* Remove the old instructions */
790             CS_DelEntries (S, IP, 7);
791
792             /* Remember, we had changes */
793             ++Changes;
794
795         }
796
797         /* Next entry */
798         ++I;
799
800     }
801
802     /* Return the number of changes made */
803     return Changes;
804 }
805
806
807
808 static unsigned OptPtrLoad2 (CodeSeg* S)
809 /* Search for the sequence:
810  *
811  *      adc     xxx
812  *      pha
813  *      txa
814  *      iny
815  *      adc     yyy
816  *      tax
817  *      pla
818  *      ldy
819  *      jsr     ldauidx
820  *
821  * and replace it by:
822  *
823  *      adc     xxx
824  *      sta     ptr1
825  *      txa
826  *      iny
827  *      adc     yyy
828  *      sta     ptr1+1
829  *      ldy
830  *      ldx     #$00
831  *      lda     (ptr1),y
832  */
833 {
834     unsigned Changes = 0;
835
836     /* Walk over the entries */
837     unsigned I = 0;
838     while (I < CS_GetEntryCount (S)) {
839
840         CodeEntry* L[9];
841
842         /* Get next entry */
843         L[0] = CS_GetEntry (S, I);
844
845         /* Check for the sequence */
846         if (L[0]->OPC == OP65_ADC               &&
847             CS_GetEntries (S, L+1, I+1, 8)      &&
848             L[1]->OPC == OP65_PHA               &&
849             L[2]->OPC == OP65_TXA               &&
850             L[3]->OPC == OP65_INY               &&
851             L[4]->OPC == OP65_ADC               &&
852             L[5]->OPC == OP65_TAX               &&
853             L[6]->OPC == OP65_PLA               &&
854             L[7]->OPC == OP65_LDY               &&
855             CE_IsCallTo (L[8], "ldauidx")       &&
856             !CS_RangeHasLabel (S, I+1, 8)) {
857
858             CodeEntry* X;
859
860             /* Store the low byte and remove the PHA instead */
861             X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
862             CS_InsertEntry (S, X, I+1);
863
864             /* Store the high byte */
865             X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1+1", 0, L[4]->LI);
866             CS_InsertEntry (S, X, I+6);
867
868             /* Load high and low byte */
869             X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[6]->LI);
870             CS_InsertEntry (S, X, I+10);
871             X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "ptr1", 0, L[6]->LI);
872             CS_InsertEntry (S, X, I+11);
873
874             /* Delete the old code */
875             CS_DelEntry (S, I+12);      /* jsr ldauidx */
876             CS_DelEntry (S, I+8);       /* pla */
877             CS_DelEntry (S, I+7);       /* tax */
878             CS_DelEntry (S, I+2);       /* pha */
879
880             /* Remember, we had changes */
881             ++Changes;
882
883         }
884
885         /* Next entry */
886         ++I;
887
888     }
889
890     /* Return the number of changes made */
891     return Changes;
892 }
893
894
895
896 static unsigned OptPtrLoad3 (CodeSeg* S)
897 /* Search for the sequence:
898  *
899  *      lda     #<(label+0)
900  *      ldx     #>(label+0)
901  *      clc
902  *      adc     xxx
903  *      bcc     L
904  *      inx
905  * L:   ldy     #$00
906  *      jsr     ldauidx
907  *
908  * and replace it by:
909  *
910  *      ldy     xxx
911  *      ldx     #$00
912  *      lda     label,y
913  */
914 {
915     unsigned Changes = 0;
916
917     /* Walk over the entries */
918     unsigned I = 0;
919     while (I < CS_GetEntryCount (S)) {
920
921         CodeEntry* L[8];
922         unsigned Len;
923
924         /* Get next entry */
925         L[0] = CS_GetEntry (S, I);
926
927         /* Check for the sequence */
928         if (L[0]->OPC == OP65_LDA                            &&
929             L[0]->AM == AM65_IMM                             &&
930             CS_GetEntries (S, L+1, I+1, 7)                   &&
931             L[1]->OPC == OP65_LDX                            &&
932             L[1]->AM == AM65_IMM                             &&
933             L[2]->OPC == OP65_CLC                            &&
934             L[3]->OPC == OP65_ADC                            &&
935             (L[3]->AM == AM65_ABS || L[3]->AM == AM65_ZP)    &&
936             (L[4]->OPC == OP65_BCC || L[4]->OPC == OP65_JCC) &&
937             L[4]->JumpTo != 0                                &&
938             L[4]->JumpTo->Owner == L[6]                      &&
939             L[5]->OPC == OP65_INX                            &&
940             L[6]->OPC == OP65_LDY                            &&
941             CE_KnownImm (L[6])                               &&
942             L[6]->Num == 0                                   &&
943             CE_IsCallTo (L[7], "ldauidx")                    &&
944             !CS_RangeHasLabel (S, I+1, 5)                    &&
945             !CE_HasLabel (L[7])                              &&
946             /* Check the label last because this is quite costly */
947             (Len = strlen (L[0]->Arg)) > 3                   &&
948             L[0]->Arg[0] == '<'                              &&
949             L[0]->Arg[1] == '('                              &&
950             strlen (L[1]->Arg) == Len                        &&
951             L[1]->Arg[0] == '>'                              &&
952             memcmp (L[0]->Arg+1, L[1]->Arg+1, Len-1) == 0) {
953
954             CodeEntry* X;
955             char* Label;
956
957             /* We will create all the new stuff behind the current one so
958              * we keep the line references.
959              */
960             X = NewCodeEntry (OP65_LDY, L[3]->AM, L[3]->Arg, 0, L[0]->LI);
961             CS_InsertEntry (S, X, I+8);
962
963             X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[0]->LI);
964             CS_InsertEntry (S, X, I+9);
965
966             Label = memcpy (xmalloc (Len-2), L[0]->Arg+2, Len-3);
967             Label[Len-3] = '\0';
968             X = NewCodeEntry (OP65_LDA, AM65_ABSY, Label, 0, L[0]->LI);
969             CS_InsertEntry (S, X, I+10);
970             xfree (Label);
971
972             /* Remove the old code */
973             CS_DelEntries (S, I, 8);
974
975             /* Remember, we had changes */
976             ++Changes;
977
978         }
979
980         /* Next entry */
981         ++I;
982
983     }
984
985     /* Return the number of changes made */
986     return Changes;
987 }
988
989
990
991 static unsigned OptPtrLoad4 (CodeSeg* S)
992 /* Search for the sequence:
993  *
994  *      lda     #<(label+0)
995  *      ldx     #>(label+0)
996  *      ldy     #$xx
997  *      clc
998  *      adc     (sp),y
999  *      bcc     L
1000  *      inx
1001  * L:   ldy     #$00
1002  *      jsr     ldauidx
1003  *
1004  * and replace it by:
1005  *
1006  *      ldy     #$xx
1007  *      lda     (sp),y
1008  *      tay
1009  *      ldx     #$00
1010  *      lda     label,y
1011  */
1012 {
1013     unsigned Changes = 0;
1014
1015     /* Walk over the entries */
1016     unsigned I = 0;
1017     while (I < CS_GetEntryCount (S)) {
1018
1019         CodeEntry* L[9];
1020         unsigned Len;
1021
1022         /* Get next entry */
1023         L[0] = CS_GetEntry (S, I);
1024
1025         /* Check for the sequence */
1026         if (L[0]->OPC == OP65_LDA                            &&
1027             L[0]->AM == AM65_IMM                             &&
1028             CS_GetEntries (S, L+1, I+1, 8)                   &&
1029             L[1]->OPC == OP65_LDX                            &&
1030             L[1]->AM == AM65_IMM                             &&
1031             !CE_HasLabel (L[1])                              &&
1032             L[2]->OPC == OP65_LDY                            &&
1033             CE_KnownImm (L[2])                               &&
1034             !CE_HasLabel (L[2])                              &&
1035             L[3]->OPC == OP65_CLC                            &&
1036             !CE_HasLabel (L[3])                              &&
1037             L[4]->OPC == OP65_ADC                            &&
1038             L[4]->AM == AM65_ZP_INDY                         &&
1039             !CE_HasLabel (L[4])                              &&
1040             (L[5]->OPC == OP65_BCC || L[5]->OPC == OP65_JCC) &&
1041             L[5]->JumpTo != 0                                &&
1042             L[5]->JumpTo->Owner == L[7]                      &&
1043             !CE_HasLabel (L[5])                              &&
1044             L[6]->OPC == OP65_INX                            &&
1045             !CE_HasLabel (L[6])                              &&
1046             L[7]->OPC == OP65_LDY                            &&
1047             CE_KnownImm (L[7])                               &&
1048             L[7]->Num == 0                                   &&
1049             CE_IsCallTo (L[8], "ldauidx")                    &&
1050             !CE_HasLabel (L[8])                              &&
1051             /* Check the label last because this is quite costly */
1052             (Len = strlen (L[0]->Arg)) > 3                   &&
1053             L[0]->Arg[0] == '<'                              &&
1054             L[0]->Arg[1] == '('                              &&
1055             strlen (L[1]->Arg) == Len                        &&
1056             L[1]->Arg[0] == '>'                              &&
1057             memcmp (L[0]->Arg+1, L[1]->Arg+1, Len-1) == 0) {
1058
1059             CodeEntry* X;
1060             char* Label;
1061
1062             /* Add the lda */
1063             X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, L[4]->Arg, 0, L[0]->LI);
1064             CS_InsertEntry (S, X, I+3);
1065
1066             /* Add the tay */
1067             X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, L[0]->LI);
1068             CS_InsertEntry (S, X, I+4);
1069
1070             /* Add the ldx */
1071             X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[0]->LI);
1072             CS_InsertEntry (S, X, I+5);
1073
1074             /* Add the lda */
1075             Label = memcpy (xmalloc (Len-2), L[0]->Arg+2, Len-3);
1076             Label[Len-3] = '\0';
1077             X = NewCodeEntry (OP65_LDA, AM65_ABSY, Label, 0, L[0]->LI);
1078             CS_InsertEntry (S, X, I+6);
1079             xfree (Label);
1080
1081             /* Remove the old code */
1082             CS_DelEntries (S, I, 2);
1083             CS_DelEntries (S, I+5, 6);
1084
1085             /* Remember, we had changes */
1086             ++Changes;
1087
1088         }
1089
1090         /* Next entry */
1091         ++I;
1092
1093     }
1094
1095     /* Return the number of changes made */
1096     return Changes;
1097 }
1098
1099
1100
1101 static unsigned OptPtrLoad5 (CodeSeg* S)
1102 /* Search for the sequence:
1103  *
1104  *      lda     regbank+n
1105  *      ldx     regbank+n+1
1106  *      sta     regsave
1107  *      stx     regsave+1
1108  *      clc
1109  *      adc     #$01
1110  *      bcc     L0005
1111  *      inx
1112  * L:   sta     regbank+n
1113  *      stx     regbank+n+1
1114  *      lda     regsave
1115  *      ldx     regsave+1
1116  *      ldy     #$00
1117  *      jsr     ldauidx
1118  *
1119  * and replace it by:
1120  *
1121  *      ldy     #$00
1122  *      ldx     #$00
1123  *      lda     (regbank+n),y
1124  *      inc     regbank+n
1125  *      bne     L1
1126  *      inc     regbank+n+1
1127  * L1:  tay                     <- only if flags are used
1128  *
1129  * This function must execute before OptPtrLoad5!
1130  *
1131  */
1132 {
1133     unsigned Changes = 0;
1134
1135     /* Walk over the entries */
1136     unsigned I = 0;
1137     while (I < CS_GetEntryCount (S)) {
1138
1139         CodeEntry* L[15];
1140         unsigned Len;
1141
1142         /* Get next entry */
1143         L[0] = CS_GetEntry (S, I);
1144
1145         /* Check for the sequence */
1146         if (L[0]->OPC == OP65_LDA                               &&
1147             L[0]->AM == AM65_ZP                                 &&
1148             strncmp (L[0]->Arg, "regbank+", 8) == 0             &&
1149             (Len = strlen (L[0]->Arg)) > 0                      &&
1150             CS_GetEntries (S, L+1, I+1, 14)                     &&
1151             !CS_RangeHasLabel (S, I+1, 7)                       &&
1152             !CS_RangeHasLabel (S, I+9, 5)                       &&
1153             L[1]->OPC == OP65_LDX                               &&
1154             L[1]->AM == AM65_ZP                                 &&
1155             strncmp (L[1]->Arg, L[0]->Arg, Len) == 0            &&
1156             strcmp (L[1]->Arg+Len, "+1") == 0                   &&
1157             L[2]->OPC == OP65_STA                               &&
1158             L[2]->AM == AM65_ZP                                 &&
1159             strcmp (L[2]->Arg, "regsave") == 0                  &&
1160             L[3]->OPC == OP65_STX                               &&
1161             L[3]->AM == AM65_ZP                                 &&
1162             strcmp (L[3]->Arg, "regsave+1") == 0                &&
1163             L[4]->OPC == OP65_CLC                               &&
1164             L[5]->OPC == OP65_ADC                               &&
1165             CE_KnownImm (L[5])                                  &&
1166             L[5]->Num == 1                                      &&
1167             L[6]->OPC == OP65_BCC                               &&
1168             L[6]->JumpTo != 0                                   &&
1169             L[6]->JumpTo->Owner == L[8]                         &&
1170             L[7]->OPC == OP65_INX                               &&
1171             L[8]->OPC == OP65_STA                               &&
1172             L[8]->AM == AM65_ZP                                 &&
1173             strcmp (L[8]->Arg, L[0]->Arg) == 0                  &&
1174             L[9]->OPC == OP65_STX                               &&
1175             L[9]->AM == AM65_ZP                                 &&
1176             strcmp (L[9]->Arg, L[1]->Arg) == 0                  &&
1177             L[10]->OPC == OP65_LDA                              &&
1178             L[10]->AM == AM65_ZP                                &&
1179             strcmp (L[10]->Arg, "regsave") == 0                 &&
1180             L[11]->OPC == OP65_LDX                              &&
1181             L[11]->AM == AM65_ZP                                &&
1182             strcmp (L[11]->Arg, "regsave+1") == 0               &&
1183             L[12]->OPC == OP65_LDY                              &&
1184             CE_KnownImm (L[12])                                 &&
1185             CE_IsCallTo (L[13], "ldauidx")) {
1186
1187             CodeEntry* X;
1188             CodeLabel* Label;
1189
1190             /* Check if the instruction following the sequence uses the flags
1191              * set by the load. If so, insert a test of the value in the
1192              * accumulator.
1193              */
1194             if (CE_UseLoadFlags (L[14])) {
1195                 X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, L[13]->LI);
1196                 CS_InsertEntry (S, X, I+14);
1197             }
1198
1199             /* Attach a label to L[14]. This may be either the just inserted
1200              * instruction, or the one following the sequence.
1201              */
1202             Label = CS_GenLabel (S, L[14]);
1203
1204             /* ldy #$xx */
1205             X = NewCodeEntry (OP65_LDY, AM65_IMM, L[12]->Arg, 0, L[12]->LI);
1206             CS_InsertEntry (S, X, I+14);
1207
1208             /* ldx #$xx */
1209             X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[13]->LI);
1210             CS_InsertEntry (S, X, I+15);
1211
1212             /* lda (regbank+n),y */
1213             X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, L[0]->Arg, 0, L[13]->LI);
1214             CS_InsertEntry (S, X, I+16);
1215
1216             /* inc regbank+n */
1217             X = NewCodeEntry (OP65_INC, AM65_ZP, L[0]->Arg, 0, L[5]->LI);
1218             CS_InsertEntry (S, X, I+17);
1219
1220             /* bne ... */
1221             X = NewCodeEntry (OP65_BNE, AM65_BRA, Label->Name, Label, L[6]->LI);
1222             CS_InsertEntry (S, X, I+18);
1223
1224             /* inc regbank+n+1 */
1225             X = NewCodeEntry (OP65_INC, AM65_ZP, L[1]->Arg, 0, L[7]->LI);
1226             CS_InsertEntry (S, X, I+19);
1227
1228             /* Delete the old code */
1229             CS_DelEntries (S, I, 14);
1230
1231             /* Remember, we had changes */
1232             ++Changes;
1233
1234         }
1235
1236         /* Next entry */
1237         ++I;
1238
1239     }
1240
1241     /* Return the number of changes made */
1242     return Changes;
1243 }
1244
1245
1246
1247 static unsigned OptPtrLoad6 (CodeSeg* S)
1248 /* Search for the sequence:
1249  *
1250  *      lda     zp
1251  *      ldx     zp+1
1252  *      ldy     xx
1253  *      jsr     ldauidx
1254  *
1255  * and replace it by:
1256  *
1257  *      ldy     xx
1258  *      ldx     #$00
1259  *      lda     (zp),y
1260  */
1261 {
1262     unsigned Changes = 0;
1263
1264     /* Walk over the entries */
1265     unsigned I = 0;
1266     while (I < CS_GetEntryCount (S)) {
1267
1268         CodeEntry* L[4];
1269         unsigned Len;
1270
1271         /* Get next entry */
1272         L[0] = CS_GetEntry (S, I);
1273
1274         /* Check for the sequence */
1275         if (L[0]->OPC == OP65_LDA && L[0]->AM == AM65_ZP        &&
1276             CS_GetEntries (S, L+1, I+1, 3)                      &&
1277             !CS_RangeHasLabel (S, I+1, 3)                       &&
1278             L[1]->OPC == OP65_LDX && L[1]->AM == AM65_ZP        &&
1279             (Len = strlen (L[0]->Arg)) > 0                      &&
1280             strncmp (L[0]->Arg, L[1]->Arg, Len) == 0            &&
1281             strcmp (L[1]->Arg + Len, "+1") == 0                 &&
1282             L[2]->OPC == OP65_LDY                               &&
1283             CE_IsCallTo (L[3], "ldauidx")) {
1284
1285             CodeEntry* X;
1286
1287             /* ldx #$00 */
1288             X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[3]->LI);
1289             CS_InsertEntry (S, X, I+3);
1290
1291             /* lda (zp),y */
1292             X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, L[0]->Arg, 0, L[3]->LI);
1293             CS_InsertEntry (S, X, I+4);
1294
1295             /* Remove the old code */
1296             CS_DelEntry (S, I+5);
1297             CS_DelEntries (S, I, 2);
1298
1299             /* Remember, we had changes */
1300             ++Changes;
1301
1302         }
1303
1304         /* Next entry */
1305         ++I;
1306
1307     }
1308
1309     /* Return the number of changes made */
1310     return Changes;
1311 }
1312
1313
1314
1315 static unsigned OptPtrLoad7 (CodeSeg* S)
1316 /* Search for the sequence:
1317  *
1318  *      lda     zp
1319  *      ldx     zp+1
1320  *      ldy     xx
1321  *      jsr     ldaxidx
1322  *
1323  * and replace it by:
1324  *
1325  *      ldy     xx
1326  *      lda     (zp),y
1327  *      tax
1328  *      dey
1329  *      lda     (zp),y
1330  */
1331 {
1332     unsigned Changes = 0;
1333
1334     /* Walk over the entries */
1335     unsigned I = 0;
1336     while (I < CS_GetEntryCount (S)) {
1337
1338         CodeEntry* L[4];
1339         unsigned Len;
1340
1341         /* Get next entry */
1342         L[0] = CS_GetEntry (S, I);
1343
1344         /* Check for the sequence */
1345         if (L[0]->OPC == OP65_LDA && L[0]->AM == AM65_ZP        &&
1346             CS_GetEntries (S, L+1, I+1, 3)                      &&
1347             !CS_RangeHasLabel (S, I+1, 3)                       &&
1348             L[1]->OPC == OP65_LDX && L[1]->AM == AM65_ZP        &&
1349             (Len = strlen (L[0]->Arg)) > 0                      &&
1350             strncmp (L[0]->Arg, L[1]->Arg, Len) == 0            &&
1351             strcmp (L[1]->Arg + Len, "+1") == 0                 &&
1352             L[2]->OPC == OP65_LDY                               &&
1353             CE_IsCallTo (L[3], "ldaxidx")) {
1354
1355             CodeEntry* X;
1356
1357             /* lda (zp),y */
1358             X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, L[0]->Arg, 0, L[3]->LI);
1359             CS_InsertEntry (S, X, I+4);
1360
1361             /* tax */
1362             X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, L[3]->LI);
1363             CS_InsertEntry (S, X, I+5);
1364
1365             /* dey */
1366             X = NewCodeEntry (OP65_DEY, AM65_IMP, 0, 0, L[3]->LI);
1367             CS_InsertEntry (S, X, I+6);
1368
1369             /* lda (zp),y */
1370             X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, L[0]->Arg, 0, L[3]->LI);
1371             CS_InsertEntry (S, X, I+7);
1372
1373             /* Remove the old code */
1374             CS_DelEntry (S, I+3);
1375             CS_DelEntries (S, I, 2);
1376
1377             /* Remember, we had changes */
1378             ++Changes;
1379
1380         }
1381
1382         /* Next entry */
1383         ++I;
1384
1385     }
1386
1387     /* Return the number of changes made */
1388     return Changes;
1389 }
1390
1391
1392
1393 static unsigned OptPtrLoad8 (CodeSeg* S)
1394 /* Search for the sequence
1395  *
1396  *      ldy     ...
1397  *      jsr     ldauidx
1398  *
1399  * and replace it by:
1400  *
1401  *      ldy     ...
1402  *      stx     ptr1+1
1403  *      sta     ptr1
1404  *      ldx     #$00
1405  *      lda     (ptr1),y
1406  *
1407  * This step must be executed *after* OptPtrLoad1!
1408  */
1409 {
1410     unsigned Changes = 0;
1411
1412     /* Walk over the entries */
1413     unsigned I = 0;
1414     while (I < CS_GetEntryCount (S)) {
1415
1416         CodeEntry* L[2];
1417
1418         /* Get next entry */
1419         L[0] = CS_GetEntry (S, I);
1420
1421         /* Check for the sequence */
1422         if (L[0]->OPC == OP65_LDY               &&
1423             CS_GetEntries (S, L+1, I+1, 1)      &&
1424             CE_IsCallTo (L[1], "ldauidx")       &&
1425             !CE_HasLabel (L[1])) {
1426
1427             CodeEntry* X;
1428
1429             /* Store the high byte */
1430             X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
1431             CS_InsertEntry (S, X, I+1);
1432
1433             /* Store the low byte */
1434             X = NewCodeEntry (OP65_STX, AM65_ZP, "ptr1+1", 0, L[0]->LI);
1435             CS_InsertEntry (S, X, I+2);
1436
1437             /* Delete the call to ldauidx */
1438             CS_DelEntry (S, I+3);
1439
1440             /* Load the high and low byte */
1441             X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[0]->LI);
1442             CS_InsertEntry (S, X, I+3);
1443             X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "ptr1", 0, L[0]->LI);
1444             CS_InsertEntry (S, X, I+4);
1445
1446             /* Remember, we had changes */
1447             ++Changes;
1448
1449         }
1450
1451         /* Next entry */
1452         ++I;
1453
1454     }
1455
1456     /* Return the number of changes made */
1457     return Changes;
1458 }
1459
1460
1461
1462 /*****************************************************************************/
1463 /*                            Decouple operations                            */
1464 /*****************************************************************************/
1465
1466
1467
1468 static unsigned OptDecouple (CodeSeg* S)
1469 /* Decouple operations, that is, do the following replacements:
1470  *
1471  *   dex        -> ldx #imm
1472  *   inx        -> ldx #imm
1473  *   dey        -> ldy #imm
1474  *   iny        -> ldy #imm
1475  *   tax        -> ldx #imm
1476  *   txa        -> lda #imm
1477  *   tay        -> ldy #imm
1478  *   tya        -> lda #imm
1479  *   lda zp     -> lda #imm
1480  *   ldx zp     -> ldx #imm
1481  *   ldy zp     -> ldy #imm
1482  *
1483  * Provided that the register values are known of course.
1484  */
1485 {
1486     unsigned Changes = 0;
1487     unsigned I;
1488
1489     /* Generate register info for the following step */
1490     CS_GenRegInfo (S);
1491
1492     /* Walk over the entries */
1493     I = 0;
1494     while (I < CS_GetEntryCount (S)) {
1495
1496         const char* Arg;
1497
1498         /* Get next entry and it's input register values */
1499         CodeEntry* E = CS_GetEntry (S, I);
1500         const RegContents* In = &E->RI->In;
1501
1502         /* Assume we have no replacement */
1503         CodeEntry* X = 0;
1504
1505         /* Check the instruction */
1506         switch (E->OPC) {
1507
1508             case OP65_DEA:
1509                 if (RegValIsKnown (In->RegA)) {
1510                     Arg = MakeHexArg ((In->RegA - 1) & 0xFF);
1511                     X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI);
1512                 }
1513                 break;
1514
1515             case OP65_DEX:
1516                 if (RegValIsKnown (In->RegX)) {
1517                     Arg = MakeHexArg ((In->RegX - 1) & 0xFF);
1518                     X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI);
1519                 }
1520                 break;
1521
1522             case OP65_DEY:
1523                 if (RegValIsKnown (In->RegY)) {
1524                     Arg = MakeHexArg ((In->RegY - 1) & 0xFF);
1525                     X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
1526                 }
1527                 break;
1528
1529             case OP65_INA:
1530                 if (RegValIsKnown (In->RegA)) {
1531                     Arg = MakeHexArg ((In->RegA + 1) & 0xFF);
1532                     X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI);
1533                 }
1534                 break;
1535
1536             case OP65_INX:
1537                 if (RegValIsKnown (In->RegX)) {
1538                     Arg = MakeHexArg ((In->RegX + 1) & 0xFF);
1539                     X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI);
1540                 }
1541                 break;
1542
1543             case OP65_INY:
1544                 if (RegValIsKnown (In->RegY)) {
1545                     Arg = MakeHexArg ((In->RegY + 1) & 0xFF);
1546                     X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
1547                 }
1548                 break;
1549
1550             case OP65_LDA:
1551                 if (E->AM == AM65_ZP) {
1552                     switch (GetKnownReg (E->Use & REG_ZP, In)) {
1553                         case REG_TMP1:
1554                             Arg = MakeHexArg (In->Tmp1);
1555                             X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI);
1556                             break;
1557
1558                         case REG_PTR1_LO:
1559                             Arg = MakeHexArg (In->Ptr1Lo);
1560                             X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI);
1561                             break;
1562
1563                         case REG_PTR1_HI:
1564                             Arg = MakeHexArg (In->Ptr1Hi);
1565                             X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI);
1566                             break;
1567
1568                         case REG_SREG_LO:
1569                             Arg = MakeHexArg (In->SRegLo);
1570                             X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI);
1571                             break;
1572
1573                         case REG_SREG_HI:
1574                             Arg = MakeHexArg (In->SRegHi);
1575                             X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI);
1576                             break;
1577                     }
1578                 }
1579                 break;
1580
1581             case OP65_LDX:
1582                 if (E->AM == AM65_ZP) {
1583                     switch (GetKnownReg (E->Use & REG_ZP, In)) {
1584                         case REG_TMP1:
1585                             Arg = MakeHexArg (In->Tmp1);
1586                             X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI);
1587                             break;
1588
1589                         case REG_PTR1_LO:
1590                             Arg = MakeHexArg (In->Ptr1Lo);
1591                             X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI);
1592                             break;
1593
1594                         case REG_PTR1_HI:
1595                             Arg = MakeHexArg (In->Ptr1Hi);
1596                             X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI);
1597                             break;
1598
1599                         case REG_SREG_LO:
1600                             Arg = MakeHexArg (In->SRegLo);
1601                             X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI);
1602                             break;
1603
1604                         case REG_SREG_HI:
1605                             Arg = MakeHexArg (In->SRegHi);
1606                             X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI);
1607                             break;
1608                     }
1609                 }
1610                 break;
1611
1612             case OP65_LDY:
1613                 if (E->AM == AM65_ZP) {
1614                     switch (GetKnownReg (E->Use, In)) {
1615                         case REG_TMP1:
1616                             Arg = MakeHexArg (In->Tmp1);
1617                             X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
1618                             break;
1619
1620                         case REG_PTR1_LO:
1621                             Arg = MakeHexArg (In->Ptr1Lo);
1622                             X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
1623                             break;
1624
1625                         case REG_PTR1_HI:
1626                             Arg = MakeHexArg (In->Ptr1Hi);
1627                             X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
1628                             break;
1629
1630                         case REG_SREG_LO:
1631                             Arg = MakeHexArg (In->SRegLo);
1632                             X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
1633                             break;
1634
1635                         case REG_SREG_HI:
1636                             Arg = MakeHexArg (In->SRegHi);
1637                             X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
1638                             break;
1639                     }
1640                 }
1641                 break;
1642
1643             case OP65_TAX:
1644                 if (E->RI->In.RegA >= 0) {
1645                     Arg = MakeHexArg (In->RegA);
1646                     X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI);
1647                 }
1648                 break;
1649
1650             case OP65_TAY:
1651                 if (E->RI->In.RegA >= 0) {
1652                     Arg = MakeHexArg (In->RegA);
1653                     X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
1654                 }
1655                 break;
1656
1657             case OP65_TXA:
1658                 if (E->RI->In.RegX >= 0) {
1659                     Arg = MakeHexArg (In->RegX);
1660                     X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI);
1661                 }
1662                 break;
1663
1664             case OP65_TYA:
1665                 if (E->RI->In.RegY >= 0) {
1666                     Arg = MakeHexArg (In->RegY);
1667                     X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI);
1668                 }
1669                 break;
1670
1671             default:
1672                 /* Avoid gcc warnings */
1673                 break;
1674
1675         }
1676
1677         /* Insert the replacement if we have one */
1678         if (X) {
1679             CS_InsertEntry (S, X, I+1);
1680             CS_DelEntry (S, I);
1681             ++Changes;
1682         }
1683
1684         /* Next entry */
1685         ++I;
1686
1687     }
1688
1689     /* Free register info */
1690     CS_FreeRegInfo (S);
1691
1692     /* Return the number of changes made */
1693     return Changes;
1694 }
1695
1696
1697
1698 /*****************************************************************************/
1699 /*                              struct OptFunc                               */
1700 /*****************************************************************************/
1701
1702
1703
1704 typedef struct OptFunc OptFunc;
1705 struct OptFunc {
1706     unsigned       (*Func) (CodeSeg*);  /* Optimizer function */
1707     const char*    Name;                /* Name of the function/group */
1708     unsigned       CodeSizeFactor;      /* Code size factor for this opt func */
1709     unsigned long  TotalRuns;           /* Total number of runs */
1710     unsigned long  LastRuns;            /* Last number of runs */
1711     unsigned long  TotalChanges;        /* Total number of changes */
1712     unsigned long  LastChanges;         /* Last number of changes */
1713     char           Disabled;            /* True if function disabled */
1714 };
1715
1716
1717
1718 /*****************************************************************************/
1719 /*                                   Code                                    */
1720 /*****************************************************************************/
1721
1722
1723
1724 /* A list of all the function descriptions */
1725 static OptFunc DOpt65C02BitOps  = { Opt65C02BitOps,  "Opt65C02BitOps",   66, 0, 0, 0, 0, 0 };
1726 static OptFunc DOpt65C02Ind     = { Opt65C02Ind,     "Opt65C02Ind",     100, 0, 0, 0, 0, 0 };
1727 static OptFunc DOpt65C02Stores  = { Opt65C02Stores,  "Opt65C02Stores",  100, 0, 0, 0, 0, 0 };
1728 static OptFunc DOptAdd1         = { OptAdd1,         "OptAdd1",         125, 0, 0, 0, 0, 0 };
1729 static OptFunc DOptAdd2         = { OptAdd2,         "OptAdd2",         200, 0, 0, 0, 0, 0 };
1730 static OptFunc DOptAdd3         = { OptAdd3,         "OptAdd3",          90, 0, 0, 0, 0, 0 };
1731 static OptFunc DOptAdd4         = { OptAdd4,         "OptAdd4",         100, 0, 0, 0, 0, 0 };
1732 static OptFunc DOptAdd5         = { OptAdd5,         "OptAdd5",          40, 0, 0, 0, 0, 0 };
1733 static OptFunc DOptBoolTrans    = { OptBoolTrans,    "OptBoolTrans",    100, 0, 0, 0, 0, 0 };
1734 static OptFunc DOptBranchDist   = { OptBranchDist,   "OptBranchDist",     0, 0, 0, 0, 0, 0 };
1735 static OptFunc DOptCmp1         = { OptCmp1,         "OptCmp1",          85, 0, 0, 0, 0, 0 };
1736 static OptFunc DOptCmp2         = { OptCmp2,         "OptCmp2",          75, 0, 0, 0, 0, 0 };
1737 static OptFunc DOptCmp3         = { OptCmp3,         "OptCmp3",          75, 0, 0, 0, 0, 0 };
1738 static OptFunc DOptCmp4         = { OptCmp4,         "OptCmp4",         100, 0, 0, 0, 0, 0 };
1739 static OptFunc DOptCmp5         = { OptCmp5,         "OptCmp5",         100, 0, 0, 0, 0, 0 };
1740 static OptFunc DOptCmp6         = { OptCmp6,         "OptCmp6",          85, 0, 0, 0, 0, 0 };
1741 static OptFunc DOptCmp7         = { OptCmp7,         "OptCmp7",          50, 0, 0, 0, 0, 0 };
1742 static OptFunc DOptCondBranches = { OptCondBranches, "OptCondBranches",  80, 0, 0, 0, 0, 0 };
1743 static OptFunc DOptDeadCode     = { OptDeadCode,     "OptDeadCode",     100, 0, 0, 0, 0, 0 };
1744 static OptFunc DOptDeadJumps    = { OptDeadJumps,    "OptDeadJumps",    100, 0, 0, 0, 0, 0 };
1745 static OptFunc DOptDecouple     = { OptDecouple,     "OptDecouple",     100, 0, 0, 0, 0, 0 };
1746 static OptFunc DOptDupLoads     = { OptDupLoads,     "OptDupLoads",       0, 0, 0, 0, 0, 0 };
1747 static OptFunc DOptJumpCascades = { OptJumpCascades, "OptJumpCascades", 100, 0, 0, 0, 0, 0 };
1748 static OptFunc DOptJumpTarget   = { OptJumpTarget,   "OptJumpTarget",   100, 0, 0, 0, 0, 0 };
1749 static OptFunc DOptLoad1        = { OptLoad1,        "OptLoad1",        100, 0, 0, 0, 0, 0 };
1750 static OptFunc DOptRTS          = { OptRTS,          "OptRTS",          100, 0, 0, 0, 0, 0 };
1751 static OptFunc DOptRTSJumps1    = { OptRTSJumps1,    "OptRTSJumps1",    100, 0, 0, 0, 0, 0 };
1752 static OptFunc DOptRTSJumps2    = { OptRTSJumps2,    "OptRTSJumps2",    100, 0, 0, 0, 0, 0 };
1753 static OptFunc DOptNegA1        = { OptNegA1,        "OptNegA1",        100, 0, 0, 0, 0, 0 };
1754 static OptFunc DOptNegA2        = { OptNegA2,        "OptNegA2",        100, 0, 0, 0, 0, 0 };
1755 static OptFunc DOptNegAX1       = { OptNegAX1,       "OptNegAX1",       100, 0, 0, 0, 0, 0 };
1756 static OptFunc DOptNegAX2       = { OptNegAX2,       "OptNegAX2",       100, 0, 0, 0, 0, 0 };
1757 static OptFunc DOptNegAX3       = { OptNegAX3,       "OptNegAX3",       100, 0, 0, 0, 0, 0 };
1758 static OptFunc DOptNegAX4       = { OptNegAX4,       "OptNegAX4",       100, 0, 0, 0, 0, 0 };
1759 static OptFunc DOptPrecalc      = { OptPrecalc,      "OptPrecalc",      100, 0, 0, 0, 0, 0 };
1760 static OptFunc DOptPtrLoad1     = { OptPtrLoad1,     "OptPtrLoad1",     100, 0, 0, 0, 0, 0 };
1761 static OptFunc DOptPtrLoad2     = { OptPtrLoad2,     "OptPtrLoad2",     100, 0, 0, 0, 0, 0 };
1762 static OptFunc DOptPtrLoad3     = { OptPtrLoad3,     "OptPtrLoad3",     100, 0, 0, 0, 0, 0 };
1763 static OptFunc DOptPtrLoad4     = { OptPtrLoad4,     "OptPtrLoad4",     100, 0, 0, 0, 0, 0 };
1764 static OptFunc DOptPtrLoad5     = { OptPtrLoad5,     "OptPtrLoad5",      50, 0, 0, 0, 0, 0 };
1765 static OptFunc DOptPtrLoad6     = { OptPtrLoad6,     "OptPtrLoad6",      65, 0, 0, 0, 0, 0 };
1766 static OptFunc DOptPtrLoad7     = { OptPtrLoad7,     "OptPtrLoad7",      86, 0, 0, 0, 0, 0 };
1767 static OptFunc DOptPtrLoad8     = { OptPtrLoad8,     "OptPtrLoad8",     100, 0, 0, 0, 0, 0 };
1768 static OptFunc DOptPtrStore1    = { OptPtrStore1,    "OptPtrStore1",    100, 0, 0, 0, 0, 0 };
1769 static OptFunc DOptPtrStore2    = { OptPtrStore2,    "OptPtrStore2",     40, 0, 0, 0, 0, 0 };
1770 static OptFunc DOptPush1        = { OptPush1,        "OptPush1",         65, 0, 0, 0, 0, 0 };
1771 static OptFunc DOptPush2        = { OptPush2,        "OptPush2",         50, 0, 0, 0, 0, 0 };
1772 static OptFunc DOptPushPop      = { OptPushPop,      "OptPushPop",        0, 0, 0, 0, 0, 0 };
1773 static OptFunc DOptShift1       = { OptShift1,       "OptShift1",       100, 0, 0, 0, 0, 0 };
1774 static OptFunc DOptShift2       = { OptShift2,       "OptShift2",       100, 0, 0, 0, 0, 0 };
1775 static OptFunc DOptShift3       = { OptShift3,       "OptShift3",       110, 0, 0, 0, 0, 0 };
1776 static OptFunc DOptSize1        = { OptSize1,        "OptSize1",        100, 0, 0, 0, 0, 0 };
1777 static OptFunc DOptSize2        = { OptSize2,        "OptSize2",        100, 0, 0, 0, 0, 0 };
1778 static OptFunc DOptStackOps     = { OptStackOps,     "OptStackOps",     100, 0, 0, 0, 0, 0 };
1779 static OptFunc DOptStore1       = { OptStore1,       "OptStore1",        70, 0, 0, 0, 0, 0 };
1780 static OptFunc DOptStore2       = { OptStore2,       "OptStore2",       220, 0, 0, 0, 0, 0 };
1781 static OptFunc DOptStore3       = { OptStore3,       "OptStore3",       120, 0, 0, 0, 0, 0 };
1782 static OptFunc DOptStore4       = { OptStore4,       "OptStore4",        50, 0, 0, 0, 0, 0 };
1783 static OptFunc DOptStoreLoad    = { OptStoreLoad,    "OptStoreLoad",      0, 0, 0, 0, 0, 0 };
1784 static OptFunc DOptSub1         = { OptSub1,         "OptSub1",         100, 0, 0, 0, 0, 0 };
1785 static OptFunc DOptSub2         = { OptSub2,         "OptSub2",         100, 0, 0, 0, 0, 0 };
1786 static OptFunc DOptTest1        = { OptTest1,        "OptTest1",        100, 0, 0, 0, 0, 0 };
1787 static OptFunc DOptTransfers1   = { OptTransfers1,   "OptTransfers1",     0, 0, 0, 0, 0, 0 };
1788 static OptFunc DOptTransfers2   = { OptTransfers2,   "OptTransfers2",    60, 0, 0, 0, 0, 0 };
1789 static OptFunc DOptUnusedLoads  = { OptUnusedLoads,  "OptUnusedLoads",    0, 0, 0, 0, 0, 0 };
1790 static OptFunc DOptUnusedStores = { OptUnusedStores, "OptUnusedStores",   0, 0, 0, 0, 0, 0 };
1791
1792
1793 /* Table containing all the steps in alphabetical order */
1794 static OptFunc* OptFuncs[] = {
1795     &DOpt65C02BitOps,
1796     &DOpt65C02Ind,
1797     &DOpt65C02Stores,
1798     &DOptAdd1,
1799     &DOptAdd2,
1800     &DOptAdd3,
1801     &DOptAdd4,
1802     &DOptAdd5,
1803     &DOptBoolTrans,
1804     &DOptBranchDist,
1805     &DOptCmp1,
1806     &DOptCmp2,
1807     &DOptCmp3,
1808     &DOptCmp4,
1809     &DOptCmp5,
1810     &DOptCmp6,
1811     &DOptCmp7,
1812     &DOptCondBranches,
1813     &DOptDeadCode,
1814     &DOptDeadJumps,
1815     &DOptDecouple,
1816     &DOptDupLoads,
1817     &DOptJumpCascades,
1818     &DOptJumpTarget,
1819     &DOptLoad1,
1820     &DOptNegA1,
1821     &DOptNegA2,
1822     &DOptNegAX1,
1823     &DOptNegAX2,
1824     &DOptNegAX3,
1825     &DOptNegAX4,
1826     &DOptPrecalc,
1827     &DOptPtrLoad1,
1828     &DOptPtrLoad2,
1829     &DOptPtrLoad3,
1830     &DOptPtrLoad4,
1831     &DOptPtrLoad5,
1832     &DOptPtrLoad6,
1833     &DOptPtrLoad7,
1834     &DOptPtrLoad8,
1835     &DOptPtrStore1,
1836     &DOptPtrStore2,
1837     &DOptPush1,
1838     &DOptPush2,
1839     &DOptPushPop,
1840     &DOptRTS,
1841     &DOptRTSJumps1,
1842     &DOptRTSJumps2,
1843     &DOptShift1,
1844     &DOptShift2,
1845     &DOptShift3,
1846     &DOptSize1,
1847     &DOptSize2,
1848     &DOptStackOps,
1849     &DOptStore1,
1850     &DOptStore2,
1851     &DOptStore3,
1852     &DOptStore4,
1853     &DOptStoreLoad,
1854     &DOptSub1,
1855     &DOptSub2,
1856     &DOptTest1,
1857     &DOptTransfers1,
1858     &DOptTransfers2,
1859     &DOptUnusedLoads,
1860     &DOptUnusedStores,
1861 };
1862 #define OPTFUNC_COUNT  (sizeof(OptFuncs) / sizeof(OptFuncs[0]))
1863
1864
1865
1866 static int CmpOptStep (const void* Key, const void* Func)
1867 /* Compare function for bsearch */
1868 {
1869     return strcmp (Key, (*(const OptFunc**)Func)->Name);
1870 }
1871
1872
1873
1874 static OptFunc* FindOptFunc (const char* Name)
1875 /* Find an optimizer step by name in the table and return a pointer. Return
1876  * NULL if no such step is found.
1877  */
1878 {
1879     /* Search for the function in the list */
1880     OptFunc** O = bsearch (Name, OptFuncs, OPTFUNC_COUNT, sizeof (OptFuncs[0]), CmpOptStep);
1881     return O? *O : 0;
1882 }
1883
1884
1885
1886 static OptFunc* GetOptFunc (const char* Name)
1887 /* Find an optimizer step by name in the table and return a pointer. Print an
1888  * error and call AbEnd if not found.
1889  */
1890 {
1891     /* Search for the function in the list */
1892     OptFunc* F = FindOptFunc (Name);
1893     if (F == 0) {
1894         /* Not found */
1895         AbEnd ("Optimization step `%s' not found", Name);
1896     }
1897     return F;
1898 }
1899
1900
1901
1902 void DisableOpt (const char* Name)
1903 /* Disable the optimization with the given name */
1904 {
1905     if (strcmp (Name, "any") == 0) {
1906         unsigned I;
1907         for (I = 0; I < OPTFUNC_COUNT; ++I) {
1908             OptFuncs[I]->Disabled = 1;
1909         }
1910     } else {
1911         GetOptFunc(Name)->Disabled = 1;
1912     }
1913 }
1914
1915
1916
1917 void EnableOpt (const char* Name)
1918 /* Enable the optimization with the given name */
1919 {
1920     if (strcmp (Name, "any") == 0) {
1921         unsigned I;
1922         for (I = 0; I < OPTFUNC_COUNT; ++I) {
1923             OptFuncs[I]->Disabled = 0;
1924         }
1925     } else {
1926         GetOptFunc(Name)->Disabled = 0;
1927     }
1928 }
1929
1930
1931
1932 void ListOptSteps (FILE* F)
1933 /* List all optimization steps */
1934 {
1935     unsigned I;
1936     for (I = 0; I < OPTFUNC_COUNT; ++I) {
1937         fprintf (F, "%s\n", OptFuncs[I]->Name);
1938     }
1939 }
1940
1941
1942
1943 static void ReadOptStats (const char* Name)
1944 /* Read the optimizer statistics file */
1945 {
1946     char Buf [256];
1947     unsigned Lines;
1948
1949     /* Try to open the file */
1950     FILE* F = fopen (Name, "r");
1951     if (F == 0) {
1952         /* Ignore the error */
1953         return;
1954     }
1955
1956     /* Read and parse the lines */
1957     Lines = 0;
1958     while (fgets (Buf, sizeof (Buf), F) != 0) {
1959
1960         char* B;
1961         unsigned Len;
1962         OptFunc* Func;
1963
1964         /* Fields */
1965         char Name[32];
1966         unsigned long  TotalRuns;
1967         unsigned long  TotalChanges;
1968
1969         /* Count lines */
1970         ++Lines;
1971
1972         /* Remove trailing white space including the line terminator */
1973         B = Buf;
1974         Len = strlen (B);
1975         while (Len > 0 && IsSpace (B[Len-1])) {
1976             --Len;
1977         }
1978         B[Len] = '\0';
1979
1980         /* Remove leading whitespace */
1981         while (IsSpace (*B)) {
1982             ++B;
1983         }
1984
1985         /* Check for empty and comment lines */
1986         if (*B == '\0' || *B == ';' || *B == '#') {
1987             continue;
1988         }
1989
1990         /* Parse the line */
1991         if (sscanf (B, "%31s %lu %*u %lu %*u", Name, &TotalRuns, &TotalChanges) != 3) {
1992             /* Syntax error */
1993             continue;
1994         }
1995
1996         /* Search for the optimizer step. */
1997         Func = FindOptFunc (Name);
1998         if (Func == 0) {
1999             /* Not found */
2000             continue;
2001         }
2002
2003         /* Found the step, set the fields */
2004         Func->TotalRuns    = TotalRuns;
2005         Func->TotalChanges = TotalChanges;
2006
2007     }
2008
2009     /* Close the file, ignore errors here. */
2010     fclose (F);
2011 }
2012
2013
2014
2015 static void WriteOptStats (const char* Name)
2016 /* Write the optimizer statistics file */
2017 {
2018     unsigned I;
2019
2020     /* Try to open the file */
2021     FILE* F = fopen (Name, "w");
2022     if (F == 0) {
2023         /* Ignore the error */
2024         return;
2025     }
2026
2027     /* Write a header */
2028     fprintf (F,
2029              "; Optimizer               Total      Last       Total      Last\n"
2030              ";   Step                  Runs       Runs        Chg       Chg\n");
2031
2032
2033     /* Write the data */
2034     for (I = 0; I < OPTFUNC_COUNT; ++I) {
2035         const OptFunc* O = OptFuncs[I];
2036         fprintf (F,
2037                  "%-20s %10lu %10lu %10lu %10lu\n",
2038                  O->Name,
2039                  O->TotalRuns,
2040                  O->LastRuns,
2041                  O->TotalChanges,
2042                  O->LastChanges);
2043     }
2044
2045     /* Close the file, ignore errors here. */
2046     fclose (F);
2047 }
2048
2049
2050
2051 static unsigned RunOptFunc (CodeSeg* S, OptFunc* F, unsigned Max)
2052 /* Run one optimizer function Max times or until there are no more changes */
2053 {
2054     unsigned Changes, C;
2055
2056     /* Don't run the function if it is disabled or if it is prohibited by the
2057      * code size factor
2058      */
2059     if (F->Disabled || F->CodeSizeFactor > S->CodeSizeFactor) {
2060         return 0;
2061     }
2062
2063     /* Run this until there are no more changes */
2064     Changes = 0;
2065     do {
2066
2067         /* Run the function */
2068         C = F->Func (S);
2069         Changes += C;
2070
2071         /* Do statistics */
2072         ++F->TotalRuns;
2073         ++F->LastRuns;
2074         F->TotalChanges += C;
2075         F->LastChanges  += C;
2076
2077     } while (--Max && C > 0);
2078
2079     /* Return the number of changes */
2080     return Changes;
2081 }
2082
2083
2084
2085 static unsigned RunOptGroup1 (CodeSeg* S)
2086 /* Run the first group of optimization steps. These steps translate known
2087  * patterns emitted by the code generator into more optimal patterns. Order
2088  * of the steps is important, because some of the steps done earlier cover
2089  * the same patterns as later steps as subpatterns.
2090  */
2091 {
2092     unsigned Changes = 0;
2093
2094     Changes += RunOptFunc (S, &DOptPtrStore1, 1);
2095     Changes += RunOptFunc (S, &DOptPtrStore2, 1);
2096     Changes += RunOptFunc (S, &DOptPtrLoad1, 1);
2097     Changes += RunOptFunc (S, &DOptPtrLoad2, 1);
2098     Changes += RunOptFunc (S, &DOptPtrLoad3, 1);
2099     Changes += RunOptFunc (S, &DOptPtrLoad4, 1);
2100     Changes += RunOptFunc (S, &DOptPtrLoad5, 1);
2101     Changes += RunOptFunc (S, &DOptPtrLoad6, 1);
2102     Changes += RunOptFunc (S, &DOptPtrLoad7, 1);
2103     Changes += RunOptFunc (S, &DOptNegAX1, 1);
2104     Changes += RunOptFunc (S, &DOptNegAX2, 1);
2105     Changes += RunOptFunc (S, &DOptNegAX3, 1);
2106     Changes += RunOptFunc (S, &DOptNegAX4, 1);
2107     Changes += RunOptFunc (S, &DOptAdd1, 1);
2108     Changes += RunOptFunc (S, &DOptAdd2, 1);
2109     Changes += RunOptFunc (S, &DOptAdd3, 1);
2110     Changes += RunOptFunc (S, &DOptStore4, 1);
2111     Changes += RunOptFunc (S, &DOptShift1, 1);
2112     Changes += RunOptFunc (S, &DOptShift2, 1);
2113     Changes += RunOptFunc (S, &DOptShift3, 1);
2114     Changes += RunOptFunc (S, &DOptStore1, 1);
2115     Changes += RunOptFunc (S, &DOptStore2, 5);
2116     Changes += RunOptFunc (S, &DOptStore3, 5);
2117
2118     /* Return the number of changes */
2119     return Changes;
2120 }
2121
2122
2123
2124 static unsigned RunOptGroup2 (CodeSeg* S)
2125 /* Run one group of optimization steps. This step involves just decoupling
2126  * instructions by replacing them by instructions that do not depend on
2127  * previous instructions. This makes it easier to find instructions that
2128  * aren't used.
2129  */
2130 {
2131     unsigned Changes = 0;
2132
2133     Changes += RunOptFunc (S, &DOptDecouple, 1);
2134
2135     /* Return the number of changes */
2136     return Changes;
2137 }
2138
2139
2140
2141 static unsigned RunOptGroup3 (CodeSeg* S)
2142 /* Run one group of optimization steps. These steps depend on each other,
2143  * that means that one step may allow another step to do additional work,
2144  * so we will repeat the steps as long as we see any changes.
2145  */
2146 {
2147     unsigned Changes, C;
2148
2149     Changes = 0;
2150     do {
2151         C = 0;
2152
2153         C += RunOptFunc (S, &DOptPtrLoad8, 1);
2154         C += RunOptFunc (S, &DOptNegA1, 1);
2155         C += RunOptFunc (S, &DOptNegA2, 1);
2156         C += RunOptFunc (S, &DOptSub1, 1);
2157         C += RunOptFunc (S, &DOptSub2, 1);
2158         C += RunOptFunc (S, &DOptAdd4, 1);
2159         C += RunOptFunc (S, &DOptAdd5, 1);
2160         C += RunOptFunc (S, &DOptStackOps, 1);
2161         C += RunOptFunc (S, &DOptJumpCascades, 1);
2162         C += RunOptFunc (S, &DOptDeadJumps, 1);
2163         C += RunOptFunc (S, &DOptRTS, 1);
2164         C += RunOptFunc (S, &DOptDeadCode, 1);
2165         C += RunOptFunc (S, &DOptJumpTarget, 1);
2166         C += RunOptFunc (S, &DOptCondBranches, 1);
2167         C += RunOptFunc (S, &DOptRTSJumps1, 1);
2168         C += RunOptFunc (S, &DOptBoolTrans, 1);
2169         C += RunOptFunc (S, &DOptCmp1, 1);
2170         C += RunOptFunc (S, &DOptCmp2, 1);
2171         C += RunOptFunc (S, &DOptCmp3, 1);
2172         C += RunOptFunc (S, &DOptCmp4, 1);
2173         C += RunOptFunc (S, &DOptCmp5, 1);
2174         C += RunOptFunc (S, &DOptCmp6, 1);
2175         C += RunOptFunc (S, &DOptCmp7, 1);
2176         C += RunOptFunc (S, &DOptTest1, 1);
2177         C += RunOptFunc (S, &DOptLoad1, 1);
2178         C += RunOptFunc (S, &DOptUnusedLoads, 1);
2179         C += RunOptFunc (S, &DOptUnusedStores, 1);
2180         C += RunOptFunc (S, &DOptDupLoads, 1);
2181         C += RunOptFunc (S, &DOptStoreLoad, 1);
2182         C += RunOptFunc (S, &DOptTransfers1, 1);
2183         C += RunOptFunc (S, &DOptPushPop, 1);
2184         C += RunOptFunc (S, &DOptPrecalc, 1);
2185
2186         Changes += C;
2187
2188     } while (C);
2189
2190     /* Return the number of changes */
2191     return Changes;
2192 }
2193
2194
2195
2196 static unsigned RunOptGroup4 (CodeSeg* S)
2197 /* 65C02 specific optimizations. */
2198 {
2199     unsigned Changes = 0;
2200
2201     if (CPUIsets[CPU] & CPU_ISET_65SC02) {
2202         Changes += RunOptFunc (S, &DOpt65C02BitOps, 1);
2203         Changes += RunOptFunc (S, &DOpt65C02Ind, 1);
2204         Changes += RunOptFunc (S, &DOpt65C02Stores, 1);
2205         if (Changes) {
2206             /* The 65C02 replacement codes do often make the use of a register
2207              * value unnecessary, so if we have changes, run another load
2208              * removal pass.
2209              */
2210             Changes += RunOptFunc (S, &DOptUnusedLoads, 1);
2211         }
2212     }
2213
2214     /* Return the number of changes */
2215     return Changes;
2216 }
2217
2218
2219
2220 static unsigned RunOptGroup5 (CodeSeg* S)
2221 /* Run another round of pattern replacements. These are done late, since there
2222  * may be better replacements before.
2223  */
2224 {
2225     unsigned Changes = 0;
2226
2227     Changes += RunOptFunc (S, &DOptPush1, 1);
2228     Changes += RunOptFunc (S, &DOptPush2, 1);
2229     Changes += RunOptFunc (S, &DOptUnusedLoads, 1);
2230     Changes += RunOptFunc (S, &DOptTransfers2, 1);
2231
2232     /* Return the number of changes */
2233     return Changes;
2234 }
2235
2236
2237
2238 static unsigned RunOptGroup6 (CodeSeg* S)
2239 /* The last group of optimization steps. Adjust branches, do size optimizations.
2240  */
2241 {
2242     unsigned Changes = 0;
2243     unsigned C;
2244
2245     if (S->CodeSizeFactor <= 100) {
2246         /* Optimize for size, that is replace operations by shorter ones, even
2247          * if this does hinder further optimizations (no problem since we're
2248          * done soon).
2249          */
2250         C = RunOptFunc (S, &DOptSize1, 1);
2251         if (C) {
2252             Changes += C;
2253             /* Run some optimization passes again, since the size optimizations
2254              * may have opened new oportunities.
2255              */
2256             Changes += RunOptFunc (S, &DOptUnusedLoads, 1);
2257             Changes += RunOptFunc (S, &DOptJumpTarget, 5);
2258         }
2259     }
2260     C = RunOptFunc (S, &DOptSize2, 1);
2261     if (C) {
2262         Changes += C;
2263         /* Run some optimization passes again, since the size optimizations
2264          * may have opened new oportunities.
2265          */
2266         Changes += RunOptFunc (S, &DOptUnusedLoads, 1);
2267         Changes += RunOptFunc (S, &DOptJumpTarget, 5);
2268     }
2269
2270     /* Adjust branch distances */
2271     Changes += RunOptFunc (S, &DOptBranchDist, 3);
2272
2273     /* Replace conditional branches to RTS. If we had changes, we must run dead
2274      * code elimination again, since the change may have introduced dead code.
2275      */
2276     C = RunOptFunc (S, &DOptRTSJumps2, 1);
2277     Changes += C;
2278     if (C) {
2279         Changes += RunOptFunc (S, &DOptDeadCode, 1);
2280     }
2281
2282     /* Return the number of changes */
2283     return Changes;
2284 }
2285
2286
2287
2288 void RunOpt (CodeSeg* S)
2289 /* Run the optimizer */
2290 {
2291     const char* StatFileName;
2292
2293     /* If we shouldn't run the optimizer, bail out */
2294     if (!S->Optimize) {
2295         return;
2296     }
2297
2298     /* Check if we are requested to write optimizer statistics */
2299     StatFileName = getenv ("CC65_OPTSTATS");
2300     if (StatFileName) {
2301         ReadOptStats (StatFileName);
2302     }
2303
2304     /* Print the name of the function we are working on */
2305     if (S->Func) {
2306         Print (stdout, 1, "Running optimizer for function `%s'\n", S->Func->Name);
2307     } else {
2308         Print (stdout, 1, "Running optimizer for global code segment\n");
2309     }
2310
2311     /* Run groups of optimizations */
2312     RunOptGroup1 (S);
2313     RunOptGroup2 (S);
2314     RunOptGroup3 (S);
2315     RunOptGroup4 (S);
2316     RunOptGroup5 (S);
2317     RunOptGroup6 (S);
2318
2319     /* Write statistics */
2320     if (StatFileName) {
2321         WriteOptStats (StatFileName);
2322     }
2323 }
2324
2325
2326