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