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