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