]> git.sur5r.net Git - cc65/blob - src/cc65/coptptrstore.c
Added an error message, in case there's a typo in the definitions of long command...
[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     unsigned Changes = 0;
657
658     /* Walk over the entries */
659     unsigned I = 0;
660     while (I < CS_GetEntryCount (S)) {
661
662         unsigned K;
663         CodeEntry* L[10];
664
665         /* Get next entry */
666         L[0] = CS_GetEntry (S, I);
667
668         /* Check for the sequence */
669         if (CE_IsCallTo (L[0], "pushax")            &&
670             CS_GetEntries (S, L+1, I+1, 3)          &&
671             L[1]->OPC == OP65_LDY                   &&
672             CE_IsConstImm (L[1])                    &&
673             !CE_HasLabel (L[1])                     &&
674             CE_IsCallTo (L[2], "ldauidx")           &&
675             !CE_HasLabel (L[2])                     &&
676             (K = OptPtrStore1Sub (S, I+3, L+3)) > 0 &&
677             CS_GetEntries (S, L+3+K, I+3+K, 2)      &&
678             L[3+K]->OPC == OP65_LDY                 &&
679             CE_IsConstImm (L[3+K])                  &&
680             !CE_HasLabel (L[3+K])                   &&
681             CE_IsCallTo (L[4+K], "staspidx")        &&
682             !CE_HasLabel (L[4+K])) {
683
684
685             const char* RegBank = 0;
686             const char* ZPLoc   = "ptr1";
687             CodeEntry* X;
688
689
690             /* Get the preceeding two instructions and check them. We check
691             ** for:
692             **          lda     regbank+n
693             **          ldx     regbank+n+1
694             */
695             if (I > 1) {
696                 CodeEntry* P[2];
697                 P[0] = CS_GetEntry (S, I-2);
698                 P[1] = CS_GetEntry (S, I-1);
699                 if (P[0]->OPC == OP65_LDA &&
700                     P[0]->AM  == AM65_ZP  &&
701                     P[1]->OPC == OP65_LDX &&
702                     P[1]->AM  == AM65_ZP  &&
703                     !CE_HasLabel (P[1])   &&
704                     strncmp (P[0]->Arg, "regbank+", 8) == 0) {
705
706                     unsigned Len = strlen (P[0]->Arg);
707
708                     if (strncmp (P[0]->Arg, P[1]->Arg, Len) == 0 &&
709                         P[1]->Arg[Len+0] == '+'                  &&
710                         P[1]->Arg[Len+1] == '1'                  &&
711                         P[1]->Arg[Len+2] == '\0') {
712
713                         /* Ok, found. Use the name of the register bank */
714                         RegBank = ZPLoc = P[0]->Arg;
715                     }
716                 }
717             }
718
719             /* Insert the load via the zp pointer */
720             X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, L[3]->LI);
721             CS_InsertEntry (S, X, I+3);
722             X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, ZPLoc, 0, L[2]->LI);
723             CS_InsertEntry (S, X, I+4);
724
725             /* Insert the store through the zp pointer */
726             X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, ZPLoc, 0, L[3]->LI);
727             CS_InsertEntry (S, X, I+6+K);
728
729             /* Delete the old code */
730             CS_DelEntry (S, I+7+K);     /* jsr spaspidx */
731             CS_DelEntry (S, I+2);       /* jsr ldauidx */
732
733             /* Create and insert the stores into the zp pointer if needed */
734             if (RegBank == 0) {
735                 X = NewCodeEntry (OP65_STA, AM65_ZP, "ptr1", 0, L[0]->LI);
736                 CS_InsertEntry (S, X, I+1);
737                 X = NewCodeEntry (OP65_STX, AM65_ZP, "ptr1+1", 0, L[0]->LI);
738                 CS_InsertEntry (S, X, I+2);
739             }
740
741             /* Delete more old code. Do it here to keep a label attached to
742             ** entry I in place.
743             */
744             CS_DelEntry (S, I);         /* jsr pushax */
745
746             /* Remember, we had changes */
747             ++Changes;
748
749         }
750
751         /* Next entry */
752         ++I;
753
754     }
755
756     /* Return the number of changes made */
757     return Changes;
758 }