]> git.sur5r.net Git - cc65/blob - src/cc65/coptptrstore.c
Another optimization for array stores.
[cc65] / src / cc65 / coptptrstore.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                               coptptrstore.c                              */
4 /*                                                                           */
5 /*                      Optimize stores through pointers                     */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 2012,      Ullrich von Bassewitz                                      */
10 /*                Roemerstrasse 52                                           */
11 /*                D-70794 Filderstadt                                        */
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 <string.h>
37
38 /* common */
39 #include "xmalloc.h"
40
41 /* cc65 */
42 #include "codeent.h"
43 #include "codeinfo.h"
44 #include "coptptrstore.h"
45
46
47
48 /*****************************************************************************/
49 /*                             Helper functions                              */
50 /*****************************************************************************/
51
52
53
54 static unsigned OptPtrStore1Sub (CodeSeg* S, unsigned I, CodeEntry** const L)
55 /* Check if this is one of the allowed suboperation for OptPtrStore1 */
56 {
57     /* Check for a label attached to the entry */
58     if (CE_HasLabel (L[0])) {
59         return 0;
60     }
61
62     /* Check for single insn sub ops */
63     if (L[0]->OPC == OP65_AND                                           ||
64         L[0]->OPC == OP65_EOR                                           ||
65         L[0]->OPC == OP65_ORA                                           ||
66         (L[0]->OPC == OP65_JSR && strncmp (L[0]->Arg, "shlax", 5) == 0) ||
67         (L[0]->OPC == OP65_JSR && strncmp (L[0]->Arg, "shrax", 5) == 0)) {
68
69         /* One insn */
70         return 1;
71
72     } else if (L[0]->OPC == OP65_CLC                      &&
73                (L[1] = CS_GetNextEntry (S, I)) != 0       &&
74                L[1]->OPC == OP65_ADC                      &&
75                !CE_HasLabel (L[1])) {
76         return 2;
77     } else if (L[0]->OPC == OP65_SEC                      &&
78                (L[1] = CS_GetNextEntry (S, I)) != 0       &&
79                L[1]->OPC == OP65_SBC                      &&
80                !CE_HasLabel (L[1])) {
81         return 2;
82     }
83
84
85
86     /* Not found */
87     return 0;
88 }
89
90
91
92 static const char* ZPLoadAX (CodeSeg* S, unsigned I)
93 /* If the two instructions at S/I are a load of A/X from a two byte zero byte
94  * location, return the name of the zero page location. Otherwise return NULL.
95  */
96 {
97     CodeEntry* L[2];
98     unsigned Len;
99
100     if (CS_GetEntries (S, L, I, 2)                              &&
101         L[0]->OPC == OP65_LDA                                   &&
102         L[0]->AM == AM65_ZP                                     &&
103         L[1]->OPC == OP65_LDX                                   &&
104         L[1]->AM == AM65_ZP                                     &&
105         !CE_HasLabel (L[1])                                     &&
106         (Len = strlen (L[0]->Arg)) == strlen (L[1]->Arg) - 2    &&
107         memcmp (L[0]->Arg, L[1]->Arg, Len) == 0                 &&
108         L[1]->Arg[Len] == '+'                                   &&
109         L[1]->Arg[Len+1] == '1') {
110
111         /* Return the label */
112         return L[0]->Arg;
113
114     } else {
115
116         /* Not found */
117         return 0;
118
119     }
120 }
121
122
123
124 /*****************************************************************************/
125 /*                                   Code                                    */
126 /*****************************************************************************/
127
128
129
130 unsigned OptPtrStore1 (CodeSeg* S)
131 /* Search for the sequence:
132  *
133  *      lda     #<(label+0)
134  *      ldx     #>(label+0)
135  *      clc
136  *      adc     xxx
137  *      bcc     L
138  *      inx
139  * L:   jsr     pushax
140  *      ldx     #$00
141  *      lda     yyy
142  *      ldy     #$00
143  *      jsr     staspidx
144  *
145  * and replace it by:
146  *
147  *      ldy     xxx
148  *      ldx     #$00
149  *      lda     yyy
150  *      sta     label,y
151  */
152 {
153     unsigned Changes = 0;
154
155     /* Walk over the entries */
156     unsigned I = 0;
157     while (I < CS_GetEntryCount (S)) {
158
159         CodeEntry* L[11];
160         unsigned Len;
161
162         /* Get next entry */
163         L[0] = CS_GetEntry (S, I);
164
165         /* Check for the sequence */
166         if (L[0]->OPC == OP65_LDA                            &&
167             L[0]->AM == AM65_IMM                             &&
168             CS_GetEntries (S, L+1, I+1, 10)                  &&
169             L[1]->OPC == OP65_LDX                            &&
170             L[1]->AM == AM65_IMM                             &&
171             L[2]->OPC == OP65_CLC                            &&
172             L[3]->OPC == OP65_ADC                            &&
173             (L[3]->AM == AM65_ABS || L[3]->AM == AM65_ZP)    &&
174             (L[4]->OPC == OP65_BCC || L[4]->OPC == OP65_JCC) &&
175             L[4]->JumpTo != 0                                &&
176             L[4]->JumpTo->Owner == L[6]                      &&
177             L[5]->OPC == OP65_INX                            &&
178             CE_IsCallTo (L[6], "pushax")                     &&
179             L[7]->OPC == OP65_LDX                            &&
180             L[8]->OPC == OP65_LDA                            &&
181             L[9]->OPC == OP65_LDY                            &&
182             CE_IsKnownImm (L[9], 0)                          &&
183             CE_IsCallTo (L[10], "staspidx")                  &&
184             !CS_RangeHasLabel (S, I+1, 5)                    &&
185             !CS_RangeHasLabel (S, I+7, 4)                    &&
186             /* Check the label last because this is quite costly */
187             (Len = strlen (L[0]->Arg)) > 3                   &&
188             L[0]->Arg[0] == '<'                              &&
189             L[0]->Arg[1] == '('                              &&
190             strlen (L[1]->Arg) == Len                        &&
191             L[1]->Arg[0] == '>'                              &&
192             memcmp (L[0]->Arg+1, L[1]->Arg+1, Len-1) == 0) {
193
194             CodeEntry* X;
195             char* Label;
196
197             /* We will create all the new stuff behind the current one so
198              * we keep the line references.
199              */
200             X = NewCodeEntry (OP65_LDY, L[3]->AM, L[3]->Arg, 0, L[0]->LI);
201             CS_InsertEntry (S, X, I+11);
202
203             X = NewCodeEntry (OP65_LDX, L[7]->AM, L[7]->Arg, 0, L[7]->LI);
204             CS_InsertEntry (S, X, I+12);
205
206             X = NewCodeEntry (OP65_LDA, L[8]->AM, L[8]->Arg, 0, L[8]->LI);
207             CS_InsertEntry (S, X, I+13);
208
209             Label = memcpy (xmalloc (Len-2), L[0]->Arg+2, Len-3);
210             Label[Len-3] = '\0';
211             X = NewCodeEntry (OP65_STA, AM65_ABSY, Label, 0, L[10]->LI);
212             CS_InsertEntry (S, X, I+14);
213             xfree (Label);
214
215             /* Remove the old code */
216             CS_DelEntries (S, I, 11);
217
218             /* Remember, we had changes */
219             ++Changes;
220
221         }
222
223         /* Next entry */
224         ++I;
225
226     }
227
228     /* Return the number of changes made */
229     return Changes;
230 }
231
232
233
234 unsigned OptPtrStore2 (CodeSeg* S)
235 /* Search for the sequence:
236  *
237  *      lda     #<(label+0)
238  *      ldx     #>(label+0)
239  *      ldy     aaa
240  *      clc
241  *      adc     (sp),y
242  *      bcc     L
243  *      inx
244  * L:   jsr     pushax
245  *      ldx     #$00
246  *      lda     yyy
247  *      ldy     #$00
248  *      jsr     staspidx
249  *
250  * and replace it by:
251  *
252  *      ldy     aaa
253  *      ldx     #$00
254  *      lda     (sp),y
255  *      tay
256  *      lda     yyy
257  *      sta     label,y
258  */
259 {
260     unsigned Changes = 0;
261
262     /* Walk over the entries */
263     unsigned I = 0;
264     while (I < CS_GetEntryCount (S)) {
265
266         CodeEntry* L[12];
267         unsigned Len;
268
269         /* Get next entry */
270         L[0] = CS_GetEntry (S, I);
271
272         /* Check for the sequence */
273         if (L[0]->OPC == OP65_LDA                            &&
274             L[0]->AM == AM65_IMM                             &&
275             CS_GetEntries (S, L+1, I+1, 11)                  &&
276             L[1]->OPC == OP65_LDX                            &&
277             L[1]->AM == AM65_IMM                             &&
278             L[2]->OPC == OP65_LDY                            &&
279             L[3]->OPC == OP65_CLC                            &&
280             L[4]->OPC == OP65_ADC                            &&
281             L[4]->AM == AM65_ZP_INDY                         &&
282             (L[5]->OPC == OP65_BCC || L[5]->OPC == OP65_JCC) &&
283             L[5]->JumpTo != 0                                &&
284             L[5]->JumpTo->Owner == L[7]                      &&
285             L[6]->OPC == OP65_INX                            &&
286             CE_IsCallTo (L[7], "pushax")                     &&
287             L[8]->OPC == OP65_LDX                            &&
288             L[9]->OPC == OP65_LDA                            &&
289             L[10]->OPC == OP65_LDY                           &&
290             CE_IsKnownImm (L[10], 0)                         &&
291             CE_IsCallTo (L[11], "staspidx")                  &&
292             !CS_RangeHasLabel (S, I+1, 6)                    &&
293             !CS_RangeHasLabel (S, I+8, 4)                    &&
294             /* Check the label last because this is quite costly */
295             (Len = strlen (L[0]->Arg)) > 3                   &&
296             L[0]->Arg[0] == '<'                              &&
297             L[0]->Arg[1] == '('                              &&
298             strlen (L[1]->Arg) == Len                        &&
299             L[1]->Arg[0] == '>'                              &&
300             memcmp (L[0]->Arg+1, L[1]->Arg+1, Len-1) == 0) {
301
302             CodeEntry* X;
303             char* Label;
304
305             /* We will create all the new stuff behind the current one so
306              * we keep the line references.
307              */
308             X = NewCodeEntry (OP65_LDY, L[2]->AM, L[2]->Arg, 0, L[2]->LI);
309             CS_InsertEntry (S, X, I+12);
310
311             X = NewCodeEntry (OP65_LDX, L[8]->AM, L[8]->Arg, 0, L[8]->LI);
312             CS_InsertEntry (S, X, I+13);
313
314             X = NewCodeEntry (OP65_LDA, L[4]->AM, L[4]->Arg, 0, L[4]->LI);
315             CS_InsertEntry (S, X, I+14);
316
317             X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, L[4]->LI);
318             CS_InsertEntry (S, X, I+15);
319
320             X = NewCodeEntry (OP65_LDA, L[9]->AM, L[9]->Arg, 0, L[9]->LI);
321             CS_InsertEntry (S, X, I+16);
322
323             Label = memcpy (xmalloc (Len-2), L[0]->Arg+2, Len-3);
324             Label[Len-3] = '\0';
325             X = NewCodeEntry (OP65_STA, AM65_ABSY, Label, 0, L[11]->LI);
326             CS_InsertEntry (S, X, I+17);
327             xfree (Label);
328
329             /* Remove the old code */
330             CS_DelEntries (S, I, 12);
331
332             /* Remember, we had changes */
333             ++Changes;
334
335         }
336
337         /* Next entry */
338         ++I;
339
340     }
341
342     /* Return the number of changes made */
343     return Changes;
344 }
345
346
347
348 unsigned OptPtrStore3 (CodeSeg* S)
349 /* Search for the sequence:
350  *
351  *      lda     #<(label+0)
352  *      ldx     #>(label+0)
353  *      ldy     aaa
354  *      clc
355  *      adc     (sp),y
356  *      bcc     L
357  *      inx
358  * L:   jsr     pushax
359  *      ldy     #bbb
360  *      ldx     #$00
361  *      lda     (sp),y
362  *      ldy     #$00
363  *      jsr     staspidx
364  *
365  * and replace it by:
366  *
367  *      ldy     aaa
368  *      lda     (sp),y
369  *      tax
370  *      ldy     #bbb-2
371  *      lda     (sp),y
372  *      sta     label,x
373  *      ldx     #$00
374  */
375 {
376     unsigned Changes = 0;
377
378     /* Walk over the entries */
379     unsigned I = 0;
380     while (I < CS_GetEntryCount (S)) {
381
382         CodeEntry* L[13];
383         unsigned Len;
384
385         /* Get next entry */
386         L[0] = CS_GetEntry (S, I);
387
388         /* Check for the sequence */
389         if (L[0]->OPC == OP65_LDA                            &&
390             L[0]->AM == AM65_IMM                             &&
391             CS_GetEntries (S, L+1, I+1, 12)                  &&
392             L[1]->OPC == OP65_LDX                            &&
393             L[1]->AM == AM65_IMM                             &&
394             L[2]->OPC == OP65_LDY                            &&
395             L[3]->OPC == OP65_CLC                            &&
396             L[4]->OPC == OP65_ADC                            &&
397             L[4]->AM == AM65_ZP_INDY                         &&
398             (L[5]->OPC == OP65_BCC || L[5]->OPC == OP65_JCC) &&
399             L[5]->JumpTo != 0                                &&
400             L[5]->JumpTo->Owner == L[7]                      &&
401             L[6]->OPC == OP65_INX                            &&
402             CE_IsCallTo (L[7], "pushax")                     &&
403             L[8]->OPC == OP65_LDY                            &&
404             CE_IsConstImm (L[8])                             &&
405             L[8]->Num >= 2                                   &&
406             L[9]->OPC == OP65_LDX                            &&
407             L[10]->OPC == OP65_LDA                           &&
408             L[10]->AM == AM65_ZP_INDY                        &&
409             L[11]->OPC == OP65_LDY                           &&
410             CE_IsKnownImm (L[11], 0)                         &&
411             CE_IsCallTo (L[12], "staspidx")                  &&
412             !CS_RangeHasLabel (S, I+1, 6)                    &&
413             !CS_RangeHasLabel (S, I+8, 5)                    &&
414             /* Check the label last because this is quite costly */
415             (Len = strlen (L[0]->Arg)) > 3                   &&
416             L[0]->Arg[0] == '<'                              &&
417             L[0]->Arg[1] == '('                              &&
418             strlen (L[1]->Arg) == Len                        &&
419             L[1]->Arg[0] == '>'                              &&
420             memcmp (L[0]->Arg+1, L[1]->Arg+1, Len-1) == 0) {
421
422             CodeEntry* X;
423             const char* Arg;
424             char* Label;
425
426             /* We will create all the new stuff behind the current one so
427              * we keep the line references.
428              */
429             X = NewCodeEntry (OP65_LDY, L[2]->AM, L[2]->Arg, 0, L[2]->LI);
430             CS_InsertEntry (S, X, I+13);
431
432             X = NewCodeEntry (OP65_LDA, L[4]->AM, L[4]->Arg, 0, L[4]->LI);
433             CS_InsertEntry (S, X, I+14);
434
435             X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, L[4]->LI);
436             CS_InsertEntry (S, X, I+15);
437
438             Arg = MakeHexArg (L[8]->Num - 2);
439             X = NewCodeEntry (OP65_LDY, L[8]->AM, Arg, 0, L[8]->LI);
440             CS_InsertEntry (S, X, I+16);
441
442             X = NewCodeEntry (OP65_LDA, L[10]->AM, L[10]->Arg, 0, L[10]->LI);
443             CS_InsertEntry (S, X, I+17);
444
445             Label = memcpy (xmalloc (Len-2), L[0]->Arg+2, Len-3);
446             Label[Len-3] = '\0';
447             X = NewCodeEntry (OP65_STA, AM65_ABSX, Label, 0, L[12]->LI);
448             CS_InsertEntry (S, X, I+18);
449             xfree (Label);
450
451             X = NewCodeEntry (OP65_LDX, L[9]->AM, L[9]->Arg, 0, L[9]->LI);
452             CS_InsertEntry (S, X, I+19);
453
454             /* Remove the old code */
455             CS_DelEntries (S, I, 13);
456
457             /* Remember, we had changes */
458             ++Changes;
459
460         }
461
462         /* Next entry */
463         ++I;
464
465     }
466
467     /* Return the number of changes made */
468     return Changes;
469 }
470
471
472
473 unsigned OptPtrStore4 (CodeSeg* S)
474 /* Search for the sequence:
475  *
476  *      clc
477  *      adc     xxx
478  *      bcc     L
479  *      inx
480  * L:   jsr     pushax
481  *      ldx     #$00
482  *      lda     yyy
483  *      ldy     #$00
484  *      jsr     staspidx
485  *
486  * and replace it by:
487  *
488  *      sta     ptr1
489  *      stx     ptr1+1
490  *      ldy     xxx
491  *      ldx     #$00
492  *      lda     yyy
493  *      sta     (ptr1),y
494  *
495  * In case a/x is loaded from the register bank before the clc, we can even
496  * use the register bank instead of ptr1.
497  */
498 {
499     unsigned Changes = 0;
500
501     /* Walk over the entries */
502     unsigned I = 0;
503     while (I < CS_GetEntryCount (S)) {
504
505         CodeEntry* L[9];
506
507         /* Get next entry */
508         L[0] = CS_GetEntry (S, I);
509
510         /* Check for the sequence */
511         if (L[0]->OPC == OP65_CLC                               &&
512             CS_GetEntries (S, L+1, I+1, 8)                      &&
513             L[1]->OPC == OP65_ADC                               &&
514             (L[1]->AM == AM65_ABS                       ||
515              L[1]->AM == AM65_ZP                        ||
516              L[1]->AM == AM65_IMM)                              &&
517             (L[2]->OPC == OP65_BCC || L[2]->OPC == OP65_JCC)    &&
518             L[2]->JumpTo != 0                                   &&
519             L[2]->JumpTo->Owner == L[4]                         &&
520             L[3]->OPC == OP65_INX                               &&
521             CE_IsCallTo (L[4], "pushax")                        &&
522             L[5]->OPC == OP65_LDX                               &&
523             L[6]->OPC == OP65_LDA                               &&
524             L[7]->OPC == OP65_LDY                               &&
525             CE_IsKnownImm (L[7], 0)                             &&
526             CE_IsCallTo (L[8], "staspidx")                      &&
527             !CS_RangeHasLabel (S, I+1, 3)                       &&
528             !CS_RangeHasLabel (S, I+5, 4)) {
529
530             CodeEntry* X;
531             const char* ZPLoc;
532
533             /* Track the insertion point */
534             unsigned IP = I + 9;
535
536             /* If the sequence is preceeded by a load of a ZP value, we can
537              * use this ZP value as a pointer.
538              */
539             if (I < 2 || (ZPLoc = ZPLoadAX (S, I-2)) == 0) {
540
541                 ZPLoc = "ptr1";
542
543                 /* Must use ptr1 */
544                 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[8]->LI);
545                 CS_InsertEntry (S, X, IP++);
546
547                 X = NewCodeEntry (OP65_STX, AM65_ZP, "ptr1+1", 0, L[8]->LI);
548                 CS_InsertEntry (S, X, IP++);
549
550             }
551
552             X = NewCodeEntry (OP65_LDY, L[1]->AM, L[1]->Arg, 0, L[1]->LI);
553             CS_InsertEntry (S, X, IP++);
554
555             X = NewCodeEntry (OP65_LDX, L[5]->AM, L[5]->Arg, 0, L[5]->LI);
556             CS_InsertEntry (S, X, IP++);
557
558             X = NewCodeEntry (OP65_LDA, L[6]->AM, L[6]->Arg, 0, L[6]->LI);
559             CS_InsertEntry (S, X, IP++);
560
561             X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, ZPLoc, 0, L[8]->LI);
562             CS_InsertEntry (S, X, IP++);
563
564             /* Remove the old code */
565             CS_DelEntries (S, I, 9);
566
567             /* Remember, we had changes */
568             ++Changes;
569
570         }
571
572         /* Next entry */
573         ++I;
574
575     }
576
577     /* Return the number of changes made */
578     return Changes;
579 }
580
581
582
583 unsigned OptPtrStore5 (CodeSeg* S)
584 /* Search for the sequence:
585  *
586  *      clc
587  *      adc     xxx
588  *      bcc     L
589  *      inx
590  * L:   jsr     pushax
591  *      ldy     yyy
592  *      ldx     #$00
593  *      lda     (sp),y
594  *      ldy     #$00
595  *      jsr     staspidx
596  *
597  * and replace it by:
598  *
599  *      sta     ptr1
600  *      stx     ptr1+1
601  *      ldy     yyy-2
602  *      ldx     #$00
603  *      lda     (sp),y
604  *      ldy     xxx
605  *      sta     (ptr1),y
606  *
607  * In case a/x is loaded from the register bank before the clc, we can even
608  * use the register bank instead of ptr1.
609  */
610 {
611     unsigned Changes = 0;
612
613     /* Walk over the entries */
614     unsigned I = 0;
615     while (I < CS_GetEntryCount (S)) {
616
617         CodeEntry* L[10];
618
619         /* Get next entry */
620         L[0] = CS_GetEntry (S, I);
621
622         /* Check for the sequence */
623         if (L[0]->OPC == OP65_CLC                               &&
624             CS_GetEntries (S, L+1, I+1, 9)                      &&
625             L[1]->OPC == OP65_ADC                               &&
626             (L[1]->AM == AM65_ABS                       ||
627              L[1]->AM == AM65_ZP                        ||
628              L[1]->AM == AM65_IMM)                              &&
629             (L[2]->OPC == OP65_BCC || L[2]->OPC == OP65_JCC)    &&
630             L[2]->JumpTo != 0                                   &&
631             L[2]->JumpTo->Owner == L[4]                         &&
632             L[3]->OPC == OP65_INX                               &&
633             CE_IsCallTo (L[4], "pushax")                        &&
634             L[5]->OPC == OP65_LDY                               &&
635             CE_IsConstImm (L[5])                                &&
636             L[6]->OPC == OP65_LDX                               &&
637             L[7]->OPC == OP65_LDA                               &&
638             L[7]->AM == AM65_ZP_INDY                            &&
639             strcmp (L[7]->Arg, "sp") == 0                       &&
640             L[8]->OPC == OP65_LDY                               &&
641             (L[8]->AM == AM65_ABS                       ||
642              L[8]->AM == AM65_ZP                        ||
643              L[8]->AM == AM65_IMM)                              &&
644             CE_IsCallTo (L[9], "staspidx")                      &&
645             !CS_RangeHasLabel (S, I+1, 3)                       &&
646             !CS_RangeHasLabel (S, I+5, 5)) {
647
648             CodeEntry* X;
649             const char* Arg;
650             const char* ZPLoc;
651
652             /* Track the insertion point */
653             unsigned IP = I + 10;
654
655             /* If the sequence is preceeded by a load of a ZP value, we can
656              * use this ZP value as a pointer.
657              */
658             if (I < 2 || (ZPLoc = ZPLoadAX (S, I-2)) == 0) {
659
660                 ZPLoc = "ptr1";
661
662                 /* Must use ptr1 */
663                 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[9]->LI);
664                 CS_InsertEntry (S, X, IP++);
665
666                 X = NewCodeEntry (OP65_STX, AM65_ZP, "ptr1+1", 0, L[9]->LI);
667                 CS_InsertEntry (S, X, IP++);
668
669             }
670
671             Arg = MakeHexArg (L[5]->Num - 2);
672             X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, L[5]->LI);
673             CS_InsertEntry (S, X, IP++);
674
675             X = NewCodeEntry (OP65_LDX, L[6]->AM, L[6]->Arg, 0, L[6]->LI);
676             CS_InsertEntry (S, X, IP++);
677
678             X = NewCodeEntry (OP65_LDA, L[7]->AM, L[7]->Arg, 0, L[7]->LI);
679             CS_InsertEntry (S, X, IP++);
680
681             X = NewCodeEntry (OP65_LDY, L[1]->AM, L[1]->Arg, 0, L[1]->LI);
682             CS_InsertEntry (S, X, IP++);
683
684             X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, ZPLoc, 0, L[9]->LI);
685             CS_InsertEntry (S, X, IP++);
686
687             /* Remove the old code */
688             CS_DelEntries (S, I, 10);
689
690             /* Remember, we had changes */
691             ++Changes;
692
693         }
694
695         /* Next entry */
696         ++I;
697
698     }
699
700     /* Return the number of changes made */
701     return Changes;
702 }
703
704
705
706 unsigned OptPtrStore6 (CodeSeg* S)
707 /* Search for the sequence:
708  *
709  *      jsr     pushax
710  *      ldy     xxx
711  *      jsr     ldauidx
712  *      subop
713  *      ldy     yyy
714  *      jsr     staspidx
715  *
716  * and replace it by:
717  *
718  *      sta     ptr1
719  *      stx     ptr1+1
720  *      ldy     xxx
721  *      ldx     #$00
722  *      lda     (ptr1),y
723  *      subop
724  *      ldy     yyy
725  *      sta     (ptr1),y
726  *
727  * In case a/x is loaded from the register bank before the pushax, we can even
728  * use the register bank instead of ptr1.
729  *
730  */
731 {
732     unsigned Changes = 0;
733
734     /* Walk over the entries */
735     unsigned I = 0;
736     while (I < CS_GetEntryCount (S)) {
737
738         unsigned K;
739         CodeEntry* L[10];
740
741         /* Get next entry */
742         L[0] = CS_GetEntry (S, I);
743
744         /* Check for the sequence */
745         if (CE_IsCallTo (L[0], "pushax")            &&
746             CS_GetEntries (S, L+1, I+1, 3)          &&
747             L[1]->OPC == OP65_LDY                   &&
748             CE_IsConstImm (L[1])                    &&
749             !CE_HasLabel (L[1])                     &&
750             CE_IsCallTo (L[2], "ldauidx")           &&
751             !CE_HasLabel (L[2])                     &&
752             (K = OptPtrStore1Sub (S, I+3, L+3)) > 0 &&
753             CS_GetEntries (S, L+3+K, I+3+K, 2)      &&
754             L[3+K]->OPC == OP65_LDY                 &&
755             CE_IsConstImm (L[3+K])                  &&
756             !CE_HasLabel (L[3+K])                   &&
757             CE_IsCallTo (L[4+K], "staspidx")        &&
758             !CE_HasLabel (L[4+K])) {
759
760
761             const char* RegBank = 0;
762             const char* ZPLoc   = "ptr1";
763             CodeEntry* X;
764
765
766             /* Get the preceeding two instructions and check them. We check
767              * for:
768              *          lda     regbank+n
769              *          ldx     regbank+n+1
770              */
771             if (I > 1) {
772                 CodeEntry* P[2];
773                 P[0] = CS_GetEntry (S, I-2);
774                 P[1] = CS_GetEntry (S, I-1);
775                 if (P[0]->OPC == OP65_LDA &&
776                     P[0]->AM  == AM65_ZP  &&
777                     P[1]->OPC == OP65_LDX &&
778                     P[1]->AM  == AM65_ZP  &&
779                     !CE_HasLabel (P[1])   &&
780                     strncmp (P[0]->Arg, "regbank+", 8) == 0) {
781
782                     unsigned Len = strlen (P[0]->Arg);
783
784                     if (strncmp (P[0]->Arg, P[1]->Arg, Len) == 0 &&
785                         P[1]->Arg[Len+0] == '+'                  &&
786                         P[1]->Arg[Len+1] == '1'                  &&
787                         P[1]->Arg[Len+2] == '\0') {
788
789                         /* Ok, found. Use the name of the register bank */
790                         RegBank = ZPLoc = P[0]->Arg;
791                     }
792                 }
793             }
794
795             /* Insert the load via the zp pointer */
796             X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[3]->LI);
797             CS_InsertEntry (S, X, I+3);
798             X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, ZPLoc, 0, L[2]->LI);
799             CS_InsertEntry (S, X, I+4);
800
801             /* Insert the store through the zp pointer */
802             X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, ZPLoc, 0, L[3]->LI);
803             CS_InsertEntry (S, X, I+6+K);
804
805             /* Delete the old code */
806             CS_DelEntry (S, I+7+K);     /* jsr spaspidx */
807             CS_DelEntry (S, I+2);       /* jsr ldauidx */
808
809             /* Create and insert the stores into the zp pointer if needed */
810             if (RegBank == 0) {
811                 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
812                 CS_InsertEntry (S, X, I+1);
813                 X = NewCodeEntry (OP65_STX, AM65_ZP, "ptr1+1", 0, L[0]->LI);
814                 CS_InsertEntry (S, X, I+2);
815             }
816
817             /* Delete more old code. Do it here to keep a label attached to
818              * entry I in place.
819              */
820             CS_DelEntry (S, I);         /* jsr pushax */
821
822             /* Remember, we had changes */
823             ++Changes;
824
825         }
826
827         /* Next entry */
828         ++I;
829
830     }
831
832     /* Return the number of changes made */
833     return Changes;
834 }
835
836
837