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