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