]> git.sur5r.net Git - cc65/blob - src/cc65/coptstop.c
Minor improvement of optimizations
[cc65] / src / cc65 / coptstop.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                 coptstop.c                                */
4 /*                                                                           */
5 /*           Optimize operations that take operands via the stack            */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 2001-2002 Ullrich von Bassewitz                                       */
10 /*               Wacholderweg 14                                             */
11 /*               D-70597 Stuttgart                                           */
12 /* EMail:        uz@cc65.org                                                 */
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 <stdlib.h>
37
38 /* cc65 */
39 #include "codeent.h"
40 #include "codeinfo.h"
41 #include "codeopt.h"
42 #include "error.h"
43 #include "coptstop.h"
44
45
46
47 /*****************************************************************************/
48 /*                                   Data                                    */
49 /*****************************************************************************/
50
51
52
53 /* Flags returned by DirectOp */
54 #define OP_DIRECT       0x01            /* Direct op may be used */
55 #define OP_ONSTACK      0x02            /* Operand is on stack */
56
57
58
59 /*****************************************************************************/
60 /*                                  Helpers                                  */
61 /*****************************************************************************/
62
63
64
65 static unsigned AdjustStackOffset (CodeSeg* S, unsigned Start, unsigned Stop,
66                                    unsigned Offs)
67 /* Adjust the offset for all stack accesses in the range Start to Stop, both
68  * inclusive. The function returns the number of instructions that have been
69  * inserted.
70  */
71 {
72     /* Number of inserted instructions */
73     unsigned Inserted = 0;
74
75     /* Walk over all entries */
76     unsigned I = Start;
77     while (I <= Stop) {
78
79         CodeEntry* E = CS_GetEntry (S, I);
80
81         if (E->Use & REG_SP) {
82
83             CodeEntry* P;
84
85             /* Check for some things that should not happen */
86             CHECK (E->AM == AM65_ZP_INDY || E->RI->In.RegY >= (short) Offs);
87             CHECK (strcmp (E->Arg, "sp") == 0);
88
89             /* Get the code entry before this one. If it's a LDY, adjust the
90              * value.
91              */
92             P = CS_GetPrevEntry (S, I);
93             if (P && P->OPC == OP65_LDY && CE_KnownImm (P)) {
94
95                 /* The Y load is just before the stack access, adjust it */
96                 CE_SetNumArg (P, P->Num - Offs);
97
98             } else {
99
100                 /* Insert a new load instruction before the stack access */
101                 const char* Arg = MakeHexArg (E->RI->In.RegY - Offs);
102                 CodeEntry* X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
103                 CS_InsertEntry (S, X, I);
104
105                 /* One more inserted entries */
106                 ++Inserted;
107                 ++Stop;
108
109                 /* Be sure to skip the stack access for the next round */
110                 ++I;
111
112             }
113
114         }
115
116         /* Next entry */
117         ++I;
118     }
119
120     /* Return the number of inserted entries */
121     return Inserted;
122 }
123
124
125
126 static unsigned DirectOp (CodeEntry* E)
127 /* Check if the given entry is a lda instruction with an addressing mode
128  * that allows us to replace it by another operation (like ora). If so, we may
129  * use this location for the or and must not save the value in the zero
130  * page location.
131  */
132 {
133     unsigned Flags = 0;
134     if (E->OPC == OP65_LDA) {
135         if (E->AM == AM65_IMM || E->AM == AM65_ZP || E->AM == AM65_ABS) {
136             /* These insns are all ok and replaceable */
137             Flags |= OP_DIRECT;
138         } else if (E->AM == AM65_ZP_INDY &&
139                    E->RI->In.RegY >= 0   &&
140                    strcmp (E->Arg, "sp") == 0) {
141             /* Load from stack with known offset is also ok */
142             Flags |= (OP_DIRECT | OP_ONSTACK);
143         }
144     }
145     return Flags;
146 }
147
148
149
150 /*****************************************************************************/
151 /*                       Actual optimization functions                       */
152 /*****************************************************************************/
153
154
155
156 static unsigned Opt_staspidx (CodeSeg* S, unsigned Push, unsigned Store,
157                               const char* ZPLo, const char* ZPHi)
158 /* Optimize the staspidx sequence if possible */
159 {
160     CodeEntry* X;
161     CodeEntry* PushEntry;
162     CodeEntry* StoreEntry;
163
164     /* Get the push entry */
165     PushEntry = CS_GetEntry (S, Push);
166
167     /* Store the value into the zeropage instead of pushing it */
168     X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, PushEntry->LI);
169     CS_InsertEntry (S, X, Push+1);
170     X = NewCodeEntry (OP65_STX, AM65_ZP, ZPHi, 0, PushEntry->LI);
171     CS_InsertEntry (S, X, Push+2);
172
173     /* Correct the index of the store and get a pointer to the entry */
174     Store += 2;
175     StoreEntry = CS_GetEntry (S, Store);
176
177     /* Inline the store */
178     X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, ZPLo, 0, StoreEntry->LI);
179     CS_InsertEntry (S, X, Store+1);
180
181     /* Remove the push and the call to the staspidx function */
182     CS_DelEntry (S, Store);
183     CS_DelEntry (S, Push);
184
185     /* We changed the sequence */
186     return 1;
187 }
188
189
190
191 static unsigned Opt_staxspidx (CodeSeg* S, unsigned Push, unsigned Store,
192                                const char* ZPLo, const char* ZPHi)
193 /* Optimize the staxspidx sequence if possible */
194 {
195     CodeEntry* X;
196     CodeEntry* PushEntry;
197     CodeEntry* StoreEntry;
198
199     /* Get the push entry */
200     PushEntry = CS_GetEntry (S, Push);
201
202     /* Store the value into the zeropage instead of pushing it */
203     X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, PushEntry->LI);
204     CS_InsertEntry (S, X, Push+1);
205     X = NewCodeEntry (OP65_STX, AM65_ZP, ZPHi, 0, PushEntry->LI);
206     CS_InsertEntry (S, X, Push+2);
207
208     /* Correct the index of the store and get a pointer to the entry */
209     Store += 2;
210     StoreEntry = CS_GetEntry (S, Store);
211
212     /* Inline the store */
213     X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, ZPLo, 0, StoreEntry->LI);
214     CS_InsertEntry (S, X, Store+1);
215     X = NewCodeEntry (OP65_INY, AM65_IMP, 0, 0, StoreEntry->LI);
216     CS_InsertEntry (S, X, Store+2);
217     if (StoreEntry->RI->In.RegX >= 0) {
218         /* Value of X is known */
219         const char* Arg = MakeHexArg (StoreEntry->RI->In.RegX);
220         X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, StoreEntry->LI);
221     } else {
222         /* Value unknown */
223         X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, StoreEntry->LI);
224     }
225     CS_InsertEntry (S, X, Store+3);
226     X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, ZPLo, 0, StoreEntry->LI);
227     CS_InsertEntry (S, X, Store+4);
228
229     /* Remove the push and the call to the staspidx function */
230     CS_DelEntry (S, Store);
231     CS_DelEntry (S, Push);
232
233     /* We changed the sequence */
234     return 1;
235 }
236
237
238
239 static unsigned Opt_tosaddax (CodeSeg* S, unsigned Push, unsigned Add,
240                               const char* ZPLo, const char* ZPHi)
241 /* Optimize the tosaddax sequence if possible */
242 {
243     CodeEntry*  P;
244     CodeEntry*  N;
245     CodeEntry*  X;
246     CodeEntry*  PushEntry;
247     CodeEntry*  AddEntry;
248     unsigned    Flags;
249
250
251     /* We need the entry behind the add */
252     CHECK ((N = CS_GetNextEntry (S, Add)) != 0);
253
254     /* And the entry before the push */
255     CHECK ((P = CS_GetPrevEntry (S, Push)) != 0);
256
257     /* Get the push entry */
258     PushEntry = CS_GetEntry (S, Push);
259
260     /* Check the entry before the push. If it's a lda instruction with an
261      * addressing mode that allows us to replace it, we may use this
262      * location for the op and must not save the value in the zero page
263      * location.
264      */
265     Flags = DirectOp (P);
266
267     /* Store the value into the zeropage instead of pushing it */
268     X = NewCodeEntry (OP65_STX, AM65_ZP, ZPHi, 0, PushEntry->LI);
269     CS_InsertEntry (S, X, Push+1);
270     ++Add;      /* Correct the index */
271     if ((Flags & OP_DIRECT) == 0) {
272         X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, PushEntry->LI);
273         CS_InsertEntry (S, X, Push+1);
274         ++Add;  /* Correct the index */
275     }
276
277     /* Get a pointer to the add entry */
278     AddEntry = CS_GetEntry (S, Add);
279
280     /* Inline the add */
281     X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, AddEntry->LI);
282     CS_InsertEntry (S, X, Add+1);
283     if ((Flags & OP_DIRECT) != 0) {
284         /* Add a variable location. If the location is on the stack, we
285          * need to reload the Y register.
286          */
287         if ((Flags & OP_ONSTACK) != 0) {
288             X = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (P->RI->In.RegY), 0, AddEntry->LI);
289             CS_InsertEntry (S, X, Add);
290             ++Add;
291         }
292         X = NewCodeEntry (OP65_ADC, P->AM, P->Arg, 0, AddEntry->LI);
293     } else {
294         /* Add from temp storage */
295         X = NewCodeEntry (OP65_ADC, AM65_ZP, ZPLo, 0, AddEntry->LI);
296     }
297     CS_InsertEntry (S, X, Add+2);
298     if (PushEntry->RI->In.RegX == 0) {
299         /* The high byte is the value in X plus the carry */
300         CodeLabel* L = CS_GenLabel (S, N);
301         X = NewCodeEntry (OP65_BCC, AM65_BRA, L->Name, L, AddEntry->LI);
302         CS_InsertEntry (S, X, Add+3);
303         X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, AddEntry->LI);
304         CS_InsertEntry (S, X, Add+4);
305     } else if (AddEntry->RI->In.RegX == 0) {
306         /* The high byte is that of the first operand plus carry */
307         CodeLabel* L;
308         if (PushEntry->RI->In.RegX >= 0) {
309             /* Value of first op high byte is known */
310             const char* Arg = MakeHexArg (PushEntry->RI->In.RegX);
311             X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, AddEntry->LI);
312         } else {
313             /* Value of first op high byte is unknown */
314             X = NewCodeEntry (OP65_LDX, AM65_ZP, ZPHi, 0, AddEntry->LI);
315         }
316         CS_InsertEntry (S, X, Add+3);
317         L = CS_GenLabel (S, N);
318         X = NewCodeEntry (OP65_BCC, AM65_BRA, L->Name, L, AddEntry->LI);
319         CS_InsertEntry (S, X, Add+4);
320         X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, AddEntry->LI);
321         CS_InsertEntry (S, X, Add+5);
322     } else {
323         /* High byte is unknown */
324         X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, AddEntry->LI);
325         CS_InsertEntry (S, X, Add+3);
326         X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, AddEntry->LI);
327         CS_InsertEntry (S, X, Add+4);
328         X = NewCodeEntry (OP65_ADC, AM65_ZP, ZPHi, 0, AddEntry->LI);
329         CS_InsertEntry (S, X, Add+5);
330         X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, AddEntry->LI);
331         CS_InsertEntry (S, X, Add+6);
332         X = NewCodeEntry (OP65_LDA, AM65_ZP, ZPLo, 0, AddEntry->LI);
333         CS_InsertEntry (S, X, Add+7);
334     }
335
336     /* Remove the push and the call to the tosaddax function */
337     CS_DelEntry (S, Add);
338     CS_DelEntry (S, Push);
339
340     /* We changed the sequence */
341     return 1;
342 }
343
344
345
346 static unsigned Opt_tosandax (CodeSeg* S, unsigned Push, unsigned And,
347                               const char* ZPLo, const char* ZPHi)
348 /* Optimize the tosandax sequence if possible */
349 {
350     CodeEntry*  P;
351     CodeEntry*  X;
352     CodeEntry*  PushEntry;
353     CodeEntry*  AndEntry;
354     unsigned    Flags;
355
356     /* Get the entry before the push */
357     CHECK ((P = CS_GetPrevEntry (S, Push)) != 0);
358
359     /* Get the push entry */
360     PushEntry = CS_GetEntry (S, Push);
361
362     /* Check the entry before the push. If it's a lda instruction with an
363      * addressing mode that allows us to replace it, we may use this
364      * location for the op and must not save the value in the zero page
365      * location.
366      */
367     Flags = DirectOp (P);
368
369     /* Store the value into the zeropage instead of pushing it */
370     X = NewCodeEntry (OP65_STX, AM65_ZP, ZPHi, 0, PushEntry->LI);
371     CS_InsertEntry (S, X, Push+1);
372     ++And;      /* Correct the index */
373     if ((Flags & OP_DIRECT) == 0) {
374         X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, PushEntry->LI);
375         CS_InsertEntry (S, X, Push+1);
376         ++And;  /* Correct the index */
377     }
378
379     /* Get a pointer to the and entry */
380     AndEntry = CS_GetEntry (S, And);
381
382     /* Inline the and */
383     if ((Flags & OP_DIRECT) != 0) {
384         /* And with variable location. If the location is on the stack, we
385          * need to reload the Y register.
386          */
387         if ((Flags & OP_ONSTACK) != 0) {
388             X = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (P->RI->In.RegY), 0, AndEntry->LI);
389             CS_InsertEntry (S, X, And);
390             ++And;
391         }
392         X = NewCodeEntry (OP65_AND, P->AM, P->Arg, 0, AndEntry->LI);
393     } else {
394         /* And with temp storage */
395         X = NewCodeEntry (OP65_AND, AM65_ZP, ZPLo, 0, AndEntry->LI);
396     }
397     CS_InsertEntry (S, X, And+1);
398     if (PushEntry->RI->In.RegX == 0 || AndEntry->RI->In.RegX == 0) {
399         /* The high byte is zero */
400         X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, AndEntry->LI);
401         CS_InsertEntry (S, X, And+2);
402     } else {
403         /* High byte is unknown */
404         X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, AndEntry->LI);
405         CS_InsertEntry (S, X, And+2);
406         X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, AndEntry->LI);
407         CS_InsertEntry (S, X, And+3);
408         X = NewCodeEntry (OP65_AND, AM65_ZP, ZPHi, 0, AndEntry->LI);
409         CS_InsertEntry (S, X, And+4);
410         X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, AndEntry->LI);
411         CS_InsertEntry (S, X, And+5);
412         X = NewCodeEntry (OP65_LDA, AM65_ZP, ZPLo, 0, AndEntry->LI);
413         CS_InsertEntry (S, X, And+6);
414     }
415
416     /* Remove the push and the call to the tosandax function */
417     CS_DelEntry (S, And);
418     CS_DelEntry (S, Push);
419
420     /* We changed the sequence */
421     return 1;
422 }
423
424
425
426 static unsigned Opt_tosorax (CodeSeg* S, unsigned Push, unsigned Or,
427                              const char* ZPLo, const char* ZPHi)
428 /* Optimize the tosorax sequence if possible */
429 {
430     CodeEntry*  P;
431     CodeEntry*  X;
432     CodeEntry*  PushEntry;
433     CodeEntry*  OrEntry;
434     unsigned    Flags;
435
436     /* Get the entry before the push */
437     CHECK ((P = CS_GetPrevEntry (S, Push)) != 0);
438
439     /* Get the push entry */
440     PushEntry = CS_GetEntry (S, Push);
441
442     /* Check the entry before the push. If it's a lda instruction with an
443      * addressing mode that allows us to replace it, we may use this
444      * location for the op and must not save the value in the zero page
445      * location.
446      */
447     Flags = DirectOp (P);
448
449     /* Store the value into the zeropage instead of pushing it */
450     X = NewCodeEntry (OP65_STX, AM65_ZP, ZPHi, 0, PushEntry->LI);
451     CS_InsertEntry (S, X, Push+1);
452     ++Or;  /* Correct the index */
453     if ((Flags & OP_DIRECT) == 0) {
454         X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, PushEntry->LI);
455         CS_InsertEntry (S, X, Push+1);
456         ++Or;  /* Correct the index */
457     }
458
459     /* Get a pointer to the or entry */
460     OrEntry = CS_GetEntry (S, Or);
461
462     /* Inline the or */
463     if ((Flags & OP_DIRECT) != 0) {
464         /* Or with variable location. If the location is on the stack, we
465          * need to reload the Y register.
466          */
467         if ((Flags & OP_ONSTACK) != 0) {
468             X = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (P->RI->In.RegY), 0, OrEntry->LI);
469             CS_InsertEntry (S, X, Or);
470             ++Or;
471         }
472         X = NewCodeEntry (OP65_ORA, P->AM, P->Arg, 0, OrEntry->LI);
473     } else {
474         /* Or with temp storage */
475         X = NewCodeEntry (OP65_ORA, AM65_ZP, ZPLo, 0, OrEntry->LI);
476     }
477     CS_InsertEntry (S, X, Or+1);
478     if (PushEntry->RI->In.RegX >= 0 && OrEntry->RI->In.RegX >= 0) {
479         /* Both values known, precalculate the result */
480         const char* Arg = MakeHexArg (PushEntry->RI->In.RegX | OrEntry->RI->In.RegX);
481         X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, OrEntry->LI);
482         CS_InsertEntry (S, X, Or+2);
483     } else if (PushEntry->RI->In.RegX != 0) {
484         /* High byte is unknown */
485         X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, OrEntry->LI);
486         CS_InsertEntry (S, X, Or+2);
487         X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, OrEntry->LI);
488         CS_InsertEntry (S, X, Or+3);
489         X = NewCodeEntry (OP65_ORA, AM65_ZP, ZPHi, 0, OrEntry->LI);
490         CS_InsertEntry (S, X, Or+4);
491         X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, OrEntry->LI);
492         CS_InsertEntry (S, X, Or+5);
493         X = NewCodeEntry (OP65_LDA, AM65_ZP, ZPLo, 0, OrEntry->LI);
494         CS_InsertEntry (S, X, Or+6);
495     }
496
497     /* Remove the push and the call to the tosorax function */
498     CS_DelEntry (S, Or);
499     CS_DelEntry (S, Push);
500
501     /* We changed the sequence */
502     return 1;
503 }
504
505
506
507 static unsigned Opt_tosxorax (CodeSeg* S, unsigned Push, unsigned Xor,
508                               const char* ZPLo, const char* ZPHi)
509 /* Optimize the tosxorax sequence if possible */
510 {
511     CodeEntry*  P;
512     CodeEntry*  X;
513     CodeEntry*  PushEntry;
514     CodeEntry*  XorEntry;
515     unsigned    Flags;
516
517     /* Get the entry before the push */
518     CHECK ((P = CS_GetPrevEntry (S, Push)) != 0);
519
520     /* Get the push entry */
521     PushEntry = CS_GetEntry (S, Push);
522
523     /* Check the entry before the push. If it's a lda instruction with an
524      * addressing mode that allows us to replace it, we may use this
525      * location for the op and must not save the value in the zero page
526      * location.
527      */
528     Flags = DirectOp (P);
529
530     /* Store the value into the zeropage instead of pushing it */
531     X = NewCodeEntry (OP65_STX, AM65_ZP, ZPHi, 0, PushEntry->LI);
532     CS_InsertEntry (S, X, Push+1);
533     ++Xor;  /* Correct the index */
534     if ((Flags & OP_DIRECT) != 0) {
535         X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, PushEntry->LI);
536         CS_InsertEntry (S, X, Push+1);
537         ++Xor;  /* Correct the index */
538     }
539
540     /* Get a pointer to the entry */
541     XorEntry = CS_GetEntry (S, Xor);
542
543     /* Inline the xor */
544     if ((Flags & OP_DIRECT) != 0) {
545         /* Xor with a variable location. If the location is on the stack, we
546          * need to reload the Y register.
547          */
548         if ((Flags & OP_ONSTACK) != 0) {
549             X = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (P->RI->In.RegY), 0, XorEntry->LI);
550             CS_InsertEntry (S, X, Xor);
551             ++Xor;
552         }
553         X = NewCodeEntry (OP65_EOR, P->AM, P->Arg, 0, XorEntry->LI);
554     } else {
555         /* Xor with temp storage */
556         X = NewCodeEntry (OP65_EOR, AM65_ZP, ZPLo, 0, XorEntry->LI);
557     }
558     CS_InsertEntry (S, X, Xor+1);
559     if (PushEntry->RI->In.RegX >= 0 && XorEntry->RI->In.RegX >= 0) {
560         /* Both values known, precalculate the result */
561         const char* Arg = MakeHexArg (PushEntry->RI->In.RegX ^ XorEntry->RI->In.RegX);
562         X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, XorEntry->LI);
563         CS_InsertEntry (S, X, Xor+2);
564     } else if (PushEntry->RI->In.RegX != 0) {
565         /* High byte is unknown */
566         X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, XorEntry->LI);
567         CS_InsertEntry (S, X, Xor+2);
568         X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, XorEntry->LI);
569         CS_InsertEntry (S, X, Xor+3);
570         X = NewCodeEntry (OP65_EOR, AM65_ZP, ZPHi, 0, XorEntry->LI);
571         CS_InsertEntry (S, X, Xor+4);
572         X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, XorEntry->LI);
573         CS_InsertEntry (S, X, Xor+5);
574         X = NewCodeEntry (OP65_LDA, AM65_ZP, ZPLo, 0, XorEntry->LI);
575         CS_InsertEntry (S, X, Xor+6);
576     }
577
578     /* Remove the push and the call to the tosandax function */
579     CS_DelEntry (S, Xor);
580     CS_DelEntry (S, Push);
581
582     /* We changed the sequence */
583     return 1;
584 }
585
586
587
588 /*****************************************************************************/
589 /*                                   Code                                    */
590 /*****************************************************************************/
591
592
593
594 /* Flags for the functions */
595 typedef enum {
596     STOP_NONE,              /* Nothing special */
597     STOP_A_UNUSED           /* Call only if a unused later */
598 } STOP_FLAGS;
599
600
601 typedef unsigned (*OptFunc) (CodeSeg* S, unsigned Push, unsigned Store,
602                              const char* ZPLo, const char* ZPHi);
603 typedef struct OptFuncDesc OptFuncDesc;
604 struct OptFuncDesc {
605     const char*     Name;   /* Name of the replaced runtime function */
606     OptFunc         Func;   /* Function pointer */
607     STOP_FLAGS      Flags;  /* Flags */
608 };
609
610 static const OptFuncDesc FuncTable[] = {
611     { "staspidx",   Opt_staspidx,  STOP_NONE },
612     { "staxspidx",  Opt_staxspidx, STOP_A_UNUSED },
613     { "tosaddax",   Opt_tosaddax,  STOP_NONE },
614     { "tosandax",   Opt_tosandax,  STOP_NONE },
615     { "tosorax",    Opt_tosorax,   STOP_NONE },
616     { "tosxorax",   Opt_tosxorax,  STOP_NONE },
617 };
618 #define FUNC_COUNT (sizeof(FuncTable) / sizeof(FuncTable[0]))
619
620
621
622 static int CmpFunc (const void* Key, const void* Func)
623 /* Compare function for bsearch */
624 {
625     return strcmp (Key, ((const OptFuncDesc*) Func)->Name);
626 }
627
628
629
630 static const OptFuncDesc* FindFunc (const char* Name)
631 /* Find the function with the given name. Return a pointer to the table entry
632  * or NULL if the function was not found.
633  */
634 {
635     return bsearch (Name, FuncTable, FUNC_COUNT, sizeof(OptFuncDesc), CmpFunc);
636 }
637
638
639
640 /*****************************************************************************/
641 /*                                   Code                                    */
642 /*****************************************************************************/
643
644
645
646 unsigned OptStackOps (CodeSeg* S)
647 /* Optimize operations that take operands via the stack */
648 {
649     unsigned Changes = 0;     /* Number of changes in one run */
650     int      InSeq = 0;       /* Inside a sequence */
651     unsigned Push = 0;        /* Index of pushax */
652     unsigned UsedRegs = 0;    /* Zeropage registers used in sequence */
653     unsigned I;
654
655     /* Generate register info */
656     CS_GenRegInfo (S);
657
658     /* Look for a call to pushax followed by a call to some other function
659      * that takes it's first argument on the stack, and the second argument
660      * in the primary register.
661      * It depends on the code between the two if we can handle/transform the
662      * sequence, so check this code for the following list of things:
663      *
664      *  - there must not be a jump or conditional branch (this may
665      *    get relaxed later).
666      *  - there may not be accesses to local variables with unknown
667      *    offsets (because we have to adjust these offsets).
668      *  - no subroutine calls
669      *  - no jump labels
670      *
671      * Since we need a zero page register later, do also check the
672      * intermediate code for zero page use.
673      */
674     I = 0;
675     while (I < CS_GetEntryCount (S)) {
676
677         /* Get the next entry */
678         CodeEntry* E = CS_GetEntry (S, I);
679
680         /* Handling depends if we're inside a sequence or not */
681         if (InSeq) {
682
683             if ((E->Info & OF_BRA) != 0                              ||
684                 ((E->Use & REG_SP) != 0                         &&
685                  (E->AM != AM65_ZP_INDY || E->RI->In.RegY < 0))      ||
686                 CE_HasLabel (E)) {
687
688                 /* All this stuff is not allowed in a sequence */
689                 InSeq = 0;
690
691             } else if (E->OPC == OP65_JSR) {
692
693                 /* Subroutine call: Check if this is one of our functions */
694                 const OptFuncDesc* F = FindFunc (E->Arg);
695                 if (F) {
696
697                     const char* ZPLo = 0;
698                     const char* ZPHi = 0;
699                     int PreCondOk    = 1;
700
701                     /* Check the flags */
702                     if (F->Flags & STOP_A_UNUSED) {
703                         /* a must be unused later */
704                         if (RegAUsed (S, I+1)) {
705                             /* Cannot optimize */
706                             PreCondOk = 0;
707                         }
708                     }
709
710                     /* Determine the zero page locations to use */
711                     if (PreCondOk) {
712                         UsedRegs |= GetRegInfo (S, I+1, REG_SREG | REG_PTR1 | REG_PTR2);
713                         if ((UsedRegs & REG_SREG) == REG_NONE) {
714                             /* SREG is available */
715                             ZPLo = "sreg";
716                             ZPHi = "sreg+1";
717                         } else if ((UsedRegs & REG_PTR1) == REG_NONE) {
718                             ZPLo = "ptr1";
719                             ZPHi = "ptr1+1";
720                         } else if ((UsedRegs & REG_PTR2) == REG_NONE) {
721                             ZPLo = "ptr2";
722                             ZPHi = "ptr2+1";
723                         } else {
724                             /* No registers available */
725                             PreCondOk = 0;
726                         }
727                     }
728
729                     /* If preconditions are ok, call the optimizer function */
730                     if (PreCondOk) {
731
732                         /* Adjust stack offsets */
733                         unsigned Op = I + AdjustStackOffset (S, Push, I, 2);
734
735                         /* Call the optimizer function */
736                         Changes += F->Func (S, Push, Op, ZPLo, ZPHi);
737
738                         /* Regenerate register info */
739                         CS_GenRegInfo (S);
740                     }
741
742                     /* End of sequence */
743                     InSeq = 0;
744
745                 } else if (strcmp (E->Arg, "pushax") == 0) {
746                     /* Restart the sequence */
747                     Push     = I;
748                     UsedRegs = REG_NONE;
749                 } else {
750                     /* A call to an unkown subroutine ends the sequence */
751                     InSeq = 0;
752                 }
753
754             } else {
755
756                 /* Other stuff: Track zeropage register usage */
757                 UsedRegs |= (E->Use | E->Chg);
758
759             }
760
761         } else if (CE_IsCall (E, "pushax")) {
762
763             /* This starts a sequence */
764             Push     = I;
765             UsedRegs = REG_NONE;
766             InSeq    = 1;
767
768         }
769
770         /* Next entry */
771         ++I;
772
773     }
774
775     /* Free the register info */
776     CS_FreeRegInfo (S);
777
778     /* Return the number of changes made */
779     return Changes;
780 }
781
782
783