]> git.sur5r.net Git - cc65/blob - src/cc65/optimize.c
Use the xmalloc module from the common directory.
[cc65] / src / cc65 / optimize.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                optimize.c                                 */
4 /*                                                                           */
5 /*                   An optimizer for the cc65 C compiler                    */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 1998     Ullrich von Bassewitz                                        */
10 /*              Wacholderweg 14                                              */
11 /*              D-70597 Stuttgart                                            */
12 /* EMail:       uz@musoftware.de                                             */
13 /*                                                                           */
14 /*                                                                           */
15 /* This software is provided 'as-is', without any expressed or implied       */
16 /* warranty.  In no event will the authors be held liable for any damages    */
17 /* arising from the use of this software.                                    */
18 /*                                                                           */
19 /* Permission is granted to anyone to use this software for any purpose,     */
20 /* including commercial applications, and to alter it and redistribute it    */
21 /* freely, subject to the following restrictions:                            */
22 /*                                                                           */
23 /* 1. The origin of this software must not be misrepresented; you must not   */
24 /*    claim that you wrote the original software. If you use this software   */
25 /*    in a product, an acknowledgment in the product documentation would be  */
26 /*    appreciated but is not required.                                       */
27 /* 2. Altered source versions must be plainly marked as such, and must not   */
28 /*    be misrepresented as being the original software.                      */
29 /* 3. This notice may not be removed or altered from any source              */
30 /*    distribution.                                                          */
31 /*                                                                           */
32 /*****************************************************************************/
33
34
35
36 #include <stdarg.h>
37 #include <string.h>
38 #include <ctype.h>
39
40 #include "../common/xmalloc.h"
41
42 #include "asmlabel.h"
43 #include "asmline.h"
44 #include "check.h"
45 #include "cpu.h"
46 #include "error.h"
47 #include "global.h"
48 #include "io.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 by
1876      * used without A, check just for A.
1877      */
1878     NeedLoad = 1;
1879     if (!RegAUsed (L3[3])) {
1880         /* We don't need to load regsave */
1881         NeedLoad = 0;
1882     }
1883
1884     /* Special code for register variables */
1885     Done = 0;
1886     if (LineMatch (L, "\tlda\tregbank+")        &&
1887         GetNextCodeLines (L3[3], &L3[4], 1)     &&
1888         Inc == 1) {
1889
1890         /* Remember the offset into the register bank */
1891         char Reg[20];
1892         strcpy (Reg, L->Line+5);
1893
1894         /* Check for several special sequences */
1895         if (LineFullMatch (L3[4], "\tjsr\tldaui")) {
1896             /* Load char indirect */
1897             L = ReplaceLine  (L, "\tldx\t#$00");
1898             L = NewLineAfter (L, "\tlda\t(%s,x)", Reg);
1899             L = NewLineAfter (L, "\tinc\t%s", Reg);
1900             L = NewLineAfter (L, "\tbne\t*+4");
1901             L = NewLineAfter (L, "\tinc\t%s+1", Reg);
1902             Done = 1;
1903             ++LinesToRemove;
1904         } else if (LineFullMatch (L3[4], "\tsta\tptr1")         &&
1905                    GetNextCodeLines (L3[4], &L3[5], 3)          &&
1906                    LineFullMatch (L3[5], "\tstx\tptr1+1")       &&
1907                    LineFullMatch (L3[6], "\tldx\t#$00")         &&
1908                    LineFullMatch (L3[7], "\tlda\t(ptr1,x)")) {
1909
1910             /* Load char indirect, inlined */
1911             L = ReplaceLine  (L, "\tldx\t#$00");
1912             L = NewLineAfter (L, "\tlda\t(%s,x)", Reg);
1913             L = NewLineAfter (L, "\tinc\t%s", Reg);
1914             L = NewLineAfter (L, "\tbne\t*+4");
1915             L = NewLineAfter (L, "\tinc\t%s+1", Reg);
1916             Done = 1;
1917             LinesToRemove += 4;
1918
1919         } else if (LineFullMatch (L3[4], "\tjsr\tpushax")) {
1920             if (GetNextCodeLines (L3[4], &L3[5], 2)             &&
1921                 LineMatch        (L3[5], "\tlda\t")             &&
1922                 LineFullMatch    (L3[6], "\tjsr\tstaspp")) {
1923
1924                 /* Store to pointer */
1925                 L = ReplaceLine  (L, L3[5]->Line);
1926                 L = NewLineAfter (L, "\tldy\t#$00");
1927                 L = NewLineAfter (L, "\tsta\t(%s),y", Reg);
1928                 L = NewLineAfter (L, "\tinc\t%s", Reg);
1929                 L = NewLineAfter (L, "\tbne\t*+4");
1930                 L = NewLineAfter (L, "\tinc\t%s+1", Reg);
1931
1932                 Done = 1;
1933                 LinesToRemove += 3;
1934
1935             } else if (GetNextCodeLines (L3[4], &L3[5], 3)      &&
1936                        LineMatch     (L3[5], "\tldy\t#$")       &&
1937                        LineFullMatch (L3[6], "\tlda\t(sp),y")   &&
1938                        LineFullMatch (L3[7], "\tjsr\tstaspp")) {
1939
1940                 /* Beware: We have to correct the stack offset, since we will
1941                  * remove the pushax instruction!
1942                  */
1943                 Offs = GetHexNum (L3[5]->Line+7) - 2;
1944
1945                 /* Store to pointer */
1946                 L = ReplaceLine  (L, "\tldy\t#$%02X", Offs);
1947                 L = NewLineAfter (L, "\tldx\t#$00");
1948                 L = NewLineAfter (L, "\tlda\t(sp),y");
1949                 L = NewLineAfter (L, "\tsta\t(%s,x)", Reg);
1950                 L = NewLineAfter (L, "\tinc\t%s", Reg);
1951                 L = NewLineAfter (L, "\tbne\t*+4");
1952                 L = NewLineAfter (L, "\tinc\t%s+1", Reg);
1953
1954                 Done = 1;
1955                 LinesToRemove += 4;
1956             }
1957         }
1958     }
1959
1960     if (Done == 0) {
1961
1962         /* No register variable - insert the first part of the code */
1963         if (NeedLoad) {
1964             L = NewLineAfter (L, "\tsta\tptr1");
1965         }
1966         L = NewLineAfter (L, "\tclc");
1967         L = NewLineAfter (L, "\tadc\t#$%02X", Inc);
1968         L = NewLineAfter (L, "\tsta\t%s", L3[0]->Line+5);
1969         L = NewLineAfter (L, "\tlda\t%s", L3[1]->Line+5);
1970         if (NeedLoad) {
1971             L = NewLineAfter (L, "\tsta\tptr1+1");
1972         }
1973         L = NewLineAfter (L, "\tadc\t#$00");
1974         L = NewLineAfter (L, "\tsta\t%s", L3[1]->Line+5);
1975
1976         /* Check if we must really load the old value into a/x or if the
1977          * code may be replaced by something else.
1978          */
1979         if (GetNextCodeLines (L3[3], &L3[4], 1)) {
1980             if (LineFullMatch (L3[4], "\tjsr\tldaui")) {
1981                 /* Load char indirect */
1982                 L = NewLineAfter (L, "\tldx\t#$00");
1983                 L = NewLineAfter (L, "\tlda\t(ptr1,x)");
1984                 NeedLoad = 0;
1985                 ++LinesToRemove;
1986             } else if (LineFullMatch (L3[4], "\tsta\tptr1")             &&
1987                        GetNextCodeLines (L3[4], &L3[5], 3)              &&
1988                        LineFullMatch (L3[5], "\tstx\tptr1+1")           &&
1989                        LineFullMatch (L3[6], "\tldx\t#$00")             &&
1990                        LineFullMatch (L3[7], "\tlda\t(ptr1,x)")) {
1991
1992                 /* Load char indirect, inlined */
1993                 L = NewLineAfter (L, "\tldx\t#$00");
1994                 L = NewLineAfter (L, "\tlda\t(ptr1,x)");
1995                 NeedLoad = 0;
1996                 LinesToRemove += 4;
1997
1998             } else if (LineFullMatch (L3[4], "\tjsr\tldaxi")) {
1999                 /* Load word indirect */
2000                 L = NewLineAfter (L, "\tldy\t#$01");
2001                 L = NewLineAfter (L, "\tlda\t(ptr1),y");
2002                 L = NewLineAfter (L, "\ttax");
2003                 L = NewLineAfter (L, "\tdey");
2004                 L = NewLineAfter (L, "\tlda\t(ptr1),y");
2005                 NeedLoad = 0;
2006                 ++LinesToRemove;
2007
2008             } else if (LineFullMatch (L3[4], "\tjsr\tpushax")) {
2009                 if (GetNextCodeLines (L3[4], &L3[5], 2)                 &&
2010                     LineMatch            (L3[5], "\tlda\t")             &&
2011                     LineFullMatch        (L3[6], "\tjsr\tstaspp")) {
2012
2013                     /* Store to pointer */
2014                     L = NewLineAfter (L, L3[5]->Line);
2015                     L = NewLineAfter (L, "\tldy\t#$00");
2016                     L = NewLineAfter (L, "\tsta\t(ptr1),y");
2017
2018                     NeedLoad = 0;
2019                     LinesToRemove += 3;
2020                 } else if (GetNextCodeLines (L3[4], &L3[5], 3)          &&
2021                            LineMatch     (L3[5], "\tldy\t#$")           &&
2022                            LineFullMatch (L3[6], "\tlda\t(sp),y")       &&
2023                            LineFullMatch (L3[7], "\tjsr\tstaspp")) {
2024
2025                     /* Beware: We have to correct the stack offset, since we will
2026                      * remove the pushax instruction!
2027                      */
2028                     sprintf (L3[5]->Line+7, "%02X", GetHexNum (L3[5]->Line+7)-2);
2029
2030                     /* Store to pointer */
2031                     L = NewLineAfter (L, L3[5]->Line);
2032                     L = NewLineAfter (L, L3[6]->Line);
2033                     L = NewLineAfter (L, "\tldy\t#$00");
2034                     L = NewLineAfter (L, "\tsta\t(ptr1),y");
2035
2036                     NeedLoad = 0;
2037                     LinesToRemove += 4;
2038                 }
2039
2040             }
2041         }
2042
2043         /* If we need to load a/x, add the code */
2044         if (NeedLoad) {
2045             L = NewLineAfter (L, "\ttax");
2046             L = NewLineAfter (L, "\tlda\tptr1");
2047         }
2048     }
2049
2050     /* Remove the code that is no longer needed */
2051     FreeLines (L2[0], L2[LinesToRemove-1]);
2052
2053     /* Return the new line and success */
2054     *Start = NextCodeLine (L);
2055     return 1;
2056 }
2057
2058
2059
2060 static int OptPtrOps2 (Line** Start)
2061 /* Optimize several pointer and array constructs - subfunction 2 */
2062 {
2063     Line* L2[25];
2064     Line** L3;
2065     unsigned NeedLoad;
2066     unsigned LinesToRemove;
2067     unsigned Inc;
2068     unsigned Offs;
2069
2070
2071     /* Use a local variable for the working line */
2072     Line* L = *Start;
2073
2074     /* Same as subfunction 1 but for local variables. */
2075     if (LineMatch (L, "\tldy\t#$") == 0) {
2076         return 0;
2077     }
2078
2079     /* Get the stack offset. The offset points to the high byte, correct that. */
2080     Offs = GetHexNum (L->Line+7) - 1;
2081
2082     /* Check for the actual sequences */
2083     if (GetNextCodeLines (L, L2, 7)                             &&
2084         LineFullMatch (L2[0], "\tjsr\tldaxysp")                 &&
2085         LineFullMatch (L2[1], "\tsta\tregsave")                 &&
2086         LineFullMatch (L2[2], "\tstx\tregsave+1")               &&
2087         LineMatch     (L2[3], "\tjsr\tincax")) {
2088
2089         /* Non inlined version */
2090         Inc = GetHexNum (L2[3]->Line+10);
2091
2092         /* Check for stack offset zero */
2093         if (LineFullMatch (L2[4], "\tjsr\tstax0sp")             &&
2094             LineFullMatch (L2[5], "\tlda\tregsave")             &&
2095             LineFullMatch (L2[6], "\tldx\tregsave+1")) {
2096
2097             LinesToRemove = 7;
2098
2099         } else if (GetNextCodeLines (L2[6], &L2[7], 1)          &&
2100                    LineMatch     (L2[4], "\tldy\t#$")           &&
2101                    GetHexNum     (L2[4]->Line+7) == Offs        &&
2102                    LineFullMatch (L2[5], "\tjsr\tstaxysp")      &&
2103                    LineFullMatch (L2[6], "\tlda\tregsave")      &&
2104                    LineFullMatch (L2[7], "\tldx\tregsave+1")) {
2105
2106             LinesToRemove = 8;
2107
2108         } else {
2109             /* Not found */
2110             return 0;
2111         }
2112
2113     } else if (GetNextCodeLines (L, L2, 13)                     &&
2114                LineFullMatch (L2[0], "\tlda\t(sp),y")           &&
2115                LineFullMatch (L2[1], "\ttax")                   &&
2116                LineFullMatch (L2[2], "\tdey")                   &&
2117                LineFullMatch (L2[3], "\tlda\t(sp),y")           &&
2118                LineFullMatch (L2[4], "\tsta\tregsave")          &&
2119                LineFullMatch (L2[5], "\tstx\tregsave+1")        &&
2120                LineFullMatch (L2[6], "\tclc")                   &&
2121                LineMatch     (L2[7], "\tadc\t#$")               &&
2122                LineFullMatch (L2[8], "\tbcc\t*+3")              &&
2123                LineFullMatch (L2[9], "\tinx")) {
2124
2125         /* Inlined version */
2126         Inc = GetHexNum (L2[7]->Line+7);
2127
2128         /* Check for stack offset zero */
2129         if (LineFullMatch (L2[10], "\tjsr\tstax0sp")            &&
2130             LineFullMatch (L2[11], "\tlda\tregsave")            &&
2131             LineFullMatch (L2[12], "\tldx\tregsave+1")) {
2132
2133             LinesToRemove = 13;
2134
2135         } else if (GetNextCodeLines (L2[12], &L2[13], 1)        &&
2136                    LineMatch     (L2[10], "\tldy\t#$")          &&
2137                    GetHexNum     (L2[10]->Line+7) == Offs       &&
2138                    LineFullMatch (L2[11], "\tjsr\tstaxysp")     &&
2139                    LineFullMatch (L2[12], "\tlda\tregsave")     &&
2140                    LineFullMatch (L2[13], "\tldx\tregsave+1")) {
2141
2142             LinesToRemove = 14;
2143
2144         } else {
2145             /* Not found */
2146             return 0;
2147         }
2148     } else {
2149         /* Not found */
2150         return 0;
2151     }
2152
2153     /* Get a pointer to the last line of the preceding sequence */
2154     L3 = &L2[LinesToRemove-1];
2155
2156     /* Check if AX is actually used following the code above. If not,
2157      * we don't need to load A/X from regsave. Since X will never by
2158      * used without A, check just for A.
2159      */
2160     NeedLoad = 1;
2161     if (!RegAUsed (L3[0])) {
2162         /* We don't need to load regsave */
2163         NeedLoad = 0;
2164     }
2165
2166     /* Replace the ldy instruction, offset must point to the low byte */
2167     sprintf (L->Line+7, "%02X", Offs);
2168
2169     /* Insert the first part of the code */
2170     L = NewLineAfter (L, "\tlda\t(sp),y");
2171     if (NeedLoad) {
2172         L = NewLineAfter (L, "\tsta\tptr1");
2173     }
2174     L = NewLineAfter (L, "\tclc");
2175     L = NewLineAfter (L, "\tadc\t#$%02X", Inc);
2176     L = NewLineAfter (L, "\tsta\t(sp),y");
2177     L = NewLineAfter (L, "\tiny");
2178     L = NewLineAfter (L, "\tlda\t(sp),y");
2179     if (NeedLoad) {
2180         L = NewLineAfter (L, "\tsta\tptr1+1");
2181     }
2182     L = NewLineAfter (L, "\tadc\t#$00");
2183     L = NewLineAfter (L, "\tsta\t(sp),y");
2184
2185     /* Check if we must really load the old value into a/x or if the
2186      * code may be replaced by something else.
2187      */
2188     if (GetNextCodeLines (L3[0], &L3[1], 1)) {
2189         if (LineFullMatch (L3[1], "\tjsr\tldaui")) {
2190             /* Load char indirect */
2191             L = NewLineAfter (L, "\tldx\t#$00");
2192             L = NewLineAfter (L, "\tlda\t(ptr1,x)");
2193             NeedLoad = 0;
2194             ++LinesToRemove;
2195         } else if (LineFullMatch (L3[1], "\tsta\tptr1")         &&
2196                    GetNextCodeLines (L3[1], &L3[2], 3)          &&
2197                    LineFullMatch (L3[2], "\tstx\tptr1+1")       &&
2198                    LineFullMatch (L3[3], "\tldx\t#$00")         &&
2199                    LineFullMatch (L3[4], "\tlda\t(ptr1,x)")) {
2200
2201             /* Load char indirect, inlined */
2202             L = NewLineAfter (L, "\tldx\t#$00");
2203             L = NewLineAfter (L, "\tlda\t(ptr1,x)");
2204             NeedLoad = 0;
2205             LinesToRemove += 4;
2206
2207         } else if (LineFullMatch (L3[1], "\tjsr\tldaxi")) {
2208             /* Load word indirect */
2209             L = NewLineAfter (L, "\tldy\t#$01");
2210             L = NewLineAfter (L, "\tlda\t(ptr1),y");
2211             L = NewLineAfter (L, "\ttax");
2212             L = NewLineAfter (L, "\tdey");
2213             L = NewLineAfter (L, "\tlda\t(ptr1),y");
2214             NeedLoad = 0;
2215             ++LinesToRemove;
2216
2217         } else if (LineFullMatch (L3[1], "\tjsr\tpushax")) {
2218             if (GetNextCodeLines (L3[1], &L3[2], 2)             &&
2219                 LineMatch        (L3[2], "\tlda\t")             &&
2220                 LineFullMatch    (L3[3], "\tjsr\tstaspp")) {
2221
2222                 /* Store to pointer */
2223                 L = NewLineAfter (L, L3[2]->Line);
2224                 L = NewLineAfter (L, "\tldy\t#$00");
2225                 L = NewLineAfter (L, "\tsta\t(ptr1),y");
2226
2227                 NeedLoad = 0;
2228                 LinesToRemove += 3;
2229             } else if (GetNextCodeLines (L3[1], &L3[2], 3)      &&
2230                        LineMatch     (L3[2], "\tldy\t#$")       &&
2231                        LineFullMatch (L3[3], "\tlda\t(sp),y")   &&
2232                        LineFullMatch (L3[4], "\tjsr\tstaspp")) {
2233
2234                 /* Beware: We have to correct the stack offset, since we will
2235                  * remove the pushax instruction!
2236                  */
2237                 sprintf (L3[2]->Line+7, "%02X", GetHexNum (L3[2]->Line+7)-2);
2238
2239                 /* Store to pointer */
2240                 L = NewLineAfter (L, L3[2]->Line);
2241                 L = NewLineAfter (L, L3[3]->Line);
2242                 L = NewLineAfter (L, "\tldy\t#$00");
2243                 L = NewLineAfter (L, "\tsta\t(ptr1),y");
2244
2245                 NeedLoad = 0;
2246                 LinesToRemove += 4;
2247             }
2248         }
2249
2250     }
2251
2252     /* If we need to load a/x, add the code */
2253     if (NeedLoad) {
2254         L = NewLineAfter (L, "\ttax");
2255         L = NewLineAfter (L, "\tlda\tptr1");
2256     }
2257
2258     /* Remove the code that is no longer needed */
2259     FreeLines (L2[0], L2[LinesToRemove-1]);
2260
2261     /* Return the new line and success */
2262     *Start = NextCodeLine (L);
2263     return 1;
2264 }
2265
2266
2267
2268 static void OptPtrOps (void)
2269 /* Optimize several pointer and array constructs */
2270 {
2271     Line* L2 [10];
2272
2273     Line* L = FirstCode;
2274     while (L) {
2275
2276         if (OptPtrOps1 (&L)) {
2277             continue;
2278         } else if (OptPtrOps2 (&L)) {
2279             continue;
2280         }
2281
2282         /* Search for the following sequence:
2283          *
2284          *      lda     regsave
2285          *      ldx     regsave+1
2286          *      jsr     pushax
2287          *      lda     #$..
2288          *      jsr     staspp
2289          *
2290          * and replace it by:
2291          *
2292          *      lda     #$..
2293          *      ldy     #$00
2294          *      sta     (regsave),y
2295          *
2296          */
2297         else if (LineFullMatch (L, "\tlda\tregsave")            && /* Match on start */
2298                  GetNextCodeLines (L, L2, 4)                    && /* Fetch next lines */
2299                  LineFullMatch (L2 [0], "\tldx\tregsave+1")     && /* Match line 2 ... */
2300                  LineFullMatch (L2 [1], "\tjsr\tpushax")        &&
2301                  LineMatch (L2 [2], "\tlda\t#$")                &&
2302                  LineFullMatch (L2 [3], "\tjsr\tstaspp")) {
2303
2304             /* Found the sequence, replace it */
2305             L      = ReplaceLine (L, L2 [2]->Line);             /* lda #$.. */
2306             L2 [0] = ReplaceLine (L2 [0], "\tldy\t#$00");
2307             L2 [1] = ReplaceLine (L2 [1], "\tsta\t(regsave),y");
2308
2309             /* Free the remaining lines */
2310             FreeLines (L2 [2], L2 [3]);
2311         }
2312
2313         /* Search for the following sequence:
2314          *
2315          *      lda     regsave
2316          *      ldx     regsave+1
2317          *      jsr     ldaui
2318          *
2319          * and replace it by:
2320          *
2321          *      ldx     #$00
2322          *      lda     (regsave,x)
2323          *
2324          */
2325         else if (LineFullMatch (L, "\tlda\tregsave")        && /* Match on start */
2326                  GetNextCodeLines (L, L2, 2)                && /* Fetch next lines */
2327                  LineFullMatch (L2 [0], "\tldx\tregsave+1") && /* Match line 2 ... */
2328                  LineFullMatch (L2 [1], "\tjsr\tldaui")) {
2329
2330             /* Found the sequence, replace it */
2331             L      = ReplaceLine (L, "\tldx\t#$00");
2332             L2 [0] = ReplaceLine (L2 [0], "\tlda\t(regsave,x)");
2333
2334             /* Free the remaining lines */
2335             FreeLine (L2 [1]);
2336         }
2337
2338         /*
2339          * Search for the following sequence:
2340          *
2341          *      lda     regsave
2342          *      ldx     regsave+1
2343          *      jsr     pushax
2344          *      ldx     #$high
2345          *      lda     #$low
2346          *      jsr     staxspp
2347          *
2348          * and replace it by:
2349          *
2350          *      ldy     #$01
2351          *      lda     #$high
2352          *      sta     (regsave),y
2353          *      tax
2354          *      dey
2355          *      lda     #$low
2356          *      sta     (regsave),y
2357          *
2358          */
2359         else if (LineFullMatch (L, "\tlda\tregsave")        &&
2360                  GetNextCodeLines (L, L2, 5)                &&
2361                  LineFullMatch (L2 [0], "\tldx\tregsave+1") &&
2362                  LineFullMatch (L2 [1], "\tjsr\tpushax")    &&
2363                  LineMatch (L2 [2], "\tldx\t#$")            &&
2364                  LineMatch (L2 [3], "\tlda\t#$")            &&
2365                  LineFullMatch (L2 [4], "\tjsr\tstaxspp")) {
2366
2367             /* Found the sequence, replace it */
2368             L      = ReplaceLine (L, "\tldy\t#$01");
2369             L2 [0] = ReplaceLine (L2 [0], L2 [2]->Line);
2370             L2 [0]->Line [3] = 'a';
2371             L2 [1] = ReplaceLine (L2 [1], "\tsta\t(regsave),y");
2372             L2 [4] = ReplaceLine (L2 [4], L2 [3]->Line);
2373             L2 [2] = ReplaceLine (L2 [2], "\ttax");
2374             L2 [3] = ReplaceLine (L2 [3], "\tdey");
2375             L      = NewLineAfter (L2 [4], "\tsta\t(regsave),y");
2376         }
2377
2378         /*
2379          * Search for the following sequence:
2380          *
2381          *      lda     regsave
2382          *      ldx     regsave+1
2383          *      sta     ptr1
2384          *      stx     ptr1+1
2385          *      ldx     #$00
2386          *      lda     (ptr1,x)
2387          *
2388          * and replace it by:
2389          *
2390          *      ldx     #$00
2391          *      lda     (regsave,x)
2392          *
2393          */
2394         else if (LineFullMatch (L, "\tlda\tregsave")        &&
2395                  GetNextCodeLines (L, L2, 5)                &&
2396                  LineFullMatch (L2 [0], "\tldx\tregsave+1") &&
2397                  LineFullMatch (L2 [1], "\tsta\tptr1")      &&
2398                  LineFullMatch (L2 [2], "\tstx\tptr1+1")    &&
2399                  LineFullMatch (L2 [3], "\tldx\t#$00")      &&
2400                  LineFullMatch (L2 [4], "\tlda\t(ptr1,x)")) {
2401
2402             /* Found the sequence, replace it */
2403             L      = ReplaceLine (L, "\tldx\t#$00");
2404             L2 [0] = ReplaceLine (L2 [0], "\tlda\t(regsave,x)");
2405
2406             /* Remove the remaining lines */
2407             FreeLines (L2 [1], L2 [4]);
2408         }
2409
2410         /* Search for the following sequence:
2411          *
2412          *      jsr     pushax
2413          *      lda     ...
2414          *      jsr     staspp
2415          *
2416          * and replace it by:
2417          *
2418          *      sta     ptr1
2419          *      stx     ptr1+1
2420          *      lda     ...
2421          *      ldy     #$00
2422          *      sta     (ptr1),y
2423          *
2424          */
2425         else if (LineFullMatch (L, "\tjsr\tpushax")         &&
2426                  GetNextCodeLines (L, L2, 2)                &&
2427                  LineMatch (L2 [0], "\tlda\t")              &&
2428                  LineFullMatch (L2 [1], "\tjsr\tstaspp")) {
2429
2430             /* Found the sequence, replace it */
2431             L      = ReplaceLine (L, "\tsta\tptr1");
2432             L2 [1] = ReplaceLine (L2 [1], L2 [0]->Line);   /* lda ... */
2433             L2 [0] = ReplaceLine (L2 [0], "\tstx\tptr1+1");
2434             L2 [2] = NewLineAfter (L2 [1], "\tldy\t#$00");
2435             L      = NewLineAfter (L2 [2], "\tsta\t(ptr1),y");
2436         }
2437
2438         /* Search for the following sequence:
2439          *
2440          *      jsr     pushax
2441          *      lda     ...
2442          *      ldy     #$nn
2443          *      jsr     staspidx
2444          *
2445          * and replace it by:
2446          *
2447          *      sta     ptr1
2448          *      stx     ptr1+1
2449          *      lda     ...
2450          *      ldy     #$nn
2451          *      sta     (ptr1),y
2452          *
2453          */
2454         else if (LineFullMatch (L, "\tjsr\tpushax")         &&
2455                  GetNextCodeLines (L, L2, 3)                &&
2456                  LineMatch (L2 [0], "\tlda\t")              &&
2457                  LineMatch (L2 [1], "\tldy\t#$")            &&
2458                  LineFullMatch (L2 [2], "\tjsr\tstaspidx")) {
2459
2460             /* Found the sequence, replace it */
2461             L      = ReplaceLine (L, "\tsta\tptr1");
2462             L      = NewLineAfter (L, "\tstx\tptr1+1");
2463             L2 [2] = ReplaceLine (L2 [2], "\tsta\t(ptr1),y");
2464         }
2465
2466         /* Search for the following sequence:
2467          *
2468          *      jsr     pushax
2469          *      ldy     #$..
2470          *      lda     (sp),y
2471          *      jsr     staspp
2472          *
2473          * and replace it by:
2474          *
2475          *      sta     ptr1
2476          *      stx     ptr1+1
2477          *      ldy     #$..
2478          *      lda     (sp),y
2479          *      ldy     #$00
2480          *      sta     (ptr1),y
2481          *
2482          * Beware: Since we remove a call to a function that changes the stack
2483          * pointer, we have to adjust the stack address for the lda.
2484          *
2485          */
2486         else if (LineFullMatch (L, "\tjsr\tpushax")         &&
2487                  GetNextCodeLines (L, L2, 3)                &&
2488                  LineMatch (L2 [0], "\tldy\t#$")            &&
2489                  LineFullMatch (L2 [1], "\tlda\t(sp),y")    &&
2490                  LineFullMatch (L2 [2], "\tjsr\tstaspp")) {
2491
2492             /* Found the sequence, replace it. First create a new load
2493              * instruction for the changed stack offset.
2494              */
2495             char Buf [30];
2496             sprintf (Buf, "\tldy\t#$%02X", GetHexNum (L2 [0]->Line+7) - 2);
2497             L      = ReplaceLine (L, "\tsta\tptr1");
2498             L2 [1] = ReplaceLine (L2 [1], Buf);   /* ldy ... */
2499             L2 [0] = ReplaceLine (L2 [0], "\tstx\tptr1+1");
2500             L2 [2] = ReplaceLine (L2 [2], "\tlda\t(sp),y");
2501             L2 [3] = NewLineAfter (L2 [2], "\tldy\t#$00");
2502             L      = NewLineAfter (L2 [3], "\tsta\t(ptr1),y");
2503         }
2504
2505         /* Search for the following sequence:
2506          *
2507          *      jsr     pushax
2508          *      ldy     #$nn
2509          *      lda     (sp),y
2510          *      ldy     #$mm
2511          *      jsr     staspidx
2512          *
2513          * and replace it by:
2514          *
2515          *      sta     ptr1
2516          *      stx     ptr1+1
2517          *      ldy     #$nn
2518          *      lda     (sp),y
2519          *      ldy     #$mm
2520          *      sta     (ptr1),y
2521          *
2522          * Beware: Since we remove a call to a function that changes the stack
2523          * pointer, we have to adjust the stack address for the lda.
2524          *
2525          */
2526         else if (LineFullMatch (L, "\tjsr\tpushax")         &&
2527                  GetNextCodeLines (L, L2, 4)                &&
2528                  LineMatch (L2 [0], "\tldy\t#$")            &&
2529                  LineFullMatch (L2 [1], "\tlda\t(sp),y")    &&
2530                  LineMatch (L2 [2], "\tldy\t#$")            &&
2531                  LineFullMatch (L2 [3], "\tjsr\tstaspidx")) {
2532
2533             /* Found the sequence, replace it. First create a new load
2534              * instruction for the changed stack offset.
2535              */
2536             char Buf [30];
2537             sprintf (Buf, "\tldy\t#$%02X", GetHexNum (L2 [0]->Line+7) - 2);
2538             L      = ReplaceLine (L, "\tsta\tptr1");
2539             L      = NewLineAfter (L, "\tstx\tptr1+1");
2540             L2 [0] = ReplaceLine (L2 [0], Buf);   /* ldy ... */
2541             L2 [3] = ReplaceLine (L2 [3], "\tsta\t(ptr1),y");
2542         }
2543
2544         /* Search for the following sequence:
2545          *
2546          *      ldax    _label+0
2547          *      ldy     #$..
2548          *      clc
2549          *      adc     (sp),y
2550          *      bcc     *+3
2551          *      inx
2552          *      sta     ptr1
2553          *      stx     ptr1+1
2554          *      ldx     #$00
2555          *      lda     (ptr1,x)
2556          *
2557          * and replace it by:
2558          *
2559          *      ldy     #$..
2560          *      lda     (sp),y
2561          *      tay
2562          *      ldx     #$00
2563          *      lda     _label+0,y
2564          *
2565          * The load of X may be omitted if X is not used below.
2566          */
2567         else if (LineMatch (L, "\tldax\t_")                 &&
2568                  GetNextCodeLines (L, L2, 9)                &&
2569                  LineMatch (L2 [0], "\tldy\t#$")            &&
2570                  LineFullMatch (L2 [1], "\tclc")            &&
2571                  LineFullMatch (L2 [2], "\tadc\t(sp),y")    &&
2572                  LineFullMatch (L2 [3], "\tbcc\t*+3")       &&
2573                  LineFullMatch (L2 [4], "\tinx")            &&
2574                  LineFullMatch (L2 [5], "\tsta\tptr1")      &&
2575                  LineFullMatch (L2 [6], "\tstx\tptr1+1")    &&
2576                  LineFullMatch (L2 [7], "\tldx\t#$00")      &&
2577                  LineFullMatch (L2 [8], "\tlda\t(ptr1,x)")) {
2578
2579             /* Found the sequence, replace it */
2580             char Label [256];
2581             strcpy (Label, L->Line + 6);                /* Remember the label */
2582             L = ReplaceLine  (L, L2 [0]->Line);         /* ldy .. */
2583             L = NewLineAfter (L, "\tlda\t(sp),y");
2584             L = NewLineAfter (L, "\ttay");
2585             if (RegXUsed (L2[8])) {
2586                 L = NewLineAfter (L, "\tldx\t#$00");
2587             }
2588             L = NewLineAfter (L, "\tlda\t%s,y", Label);
2589
2590             /* Remove the remaining stuff. There may be hints between the
2591              * instructions, remove them too
2592              */
2593             FreeLines (L2[0], L2 [8]);
2594
2595         }
2596
2597         /* Check for
2598          *
2599          *      ldy     #$xx
2600          *      lda     (sp),y
2601          *      tax
2602          *      dey
2603          *      lda     (sp),y
2604          *      ldy     #$yy
2605          *      jsr     ldauidx
2606          *
2607          * and replace it by
2608          *
2609          *      ldy     #$xx
2610          *      ldx     #$yy
2611          *      jsr     ldauiysp
2612          *
2613          * or even
2614          *
2615          *      jsr     ldaui0sp
2616          *
2617          * This change will cost 2 cycles, but it saves a lot of code (6 bytes
2618          * per occurrence), so we will accept the overhead. It may even be
2619          * possible to rewrite the library routine to get rid of the additional
2620          * overhead.
2621          */
2622         else if (LineMatch (L, "\tldy\t#$")                     &&
2623                  GetNextCodeLines (L, L2, 6)                    &&
2624                  LineFullMatch (L2 [0], "\tlda\t(sp),y")        &&
2625                  LineFullMatch (L2 [1], "\ttax")                &&
2626                  LineFullMatch (L2 [2], "\tdey")                &&
2627                  LineFullMatch (L2 [3], "\tlda\t(sp),y")        &&
2628                  LineMatch     (L2 [4], "\tldy\t#$")            &&
2629                  LineFullMatch (L2 [5], "\tjsr\tldauidx")) {
2630
2631             /* Found - replace it */
2632             L2 [4]->Line [3] = 'x';             /* Change to ldx */
2633             if (LineFullMatch (L, "\tldy\t#$01")) {
2634                 /* Word at offset zero */
2635                 FreeLine (L);
2636                 L = ReplaceLine (L2 [5], "\tjsr\tldaui0sp");
2637             } else {
2638                 ReplaceLine (L2 [5], "\tjsr\tldauiysp");
2639             }
2640
2641             /* Delete the remaining lines */
2642             FreeLines (L2 [0], L2 [3]);
2643         }
2644
2645         /* Check for
2646          *
2647          *      ldy     #$xx
2648          *      lda     (sp),y
2649          *      tax
2650          *      dey
2651          *      lda     (sp),y
2652          *      sta     ptr1
2653          *      stx     ptr1+1
2654          *      ldx     #$00
2655          *      lda     (ptr1,x)
2656          *
2657          * and replace it by
2658          *
2659          *      ldy     #$xx
2660          *      jsr     ldau0ysp
2661          *
2662          * or even
2663          *
2664          *      jsr     ldau00sp
2665          *
2666          * This change will has an overhead of 10 cycles, but it saves 11(!)
2667          * bytes per invocation. Maybe we should apply only if FavourSize is
2668          * true?
2669          */
2670         else if (LineMatch (L, "\tldy\t#$")                     &&
2671                  GetNextCodeLines (L, L2, 8)                    &&
2672                  LineFullMatch (L2 [0], "\tlda\t(sp),y")        &&
2673                  LineFullMatch (L2 [1], "\ttax")                &&
2674                  LineFullMatch (L2 [2], "\tdey")                &&
2675                  LineFullMatch (L2 [3], "\tlda\t(sp),y")        &&
2676                  LineFullMatch (L2 [4], "\tsta\tptr1")          &&
2677                  LineFullMatch (L2 [5], "\tstx\tptr1+1")        &&
2678                  LineFullMatch (L2 [6], "\tldx\t#$00")          &&
2679                  LineFullMatch (L2 [7], "\tlda\t(ptr1,x)")) {
2680
2681             /* Found - replace it */
2682             if (LineFullMatch (L, "\tldy\t#$01")) {
2683                 /* Word at offset zero */
2684                 FreeLine (L);
2685                 L = ReplaceLine (L2 [0], "\tjsr\tldau00sp");
2686             } else {
2687                 ReplaceLine (L2 [0], "\tjsr\tldau0ysp");
2688             }
2689
2690             /* Delete the remaining lines */
2691             FreeLines (L2 [1], L2 [7]);
2692         }
2693
2694         /* Next Line */
2695         L = NextCodeLine (L);
2696     }
2697 }
2698
2699
2700
2701 static void OptRegVars (void)
2702 /* Optimize register variable uses */
2703 {
2704     Line* L2 [10];
2705
2706     Line* L = FirstCode;
2707     while (L) {
2708
2709         /* Search for the following sequence:
2710          *
2711          *      lda     regbank+n
2712          *      ldx     regbank+n+1
2713          *      jsr     ldaui
2714          *
2715          * and replace it by:
2716          *
2717          *      ldx     #$00
2718          *      lda     (regbank+n,x)
2719          *
2720          */
2721         if (LineMatch (L, "\tlda\tregbank+")            && /* Match on start */
2722             GetNextCodeLines (L, L2, 2)                 && /* Fetch next lines */
2723             LineMatch (L2 [0], "\tldx\tregbank+")       && /* Match line 2 ... */
2724             LineFullMatch (L2 [1], "\tjsr\tldaui")      &&
2725             L->Line [13] == L2 [0]->Line [13]           && /* Offset equal */
2726             strcmp (L2 [0]->Line + 14, "+1") == 0) {
2727
2728             char Buf [100];
2729             sprintf (Buf, "\tlda\t(%s,x)", L->Line + 5);
2730
2731             /* Found the sequence, replace it */
2732             L      = ReplaceLine (L, "\tldx\t#$00");
2733             L2 [0] = ReplaceLine (L2 [0], Buf);
2734
2735             /* Free the remaining lines */
2736             FreeLine (L2 [1]);
2737         }
2738
2739         /* Search for the following sequence:
2740          *
2741          *      lda     regbank+n
2742          *      ldx     regbank+n+1
2743          *      sta     ptr1
2744          *      stx     ptr1+1
2745          *      ldx     #$00
2746          *      lda     (ptr1,x)
2747          *
2748          * and replace it by:
2749          *
2750          *      ldx     #$00
2751          *      lda     (regbank+n,x)
2752          *
2753          */
2754         else if (LineMatch (L, "\tlda\tregbank+")        && /* Match on start */
2755                  GetNextCodeLines (L, L2, 5)             && /* Fetch next lines */
2756                  LineMatch (L2 [0], "\tldx\tregbank+")   && /* Match line 2 ... */
2757                  L->Line [13] == L2 [0]->Line [13]       && /* Offset equal */
2758                  strcmp (L2 [0]->Line + 14, "+1") == 0   &&
2759                  LineFullMatch (L2 [1], "\tsta\tptr1")   &&
2760                  LineFullMatch (L2 [2], "\tstx\tptr1+1") &&
2761                  LineFullMatch (L2 [3], "\tldx\t#$00")   &&
2762                  LineFullMatch (L2 [4], "\tlda\t(ptr1,x)")) {
2763
2764             char Buf [100];
2765             sprintf (Buf, "\tlda\t(%s,x)", L->Line + 5);
2766
2767             /* Found the sequence, replace it */
2768             L      = ReplaceLine (L, "\tldx\t#$00");
2769             L2 [0] = ReplaceLine (L2 [0], Buf);
2770
2771             /* Free the remaining lines */
2772             FreeLines (L2 [1], L2 [4]);
2773         }
2774
2775         /* Search for the following sequence:
2776          *
2777          *      lda     regbank+n
2778          *      ldx     regbank+n+1
2779          *      ldy     #$..
2780          *      jsr     ldauidx
2781          *
2782          * and replace it by:
2783          *
2784          *      ldy     #$..
2785          *      ldx     #$00
2786          *      lda     (regbank+n),y
2787          *
2788          */
2789         else if (LineMatch (L, "\tlda\tregbank+")        && /* Match on start */
2790                  GetNextCodeLines (L, L2, 3)             && /* Fetch next lines */
2791                  LineMatch (L2 [0], "\tldx\tregbank+")   && /* Match line 2 ... */
2792                  L->Line [13] == L2 [0]->Line [13]       && /* Offset equal */
2793                  strcmp (L2 [0]->Line + 14, "+1") == 0   &&
2794                  LineMatch (L2 [1], "\tldy\t#$")         &&
2795                  LineFullMatch (L2 [2], "\tjsr\tldauidx")) {
2796
2797             char Buf [100];
2798             sprintf (Buf, "\tlda\t(%s),y", L->Line + 5);
2799
2800             /* Found the sequence, replace it */
2801             L      = ReplaceLine (L, L2 [1]->Line);
2802             L2 [0] = ReplaceLine (L2 [0], "\tldx\t#$00");
2803             L2 [1] = ReplaceLine (L2 [1], Buf);
2804
2805             /* Free the remaining lines */
2806             FreeLine (L2 [2]);
2807         }
2808
2809         /* Search for the following sequence:
2810          *
2811          *      lda     regbank+n
2812          *      ldx     regbank+n+1
2813          *      sta     ptr1
2814          *      stx     ptr1+1
2815          *      lda     ...
2816          *      ldy     #$mm
2817          *      sta     (ptr1),y
2818          *
2819          * and replace it by:
2820          *
2821          *      lda     ...
2822          *      ldy     #$mm
2823          *      sta     (regbank+n),y
2824          *
2825          * The source form is not generated by the parser but by the optimizer.
2826          */
2827         else if (LineMatch (L, "\tlda\tregbank+")        && /* Match on start */
2828                  GetNextCodeLines (L, L2, 6)             && /* Fetch next lines */
2829                  LineMatch (L2 [0], "\tldx\tregbank+")   && /* Match line 2 ... */
2830                  L->Line [13] == L2 [0]->Line [13]       && /* Offset equal */
2831                  strcmp (L2 [0]->Line + 14, "+1") == 0   &&
2832                  LineFullMatch (L2 [1], "\tsta\tptr1")   &&
2833                  LineFullMatch (L2 [2], "\tstx\tptr1+1") &&
2834                  LineMatch (L2 [3], "\tlda\t")           &&
2835                  LineMatch (L2 [4], "\tldy\t#$")         &&
2836                  LineMatch (L2 [5], "\tsta\t(ptr1),y")) {
2837
2838             char Buf [100];
2839             sprintf (Buf, "\tsta\t(%s),y", L->Line + 5);
2840
2841             /* Found the sequence, replace it */
2842             L2 [5] = ReplaceLine (L2 [5], Buf);
2843
2844             /* Free the remaining lines */
2845             FreeLines (L, L2 [2]);
2846
2847             /* Make the line pointer valid again */
2848             L = L2 [5];
2849         }
2850
2851         /* Search for the following sequence:
2852          *
2853          *      lda     regbank+n
2854          *      ldx     regbank+n+1
2855          *      sta     ptr1
2856          *      stx     ptr1+1
2857          *      ldy     #$mm
2858          *      lda     (sp),y
2859          *      ldy     #$ll
2860          *      sta     (ptr1),y
2861          *
2862          * and replace it by:
2863          *
2864          *      ldy     #$mm
2865          *      lda     (sp),y
2866          *      ldy     #$ll
2867          *      sta     (regbank+n),y
2868          *
2869          * The source form is not generated by the parser but by the optimizer.
2870          */
2871         else if (LineMatch (L, "\tlda\tregbank+")        && /* Match on start */
2872                  GetNextCodeLines (L, L2, 7)             && /* Fetch next lines */
2873                  LineMatch (L2 [0], "\tldx\tregbank+")   && /* Match line 2 ... */
2874                  L->Line [13] == L2 [0]->Line [13]       && /* Offset equal */
2875                  strcmp (L2 [0]->Line + 14, "+1") == 0   &&
2876                  LineFullMatch (L2 [1], "\tsta\tptr1")   &&
2877                  LineFullMatch (L2 [2], "\tstx\tptr1+1") &&
2878                  LineMatch (L2 [3], "\tldy\t#$")         &&
2879                  LineFullMatch (L2 [4], "\tlda\t(sp),y") &&
2880                  LineMatch (L2 [5], "\tldy\t#$")         &&
2881                  LineMatch (L2 [6], "\tsta\t(ptr1),y")) {
2882
2883             char Buf [100];
2884             sprintf (Buf, "\tsta\t(%s),y", L->Line + 5);
2885
2886             /* Found the sequence, replace it */
2887             L2 [6] = ReplaceLine (L2 [6], Buf);
2888
2889             /* Free the remaining lines */
2890             FreeLines (L, L2 [2]);
2891
2892             /* Make the line pointer valid again */
2893             L = L2 [6];
2894         }
2895
2896         /* Next Line */
2897         L = NextCodeLine (L);
2898     }
2899 }
2900
2901
2902
2903 static void OptDoubleJumps (void)
2904 /* Remove/rearrange jumps that jump to other jumps */
2905 {
2906     static const char* Jumps [] = {
2907         "\tjeq\tL",
2908         "\tjne\tL",
2909         "\tbeq\tL",
2910         "\tbne\tL",
2911         "\tjmp\tL",
2912         0
2913     };
2914
2915     unsigned D;
2916
2917     Line* L = FirstCode;
2918     while (L) {
2919
2920         int I;
2921
2922         /* Is this a jump? */
2923         while ((I = LineMatchX (L, Jumps)) >= 0) {
2924
2925             /* Yes. Get the target label */
2926             Line* Target = GetTargetLine (L->Line + 5);
2927
2928             /* Target points to the label itself. Skip lines until we reach
2929              * one that is not a label.
2930              */
2931             Target = NextInstruction (Target);
2932
2933             /* Be sure, this line is not the same as the one the jump is
2934              * in (this happens if there is an empty loop).
2935              */
2936             if (Target == L) {
2937                 break;
2938             }
2939             D = 0;
2940             if (LineMatch (Target, "\tjmp\t")) {
2941
2942                 /* The target is itself a jump. If this is a short branch, get
2943                  * the final target and check if it is in reach. Bail out if
2944                  * not.
2945                  */
2946                 if (L->Line[1] == 'b') {
2947                     Line* FinalTarget = GetTargetLine (Target->Line+5);
2948                     FinalTarget = NextInstruction (FinalTarget);
2949                     if ((D = GetJumpDistance (L, FinalTarget)) >= 123) {
2950                         break;
2951                     }
2952                 }
2953
2954                 /* Make sure the jump does indeed point to another label.
2955                  * It may happen that this is not the case for some endless
2956                  * loop (while(1) and similar).
2957                  */
2958                 if (strcmp (L->Line+5, Target->Line+5) == 0) {
2959                     /* Same label, bail out */
2960                     break;
2961                 }
2962
2963                 /* Use the label in the original jump instead */
2964                 L = ReplaceLine (L, "%.5s%s", L->Line, Target->Line+5);
2965
2966             } else if (I < 2 && LineMatch (Target, Jumps [I])) {
2967
2968                 /* Conditional jump. Use final label */
2969                 strcpy (L->Line+5, Target->Line+5);
2970
2971             } else {
2972                 break;
2973             }
2974         }
2975
2976         /* Next line */
2977         L = NextCodeLine (L);
2978     }
2979 }
2980
2981
2982
2983 static void OptJumpRTS (void)
2984 /* Replace jumps to an RTS by an RTS */
2985 {
2986     Line* L = FirstCode;
2987     while (L) {
2988         /* Is this a jump to a numbered label? */
2989         if (LineMatch (L, "\tjmp\t") && L->Line [5] == 'L' && isdigit (L->Line [6])) {
2990
2991             /* Yes. Get the target label */
2992             Line* Target = GetTargetLine (L->Line+5);
2993
2994             /* Target points to the label itself. Get the next line */
2995             Target = NextCodeLine (Target);
2996             if (LineFullMatch (Target, "\trts")) {
2997                 /* Replace the jump by an RTS */
2998                 L = ReplaceLine (L, "\trts");
2999             }
3000         }
3001         L = NextCodeLine (L);
3002     }
3003 }
3004
3005
3006
3007 static void OptBoolTransforms (void)
3008 /* Try to remove the boolean transformation subroutines where they aren't
3009  * necessary.
3010  */
3011 {
3012     Line* L2 [2];
3013     unsigned Label;
3014     const char* BranchTarget;
3015
3016     Line* L = FirstCode;
3017     while (L) {
3018
3019         /* Search for a boolean transformer followed by a conditional jump. */
3020         if (LineMatch (L, "\tjsr\tbool") &&
3021             GetNextCodeLines (L, L2, 1) &&
3022             IsCondJump (L2 [0])) {
3023
3024             /* Make the boolean transformer unnecessary by changing the
3025              * the conditional jump to evaluate the condition flags that
3026              * are set after the compare directly. Note: jeq jumps if
3027              * the condition is not met, jne jumps if the condition is met.
3028              */
3029
3030             /* Get the condition code */
3031             int Cond = FindCond (L->Line + 9);
3032             if (Cond < 0) {
3033                 /* OOPS! */
3034                 goto NextLine;
3035             }
3036
3037             /* Invert the code if we jump on condition not met. */
3038             if (L2[0]->Line [2] == 'e' && L2[0]->Line [3] == 'q') {
3039                 /* Jumps if condition false, invert condition */
3040                 Cond = CmpInvertTab [Cond];
3041             }
3042
3043             /* For easier reading, get a pointer to the jump target */
3044             BranchTarget = L2[0]->Line+5;
3045
3046             /* Check if we can replace the jump (sometimes we would need two
3047              * conditional jumps, we will not handle that for now since it
3048              * has some complications - both jumps may be far jumps for
3049              * example making the jumps more costly than the bool transformer
3050              * subroutine). If we cannot replace the jump, bail out.
3051              */
3052             switch (Cond) {
3053
3054                 case CMP_EQ:
3055                     L = ReplaceLine (L, "\tjeq\t%s", BranchTarget);
3056                     break;
3057
3058                 case CMP_NE:
3059                     L = ReplaceLine (L, "\tjne\t%s", BranchTarget);
3060                     break;
3061
3062                 case CMP_GT:
3063                     Label = AllocLabel ();
3064                     L = ReplaceLine  (L, "\tbeq\tL%04X", Label);
3065                     L = NewLineAfter (L, "\tjpl\t%s", BranchTarget);
3066                     L = NewLabelAfter(L, Label);
3067                     break;
3068
3069                 case CMP_GE:
3070                     L = ReplaceLine (L, "\tjpl\t%s", BranchTarget);
3071                     break;
3072
3073                 case CMP_LT:
3074                     L = ReplaceLine (L, "\tjmi\t%s", BranchTarget);
3075                     break;
3076
3077                 case CMP_LE:
3078                     L = ReplaceLine  (L, "\tjeq\t%s", BranchTarget);
3079                     L = NewLineAfter (L, "\tjmi\t%s", BranchTarget);
3080                     break;
3081
3082                 case CMP_UGT:
3083                     Label = AllocLabel ();
3084                     L = ReplaceLine  (L, "\tbeq\tL%04X", Label);
3085                     L = NewLineAfter (L, "\tjcs\t%s", BranchTarget);
3086                     L = NewLabelAfter(L, Label);
3087                     break;
3088
3089                 case CMP_UGE:
3090                     L = ReplaceLine (L, "\tjcs\t%s", BranchTarget);
3091                     break;
3092
3093                 case CMP_ULT:
3094                     L = ReplaceLine (L, "\tjcc\t%s", BranchTarget);
3095                     break;
3096
3097                 case CMP_ULE:
3098                     L = ReplaceLine (L, "\tjeq\t%s", BranchTarget);
3099                     L = NewLineAfter (L, "\tjcc\t%s", BranchTarget);
3100                     break;
3101
3102                 default:
3103                     Internal ("Unknown jump condition: %u", Cond);
3104
3105             }
3106
3107             /* Remove the old stuff */
3108             FreeLine (L2[0]);
3109
3110         }
3111
3112 NextLine:
3113         L = NextCodeLine (L);
3114     }
3115 }
3116
3117
3118
3119 static void OptCompares2 (void)
3120 /* Try to optimize the integer compare subroutines. */
3121 {
3122     Line* L2[10];
3123     unsigned Label;
3124     const char* BranchTarget;
3125     int C;
3126
3127     Line* L = FirstCode;
3128     while (L) {
3129
3130         /* Search for
3131          *
3132          *      lda     x
3133          *      ldx     x+1
3134          *      cpx     #$00
3135          *      bne     *+4
3136          *      cmp     #$00
3137          *      jne/jeq ...
3138          *
3139          * and replace it by
3140          *
3141          *      lda     x
3142          *      ora     x+1
3143          *      jne/jeq ...
3144          */
3145         if (LineMatch (L, "\tlda\t")                                    &&
3146             GetNextCodeLines (L, L2, 5)                                 &&
3147             IsLoadAX (L, L2[0])                                         &&
3148             LineFullMatch (L2[1], "\tcpx\t#$00")                        &&
3149             LineFullMatch (L2[2], "\tbne\t*+4")                         &&
3150             LineFullMatch (L2[3], "\tcmp\t#$00")                        &&
3151             IsCondJump (L2[4])) {
3152
3153             /* Replace the load of X by an ora */
3154             L2[0]->Line[1] = 'o';
3155             L2[0]->Line[2] = 'r';
3156             L2[0]->Line[3] = 'a';
3157
3158             /* Remove unneeded stuff */
3159             FreeLines (L2[1], L2[3]);
3160
3161         }
3162
3163         /* Same for local variables: Replace
3164          *
3165          *      ldy     #$xx
3166          *      lda     (sp),y
3167          *      tax
3168          *      dey
3169          *      lda     (sp),y
3170          *      cpx     #$00
3171          *      bne     *+4                                                                                  cmp     #$00
3172          *      cmp     #$00
3173          *      jne/jeq ...
3174          *
3175          * by
3176          *
3177          *      ldy     #$xx
3178          *      lda     (sp),y
3179          *      dey
3180          *      ora     (sp),y
3181          *      jne/jeq ...
3182          */
3183         else if (LineMatch (L, "\tldy\t#$")                             &&
3184                  GetNextCodeLines (L, L2, 8)                            &&
3185                  LineFullMatch (L2[0], "\tlda\t(sp),y")                 &&
3186                  LineFullMatch (L2[1], "\ttax")                         &&
3187                  LineFullMatch (L2[2], "\tdey")                         &&
3188                  LineFullMatch (L2[3], "\tlda\t(sp),y")                 &&
3189                  LineFullMatch (L2[4], "\tcpx\t#$00")                   &&
3190                  LineFullMatch (L2[5], "\tbne\t*+4")                    &&
3191                  LineFullMatch (L2[6], "\tcmp\t#$00")                   &&
3192                  IsCondJump (L2[7])) {
3193
3194             /* Replace the second load by an ora */
3195             L2[3]->Line[1] = 'o';
3196             L2[3]->Line[2] = 'r';
3197             L2[3]->Line[3] = 'a';
3198
3199             /* Remove unneeded stuff */
3200             FreeLine (L2[1]);
3201             FreeLines (L2[4], L2[6]);
3202
3203         }
3204
3205         /* Search for the call to a compare subroutine followed by a
3206          * conditional jump.
3207          */
3208         else if (LineMatch (L, "\tjsr\ttos")                            &&
3209                 (L2[0] = NextCodeLine (L)) != 0                         &&
3210                 IsCondJump (L2[0])) {
3211
3212             /* Extract the condition from the function name and branch */
3213             C = CheckAndGetIntCmp (L, L2[0]);
3214             if (C < 0) {
3215                 /* Something is wrong */
3216                 goto NextLine;
3217             }
3218
3219             /* Replace the subroutine call by a cheaper one */
3220             L = ReplaceLine (L, "\tjsr\ttosicmp");
3221
3222             /* For easier reading, get a pointer to the jump target */
3223             BranchTarget = L2[0]->Line+5;
3224
3225             /* Check if we can replace the jump (sometimes we would need two
3226              * conditional jumps, we will not handle that for now since it
3227              * has some complications - both jumps may be far jumps for
3228              * example making the jumps more costly than the bool transformer
3229              * subroutine). If we cannot replace the jump, bail out.
3230              */
3231             switch (C) {
3232
3233                 case CMP_EQ:
3234                     L = NewLineAfter (L, "\tjeq\t%s", BranchTarget);
3235                     break;
3236
3237                 case CMP_NE:
3238                     L = NewLineAfter (L, "\tjne\t%s", BranchTarget);
3239                     break;
3240
3241                 case CMP_GT:
3242                     Label = AllocLabel ();
3243                     L = NewLineAfter (L, "\tbeq\tL%04X", Label);
3244                     L = NewLineAfter (L, "\tjpl\t%s", BranchTarget);
3245                     L = NewLabelAfter(L, Label);
3246                     break;
3247
3248                 case CMP_GE:
3249                     L = NewLineAfter (L, "\tjpl\t%s", BranchTarget);
3250                     break;
3251
3252                 case CMP_LT:
3253                     L = NewLineAfter (L, "\tjmi\t%s", BranchTarget);
3254                     break;
3255
3256                 case CMP_LE:
3257                     L = NewLineAfter (L, "\tjeq\t%s", BranchTarget);
3258                     L = NewLineAfter (L, "\tjmi\t%s", BranchTarget);
3259                     break;
3260
3261                 case CMP_UGT:
3262                     Label = AllocLabel ();
3263                     L = NewLineAfter (L, "\tbeq\tL%04X", Label);
3264                     L = NewLineAfter (L, "\tjcs\t%s", BranchTarget);
3265                     L = NewLabelAfter(L, Label);
3266                     break;
3267
3268                 case CMP_UGE:
3269                     L = NewLineAfter (L, "\tjcs\t%s", BranchTarget);
3270                     break;
3271
3272                 case CMP_ULT:
3273                     L = NewLineAfter (L, "\tjcc\t%s", BranchTarget);
3274                     break;
3275
3276                 case CMP_ULE:
3277                     L = NewLineAfter (L, "\tjeq\t%s", BranchTarget);
3278                     L = NewLineAfter (L, "\tjcc\t%s", BranchTarget);
3279                     break;
3280
3281                 default:
3282                     Internal ("Unknown jump condition: %u", C);
3283
3284             }
3285
3286             /* Remove the old stuff */
3287             FreeLine (L2[0]);
3288         }
3289
3290 NextLine:
3291         L = NextCodeLine (L);
3292     }
3293 }
3294
3295
3296
3297 static void OptTests (void)
3298 /* Remove unnecessary tests */
3299 {
3300     Line* L2 [2];
3301
3302     const char* BitOps [] = {
3303         "\tand\t",
3304         "\tora\t",
3305         "\teor\t",
3306         0
3307     };
3308
3309     /* Search for lda/tay/jne or lda/tay/jeq, remove the tay.
3310      * Search for
3311      *  lda ...
3312      *  cmp #$00
3313      *  jne/jeq
3314      * Remove the cmp.
3315      */
3316     Line* L = FirstCode;
3317     while (L) {
3318
3319         /* Search for lda/tay/jne or lda/tay/jeq, remove the tay.
3320          * Search for
3321          *      lda/and/ora/eor
3322          *      cmp     #$00
3323          *      jne/jeq ...
3324          * Remove the cmp.
3325          */
3326         if ((LineMatch (L, "\tlda\t")               ||
3327              LineMatch (L, "\tand\t")               ||
3328              LineMatch (L, "\tora\t")               ||
3329              LineMatch (L, "\teor\t"))                  &&
3330             GetNextCodeLines (L, L2, 2)                 &&
3331             (LineFullMatch (L2 [0], "\ttay")        ||
3332              LineFullMatch (L2 [0], "\tcmp\t#$00"))     &&
3333             IsCondJump (L2 [1])) {
3334
3335             /* We can remove the tay */
3336             FreeLine (L2 [0]);
3337
3338         }
3339
3340         /* Search for
3341          *
3342          *      and     ...
3343          *      tax
3344          *      jeq/jne
3345          *
3346          * and remove the tax.
3347          */
3348         else if (LineMatchX (L, BitOps) >= 0            &&
3349                  GetNextCodeLines (L, L2, 2)            &&
3350                  LineFullMatch (L2[0], "\ttax")         &&
3351                  IsCondJump (L2[1])) {
3352
3353             /* Remove the tax including a hint line of there is one */
3354             if (LineFullMatch (L2[0]->Prev, "+forcetest")) {
3355                 FreeLine (L2[0]->Prev);
3356             }
3357             FreeLine (L2[0]);
3358
3359             /* If the line before L loads X, this is useless and may be removed */
3360             L2[0] = PrevCodeLine (L);
3361             if (LineFullMatch (L2[0], "\tldx\t#$00")) {
3362                 FreeLine (L2[0]);
3363             }
3364
3365         }
3366
3367         /* Search for the sequence
3368          *
3369          *      stx     xx
3370          *      stx     tmp1
3371          *      ora     tmp1
3372          *
3373          * and replace it by
3374          *
3375          *      stx     xx
3376          *      ora     xx
3377          */
3378         else if (LineMatch (L, "\tstx\t")               &&
3379                  GetNextCodeLines (L, L2, 2)            &&
3380                  LineFullMatch (L2[0], "\tstx\ttmp1")   &&
3381                  LineFullMatch (L2[1], "\tora\ttmp1")) {
3382
3383             /* Found, replace it */
3384             L = NewLineAfter (L, "\tora\t%s", L->Line+5);
3385
3386             /* Remove remaining stuff */
3387             FreeLines (L2[0], L2[1]);
3388
3389         }
3390
3391
3392         /* Next line */
3393         L = NextCodeLine (L);
3394     }
3395 }
3396
3397
3398
3399 static void OptNeg (void)
3400 /* Optimize the "bnegax/jeq" and "bnegax/jne" sequences */
3401 {
3402     Line* L2 [10];
3403
3404     Line* L = FirstCode;
3405     while (L) {
3406
3407         /* Search for the sequence:
3408          *
3409          *      lda     ...
3410          *      jsr     bnega
3411          *      jeq/jne ...
3412          *
3413          * and replace it by:
3414          *
3415          *      lda     ...
3416          *      jne/jeq ...
3417          */
3418         if (LineMatch (L, "\tlda\t")                    && /* Match on start */
3419             GetNextCodeLines (L, L2, 2)                 && /* Fetch next lines */
3420             LineFullMatch (L2 [0], "\tjsr\tbnega")      &&
3421             IsCondJump (L2 [1])) {
3422
3423             /* Found the sequence, replace it */
3424             FreeLine (L2 [0]);
3425             InvertZJump (L2 [1]);
3426
3427         }
3428
3429         /* Search for the sequence:
3430          *
3431          *      ldy     #$xx
3432          *      lda     (sp),y
3433          *      tax
3434          *      dey
3435          *      lda     (sp),y
3436          *      jsr     bnegax
3437          *      jne/jeq ...
3438          *
3439          * and replace it by
3440          *
3441          *      ldy     #$xx
3442          *      lda     (sp),y
3443          *      dey
3444          *      ora     (sp),y
3445          *      jeq/jne ...
3446          */
3447         else if (LineMatch (L, "\tldy\t#$")                     &&
3448                  GetNextCodeLines (L, L2, 6)                    &&
3449                  LineFullMatch (L2[0], "\tlda\t(sp),y")         &&
3450                  LineFullMatch (L2[1], "\ttax")                 &&
3451                  LineFullMatch (L2[2], "\tdey")                 &&
3452                  LineFullMatch (L2[3], "\tlda\t(sp),y")         &&
3453                  LineFullMatch (L2[4], "\tjsr\tbnegax")         &&
3454                  IsCondJump    (L2[5])) {
3455
3456             L2[1] = ReplaceLine (L2[1], "\tdey");
3457             L2[2] = ReplaceLine (L2[2], "\tora\t(sp),y");
3458             FreeLines (L2[3], L2[4]);
3459             InvertZJump (L2[5]);
3460
3461         }
3462
3463         /* Search for the sequence:
3464          *
3465          *      lda     xx
3466          *      ldx     xx+1
3467          *      jsr     bnegax
3468          *      jne/jeq ...
3469          *
3470          * and replace it by
3471          *
3472          *      lda     xx
3473          *      ora     xx+1
3474          *      jeq/jne ...
3475          */
3476         else if (LineMatch (L, "\tlda\t")                       &&
3477                  GetNextCodeLines (L, L2, 3)                    &&
3478                  IsLoadAX (L, L2[0])                            &&
3479                  LineFullMatch (L2[1], "\tjsr\tbnegax")         &&
3480                  IsCondJump    (L2[2])) {
3481
3482             /* Replace the load of X by ora */
3483             L2[0]->Line[1] = 'o';
3484             L2[0]->Line[2] = 'r';
3485             L2[0]->Line[3] = 'a';
3486             FreeLine (L2[1]);
3487             InvertZJump (L2[2]);
3488
3489         }
3490
3491         /* Search for the sequence:
3492          *
3493          *      jsr     _xxx
3494          *      jsr     bnega(x)
3495          *      jeq/jne ...
3496          *
3497          * and replace it by:
3498          *
3499          *      jsr     _xxx
3500          *      <boolean test>
3501          *      jne/jeq ...
3502          */
3503         else if (LineMatch (L, "\tjsr\t_")              && /* Match on start */
3504                  GetNextCodeLines (L, L2, 2)            &&
3505                  LineMatch (L2 [0], "\tjsr\tbnega")     &&
3506                  IsCondJump (L2 [1])) {
3507
3508             if (LineFullMatch (L2 [0], "\tjsr\tbnega")) {
3509                 /* Byte sized */
3510                 L2 [0] = ReplaceLine (L2 [0], "\ttax"); /* Test a */
3511             } else {
3512                 /* Word sized */
3513                 L2 [0] = ReplaceLine (L2 [0], "\tstx\ttmp1");
3514                 NewLineAfter (L2 [0], "\tora\ttmp1");
3515             }
3516
3517             /* Invert the jump */
3518             InvertZJump (L2 [1]);
3519
3520         }
3521
3522         /* Next line */
3523         L = NextCodeLine (L);
3524     }
3525 }
3526
3527
3528
3529 static void OptTriples (void)
3530 /* Replace code triples */
3531 {
3532     static const char* Pat1 [] = {
3533         "\tjsr\tldaxysp",
3534         "\tjsr\tldax0sp",
3535         "\tjsr\tldaysp",
3536         "\tjsr\tleaasp",
3537         "\tjsr\tldaxi",
3538         0
3539     };
3540     static const char* Pat2 [] = {
3541         "\tjsr\tpushax",
3542         "\tjsr\tpushax",
3543         "\tjsr\tpushax",
3544         "\tjsr\tpushax",
3545         "\tjsr\tpushax",
3546         0
3547     };
3548     static const char* Replace [] = {
3549         "\tjsr\tpushwysp",
3550         "\tjsr\tpushw0sp",
3551         "\tjsr\tpushbysp",
3552         "\tjsr\tpleaasp",
3553         "\tjsr\tpushw",
3554     };
3555
3556     Line* L = FirstCode;
3557     while (L) {
3558         int I = LineFullMatchX (L, Pat1);
3559         if (I >= 0) {
3560             /* We found the first match, get the next line */
3561             Line* L2 = NextCodeLine (L);
3562             if (L2 && LineFullMatch (L2, Pat2 [I])) {
3563                 /* Found. Replace by the short call */
3564                 FreeLine (L2);
3565                 L = ReplaceLine (L, Replace [I]);
3566             }
3567         }
3568         /* Next line */
3569         L = NextCodeLine (L);
3570     }
3571 }
3572
3573
3574
3575 static Line* OptOneBlock (Line* L)
3576 /* Optimize the register contents inside one basic block */
3577 {
3578     static const char* Compares [] = {
3579         "\tjsr\ttoseq00",   "\tjsr\ttoseqa0",   "\tjsr\ttoseqax",
3580         "\tjsr\ttoseqeax",  "\tjsr\ttosne00",   "\tjsr\ttosnea0",
3581         "\tjsr\ttosneax",   "\tjsr\ttosneeax",  "\tjsr\ttoslt00",
3582         "\tjsr\ttoslta0",   "\tjsr\ttosltax",   "\tjsr\ttosult00",
3583         "\tjsr\ttosulta0",  "\tjsr\ttosultax",  "\tjsr\ttoslteax",
3584         "\tjsr\ttosulteax", "\tjsr\ttosle00",   "\tjsr\ttoslea0",
3585         "\tjsr\ttosleax",   "\tjsr\ttosule00",  "\tjsr\ttosulea0",
3586         "\tjsr\ttosuleax",  "\tjsr\ttosleeax",  "\tjsr\ttosuleeax",
3587         "\tjsr\ttosgt00",   "\tjsr\ttosgta0",   "\tjsr\ttosgtax",
3588         "\tjsr\ttosugt00",  "\tjsr\ttosugta0",  "\tjsr\ttosugtax",
3589         "\tjsr\ttosgteax",  "\tjsr\ttosugteax", "\tjsr\ttosge00",
3590         "\tjsr\ttosgea0",   "\tjsr\ttosgeax",   "\tjsr\ttosuge00",
3591         "\tjsr\ttosugea0",  "\tjsr\ttosugeax",  "\tjsr\ttosgeeax",
3592         "\tjsr\ttosugeeax",
3593         0
3594     };
3595
3596     static const char* MakeBool [] = {
3597         "\tjsr\tbooleq",    "\tjsr\tboolne",    "\tjsr\tboollt",
3598         "\tjsr\tboolle",    "\tjsr\tboolgt",    "\tjsr\tboolge",
3599         "\tjsr\tboolult",   "\tjsr\tboolule",   "\tjsr\tboolugt",
3600         "\tjsr\tbooluge",
3601         0
3602     };
3603
3604     int A = -1;                 /* Contents of A register */
3605     int X = -1;                 /* Contents of X register */
3606     int Y = -1;                 /* Contents of Y register */
3607     Line* L2;
3608     unsigned NewVal;
3609     int Delete;
3610
3611     while (L && !IsLabel (L)) {
3612
3613         /* Handle all instructions. All instructions not tested here have
3614          * no effects on the register contents.
3615          */
3616         Delete = 0;
3617         if (L->Line [0] == '+') {
3618             /* This is a hint */
3619             if (LineMatch (L, "+a:")) {
3620                 /* Information about a */
3621                 switch (L->Line [3]) {
3622                     case '!':   A = -1;                         break;
3623                     case '=':   A = GetHexNum (L->Line + 4);    break;
3624                 }
3625             } else if (LineMatch (L, "+x:")) {
3626                 /* The code generator tells something about the x register */
3627                 switch (L->Line [3]) {
3628                     case '!':   X = -1;                         break;
3629                     case '=':   X = GetHexNum (L->Line + 4);    break;
3630                 }
3631             } else if (LineMatch (L, "+y:")) {
3632                 /* Information about the y register */
3633                 switch (L->Line [3]) {
3634                     case '!':   Y = -1;                         break;
3635                     case '=':   Y = GetHexNum (L->Line + 4);    break;
3636                 }
3637             }
3638         } else if (LineMatch (L, "\tadc\t")) {
3639             if (CPU == CPU_65C02 && Y == 0 && L->Line[5] == '(' && IsYAddrMode(L)) {
3640                 L->Line[strlen(L->Line)-2] = '\0';
3641             }
3642             A = -1;
3643         } else if (LineMatch (L, "\tand\t")) {
3644             A = -1;
3645         } else if (LineFullMatch (L, "\tasl\ta")) {
3646             if (A != -1) {
3647                 A = (A << 1) & 0xFF;
3648             }
3649         } else if (CPU == CPU_65C02 && Y == 0 && L->Line[5] == '(' && IsYAddrMode(L)) {
3650             L->Line[strlen(L->Line)-2] = '\0';
3651         } else if (CPU == CPU_65C02 && (LineFullMatch (L, "\tdea") ||
3652                                         LineFullMatch (L, "\tdec\ta"))) {
3653             DEC (A, 1);
3654         } else if (LineFullMatch (L, "\tdex")) {
3655             DEC (X, 1);
3656         } else if (LineFullMatch (L, "\tdey")) {
3657             DEC (Y, 1);
3658         } else if (LineMatch (L, "\teor")) {
3659             A = -1;
3660         } else if (CPU == CPU_65C02 && (LineFullMatch (L, "\tina") ||
3661                                         LineFullMatch (L, "\tinc\ta"))) {
3662             INC (A, 1);
3663         } else if (LineFullMatch (L, "\tinx")) {
3664             INC (X, 1);
3665         } else if (LineFullMatch (L, "\tiny")) {
3666             INC (Y, 1);
3667         } else if (LineFullMatch (L, "\tjsr\taddeq0sp")) {
3668             /* We know about this function */
3669             A = X = -1;
3670             Y = 1;
3671         } else if (LineFullMatch (L, "\tjsr\taddeqysp")) {
3672             /* We know about this function */
3673             A = X = -1;
3674             INC (Y, 1);
3675         } else if (LineFullMatch (L, "\tjsr\taxulong")) {
3676             /* We know about this function and we're trying to replace it by
3677              * inline code if we have already a register that contains zero.
3678              */
3679             char C;
3680             if (A == 0) {
3681                 C = 'a';
3682             } else if (X == 0) {
3683                 C = 'x';
3684             } else if (Y == 0) {
3685                 C = 'y';
3686             } else {
3687                 C = '\0';
3688             }
3689             if (C == '\0') {
3690                 /* We cannot replace the code, but we know about the results */
3691                 Y = 0;
3692             } else {
3693                 L = ReplaceLine (L, "\tst%c\tsreg", C);
3694                 NewLineAfter (L, "\tst%c\tsreg+1", C);
3695             }
3696         } else if (LineFullMatch (L, "\tjsr\tbnega")) {
3697             /* We know about this function */
3698             A = -1;
3699             X = 0;
3700         } else if (LineFullMatch (L, "\tjsr\tbnegax")) {
3701             /* We know about this function */
3702             A = -1;
3703             X = 0;
3704         } else if (LineFullMatch (L, "\tjsr\tbnegeax")) {
3705             /* We know about this function */
3706             A = -1;
3707             X = 0;
3708         } else if (LineFullMatch (L, "\tjsr\tcomplax")) {
3709             /* We know about this function */
3710             if (A != -1) {
3711                 A ^= 0xFF;
3712             }
3713             if (X != -1) {
3714                 X ^= 0xFF;
3715             }
3716         } else if (LineFullMatch (L, "\tjsr\tdecax1")) {
3717             /* We know about this function */
3718             A = X = -1;
3719         } else if (LineFullMatch (L, "\tjsr\tdecax2")) {
3720             /* We know about this function */
3721             A = X = -1;
3722         } else if (LineFullMatch (L, "\tjsr\tdecaxy")) {
3723             /* We know about this function */
3724             A = X = -1;
3725         } else if (LineFullMatch (L, "\tjsr\tdeceaxy")) {
3726             /* We know about this function */
3727             A = X = -1;
3728         } else if (LineFullMatch (L, "\tjsr\tincax1")) {
3729             /* We know about this function */
3730             A = X = -1;
3731         } else if (LineFullMatch (L, "\tjsr\tincax2")) {
3732             /* We know about this function */
3733             A = X = -1;
3734         } else if (LineFullMatch (L, "\tjsr\tinceaxy")) {
3735             /* We know about this function */
3736             A = X = -1;
3737         } else if (LineFullMatch (L, "\tjsr\tladdeq")) {
3738             /* We know about this function */
3739             A = X = -1;
3740             Y = 3;
3741         } else if (LineFullMatch (L, "\tjsr\tladdeqb")) {
3742             /* We know about this function */
3743             A = X = -1;
3744             Y = 3;
3745         } else if (LineFullMatch (L, "\tjsr\tlbneg")) {
3746             /* We know about this function */
3747             A = -1;
3748             X = 0;
3749         } else if (LineFullMatch (L, "\tjsr\tldai")) {
3750             /* We know about this function */
3751             A = X = -1;
3752             Y = 0;
3753         } else if (LineFullMatch (L, "\tjsr\tldaidx")) {
3754             /* We know about this function */
3755             A = X = -1;
3756         } else if (LineFullMatch (L, "\tjsr\tldau00sp")) {
3757             /* We know about this function */
3758             A = -1;
3759             X = 0;
3760             Y = 0;
3761         } else if (LineFullMatch (L, "\tjsr\tldau0ysp")) {
3762             /* We know about this function */
3763             A = -1;
3764             X = 0;
3765             DEC (Y, 1);
3766         } else if (LineFullMatch (L, "\tjsr\tldaui")) {
3767             /* We know about this function */
3768             A = -1;
3769             X = 0;
3770             Y = 0;
3771         } else if (LineFullMatch (L, "\tjsr\tldaui0sp")) {
3772             A = -1;
3773             Y = X;
3774             X = 0;
3775         } else if (LineFullMatch (L, "\tjsr\tldauidx")) {
3776             /* We know about this function */
3777             A = -1;
3778             X = 0;
3779         } else if (LineFullMatch (L, "\tjsr\tldauiysp")) {
3780             /* We know about this function */
3781             A = -1;
3782             Y = X;
3783             X = 0;
3784         } else if (LineFullMatch (L, "\tjsr\tldax0sp")) {
3785             /* We know about this function */
3786             A = X = -1;
3787             Y = 0;
3788         } else if (LineFullMatch (L, "\tjsr\tldaxi")) {
3789             /* We know about this function */
3790             A = X = -1;
3791             Y = 0;
3792         } else if (LineFullMatch (L, "\tjsr\tldaxidx")) {
3793             /* We know about this function */
3794             A = X = -1;
3795             DEC (Y, 1);
3796         } else if (LineFullMatch (L, "\tjsr\tldaxysp")) {
3797             /* We know about this function */
3798             A = X = -1;
3799             DEC (Y, 1);
3800         } else if (LineFullMatch (L, "\tjsr\tldeaxi")) {
3801             /* We know about this function */
3802             A = X = -1;
3803             Y = 0;
3804         } else if (LineFullMatch (L, "\tjsr\tldeaxidx")) {
3805             /* We know about this function */
3806             A = X = -1;
3807             DEC (Y, 3);
3808         } else if (LineFullMatch (L, "\tjsr\tlsubeq")) {
3809             /* We know about this function */
3810             A = X = -1;
3811             Y = 3;
3812         } else if (LineFullMatch (L, "\tjsr\tlsubeqb")) {
3813             /* We know about this function */
3814             A = X = -1;
3815             Y = 3;
3816         } else if (LineFullMatch (L, "\tjsr\tnegax")) {
3817             /* We know about this function */
3818             A = X = -1;
3819         } else if (LineFullMatch (L, "\tjsr\tnegeax")) {
3820             /* We know about this function */
3821             A = X = -1;
3822         } else if (LineFullMatch (L, "\tjsr\tpush0")) {
3823             /* We know about this function */
3824             A = 0;
3825             X = 0;
3826             Y = 1;
3827         } else if (LineFullMatch (L, "\tjsr\tpush1")) {
3828             /* We know about this function */
3829             A = 1;
3830             X = 0;
3831             Y = 1;
3832         } else if (LineFullMatch (L, "\tjsr\tpush2")) {
3833             /* We know about this function */
3834             A = 2;
3835             X = 0;
3836             Y = 1;
3837         } else if (LineFullMatch (L, "\tjsr\tpush3")) {
3838             /* We know about this function */
3839             A = 3;
3840             X = 0;
3841             Y = 1;
3842         } else if (LineFullMatch (L, "\tjsr\tpush4")) {
3843             /* We know about this function */
3844             A = 4;
3845             X = 0;
3846             Y = 1;
3847         } else if (LineFullMatch (L, "\tjsr\tpush5")) {
3848             /* We know about this function */
3849             A = 5;
3850             X = 0;
3851             Y = 1;
3852         } else if (LineFullMatch (L, "\tjsr\tpush6")) {
3853             /* We know about this function */
3854             A = 6;
3855             X = 0;
3856             Y = 1;
3857         } else if (LineFullMatch (L, "\tjsr\tpush7")) {
3858             /* We know about this function */
3859             A = 7;
3860             X = 0;
3861             Y = 1;
3862         } else if (LineFullMatch (L, "\tjsr\tpusha")) {
3863             /* We know about this function */
3864             Y = 0;
3865         } else if (LineFullMatch (L, "\tjsr\tpusha0")) {
3866             /* We know about this function
3867              * If X is already zero, we may call pushax instead and save two
3868              * cycles.
3869              */
3870             if (X == 0) {
3871                 L = ReplaceLine (L, "\tjsr\tpushax");
3872             }
3873             X = 0;
3874             Y = 1;
3875         } else if (LineFullMatch (L, "\tjsr\tpushax")) {
3876             /* We know about this function */
3877             Y = 1;
3878         } else if (LineFullMatch (L, "\tjsr\tpushaysp")) {
3879             /* We know about this function */
3880             A = -1;
3881             Y = 0;
3882         } else if (LineFullMatch (L, "\tjsr\tpushc0")) {
3883             /* We know about this function */
3884             A = 0;
3885             Y = 0;
3886         } else if (LineFullMatch (L, "\tjsr\tpushc1")) {
3887             /* We know about this function */
3888             A = 1;
3889             Y = 0;
3890         } else if (LineFullMatch (L, "\tjsr\tpushc2")) {
3891             /* We know about this function */
3892             A = 2;
3893             Y = 0;
3894         } else if (LineFullMatch (L, "\tjsr\tpushw")) {
3895             /* We know about this function (calls pushax) */
3896             A = X = -1;
3897             Y = 1;
3898         } else if (LineFullMatch (L, "\tjsr\tpushw0sp")) {
3899             /* We know about this function(calls pushax)  */
3900             A = X = -1;
3901             Y = 1;
3902         } else if (LineFullMatch (L, "\tjsr\tpushwidx")) {
3903             /* We know about this function (calls pushax) */
3904             A = X = -1;
3905             Y = 1;
3906         } else if (LineFullMatch (L, "\tjsr\tpushwysp")) {
3907             /* We know about this function (calls pushax) */
3908             A = X = -1;
3909             Y = 1;
3910         } else if (LineFullMatch (L, "\tjsr\tresteax")) {
3911             /* We know about this function */
3912             A = X = -1;
3913         } else if (LineFullMatch (L, "\tjsr\tsaveeax")) {
3914             /* We know about this function */
3915             /* Changes nothing */
3916         } else if (LineFullMatch (L, "\tjsr\tshrax1")) {
3917             /* We know about this function */
3918             A = X = -1;
3919         } else if (LineFullMatch (L, "\tjsr\tshrax2")) {
3920             /* We know about this function */
3921             A = X = -1;
3922         } else if (LineFullMatch (L, "\tjsr\tshrax3")) {
3923             /* We know about this function */
3924             A = X = -1;
3925         } else if (LineFullMatch (L, "\tjsr\tshreax1")) {
3926             /* We know about this function */
3927             A = X = -1;
3928         } else if (LineFullMatch (L, "\tjsr\tshreax2")) {
3929             /* We know about this function */
3930             A = X = -1;
3931         } else if (LineFullMatch (L, "\tjsr\tshreax3")) {
3932             /* We know about this function */
3933             A = X = -1;
3934         } else if (LineFullMatch (L, "\tjsr\tstaspp")) {
3935             /* We know about this function */
3936             Y = -1;
3937         } else if (LineFullMatch (L, "\tjsr\tstaxspp")) {
3938             /* We know about this function */
3939             Y = -1;
3940         } else if (LineFullMatch (L, "\tjsr\tstax0sp")) {
3941             /* We know about this function */
3942             Y = 1;
3943         } else if (LineFullMatch (L, "\tjsr\tstaxysp")) {
3944             /* We know about this function */
3945             INC (Y, 1);
3946         } else if (LineFullMatch (L, "\tjsr\tsubeq0sp")) {
3947             /* We know about this function */
3948             A = X = -1;
3949             Y = 1;
3950         } else if (LineFullMatch (L, "\tjsr\tsubeqysp")) {
3951             /* We know about this function */
3952             A = X = -1;
3953             INC (Y, 1);
3954         } else if (LineFullMatch (L, "\tjsr\ttosadda0")) {
3955             /* We know about this function */
3956             A = X = -1;
3957             Y = 1;
3958         } else if (LineFullMatch (L, "\tjsr\ttosaddax")) {
3959             /* We know about this function */
3960             A = X = -1;
3961             Y = 1;
3962         } else if (LineFullMatch (L, "\tjsr\ttosicmp")) {
3963             /* We know about this function */
3964             A = X = -1;
3965             Y = 0;
3966         } else if (LineFullMatchX (L, Compares) >= 0) {
3967             A = Y = -1;
3968             X = 0;
3969         } else if (LineFullMatchX (L, MakeBool) >= 0) {
3970             A = -1;
3971             X = 0;
3972         } else if (LineMatch (L, "\tjsr\t")) {
3973             /* Subroutine call, forget all register information */
3974             A = X = Y = -1;
3975         } else if (LineMatch (L, "\tlda\t")) {
3976             if (!RegAUsed (L) && !IsCondJump (NextInstruction (L))) {
3977                 /* The value loaded is not used later, remove it */
3978                 Delete = 1;
3979             } else if (LineMatch (L, "\tlda\t(")) {
3980                 if (IsXAddrMode (L)) {
3981                     /* lda (zp,x) - if Y and X are both zero, replace by
3982                      * load indirect y and save one cycle in some cases.
3983                      */
3984                     if (X == 0 && Y == 0) {
3985                         char Buf [256];
3986                         const char* S = L->Line + 6;
3987                         char* T = Buf + 6;
3988                         strcpy (Buf, "\tlda\t(");
3989                         while (*S != ',') {
3990                             *T++ = *S++;
3991                         }
3992                         *T++ = ')';
3993                         *T++ = ',';
3994                         *T++ = 'y';
3995                         *T   = '\0';
3996                         L = ReplaceLine (L, Buf);
3997                     }
3998                 }
3999                 /* In any case invalidate A */
4000                 A = -1;
4001             } else if (LineMatch (L, "\tlda\t#$")) {
4002                 /* Immidiate load into A */
4003                 NewVal = GetHexNum (L->Line + 7);
4004                 if (NewVal == A) {
4005                     /* Load has no effect */
4006                     Delete = 1;
4007                 } else if (NewVal == X) {
4008                     /* Requested value is already in X */
4009                     L = ReplaceLine (L, "\ttxa");
4010                 } else if (NewVal == Y) {
4011                     /* Requested value is already in Y */
4012                     L = ReplaceLine (L, "\ttya");
4013                 } else if (CPU == CPU_65C02 && A != -1) {
4014                     /* Try ina/dea operators of 65C02 */
4015                     if (NewVal == ((A - 1) & 0xFF)) {
4016                         L = ReplaceLine (L, "\tdea");
4017                     } else if (NewVal == ((A + 1) & 0xFF)) {
4018                         L = ReplaceLine (L, "\tina");
4019                     }
4020                 }
4021                 /* Anyway, the new value is now in A */
4022                 A = NewVal;
4023             } else {
4024                 /* Memory load into A */
4025                 A = -1;
4026             }
4027         } else if (LineMatch (L, "\tldax\t")) {
4028             /* Memory load into A and X */
4029             A = X = -1;
4030         } else if (LineMatch (L, "\tldx\t")) {
4031             if (!RegXUsed (L) && !IsCondJump (NextInstruction (L))) {
4032                 /* The value loaded is not used later, remove it */
4033                 Delete = 1;
4034             } else if (LineMatch (L, "\tldx\t#$")) {
4035                 /* Immidiate load into X */
4036                 NewVal = GetHexNum (L->Line + 7);
4037                 if (NewVal == X) {
4038                     /* Load has no effect */
4039                     Delete = 1;
4040                 } else if (NewVal == A) {
4041                     /* Requested value is already in A */
4042                     L = ReplaceLine (L, "\ttax");
4043                 } else if (X != -1 && NewVal == ((X + 1) & 0xFF)) {
4044                     /* Requested value is one more than current contents */
4045                     L = ReplaceLine (L, "\tinx");
4046                 } else if (X != -1 && NewVal == ((X - 1) & 0xFF)) {
4047                     /* Requested value is one less than current contents */
4048                     L = ReplaceLine (L, "\tdex");
4049                 }
4050                 /* Anyway, the new value is now in X */
4051                 X = NewVal;
4052             } else {
4053                 /* Memory load into X */
4054                 X = -1;
4055             }
4056         } else if (LineMatch (L, "\tldy\t")) {
4057             if (!RegYUsed (L) && !IsCondJump (NextInstruction (L))) {
4058                 /* The value loaded is not used later, remove it */
4059                 Delete = 1;
4060             } else if (LineMatch (L, "\tldy\t#$")) {
4061                 /* Immidiate load into Y */
4062                 NewVal = GetHexNum (L->Line + 7);
4063                 if (NewVal == Y) {
4064                     /* Load has no effect */
4065                     Delete = 1;
4066                 } else if (NewVal == A) {
4067                     /* Requested value is already in A */
4068                     L = ReplaceLine (L, "\ttay");
4069                 } else if (Y != -1 && NewVal == ((Y + 1) & 0xFF)) {
4070                     /* Requested value is one more than current contents */
4071                     L = ReplaceLine (L, "\tiny");
4072                 } else if (Y != -1 && NewVal == ((Y - 1) & 0xFF)) {
4073                     /* Requested value is one less than current contents */
4074                     L = ReplaceLine (L, "\tdey");
4075                 }
4076                 /* Anyway, the new value is now in Y */
4077                 Y = NewVal;
4078             } else {
4079                 /* Memory load into Y */
4080                 Y = -1;
4081             }
4082         } else if (LineFullMatch (L, "\tlsr\ta")) {
4083             if (A != -1) {
4084                 A >>= 1;
4085             }
4086         } else if (LineMatch (L, "\tora\t#$")) {
4087             if (A != -1) {
4088                 A |= GetHexNum (L->Line + 7);
4089             }
4090         } else if (LineMatch (L, "\tora\t")) {
4091             A = -1;
4092         } else if (LineFullMatch (L, "\tpla")) {
4093             A = -1;
4094         } else if (LineFullMatch (L, "\trol\ta")) {
4095             A = -1;
4096         } else if (LineFullMatch (L, "\tror\ta")) {
4097             A = -1;
4098         } else if (LineFullMatch (L, "\trts")) {
4099             A = X = Y = -1;
4100         } else if (LineFullMatch (L, "\trti")) {
4101             A = X = Y = -1;
4102         } else if (LineMatch (L, "\tsbc\t")) {
4103             if (CPU == CPU_65C02 && Y == 0 && L->Line[5] == '(' && IsYAddrMode(L)) {
4104                 L->Line[strlen(L->Line)-2] = '\0';
4105             }
4106             A = -1;
4107         } else if (CPU == CPU_65C02 && LineMatch (L, "\tst")) {
4108             /* Try to replace by stz if possible */
4109             if (A == 0 && LineMatch (L, "\tsta\t")) {
4110                 /* Not indirect and not Y allowed */
4111                 if (L->Line[5] != '(' && !IsYAddrMode (L)) {
4112                     L->Line[3] = 'z';
4113                 }
4114             } else if (X == 0 && LineMatch (L, "\tstx\t")) {
4115                 /* absolute,y not allowed */
4116                 if (!IsYAddrMode (L)) {
4117                     L->Line[3] = 'z';
4118                 }
4119             } else if (Y == 0 && LineMatch (L, "\tsty\t")) {
4120                 /* sty and stz share all addressing modes */
4121                 L->Line[3] = 'z';
4122             }
4123         } else if (LineFullMatch (L, "\ttax")) {
4124             if (A != -1 && X == A) {
4125                 /* Load has no effect */
4126                 Delete = 1;
4127             } else {
4128                 X = A;
4129             }
4130         } else if (LineFullMatch (L, "\ttay")) {
4131             if (A != -1 && Y == A) {
4132                 /* Load has no effect */
4133                 Delete = 1;
4134             } else {
4135                 Y = A;
4136             }
4137         } else if (LineFullMatch (L, "\ttsx")) {
4138             X = -1;
4139         } else if (LineFullMatch (L, "\ttxa")) {
4140             if (X != -1 && A == X) {
4141                 /* Load has no effect */
4142                 Delete = 1;
4143             } else {
4144                 A = X;
4145             }
4146         } else if (LineFullMatch (L, "\ttya")) {
4147             if (Y != -1 && A == Y) {
4148                 /* Load has no effect */
4149                 Delete = 1;
4150             } else {
4151                 A = Y;
4152             }
4153         }
4154
4155         /* Set to next line, handle deletions */
4156         L2 = NextCodeSegLine (L);
4157         if (Delete) {
4158             FreeLine (L);
4159         }
4160         L = L2;
4161
4162     }
4163     if (L) {
4164         /* Skip the label */
4165         L = NextCodeSegLine (L);
4166     }
4167     return L;
4168 }
4169
4170
4171
4172 static void OptBlocks (void)
4173 /* Optimize the register contents inside basic blocks */
4174 {
4175     Line* L = FirstCode;
4176     while (L) {
4177         L = OptOneBlock (L);
4178     }
4179 }
4180
4181
4182
4183 static void OptJumps (void)
4184 /* Optimize jumps */
4185 {
4186     static const char* Jumps [] = {
4187         "\tjeq\tL",
4188         "\tjne\tL",
4189         "\tjmi\tL",
4190         "\tjpl\tL",
4191         "\tjcs\tL",
4192         "\tjcc\tL",
4193         0
4194     };
4195
4196     Line* L = FirstCode;
4197     while (L) {
4198         int I = LineMatchX (L, Jumps);
4199         if (I >= 0) {
4200             Line* Target = GetTargetLine (L->Line+5);
4201             if (Target->Index > L->Index) {
4202                 /* This is a forward jump. Backward jumps are handled
4203                  * automagically by the assembler.
4204                  */
4205                 unsigned Distance = GetJumpDistance (L, Target);
4206                 if (Distance < 123) {           /* Safety */
4207                     L->Line [1] = 'b';          /* Make a short branch */
4208                     L->Size = 2;                /* Set new size */
4209                 }
4210             }
4211         }
4212         L = NextCodeLine (L);
4213     }
4214
4215     /* Special treatment for jumps on the 65C02 */
4216     if (CPU == CPU_65C02) {
4217
4218         Line* L = FirstCode;
4219         while (L) {
4220             if (LineMatch (L, "\tjmp\tL")) {
4221                 Line* Target = GetTargetLine (L->Line+5);
4222                 unsigned Distance = GetJumpDistance (L, Target);
4223                 if (Distance < 123) {           /* Safety */
4224                     L->Line [1] = 'b';          /* Make a short branch */
4225                     L->Line [2] = 'r';
4226                     L->Line [3] = 'a';
4227                     L->Size = 2;                /* Set new size */
4228                 }
4229             }
4230             L = NextCodeLine (L);
4231         }
4232
4233     }
4234 }
4235
4236
4237
4238 static void OptRTS (void)
4239 /* Change sequences of jsr XXX/rts to jmp XXX */
4240 {
4241     Line* L = FirstCode;
4242     while (L) {
4243         if (LineMatch (L, "\tjsr\t")) {
4244             /* This is a jsr, get the next instruction */
4245             Line* L2 = NextCodeLine (L);
4246             if (L2 && LineFullMatch (L2, "\trts")) {
4247                 /* We found a sequence */
4248                 FreeLine (L2);
4249                 L->Line [2] = 'm';
4250                 L->Line [3] = 'p';
4251             }
4252         }
4253         /* Try the next line */
4254         L = NextCodeLine (L);
4255     }
4256 }
4257
4258
4259
4260 /*****************************************************************************/
4261 /*                                   Code                                    */
4262 /*****************************************************************************/
4263
4264
4265
4266 static void OptOnePass (unsigned long Flag, void (*F) (void))
4267 /* Call one optimizer pass if enabled */
4268 {
4269     if ((OptDisable & Flag) == 0) {
4270         F ();
4271     } else if (Verbose || Debug) {
4272         printf ("Optimizer pass %04lX skipped\n", Flag);
4273     }
4274 }
4275
4276
4277
4278 void OptDoOpt (void)
4279 /* Run the optimizer over the collected stuff */
4280 {
4281     /* Find and remember the first line of code */
4282     FindCodeStart ();
4283
4284     /* Mark all lines inside the code segment */
4285     MarkCodeLines ();
4286
4287     /* Create a list of all local labels for fast access */
4288     CreateLabelList ();
4289
4290     /* Ok, now start the real optimizations */
4291
4292     /* Optimize compares - first step */
4293     OptOnePass (0x0001, OptCompares1);
4294
4295     /* Remove dead jumps */
4296     OptOnePass (0x0002, OptDeadJumps);
4297
4298     /* Remove unnecessary loads */
4299     OptOnePass (0x0004, OptLoads);
4300
4301     /* Remove unnecessary register loads */
4302     OptOnePass (0x0008, OptRegLoads);
4303
4304     /* Optimize stores through pointers */
4305     OptOnePass (0x0010, OptPtrOps);
4306
4307     /* Optimize use of register variables */
4308     OptOnePass (0x0020, OptRegVars);
4309
4310     /* Remove jump cascades - must be used before OptNeg */
4311     OptOnePass (0x0040, OptDoubleJumps);
4312
4313     /* Remove unnecessary boolean negates */
4314     OptOnePass (0x0080, OptNeg);
4315
4316     /* Replace jumps to an RTS by an RTS */
4317     OptOnePass (0x0100, OptJumpRTS);
4318
4319     /* Optimize boolean transforms */
4320     OptOnePass (0x0200, OptBoolTransforms);
4321
4322     /* Optimize compares */
4323     OptOnePass (0x0400, OptCompares2);
4324
4325     /* Remove unnecessary tests */
4326     OptOnePass (0x0800, OptTests);
4327
4328     /* Optimize several triples */
4329     OptOnePass (0x1000, OptTriples);
4330
4331     /* Optimize basic blocks */
4332     OptOnePass (0x2000, OptBlocks);
4333
4334     /* Remove unnecessary register loads (another pass) */
4335     OptOnePass (0x0008, OptRegLoads);
4336
4337     /* Optimize jumps */
4338     OptOnePass (0x4000, OptJumps);
4339
4340     /* Optimize jsr/rts sequences */
4341     OptOnePass (0x8000, OptRTS);
4342 }
4343
4344
4345