1 /*****************************************************************************/
5 /* Optimize operations that take operands via the stack */
9 /* (C) 2001-2002 Ullrich von Bassewitz */
11 /* D-70597 Stuttgart */
12 /* EMail: uz@cc65.org */
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. */
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: */
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 */
32 /*****************************************************************************/
47 /*****************************************************************************/
49 /*****************************************************************************/
53 /* Flags returned by DirectOp */
54 #define OP_DIRECT 0x01 /* Direct op may be used */
55 #define OP_ONSTACK 0x02 /* Operand is on stack */
59 /*****************************************************************************/
61 /*****************************************************************************/
65 static unsigned AdjustStackOffset (CodeSeg* S, unsigned Start, unsigned Stop,
67 /* Adjust the offset for all stack accesses in the range Start to Stop, both
68 * inclusive. The function returns the number of instructions that have been
72 /* Number of inserted instructions */
73 unsigned Inserted = 0;
75 /* Walk over all entries */
79 CodeEntry* E = CS_GetEntry (S, I);
81 if (E->Use & REG_SP) {
85 /* Check for some things that should not happen */
86 CHECK (E->AM == AM65_ZP_INDY || E->RI->In.RegY >= (short) Offs);
87 CHECK (strcmp (E->Arg, "sp") == 0);
89 /* Get the code entry before this one. If it's a LDY, adjust the
92 P = CS_GetPrevEntry (S, I);
93 if (P && P->OPC == OP65_LDY && CE_KnownImm (P)) {
95 /* The Y load is just before the stack access, adjust it */
96 CE_SetNumArg (P, P->Num - Offs);
100 /* Insert a new load instruction before the stack access */
101 const char* Arg = MakeHexArg (E->RI->In.RegY - Offs);
102 CodeEntry* X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
103 CS_InsertEntry (S, X, I);
105 /* One more inserted entries */
109 /* Be sure to skip the stack access for the next round */
120 /* Return the number of inserted entries */
126 static unsigned DirectOp (CodeEntry* E)
127 /* Check if the given entry is a lda instruction with an addressing mode
128 * that allows us to replace it by another operation (like ora). If so, we may
129 * use this location for the or and must not save the value in the zero
134 if (E->OPC == OP65_LDA) {
135 if (E->AM == AM65_IMM || E->AM == AM65_ZP || E->AM == AM65_ABS) {
136 /* These insns are all ok and replaceable */
138 } else if (E->AM == AM65_ZP_INDY &&
139 E->RI->In.RegY >= 0 &&
140 strcmp (E->Arg, "sp") == 0) {
141 /* Load from stack with known offset is also ok */
142 Flags |= (OP_DIRECT | OP_ONSTACK);
150 /*****************************************************************************/
151 /* Actual optimization functions */
152 /*****************************************************************************/
156 static unsigned Opt_staspidx (CodeSeg* S, unsigned Push, unsigned Store,
157 const char* ZPLo, const char* ZPHi)
158 /* Optimize the staspidx sequence if possible */
161 CodeEntry* PushEntry;
162 CodeEntry* StoreEntry;
164 /* Get the push entry */
165 PushEntry = CS_GetEntry (S, Push);
167 /* Store the value into the zeropage instead of pushing it */
168 X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, PushEntry->LI);
169 CS_InsertEntry (S, X, Push+1);
170 X = NewCodeEntry (OP65_STX, AM65_ZP, ZPHi, 0, PushEntry->LI);
171 CS_InsertEntry (S, X, Push+2);
173 /* Correct the index of the store and get a pointer to the entry */
175 StoreEntry = CS_GetEntry (S, Store);
177 /* Inline the store */
178 X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, ZPLo, 0, StoreEntry->LI);
179 CS_InsertEntry (S, X, Store+1);
181 /* Remove the push and the call to the staspidx function */
182 CS_DelEntry (S, Store);
183 CS_DelEntry (S, Push);
185 /* We changed the sequence */
191 static unsigned Opt_staxspidx (CodeSeg* S, unsigned Push, unsigned Store,
192 const char* ZPLo, const char* ZPHi)
193 /* Optimize the staxspidx sequence if possible */
196 CodeEntry* PushEntry;
197 CodeEntry* StoreEntry;
199 /* Get the push entry */
200 PushEntry = CS_GetEntry (S, Push);
202 /* Store the value into the zeropage instead of pushing it */
203 X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, PushEntry->LI);
204 CS_InsertEntry (S, X, Push+1);
205 X = NewCodeEntry (OP65_STX, AM65_ZP, ZPHi, 0, PushEntry->LI);
206 CS_InsertEntry (S, X, Push+2);
208 /* Correct the index of the store and get a pointer to the entry */
210 StoreEntry = CS_GetEntry (S, Store);
212 /* Inline the store */
213 X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, ZPLo, 0, StoreEntry->LI);
214 CS_InsertEntry (S, X, Store+1);
215 X = NewCodeEntry (OP65_INY, AM65_IMP, 0, 0, StoreEntry->LI);
216 CS_InsertEntry (S, X, Store+2);
217 if (StoreEntry->RI->In.RegX >= 0) {
218 /* Value of X is known */
219 const char* Arg = MakeHexArg (StoreEntry->RI->In.RegX);
220 X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, StoreEntry->LI);
223 X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, StoreEntry->LI);
225 CS_InsertEntry (S, X, Store+3);
226 X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, ZPLo, 0, StoreEntry->LI);
227 CS_InsertEntry (S, X, Store+4);
229 /* Remove the push and the call to the staspidx function */
230 CS_DelEntry (S, Store);
231 CS_DelEntry (S, Push);
233 /* We changed the sequence */
239 static unsigned Opt_tosaddax (CodeSeg* S, unsigned Push, unsigned Add,
240 const char* ZPLo, const char* ZPHi)
241 /* Optimize the tosaddax sequence if possible */
246 CodeEntry* PushEntry;
251 /* We need the entry behind the add */
252 CHECK ((N = CS_GetNextEntry (S, Add)) != 0);
254 /* And the entry before the push */
255 CHECK ((P = CS_GetPrevEntry (S, Push)) != 0);
257 /* Get the push entry */
258 PushEntry = CS_GetEntry (S, Push);
260 /* Check the entry before the push. If it's a lda instruction with an
261 * addressing mode that allows us to replace it, we may use this
262 * location for the op and must not save the value in the zero page
265 Flags = DirectOp (P);
267 /* Store the value into the zeropage instead of pushing it */
268 X = NewCodeEntry (OP65_STX, AM65_ZP, ZPHi, 0, PushEntry->LI);
269 CS_InsertEntry (S, X, Push+1);
270 ++Add; /* Correct the index */
271 if ((Flags & OP_DIRECT) == 0) {
272 X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, PushEntry->LI);
273 CS_InsertEntry (S, X, Push+1);
274 ++Add; /* Correct the index */
277 /* Get a pointer to the add entry */
278 AddEntry = CS_GetEntry (S, Add);
281 X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, AddEntry->LI);
282 CS_InsertEntry (S, X, Add+1);
283 if ((Flags & OP_DIRECT) != 0) {
284 /* Add a variable location. If the location is on the stack, we
285 * need to reload the Y register.
287 if ((Flags & OP_ONSTACK) != 0) {
288 X = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (P->RI->In.RegY), 0, AddEntry->LI);
289 CS_InsertEntry (S, X, Add);
292 X = NewCodeEntry (OP65_ADC, P->AM, P->Arg, 0, AddEntry->LI);
294 /* Add from temp storage */
295 X = NewCodeEntry (OP65_ADC, AM65_ZP, ZPLo, 0, AddEntry->LI);
297 CS_InsertEntry (S, X, Add+2);
298 if (PushEntry->RI->In.RegX == 0) {
299 /* The high byte is the value in X plus the carry */
300 CodeLabel* L = CS_GenLabel (S, N);
301 X = NewCodeEntry (OP65_BCC, AM65_BRA, L->Name, L, AddEntry->LI);
302 CS_InsertEntry (S, X, Add+3);
303 X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, AddEntry->LI);
304 CS_InsertEntry (S, X, Add+4);
305 } else if (AddEntry->RI->In.RegX == 0) {
306 /* The high byte is that of the first operand plus carry */
308 if (PushEntry->RI->In.RegX >= 0) {
309 /* Value of first op high byte is known */
310 const char* Arg = MakeHexArg (PushEntry->RI->In.RegX);
311 X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, AddEntry->LI);
313 /* Value of first op high byte is unknown */
314 X = NewCodeEntry (OP65_LDX, AM65_ZP, ZPHi, 0, AddEntry->LI);
316 CS_InsertEntry (S, X, Add+3);
317 L = CS_GenLabel (S, N);
318 X = NewCodeEntry (OP65_BCC, AM65_BRA, L->Name, L, AddEntry->LI);
319 CS_InsertEntry (S, X, Add+4);
320 X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, AddEntry->LI);
321 CS_InsertEntry (S, X, Add+5);
323 /* High byte is unknown */
324 X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, AddEntry->LI);
325 CS_InsertEntry (S, X, Add+3);
326 X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, AddEntry->LI);
327 CS_InsertEntry (S, X, Add+4);
328 X = NewCodeEntry (OP65_ADC, AM65_ZP, ZPHi, 0, AddEntry->LI);
329 CS_InsertEntry (S, X, Add+5);
330 X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, AddEntry->LI);
331 CS_InsertEntry (S, X, Add+6);
332 X = NewCodeEntry (OP65_LDA, AM65_ZP, ZPLo, 0, AddEntry->LI);
333 CS_InsertEntry (S, X, Add+7);
336 /* Remove the push and the call to the tosaddax function */
337 CS_DelEntry (S, Add);
338 CS_DelEntry (S, Push);
340 /* We changed the sequence */
346 static unsigned Opt_tosandax (CodeSeg* S, unsigned Push, unsigned And,
347 const char* ZPLo, const char* ZPHi)
348 /* Optimize the tosandax sequence if possible */
352 CodeEntry* PushEntry;
356 /* Get the entry before the push */
357 CHECK ((P = CS_GetPrevEntry (S, Push)) != 0);
359 /* Get the push entry */
360 PushEntry = CS_GetEntry (S, Push);
362 /* Check the entry before the push. If it's a lda instruction with an
363 * addressing mode that allows us to replace it, we may use this
364 * location for the op and must not save the value in the zero page
367 Flags = DirectOp (P);
369 /* Store the value into the zeropage instead of pushing it */
370 X = NewCodeEntry (OP65_STX, AM65_ZP, ZPHi, 0, PushEntry->LI);
371 CS_InsertEntry (S, X, Push+1);
372 ++And; /* Correct the index */
373 if ((Flags & OP_DIRECT) == 0) {
374 X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, PushEntry->LI);
375 CS_InsertEntry (S, X, Push+1);
376 ++And; /* Correct the index */
379 /* Get a pointer to the and entry */
380 AndEntry = CS_GetEntry (S, And);
383 if ((Flags & OP_DIRECT) != 0) {
384 /* And with variable location. If the location is on the stack, we
385 * need to reload the Y register.
387 if ((Flags & OP_ONSTACK) != 0) {
388 X = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (P->RI->In.RegY), 0, AndEntry->LI);
389 CS_InsertEntry (S, X, And);
392 X = NewCodeEntry (OP65_AND, P->AM, P->Arg, 0, AndEntry->LI);
394 /* And with temp storage */
395 X = NewCodeEntry (OP65_AND, AM65_ZP, ZPLo, 0, AndEntry->LI);
397 CS_InsertEntry (S, X, And+1);
398 if (PushEntry->RI->In.RegX == 0 || AndEntry->RI->In.RegX == 0) {
399 /* The high byte is zero */
400 X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, AndEntry->LI);
401 CS_InsertEntry (S, X, And+2);
403 /* High byte is unknown */
404 X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, AndEntry->LI);
405 CS_InsertEntry (S, X, And+2);
406 X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, AndEntry->LI);
407 CS_InsertEntry (S, X, And+3);
408 X = NewCodeEntry (OP65_AND, AM65_ZP, ZPHi, 0, AndEntry->LI);
409 CS_InsertEntry (S, X, And+4);
410 X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, AndEntry->LI);
411 CS_InsertEntry (S, X, And+5);
412 X = NewCodeEntry (OP65_LDA, AM65_ZP, ZPLo, 0, AndEntry->LI);
413 CS_InsertEntry (S, X, And+6);
416 /* Remove the push and the call to the tosandax function */
417 CS_DelEntry (S, And);
418 CS_DelEntry (S, Push);
420 /* We changed the sequence */
426 static unsigned Opt_tosorax (CodeSeg* S, unsigned Push, unsigned Or,
427 const char* ZPLo, const char* ZPHi)
428 /* Optimize the tosorax sequence if possible */
432 CodeEntry* PushEntry;
436 /* Get the entry before the push */
437 CHECK ((P = CS_GetPrevEntry (S, Push)) != 0);
439 /* Get the push entry */
440 PushEntry = CS_GetEntry (S, Push);
442 /* Check the entry before the push. If it's a lda instruction with an
443 * addressing mode that allows us to replace it, we may use this
444 * location for the op and must not save the value in the zero page
447 Flags = DirectOp (P);
449 /* Store the value into the zeropage instead of pushing it */
450 X = NewCodeEntry (OP65_STX, AM65_ZP, ZPHi, 0, PushEntry->LI);
451 CS_InsertEntry (S, X, Push+1);
452 ++Or; /* Correct the index */
453 if ((Flags & OP_DIRECT) == 0) {
454 X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, PushEntry->LI);
455 CS_InsertEntry (S, X, Push+1);
456 ++Or; /* Correct the index */
459 /* Get a pointer to the or entry */
460 OrEntry = CS_GetEntry (S, Or);
463 if ((Flags & OP_DIRECT) != 0) {
464 /* Or with variable location. If the location is on the stack, we
465 * need to reload the Y register.
467 if ((Flags & OP_ONSTACK) != 0) {
468 X = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (P->RI->In.RegY), 0, OrEntry->LI);
469 CS_InsertEntry (S, X, Or);
472 X = NewCodeEntry (OP65_ORA, P->AM, P->Arg, 0, OrEntry->LI);
474 /* Or with temp storage */
475 X = NewCodeEntry (OP65_ORA, AM65_ZP, ZPLo, 0, OrEntry->LI);
477 CS_InsertEntry (S, X, Or+1);
478 if (PushEntry->RI->In.RegX >= 0 && OrEntry->RI->In.RegX >= 0) {
479 /* Both values known, precalculate the result */
480 const char* Arg = MakeHexArg (PushEntry->RI->In.RegX | OrEntry->RI->In.RegX);
481 X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, OrEntry->LI);
482 CS_InsertEntry (S, X, Or+2);
483 } else if (PushEntry->RI->In.RegX != 0) {
484 /* High byte is unknown */
485 X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, OrEntry->LI);
486 CS_InsertEntry (S, X, Or+2);
487 X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, OrEntry->LI);
488 CS_InsertEntry (S, X, Or+3);
489 X = NewCodeEntry (OP65_ORA, AM65_ZP, ZPHi, 0, OrEntry->LI);
490 CS_InsertEntry (S, X, Or+4);
491 X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, OrEntry->LI);
492 CS_InsertEntry (S, X, Or+5);
493 X = NewCodeEntry (OP65_LDA, AM65_ZP, ZPLo, 0, OrEntry->LI);
494 CS_InsertEntry (S, X, Or+6);
497 /* Remove the push and the call to the tosorax function */
499 CS_DelEntry (S, Push);
501 /* We changed the sequence */
507 static unsigned Opt_tosxorax (CodeSeg* S, unsigned Push, unsigned Xor,
508 const char* ZPLo, const char* ZPHi)
509 /* Optimize the tosxorax sequence if possible */
513 CodeEntry* PushEntry;
517 /* Get the entry before the push */
518 CHECK ((P = CS_GetPrevEntry (S, Push)) != 0);
520 /* Get the push entry */
521 PushEntry = CS_GetEntry (S, Push);
523 /* Check the entry before the push. If it's a lda instruction with an
524 * addressing mode that allows us to replace it, we may use this
525 * location for the op and must not save the value in the zero page
528 Flags = DirectOp (P);
530 /* Store the value into the zeropage instead of pushing it */
531 X = NewCodeEntry (OP65_STX, AM65_ZP, ZPHi, 0, PushEntry->LI);
532 CS_InsertEntry (S, X, Push+1);
533 ++Xor; /* Correct the index */
534 if ((Flags & OP_DIRECT) != 0) {
535 X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, PushEntry->LI);
536 CS_InsertEntry (S, X, Push+1);
537 ++Xor; /* Correct the index */
540 /* Get a pointer to the entry */
541 XorEntry = CS_GetEntry (S, Xor);
544 if ((Flags & OP_DIRECT) != 0) {
545 /* Xor with a variable location. If the location is on the stack, we
546 * need to reload the Y register.
548 if ((Flags & OP_ONSTACK) != 0) {
549 X = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (P->RI->In.RegY), 0, XorEntry->LI);
550 CS_InsertEntry (S, X, Xor);
553 X = NewCodeEntry (OP65_EOR, P->AM, P->Arg, 0, XorEntry->LI);
555 /* Xor with temp storage */
556 X = NewCodeEntry (OP65_EOR, AM65_ZP, ZPLo, 0, XorEntry->LI);
558 CS_InsertEntry (S, X, Xor+1);
559 if (PushEntry->RI->In.RegX >= 0 && XorEntry->RI->In.RegX >= 0) {
560 /* Both values known, precalculate the result */
561 const char* Arg = MakeHexArg (PushEntry->RI->In.RegX ^ XorEntry->RI->In.RegX);
562 X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, XorEntry->LI);
563 CS_InsertEntry (S, X, Xor+2);
564 } else if (PushEntry->RI->In.RegX != 0) {
565 /* High byte is unknown */
566 X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, XorEntry->LI);
567 CS_InsertEntry (S, X, Xor+2);
568 X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, XorEntry->LI);
569 CS_InsertEntry (S, X, Xor+3);
570 X = NewCodeEntry (OP65_EOR, AM65_ZP, ZPHi, 0, XorEntry->LI);
571 CS_InsertEntry (S, X, Xor+4);
572 X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, XorEntry->LI);
573 CS_InsertEntry (S, X, Xor+5);
574 X = NewCodeEntry (OP65_LDA, AM65_ZP, ZPLo, 0, XorEntry->LI);
575 CS_InsertEntry (S, X, Xor+6);
578 /* Remove the push and the call to the tosandax function */
579 CS_DelEntry (S, Xor);
580 CS_DelEntry (S, Push);
582 /* We changed the sequence */
588 /*****************************************************************************/
590 /*****************************************************************************/
594 /* Flags for the functions */
596 STOP_NONE, /* Nothing special */
597 STOP_A_UNUSED /* Call only if a unused later */
601 typedef unsigned (*OptFunc) (CodeSeg* S, unsigned Push, unsigned Store,
602 const char* ZPLo, const char* ZPHi);
603 typedef struct OptFuncDesc OptFuncDesc;
605 const char* Name; /* Name of the replaced runtime function */
606 OptFunc Func; /* Function pointer */
607 STOP_FLAGS Flags; /* Flags */
610 static const OptFuncDesc FuncTable[] = {
611 { "staspidx", Opt_staspidx, STOP_NONE },
612 { "staxspidx", Opt_staxspidx, STOP_A_UNUSED },
613 { "tosaddax", Opt_tosaddax, STOP_NONE },
614 { "tosandax", Opt_tosandax, STOP_NONE },
615 { "tosorax", Opt_tosorax, STOP_NONE },
616 { "tosxorax", Opt_tosxorax, STOP_NONE },
618 #define FUNC_COUNT (sizeof(FuncTable) / sizeof(FuncTable[0]))
622 static int CmpFunc (const void* Key, const void* Func)
623 /* Compare function for bsearch */
625 return strcmp (Key, ((const OptFuncDesc*) Func)->Name);
630 static const OptFuncDesc* FindFunc (const char* Name)
631 /* Find the function with the given name. Return a pointer to the table entry
632 * or NULL if the function was not found.
635 return bsearch (Name, FuncTable, FUNC_COUNT, sizeof(OptFuncDesc), CmpFunc);
640 /*****************************************************************************/
642 /*****************************************************************************/
646 unsigned OptStackOps (CodeSeg* S)
647 /* Optimize operations that take operands via the stack */
649 unsigned Changes = 0; /* Number of changes in one run */
650 int InSeq = 0; /* Inside a sequence */
651 unsigned Push = 0; /* Index of pushax */
652 unsigned UsedRegs = 0; /* Zeropage registers used in sequence */
655 /* Generate register info */
658 /* Look for a call to pushax followed by a call to some other function
659 * that takes it's first argument on the stack, and the second argument
660 * in the primary register.
661 * It depends on the code between the two if we can handle/transform the
662 * sequence, so check this code for the following list of things:
664 * - there must not be a jump or conditional branch (this may
665 * get relaxed later).
666 * - there may not be accesses to local variables with unknown
667 * offsets (because we have to adjust these offsets).
668 * - no subroutine calls
671 * Since we need a zero page register later, do also check the
672 * intermediate code for zero page use.
675 while (I < CS_GetEntryCount (S)) {
677 /* Get the next entry */
678 CodeEntry* E = CS_GetEntry (S, I);
680 /* Handling depends if we're inside a sequence or not */
683 if ((E->Info & OF_BRA) != 0 ||
684 ((E->Use & REG_SP) != 0 &&
685 (E->AM != AM65_ZP_INDY || E->RI->In.RegY < 0)) ||
688 /* All this stuff is not allowed in a sequence */
691 } else if (E->OPC == OP65_JSR) {
693 /* Subroutine call: Check if this is one of our functions */
694 const OptFuncDesc* F = FindFunc (E->Arg);
697 const char* ZPLo = 0;
698 const char* ZPHi = 0;
701 /* Check the flags */
702 if (F->Flags & STOP_A_UNUSED) {
703 /* a must be unused later */
704 if (RegAUsed (S, I+1)) {
705 /* Cannot optimize */
710 /* Determine the zero page locations to use */
712 UsedRegs |= GetRegInfo (S, I+1, REG_SREG | REG_PTR1 | REG_PTR2);
713 if ((UsedRegs & REG_SREG) == REG_NONE) {
714 /* SREG is available */
717 } else if ((UsedRegs & REG_PTR1) == REG_NONE) {
720 } else if ((UsedRegs & REG_PTR2) == REG_NONE) {
724 /* No registers available */
729 /* If preconditions are ok, call the optimizer function */
732 /* Adjust stack offsets */
733 unsigned Op = I + AdjustStackOffset (S, Push, I, 2);
735 /* Call the optimizer function */
736 Changes += F->Func (S, Push, Op, ZPLo, ZPHi);
738 /* Regenerate register info */
742 /* End of sequence */
745 } else if (strcmp (E->Arg, "pushax") == 0) {
746 /* Restart the sequence */
750 /* A call to an unkown subroutine ends the sequence */
756 /* Other stuff: Track zeropage register usage */
757 UsedRegs |= (E->Use | E->Chg);
761 } else if (CE_IsCall (E, "pushax")) {
763 /* This starts a sequence */
775 /* Free the register info */
778 /* Return the number of changes made */