]> git.sur5r.net Git - cc65/blob - src/cc65/coptptrstore.c
add gotox, gotoy, and gotoxy
[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 "chartype.h"
40 #include "strbuf.h"
41 #include "xmalloc.h"
42 #include "xsprintf.h"
43
44 /* cc65 */
45 #include "codeent.h"
46 #include "codeinfo.h"
47 #include "coptptrstore.h"
48
49
50
51 /*****************************************************************************/
52 /*                             Helper functions                              */
53 /*****************************************************************************/
54
55
56
57 static unsigned OptPtrStore1Sub (CodeSeg* S, unsigned I, CodeEntry** const L)
58 /* Check if this is one of the allowed suboperation for OptPtrStore1 */
59 {
60     /* Check for a label attached to the entry */
61     if (CE_HasLabel (L[0])) {
62         return 0;
63     }
64
65     /* Check for single insn sub ops */
66     if (L[0]->OPC == OP65_AND                                           ||
67         L[0]->OPC == OP65_EOR                                           ||
68         L[0]->OPC == OP65_ORA                                           ||
69         (L[0]->OPC == OP65_JSR                          &&
70          (strncmp (L[0]->Arg, "shlax", 5) == 0  ||
71           strncmp (L[0]->Arg, "shrax", 5) == 0)         &&
72          strlen (L[0]->Arg) == 6                        &&
73          IsDigit (L[0]->Arg[5]))) {
74
75         /* One insn */
76         return 1;
77
78     } else if (L[0]->OPC == OP65_CLC                      &&
79                (L[1] = CS_GetNextEntry (S, I)) != 0       &&
80                L[1]->OPC == OP65_ADC                      &&
81                !CE_HasLabel (L[1])) {
82         return 2;
83     } else if (L[0]->OPC == OP65_SEC                      &&
84                (L[1] = CS_GetNextEntry (S, I)) != 0       &&
85                L[1]->OPC == OP65_SBC                      &&
86                !CE_HasLabel (L[1])) {
87         return 2;
88     }
89
90
91
92     /* Not found */
93     return 0;
94 }
95
96
97
98 static const char* LoadAXZP (CodeSeg* S, unsigned I)
99 /* If the two instructions preceeding S/I are a load of A/X from a two byte
100  * zero byte location, return the name of the zero page location. Otherwise
101  * return NULL.
102  */
103 {
104     CodeEntry* L[2];
105     unsigned Len;
106
107     if (I >= 2                                                  &&
108         CS_GetEntries (S, L, I-2, 2)                            &&
109         L[0]->OPC == OP65_LDA                                   &&
110         L[0]->AM == AM65_ZP                                     &&
111         L[1]->OPC == OP65_LDX                                   &&
112         L[1]->AM == AM65_ZP                                     &&
113         !CE_HasLabel (L[1])                                     &&
114         (Len = strlen (L[0]->Arg)) == strlen (L[1]->Arg) - 2    &&
115         memcmp (L[0]->Arg, L[1]->Arg, Len) == 0                 &&
116         L[1]->Arg[Len] == '+'                                   &&
117         L[1]->Arg[Len+1] == '1') {
118
119         /* Return the label */
120         return L[0]->Arg;
121
122     } else {
123
124         /* Not found */
125         return 0;
126
127     }
128 }
129
130
131
132 static const char* LoadAXImm (CodeSeg* S, unsigned I)
133 /* If the instructions preceeding S/I are a load of A/X of a constant value
134  * or a word sized address label, return the address of the location as a
135  * string.
136  * Beware: In case of a numeric value, the result is returned in static
137  * storage which is overwritten with each call.
138  */
139 {
140     static StrBuf Buf = STATIC_STRBUF_INITIALIZER;
141     CodeEntry* L[2];
142     CodeEntry* ALoad;
143     CodeEntry* XLoad;
144     unsigned Len;
145
146     /* Fetch entry at I and check if A/X is known */
147     L[0] = CS_GetEntry (S, I);
148     if (L[0] != 0                                               &&
149         RegValIsKnown (L[0]->RI->In.RegA)                       &&
150         RegValIsKnown (L[0]->RI->In.RegX)) {
151
152         /* Numeric argument - get low and high byte */
153         unsigned Lo = (L[0]->RI->In.RegA & 0xFF);
154         unsigned Hi = (L[0]->RI->In.RegX & 0xFF);
155
156         /* Format into buffer */
157         SB_Printf (&Buf, "$%04X", Lo | (Hi << 8));
158
159         /* Return the address as a string */
160         return SB_GetConstBuf (&Buf);
161
162     }
163
164     /* Search back for the two instructions loading A and X. Abort
165      * the search if the registers are changed in any other way or
166      * if a label is reached while we don't have both loads.
167      */
168     ALoad = 0;
169     XLoad = 0;
170     while (I-- > 0) {
171         /* Get next entry */
172         CodeEntry* E = CS_GetEntry (S, I);
173
174         /* Check for the loads of A and X */
175         if (ALoad == 0 && E->OPC == OP65_LDA && E->AM == AM65_IMM) {
176             ALoad = E;
177         } else if (E->Chg & REG_A) {
178             /* A is changed before we get the load */
179             return 0;
180         } else if (XLoad == 0 && E->OPC == OP65_LDX && E->AM == AM65_IMM) {
181             XLoad = E;
182         } else if (E->Chg & REG_X) {
183             /* X is changed before we get the load */
184             return 0;
185         }
186
187         if (ALoad != 0 && XLoad != 0) {
188             /* We have both */
189             break;
190         }
191
192         /* If we have a label, before both are found, bail out */
193         if (CE_HasLabel (E)) {
194             return 0;
195         }
196     }
197
198     /* Check for a load of a label address */
199     if ((Len = strlen (ALoad->Arg)) > 3                         &&
200         ALoad->Arg[0] == '<'                                    &&
201         ALoad->Arg[1] == '('                                    &&
202         strlen (XLoad->Arg) == Len                              &&
203         XLoad->Arg[0] == '>'                                    &&
204         memcmp (ALoad->Arg+1, XLoad->Arg+1, Len-1) == 0) {
205
206         /* Load of an address label */
207         SB_CopyBuf (&Buf, ALoad->Arg + 2, Len - 3);
208         SB_Terminate (&Buf);
209         return SB_GetConstBuf (&Buf);
210     }
211
212     /* Not found */
213     return 0;
214 }
215
216
217
218 /*****************************************************************************/
219 /*                                   Code                                    */
220 /*****************************************************************************/
221
222
223
224 unsigned OptPtrStore1 (CodeSeg* S)
225 /* Search for the sequence:
226  *
227  *      clc
228  *      adc     xxx
229  *      bcc     L
230  *      inx
231  * L:   jsr     pushax
232  *      ldx     #$00
233  *      lda     yyy
234  *      ldy     #$00
235  *      jsr     staspidx
236  *
237  * and replace it by:
238  *
239  *      sta     ptr1
240  *      stx     ptr1+1
241  *      ldy     xxx
242  *      ldx     #$00
243  *      lda     yyy
244  *      sta     (ptr1),y
245  *
246  * or by
247  *
248  *      ldy     xxx
249  *      ldx     #$00
250  *      lda     yyy
251  *      sta     (zp),y
252  *
253  * or by
254  *
255  *      ldy     xxx
256  *      ldx     #$00
257  *      lda     yyy
258  *      sta     label,y
259  *
260  * or by
261  *
262  *      ldy     xxx
263  *      ldx     #$00
264  *      lda     yyy
265  *      sta     $xxxx,y
266  *
267  * depending on the code preceeding the sequence above.
268  */
269 {
270     unsigned Changes = 0;
271     unsigned I;
272
273     /* Walk over the entries */
274     I = 0;
275     while (I < CS_GetEntryCount (S)) {
276
277         CodeEntry* L[9];
278
279         /* Get next entry */
280         L[0] = CS_GetEntry (S, I);
281
282         /* Check for the sequence */
283         if (L[0]->OPC == OP65_CLC                               &&
284             CS_GetEntries (S, L+1, I+1, 8)                      &&
285             L[1]->OPC == OP65_ADC                               &&
286             (L[1]->AM == AM65_ABS                       ||
287              L[1]->AM == AM65_ZP                        ||
288              L[1]->AM == AM65_IMM                       ||
289              (L[1]->AM == AM65_ZP_INDY          &&
290               RegValIsKnown (L[1]->RI->In.RegY)))               &&
291             (L[2]->OPC == OP65_BCC || L[2]->OPC == OP65_JCC)    &&
292             L[2]->JumpTo != 0                                   &&
293             L[2]->JumpTo->Owner == L[4]                         &&
294             L[3]->OPC == OP65_INX                               &&
295             CE_IsCallTo (L[4], "pushax")                        &&
296             L[5]->OPC == OP65_LDX                               &&
297             L[6]->OPC == OP65_LDA                               &&
298             L[7]->OPC == OP65_LDY                               &&
299             CE_IsKnownImm (L[7], 0)                             &&
300             CE_IsCallTo (L[8], "staspidx")                      &&
301             !CS_RangeHasLabel (S, I+1, 3)                       &&
302             !CS_RangeHasLabel (S, I+5, 4)) {
303
304             CodeEntry* X;
305             const char* Loc;
306             am_t AM;
307
308             /* Track the insertion point */
309             unsigned IP = I + 9;
310             if ((Loc = LoadAXZP (S, I)) != 0) {
311                 /* If the sequence is preceeded by a load of a ZP value,
312                  * we can use this ZP value as a pointer using ZP
313                  * indirect Y addressing.
314                  */
315                 AM = AM65_ZP_INDY;
316             } else if ((Loc = LoadAXImm (S, I)) != 0) {
317                 /* If the sequence is preceeded by a load of an immediate
318                  * value, we can use this absolute value as an address
319                  * using absolute indexed Y addressing.
320                  */
321                 AM = AM65_ABSY;
322             }
323
324             /* If we don't have a store location, we use ptr1 with zp
325              * indirect Y addressing. We must store the value in A/X into
326              * ptr1 in this case.
327              */
328             if (Loc == 0) {
329
330                 /* Must use ptr1 */
331                 Loc = "ptr1";
332                 AM  = AM65_ZP_INDY;
333
334                 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[8]->LI);
335                 CS_InsertEntry (S, X, IP++);
336
337                 X = NewCodeEntry (OP65_STX, AM65_ZP, "ptr1+1", 0, L[8]->LI);
338                 CS_InsertEntry (S, X, IP++);
339
340             }
341
342             /* If the index is loaded from (zp),y, we cannot do that directly.
343              * Note: In this case, the Y register will contain the correct
344              * value after removing the old code, so we don't need to load
345              * it here.
346              */
347             if (L[1]->AM == AM65_ZP_INDY) {
348                 X = NewCodeEntry (OP65_LDA, L[1]->AM, L[1]->Arg, 0, L[1]->LI);
349                 CS_InsertEntry (S, X, IP++);
350
351                 X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, L[1]->LI);
352                 CS_InsertEntry (S, X, IP++);
353             } else {
354                 X = NewCodeEntry (OP65_LDY, L[1]->AM, L[1]->Arg, 0, L[1]->LI);
355                 CS_InsertEntry (S, X, IP++);
356             }
357
358             X = NewCodeEntry (OP65_LDX, L[5]->AM, L[5]->Arg, 0, L[5]->LI);
359             CS_InsertEntry (S, X, IP++);
360
361             X = NewCodeEntry (OP65_LDA, L[6]->AM, L[6]->Arg, 0, L[6]->LI);
362             CS_InsertEntry (S, X, IP++);
363
364             X = NewCodeEntry (OP65_STA, AM, Loc, 0, L[8]->LI);
365             CS_InsertEntry (S, X, IP++);
366
367             /* Remove the old code */
368             CS_DelEntries (S, I, 9);
369
370             /* Skip most of the generated replacement code */
371             I += 3;
372
373             /* Remember, we had changes */
374             ++Changes;
375
376         }
377
378         /* Next entry */
379         ++I;
380
381     }
382
383     /* Return the number of changes made */
384     return Changes;
385 }
386
387
388
389 unsigned OptPtrStore2 (CodeSeg* S)
390 /* Search for the sequence:
391  *
392  *      clc
393  *      adc     xxx
394  *      bcc     L
395  *      inx
396  * L:   jsr     pushax
397  *      ldy     yyy
398  *      ldx     #$00
399  *      lda     (sp),y
400  *      ldy     #$00
401  *      jsr     staspidx
402  *
403  * and replace it by:
404  *
405  *      sta     ptr1
406  *      stx     ptr1+1
407  *      ldy     yyy-2
408  *      ldx     #$00
409  *      lda     (sp),y
410  *      ldy     xxx
411  *      sta     (ptr1),y
412  *
413  * or by
414  *
415  *      ldy     yyy-2
416  *      ldx     #$00
417  *      lda     (sp),y
418  *      ldy     xxx
419  *      sta     (zp),y
420  *
421  * or by
422  *
423  *      ldy     yyy-2
424  *      ldx     #$00
425  *      lda     (sp),y
426  *      ldy     xxx
427  *      sta     label,y
428  *
429  * or by
430  *
431  *      ldy     yyy-2
432  *      ldx     #$00
433  *      lda     (sp),y
434  *      ldy     xxx
435  *      sta     $xxxx,y
436  *
437  * depending on the code preceeding the sequence above.
438  */
439 {
440     unsigned Changes = 0;
441     unsigned I;
442
443     /* Walk over the entries */
444     I = 0;
445     while (I < CS_GetEntryCount (S)) {
446
447         CodeEntry* L[10];
448
449         /* Get next entry */
450         L[0] = CS_GetEntry (S, I);
451
452         /* Check for the sequence */
453         if (L[0]->OPC == OP65_CLC                               &&
454             CS_GetEntries (S, L+1, I+1, 9)                      &&
455             L[1]->OPC == OP65_ADC                               &&
456             (L[1]->AM == AM65_ABS                       ||
457              L[1]->AM == AM65_ZP                        ||
458              L[1]->AM == AM65_IMM                       ||
459              (L[1]->AM == AM65_ZP_INDY          &&
460               RegValIsKnown (L[1]->RI->In.RegY)))               &&
461             (L[2]->OPC == OP65_BCC || L[2]->OPC == OP65_JCC)    &&
462             L[2]->JumpTo != 0                                   &&
463             L[2]->JumpTo->Owner == L[4]                         &&
464             L[3]->OPC == OP65_INX                               &&
465             CE_IsCallTo (L[4], "pushax")                        &&
466             L[5]->OPC == OP65_LDY                               &&
467             CE_IsConstImm (L[5])                                &&
468             L[6]->OPC == OP65_LDX                               &&
469             L[7]->OPC == OP65_LDA                               &&
470             L[7]->AM == AM65_ZP_INDY                            &&
471             strcmp (L[7]->Arg, "sp") == 0                       &&
472             L[8]->OPC == OP65_LDY                               &&
473             (L[8]->AM == AM65_ABS                       ||
474              L[8]->AM == AM65_ZP                        ||
475              L[8]->AM == AM65_IMM)                              &&
476             CE_IsCallTo (L[9], "staspidx")                      &&
477             !CS_RangeHasLabel (S, I+1, 3)                       &&
478             !CS_RangeHasLabel (S, I+5, 5)) {
479
480             CodeEntry* X;
481             const char* Arg;
482             const char* Loc;
483             am_t AM;
484
485             /* Track the insertion point */
486             unsigned IP = I + 10;
487             if ((Loc = LoadAXZP (S, I)) != 0) {
488                 /* If the sequence is preceeded by a load of a ZP value,
489                  * we can use this ZP value as a pointer using ZP
490                  * indirect Y addressing.
491                  */
492                 AM = AM65_ZP_INDY;
493             } else if ((Loc = LoadAXImm (S, I)) != 0) {
494                 /* If the sequence is preceeded by a load of an immediate
495                  * value, we can use this absolute value as an address
496                  * using absolute indexed Y addressing.
497                  */
498                 AM = AM65_ABSY;
499             }
500
501             /* If we don't have a store location, we use ptr1 with zp
502              * indirect Y addressing. We must store the value in A/X into
503              * ptr1 in this case.
504              */
505             if (Loc == 0) {
506
507                 /* Must use ptr1 */
508                 Loc = "ptr1";
509                 AM  = AM65_ZP_INDY;
510
511                 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[8]->LI);
512                 CS_InsertEntry (S, X, IP++);
513
514                 X = NewCodeEntry (OP65_STX, AM65_ZP, "ptr1+1", 0, L[8]->LI);
515                 CS_InsertEntry (S, X, IP++);
516
517             }
518
519             /* Generate four different replacements depending on the addressing
520              * mode of the store and from where the index is loaded:
521              *
522              * 1. If the index is not loaded ZP indirect Y, we can use Y for
523              *    the store index.
524              *
525              * 2. If the index is loaded ZP indirect Y and we store absolute
526              *    indexed, we need Y to load the index and will therefore
527              *    use X as index for the store. The disadvantage is that we
528              *    need to reload X later.
529              *
530              * 3. If the index is loaded ZP indirect Y and we store ZP indirect
531              *    Y, we must use Y for load and store and must therefore save
532              *    the A register when loading Y the second time.
533              */
534             if (L[1]->AM != AM65_ZP_INDY) {
535
536                 /* Case 1 */
537                 Arg = MakeHexArg (L[5]->Num - 2);
538                 X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, L[5]->LI);
539                 CS_InsertEntry (S, X, IP++);
540
541                 X = NewCodeEntry (OP65_LDX, L[6]->AM, L[6]->Arg, 0, L[6]->LI);
542                 CS_InsertEntry (S, X, IP++);
543
544                 X = NewCodeEntry (OP65_LDA, L[7]->AM, L[7]->Arg, 0, L[7]->LI);
545                 CS_InsertEntry (S, X, IP++);
546
547                 X = NewCodeEntry (OP65_LDY, L[1]->AM, L[1]->Arg, 0, L[1]->LI);
548                 CS_InsertEntry (S, X, IP++);
549
550                 X = NewCodeEntry (OP65_STA, AM, Loc, 0, L[9]->LI);
551                 CS_InsertEntry (S, X, IP++);
552
553             } else if (AM == AM65_ABSY) {
554
555                 /* Case 2 */
556                 X = NewCodeEntry (OP65_LDA, L[1]->AM, L[1]->Arg, 0, L[1]->LI);
557                 CS_InsertEntry (S, X, IP++);
558
559                 X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, L[1]->LI);
560                 CS_InsertEntry (S, X, IP++);
561
562                 Arg = MakeHexArg (L[5]->Num - 2);
563                 X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, L[5]->LI);
564                 CS_InsertEntry (S, X, IP++);
565
566                 X = NewCodeEntry (OP65_LDA, L[7]->AM, L[7]->Arg, 0, L[7]->LI);
567                 CS_InsertEntry (S, X, IP++);
568
569                 X = NewCodeEntry (OP65_STA, AM65_ABSX, Loc, 0, L[9]->LI);
570                 CS_InsertEntry (S, X, IP++);
571
572                 X = NewCodeEntry (OP65_LDX, L[6]->AM, L[6]->Arg, 0, L[6]->LI);
573                 CS_InsertEntry (S, X, IP++);
574
575             } else {
576
577                 /* Case 3 */
578                 Arg = MakeHexArg (L[5]->Num - 2);
579                 X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, L[5]->LI);
580                 CS_InsertEntry (S, X, IP++);
581
582                 X = NewCodeEntry (OP65_LDX, L[6]->AM, L[6]->Arg, 0, L[6]->LI);
583                 CS_InsertEntry (S, X, IP++);
584
585                 X = NewCodeEntry (OP65_LDA, L[7]->AM, L[7]->Arg, 0, L[7]->LI);
586                 CS_InsertEntry (S, X, IP++);
587
588                 X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, L[6]->LI);
589                 CS_InsertEntry (S, X, IP++);
590
591                 Arg = MakeHexArg (L[1]->RI->In.RegY);
592                 X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, L[1]->LI);
593                 CS_InsertEntry (S, X, IP++);
594
595                 X = NewCodeEntry (OP65_LDA, L[1]->AM, L[1]->Arg, 0, L[1]->LI);
596                 CS_InsertEntry (S, X, IP++);
597
598                 X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, L[1]->LI);
599                 CS_InsertEntry (S, X, IP++);
600
601                 X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, L[6]->LI);
602                 CS_InsertEntry (S, X, IP++);
603
604                 X = NewCodeEntry (OP65_STA, AM, Loc, 0, L[9]->LI);
605                 CS_InsertEntry (S, X, IP++);
606
607             }
608
609             /* Remove the old code */
610             CS_DelEntries (S, I, 10);
611
612             /* Skip most of the generated replacement code */
613             I += 4;
614
615             /* Remember, we had changes */
616             ++Changes;
617
618         }
619
620         /* Next entry */
621         ++I;
622
623     }
624
625     /* Return the number of changes made */
626     return Changes;
627 }
628
629
630
631 unsigned OptPtrStore3 (CodeSeg* S)
632 /* Search for the sequence:
633  *
634  *      jsr     pushax
635  *      ldy     xxx
636  *      jsr     ldauidx
637  *      subop
638  *      ldy     yyy
639  *      jsr     staspidx
640  *
641  * and replace it by:
642  *
643  *      sta     ptr1
644  *      stx     ptr1+1
645  *      ldy     xxx
646  *      ldx     #$00
647  *      lda     (ptr1),y
648  *      subop
649  *      ldy     yyy
650  *      sta     (ptr1),y
651  *
652  * In case a/x is loaded from the register bank before the pushax, we can even
653  * use the register bank instead of ptr1.
654  *
655  */
656 {
657     unsigned Changes = 0;
658
659     /* Walk over the entries */
660     unsigned I = 0;
661     while (I < CS_GetEntryCount (S)) {
662
663         unsigned K;
664         CodeEntry* L[10];
665
666         /* Get next entry */
667         L[0] = CS_GetEntry (S, I);
668
669         /* Check for the sequence */
670         if (CE_IsCallTo (L[0], "pushax")            &&
671             CS_GetEntries (S, L+1, I+1, 3)          &&
672             L[1]->OPC == OP65_LDY                   &&
673             CE_IsConstImm (L[1])                    &&
674             !CE_HasLabel (L[1])                     &&
675             CE_IsCallTo (L[2], "ldauidx")           &&
676             !CE_HasLabel (L[2])                     &&
677             (K = OptPtrStore1Sub (S, I+3, L+3)) > 0 &&
678             CS_GetEntries (S, L+3+K, I+3+K, 2)      &&
679             L[3+K]->OPC == OP65_LDY                 &&
680             CE_IsConstImm (L[3+K])                  &&
681             !CE_HasLabel (L[3+K])                   &&
682             CE_IsCallTo (L[4+K], "staspidx")        &&
683             !CE_HasLabel (L[4+K])) {
684
685
686             const char* RegBank = 0;
687             const char* ZPLoc   = "ptr1";
688             CodeEntry* X;
689
690
691             /* Get the preceeding two instructions and check them. We check
692              * for:
693              *          lda     regbank+n
694              *          ldx     regbank+n+1
695              */
696             if (I > 1) {
697                 CodeEntry* P[2];
698                 P[0] = CS_GetEntry (S, I-2);
699                 P[1] = CS_GetEntry (S, I-1);
700                 if (P[0]->OPC == OP65_LDA &&
701                     P[0]->AM  == AM65_ZP  &&
702                     P[1]->OPC == OP65_LDX &&
703                     P[1]->AM  == AM65_ZP  &&
704                     !CE_HasLabel (P[1])   &&
705                     strncmp (P[0]->Arg, "regbank+", 8) == 0) {
706
707                     unsigned Len = strlen (P[0]->Arg);
708
709                     if (strncmp (P[0]->Arg, P[1]->Arg, Len) == 0 &&
710                         P[1]->Arg[Len+0] == '+'                  &&
711                         P[1]->Arg[Len+1] == '1'                  &&
712                         P[1]->Arg[Len+2] == '\0') {
713
714                         /* Ok, found. Use the name of the register bank */
715                         RegBank = ZPLoc = P[0]->Arg;
716                     }
717                 }
718             }
719
720             /* Insert the load via the zp pointer */
721             X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[3]->LI);
722             CS_InsertEntry (S, X, I+3);
723             X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, ZPLoc, 0, L[2]->LI);
724             CS_InsertEntry (S, X, I+4);
725
726             /* Insert the store through the zp pointer */
727             X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, ZPLoc, 0, L[3]->LI);
728             CS_InsertEntry (S, X, I+6+K);
729
730             /* Delete the old code */
731             CS_DelEntry (S, I+7+K);     /* jsr spaspidx */
732             CS_DelEntry (S, I+2);       /* jsr ldauidx */
733
734             /* Create and insert the stores into the zp pointer if needed */
735             if (RegBank == 0) {
736                 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
737                 CS_InsertEntry (S, X, I+1);
738                 X = NewCodeEntry (OP65_STX, AM65_ZP, "ptr1+1", 0, L[0]->LI);
739                 CS_InsertEntry (S, X, I+2);
740             }
741
742             /* Delete more old code. Do it here to keep a label attached to
743              * entry I in place.
744              */
745             CS_DelEntry (S, I);         /* jsr pushax */
746
747             /* Remember, we had changes */
748             ++Changes;
749
750         }
751
752         /* Next entry */
753         ++I;
754
755     }
756
757     /* Return the number of changes made */
758     return Changes;
759 }
760
761
762