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