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