]> git.sur5r.net Git - cc65/blob - src/cc65/optimize.c
2c6a588e7014ee979128d0059e527f31099c9bdc
[cc65] / src / cc65 / optimize.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                optimize.c                                 */
4 /*                                                                           */
5 /*                   An optimizer for the cc65 C compiler                    */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 1998     Ullrich von Bassewitz                                        */
10 /*              Wacholderweg 14                                              */
11 /*              D-70597 Stuttgart                                            */
12 /* EMail:       uz@musoftware.de                                             */
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 <stdarg.h>
37 #include <stdio.h>
38 #include <string.h>
39
40 /* common */
41 #include "attrib.h"
42 #include "chartype.h"
43 #include "check.h"
44 #include "print.h"
45 #include "xmalloc.h"
46 #include "xsprintf.h"
47
48 /* cc65 */
49 #include "asmlabel.h"
50 #include "asmline.h"
51 #include "cpu.h"
52 #include "error.h"
53 #include "global.h"
54 #include "optimize.h"
55
56
57
58 /*****************************************************************************/
59 /*                                   Data                                    */
60 /*****************************************************************************/
61
62
63
64 /* Bitset of flags that switch the different optimizer passes */
65 unsigned long OptDisable        = 0;
66
67
68
69 /* Bitmapped flags for the Flags field in the Line struct */
70 #define OF_CODE         0x0001          /* This line is in a code segment */
71
72 /* Pointer to first code line */
73 static Line*            FirstCode;
74
75 /* Label list */
76 static Line**           Labels = 0;     /* Pointers to label lines */
77 static unsigned         LabelCount = 0; /* Count of local labels found */
78
79 /* A collection of lines */
80 typedef struct LineColl_ LineColl;
81 struct LineColl_ {
82     unsigned    Count;                  /* Count of lines in the collection */
83     unsigned    Max;                    /* Maximum count of lines */
84     Line*       Lines[1];               /* Lines, dynamically allocated */
85 };
86
87
88
89 /* Calculate the element count of a table */
90 #define COUNT(T)        (sizeof (T) / sizeof (T [0]))
91
92 /* Macro to increment and decrement register contents if they're valid */
93 #define INC(reg,val)    if ((reg) >= 0) (reg) = ((reg) + val) & 0xFF
94 #define DEC(reg,val)    if ((reg) >= 0) (reg) = ((reg) - val) & 0xFF
95
96 /* Defines for the conditions in a compare */
97 #define CMP_EQ          0
98 #define CMP_NE          1
99 #define CMP_GT          2
100 #define CMP_GE          3
101 #define CMP_LT          4
102 #define CMP_LE          5
103 #define CMP_UGT         6
104 #define CMP_UGE         7
105 #define CMP_ULT         8
106 #define CMP_ULE         9
107
108 /* Defines for registers */
109 #define REG_NONE        0x00
110 #define REG_A           0x01
111 #define REG_X           0x02
112 #define REG_Y           0x04
113 #define REG_AX          (REG_A | REG_X)
114 #define REG_ALL         (REG_A | REG_X | REG_Y)
115
116 /* Description of the commands */
117 static const struct {
118     const char*         Insn;           /* Instruction */
119     unsigned char       FullMatch;      /* Match full instuction? */
120     unsigned char       Use;            /* Registers used */
121     unsigned char       Load;           /* Registers loaded */
122 } CmdDesc [] = {
123     { "\tadc\t",          0,    REG_A,      REG_NONE      },
124     { "\tand\t",          0,    REG_A,      REG_NONE      },
125     { "\tasl\ta",         1,    REG_A,      REG_NONE      },
126     { "\tasl\t",          0,    REG_NONE,   REG_NONE      },
127     { "\tclc",            1,    REG_NONE,   REG_NONE      },
128     { "\tcld",            1,    REG_NONE,   REG_NONE      },
129     { "\tcli",            1,    REG_NONE,   REG_NONE      },
130     { "\tcmp\t",          0,    REG_A,      REG_NONE      },
131     { "\tcpx\t",          0,    REG_X,      REG_NONE      },
132     { "\tcpy\t",          0,    REG_Y,      REG_NONE      },
133     { "\tdea",            1,    REG_A,      REG_NONE      },
134     { "\tdec\ta",         1,    REG_A,      REG_NONE      },
135     { "\tdec\t",          0,    REG_NONE,   REG_NONE      },
136     { "\tdex",            1,    REG_X,      REG_NONE      },
137     { "\tdey",            1,    REG_Y,      REG_NONE      },
138     { "\teor\t",          0,    REG_A,      REG_NONE      },
139     { "\tina",            1,    REG_A,      REG_NONE      },
140     { "\tinc\ta",         1,    REG_A,      REG_NONE      },
141     { "\tinc\t",          0,    REG_NONE,   REG_NONE      },
142     { "\tinx",            1,    REG_X,      REG_NONE      },
143     { "\tiny",            1,    REG_Y,      REG_NONE      },
144     { "\tjsr\tbool",      0,    REG_NONE,   REG_AX        },
145     { "\tjsr\tdecaxy",    1,    REG_ALL,    REG_AX        },
146     { "\tjsr\tdecax",     0,    REG_AX,     REG_AX        },
147     { "\tjsr\tdecsp2",    1,    REG_NONE,   REG_A         },
148     { "\tjsr\tdecsp3",    1,    REG_NONE,   REG_A         },
149     { "\tjsr\tdecsp4",    1,    REG_NONE,   REG_A         },
150     { "\tjsr\tdecsp5",    1,    REG_NONE,   REG_A         },
151     { "\tjsr\tdecsp6",    1,    REG_NONE,   REG_A         },
152     { "\tjsr\tdecsp7",    1,    REG_NONE,   REG_A         },
153     { "\tjsr\tdecsp8",    1,    REG_NONE,   REG_A         },
154     { "\tjsr\tldax0sp",   1,    REG_Y,      REG_AX        },
155     { "\tjsr\tldaxysp",   1,    REG_Y,      REG_AX        },
156     { "\tjsr\tpusha",     1,    REG_A,      REG_Y         },
157     { "\tjsr\tpusha0",    1,    REG_A,      REG_X | REG_Y },
158     { "\tjsr\tpushax",    1,    REG_AX,     REG_Y         },
159     { "\tjsr\tpushw0sp",  1,    REG_NONE,   REG_ALL       },
160     { "\tjsr\tpushwysp",  1,    REG_Y,      REG_ALL       },
161     { "\tjsr\ttosicmp",   1,    REG_AX,     REG_ALL       },
162     { "\tlda\t",          0,    REG_NONE,   REG_A         },
163     { "\tldax\t",         0,    REG_NONE,   REG_AX        },
164     { "\tldx\t",          0,    REG_NONE,   REG_X         },
165     { "\tldy\t",          0,    REG_NONE,   REG_Y         },
166     { "\tlsr\ta",         1,    REG_A,      REG_NONE      },
167     { "\tlsr\t",          0,    REG_NONE,   REG_NONE      },
168     { "\tnop",            1,    REG_NONE,   REG_NONE      },
169     { "\tora\t",          0,    REG_A,      REG_NONE      },
170     { "\tpha",            1,    REG_A,      REG_NONE      },
171     { "\tphp",            1,    REG_NONE,   REG_NONE      },
172     { "\tpla",            1,    REG_NONE,   REG_A         },
173     { "\tplp",            1,    REG_NONE,   REG_NONE      },
174     { "\trol\ta",         1,    REG_A,      REG_A         },
175     { "\trol\t",          0,    REG_NONE,   REG_NONE      },
176     { "\tror\ta",         1,    REG_A,      REG_A         },
177     { "\tror\t",          0,    REG_NONE,   REG_NONE      },
178     { "\tsbc\t",          0,    REG_A,      REG_NONE      },
179     { "\tsec",            1,    REG_NONE,   REG_NONE      },
180     { "\tsed",            1,    REG_NONE,   REG_NONE      },
181     { "\tsei",            1,    REG_NONE,   REG_NONE      },
182     { "\tsta\t",          0,    REG_A,      REG_NONE      },
183     { "\tstx\t",          0,    REG_X,      REG_NONE      },
184     { "\tsty\t",          0,    REG_Y,      REG_NONE      },
185     { "\tstz\t",          0,    REG_NONE,   REG_NONE      },
186     { "\ttax",            1,    REG_A,      REG_X         },
187     { "\ttay",            1,    REG_A,      REG_Y         },
188     { "\ttrb\t",          0,    REG_A,      REG_NONE      },
189     { "\ttsb\t",          0,    REG_A,      REG_NONE      },
190     { "\ttsx",            1,    REG_NONE,   REG_X         },
191     { "\ttxa",            1,    REG_X,      REG_A         },
192     { "\ttya",            1,    REG_Y,      REG_A         },
193 };
194
195
196
197 /* Table with the compare suffixes */
198 static const char CmpSuffixTab [][4] = {
199     "eq", "ne", "gt", "ge", "lt", "le", "ugt", "uge", "ult", "ule"
200 };
201
202 /* Table used to invert a condition, indexed by condition */
203 static const unsigned char CmpInvertTab [] = {
204     CMP_NE, CMP_EQ,
205     CMP_LE, CMP_LT, CMP_GE, CMP_GT,
206     CMP_ULE, CMP_ULT, CMP_UGE, CMP_UGT
207 };
208
209 /* Table to show which compares are signed (use the N flag) */
210 static const char CmpSignedTab [] = {
211     0, 0, 1, 1, 1, 1, 0, 0, 0, 0
212 };
213
214
215
216 /* Lists of branches */
217 static const char* ShortBranches [] = {
218     "\tbeq\t",
219     "\tbne\t",
220     "\tbpl\t",
221     "\tbmi\t",
222     "\tbcc\t",
223     "\tbcs\t",
224     "\tbvc\t",
225     "\tbvs\t",
226     0
227 };
228 static const char* LongBranches [] = {
229     "\tjeq\t",
230     "\tjne\t",
231     "\tjpl\t",
232     "\tjmi\t",
233     "\tjcc\t",
234     "\tjcs\t",
235     "\tjvc\t",
236     "\tjvs\t",
237     0
238 };
239
240
241
242 /*****************************************************************************/
243 /*                                 Forwards                                  */
244 /*****************************************************************************/
245
246
247
248 static unsigned EstimateSize (Line* L);
249 /* Estimate the size of an instruction */
250
251 static int IsLocalLabel (const Line* L);
252 /* Return true if the line is a local label line */
253
254 static unsigned GetLabelNum (const char* L);
255 /* Return the label number of a label line */
256
257 static unsigned RVUInt1 (Line* L, LineColl* LC, unsigned Used, unsigned Unused);
258 /* Subfunction for RegValUsed. Will be called recursively in case of branches. */
259
260 static Line* NewLineAfter (Line* LineBefore, const char* Format, ...) attribute ((format(printf,2,3)));
261 /* Create a new line, insert it after L and return it. The new line is marked
262  * as code line.
263  */
264
265 static Line* ReplaceLine (Line* L, const char* Format, ...)
266         attribute ((format(printf,2,3)));
267 /* Replace one line by another */
268
269
270
271 /*****************************************************************************/
272 /*                                List stuff                                 */
273 /*****************************************************************************/
274
275
276
277 static Line* NewLineAfter (Line* LineBefore, const char* Format, ...)
278 /* Create a new line, insert it after L and return it. The new line is marked
279  * as code line.
280  */
281 {
282     Line* L;
283
284     /* Format the new line and add it */
285     va_list ap;
286     va_start (ap, Format);
287     L = NewCodeLineAfter (LineBefore, Format, ap);
288     va_end (ap);
289
290     /* Make the line a code line */
291     L->Flags = OF_CODE;
292
293     /* Estimate the code size */
294     L->Size = EstimateSize (L);
295
296     /* Return the new line */
297     return L;
298 }
299
300
301
302 static Line* NewLabelAfter (Line* L, unsigned Label)
303 /* Add a new line with a definition of a local label after the line L */
304 {
305     char Buf [32];
306
307     /* Create the label */
308     sprintf (Buf, "L%04X:", Label);
309
310     /* Create a new line */
311     L = NewLineAfter (L, Buf);
312
313     /* Insert this label into the label list */
314     Labels [Label] = L;
315
316     /* Return the new line */
317     return L;
318 }
319
320
321
322 static void FreeLine (Line* L)
323 /* Remove a line from the list and free it */
324 {
325     /* If this is a label line, remove it from the label list */
326     if (IsLocalLabel (L)) {
327         Labels [GetLabelNum (L->Line)] = 0;
328     }
329
330     /* Unlink the line */
331     FreeCodeLine (L);
332 }
333
334
335
336 static Line* ReplaceLine (Line* L, const char* Format, ...)
337 /* Replace one line by another */
338 {
339     unsigned Len;
340     char S [256];
341
342     /* Format the new line */
343     va_list ap;
344     va_start (ap, Format);
345     xvsprintf (S, sizeof (S), Format, ap);
346     va_end (ap);
347
348     /* Get the length of the new line */
349     Len = strlen (S);
350
351     /* We can copy the line if the old line has space enough */
352     if (Len <= L->Len) {
353
354         /* Just copy the new line, but don't update the length */
355         memcpy (L->Line, S, Len);
356         L->Line [Len] = '\0';
357
358     } else {
359
360         /* We must allocate new space */
361         Line* NewLine = xmalloc (sizeof (Line) + Len);
362
363         /* Set the values in the new struct */
364         NewLine->Flags  = L->Flags;
365         NewLine->Index  = L->Index;
366         NewLine->Size   = L->Size;              /* Hmm ... */
367         NewLine->Len    = Len;
368         memcpy (NewLine->Line, S, Len + 1);
369
370         /* Replace the old struct in the list */
371         NewLine->Next = L->Next;
372         if (NewLine->Next) {
373             NewLine->Next->Prev = NewLine;
374         } else {
375             /* Last line */
376             LastLine = NewLine;
377         }
378         NewLine->Prev = L->Prev;
379         if (NewLine->Prev) {
380             NewLine->Prev->Next = NewLine;
381         } else {
382             /* First line */
383             FirstLine = NewLine;
384         }
385
386         /* Free the old struct */
387         xfree (L);
388         L = NewLine;
389     }
390
391     /* Estimate the new size */
392     if (L->Flags & OF_CODE) {
393         L->Size = EstimateSize (L);
394     }
395
396     /* Return the line */
397     return L;
398 }
399
400
401
402 static Line* PrevCodeLine (Line* L)
403 /* Return the previous line containing code */
404 {
405     L = L->Prev;
406     while (L) {
407         if (L->Flags & OF_CODE && L->Line [0] != '+') {
408             break;
409         }
410         L = L->Prev;
411     }
412     return L;
413 }
414
415
416
417 static Line* NextCodeSegLine (Line* L)
418 /* Return the next line in the code segment */
419 {
420     L = L->Next;
421     while (L) {
422         if (L->Flags & OF_CODE) {
423             break;
424         }
425         L = L->Next;
426     }
427     return L;
428 }
429
430
431
432 static Line* NextCodeLine (Line* L)
433 /* Return the next line containing code */
434 {
435     L = L->Next;
436     while (L) {
437         if ((L->Flags & OF_CODE) != 0 && L->Line [0] != '+') {
438             break;
439         }
440         L = L->Next;
441     }
442     return L;
443 }
444
445
446
447 static Line* NextInstruction (Line* L)
448 /* Return the next line containing code, ignoring labels. */
449 {
450     do {
451         L = NextCodeLine (L);
452     } while (L && (L->Line[0] == '+' || IsLocalLabel(L)));
453     return L;
454 }
455
456
457
458 static void FreeLines (Line* Start, Line* End)
459 /* Delete all lines from Start to End, both inclusive */
460 {
461     Line* L;
462     do {
463         L = Start;
464         Start = NextCodeSegLine (Start);
465         FreeLine (L);
466     } while (L != End);
467 }
468
469
470
471 /*****************************************************************************/
472 /*                           Line Collections                                */
473 /*****************************************************************************/
474
475
476
477 static LineColl* NewLineColl (unsigned Size)
478 /* Create a new line collection and return it */
479 {
480     /* Allocate memory */
481     LineColl* LC = xmalloc (sizeof (LineColl) + sizeof (Line) * (Size-1));
482
483     /* Initialize members */
484     LC->Count = 0;
485     LC->Max   = Size;
486
487     /* Return the new collection */
488     return LC;
489 }
490
491
492
493 static void FreeLineColl (LineColl* LC)
494 /* Delete a line collection */
495 {
496     xfree (LC);
497 }
498
499
500
501 static int LCAddLine (LineColl* LC, Line* L)
502 /* Add a line. Return 0 if no space available, return 1 otherwise */
503 {
504     /* Check if there is enough space available */
505     if (LC->Count >= LC->Max) {
506         /* No room available */
507         return 0;
508     }
509
510     /* Add the line */
511     LC->Lines [LC->Count++] = L;
512
513     /* Done */
514     return 1;
515 }
516
517
518
519 static int LCHasLine (LineColl* LC, Line* L)
520 /* Check if the given line is in the collection */
521 {
522     unsigned I;
523     for (I = 0; I < LC->Count; ++I) {
524         if (LC->Lines[I] == L) {
525             return 1;
526         }
527     }
528     return 0;
529 }
530
531
532
533 /*****************************************************************************/
534 /*                      Test a line for several things                       */
535 /*****************************************************************************/
536
537
538
539 static int IsLocalLabel (const Line* L)
540 /* Return true if the line is a local label line */
541 {
542     return (L->Line [0] == 'L' && IsXDigit (L->Line [1]));
543 }
544
545
546
547 static int IsExtLabel (const Line* L)
548 /* Return true if the line is an external label line */
549 {
550     return (L->Line [0] == '_');
551 }
552
553
554
555 static int IsLabel (const Line* L)
556 /* Return true if the line is a label line */
557 {
558     return (L->Line [0] == 'L' && IsXDigit (L->Line [1])) ||
559            (L->Line [0] == '_');;
560 }
561
562
563
564 static int IsHintLine (const Line* L)
565 /* Return true if the line contains an optimizer hint */
566 {
567     return L->Line [0] == '+';
568 }
569
570
571
572 static int IsSegHint (const Line* L)
573 /* Return true if the given line contains a segment hint */
574 {
575     return (L->Line [0] == '+' && strncmp (L->Line + 1, "seg:", 4) == 0);
576 }
577
578
579
580 static int IsHint (const Line* L, const char* Hint)
581 /* Check if the line contains a given hint */
582 {
583     return (L->Line [0] == '+' && strcmp (L->Line + 1, Hint) == 0);
584 }
585
586
587
588 static int IsCondJump (const Line* L)
589 /* Return true if the line contains a conditional jump */
590 {
591     return (L->Line [0] == '\t' &&
592             (strncmp (L->Line + 1, "beq\t", 4) == 0 ||
593              strncmp (L->Line + 1, "bne\t", 4) == 0 ||
594              strncmp (L->Line + 1, "jeq\t", 4) == 0 ||
595              strncmp (L->Line + 1, "jne\t", 4) == 0));
596 }
597
598
599
600 static int IsXAddrMode (const Line* L)
601 /* Return true if the given line does use the X register */
602 {
603     unsigned Len = strlen (L->Line);
604     return (strcmp (L->Line + Len - 3, ",x)") == 0 ||
605             strcmp (L->Line + Len - 2, ",x")  == 0);
606 }
607
608
609
610 static int NoXAddrMode (const Line* L)
611 /* Return true if the given line does use the X register */
612 {
613     return !IsXAddrMode (L);
614 }
615
616
617
618 static int IsYAddrMode (const Line* L)
619 /* Return true if the given line does use the Y register */
620 {
621     unsigned Len = strlen (L->Line);
622     return (strcmp (L->Line + Len - 2, ",y") == 0);
623 }
624
625
626
627 static int Is16BitStore (const Line* L1, const Line* L2)
628 /* Check if L1 and L2 are a store of ax into a 16 bit location */
629 {
630     unsigned Len1 = strlen (L1->Line);
631     return (strncmp (L1->Line, "\tsta\t", 5) == 0               &&
632             strncmp (L2->Line, "\tstx\t", 5) == 0               &&
633             strncmp (L1->Line+5, L2->Line+5, Len1-5) == 0       &&
634             strcmp (L2->Line+Len1, "+1") == 0);
635 }
636
637
638
639 static Line* FindHint (Line* L, const char* Hint)
640 /* Search for a line with the given hint */
641 {
642     while (L) {
643         if (IsHint (L, Hint)) {
644             break;
645         }
646         L = L->Next;
647     }
648     return L;
649 }
650
651
652
653 static unsigned GetHexNum (const char* S)
654 /* Get a hex number from a string */
655 {
656     unsigned I = 0;
657     unsigned Val = 0;
658     while (IsXDigit (S [I])) {
659         int C = (unsigned char) (S [I++]);
660         if (C >= 'A') {
661             C -= 'A' - 10;
662         } else {
663             C -= '0';
664         }
665         Val = (Val << 4) + C;
666     }
667     return Val;
668 }
669
670
671
672 static unsigned GetLabelNum (const char* L)
673 /* Return the label number of a label line */
674 {
675     CHECK (*L == 'L');
676     return GetHexNum (L+1);
677 }
678
679
680
681 static Line* GetTargetLine (const char* L)
682 /* Get the line with the target label of a jump. L must be a pointer to the
683  * string containing the label number.
684  */
685 {
686     Line* Target;
687
688     /* Get the label number of the target */
689     unsigned Label = GetLabelNum (L);
690     CHECK (Label < LabelCount);
691
692     /* Get the line with this label */
693     Target = Labels [Label];
694     CHECK (Target != 0 && (Target->Flags & OF_CODE) != 0);
695
696     /* And return it */
697     return Target;
698 }
699
700
701
702 static unsigned GetJumpDistance (Line* L, Line* Target)
703 /* Get the distance between both lines */
704 {
705     unsigned Distance = 0;
706
707     if (L != Target) {
708         if (Target->Index > L->Index) {
709             /* This is a forward jump. */
710             do {
711                 L = NextCodeLine (L);
712                 Distance += L->Size;
713             } while (L != Target);
714         } else {
715             /* This is a backward jump */
716             do {
717                 L = PrevCodeLine (L);
718                 Distance += L->Size;
719             } while (L != Target);
720         }
721     }
722
723     /* Return the calculated distance */
724     return Distance;
725 }
726
727
728
729 static int LineMatch (const Line* L, const char* Start)
730 /* Check if the start of the line matches Start */
731 {
732     return strncmp (L->Line, Start, strlen (Start)) == 0;
733 }
734
735
736
737 static int LineFullMatch (const Line* L, const char* Start)
738 /* Check if the matches Start */
739 {
740     return strcmp (L->Line, Start) == 0;
741 }
742
743
744
745 static int LineMatchX (const Line* L, const char** Start)
746 /* Check the start of the line against a list of patterns. Return the
747  * number of the pattern that matched, or -1 in case of no match.
748  */
749 {
750     unsigned I = 0;
751     while (*Start) {
752         if (LineMatch (L, *Start)) {
753             /* Found */
754             return I;
755         }
756         ++Start;
757         ++I;
758     }
759     /* Not found */
760     return -1;
761 }
762
763
764
765 static int LineFullMatchX (const Line* L, const char** Start)
766 /* Check the the line against a list of patterns. Return the
767  * number of the pattern that matched, or -1 in case of no match.
768  */
769 {
770     unsigned I = 0;
771     while (*Start) {
772         if (LineFullMatch (L, *Start)) {
773             /* Found */
774             return I;
775         }
776         ++Start;
777         ++I;
778     }
779     /* Not found */
780     return -1;
781 }
782
783
784
785 static int IsLoadAX (Line* L1, Line* L2)
786 /* Check if the both lines load a static variable into ax. That is, both lines
787  * look like
788  *      lda     x+0
789  *      ldx     x+0+1
790  */
791 {
792     return LineMatch (L1, "\tlda\t")                                    &&
793            LineMatch (L2, "\tldx\t")                                    &&
794            strncmp (L1->Line+5, L2->Line+5, strlen (L1->Line+5)) == 0   &&
795            strcmp (L2->Line+strlen(L1->Line), "+1") == 0;
796 }
797
798
799
800 /*****************************************************************************/
801 /*                          Initial optimizer setup                          */
802 /*****************************************************************************/
803
804
805
806 static void FindCodeStart (void)
807 /* Find and remember the first line of actual code */
808 {
809     Line* L = FindHint (FirstLine, "end_of_preamble");
810     FirstCode = L? L->Next : 0;
811 }
812
813
814
815 static unsigned EstimateDataSize (Line* L, unsigned Chunk)
816 /* Estimate the size of a .byte, .word or .dword command */
817 {
818     unsigned Size = Chunk;
819     char* S = L->Line;
820     while ((S = strchr (S, ',')) != 0) {
821         Size += Chunk;
822         ++S;
823     }
824     return Size;
825 }
826
827
828
829 static unsigned EstimateSize (Line* L)
830 /* Estimate the size of an instruction */
831 {
832     static const char* OneByteCmds [] = {
833         "\tdea",
834         "\tdex",
835         "\tdey",
836         "\tina",
837         "\tinx",
838         "\tiny"
839         "\ttax",
840         "\ttay",
841         "\ttsx",
842         "\ttxa",
843         "\ttya",
844         0
845     };
846     char OpStart;
847
848     if (L->Line [0] != '\t') {
849         return 0;
850     }
851     if (LineMatch (L, "\tldax\t")) {
852         /* Immidiate load of both, A and X */
853         return 4;
854     }
855     if (LineMatch (L, "\tld")) {
856         OpStart = L->Line [5];
857         return (OpStart == '#' || OpStart == '(')? 2 : 3;
858     }
859     if (LineMatch (L, "\tst")) {
860         OpStart = L->Line [5];
861         return (OpStart == '(')? 2 : 3;
862     }
863     if (LineMatch (L, "\t.byte\t")) {
864         return EstimateDataSize (L, 1);
865     }
866     if (LineMatch (L, "\t.word\t")) {
867         return EstimateDataSize (L, 2);
868     }
869     if (LineMatch (L, "\t.dword\t")) {
870         return EstimateDataSize (L, 4);
871     }
872     if (LineMatchX (L, ShortBranches) >= 0) {
873         return 2;
874     }
875     if (LineMatchX (L, LongBranches) >= 0) {
876         return 5;
877     }
878     if (LineMatchX (L, OneByteCmds) >= 0) {
879         return 1;
880     }
881     return 3;
882 }
883
884
885
886 static void MarkCodeLines (void)
887 /* Mark all lines that are inside a code segment */
888 {
889     int InCode = 1;
890     Line* L = FirstCode;
891     while (L) {
892         if (IsSegHint (L)) {
893             InCode = IsHint (L, "seg:code");
894         } else if (InCode && L->Line[0] != '\0') {
895             L->Flags |= OF_CODE;
896             L->Size = EstimateSize (L);
897         }
898         L = L->Next;
899     }
900 }
901
902
903
904 static void CreateLabelList (void)
905 /* Create a list with pointers to local labels */
906 {
907     unsigned I;
908     Line* L;
909
910
911     /* Get the next label number. This is also the current label count.
912      * Make some room for more labels when optimizing code.
913      */                            
914     LabelCount = GetLocalLabel () + 100;
915
916     /* Allocate memory for the array and clear it */
917     Labels = xmalloc (LabelCount * sizeof (Line*));
918     for (I = 0; I < LabelCount; ++I) {
919         Labels [I] = 0;
920     }
921
922     /* Walk through the code and insert all label lines */
923     L = FirstLine;
924     while (L) {
925         if (IsLocalLabel (L)) {
926             unsigned LabelNum = GetLabelNum (L->Line);
927             CHECK (LabelNum < LabelCount);
928             Labels [LabelNum] = L;
929         }
930         L = L->Next;
931     }
932 }
933
934
935
936 static unsigned AllocLabel (void)
937 /* Get a new label. The current code does not realloc the label list, so there
938  * must be room enough in the current list.
939  */
940 {
941     unsigned I;
942
943     /* Search for a free slot, start at 1, since 0 is "no label" */
944     for (I = 1; I < LabelCount; ++I) {
945         if (Labels[I] == 0) {
946             /* Found a free slot */
947             return I;
948         }
949     }
950
951     /* No label space available */
952     Internal ("Out of label space in the optimizer");
953
954     /* Not reached */
955     return 0;
956 }
957
958
959
960 /*****************************************************************************/
961 /*                             Helper functions                              */
962 /*****************************************************************************/
963
964
965
966 static int GetNextCodeLines (Line* L, Line** Lines, unsigned Count)
967 /* Get a number of code lines ignoring hints and other stuff. The function
968  * returns 1 if we got the lines and 0 if we are at the end of the code
969  * segment or if we hit a label.
970  */
971 {
972     while (Count--) {
973
974         /* Get the next valid line */
975         do {
976             L = NextCodeLine (L);
977         } while (L && IsHintLine (L));
978
979         /* Did we get one? */
980         if (L == 0 || IsLabel (L)) {
981             /* Error */
982             return 0;
983         }
984
985         /* Remember the line */
986         *Lines++ = L;
987     }
988
989     /* Success */
990     return 1;
991 }
992
993
994
995 static int FindCond (const char* Suffix)
996 /* Map a condition suffix to a code. Return the code or -1 on failure */
997 {
998     int I;
999
1000     /* Linear search */
1001     for (I = 0; I < sizeof (CmpSuffixTab) / sizeof (CmpSuffixTab [0]); ++I) {
1002         if (strncmp (Suffix, CmpSuffixTab [I], strlen (CmpSuffixTab[I])) == 0) {
1003             /* Found */
1004             return I;
1005         }
1006     }
1007
1008     /* Not found */
1009     return -1;
1010 }
1011
1012
1013
1014 static int CheckAndGetIntCmp (const Line* JSR, const Line* JMP)
1015 /* Helper function to check for a compare subroutine call followed by a
1016  * conditional branch. Will return the condition found, or -1 if no
1017  * or invalid condition.
1018  */
1019 {
1020     char Cond[5];
1021     const char* Tail;
1022     int C;
1023
1024     /* Extract the condition from the function name. */
1025     if ((Cond [0] = JSR->Line [8]) == 'u') {
1026         Cond [1] = JSR->Line [9];
1027         Cond [2] = JSR->Line [10];
1028         Cond [3] = '\0';
1029         Tail = JSR->Line + 11;
1030     } else {
1031         Cond [1] = JSR->Line [9];
1032         Cond [2] = '\0';
1033         Tail = JSR->Line + 10;
1034     }
1035
1036     /* Check if this is indeed an integer function */
1037     if (strcmp (Tail, "ax") != 0) {
1038         /* No! */
1039         return -1;
1040     }
1041
1042     /* Get the condition code */
1043     C = FindCond (Cond);
1044     if (C < 0) {
1045         /* OOPS! */
1046         return -1;
1047     }
1048
1049     /* Invert the code if we jump on condition not met. */
1050     if (JMP->Line [2] == 'e' && JMP->Line [3] == 'q') {
1051         /* Jumps if condition false, invert condition */
1052         C = CmpInvertTab [C];
1053     }
1054
1055     /* Return the condition code */
1056     return C;
1057 }
1058
1059
1060
1061 static int TosCmpFunc (Line* L)
1062 /* Check if this is a call to one of the TOS compare functions (tosgtax).
1063  * Return the condition code or -1 if not found.
1064  */
1065 {
1066     if (LineMatch (L, "\tjsr\ttos")                             &&
1067         strcmp (L->Line+strlen(L->Line)-2, "ax") == 0) {
1068
1069         /* Ok, found. Get the condition. */
1070         return FindCond (L->Line+8);
1071
1072     } else {
1073
1074         /* Not found */
1075         return -1;
1076     }
1077 }
1078
1079
1080
1081 static int IsUnsignedCmp (int Code)
1082 /* Check if this is an unsigned compare */
1083 {
1084     CHECK (Code >= 0);
1085     return CmpSignedTab [Code] == 0;
1086 }
1087
1088
1089
1090 static void InvertZJump (Line* L)
1091 /* Invert a jeq/jne jump */
1092 {
1093     if (L->Line [2] == 'n' && L->Line [3] == 'e') {
1094         /* This was a bne/jne */
1095         L->Line [2] = 'e';
1096         L->Line [3] = 'q';
1097     } else {
1098         /* This was (hopefully) a beq/jeq */
1099         L->Line [2] = 'n';
1100         L->Line [3] = 'e';
1101     }
1102 }
1103
1104
1105
1106 static int FindCmd (Line* L)
1107 {
1108     int I;
1109
1110     /* Search for the known patterns */
1111     for (I = 0; I < COUNT(CmdDesc); ++I) {
1112         if (CmdDesc[I].FullMatch) {
1113             if (LineFullMatch (L, CmdDesc[I].Insn)) {
1114                 /* found */
1115                 return I;
1116             }
1117         } else {
1118             if (LineMatch (L, CmdDesc[I].Insn)) {
1119                 /* found */
1120                 return I;
1121             }
1122         }
1123     }
1124     /* Not found */
1125     return -1;
1126 }
1127
1128
1129
1130 static unsigned RVUInt2 (Line* L,
1131                          LineColl* LC,      /* To remember visited lines */
1132                          unsigned Used,     /* Definitely used registers */
1133                          unsigned Unused)   /* Definitely unused registers */
1134 /* Subfunction for RegValUsed. Will be called recursively in case of branches. */
1135 {
1136     int I;
1137
1138     /* Check the following instructions. We classifiy them into primary
1139      * loads (register value not used), neutral (check next instruction),
1140      * and unknown (assume register was used).
1141      */
1142     while (1) {
1143
1144         unsigned R;
1145
1146         /* Get the next line and follow jumps */
1147         do {
1148
1149             /* Handle jumps to local labels (continue there) */
1150             if (LineMatch (L, "\tjmp\tL") || LineMatch (L, "\tbra\tL")) {
1151                 /* Get the target of the jump */
1152                 L = GetTargetLine (L->Line+5);
1153             }
1154
1155             /* Get the next line, skip local labels */
1156             do {
1157                 L = NextCodeSegLine (L);
1158             } while (L && (IsLocalLabel (L) || L->Line[0] == '\0'));
1159
1160             /* Bail out if we're done */
1161             if (L == 0 || IsExtLabel (L)) {
1162                 /* End of function reached */
1163                 goto ExitPoint;
1164             }
1165
1166             /* Check if we had this line already. If so, bail out, if not,
1167              * add it to the list of known lines.
1168              */
1169             if (LCHasLine (LC, L) || !LCAddLine (LC, L)) {
1170                 goto ExitPoint;
1171             }
1172
1173         } while (LineMatch (L, "\tjmp\tL") || LineMatch (L, "\tbra\tL"));
1174
1175         /* Special handling of code hints */
1176         if (IsHintLine (L)) {
1177
1178             if (IsHint (L, "a:-") && (Used & REG_A) == 0) {
1179                 Unused |= REG_A;
1180             } else if (IsHint (L, "x:-") && (Used & REG_X) == 0) {
1181                 Unused |= REG_X;
1182             } else if (IsHint (L, "y:-") && (Used & REG_Y) == 0) {
1183                 Unused |= REG_Y;
1184             }
1185
1186         /* Special handling for branches */
1187         } else if (LineMatchX (L, ShortBranches) >= 0 ||
1188             LineMatchX (L, LongBranches) >= 0) {
1189             const char* Target = L->Line+5;
1190             if (Target[0] == 'L') {
1191                 /* Jump to local label. Check the register usage starting at
1192                  * the branch target and at the code following the branch.
1193                  * All registers that are unused in both execution flows are
1194                  * returned as unused.
1195                  */
1196                 unsigned U1, U2;
1197                 U2 = RVUInt1 (GetTargetLine (Target), LC, Used, Unused);
1198                 U1 = RVUInt1 (L, LC, Used, Unused);
1199                 return U1 | U2;         /* Used in any of the branches */
1200             }
1201         } else {
1202
1203             /* Search for the instruction in this line */
1204             I = FindCmd (L);
1205
1206             /* If we don't find it, assume all other registers are used */
1207             if (I < 0) {
1208                 break;
1209             }
1210
1211             /* Evaluate the use flags, check for addressing modes */
1212             R = CmdDesc[I].Use;
1213             if (IsXAddrMode (L)) {
1214                 R |= REG_X;
1215             } else if (IsYAddrMode (L)) {
1216                 R |= REG_Y;
1217             }
1218             if (R) {
1219                 /* Remove registers that were already new loaded */
1220                 R &= ~Unused;
1221
1222                 /* Remember the remaining registers */
1223                 Used |= R;
1224             }
1225
1226             /* Evaluate the load flags */
1227             R = CmdDesc[I].Load;
1228             if (R) {
1229                 /* Remove registers that were already used */
1230                 R &= ~Used;
1231
1232                 /* Remember the remaining registers */
1233                 Unused |= R;
1234             }
1235
1236         }
1237
1238         /* If we know about all registers, bail out */
1239         if ((Used | Unused) == REG_ALL) {
1240             break;
1241         }
1242     }
1243
1244 ExitPoint:
1245     /* Return to the caller the complement of all unused registers */
1246     return ~Unused & REG_ALL;
1247 }
1248
1249
1250
1251 static unsigned RVUInt1 (Line* L,
1252                          LineColl* LC,      /* To remember visited lines */
1253                          unsigned Used,     /* Definitely used registers */
1254                          unsigned Unused)   /* Definitely unused registers */
1255 /* Subfunction for RegValUsed. Will be called recursively in case of branches. */
1256 {
1257     /* Remember the current count of the line collection */
1258     unsigned Count = LC->Count;
1259
1260     /* Call the worker routine */
1261     unsigned R = RVUInt2 (L, LC, Used, Unused);
1262
1263     /* Restore the old count */
1264     LC->Count = Count;
1265
1266     /* Return the result */
1267     return R;
1268 }
1269
1270
1271
1272 static unsigned RegValUsed (Line* Start)
1273 /* Check the next instructions after the one in L for register usage. If
1274  * a register is used as an index, or in a store or other instruction, it
1275  * is assumed to be used. If a register is loaded with a value, before it
1276  * was used by one of the actions described above, it is assumed unused.
1277  * If the end of the lookahead is reached, all registers that are uncertain
1278  * are marked as used.
1279  * The result of the search is returned.
1280  */
1281 {
1282     unsigned R;
1283
1284     /* Create a new line collection and enter the start line */
1285     LineColl* LC = NewLineColl (256);
1286     LCAddLine (LC, Start);
1287
1288     /* Call the recursive subfunction */
1289     R = RVUInt1 (Start, LC, REG_NONE, REG_NONE);
1290
1291     /* Delete the line collection */
1292     FreeLineColl (LC);
1293
1294     /* Return the registers used */
1295     return R;
1296 }
1297
1298
1299
1300 static int RegAUsed (Line* Start)
1301 /* Check if the value in A is used. */
1302 {
1303     return (RegValUsed (Start) & REG_A) != 0;
1304 }
1305
1306
1307
1308 static int RegXUsed (Line* Start)
1309 /* Check if the value in X is used. */
1310 {
1311     return (RegValUsed (Start) & REG_X) != 0;
1312 }
1313
1314
1315
1316 static int RegYUsed (Line* Start)
1317 /* Check if the value in Y is used. */
1318 {
1319     return (RegValUsed (Start) & REG_Y) != 0;
1320 }
1321
1322
1323
1324 /*****************************************************************************/
1325 /*                          Real optimizer routines                          */
1326 /*****************************************************************************/
1327
1328
1329
1330 static void OptCompares1 (void)
1331 /* Try to optimize the integer compare subroutines. */
1332 {
1333     Line*    L2[10];            /* Line lookahead */
1334     int      Cond;              /* Condition to evaluate */
1335     unsigned Label;             /* Local label number */
1336     unsigned Offs;              /* Stack offset */
1337     Line*    DelStart;          /* First line to delete */
1338
1339     Line* L = FirstCode;
1340     while (L) {
1341
1342         /* Search for compares of local byte sized variables. This looks
1343          * like:
1344          *
1345          *      ldy     #$xx
1346          *      ldx     #$00
1347          *      lda     (sp),y
1348          *      jsr     pushax
1349          *      ldy     #$yy
1350          *      ldx     #$00
1351          *      lda     (sp),y
1352          *      jsr     tosugtax
1353          *
1354          * Replace it by a direct compare:
1355          *
1356          *      ldy     #$xx
1357          *      lda     (sp),y
1358          *      ldy     #$yy
1359          *      cmp     (sp),y
1360          *      jsr     boolugt
1361          */
1362         if (LineMatch (L, "\tldy\t#$")                                  &&
1363             GetNextCodeLines (L, L2, 7)                                 &&
1364             LineFullMatch (L2[0], "\tldx\t#$00")                        &&
1365             LineFullMatch (L2[1], "\tlda\t(sp),y")                      &&
1366             LineFullMatch (L2[2], "\tjsr\tpushax")                      &&
1367             LineMatch     (L2[3], "\tldy\t#$")                          &&
1368             LineFullMatch (L2[4], "\tldx\t#$00")                        &&
1369             LineFullMatch (L2[5], "\tlda\t(sp),y")                      &&
1370             (Cond = TosCmpFunc (L2[6])) >= 0) {
1371
1372             /* Get the stack offset and correct it, since we will remove
1373              * the pushax.
1374              */
1375             Offs = GetHexNum (L2[3]->Line+7) - 2;
1376
1377             /* Replace it */
1378             L = NewLineAfter (L, "\tlda\t(sp),y");
1379             L = NewLineAfter (L, "\tldy\t#$%02X", Offs);
1380             L = NewLineAfter (L, "\tcmp\t(sp),y");
1381             L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
1382
1383             /* Remove the old cruft */
1384             FreeLines (L2[0], L2[6]);
1385         }
1386
1387         /* Compares of byte sized global variables */
1388         else if (LineFullMatch (L, "\tldx\t#$00")                       &&
1389                  GetNextCodeLines (L, L2, 5)                            &&
1390                  LineMatch     (L2[0], "\tlda\t")                       &&
1391                  LineFullMatch (L2[1], "\tjsr\tpushax")                 &&
1392                  LineFullMatch (L2[2], "\tldx\t#$00")                   &&
1393                  LineMatch     (L2[3], "\tlda\t")                       &&
1394                  (Cond = TosCmpFunc (L2[4])) >= 0) {
1395
1396             /* Replace it */
1397             if (IsXAddrMode (L2[0])) {
1398                 /* The load is X indirect, so we may not remove the load
1399                  * of the X register.
1400                  */
1401                 L = L2[0];
1402                 DelStart = L2[1];
1403             } else {
1404                 L = ReplaceLine  (L, L2[0]->Line);
1405                 DelStart = L2[0];
1406             }
1407             L = NewLineAfter (L, "\tcmp\t%s", L2[3]->Line+5);
1408             L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
1409
1410             /* Remove the old cruft */
1411             FreeLines (DelStart, L2[4]);
1412
1413         }
1414
1415         /* Byte sized local to global */
1416         else if (LineMatch (L, "\tldy\t#$")                             &&
1417                  GetNextCodeLines (L, L2, 6)                            &&
1418                  LineFullMatch (L2[0], "\tldx\t#$00")                   &&
1419                  LineFullMatch (L2[1], "\tlda\t(sp),y")                 &&
1420                  LineFullMatch (L2[2], "\tjsr\tpushax")                 &&
1421                  LineFullMatch (L2[3], "\tldx\t#$00")                   &&
1422                  LineMatch     (L2[4], "\tlda\t")                       &&
1423                  (Cond = TosCmpFunc (L2[5])) >= 0) {
1424
1425             /* Replace it */
1426             L = NewLineAfter (L, L2[1]->Line);
1427             L = NewLineAfter (L, "\tcmp\t%s", L2[4]->Line+5);
1428             L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
1429
1430             /* Remove the old cruft */
1431             FreeLines (L2[0], L2[5]);
1432
1433         }
1434
1435         /* Byte sized global to local */
1436         else if (LineFullMatch (L, "\tldx\t#$00")                       &&
1437                  GetNextCodeLines (L, L2, 6)                            &&
1438                  LineMatch     (L2[0], "\tlda\t")                       &&
1439                  LineFullMatch (L2[1], "\tjsr\tpushax")                 &&
1440                  LineMatch     (L2[2], "\tldy\t#$")                     &&
1441                  LineFullMatch (L2[3], "\tldx\t#$00")                   &&
1442                  LineFullMatch (L2[4], "\tlda\t(sp),y")                 &&
1443                  (Cond = TosCmpFunc (L2[5])) >= 0) {
1444
1445             /* Get the stack offset and correct it, since we will remove
1446              * the pushax.
1447              */
1448             Offs = GetHexNum (L2[2]->Line+7) - 2;
1449
1450             /* Replace it */
1451             if (IsXAddrMode (L2[0])) {
1452                 /* The load is X indirect, so we may not remove the load
1453                  * of the X register.
1454                  */
1455                 L = L2[0];
1456                 DelStart = L2[1];
1457             } else {
1458                 L = ReplaceLine  (L, L2[0]->Line);
1459                 DelStart = L2[0];
1460             }
1461             L = NewLineAfter (L, "\tldy\t#$%02X", Offs);
1462             L = NewLineAfter (L, "\tcmp\t(sp),y");
1463             L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
1464
1465             /* Remove the old cruft */
1466             FreeLines (DelStart, L2[5]);
1467
1468         }
1469
1470         /* Search for unsigned compares against global variables. This looks
1471          * like:
1472          *
1473          *      jsr     pushax
1474          *      lda     _b+0
1475          *      ldx     _b+0+1
1476          *      jsr     tosugtax
1477          *
1478          * Replace that by a direct compare:
1479          *
1480          *      cpx     _b+0+1
1481          *      bne     L
1482          *      cmp     _b+0
1483          * L:
1484          *      jsr     boolugt
1485          */
1486         else if (LineFullMatch (L, "\tjsr\tpushax")                     &&
1487                  GetNextCodeLines (L, L2, 3)                            &&
1488                  IsLoadAX (L2[0], L2[1])                                &&
1489                  (Cond = TosCmpFunc (L2[2])) >= 0                       &&
1490                  IsUnsignedCmp (Cond)) {
1491
1492             /* Get a free label number */
1493             Label = AllocLabel ();
1494
1495             /* Replace the code */
1496             L = ReplaceLine  (L, "\tcpx\t%s", L2[1]->Line+5);
1497             L = NewLineAfter (L, "\tbne\tL%04X", Label);
1498             L = NewLineAfter (L, "\tcmp\t%s", L2[0]->Line+5);
1499             L = NewLabelAfter(L, Label);
1500             L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
1501
1502             /* Remove the old code */
1503             FreeLines (L2[0], L2[2]);
1504
1505         }
1506
1507         L = NextCodeLine (L);
1508     }
1509 }
1510
1511
1512
1513 static void OptDeadJumps (void)
1514 /* Remove jumps to the following instruction */
1515 {
1516     static const char* Jumps [] = {
1517         "\tbeq\tL",
1518         "\tbne\tL",
1519         "\tjeq\tL",
1520         "\tjne\tL",
1521         "\tjmp\tL",
1522         0
1523     };
1524
1525     Line* L = FirstCode;
1526     while (L) {
1527
1528         /* Get a pointer to the next instruction line */
1529         Line* NextLine = NextInstruction (L);
1530
1531         /* Is this line a jump? */
1532         int I = LineMatchX (L, Jumps);
1533         if (I >= 0) {
1534             /* Yes. Get the target label, skip labels */
1535             Line* Target = NextInstruction (GetTargetLine (L->Line+5));
1536
1537             /* If the target label is the next line, remove the jump */
1538             if (Target == NextLine) {
1539                 FreeLine (L);
1540             }
1541         }
1542
1543         /* Go to the next line */
1544         L = NextLine;
1545     }
1546 }
1547
1548
1549
1550 static void OptLoads (void)
1551 /* Remove unnecessary loads of values */
1552 {
1553     unsigned Changes;
1554
1555     do {
1556
1557         Line* L2 [10];
1558         Line* L = FirstCode;
1559         Changes = 0;
1560         while (L) {
1561
1562             /* Check for
1563              *
1564              *          ldy     #$..
1565              *          lda     (sp),y
1566              *          tax
1567              *          dey
1568              *          lda     (sp),y
1569              *          jsr     pushax
1570              *
1571              * and replace it by
1572              *
1573              *          ldy     #$..
1574              *          jsr     pushwysp
1575              *
1576              * or even
1577              *
1578              *          jsr     pushw0sp
1579              *
1580              * This change will cost 3 cycles (one additional jump inside the
1581              * subroutine), but it saves a lot of code (6 bytes per occurrence),
1582              * so we will accept the overhead. It may even be possible to rewrite
1583              * the library routine to get rid of the additional overhead.
1584              */
1585             if (LineMatch (L, "\tldy\t#$")                      &&
1586                 GetNextCodeLines (L, L2, 5)                     &&
1587                 LineFullMatch (L2 [0], "\tlda\t(sp),y") &&
1588                 LineFullMatch (L2 [1], "\ttax")         &&
1589                 LineFullMatch (L2 [2], "\tdey")         &&
1590                 LineFullMatch (L2 [3], "\tlda\t(sp),y") &&
1591                 LineFullMatch (L2 [4], "\tjsr\tpushax")) {
1592
1593                 /* Found - replace it */
1594                 if (LineFullMatch (L, "\tldy\t#$01")) {
1595                     /* Word at offset zero */
1596                     FreeLine (L);
1597                     L = ReplaceLine (L2 [4], "\tjsr\tpushw0sp");
1598                 } else {
1599                     ReplaceLine (L2 [4], "\tjsr\tpushwysp");
1600                 }
1601
1602                 /* Delete the remaining lines */
1603                 FreeLines (L2 [0], L2 [3]);
1604
1605                 /* We have changes */
1606                 ++Changes;
1607
1608             /* Check for
1609              *
1610              *          ldy     #$xx
1611              *          lda     (sp),y
1612              *          tax
1613              *          dey
1614              *          lda     (sp),y
1615              *          ldy     #$yy
1616              *          jsr     ldauidx
1617              *
1618              * and replace it by
1619              *
1620              *          ldy     #$xx
1621              *          ldx     #$yy
1622              *          jsr     ldauiysp
1623              *
1624              * or even
1625              *
1626              *          jsr     ldaui0sp
1627              *
1628              * This change will cost 2 cycles, but it saves a lot of code (6 bytes
1629              * per occurrence), so we will accept the overhead. It may even be
1630              * possible to rewrite the library routine to get rid of the additional
1631              * overhead.
1632              */
1633             } else if (LineMatch (L, "\tldy\t#$")               &&
1634                 GetNextCodeLines (L, L2, 6)                     &&
1635                 LineFullMatch (L2 [0], "\tlda\t(sp),y") &&
1636                 LineFullMatch (L2 [1], "\ttax")         &&
1637                 LineFullMatch (L2 [2], "\tdey")         &&
1638                 LineFullMatch (L2 [3], "\tlda\t(sp),y") &&
1639                 LineMatch     (L2 [4], "\tldy\t#$")             &&
1640                 LineFullMatch (L2 [5], "\tjsr\tldauidx")) {
1641
1642                 /* Found - replace it */
1643                 L2 [4]->Line [3] = 'x';         /* Change to ldx */
1644                 if (LineFullMatch (L, "\tldy\t#$01")) {
1645                     /* Word at offset zero */
1646                     FreeLine (L);
1647                     L = ReplaceLine (L2 [5], "\tjsr\tldaui0sp");
1648                 } else {
1649                     ReplaceLine (L2 [5], "\tjsr\tldauiysp");
1650                 }
1651
1652                 /* Delete the remaining lines */
1653                 FreeLines (L2 [0], L2 [3]);
1654
1655                 /* We have changes */
1656                 ++Changes;
1657
1658             /* Search for:
1659              *
1660              *          lda     (sp),y
1661              *          jsr     pusha
1662              *
1663              * And replace by
1664              *
1665              *          jsr     pushaysp
1666              */
1667             } else if (LineFullMatch (L, "\tlda\t(sp),y")       &&
1668                 GetNextCodeLines (L, L2, 1)                     &&
1669                 LineFullMatch (L2 [0], "\tjsr\tpusha")) {
1670
1671                 /* Found, replace it */
1672                 L = ReplaceLine (L, "\tjsr\tpushaysp");
1673                 FreeLine (L2 [0]);
1674
1675                 /* We have changes */
1676                 ++Changes;
1677
1678             /* Search for:
1679              *
1680              *          ldx     xx
1681              *          lda     yy
1682              *          sta     zzz
1683              *          stx     zzz+1
1684              *
1685              * and replace it by:
1686              *
1687              *          lda     xx
1688              *          sta     zzz+1
1689              *          lda     yy
1690              *          sta     zzz
1691              *
1692              * provided that that the X register is not used later. While this is
1693              * no direct optimization, it helps with other optimizations.
1694              */
1695             } else if (LineMatch (L, "\tldx\t")         &&
1696                 GetNextCodeLines (L, L2, 3)                     &&
1697                 LineMatch (L2 [0], "\tlda\t")           &&
1698                 Is16BitStore (L2[1], L2[2])                     &&
1699                 !RegXUsed (L2[2])) {
1700
1701                 /* Found - replace it */
1702                 L->Line[3] = 'a';
1703                 NewLineAfter (L, "\tsta\t%s", L2[2]->Line+5);
1704                 FreeLine (L2[2]);
1705                 L = L2[1];
1706
1707                 /* We have changes */
1708                 ++Changes;
1709
1710             /* Search for:
1711              *
1712              *          ldx     xx
1713              *          lda     yy
1714              *          ldy     #$zz
1715              *          jsr     staxysp
1716              *
1717              * and replace it by:
1718              *
1719              *          lda     xx
1720              *  ldy     #$zz+1
1721              *          sta     (sp),y
1722              *          dey
1723              *  lda     yy
1724              *          sta     (sp),y
1725              *
1726              * provided that that the X register is not used later. This code
1727              * sequence is two bytes longer, but a lot faster and it does not
1728              * use the X register, so other loads may get removed later.
1729              */
1730             } else if (LineMatch (L, "\tldx\t")         &&
1731                 GetNextCodeLines (L, L2, 3)                     &&
1732                 LineMatch (L2 [0], "\tlda\t")           &&
1733                 LineMatch (L2 [1], "\tldy\t#$")         &&
1734                 LineFullMatch (L2 [2], "\tjsr\tstaxysp")        &&
1735                 !RegXUsed (L2[2])) {
1736
1737                 /* Found - replace it */
1738                 L->Line[3] = 'a';
1739                 L = NewLineAfter (L, "\tldy\t#$%02X", GetHexNum (L2[1]->Line+7)+1);
1740                 L = NewLineAfter (L, "\tsta\t(sp),y");
1741                 L = NewLineAfter (L, "\tdey");
1742                 L = NewLineAfter (L2[0], "\tsta\t(sp),y");
1743
1744                 /* Remove the remaining lines */
1745                 FreeLines (L2[1], L2[2]);
1746
1747                 /* We have changes */
1748                 ++Changes;
1749
1750             /* Search for:
1751              *
1752              *          ldx     xx
1753              *          lda     yy
1754              *          jsr     stax0sp
1755              *
1756              * and replace it by:
1757              *
1758              *          lda     xx
1759              *          ldy     #$01
1760              *          sta     (sp),y
1761              *          dey
1762              *          lda     yy
1763              *          sta     (sp),y
1764              *
1765              * provided that that the X register is not used later. This code
1766              * sequence is four bytes longer, but a lot faster and it does not
1767              * use the X register, so other loads may get removed later.
1768              */
1769             } else if (LineMatch (L, "\tldx\t")         &&
1770                 GetNextCodeLines (L, L2, 2)                     &&
1771                 LineMatch (L2 [0], "\tlda\t")           &&
1772                 LineFullMatch (L2 [1], "\tjsr\tstax0sp")        &&
1773                 !RegXUsed (L2[1])) {
1774
1775                 /* Found - replace it */
1776                 L->Line[3] = 'a';
1777                 L = NewLineAfter (L, "\tldy\t#$01");
1778                 L = NewLineAfter (L, "\tsta\t(sp),y");
1779                 L = NewLineAfter (L, "\tdey");
1780                 L = NewLineAfter (L2[0], "\tsta\t(sp),y");
1781
1782                 /* Remove the remaining line */
1783                 FreeLine (L2[1]);
1784
1785                 /* We have changes */
1786                 ++Changes;
1787
1788             /* Search for
1789              *
1790              *          adc     xx
1791              *          bcc     *+3
1792              *          inx
1793              *
1794              * Remove the handling of the high byte if the X register
1795              * is not used any more
1796              */
1797             } else if (LineMatch (L, "\tadc\t")                 &&
1798                 GetNextCodeLines (L, L2, 3)                     &&
1799                 LineFullMatch (L2[0], "\tbcc\t*+3")             &&
1800                 LineFullMatch (L2[1], "\tinx")          &&
1801                 L2[1]->Next                                     &&
1802                 IsHint (L2[1]->Next, "x:!")                     &&
1803                 !RegXUsed (L2[1])) {
1804
1805                 /* Delete the lines */
1806                 FreeLines (L2[0], L2[1]->Next);
1807
1808                 /* We have changes */
1809                 ++Changes;
1810
1811             /* Search for
1812              *
1813              *          sbc     xx
1814              *          bcs     *+3
1815              *          dex
1816              *
1817              * Remove the handling of the high byte if the X register
1818              * is not used any more
1819              */
1820             } else if (LineMatch (L, "\tsbc\t")                 &&
1821                 GetNextCodeLines (L, L2, 3)                     &&
1822                 LineFullMatch (L2[0], "\tbcs\t*+3")             &&
1823                 LineFullMatch (L2[1], "\tdex")          &&
1824                 L2[1]->Next                                     &&
1825                 IsHint (L2[1]->Next, "x:!")                     &&
1826                 !RegXUsed (L2[1])) {
1827
1828                 /* Delete the lines */
1829                 FreeLines (L2[0], L2[1]->Next);
1830
1831                 /* We have changes */
1832                 ++Changes;
1833
1834             /* Search for
1835              *
1836              *          lda     xx
1837              *          bpl     *+3
1838              *          dex
1839              *
1840              * Remove the handling of the high byte if the X register
1841              * is not used any more
1842              */
1843             } else if (LineMatch (L, "\tlda\t")                 &&
1844                 GetNextCodeLines (L, L2, 3)                     &&
1845                 LineFullMatch (L2[0], "\tbpl\t*+3")             &&
1846                 LineFullMatch (L2[1], "\tdex")          &&
1847                 L2[1]->Next                                     &&
1848                 IsHint (L2[1]->Next, "x:!")                     &&
1849                 !RegXUsed (L2[1])) {
1850
1851                 /* Delete the lines */
1852                 FreeLines (L2[0], L2[1]->Next);
1853
1854                 /* We have changes */
1855                 ++Changes;
1856
1857             }
1858
1859             /* All other patterns start with this one: */
1860             if (!LineFullMatch (L, "\tldx\t#$00")) {
1861                 /* Next line */
1862                 goto NextLine;
1863             }
1864
1865             /* Search for:
1866              *
1867              *          ldx     #$00
1868              *          jsr     pushax
1869              *
1870              * and replace it by:
1871              *
1872              *          jsr     pusha0
1873              *
1874              */
1875             if (GetNextCodeLines (L, L2, 1)                     &&
1876                 LineFullMatch (L2 [0], "\tjsr\tpushax")) {
1877
1878                 /* Replace the subroutine call */
1879                 L = ReplaceLine (L, "\tjsr\tpusha0");
1880
1881                 /* Remove the unnecessary line */
1882                 FreeLine (L2[0]);
1883
1884                 /* We have changes */
1885                 ++Changes;
1886             }
1887
1888             /* Search for:
1889              *
1890              *          ldx     #$00
1891              *          lda     ...
1892              *          jsr     pushax
1893              *
1894              * and replace it by:
1895              *
1896              *          lda     ...
1897              *          jsr     pusha0
1898              *
1899              */
1900             else if (GetNextCodeLines (L, L2, 2)                        &&
1901                      LineMatch (L2 [0], "\tlda\t")                      &&
1902                      LineFullMatch (L2 [1], "\tjsr\tpushax")) {
1903
1904                 /* Be sure, X is not used in the load */
1905                 if (NoXAddrMode (L2 [0])) {
1906
1907                     /* Replace the subroutine call */
1908                     L2 [1] = ReplaceLine (L2 [1], "\tjsr\tpusha0");
1909
1910                     /* Remove the unnecessary load */
1911                     FreeLine (L);
1912
1913                     /* L must be valid */
1914                     L = L2 [0];
1915
1916                     /* We have changes */
1917                     ++Changes;
1918                 }
1919
1920             }
1921
1922             /* Search for:
1923              *
1924              *          ldx     #$00
1925              *          lda     ...
1926              *          cmp     #$..
1927              *
1928              * and replace it by:
1929              *
1930              *          lda     ...
1931              *          cmp     #$..
1932              */
1933             else if (GetNextCodeLines (L, L2, 2)                &&
1934                      LineMatch (L2 [0], "\tlda\t")              &&
1935                      LineMatch (L2 [1], "\tcmp\t#$")) {
1936
1937                 /* Be sure, X is not used in the load */
1938                 if (NoXAddrMode (L2 [0])) {
1939
1940                     /* Remove the unnecessary load */
1941                     FreeLine (L);
1942
1943                     /* L must be valid */
1944                     L = L2 [0];
1945
1946                     /* We have changes */
1947                     ++Changes;
1948                 }
1949             }
1950
1951             /* Search for:
1952              *
1953              *          ldx     #$00
1954              *          lda     ...
1955              *          jsr     bnega
1956              *
1957              * and replace it by:
1958              *
1959              *          lda     ...
1960              *          jsr     bnega
1961              */
1962             else if (GetNextCodeLines (L, L2, 2)                &&
1963                      LineMatch (L2 [0], "\tlda\t")              &&
1964                      LineFullMatch (L2 [1], "\tjsr\tbnega")) {
1965
1966                 /* Be sure, X is not used in the load */
1967                 if (NoXAddrMode (L2 [0])) {
1968
1969                     /* Remove the unnecessary load */
1970                     FreeLine (L);
1971
1972                     /* L must be valid */
1973                     L = L2 [0];
1974
1975                     /* We have changes */
1976                     ++Changes;
1977                 }
1978             }
1979
1980     NextLine:
1981             /* Go to the next line */
1982             L = NextCodeLine (L);
1983         }
1984
1985     } while (Changes);
1986 }
1987
1988
1989
1990 static void OptRegLoads (void)
1991 /* Remove unnecessary loads of registers */
1992 {
1993     unsigned Deletions;
1994     Line* L;
1995     Line* Lx;
1996
1997     /* Repeat this until there is nothing more to delete */
1998     do {
1999         Deletions = 0;
2000         L = FirstCode;
2001         while (L) {
2002
2003             int Delete = 0;
2004
2005             /* Search for a load of X and check if the value is used later */
2006             if (LineMatch (L, "\tldx\t")                &&
2007                 !RegXUsed (L)                           &&
2008                 !IsCondJump (NextInstruction (L))) {
2009
2010                 /* Remember to delete this line */
2011                 Delete = 1;
2012             }
2013
2014             /* Search for a load of A and check if the value is used later */
2015             else if (LineMatch (L, "\tlda\t")           &&
2016                        !RegAUsed (L)                    &&
2017                        !IsCondJump (NextInstruction (L))) {
2018
2019                 /* Remember to delete this line */
2020                 Delete = 1;
2021             }
2022
2023             /* Search for a load of Y and check if the value is used later */
2024             else if (LineMatch (L, "\tldy\t")           &&
2025                        !RegYUsed (L)                    &&
2026                        !IsCondJump (NextInstruction (L))) {
2027
2028                 /* Remember to delete this line */
2029                 Delete = 1;
2030             }
2031
2032             /* Go to the next line, delete the current if requested */
2033             Lx = L;
2034             L = NextCodeLine (L);
2035             if (Delete) {
2036                 FreeLine (Lx);
2037                 ++Deletions;
2038             }
2039         }
2040     } while (Deletions > 0);
2041 }
2042
2043
2044
2045 static int OptPtrOps1 (Line** Start)
2046 /* Optimize several pointer and array constructs - subfunction 1 */
2047 {
2048     Line* L2[15];
2049     Line** L3;
2050     unsigned NeedLoad;
2051     unsigned LinesToRemove;
2052     unsigned Inc;
2053     unsigned Done;
2054     unsigned Offs;
2055
2056     /* Use a local variable for the working line */
2057     Line* L = *Start;
2058
2059     /* Search for (23B/XXT)
2060      *
2061      *          lda     _b+0
2062      *          ldx     _b+0+1
2063      *          sta     regsave
2064      *          stx     regsave+1
2065      *          jsr     incax1
2066      *          sta     _b+0
2067      *          stx     _b+0+1
2068      *          lda     regsave
2069      *          ldx     regsave+1
2070      *
2071      * and replace it by something like (24B/26T)
2072      *
2073      *          lda     _b+0
2074      *          sta     regsave
2075      *          clc
2076      *          adc     #$01
2077      *          sta     _b+0
2078      *          lda     _b+0+1
2079      *          sta     regsave+1
2080      *          adc     #$00
2081      *          sta     _b+0+1
2082      *          tax
2083      *          lda     regsave
2084      */
2085     if (!LineMatch (L, "\tlda\t")                               ||
2086         !GetNextCodeLines (L, L2, 4)                            ||
2087         !IsLoadAX (L, L2 [0])                                   ||
2088         !LineFullMatch (L2[1], "\tsta\tregsave")                ||
2089         !LineFullMatch (L2[2], "\tstx\tregsave+1")) {
2090
2091         /* Not found */
2092         return 0;
2093     }
2094
2095     /* */
2096     if (LineMatch (L2[3], "\tjsr\tincax")) {
2097         /* Get next code lines */
2098         if (GetNextCodeLines (L2[3], &L2[4], 4) == 0) {
2099             /* Cannot get lines */
2100             return 0;
2101         }
2102         Inc = GetHexNum (L2[3]->Line+10);
2103         L3 = &L2[4];
2104         LinesToRemove = 8;
2105     } else {
2106         /* Get next code lines */
2107         if (GetNextCodeLines (L2[3], &L2[4], 7) == 0) {
2108             /* Cannot get lines */
2109             return 0;
2110         }
2111         if (LineFullMatch (L2[3], "\tclc")                      &&
2112             LineMatch (L2[4], "\tadc\t#$")                      &&
2113             LineFullMatch (L2[5], "\tbcc\t*+3")                 &&
2114             LineFullMatch (L2[6], "\tinx")) {
2115             /* Inlined increment */
2116             Inc = GetHexNum (L2[4]->Line+7);
2117             L3 = &L2[7];
2118             LinesToRemove = 11;
2119         } else {
2120             /* Not found */
2121             return 0;
2122         }
2123     }
2124
2125     /* Check for the remainder */
2126     if (!LineMatch (L3[0], "\tsta\t")                           ||
2127         strcmp (L3[0]->Line+5, L->Line+5) != 0                  ||
2128         !LineMatch (L3[1], "\tstx\t")                           ||
2129         strcmp (L3[1]->Line+5, L2[0]->Line+5) != 0              ||
2130         !LineFullMatch (L3[2], "\tlda\tregsave")                ||
2131         !LineFullMatch (L3[3], "\tldx\tregsave+1")) {
2132
2133         /* Not found */
2134         return 0;
2135     }
2136
2137     /* Check if AX is actually used following the code above. If not,
2138      * we don't need to load A/X from regsave. Since X will never be
2139      * used without A, check just for A.
2140      */
2141     NeedLoad = RegAUsed (L3[3]);
2142
2143     /* Special code for register variables */
2144     Done = 0;
2145     if (LineMatch (L, "\tlda\tregbank+")        &&
2146         GetNextCodeLines (L3[3], &L3[4], 1)     &&
2147         Inc == 1) {
2148
2149         /* Remember the offset into the register bank */
2150         char Reg[20];
2151         strcpy (Reg, L->Line+5);
2152
2153         /* Check for several special sequences */
2154         if (LineFullMatch (L3[4], "\tjsr\tldaui")) {
2155             /* Load char indirect */
2156             L = ReplaceLine  (L, "\tldx\t#$00");
2157             L = NewLineAfter (L, "\tlda\t(%s,x)", Reg);
2158             L = NewLineAfter (L, "\tinc\t%s", Reg);
2159             L = NewLineAfter (L, "\tbne\t*+4");
2160             L = NewLineAfter (L, "\tinc\t%s+1", Reg);
2161             Done = 1;
2162             ++LinesToRemove;
2163         } else if (LineFullMatch (L3[4], "\tsta\tptr1")         &&
2164                    GetNextCodeLines (L3[4], &L3[5], 3)          &&
2165                    LineFullMatch (L3[5], "\tstx\tptr1+1")       &&
2166                    LineFullMatch (L3[6], "\tldx\t#$00")         &&
2167                    LineFullMatch (L3[7], "\tlda\t(ptr1,x)")) {
2168
2169             /* Load char indirect, inlined */
2170             L = ReplaceLine  (L, "\tldx\t#$00");
2171             L = NewLineAfter (L, "\tlda\t(%s,x)", Reg);
2172             L = NewLineAfter (L, "\tinc\t%s", Reg);
2173             L = NewLineAfter (L, "\tbne\t*+4");
2174             L = NewLineAfter (L, "\tinc\t%s+1", Reg);
2175             Done = 1;
2176             LinesToRemove += 4;
2177
2178         } else if (LineFullMatch (L3[4], "\tjsr\tpushax")) {
2179             if (GetNextCodeLines (L3[4], &L3[5], 2)             &&
2180                 LineMatch        (L3[5], "\tlda\t")             &&
2181                 LineFullMatch    (L3[6], "\tjsr\tstaspp")) {
2182
2183                 /* Store to pointer */
2184                 L = ReplaceLine  (L, L3[5]->Line);
2185                 L = NewLineAfter (L, "\tldy\t#$00");
2186                 L = NewLineAfter (L, "\tsta\t(%s),y", Reg);
2187                 L = NewLineAfter (L, "\tinc\t%s", Reg);
2188                 L = NewLineAfter (L, "\tbne\t*+4");
2189                 L = NewLineAfter (L, "\tinc\t%s+1", Reg);
2190
2191                 Done = 1;
2192                 LinesToRemove += 3;
2193
2194             } else if (GetNextCodeLines (L3[4], &L3[5], 3)      &&
2195                        LineMatch     (L3[5], "\tldy\t#$")       &&
2196                        LineFullMatch (L3[6], "\tlda\t(sp),y")   &&
2197                        LineFullMatch (L3[7], "\tjsr\tstaspp")) {
2198
2199                 /* Beware: We have to correct the stack offset, since we will
2200                  * remove the pushax instruction!
2201                  */
2202                 Offs = GetHexNum (L3[5]->Line+7) - 2;
2203
2204                 /* Store to pointer */
2205                 L = ReplaceLine  (L, "\tldy\t#$%02X", Offs);
2206                 L = NewLineAfter (L, "\tldx\t#$00");
2207                 L = NewLineAfter (L, "\tlda\t(sp),y");
2208                 L = NewLineAfter (L, "\tsta\t(%s,x)", Reg);
2209                 L = NewLineAfter (L, "\tinc\t%s", Reg);
2210                 L = NewLineAfter (L, "\tbne\t*+4");
2211                 L = NewLineAfter (L, "\tinc\t%s+1", Reg);
2212
2213                 Done = 1;
2214                 LinesToRemove += 4;
2215             }
2216         }
2217     }
2218
2219     if (Done == 0) {
2220
2221         /* No register variable - insert the first part of the code */
2222         if (NeedLoad) {
2223             L = NewLineAfter (L, "\tsta\tptr1");
2224         }
2225         L = NewLineAfter (L, "\tclc");
2226         L = NewLineAfter (L, "\tadc\t#$%02X", Inc);
2227         L = NewLineAfter (L, "\tsta\t%s", L3[0]->Line+5);
2228         L = NewLineAfter (L, "\tlda\t%s", L3[1]->Line+5);
2229         if (NeedLoad) {
2230             L = NewLineAfter (L, "\tsta\tptr1+1");
2231         }
2232         L = NewLineAfter (L, "\tadc\t#$00");
2233         L = NewLineAfter (L, "\tsta\t%s", L3[1]->Line+5);
2234
2235         /* Check if we must really load the old value into a/x or if the
2236          * code may be replaced by something else.
2237          */
2238         if (GetNextCodeLines (L3[3], &L3[4], 1)) {
2239             if (LineFullMatch (L3[4], "\tjsr\tldaui")) {
2240                 /* Load char indirect */
2241                 L = NewLineAfter (L, "\tldx\t#$00");
2242                 L = NewLineAfter (L, "\tlda\t(ptr1,x)");
2243                 NeedLoad = 0;
2244                 ++LinesToRemove;
2245             } else if (LineFullMatch (L3[4], "\tsta\tptr1")             &&
2246                        GetNextCodeLines (L3[4], &L3[5], 3)              &&
2247                        LineFullMatch (L3[5], "\tstx\tptr1+1")           &&
2248                        LineFullMatch (L3[6], "\tldx\t#$00")             &&
2249                        LineFullMatch (L3[7], "\tlda\t(ptr1,x)")) {
2250
2251                 /* Load char indirect, inlined */
2252                 L = NewLineAfter (L, "\tldx\t#$00");
2253                 L = NewLineAfter (L, "\tlda\t(ptr1,x)");
2254                 NeedLoad = 0;
2255                 LinesToRemove += 4;
2256
2257             } else if (LineFullMatch (L3[4], "\tjsr\tldaxi")) {
2258                 /* Load word indirect */
2259                 L = NewLineAfter (L, "\tldy\t#$01");
2260                 L = NewLineAfter (L, "\tlda\t(ptr1),y");
2261                 L = NewLineAfter (L, "\ttax");
2262                 L = NewLineAfter (L, "\tdey");
2263                 L = NewLineAfter (L, "\tlda\t(ptr1),y");
2264                 NeedLoad = 0;
2265                 ++LinesToRemove;
2266
2267             } else if (LineFullMatch (L3[4], "\tjsr\tpushax")) {
2268                 if (GetNextCodeLines (L3[4], &L3[5], 2)                 &&
2269                     LineMatch            (L3[5], "\tlda\t")             &&
2270                     LineFullMatch        (L3[6], "\tjsr\tstaspp")) {
2271
2272                     /* Store to pointer */
2273                     L = NewLineAfter (L, L3[5]->Line);
2274                     L = NewLineAfter (L, "\tldy\t#$00");
2275                     L = NewLineAfter (L, "\tsta\t(ptr1),y");
2276
2277                     NeedLoad = 0;
2278                     LinesToRemove += 3;
2279                 } else if (GetNextCodeLines (L3[4], &L3[5], 3)          &&
2280                            LineMatch     (L3[5], "\tldy\t#$")           &&
2281                            LineFullMatch (L3[6], "\tlda\t(sp),y")       &&
2282                            LineFullMatch (L3[7], "\tjsr\tstaspp")) {
2283
2284                     /* Beware: We have to correct the stack offset, since we will
2285                      * remove the pushax instruction!
2286                      */
2287                     sprintf (L3[5]->Line+7, "%02X", GetHexNum (L3[5]->Line+7)-2);
2288
2289                     /* Store to pointer */
2290                     L = NewLineAfter (L, L3[5]->Line);
2291                     L = NewLineAfter (L, L3[6]->Line);
2292                     L = NewLineAfter (L, "\tldy\t#$00");
2293                     L = NewLineAfter (L, "\tsta\t(ptr1),y");
2294
2295                     NeedLoad = 0;
2296                     LinesToRemove += 4;
2297                 }
2298
2299             }
2300         }
2301
2302         /* If we need to load a/x, add the code */
2303         if (NeedLoad) {
2304             L = NewLineAfter (L, "\tlda\tptr1");
2305             L = NewLineAfter (L, "\tldx\tptr1+1");
2306         }
2307     }
2308
2309     /* Remove the code that is no longer needed */
2310     FreeLines (L2[0], L2[LinesToRemove-1]);
2311
2312     /* Return the new line and success */
2313     *Start = NextCodeLine (L);
2314     return 1;
2315 }
2316
2317
2318
2319 static int OptPtrOps2 (Line** Start)
2320 /* Optimize several pointer and array constructs - subfunction 2 */
2321 {
2322     Line* L2[25];
2323     Line** L3;
2324     unsigned NeedLoad;
2325     unsigned LinesToRemove;
2326     unsigned Inc;
2327     unsigned Offs;
2328
2329
2330     /* Use a local variable for the working line */
2331     Line* L = *Start;
2332
2333     /* Same as subfunction 1 but for local variables. */
2334     if (LineMatch (L, "\tldy\t#$") == 0) {
2335         return 0;
2336     }
2337
2338     /* Get the stack offset. The offset points to the high byte, correct that. */
2339     Offs = GetHexNum (L->Line+7) - 1;
2340
2341     /* Check for the actual sequences */
2342     if (GetNextCodeLines (L, L2, 7)                             &&
2343         LineFullMatch (L2[0], "\tjsr\tldaxysp")                 &&
2344         LineFullMatch (L2[1], "\tsta\tregsave")                 &&
2345         LineFullMatch (L2[2], "\tstx\tregsave+1")               &&
2346         LineMatch     (L2[3], "\tjsr\tincax")) {
2347
2348         /* Non inlined version */
2349         Inc = GetHexNum (L2[3]->Line+10);
2350
2351         /* Check for stack offset zero */
2352         if (LineFullMatch (L2[4], "\tjsr\tstax0sp")             &&
2353             LineFullMatch (L2[5], "\tlda\tregsave")             &&
2354             LineFullMatch (L2[6], "\tldx\tregsave+1")) {
2355
2356             LinesToRemove = 7;
2357
2358         } else if (GetNextCodeLines (L2[6], &L2[7], 1)          &&
2359                    LineMatch     (L2[4], "\tldy\t#$")           &&
2360                    GetHexNum     (L2[4]->Line+7) == Offs        &&
2361                    LineFullMatch (L2[5], "\tjsr\tstaxysp")      &&
2362                    LineFullMatch (L2[6], "\tlda\tregsave")      &&
2363                    LineFullMatch (L2[7], "\tldx\tregsave+1")) {
2364
2365             LinesToRemove = 8;
2366
2367         } else {
2368             /* Not found */
2369             return 0;
2370         }
2371
2372     } else if (GetNextCodeLines (L, L2, 13)                     &&
2373                LineFullMatch (L2[0], "\tlda\t(sp),y")           &&
2374                LineFullMatch (L2[1], "\ttax")                   &&
2375                LineFullMatch (L2[2], "\tdey")                   &&
2376                LineFullMatch (L2[3], "\tlda\t(sp),y")           &&
2377                LineFullMatch (L2[4], "\tsta\tregsave")          &&
2378                LineFullMatch (L2[5], "\tstx\tregsave+1")        &&
2379                LineFullMatch (L2[6], "\tclc")                   &&
2380                LineMatch     (L2[7], "\tadc\t#$")               &&
2381                LineFullMatch (L2[8], "\tbcc\t*+3")              &&
2382                LineFullMatch (L2[9], "\tinx")) {
2383
2384         /* Inlined version */
2385         Inc = GetHexNum (L2[7]->Line+7);
2386
2387         /* Check for stack offset zero */
2388         if (LineFullMatch (L2[10], "\tjsr\tstax0sp")            &&
2389             LineFullMatch (L2[11], "\tlda\tregsave")            &&
2390             LineFullMatch (L2[12], "\tldx\tregsave+1")) {
2391
2392             LinesToRemove = 13;
2393
2394         } else if (GetNextCodeLines (L2[12], &L2[13], 1)        &&
2395                    LineMatch     (L2[10], "\tldy\t#$")          &&
2396                    GetHexNum     (L2[10]->Line+7) == Offs       &&
2397                    LineFullMatch (L2[11], "\tjsr\tstaxysp")     &&
2398                    LineFullMatch (L2[12], "\tlda\tregsave")     &&
2399                    LineFullMatch (L2[13], "\tldx\tregsave+1")) {
2400
2401             LinesToRemove = 14;
2402
2403         } else {
2404             /* Not found */
2405             return 0;
2406         }
2407     } else {
2408         /* Not found */
2409         return 0;
2410     }
2411
2412     /* Get a pointer to the last line of the preceding sequence */
2413     L3 = &L2[LinesToRemove-1];
2414
2415     /* Check if AX is actually used following the code above. If not,
2416      * we don't need to load A/X from regsave. Since X will never by
2417      * used without A, check just for A.
2418      */
2419     NeedLoad = RegAUsed (L3[0]);
2420
2421     /* Replace the ldy instruction, offset must point to the low byte */
2422     sprintf (L->Line+7, "%02X", Offs);
2423
2424     /* Insert the first part of the code */
2425     L = NewLineAfter (L, "\tlda\t(sp),y");
2426     if (NeedLoad) {
2427         L = NewLineAfter (L, "\tsta\tptr1");
2428     }
2429     L = NewLineAfter (L, "\tclc");
2430     L = NewLineAfter (L, "\tadc\t#$%02X", Inc);
2431     L = NewLineAfter (L, "\tsta\t(sp),y");
2432     L = NewLineAfter (L, "\tiny");
2433     L = NewLineAfter (L, "\tlda\t(sp),y");
2434     if (NeedLoad) {
2435         L = NewLineAfter (L, "\tsta\tptr1+1");
2436     }
2437     L = NewLineAfter (L, "\tadc\t#$00");
2438     L = NewLineAfter (L, "\tsta\t(sp),y");
2439
2440     /* Check if we must really load the old value into a/x or if the
2441      * code may be replaced by something else.
2442      */
2443     if (GetNextCodeLines (L3[0], &L3[1], 1)) {
2444         if (LineFullMatch (L3[1], "\tjsr\tldaui")) {
2445             /* Load char indirect */
2446             L = NewLineAfter (L, "\tldx\t#$00");
2447             L = NewLineAfter (L, "\tlda\t(ptr1,x)");
2448             NeedLoad = 0;
2449             ++LinesToRemove;
2450         } else if (LineFullMatch (L3[1], "\tsta\tptr1")         &&
2451                    GetNextCodeLines (L3[1], &L3[2], 3)          &&
2452                    LineFullMatch (L3[2], "\tstx\tptr1+1")       &&
2453                    LineFullMatch (L3[3], "\tldx\t#$00")         &&
2454                    LineFullMatch (L3[4], "\tlda\t(ptr1,x)")) {
2455
2456             /* Load char indirect, inlined */
2457             L = NewLineAfter (L, "\tldx\t#$00");
2458             L = NewLineAfter (L, "\tlda\t(ptr1,x)");
2459             NeedLoad = 0;
2460             LinesToRemove += 4;
2461
2462         } else if (LineFullMatch (L3[1], "\tjsr\tldaxi")) {
2463             /* Load word indirect */
2464             L = NewLineAfter (L, "\tldy\t#$01");
2465             L = NewLineAfter (L, "\tlda\t(ptr1),y");
2466             L = NewLineAfter (L, "\ttax");
2467             L = NewLineAfter (L, "\tdey");
2468             L = NewLineAfter (L, "\tlda\t(ptr1),y");
2469             NeedLoad = 0;
2470             ++LinesToRemove;
2471
2472         } else if (LineFullMatch (L3[1], "\tjsr\tpushax")) {
2473             if (GetNextCodeLines (L3[1], &L3[2], 2)             &&
2474                 LineMatch        (L3[2], "\tlda\t")             &&
2475                 LineFullMatch    (L3[3], "\tjsr\tstaspp")) {
2476
2477                 /* Store to pointer */
2478                 L = NewLineAfter (L, L3[2]->Line);
2479                 L = NewLineAfter (L, "\tldy\t#$00");
2480                 L = NewLineAfter (L, "\tsta\t(ptr1),y");
2481
2482                 NeedLoad = 0;
2483                 LinesToRemove += 3;
2484             } else if (GetNextCodeLines (L3[1], &L3[2], 3)      &&
2485                        LineMatch     (L3[2], "\tldy\t#$")       &&
2486                        LineFullMatch (L3[3], "\tlda\t(sp),y")   &&
2487                        LineFullMatch (L3[4], "\tjsr\tstaspp")) {
2488
2489                 /* Beware: We have to correct the stack offset, since we will
2490                  * remove the pushax instruction!
2491                  */
2492                 sprintf (L3[2]->Line+7, "%02X", GetHexNum (L3[2]->Line+7)-2);
2493
2494                 /* Store to pointer */
2495                 L = NewLineAfter (L, L3[2]->Line);
2496                 L = NewLineAfter (L, L3[3]->Line);
2497                 L = NewLineAfter (L, "\tldy\t#$00");
2498                 L = NewLineAfter (L, "\tsta\t(ptr1),y");
2499
2500                 NeedLoad = 0;
2501                 LinesToRemove += 4;
2502             }
2503         }
2504
2505     }
2506
2507     /* If we need to load a/x, add the code */
2508     if (NeedLoad) {
2509         L = NewLineAfter (L, "\tlda\tptr1");
2510         L = NewLineAfter (L, "\tldx\tptr1+1");
2511     }
2512
2513     /* Remove the code that is no longer needed */
2514     FreeLines (L2[0], L2[LinesToRemove-1]);
2515
2516     /* Return the new line and success */
2517     *Start = NextCodeLine (L);
2518     return 1;
2519 }
2520
2521
2522
2523 static void OptPtrOps (void)
2524 /* Optimize several pointer and array constructs */
2525 {
2526     Line* L2 [10];
2527
2528     Line* L = FirstCode;
2529     while (L) {
2530
2531         if (OptPtrOps1 (&L)) {
2532             continue;
2533         } else if (OptPtrOps2 (&L)) {
2534             continue;
2535         }
2536
2537         /* Search for the following sequence:
2538          *
2539          *      lda     regsave
2540          *      ldx     regsave+1
2541          *      jsr     pushax
2542          *      lda     #$..
2543          *      jsr     staspp
2544          *
2545          * and replace it by:
2546          *
2547          *      lda     #$..
2548          *      ldy     #$00
2549          *      sta     (regsave),y
2550          *
2551          */
2552         else if (LineFullMatch (L, "\tlda\tregsave")            && /* Match on start */
2553                  GetNextCodeLines (L, L2, 4)                    && /* Fetch next lines */
2554                  LineFullMatch (L2 [0], "\tldx\tregsave+1")     && /* Match line 2 ... */
2555                  LineFullMatch (L2 [1], "\tjsr\tpushax")        &&
2556                  LineMatch (L2 [2], "\tlda\t#$")                &&
2557                  LineFullMatch (L2 [3], "\tjsr\tstaspp")) {
2558
2559             /* Found the sequence, replace it */
2560             L      = ReplaceLine (L, L2 [2]->Line);             /* lda #$.. */
2561             L2 [0] = ReplaceLine (L2 [0], "\tldy\t#$00");
2562             L2 [1] = ReplaceLine (L2 [1], "\tsta\t(regsave),y");
2563
2564             /* Free the remaining lines */
2565             FreeLines (L2 [2], L2 [3]);
2566         }
2567
2568         /* Search for the following sequence:
2569          *
2570          *      lda     regsave
2571          *      ldx     regsave+1
2572          *      jsr     ldaui
2573          *
2574          * and replace it by:
2575          *
2576          *      ldx     #$00
2577          *      lda     (regsave,x)
2578          *
2579          */
2580         else if (LineFullMatch (L, "\tlda\tregsave")        && /* Match on start */
2581                  GetNextCodeLines (L, L2, 2)                && /* Fetch next lines */
2582                  LineFullMatch (L2 [0], "\tldx\tregsave+1") && /* Match line 2 ... */
2583                  LineFullMatch (L2 [1], "\tjsr\tldaui")) {
2584
2585             /* Found the sequence, replace it */
2586             L      = ReplaceLine (L, "\tldx\t#$00");
2587             L2 [0] = ReplaceLine (L2 [0], "\tlda\t(regsave,x)");
2588
2589             /* Free the remaining lines */
2590             FreeLine (L2 [1]);
2591         }
2592
2593         /*
2594          * Search for the following sequence:
2595          *
2596          *      lda     regsave
2597          *      ldx     regsave+1
2598          *      jsr     pushax
2599          *      ldx     #$high
2600          *      lda     #$low
2601          *      jsr     staxspp
2602          *
2603          * and replace it by:
2604          *
2605          *      ldy     #$01
2606          *      lda     #$high
2607          *      sta     (regsave),y
2608          *      tax
2609          *      dey
2610          *      lda     #$low
2611          *      sta     (regsave),y
2612          *
2613          */
2614         else if (LineFullMatch (L, "\tlda\tregsave")        &&
2615                  GetNextCodeLines (L, L2, 5)                &&
2616                  LineFullMatch (L2 [0], "\tldx\tregsave+1") &&
2617                  LineFullMatch (L2 [1], "\tjsr\tpushax")    &&
2618                  LineMatch (L2 [2], "\tldx\t#$")            &&
2619                  LineMatch (L2 [3], "\tlda\t#$")            &&
2620                  LineFullMatch (L2 [4], "\tjsr\tstaxspp")) {
2621
2622             /* Found the sequence, replace it */
2623             L      = ReplaceLine (L, "\tldy\t#$01");
2624             L2 [0] = ReplaceLine (L2 [0], L2 [2]->Line);
2625             L2 [0]->Line [3] = 'a';
2626             L2 [1] = ReplaceLine (L2 [1], "\tsta\t(regsave),y");
2627             L2 [4] = ReplaceLine (L2 [4], L2 [3]->Line);
2628             L2 [2] = ReplaceLine (L2 [2], "\ttax");
2629             L2 [3] = ReplaceLine (L2 [3], "\tdey");
2630             L      = NewLineAfter (L2 [4], "\tsta\t(regsave),y");
2631         }
2632
2633         /*
2634          * Search for the following sequence:
2635          *
2636          *      lda     regsave
2637          *      ldx     regsave+1
2638          *      sta     ptr1
2639          *      stx     ptr1+1
2640          *      ldx     #$00
2641          *      lda     (ptr1,x)
2642          *
2643          * and replace it by:
2644          *
2645          *      ldx     #$00
2646          *      lda     (regsave,x)
2647          *
2648          */
2649         else if (LineFullMatch (L, "\tlda\tregsave")        &&
2650                  GetNextCodeLines (L, L2, 5)                &&
2651                  LineFullMatch (L2 [0], "\tldx\tregsave+1") &&
2652                  LineFullMatch (L2 [1], "\tsta\tptr1")      &&
2653                  LineFullMatch (L2 [2], "\tstx\tptr1+1")    &&
2654                  LineFullMatch (L2 [3], "\tldx\t#$00")      &&
2655                  LineFullMatch (L2 [4], "\tlda\t(ptr1,x)")) {
2656
2657             /* Found the sequence, replace it */
2658             L      = ReplaceLine (L, "\tldx\t#$00");
2659             L2 [0] = ReplaceLine (L2 [0], "\tlda\t(regsave,x)");
2660
2661             /* Remove the remaining lines */
2662             FreeLines (L2 [1], L2 [4]);
2663         }
2664
2665         /* Search for the following sequence:
2666          *
2667          *      jsr     pushax
2668          *      lda     ...
2669          *      jsr     staspp
2670          *
2671          * and replace it by:
2672          *
2673          *      sta     ptr1
2674          *      stx     ptr1+1
2675          *      lda     ...
2676          *      ldy     #$00
2677          *      sta     (ptr1),y
2678          *
2679          */
2680         else if (LineFullMatch (L, "\tjsr\tpushax")         &&
2681                  GetNextCodeLines (L, L2, 2)                &&
2682                  LineMatch (L2 [0], "\tlda\t")              &&
2683                  LineFullMatch (L2 [1], "\tjsr\tstaspp")) {
2684
2685             /* Found the sequence, replace it */
2686             L      = ReplaceLine (L, "\tsta\tptr1");
2687             L2 [1] = ReplaceLine (L2 [1], L2 [0]->Line);   /* lda ... */
2688             L2 [0] = ReplaceLine (L2 [0], "\tstx\tptr1+1");
2689             L2 [2] = NewLineAfter (L2 [1], "\tldy\t#$00");
2690             L      = NewLineAfter (L2 [2], "\tsta\t(ptr1),y");
2691         }
2692
2693         /* Search for the following sequence:
2694          *
2695          *      jsr     pushax
2696          *      lda     ...
2697          *      ldy     #$nn
2698          *      jsr     staspidx
2699          *
2700          * and replace it by:
2701          *
2702          *      sta     ptr1
2703          *      stx     ptr1+1
2704          *      lda     ...
2705          *      ldy     #$nn
2706          *      sta     (ptr1),y
2707          *
2708          */
2709         else if (LineFullMatch (L, "\tjsr\tpushax")         &&
2710                  GetNextCodeLines (L, L2, 3)                &&
2711                  LineMatch (L2 [0], "\tlda\t")              &&
2712                  LineMatch (L2 [1], "\tldy\t#$")            &&
2713                  LineFullMatch (L2 [2], "\tjsr\tstaspidx")) {
2714
2715             /* Found the sequence, replace it */
2716             L      = ReplaceLine (L, "\tsta\tptr1");
2717             L      = NewLineAfter (L, "\tstx\tptr1+1");
2718             L2 [2] = ReplaceLine (L2 [2], "\tsta\t(ptr1),y");
2719         }
2720
2721         /* Search for the following sequence:
2722          *
2723          *      jsr     pushax
2724          *      ldy     #$..
2725          *      lda     (sp),y
2726          *      jsr     staspp
2727          *
2728          * and replace it by:
2729          *
2730          *      sta     ptr1
2731          *      stx     ptr1+1
2732          *      ldy     #$..
2733          *      lda     (sp),y
2734          *      ldy     #$00
2735          *      sta     (ptr1),y
2736          *
2737          * Beware: Since we remove a call to a function that changes the stack
2738          * pointer, we have to adjust the stack address for the lda.
2739          *
2740          */
2741         else if (LineFullMatch (L, "\tjsr\tpushax")         &&
2742                  GetNextCodeLines (L, L2, 3)                &&
2743                  LineMatch (L2 [0], "\tldy\t#$")            &&
2744                  LineFullMatch (L2 [1], "\tlda\t(sp),y")    &&
2745                  LineFullMatch (L2 [2], "\tjsr\tstaspp")) {
2746
2747             /* Found the sequence, replace it. First create a new load
2748              * instruction for the changed stack offset.
2749              */
2750             char Buf [30];
2751             sprintf (Buf, "\tldy\t#$%02X", GetHexNum (L2 [0]->Line+7) - 2);
2752             L      = ReplaceLine (L, "\tsta\tptr1");
2753             L2 [1] = ReplaceLine (L2 [1], Buf);   /* ldy ... */
2754             L2 [0] = ReplaceLine (L2 [0], "\tstx\tptr1+1");
2755             L2 [2] = ReplaceLine (L2 [2], "\tlda\t(sp),y");
2756             L2 [3] = NewLineAfter (L2 [2], "\tldy\t#$00");
2757             L      = NewLineAfter (L2 [3], "\tsta\t(ptr1),y");
2758         }
2759
2760         /* Search for the following sequence:
2761          *
2762          *      jsr     pushax
2763          *      ldy     #$nn
2764          *      lda     (sp),y
2765          *      ldy     #$mm
2766          *      jsr     staspidx
2767          *
2768          * and replace it by:
2769          *
2770          *      sta     ptr1
2771          *      stx     ptr1+1
2772          *      ldy     #$nn
2773          *      lda     (sp),y
2774          *      ldy     #$mm
2775          *      sta     (ptr1),y
2776          *
2777          * Beware: Since we remove a call to a function that changes the stack
2778          * pointer, we have to adjust the stack address for the lda.
2779          *
2780          */
2781         else if (LineFullMatch (L, "\tjsr\tpushax")         &&
2782                  GetNextCodeLines (L, L2, 4)                &&
2783                  LineMatch (L2 [0], "\tldy\t#$")            &&
2784                  LineFullMatch (L2 [1], "\tlda\t(sp),y")    &&
2785                  LineMatch (L2 [2], "\tldy\t#$")            &&
2786                  LineFullMatch (L2 [3], "\tjsr\tstaspidx")) {
2787
2788             /* Found the sequence, replace it. First create a new load
2789              * instruction for the changed stack offset.
2790              */
2791             char Buf [30];
2792             sprintf (Buf, "\tldy\t#$%02X", GetHexNum (L2 [0]->Line+7) - 2);
2793             L      = ReplaceLine (L, "\tsta\tptr1");
2794             L      = NewLineAfter (L, "\tstx\tptr1+1");
2795             L2 [0] = ReplaceLine (L2 [0], Buf);   /* ldy ... */
2796             L2 [3] = ReplaceLine (L2 [3], "\tsta\t(ptr1),y");
2797         }
2798
2799         /* Search for the following sequence:
2800          *
2801          *      ldax    _label+0
2802          *      ldy     #$..
2803          *      clc
2804          *      adc     (sp),y
2805          *      bcc     *+3
2806          *      inx
2807          *      sta     ptr1
2808          *      stx     ptr1+1
2809          *      ldx     #$00
2810          *      lda     (ptr1,x)
2811          *
2812          * and replace it by:
2813          *
2814          *      ldy     #$..
2815          *      lda     (sp),y
2816          *      tay
2817          *      ldx     #$00
2818          *      lda     _label+0,y
2819          *
2820          * The load of X may be omitted if X is not used below.
2821          */
2822         else if (LineMatch (L, "\tldax\t_")                 &&
2823                  GetNextCodeLines (L, L2, 9)                &&
2824                  LineMatch (L2 [0], "\tldy\t#$")            &&
2825                  LineFullMatch (L2 [1], "\tclc")            &&
2826                  LineFullMatch (L2 [2], "\tadc\t(sp),y")    &&
2827                  LineFullMatch (L2 [3], "\tbcc\t*+3")       &&
2828                  LineFullMatch (L2 [4], "\tinx")            &&
2829                  LineFullMatch (L2 [5], "\tsta\tptr1")      &&
2830                  LineFullMatch (L2 [6], "\tstx\tptr1+1")    &&
2831                  LineFullMatch (L2 [7], "\tldx\t#$00")      &&
2832                  LineFullMatch (L2 [8], "\tlda\t(ptr1,x)")) {
2833
2834             /* Found the sequence, replace it */
2835             char Label [256];
2836             strcpy (Label, L->Line + 6);                /* Remember the label */
2837             L = ReplaceLine  (L, L2 [0]->Line);         /* ldy .. */
2838             L = NewLineAfter (L, "\tlda\t(sp),y");
2839             L = NewLineAfter (L, "\ttay");
2840             if (RegXUsed (L2[8])) {
2841                 L = NewLineAfter (L, "\tldx\t#$00");
2842             }
2843             L = NewLineAfter (L, "\tlda\t%s,y", Label);
2844
2845             /* Remove the remaining stuff. There may be hints between the
2846              * instructions, remove them too
2847              */
2848             FreeLines (L2[0], L2 [8]);
2849
2850         }
2851
2852         /* Check for
2853          *
2854          *      ldy     #$xx
2855          *      lda     (sp),y
2856          *      tax
2857          *      dey
2858          *      lda     (sp),y
2859          *      ldy     #$yy
2860          *      jsr     ldauidx
2861          *
2862          * and replace it by
2863          *
2864          *      ldy     #$xx
2865          *      ldx     #$yy
2866          *      jsr     ldauiysp
2867          *
2868          * or even
2869          *
2870          *      jsr     ldaui0sp
2871          *
2872          * This change will cost 2 cycles, but it saves a lot of code (6 bytes
2873          * per occurrence), so we will accept the overhead. It may even be
2874          * possible to rewrite the library routine to get rid of the additional
2875          * overhead.
2876          */
2877         else if (LineMatch (L, "\tldy\t#$")                     &&
2878                  GetNextCodeLines (L, L2, 6)                    &&
2879                  LineFullMatch (L2 [0], "\tlda\t(sp),y")        &&
2880                  LineFullMatch (L2 [1], "\ttax")                &&
2881                  LineFullMatch (L2 [2], "\tdey")                &&
2882                  LineFullMatch (L2 [3], "\tlda\t(sp),y")        &&
2883                  LineMatch     (L2 [4], "\tldy\t#$")            &&
2884                  LineFullMatch (L2 [5], "\tjsr\tldauidx")) {
2885
2886             /* Found - replace it */
2887             L2 [4]->Line [3] = 'x';             /* Change to ldx */
2888             if (LineFullMatch (L, "\tldy\t#$01")) {
2889                 /* Word at offset zero */
2890                 FreeLine (L);
2891                 L = ReplaceLine (L2 [5], "\tjsr\tldaui0sp");
2892             } else {
2893                 ReplaceLine (L2 [5], "\tjsr\tldauiysp");
2894             }
2895
2896             /* Delete the remaining lines */
2897             FreeLines (L2 [0], L2 [3]);
2898         }
2899
2900         /* Check for
2901          *
2902          *      ldy     #$xx
2903          *      lda     (sp),y
2904          *      tax
2905          *      dey
2906          *      lda     (sp),y
2907          *      sta     ptr1
2908          *      stx     ptr1+1
2909          *      ldx     #$00
2910          *      lda     (ptr1,x)
2911          *
2912          * and replace it by
2913          *
2914          *      ldy     #$xx
2915          *      jsr     ldau0ysp
2916          *
2917          * or even
2918          *
2919          *      jsr     ldau00sp
2920          *
2921          * This change will has an overhead of 10 cycles, but it saves 11(!)
2922          * bytes per invocation. Maybe we should apply only if FavourSize is
2923          * true?
2924          */
2925         else if (LineMatch (L, "\tldy\t#$")                     &&
2926                  GetNextCodeLines (L, L2, 8)                    &&
2927                  LineFullMatch (L2 [0], "\tlda\t(sp),y")        &&
2928                  LineFullMatch (L2 [1], "\ttax")                &&
2929                  LineFullMatch (L2 [2], "\tdey")                &&
2930                  LineFullMatch (L2 [3], "\tlda\t(sp),y")        &&
2931                  LineFullMatch (L2 [4], "\tsta\tptr1")          &&
2932                  LineFullMatch (L2 [5], "\tstx\tptr1+1")        &&
2933                  LineFullMatch (L2 [6], "\tldx\t#$00")          &&
2934                  LineFullMatch (L2 [7], "\tlda\t(ptr1,x)")) {
2935
2936             /* Found - replace it */
2937             if (LineFullMatch (L, "\tldy\t#$01")) {
2938                 /* Word at offset zero */
2939                 FreeLine (L);
2940                 L = ReplaceLine (L2 [0], "\tjsr\tldau00sp");
2941             } else {
2942                 ReplaceLine (L2 [0], "\tjsr\tldau0ysp");
2943             }
2944
2945             /* Delete the remaining lines */
2946             FreeLines (L2 [1], L2 [7]);
2947         }
2948
2949         /* Check for
2950          *
2951          *      ldy     #$xx
2952          *      lda     (sp),y
2953          *      tax
2954          *      dey
2955          *      lda     (sp),y
2956          *      sta     yyy
2957          *      stx     yyy+1
2958          *
2959          * and replace it by
2960          *
2961          *      ldy     #$xx
2962          *      lda     (sp),y
2963          *      sta     yyy+1
2964          *      dey
2965          *      lda     (sp),y
2966          *      sta     yyy
2967          *
2968          * Provided that X is not used later.
2969          */
2970         else if (LineMatch (L, "\tldy\t#$")                     &&
2971                  GetNextCodeLines (L, L2, 6)                    &&
2972                  LineFullMatch (L2 [0], "\tlda\t(sp),y")        &&
2973                  LineFullMatch (L2 [1], "\ttax")                &&
2974                  LineFullMatch (L2 [2], "\tdey")                &&
2975                  LineFullMatch (L2 [3], "\tlda\t(sp),y")        &&
2976                  Is16BitStore (L2[4], L2[5])                    &&
2977                  !RegXUsed (L2[5])) {
2978
2979             /* Found - replace it */
2980             L2[1] = ReplaceLine (L2[1], L2[5]->Line);
2981             L2[1]->Line[3] = 'a';
2982
2983             /* Delete the remaining lines */
2984             FreeLine (L2[5]);
2985
2986             /* Start over at last line */
2987             L = L2[4];
2988         }
2989
2990         /* Next Line */
2991         L = NextCodeLine (L);
2992     }
2993 }
2994
2995
2996
2997 static void OptRegVars (void)
2998 /* Optimize register variable uses */
2999 {
3000     Line* L2 [10];
3001
3002     Line* L = FirstCode;
3003     while (L) {
3004
3005         /* Search for the following sequence:
3006          *
3007          *      lda     regbank+n
3008          *      ldx     regbank+n+1
3009          *      jsr     ldaui
3010          *
3011          * and replace it by:
3012          *
3013          *      ldx     #$00
3014          *      lda     (regbank+n,x)
3015          *
3016          */
3017         if (LineMatch (L, "\tlda\tregbank+")            && /* Match on start */
3018             GetNextCodeLines (L, L2, 2)                 && /* Fetch next lines */
3019             LineMatch (L2 [0], "\tldx\tregbank+")       && /* Match line 2 ... */
3020             LineFullMatch (L2 [1], "\tjsr\tldaui")      &&
3021             L->Line [13] == L2 [0]->Line [13]           && /* Offset equal */
3022             strcmp (L2 [0]->Line + 14, "+1") == 0) {
3023
3024             char Buf [100];
3025             sprintf (Buf, "\tlda\t(%s,x)", L->Line + 5);
3026
3027             /* Found the sequence, replace it */
3028             L      = ReplaceLine (L, "\tldx\t#$00");
3029             L2 [0] = ReplaceLine (L2 [0], Buf);
3030
3031             /* Free the remaining lines */
3032             FreeLine (L2 [1]);
3033         }
3034
3035         /* Search for the following sequence:
3036          *
3037          *      lda     regbank+n
3038          *      ldx     regbank+n+1
3039          *      sta     ptr1
3040          *      stx     ptr1+1
3041          *      ldx     #$00
3042          *      lda     (ptr1,x)
3043          *
3044          * and replace it by:
3045          *
3046          *      ldx     #$00
3047          *      lda     (regbank+n,x)
3048          *
3049          */
3050         else if (LineMatch (L, "\tlda\tregbank+")        && /* Match on start */
3051                  GetNextCodeLines (L, L2, 5)             && /* Fetch next lines */
3052                  LineMatch (L2 [0], "\tldx\tregbank+")   && /* Match line 2 ... */
3053                  L->Line [13] == L2 [0]->Line [13]       && /* Offset equal */
3054                  strcmp (L2 [0]->Line + 14, "+1") == 0   &&
3055                  LineFullMatch (L2 [1], "\tsta\tptr1")   &&
3056                  LineFullMatch (L2 [2], "\tstx\tptr1+1") &&
3057                  LineFullMatch (L2 [3], "\tldx\t#$00")   &&
3058                  LineFullMatch (L2 [4], "\tlda\t(ptr1,x)")) {
3059
3060             char Buf [100];
3061             sprintf (Buf, "\tlda\t(%s,x)", L->Line + 5);
3062
3063             /* Found the sequence, replace it */
3064             L      = ReplaceLine (L, "\tldx\t#$00");
3065             L2 [0] = ReplaceLine (L2 [0], Buf);
3066
3067             /* Free the remaining lines */
3068             FreeLines (L2 [1], L2 [4]);
3069         }
3070
3071         /* Search for the following sequence:
3072          *
3073          *      lda     regbank+n
3074          *      ldx     regbank+n+1
3075          *      ldy     #$..
3076          *      jsr     ldauidx
3077          *
3078          * and replace it by:
3079          *
3080          *      ldy     #$..
3081          *      ldx     #$00
3082          *      lda     (regbank+n),y
3083          *
3084          */
3085         else if (LineMatch (L, "\tlda\tregbank+")        && /* Match on start */
3086                  GetNextCodeLines (L, L2, 3)             && /* Fetch next lines */
3087                  LineMatch (L2 [0], "\tldx\tregbank+")   && /* Match line 2 ... */
3088                  L->Line [13] == L2 [0]->Line [13]       && /* Offset equal */
3089                  strcmp (L2 [0]->Line + 14, "+1") == 0   &&
3090                  LineMatch (L2 [1], "\tldy\t#$")         &&
3091                  LineFullMatch (L2 [2], "\tjsr\tldauidx")) {
3092
3093             char Buf [100];
3094             sprintf (Buf, "\tlda\t(%s),y", L->Line + 5);
3095
3096             /* Found the sequence, replace it */
3097             L      = ReplaceLine (L, L2 [1]->Line);
3098             L2 [0] = ReplaceLine (L2 [0], "\tldx\t#$00");
3099             L2 [1] = ReplaceLine (L2 [1], Buf);
3100
3101             /* Free the remaining lines */
3102             FreeLine (L2 [2]);
3103         }
3104
3105         /* Search for the following sequence:
3106          *
3107          *      lda     regbank+n
3108          *      ldx     regbank+n+1
3109          *      sta     ptr1
3110          *      stx     ptr1+1
3111          *      lda     ...
3112          *      ldy     #$mm
3113          *      sta     (ptr1),y
3114          *
3115          * and replace it by:
3116          *
3117          *      lda     ...
3118          *      ldy     #$mm
3119          *      sta     (regbank+n),y
3120          *
3121          * The source form is not generated by the parser but by the optimizer.
3122          */
3123         else if (LineMatch (L, "\tlda\tregbank+")        && /* Match on start */
3124                  GetNextCodeLines (L, L2, 6)             && /* Fetch next lines */
3125                  LineMatch (L2 [0], "\tldx\tregbank+")   && /* Match line 2 ... */
3126                  L->Line [13] == L2 [0]->Line [13]       && /* Offset equal */
3127                  strcmp (L2 [0]->Line + 14, "+1") == 0   &&
3128                  LineFullMatch (L2 [1], "\tsta\tptr1")   &&
3129                  LineFullMatch (L2 [2], "\tstx\tptr1+1") &&
3130                  LineMatch (L2 [3], "\tlda\t")           &&
3131                  LineMatch (L2 [4], "\tldy\t#$")         &&
3132                  LineMatch (L2 [5], "\tsta\t(ptr1),y")) {
3133
3134             char Buf [100];
3135             sprintf (Buf, "\tsta\t(%s),y", L->Line + 5);
3136
3137             /* Found the sequence, replace it */
3138             L2 [5] = ReplaceLine (L2 [5], Buf);
3139
3140             /* Free the remaining lines */
3141             FreeLines (L, L2 [2]);
3142
3143             /* Make the line pointer valid again */
3144             L = L2 [5];
3145         }
3146
3147         /* Search for the following sequence:
3148          *
3149          *      lda     regbank+n
3150          *      ldx     regbank+n+1
3151          *      sta     ptr1
3152          *      stx     ptr1+1
3153          *      ldy     #$mm
3154          *      lda     (sp),y
3155          *      ldy     #$ll
3156          *      sta     (ptr1),y
3157          *
3158          * and replace it by:
3159          *
3160          *      ldy     #$mm
3161          *      lda     (sp),y
3162          *      ldy     #$ll
3163          *      sta     (regbank+n),y
3164          *
3165          * The source form is not generated by the parser but by the optimizer.
3166          */
3167         else if (LineMatch (L, "\tlda\tregbank+")        && /* Match on start */
3168                  GetNextCodeLines (L, L2, 7)             && /* Fetch next lines */
3169                  LineMatch (L2 [0], "\tldx\tregbank+")   && /* Match line 2 ... */
3170                  L->Line [13] == L2 [0]->Line [13]       && /* Offset equal */
3171                  strcmp (L2 [0]->Line + 14, "+1") == 0   &&
3172                  LineFullMatch (L2 [1], "\tsta\tptr1")   &&
3173                  LineFullMatch (L2 [2], "\tstx\tptr1+1") &&
3174                  LineMatch (L2 [3], "\tldy\t#$")         &&
3175                  LineFullMatch (L2 [4], "\tlda\t(sp),y") &&
3176                  LineMatch (L2 [5], "\tldy\t#$")         &&
3177                  LineMatch (L2 [6], "\tsta\t(ptr1),y")) {
3178
3179             char Buf [100];
3180             sprintf (Buf, "\tsta\t(%s),y", L->Line + 5);
3181
3182             /* Found the sequence, replace it */
3183             L2 [6] = ReplaceLine (L2 [6], Buf);
3184
3185             /* Free the remaining lines */
3186             FreeLines (L, L2 [2]);
3187
3188             /* Make the line pointer valid again */
3189             L = L2 [6];
3190         }
3191
3192         /* Next Line */
3193         L = NextCodeLine (L);
3194     }
3195 }
3196
3197
3198
3199 static void OptDoubleJumps (void)
3200 /* Remove/rearrange jumps that jump to other jumps */
3201 {
3202     static const char* Jumps [] = {
3203         "\tjeq\tL",
3204         "\tjne\tL",
3205         "\tbeq\tL",
3206         "\tbne\tL",
3207         "\tjmp\tL",
3208         0
3209     };
3210
3211     unsigned D;
3212
3213     Line* L = FirstCode;
3214     while (L) {
3215
3216         int I;
3217
3218         /* Is this a jump? */
3219         while ((I = LineMatchX (L, Jumps)) >= 0) {
3220
3221             /* Yes. Get the target label */
3222             Line* Target = GetTargetLine (L->Line + 5);
3223
3224             /* Target points to the label itself. Skip lines until we reach
3225              * one that is not a label.
3226              */
3227             Target = NextInstruction (Target);
3228
3229             /* Be sure, this line is not the same as the one the jump is
3230              * in (this happens if there is an empty loop).
3231              */
3232             if (Target == L) {
3233                 break;
3234             }
3235             D = 0;
3236             if (LineMatch (Target, "\tjmp\t")) {
3237
3238                 /* The target is itself a jump. If this is a short branch, get
3239                  * the final target and check if it is in reach. Bail out if
3240                  * not.
3241                  */
3242                 if (L->Line[1] == 'b') {
3243                     Line* FinalTarget = GetTargetLine (Target->Line+5);
3244                     FinalTarget = NextInstruction (FinalTarget);
3245                     if ((D = GetJumpDistance (L, FinalTarget)) >= 123) {
3246                         break;
3247                     }
3248                 }
3249
3250                 /* Make sure the jump does indeed point to another label.
3251                  * It may happen that this is not the case for some endless
3252                  * loop (while(1) and similar).
3253                  */
3254                 if (strcmp (L->Line+5, Target->Line+5) == 0) {
3255                     /* Same label, bail out */
3256                     break;
3257                 }
3258
3259                 /* Use the label in the original jump instead */
3260                 L = ReplaceLine (L, "%.5s%s", L->Line, Target->Line+5);
3261
3262             } else if (I < 2 && LineMatch (Target, Jumps [I])) {
3263
3264                 /* Conditional jump. Use final label */
3265                 strcpy (L->Line+5, Target->Line+5);
3266
3267             } else {
3268                 break;
3269             }
3270         }
3271
3272         /* Next line */
3273         L = NextCodeLine (L);
3274     }
3275 }
3276
3277
3278
3279 static void OptJumpRTS (void)
3280 /* Replace jumps to an RTS by an RTS */
3281 {
3282     Line* L = FirstCode;
3283     while (L) {
3284         /* Is this a jump to a numbered label? */
3285         if (LineMatch (L, "\tjmp\t") && L->Line [5] == 'L' && IsDigit (L->Line [6])) {
3286
3287             /* Yes. Get the target label */
3288             Line* Target = GetTargetLine (L->Line+5);
3289
3290             /* Target points to the label itself. Get the next line */
3291             Target = NextCodeLine (Target);
3292             if (LineFullMatch (Target, "\trts")) {
3293                 /* Replace the jump by an RTS */
3294                 L = ReplaceLine (L, "\trts");
3295             }
3296         }
3297         L = NextCodeLine (L);
3298     }
3299 }
3300
3301
3302
3303 static void OptBoolTransforms (void)
3304 /* Try to remove the boolean transformation subroutines where they aren't
3305  * necessary.
3306  */
3307 {
3308     Line* L2 [2];
3309     unsigned Label;
3310     const char* BranchTarget;
3311
3312     Line* L = FirstCode;
3313     while (L) {
3314
3315         /* Search for a boolean transformer followed by a conditional jump. */
3316         if (LineMatch (L, "\tjsr\tbool") &&
3317             GetNextCodeLines (L, L2, 1) &&
3318             IsCondJump (L2 [0])) {
3319
3320             /* Make the boolean transformer unnecessary by changing the
3321              * the conditional jump to evaluate the condition flags that
3322              * are set after the compare directly. Note: jeq jumps if
3323              * the condition is not met, jne jumps if the condition is met.
3324              */
3325
3326             /* Get the condition code */
3327             int Cond = FindCond (L->Line + 9);
3328             if (Cond < 0) {
3329                 /* OOPS! */
3330                 goto NextLine;
3331             }
3332
3333             /* Invert the code if we jump on condition not met. */
3334             if (L2[0]->Line [2] == 'e' && L2[0]->Line [3] == 'q') {
3335                 /* Jumps if condition false, invert condition */
3336                 Cond = CmpInvertTab [Cond];
3337             }
3338
3339             /* For easier reading, get a pointer to the jump target */
3340             BranchTarget = L2[0]->Line+5;
3341
3342             /* Check if we can replace the jump (sometimes we would need two
3343              * conditional jumps, we will not handle that for now since it
3344              * has some complications - both jumps may be far jumps for
3345              * example making the jumps more costly than the bool transformer
3346              * subroutine). If we cannot replace the jump, bail out.
3347              */
3348             switch (Cond) {
3349
3350                 case CMP_EQ:
3351                     L = ReplaceLine (L, "\tjeq\t%s", BranchTarget);
3352                     break;
3353
3354                 case CMP_NE:
3355                     L = ReplaceLine (L, "\tjne\t%s", BranchTarget);
3356                     break;
3357
3358                 case CMP_GT:
3359                     Label = AllocLabel ();
3360                     L = ReplaceLine  (L, "\tbeq\tL%04X", Label);
3361                     L = NewLineAfter (L, "\tjpl\t%s", BranchTarget);
3362                     L = NewLabelAfter(L, Label);
3363                     break;
3364
3365                 case CMP_GE:
3366                     L = ReplaceLine (L, "\tjpl\t%s", BranchTarget);
3367                     break;
3368
3369                 case CMP_LT:
3370                     L = ReplaceLine (L, "\tjmi\t%s", BranchTarget);
3371                     break;
3372
3373                 case CMP_LE:
3374                     L = ReplaceLine  (L, "\tjeq\t%s", BranchTarget);
3375                     L = NewLineAfter (L, "\tjmi\t%s", BranchTarget);
3376                     break;
3377
3378                 case CMP_UGT:
3379                     Label = AllocLabel ();
3380                     L = ReplaceLine  (L, "\tbeq\tL%04X", Label);
3381                     L = NewLineAfter (L, "\tjcs\t%s", BranchTarget);
3382                     L = NewLabelAfter(L, Label);
3383                     break;
3384
3385                 case CMP_UGE:
3386                     L = ReplaceLine (L, "\tjcs\t%s", BranchTarget);
3387                     break;
3388
3389                 case CMP_ULT:
3390                     L = ReplaceLine (L, "\tjcc\t%s", BranchTarget);
3391                     break;
3392
3393                 case CMP_ULE:
3394                     L = ReplaceLine (L, "\tjeq\t%s", BranchTarget);
3395                     L = NewLineAfter (L, "\tjcc\t%s", BranchTarget);
3396                     break;
3397
3398                 default:
3399                     Internal ("Unknown jump condition: %u", Cond);
3400
3401             }
3402
3403             /* Remove the old stuff */
3404             FreeLine (L2[0]);
3405
3406         }
3407
3408 NextLine:
3409         L = NextCodeLine (L);
3410     }
3411 }
3412
3413
3414
3415 static void OptCompares2 (void)
3416 /* Try to optimize the integer compare subroutines. */
3417 {
3418     Line* L2[10];
3419     unsigned Label;
3420     const char* BranchTarget;
3421     int C;
3422
3423     Line* L = FirstCode;
3424     while (L) {
3425
3426         /* Search for
3427          *
3428          *      lda     x
3429          *      ldx     x+1
3430          *      cpx     #$00
3431          *      bne     *+4
3432          *      cmp     #$00
3433          *      jne/jeq ...
3434          *
3435          * and replace it by
3436          *
3437          *      lda     x
3438          *      ora     x+1
3439          *      jne/jeq ...
3440          */
3441         if (LineMatch (L, "\tlda\t")                                    &&
3442             GetNextCodeLines (L, L2, 5)                                 &&
3443             IsLoadAX (L, L2[0])                                         &&
3444             LineFullMatch (L2[1], "\tcpx\t#$00")                        &&
3445             LineFullMatch (L2[2], "\tbne\t*+4")                         &&
3446             LineFullMatch (L2[3], "\tcmp\t#$00")                        &&
3447             IsCondJump (L2[4])) {
3448
3449             /* Replace the load of X by an ora */
3450             L2[0]->Line[1] = 'o';
3451             L2[0]->Line[2] = 'r';
3452             L2[0]->Line[3] = 'a';
3453
3454             /* Remove unneeded stuff */
3455             FreeLines (L2[1], L2[3]);
3456
3457         }
3458
3459         /* Same for local variables: Replace
3460          *
3461          *      ldy     #$xx
3462          *      lda     (sp),y
3463          *      tax
3464          *      dey
3465          *      lda     (sp),y
3466          *      cpx     #$00
3467          *      bne     *+4                                                                                  cmp     #$00
3468          *      cmp     #$00
3469          *      jne/jeq ...
3470          *
3471          * by
3472          *
3473          *      ldy     #$xx
3474          *      lda     (sp),y
3475          *      dey
3476          *      ora     (sp),y
3477          *      jne/jeq ...
3478          */
3479         else if (LineMatch (L, "\tldy\t#$")                             &&
3480                  GetNextCodeLines (L, L2, 8)                            &&
3481                  LineFullMatch (L2[0], "\tlda\t(sp),y")                 &&
3482                  LineFullMatch (L2[1], "\ttax")                         &&
3483                  LineFullMatch (L2[2], "\tdey")                         &&
3484                  LineFullMatch (L2[3], "\tlda\t(sp),y")                 &&
3485                  LineFullMatch (L2[4], "\tcpx\t#$00")                   &&
3486                  LineFullMatch (L2[5], "\tbne\t*+4")                    &&
3487                  LineFullMatch (L2[6], "\tcmp\t#$00")                   &&
3488                  IsCondJump (L2[7])) {
3489
3490             /* Replace the second load by an ora */
3491             L2[3]->Line[1] = 'o';
3492             L2[3]->Line[2] = 'r';
3493             L2[3]->Line[3] = 'a';
3494
3495             /* Remove unneeded stuff */
3496             FreeLine (L2[1]);
3497             FreeLines (L2[4], L2[6]);
3498
3499         }
3500
3501         /* Search for the call to a compare subroutine followed by a
3502          * conditional jump.
3503          */
3504         else if (LineMatch (L, "\tjsr\ttos")                            &&
3505                 (L2[0] = NextCodeLine (L)) != 0                         &&
3506                 IsCondJump (L2[0])) {
3507
3508             /* Extract the condition from the function name and branch */
3509             C = CheckAndGetIntCmp (L, L2[0]);
3510             if (C < 0) {
3511                 /* Something is wrong */
3512                 goto NextLine;
3513             }
3514
3515             /* Replace the subroutine call by a cheaper one */
3516             L = ReplaceLine (L, "\tjsr\ttosicmp");
3517
3518             /* For easier reading, get a pointer to the jump target */
3519             BranchTarget = L2[0]->Line+5;
3520
3521             /* Check if we can replace the jump (sometimes we would need two
3522              * conditional jumps, we will not handle that for now since it
3523              * has some complications - both jumps may be far jumps for
3524              * example making the jumps more costly than the bool transformer
3525              * subroutine). If we cannot replace the jump, bail out.
3526              */
3527             switch (C) {
3528
3529                 case CMP_EQ:
3530                     L = NewLineAfter (L, "\tjeq\t%s", BranchTarget);
3531                     break;
3532
3533                 case CMP_NE:
3534                     L = NewLineAfter (L, "\tjne\t%s", BranchTarget);
3535                     break;
3536
3537                 case CMP_GT:
3538                     Label = AllocLabel ();
3539                     L = NewLineAfter (L, "\tbeq\tL%04X", Label);
3540                     L = NewLineAfter (L, "\tjpl\t%s", BranchTarget);
3541                     L = NewLabelAfter(L, Label);
3542                     break;
3543
3544                 case CMP_GE:
3545                     L = NewLineAfter (L, "\tjpl\t%s", BranchTarget);
3546                     break;
3547
3548                 case CMP_LT:
3549                     L = NewLineAfter (L, "\tjmi\t%s", BranchTarget);
3550                     break;
3551
3552                 case CMP_LE:
3553                     L = NewLineAfter (L, "\tjeq\t%s", BranchTarget);
3554                     L = NewLineAfter (L, "\tjmi\t%s", BranchTarget);
3555                     break;
3556
3557                 case CMP_UGT:
3558                     Label = AllocLabel ();
3559                     L = NewLineAfter (L, "\tbeq\tL%04X", Label);
3560                     L = NewLineAfter (L, "\tjcs\t%s", BranchTarget);
3561                     L = NewLabelAfter(L, Label);
3562                     break;
3563
3564                 case CMP_UGE:
3565                     L = NewLineAfter (L, "\tjcs\t%s", BranchTarget);
3566                     break;
3567
3568                 case CMP_ULT:
3569                     L = NewLineAfter (L, "\tjcc\t%s", BranchTarget);
3570                     break;
3571
3572                 case CMP_ULE:
3573                     L = NewLineAfter (L, "\tjeq\t%s", BranchTarget);
3574                     L = NewLineAfter (L, "\tjcc\t%s", BranchTarget);
3575                     break;
3576
3577                 default:
3578                     Internal ("Unknown jump condition: %u", C);
3579
3580             }
3581
3582             /* Remove the old stuff */
3583             FreeLine (L2[0]);
3584         }
3585
3586 NextLine:
3587         L = NextCodeLine (L);
3588     }
3589 }
3590
3591
3592
3593 static void OptTests (void)
3594 /* Remove unnecessary tests */
3595 {
3596     Line* L2 [2];
3597
3598     static const char* BitOps [] = {
3599         "\tand\t",
3600         "\tora\t",
3601         "\teor\t",
3602         0
3603     };
3604
3605     /* Search for lda/tay/jne or lda/tay/jeq, remove the tay.
3606      * Search for
3607      *  lda ...
3608      *  cmp #$00
3609      *  jne/jeq
3610      * Remove the cmp.
3611      */
3612     Line* L = FirstCode;
3613     while (L) {
3614
3615         /* Search for lda/tay/jne or lda/tay/jeq, remove the tay.
3616          * Search for
3617          *      lda/and/ora/eor
3618          *      cmp     #$00
3619          *      jne/jeq ...
3620          * Remove the cmp.
3621          */
3622         if ((LineMatch (L, "\tlda\t")               ||
3623              LineMatch (L, "\tand\t")               ||
3624              LineMatch (L, "\tora\t")               ||
3625              LineMatch (L, "\teor\t"))                  &&
3626             GetNextCodeLines (L, L2, 2)                 &&
3627             (LineFullMatch (L2 [0], "\ttay")        ||
3628              LineFullMatch (L2 [0], "\tcmp\t#$00"))     &&
3629             IsCondJump (L2 [1])) {
3630
3631             /* We can remove the tay */
3632             FreeLine (L2 [0]);
3633
3634         }
3635
3636         /* Search for
3637          *
3638          *      and     ...
3639          *      tax
3640          *      jeq/jne
3641          *
3642          * and remove the tax.
3643          */
3644         else if (LineMatchX (L, BitOps) >= 0            &&
3645                  GetNextCodeLines (L, L2, 2)            &&
3646                  LineFullMatch (L2[0], "\ttax")         &&
3647                  IsCondJump (L2[1])) {
3648
3649             /* Remove the tax including a hint line of there is one */
3650             if (LineFullMatch (L2[0]->Prev, "+forcetest")) {
3651                 FreeLine (L2[0]->Prev);
3652             }
3653             FreeLine (L2[0]);
3654
3655             /* If the line before L loads X, this is useless and may be removed */
3656             L2[0] = PrevCodeLine (L);
3657             if (LineFullMatch (L2[0], "\tldx\t#$00")) {
3658                 FreeLine (L2[0]);
3659             }
3660
3661         }
3662
3663         /* Search for the sequence
3664          *
3665          *      stx     xx
3666          *      stx     tmp1
3667          *      ora     tmp1
3668          *
3669          * and replace it by
3670          *
3671          *      stx     xx
3672          *      ora     xx
3673          */
3674         else if (LineMatch (L, "\tstx\t")               &&
3675                  GetNextCodeLines (L, L2, 2)            &&
3676                  LineFullMatch (L2[0], "\tstx\ttmp1")   &&
3677                  LineFullMatch (L2[1], "\tora\ttmp1")) {
3678
3679             /* Found, replace it */
3680             L = NewLineAfter (L, "\tora\t%s", L->Line+5);
3681
3682             /* Remove remaining stuff */
3683             FreeLines (L2[0], L2[1]);
3684
3685         }
3686
3687
3688         /* Next line */
3689         L = NextCodeLine (L);
3690     }
3691 }
3692
3693
3694
3695 static void OptBitOps (void)
3696 /* Optimize bit oeprations */
3697 {
3698     Line* L2 [2];
3699
3700     /* Walk over the code */
3701     Line* L = FirstCode;
3702     while (L) {
3703
3704         /* Search for
3705          *
3706          *      lda     xxx
3707          *      and     #$yy    ; adc/eor/ora
3708          *      sta     xxx
3709          *
3710          * and replace it by
3711          *
3712          *      lda     #$yy
3713          *      and     xxx
3714          *      sta     xxx
3715          *
3716          * While this saves nothing here, it transforms the code to contain an
3717          * explicit register load that may be removed by the basic block
3718          * optimization later. As a special optimization for the 65C02, the
3719          * "ora" and "and" ops may be replaced by "trb" and "tsb" resp. if the
3720          * value in A is not used later.
3721          */
3722         if (LineMatch (L, "\tlda\t")                    &&
3723             L->Line[5] != '#'                           &&
3724             GetNextCodeLines (L, L2, 2)                 &&
3725             LineMatch (L2[1], "\tsta\t")                &&
3726             strcmp (L->Line+5, L2[1]->Line+5) == 0) {
3727
3728             if (LineMatch (L2[0], "\tand\t#$")) {
3729
3730                 unsigned Val = GetHexNum (L2[0]->Line+7);
3731                 if (Val == 0x00) {
3732
3733                     /* AND with 0x00, remove the mem access */
3734                     FreeLine (L);
3735                     FreeLine (L2[1]);
3736
3737                     /* Replace the AND by a load */
3738                     L = ReplaceLine (L2[0], "\tlda\t#$%02X", Val);
3739
3740                 } else if (Val == 0xFF) {
3741
3742                     /* AND with 0xFF, just load the value from memory */
3743                     FreeLines (L2[0], L2[1]);
3744
3745                 } else if (CPU == CPU_65C02     &&
3746                            !IsXAddrMode (L)     &&
3747                            !IsYAddrMode (L)     &&
3748                            !RegAUsed (L2[1])) {
3749
3750                     /* Replace by trb */
3751                     ReplaceLine (L,     "\tlda\t#$%02X", (~Val) & 0xFF);
3752                     L = ReplaceLine (L2[0], "\ttrb\t%s", L2[1]->Line+5);
3753                     FreeLine (L2[1]);
3754
3755                 } else {
3756
3757                     /* Just reorder */
3758                     ReplaceLine (L, "\tlda\t#$%02X", Val);
3759                     ReplaceLine (L2[0], "\tand\t%s", L2[1]->Line+5);
3760                     L = L2[1];
3761
3762                 }
3763
3764             } else if (LineMatch (L2[0], "\tora\t#$")) {
3765
3766                 unsigned Val = GetHexNum (L2[0]->Line+7);
3767                 if (Val == 0x00) {
3768
3769                     /* ORA with 0x00, just load the value from memory */
3770                     FreeLines (L2[0], L2[1]);
3771
3772                 } else if (Val == 0xFF) {
3773
3774                     /* ORA with 0xFF, replace by a store of $FF */
3775                     FreeLine (L);
3776                     L = ReplaceLine (L2[0], "\tlda\t#$FF");
3777
3778                 } else if (CPU == CPU_65C02     &&
3779                            !IsXAddrMode (L)     &&
3780                            !IsYAddrMode (L)     &&
3781                            !RegAUsed (L2[1])) {
3782
3783                     /* Replace by trb */
3784                     ReplaceLine (L,     "\tlda\t#$%02X", Val);
3785                     L = ReplaceLine (L2[0], "\ttsb\t%s", L2[1]->Line+5);
3786                     FreeLine (L2[1]);
3787
3788                 } else {
3789
3790                     /* Just reorder */
3791                     ReplaceLine (L, "\tlda\t#$%02X", Val);
3792                     ReplaceLine (L2[0], "\tora\t%s", L2[1]->Line+5);
3793                     L = L2[1];
3794
3795                 }
3796
3797             } else if (LineMatch (L2[0], "\teor\t#$") ||
3798                        LineMatch (L2[0], "\tadc\t#$")) {
3799
3800                 /* Just reorder */
3801                 L = ReplaceLine (L, "\tlda\t%s", L2[0]->Line+5);
3802                 ReplaceLine (L2[0], "\t%.3s\t%s", L2[0]->Line+1, L2[1]->Line+5);
3803
3804             }
3805         }
3806
3807         /* Next line */
3808         L = NextCodeLine (L);
3809     }
3810 }
3811
3812
3813
3814 static void OptNeg (void)
3815 /* Optimize the "bnegax/jeq" and "bnegax/jne" sequences */
3816 {
3817     Line* L2 [10];
3818
3819     Line* L = FirstCode;
3820     while (L) {
3821
3822         /* Search for the sequence:
3823          *
3824          *      lda     ...
3825          *      jsr     bnega
3826          *      jeq/jne ...
3827          *
3828          * and replace it by:
3829          *
3830          *      lda     ...
3831          *      jne/jeq ...
3832          */
3833         if (LineMatch (L, "\tlda\t")                    && /* Match on start */
3834             GetNextCodeLines (L, L2, 2)                 && /* Fetch next lines */
3835             LineFullMatch (L2 [0], "\tjsr\tbnega")      &&
3836             IsCondJump (L2 [1])) {
3837
3838             /* Found the sequence, replace it */
3839             FreeLine (L2 [0]);
3840             InvertZJump (L2 [1]);
3841
3842         }
3843
3844         /* Search for the sequence:
3845          *
3846          *      ldy     #$xx
3847          *      lda     (sp),y
3848          *      tax
3849          *      dey
3850          *      lda     (sp),y
3851          *      jsr     bnegax
3852          *      jne/jeq ...
3853          *
3854          * and replace it by
3855          *
3856          *      ldy     #$xx
3857          *      lda     (sp),y
3858          *      dey
3859          *      ora     (sp),y
3860          *      jeq/jne ...
3861          */
3862         else if (LineMatch (L, "\tldy\t#$")                     &&
3863                  GetNextCodeLines (L, L2, 6)                    &&
3864                  LineFullMatch (L2[0], "\tlda\t(sp),y")         &&
3865                  LineFullMatch (L2[1], "\ttax")                 &&
3866                  LineFullMatch (L2[2], "\tdey")                 &&
3867                  LineFullMatch (L2[3], "\tlda\t(sp),y")         &&
3868                  LineFullMatch (L2[4], "\tjsr\tbnegax")         &&
3869                  IsCondJump    (L2[5])) {
3870
3871             L2[1] = ReplaceLine (L2[1], "\tdey");
3872             L2[2] = ReplaceLine (L2[2], "\tora\t(sp),y");
3873             FreeLines (L2[3], L2[4]);
3874             InvertZJump (L2[5]);
3875
3876         }
3877
3878         /* Search for the sequence:
3879          *
3880          *      lda     xx
3881          *      ldx     xx+1
3882          *      jsr     bnegax
3883          *      jne/jeq ...
3884          *
3885          * and replace it by
3886          *
3887          *      lda     xx
3888          *      ora     xx+1
3889          *      jeq/jne ...
3890          */
3891         else if (LineMatch (L, "\tlda\t")                       &&
3892                  GetNextCodeLines (L, L2, 3)                    &&
3893                  IsLoadAX (L, L2[0])                            &&
3894                  LineFullMatch (L2[1], "\tjsr\tbnegax")         &&
3895                  IsCondJump    (L2[2])) {
3896
3897             /* Replace the load of X by ora */
3898             L2[0]->Line[1] = 'o';
3899             L2[0]->Line[2] = 'r';
3900             L2[0]->Line[3] = 'a';
3901             FreeLine (L2[1]);
3902             InvertZJump (L2[2]);
3903
3904         }
3905
3906         /* Search for the sequence:
3907          *
3908          *      jsr     _xxx
3909          *      jsr     bnega(x)
3910          *      jeq/jne ...
3911          *
3912          * and replace it by:
3913          *
3914          *      jsr     _xxx
3915          *      <boolean test>
3916          *      jne/jeq ...
3917          */
3918         else if (LineMatch (L, "\tjsr\t_")              && /* Match on start */
3919                  GetNextCodeLines (L, L2, 2)            &&
3920                  LineMatch (L2 [0], "\tjsr\tbnega")     &&
3921                  IsCondJump (L2 [1])) {
3922
3923             if (LineFullMatch (L2 [0], "\tjsr\tbnega")) {
3924                 /* Byte sized */
3925                 L2 [0] = ReplaceLine (L2 [0], "\ttax"); /* Test a */
3926             } else {
3927                 /* Word sized */
3928                 L2 [0] = ReplaceLine (L2 [0], "\tstx\ttmp1");
3929                 NewLineAfter (L2 [0], "\tora\ttmp1");
3930             }
3931
3932             /* Invert the jump */
3933             InvertZJump (L2 [1]);
3934
3935         }
3936
3937         /* Next line */
3938         L = NextCodeLine (L);
3939     }
3940 }
3941
3942
3943
3944 static void OptTriples (void)
3945 /* Replace code triples */
3946 {
3947     static const char* Pat1 [] = {
3948         "\tjsr\tldaxysp",
3949         "\tjsr\tldax0sp",
3950         "\tjsr\tldaysp",
3951         "\tjsr\tleaasp",
3952         "\tjsr\tldaxi",
3953         0
3954     };
3955     static const char* Pat2 [] = {
3956         "\tjsr\tpushax",
3957         "\tjsr\tpushax",
3958         "\tjsr\tpushax",
3959         "\tjsr\tpushax",
3960         "\tjsr\tpushax",
3961         0
3962     };
3963     static const char* Replace [] = {
3964         "\tjsr\tpushwysp",
3965         "\tjsr\tpushw0sp",
3966         "\tjsr\tpushbysp",
3967         "\tjsr\tpleaasp",
3968         "\tjsr\tpushw",
3969     };
3970
3971     Line* L = FirstCode;
3972     while (L) {
3973         int I = LineFullMatchX (L, Pat1);
3974         if (I >= 0) {
3975             /* We found the first match, get the next line */
3976             Line* L2 = NextCodeLine (L);
3977             if (L2 && LineFullMatch (L2, Pat2 [I])) {
3978                 /* Found. Replace by the short call */
3979                 FreeLine (L2);
3980                 L = ReplaceLine (L, Replace [I]);
3981             }
3982         }
3983         /* Next line */
3984         L = NextCodeLine (L);
3985     }
3986 }
3987
3988
3989
3990 static Line* OptOneBlock (Line* L)
3991 /* Optimize the register contents inside one basic block */
3992 {
3993     static const char* Compares [] = {
3994         "\tjsr\ttoseq00",   "\tjsr\ttoseqa0",   "\tjsr\ttoseqax",
3995         "\tjsr\ttoseqeax",  "\tjsr\ttosne00",   "\tjsr\ttosnea0",
3996         "\tjsr\ttosneax",   "\tjsr\ttosneeax",  "\tjsr\ttoslt00",
3997         "\tjsr\ttoslta0",   "\tjsr\ttosltax",   "\tjsr\ttosult00",
3998         "\tjsr\ttosulta0",  "\tjsr\ttosultax",  "\tjsr\ttoslteax",
3999         "\tjsr\ttosulteax", "\tjsr\ttosle00",   "\tjsr\ttoslea0",
4000         "\tjsr\ttosleax",   "\tjsr\ttosule00",  "\tjsr\ttosulea0",
4001         "\tjsr\ttosuleax",  "\tjsr\ttosleeax",  "\tjsr\ttosuleeax",
4002         "\tjsr\ttosgt00",   "\tjsr\ttosgta0",   "\tjsr\ttosgtax",
4003         "\tjsr\ttosugt00",  "\tjsr\ttosugta0",  "\tjsr\ttosugtax",
4004         "\tjsr\ttosgteax",  "\tjsr\ttosugteax", "\tjsr\ttosge00",
4005         "\tjsr\ttosgea0",   "\tjsr\ttosgeax",   "\tjsr\ttosuge00",
4006         "\tjsr\ttosugea0",  "\tjsr\ttosugeax",  "\tjsr\ttosgeeax",
4007         "\tjsr\ttosugeeax",
4008         0
4009     };
4010
4011     static const char* MakeBool [] = {
4012         "\tjsr\tbooleq",    "\tjsr\tboolne",    "\tjsr\tboollt",
4013         "\tjsr\tboolle",    "\tjsr\tboolgt",    "\tjsr\tboolge",
4014         "\tjsr\tboolult",   "\tjsr\tboolule",   "\tjsr\tboolugt",
4015         "\tjsr\tbooluge",
4016         0
4017     };
4018
4019     int A = -1;                 /* Contents of A register */
4020     int X = -1;                 /* Contents of X register */
4021     int Y = -1;                 /* Contents of Y register */
4022     Line* L2;
4023     unsigned NewVal;
4024     int Delete;
4025
4026     while (L && !IsLabel (L)) {
4027
4028         /* Handle all instructions. All instructions not tested here have
4029          * no effects on the register contents.
4030          */
4031         Delete = 0;
4032         if (L->Line [0] == '+') {
4033             /* This is a hint */
4034             if (LineMatch (L, "+a:")) {
4035                 /* Information about a */
4036                 switch (L->Line [3]) {
4037                     case '!':   A = -1;                         break;
4038                     case '=':   A = GetHexNum (L->Line + 4);    break;
4039                 }
4040             } else if (LineMatch (L, "+x:")) {
4041                 /* The code generator tells something about the x register */
4042                 switch (L->Line [3]) {
4043                     case '!':   X = -1;                         break;
4044                     case '=':   X = GetHexNum (L->Line + 4);    break;
4045                 }
4046             } else if (LineMatch (L, "+y:")) {
4047                 /* Information about the y register */
4048                 switch (L->Line [3]) {
4049                     case '!':   Y = -1;                         break;
4050                     case '=':   Y = GetHexNum (L->Line + 4);    break;
4051                 }
4052             }
4053         } else if (LineMatch (L, "\tadc\t")) {
4054             if (CPU == CPU_65C02 && Y == 0 && L->Line[5] == '(' && IsYAddrMode(L)) {
4055                 L->Line[strlen(L->Line)-2] = '\0';
4056             }
4057             A = -1;
4058         } else if (LineMatch (L, "\tand\t")) {
4059             A = -1;
4060         } else if (LineFullMatch (L, "\tasl\ta")) {
4061             if (A != -1) {
4062                 A = (A << 1) & 0xFF;
4063             }
4064         } else if (CPU == CPU_65C02 && Y == 0 && L->Line[5] == '(' && IsYAddrMode(L)) {
4065             L->Line[strlen(L->Line)-2] = '\0';
4066         } else if (CPU == CPU_65C02 && (LineFullMatch (L, "\tdea") ||
4067                                         LineFullMatch (L, "\tdec\ta"))) {
4068             DEC (A, 1);
4069         } else if (LineFullMatch (L, "\tdex")) {
4070             DEC (X, 1);
4071         } else if (LineFullMatch (L, "\tdey")) {
4072             DEC (Y, 1);
4073         } else if (LineMatch (L, "\teor")) {
4074             A = -1;
4075         } else if (CPU == CPU_65C02 && (LineFullMatch (L, "\tina") ||
4076                                         LineFullMatch (L, "\tinc\ta"))) {
4077             INC (A, 1);
4078         } else if (LineFullMatch (L, "\tinx")) {
4079             INC (X, 1);
4080         } else if (LineFullMatch (L, "\tiny")) {
4081             INC (Y, 1);
4082         } else if (LineFullMatch (L, "\tjsr\taddeq0sp")) {
4083             /* We know about this function */
4084             A = X = -1;
4085             Y = 1;
4086         } else if (LineFullMatch (L, "\tjsr\taddeqysp")) {
4087             /* We know about this function */
4088             A = X = -1;
4089             INC (Y, 1);
4090         } else if (LineFullMatch (L, "\tjsr\taxulong")) {
4091             /* We know about this function and we're trying to replace it by
4092              * inline code if we have already a register that contains zero.
4093              */
4094             char C;
4095             if (A == 0) {
4096                 C = 'a';
4097             } else if (X == 0) {
4098                 C = 'x';
4099             } else if (Y == 0) {
4100                 C = 'y';
4101             } else {
4102                 C = '\0';
4103             }
4104             if (C == '\0') {
4105                 /* We cannot replace the code, but we know about the results */
4106                 Y = 0;
4107             } else {
4108                 L = ReplaceLine (L, "\tst%c\tsreg", C);
4109                 NewLineAfter (L, "\tst%c\tsreg+1", C);
4110             }
4111         } else if (LineFullMatch (L, "\tjsr\tbnega")) {
4112             /* We know about this function */
4113             A = -1;
4114             X = 0;
4115         } else if (LineFullMatch (L, "\tjsr\tbnegax")) {
4116             /* We know about this function */
4117             A = -1;
4118             X = 0;
4119         } else if (LineFullMatch (L, "\tjsr\tbnegeax")) {
4120             /* We know about this function */
4121             A = -1;
4122             X = 0;
4123         } else if (LineFullMatch (L, "\tjsr\tcomplax")) {
4124             /* We know about this function */
4125             if (A != -1) {
4126                 A ^= 0xFF;
4127             }
4128             if (X != -1) {
4129                 X ^= 0xFF;
4130             }
4131         } else if (LineFullMatch (L, "\tjsr\tdecax1")) {
4132             /* We know about this function */
4133             A = X = -1;
4134         } else if (LineFullMatch (L, "\tjsr\tdecax2")) {
4135             /* We know about this function */
4136             A = X = -1;
4137         } else if (LineFullMatch (L, "\tjsr\tdecaxy")) {
4138             /* We know about this function */
4139             A = X = -1;
4140         } else if (LineFullMatch (L, "\tjsr\tdeceaxy")) {
4141             /* We know about this function */
4142             A = X = -1;
4143         } else if (LineMatch (L, "\tjsr\tdecsp") && IsDigit (L->Line[10])) {
4144             if (L->Line[10] == '1') {
4145                 /* Destroys Y, leaves all other regs alone */
4146                 Y = -1;
4147             } else {
4148                 /* Destroys A, leaves all other regs alone */
4149                 A = -1;
4150             }
4151         } else if (LineFullMatch (L, "\tjsr\tincax1")) {
4152             /* We know about this function */
4153             A = X = -1;
4154         } else if (LineFullMatch (L, "\tjsr\tincax2")) {
4155             /* We know about this function */
4156             A = X = -1;
4157         } else if (LineFullMatch (L, "\tjsr\tinceaxy")) {
4158             /* We know about this function */
4159             A = X = -1;
4160         } else if (LineFullMatch (L, "\tjsr\tladdeq")) {
4161             /* We know about this function */
4162             A = X = -1;
4163             Y = 3;
4164         } else if (LineFullMatch (L, "\tjsr\tladdeqb")) {
4165             /* We know about this function */
4166             A = X = -1;
4167             Y = 3;
4168         } else if (LineFullMatch (L, "\tjsr\tlbneg")) {
4169             /* We know about this function */
4170             A = -1;
4171             X = 0;
4172         } else if (LineFullMatch (L, "\tjsr\tldai")) {
4173             /* We know about this function */
4174             A = X = -1;
4175             Y = 0;
4176         } else if (LineFullMatch (L, "\tjsr\tldaidx")) {
4177             /* We know about this function */
4178             A = X = -1;
4179         } else if (LineFullMatch (L, "\tjsr\tldau00sp")) {
4180             /* We know about this function */
4181             A = -1;
4182             X = 0;
4183             Y = 0;
4184         } else if (LineFullMatch (L, "\tjsr\tldau0ysp")) {
4185             /* We know about this function */
4186             A = -1;
4187             X = 0;
4188             DEC (Y, 1);
4189         } else if (LineFullMatch (L, "\tjsr\tldaui")) {
4190             /* We know about this function */
4191             A = -1;
4192             X = 0;
4193             Y = 0;
4194         } else if (LineFullMatch (L, "\tjsr\tldaui0sp")) {
4195             A = -1;
4196             Y = X;
4197             X = 0;
4198         } else if (LineFullMatch (L, "\tjsr\tldauidx")) {
4199             /* We know about this function */
4200             A = -1;
4201             X = 0;
4202         } else if (LineFullMatch (L, "\tjsr\tldauiysp")) {
4203             /* We know about this function */
4204             A = -1;
4205             Y = X;
4206             X = 0;
4207         } else if (LineFullMatch (L, "\tjsr\tldax0sp")) {
4208             /* We know about this function */
4209             A = X = -1;
4210             Y = 0;
4211         } else if (LineFullMatch (L, "\tjsr\tldaxi")) {
4212             /* We know about this function */
4213             A = X = -1;
4214             Y = 0;
4215         } else if (LineFullMatch (L, "\tjsr\tldaxidx")) {
4216             /* We know about this function */
4217             A = X = -1;
4218             DEC (Y, 1);
4219         } else if (LineFullMatch (L, "\tjsr\tldaxysp")) {
4220             /* We know about this function */
4221             A = X = -1;
4222             DEC (Y, 1);
4223         } else if (LineFullMatch (L, "\tjsr\tldeaxi")) {
4224             /* We know about this function */
4225             A = X = -1;
4226             Y = 0;
4227         } else if (LineFullMatch (L, "\tjsr\tldeaxidx")) {
4228             /* We know about this function */
4229             A = X = -1;
4230             DEC (Y, 3);
4231         } else if (LineFullMatch (L, "\tjsr\tlsubeq")) {
4232             /* We know about this function */
4233             A = X = -1;
4234             Y = 3;
4235         } else if (LineFullMatch (L, "\tjsr\tlsubeqb")) {
4236             /* We know about this function */
4237             A = X = -1;
4238             Y = 3;
4239         } else if (LineFullMatch (L, "\tjsr\tnegax")) {
4240             /* We know about this function */
4241             A = X = -1;
4242         } else if (LineFullMatch (L, "\tjsr\tnegeax")) {
4243             /* We know about this function */
4244             A = X = -1;
4245         } else if (LineFullMatch (L, "\tjsr\tpush0")) {
4246             /* We know about this function */
4247             A = 0;
4248             X = 0;
4249             Y = 0;
4250         } else if (LineFullMatch (L, "\tjsr\tpush0ax")) {
4251             /* We know about this function */
4252             Y = 0;
4253         } else if (LineFullMatch (L, "\tjsr\tpush1")) {
4254             /* We know about this function */
4255             A = 1;
4256             X = 0;
4257             Y = 0;
4258         } else if (LineFullMatch (L, "\tjsr\tpush2")) {
4259             /* We know about this function */
4260             A = 2;
4261             X = 0;
4262             Y = 0;
4263         } else if (LineFullMatch (L, "\tjsr\tpush3")) {
4264             /* We know about this function */
4265             A = 3;
4266             X = 0;
4267             Y = 0;
4268         } else if (LineFullMatch (L, "\tjsr\tpush4")) {
4269             /* We know about this function */
4270             A = 4;
4271             X = 0;
4272             Y = 0;
4273         } else if (LineFullMatch (L, "\tjsr\tpush5")) {
4274             /* We know about this function */
4275             A = 5;
4276             X = 0;
4277             Y = 0;
4278         } else if (LineFullMatch (L, "\tjsr\tpush6")) {
4279             /* We know about this function */
4280             A = 6;
4281             X = 0;
4282             Y = 0;
4283         } else if (LineFullMatch (L, "\tjsr\tpush7")) {
4284             /* We know about this function */
4285             A = 7;
4286             X = 0;
4287             Y = 0;
4288         } else if (CPU != CPU_65C02 && LineFullMatch (L, "\tjsr\tpusha")) {
4289             /* We know about this function */
4290             Y = 0;
4291         } else if (LineFullMatch (L, "\tjsr\tpusha0")) {
4292             /* We know about this function
4293              * If X is already zero, we may call pushax instead and save two
4294              * cycles.
4295              */
4296             if (X == 0) {
4297                 L = ReplaceLine (L, "\tjsr\tpushax");
4298             }
4299             X = 0;
4300             Y = 0;
4301         } else if (LineFullMatch (L, "\tjsr\tpushax")) {
4302             /* We know about this function */
4303             Y = 0;
4304         } else if (CPU != CPU_65C02  && LineFullMatch (L, "\tjsr\tpushaysp")) {
4305             /* We know about this function */
4306             A = -1;
4307             Y = 0;
4308         } else if (LineFullMatch (L, "\tjsr\tpushc0")) {
4309             /* We know about this function */
4310             A = 0;
4311             Y = 0;
4312         } else if (LineFullMatch (L, "\tjsr\tpushc1")) {
4313             /* We know about this function */
4314             A = 1;
4315             Y = 0;
4316         } else if (LineFullMatch (L, "\tjsr\tpushc2")) {
4317             /* We know about this function */
4318             A = 2;
4319             Y = 0;
4320         } else if (LineFullMatch (L, "\tjsr\tpusheax")) {
4321             /* We know about this function */
4322             Y = 0;
4323         } else if (LineFullMatch (L, "\tjsr\tpushw")) {
4324             /* We know about this function (calls pushax) */
4325             A = X = -1;
4326             Y = 0;
4327         } else if (LineFullMatch (L, "\tjsr\tpushw0sp")) {
4328             /* We know about this function(calls pushax)  */
4329             A = X = -1;
4330             Y = 0;
4331         } else if (LineFullMatch (L, "\tjsr\tpushwidx")) {
4332             /* We know about this function (calls pushax) */
4333             A = X = -1;
4334             Y = 0;
4335         } else if (LineFullMatch (L, "\tjsr\tpushwysp")) {
4336             /* We know about this function (calls pushax) */
4337             A = X = -1;
4338             Y = 0;
4339         } else if (LineFullMatch (L, "\tjsr\tresteax")) {
4340             /* We know about this function */
4341             A = X = -1;
4342         } else if (LineFullMatch (L, "\tjsr\tsaveeax")) {
4343             /* We know about this function */
4344             /* Changes nothing */
4345         } else if (LineFullMatch (L, "\tjsr\tshrax1")) {
4346             /* We know about this function */
4347             A = X = -1;
4348         } else if (LineFullMatch (L, "\tjsr\tshrax2")) {
4349             /* We know about this function */
4350             A = X = -1;
4351         } else if (LineFullMatch (L, "\tjsr\tshrax3")) {
4352             /* We know about this function */
4353             A = X = -1;
4354         } else if (LineFullMatch (L, "\tjsr\tshreax1")) {
4355             /* We know about this function */
4356             A = X = -1;
4357         } else if (LineFullMatch (L, "\tjsr\tshreax2")) {
4358             /* We know about this function */
4359             A = X = -1;
4360         } else if (LineFullMatch (L, "\tjsr\tshreax3")) {
4361             /* We know about this function */
4362             A = X = -1;
4363         } else if (LineFullMatch (L, "\tjsr\tstaspp")) {
4364             /* We know about this function */
4365             Y = -1;
4366         } else if (LineFullMatch (L, "\tjsr\tstaxspp")) {
4367             /* We know about this function */
4368             Y = -1;
4369         } else if (LineFullMatch (L, "\tjsr\tstax0sp")) {
4370             /* We know about this function */
4371             Y = 1;
4372         } else if (LineFullMatch (L, "\tjsr\tstaxysp")) {
4373             /* We know about this function */
4374             INC (Y, 1);
4375         } else if (LineFullMatch (L, "\tjsr\tsubeq0sp")) {
4376             /* We know about this function */
4377             A = X = -1;
4378             Y = 1;
4379         } else if (LineFullMatch (L, "\tjsr\tsubeqysp")) {
4380             /* We know about this function */
4381             A = X = -1;
4382             INC (Y, 1);
4383         } else if (LineFullMatch (L, "\tjsr\tsubysp")) {
4384             /* We know about this function */
4385             A = -1;
4386         } else if (LineFullMatch (L, "\tjsr\ttosadda0")) {
4387             /* We know about this function */
4388             A = X = -1;
4389             Y = 1;
4390         } else if (LineFullMatch (L, "\tjsr\ttosaddax")) {
4391             /* We know about this function */
4392             A = X = -1;
4393             Y = 1;
4394         } else if (LineFullMatch (L, "\tjsr\ttosicmp")) {
4395             /* We know about this function */
4396             A = X = -1;
4397             Y = 0;
4398         } else if (LineFullMatchX (L, Compares) >= 0) {
4399             A = Y = -1;
4400             X = 0;
4401         } else if (LineFullMatchX (L, MakeBool) >= 0) {
4402             A = -1;
4403             X = 0;
4404         } else if (LineMatch (L, "\tjsr\t")) {
4405             /* Subroutine call, forget all register information */
4406             A = X = Y = -1;
4407         } else if (LineMatch (L, "\tlda\t")) {
4408             if (!RegAUsed (L) && !IsCondJump (NextInstruction (L))) {
4409                 /* The value loaded is not used later, remove it */
4410                 Delete = 1;
4411             } else if (LineMatch (L, "\tlda\t(")) {
4412                 if (IsXAddrMode (L)) {
4413                     /* lda (zp,x). */
4414                     /* If X is zero and we have a 65C02 cpu, replace it by
4415                      * an indirect load.
4416                      */
4417                     if (X == 0 && CPU == CPU_65C02) {
4418                         unsigned Len = strlen (L->Line);
4419                         L->Line [Len-3] = ')';
4420                         L->Line [Len-2] = '\0';
4421                     /* If Y and X are both zero, replace by load indirect
4422                      * y and save one cycle in some cases.
4423                      */
4424                     } else if (X == 0 && Y == 0) {
4425                         char Buf [256];
4426                         const char* S = L->Line + 6;
4427                         char* T = Buf + 6;
4428                         strcpy (Buf, "\tlda\t(");
4429                         while (*S != ',') {
4430                             *T++ = *S++;
4431                         }
4432                         *T++ = ')';
4433                         *T++ = ',';
4434                         *T++ = 'y';
4435                         *T   = '\0';
4436                         L = ReplaceLine (L, Buf);
4437                     }
4438                 } else if (IsYAddrMode (L)) {
4439                     /* lda (zp),y. If Y is zero and we have a 65C02 CPU,
4440                      * replace it by an indirect load.
4441                      */
4442                     if (Y == 0 && CPU == CPU_65C02) {
4443                         unsigned Len = strlen (L->Line);
4444                         L->Line [Len-3] = ')';
4445                         L->Line [Len-2] = '\0';
4446                     }
4447                 }
4448                 /* In any case invalidate A */
4449                 A = -1;
4450             } else if (LineMatch (L, "\tlda\t#$")) {
4451                 /* Immidiate load into A */
4452                 NewVal = GetHexNum (L->Line + 7);
4453                 if (NewVal == A) {
4454                     /* Load has no effect */
4455                     Delete = 1;
4456                 } else if (NewVal == X) {
4457                     /* Requested value is already in X */
4458                     L = ReplaceLine (L, "\ttxa");
4459                 } else if (NewVal == Y) {
4460                     /* Requested value is already in Y */
4461                     L = ReplaceLine (L, "\ttya");
4462                 } else if (CPU == CPU_65C02 && A != -1) {
4463                     /* Try ina/dea operators of 65C02 */
4464                     if (NewVal == ((A - 1) & 0xFF)) {
4465                         L = ReplaceLine (L, "\tdea");
4466                     } else if (NewVal == ((A + 1) & 0xFF)) {
4467                         L = ReplaceLine (L, "\tina");
4468                     }
4469                 }
4470                 /* Anyway, the new value is now in A */
4471                 A = NewVal;
4472             } else {
4473                 /* Memory load into A */
4474                 A = -1;
4475             }
4476         } else if (LineMatch (L, "\tldax\t")) {
4477             /* Memory load into A and X */
4478             A = X = -1;
4479         } else if (LineMatch (L, "\tldx\t")) {
4480             if (!RegXUsed (L) && !IsCondJump (NextInstruction (L))) {
4481                 /* The value loaded is not used later, remove it */
4482                 Delete = 1;
4483             } else if (LineMatch (L, "\tldx\t#$")) {
4484                 /* Immidiate load into X */
4485                 NewVal = GetHexNum (L->Line + 7);
4486                 if (NewVal == X) {
4487                     /* Load has no effect */
4488                     Delete = 1;
4489                 } else if (NewVal == A) {
4490                     /* Requested value is already in A */
4491                     L = ReplaceLine (L, "\ttax");
4492                 } else if (X != -1 && NewVal == ((X + 1) & 0xFF)) {
4493                     /* Requested value is one more than current contents */
4494                     L = ReplaceLine (L, "\tinx");
4495                 } else if (X != -1 && NewVal == ((X - 1) & 0xFF)) {
4496                     /* Requested value is one less than current contents */
4497                     L = ReplaceLine (L, "\tdex");
4498                 }
4499                 /* Anyway, the new value is now in X */
4500                 X = NewVal;
4501             } else {
4502                 /* Memory load into X */
4503                 X = -1;
4504             }
4505         } else if (LineMatch (L, "\tldy\t")) {
4506             if (!RegYUsed (L) && !IsCondJump (NextInstruction (L))) {
4507                 /* The value loaded is not used later, remove it */
4508                 Delete = 1;
4509             } else if (LineMatch (L, "\tldy\t#$")) {
4510                 /* Immidiate load into Y */
4511                 NewVal = GetHexNum (L->Line + 7);
4512                 if (NewVal == Y) {
4513                     /* Load has no effect */
4514                     Delete = 1;
4515                 } else if (NewVal == A) {
4516                     /* Requested value is already in A */
4517                     L = ReplaceLine (L, "\ttay");
4518                 } else if (Y != -1 && NewVal == ((Y + 1) & 0xFF)) {
4519                     /* Requested value is one more than current contents */
4520                     L = ReplaceLine (L, "\tiny");
4521                 } else if (Y != -1 && NewVal == ((Y - 1) & 0xFF)) {
4522                     /* Requested value is one less than current contents */
4523                     L = ReplaceLine (L, "\tdey");
4524                 }
4525                 /* Anyway, the new value is now in Y */
4526                 Y = NewVal;
4527             } else {
4528                 /* Memory load into Y */
4529                 Y = -1;
4530             }
4531         } else if (LineFullMatch (L, "\tlsr\ta")) {
4532             if (A != -1) {
4533                 A >>= 1;
4534             }
4535         } else if (LineMatch (L, "\tora\t#$")) {
4536             if (A != -1) {
4537                 A |= GetHexNum (L->Line + 7);
4538             }
4539         } else if (LineMatch (L, "\tora\t")) {
4540             A = -1;
4541         } else if (LineFullMatch (L, "\tpla")) {
4542             A = -1;
4543         } else if (LineFullMatch (L, "\trol\ta")) {
4544             A = -1;
4545         } else if (LineFullMatch (L, "\tror\ta")) {
4546             A = -1;
4547         } else if (LineFullMatch (L, "\trts")) {
4548             A = X = Y = -1;
4549         } else if (LineFullMatch (L, "\trti")) {
4550             A = X = Y = -1;
4551         } else if (LineMatch (L, "\tsbc\t")) {
4552             if (CPU == CPU_65C02 && Y == 0 && L->Line[5] == '(' && IsYAddrMode(L)) {
4553                 L->Line[strlen(L->Line)-2] = '\0';
4554             }
4555             A = -1;
4556         } else if (CPU == CPU_65C02 && LineMatch (L, "\tst")) {
4557             /* Try to replace by stz if possible */
4558             if (A == 0 && LineMatch (L, "\tsta\t")) {
4559                 /* Not indirect and not Y allowed */
4560                 if (L->Line[5] != '(' && !IsYAddrMode (L)) {
4561                     L->Line[3] = 'z';
4562                 }
4563             } else if (X == 0 && LineMatch (L, "\tstx\t")) {
4564                 /* absolute,y not allowed */
4565                 if (!IsYAddrMode (L)) {
4566                     L->Line[3] = 'z';
4567                 }
4568             } else if (Y == 0 && LineMatch (L, "\tsty\t")) {
4569                 /* sty and stz share all addressing modes */
4570                 L->Line[3] = 'z';
4571             }
4572         } else if (LineFullMatch (L, "\ttax")) {
4573             if (A != -1 && X == A) {
4574                 /* Load has no effect */
4575                 Delete = 1;
4576             } else {
4577                 X = A;
4578             }
4579         } else if (LineFullMatch (L, "\ttay")) {
4580             if (A != -1 && Y == A) {
4581                 /* Load has no effect */
4582                 Delete = 1;
4583             } else {
4584                 Y = A;
4585             }
4586         } else if (LineFullMatch (L, "\ttsx")) {
4587             X = -1;
4588         } else if (LineFullMatch (L, "\ttxa")) {
4589             if (X != -1 && A == X) {
4590                 /* Load has no effect */
4591                 Delete = 1;
4592             } else {
4593                 A = X;
4594             }
4595         } else if (LineFullMatch (L, "\ttya")) {
4596             if (Y != -1 && A == Y) {
4597                 /* Load has no effect */
4598                 Delete = 1;
4599             } else {
4600                 A = Y;
4601             }
4602         }
4603
4604         /* Set to next line, handle deletions */
4605         L2 = NextCodeSegLine (L);
4606         if (Delete) {
4607             FreeLine (L);
4608         }
4609         L = L2;
4610
4611     }
4612     if (L) {
4613         /* Skip the label */
4614         L = NextCodeSegLine (L);
4615     }
4616     return L;
4617 }
4618
4619
4620
4621 static void OptBlocks (void)
4622 /* Optimize the register contents inside basic blocks */
4623 {
4624     Line* L = FirstCode;
4625     while (L) {
4626         L = OptOneBlock (L);
4627     }
4628 }
4629
4630
4631
4632 static void OptJumps (void)
4633 /* Optimize jumps */
4634 {
4635     static const char* Jumps [] = {
4636         "\tjeq\tL",
4637         "\tjne\tL",
4638         "\tjmi\tL",
4639         "\tjpl\tL",
4640         "\tjcs\tL",
4641         "\tjcc\tL",
4642         0
4643     };
4644
4645     Line* L = FirstCode;
4646     while (L) {
4647         int I = LineMatchX (L, Jumps);
4648         if (I >= 0) {
4649             Line* Target = GetTargetLine (L->Line+5);
4650             if (Target->Index > L->Index) {
4651                 /* This is a forward jump. Backward jumps are handled
4652                  * automagically by the assembler.
4653                  */
4654                 unsigned Distance = GetJumpDistance (L, Target);
4655                 if (Distance < 123) {           /* Safety */
4656                     L->Line [1] = 'b';          /* Make a short branch */
4657                     L->Size = 2;                /* Set new size */
4658                 }
4659             }
4660         }
4661         L = NextCodeLine (L);
4662     }
4663
4664     /* Special treatment for jumps on the 65C02 */
4665     if (CPU == CPU_65C02) {
4666
4667         Line* L = FirstCode;
4668         while (L) {
4669             if (LineMatch (L, "\tjmp\tL")) {
4670                 Line* Target = GetTargetLine (L->Line+5);
4671                 unsigned Distance = GetJumpDistance (L, Target);
4672                 if (Distance < 123) {           /* Safety */
4673                     L->Line [1] = 'b';          /* Make a short branch */
4674                     L->Line [2] = 'r';
4675                     L->Line [3] = 'a';
4676                     L->Size = 2;                /* Set new size */
4677                 }
4678             }
4679             L = NextCodeLine (L);
4680         }
4681
4682     }
4683 }
4684
4685
4686
4687 static void OptRTS (void)
4688 /* Change sequences of jsr XXX/rts to jmp XXX */
4689 {
4690     Line* L = FirstCode;
4691     while (L) {
4692         if (LineMatch (L, "\tjsr\t")) {
4693             /* This is a jsr, get the next instruction */
4694             Line* L2 = NextCodeLine (L);
4695             if (L2 && LineFullMatch (L2, "\trts")) {
4696                 /* We found a sequence */
4697                 FreeLine (L2);
4698                 L->Line [2] = 'm';
4699                 L->Line [3] = 'p';
4700             }
4701         }
4702         /* Try the next line */
4703         L = NextCodeLine (L);
4704     }
4705 }
4706
4707
4708
4709 /*****************************************************************************/
4710 /*                                   Code                                    */
4711 /*****************************************************************************/
4712
4713
4714
4715 void OptDoOpt (void)
4716 /* Run the optimizer over the collected stuff */
4717 {
4718     typedef void (*OptFunc)(void);
4719
4720     /* Table with optimizer steps -  are called in this order */
4721     static const OptFunc OptFuncs [] = {
4722         OptCompares1,           /* Optimize compares - first step */
4723         OptDeadJumps,           /* Remove dead jumps */
4724         OptLoads,               /* Remove unnecessary loads */
4725         OptRegLoads,            /* Remove unnecessary register loads */
4726         OptPtrOps,              /* Optimize stores through pointers */
4727         OptRegVars,             /* Optimize use of register variables */
4728         OptDoubleJumps,         /* Remove jump cascades - must be used before OptNeg */
4729         OptNeg,                 /* Remove unnecessary boolean negates */
4730         OptJumpRTS,             /* Replace jumps to an RTS by an RTS */
4731         OptBoolTransforms,      /* Optimize boolean transforms */
4732         OptCompares2,           /* Optimize compares */
4733         OptTests,               /* Remove unnecessary tests */
4734         OptBitOps,              /* Optimize bit operations */
4735         OptTriples,             /* Optimize several triples */
4736         OptBlocks,              /* Optimize basic blocks */
4737         OptRegLoads,            /* Remove unnecessary register loads (another pass) */
4738         OptBlocks,              /* Optimize basic blocks */
4739         OptJumps,               /* Optimize jumps */
4740         OptRTS,                 /* Optimize jsr/rts sequences */
4741     };
4742
4743     unsigned long Flags;
4744     unsigned      I;
4745
4746     /* Find and remember the first line of code */
4747     FindCodeStart ();
4748
4749     /* Mark all lines inside the code segment */
4750     MarkCodeLines ();
4751
4752     /* Create a list of all local labels for fast access */
4753     CreateLabelList ();
4754
4755     /* Ok, now start the real optimizations */
4756     Flags = 1UL;
4757     for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I, Flags <<= 1) {
4758         if ((OptDisable & Flags) == 0) {
4759             OptFuncs[I] ();
4760         } else if (Verbosity > 0 || Debug) {
4761             printf ("Optimizer pass %u skipped\n", I);
4762         }
4763     }
4764 }
4765
4766
4767