1/*****************************************************************************/
2/* */
3/* coptadd.c */
4/* */
5/* Optimize addition sequences */
6/* */
7/* */
8/* */
9/* (C) 2001-2005, Ullrich von Bassewitz */
10/* Römerstrasse 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/* common */
37#include "chartype.h"
38
39/* cc65 */
40#include "codeent.h"
41#include "codeinfo.h"
42#include "coptadd.h"
43
44
45
46/*****************************************************************************/
47/* Optimize additions */
48/*****************************************************************************/
49
50
51
52unsigned OptAdd1 (CodeSeg* S)
53/* Search for the sequence
54**
55** ldy #xx
56** jsr ldaxysp
57** jsr pushax
58** ldy #yy
59** jsr ldaxysp
60** jsr tosaddax
61**
62** and replace it by:
63**
64** ldy #xx-1
65** lda (sp),y
66** ldy #yy-3
67** clc
68** adc (sp),y
69** pha
70** ldy #xx
71** lda (sp),y
72** ldy #yy-2
73** adc (sp),y
74** tax
75** pla
76*/
77{
78 unsigned Changes = 0;
79
80 /* Walk over the entries */
81 unsigned I = 0;
82 while (I < CS_GetEntryCount (S)) {
83
84 CodeEntry* L[6];
85
86 /* Get next entry */
87 L[0] = CS_GetEntry (S, I);
88
89 /* Check for the sequence */
90 if (L[0]->OPC == OP65_LDY &&
91 CE_IsConstImm (L[0]) &&
92 !CS_RangeHasLabel (S, I+1, 5) &&
93 CS_GetEntries (S, L+1, I+1, 5) &&
94 CE_IsCallTo (L[1], "ldaxysp") &&
95 CE_IsCallTo (L[2], "pushax") &&
96 L[3]->OPC == OP65_LDY &&
97 CE_IsConstImm (L[3]) &&
98 CE_IsCallTo (L[4], "ldaxysp") &&
99 CE_IsCallTo (L[5], "tosaddax")) {
100
101 CodeEntry* X;
102 const char* Arg;
103
104 /* Correct the stack of the first Y register load */
105 CE_SetNumArg (L[0], L[0]->Num - 1);
106
107 /* lda (sp),y */
108 X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[1]->LI);
109 CS_InsertEntry (S, X, I+1);
110
111 /* ldy #yy-3 */
112 Arg = MakeHexArg (L[3]->Num - 3);
113 X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, L[4]->LI);
114 CS_InsertEntry (S, X, I+2);
115
116 /* clc */
117 X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, L[5]->LI);
118 CS_InsertEntry (S, X, I+3);
119
120 /* adc (sp),y */
121 X = NewCodeEntry (OP65_ADC, AM65_ZP_INDY, "sp", 0, L[5]->LI);
122 CS_InsertEntry (S, X, I+4);
123
124 /* pha */
125 X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, L[5]->LI);
126 CS_InsertEntry (S, X, I+5);
127
128 /* ldy #xx (beware: L[0] has changed) */
129 Arg = MakeHexArg (L[0]->Num + 1);
130 X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, L[1]->LI);
131 CS_InsertEntry (S, X, I+6);
132
133 /* lda (sp),y */
134 X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[1]->LI);
135 CS_InsertEntry (S, X, I+7);
136
137 /* ldy #yy-2 */
138 Arg = MakeHexArg (L[3]->Num - 2);
139 X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, L[4]->LI);
140 CS_InsertEntry (S, X, I+8);
141
142 /* adc (sp),y */
143 X = NewCodeEntry (OP65_ADC, AM65_ZP_INDY, "sp", 0, L[5]->LI);
144 CS_InsertEntry (S, X, I+9);
145
146 /* tax */
147 X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, L[5]->LI);
148 CS_InsertEntry (S, X, I+10);
149
150 /* pla */
151 X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, L[5]->LI);
152 CS_InsertEntry (S, X, I+11);
153
154 /* Delete the old code */
155 CS_DelEntries (S, I+12, 5);
156
157 /* Remember, we had changes */
158 ++Changes;
159
160 }
161
162 /* Next entry */
163 ++I;
164
165 }
166
167 /* Return the number of changes made */
168 return Changes;
169}
170
171
172
173unsigned OptAdd2 (CodeSeg* S)
174/* Search for the sequence
175**
176** ldy #xx
177** jsr ldaxysp
178** ldy #yy
179** jsr addeqysp
180**
181** and replace it by:
182**
183** ldy #xx-1
184** lda (sp),y
185** ldy #yy
186** clc
187** adc (sp),y
188** sta (sp),y
189** ldy #xx
190** lda (sp),y
191** ldy #yy+1
192** adc (sp),y
193** sta (sp),y
194**
195** provided that a/x is not used later.
196*/
197{
198 unsigned Changes = 0;
199
200 /* Walk over the entries */
201 unsigned I = 0;
202 while (I < CS_GetEntryCount (S)) {
203
204 CodeEntry* L[4];
205
206 /* Get next entry */
207 L[0] = CS_GetEntry (S, I);
208
209 /* Check for the sequence */
210 if (L[0]->OPC == OP65_LDY &&
211 CE_IsConstImm (L[0]) &&
212 !CS_RangeHasLabel (S, I+1, 3) &&
213 CS_GetEntries (S, L+1, I+1, 3) &&
214 CE_IsCallTo (L[1], "ldaxysp") &&
215 L[2]->OPC == OP65_LDY &&
216 CE_IsConstImm (L[2]) &&
217 CE_IsCallTo (L[3], "addeqysp") &&
218 (GetRegInfo (S, I+4, REG_AX) & REG_AX) == 0) {
219
220 /* Insert new code behind the addeqysp */
221 const char* Arg;
222 CodeEntry* X;
223
224 /* ldy #xx-1 */
225 Arg = MakeHexArg (L[0]->Num-1);
226 X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, L[0]->LI);
227 CS_InsertEntry (S, X, I+4);
228
229 /* lda (sp),y */
230 X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[1]->LI);
231 CS_InsertEntry (S, X, I+5);
232
233 /* ldy #yy */
234 X = NewCodeEntry (OP65_LDY, AM65_IMM, L[2]->Arg, 0, L[2]->LI);
235 CS_InsertEntry (S, X, I+6);
236
237 /* clc */
238 X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, L[3]->LI);
239 CS_InsertEntry (S, X, I+7);
240
241 /* adc (sp),y */
242 X = NewCodeEntry (OP65_ADC, AM65_ZP_INDY, "sp", 0, L[3]->LI);
243 CS_InsertEntry (S, X, I+8);
244
245 /* sta (sp),y */
246 X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, "sp", 0, L[3]->LI);
247 CS_InsertEntry (S, X, I+9);
248
249 /* ldy #xx */
250 X = NewCodeEntry (OP65_LDY, AM65_IMM, L[0]->Arg, 0, L[0]->LI);
251 CS_InsertEntry (S, X, I+10);
252
253 /* lda (sp),y */
254 X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[1]->LI);
255 CS_InsertEntry (S, X, I+11);
256
257 /* ldy #yy+1 */
258 Arg = MakeHexArg (L[2]->Num+1);
259 X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, L[2]->LI);
260 CS_InsertEntry (S, X, I+12);
261
262 /* adc (sp),y */
263 X = NewCodeEntry (OP65_ADC, AM65_ZP_INDY, "sp", 0, L[3]->LI);
264 CS_InsertEntry (S, X, I+13);
265
266 /* sta (sp),y */
267 X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, "sp", 0, L[3]->LI);
268 CS_InsertEntry (S, X, I+14);
269
270 /* Delete the old code */
271 CS_DelEntries (S, I, 4);
272
273 /* Remember, we had changes */
274 ++Changes;
275
276 }
277
278 /* Next entry */
279 ++I;
280
281 }
282
283 /* Return the number of changes made */
284 return Changes;
285}
286
287
288
289unsigned OptAdd3 (CodeSeg* S)
290/* Search for the sequence
291**
292** jsr pushax
293** ldx #$00
294** lda xxx
295** jsr tosaddax
296**
297** and replace it by
298**
299** clc
300** adc xxx
301** bcc L1
302** inx
303** L1:
304*/
305{
306 unsigned Changes = 0;
307
308 /* Walk over the entries */
309 unsigned I = 0;
310 while (I < CS_GetEntryCount (S)) {
311
312 CodeEntry* L[5];
313
314 /* Get next entry */
315 L[0] = CS_GetEntry (S, I);
316
317 /* Check for the sequence */
318 if (CE_IsCallTo (L[0], "pushax") &&
319 CS_GetEntries (S, L+1, I+1, 4) &&
320 !CS_RangeHasLabel (S, I+1, 3) &&
321 L[1]->OPC == OP65_LDX &&
322 CE_IsKnownImm (L[1], 0) &&
323 L[2]->OPC == OP65_LDA &&
324 CE_IsCallTo (L[3], "tosaddax")) {
325
326 CodeEntry* X;
327 CodeLabel* Label;
328
329 /* Insert new code behind the sequence */
330 X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, L[3]->LI);
331 CS_InsertEntry (S, X, I+4);
332
333 /* adc xxx */
334 X = NewCodeEntry (OP65_ADC, L[2]->AM, L[2]->Arg, 0, L[3]->LI);
335 CS_InsertEntry (S, X, I+5);
336
337 /* bcc L1 */
338 Label = CS_GenLabel (S, L[4]);
339 X = NewCodeEntry (OP65_BCC, AM65_BRA, Label->Name, Label, L[3]->LI);
340 CS_InsertEntry (S, X, I+6);
341
342 /* inx */
343 X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, L[3]->LI);
344 CS_InsertEntry (S, X, I+7);
345
346 /* Delete the old code */
347 CS_DelEntries (S, I, 4);
348
349 /* Remember, we had changes */
350 ++Changes;
351
352 }
353
354 /* Next entry */
355 ++I;
356
357 }
358
359 /* Return the number of changes made */
360 return Changes;
361}
362
363
364
365unsigned OptAdd4 (CodeSeg* S)
366/* Search for the sequence
367**
368** jsr pushax
369** lda xxx
370** ldx yyy
371** jsr tosaddax
372**
373** and replace it by
374**
375** clc
376** adc xxx
377** pha
378** txa
379** adc yyy
380** tax
381** pla
382*/
383{
384 unsigned Changes = 0;
385
386 /* Walk over the entries */
387 unsigned I = 0;
388 while (I < CS_GetEntryCount (S)) {
389
390 CodeEntry* L[4];
391
392 /* Get next entry */
393 L[0] = CS_GetEntry (S, I);
394
395 /* Check for the sequence */
396 if (CE_IsCallTo (L[0], "pushax") &&
397 CS_GetEntries (S, L+1, I+1, 3) &&
398 !CS_RangeHasLabel (S, I+1, 3) &&
399 L[1]->OPC == OP65_LDA &&
400 (L[1]->AM == AM65_ABS || L[1]->AM == AM65_ZP) &&
401 L[2]->OPC == OP65_LDX &&
402 (L[2]->AM == AM65_ABS || L[2]->AM == AM65_ZP) &&
403 CE_IsCallTo (L[3], "tosaddax")) {
404
405 CodeEntry* X;
406
407 /* Insert new code behind the sequence */
408 X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, L[3]->LI);
409 CS_InsertEntry (S, X, I+4);
410
411 /* adc xxx */
412 X = NewCodeEntry (OP65_ADC, L[1]->AM, L[1]->Arg, 0, L[3]->LI);
413 CS_InsertEntry (S, X, I+5);
414
415 /* pha */
416 X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, L[3]->LI);
417 CS_InsertEntry (S, X, I+6);
418
419 /* txa */
420 X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, L[3]->LI);
421 CS_InsertEntry (S, X, I+7);
422
423 /* adc yyy */
424 X = NewCodeEntry (OP65_ADC, L[2]->AM, L[2]->Arg, 0, L[3]->LI);
425 CS_InsertEntry (S, X, I+8);
426
427 /* tax */
428 X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, L[3]->LI);
429 CS_InsertEntry (S, X, I+9);
430
431 /* pla */
432 X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, L[3]->LI);
433 CS_InsertEntry (S, X, I+10);
434
435 /* Delete the old code */
436 CS_DelEntries (S, I, 4);
437
438 /* Remember, we had changes */
439 ++Changes;
440
441 }
442
443 /* Next entry */
444 ++I;
445
446 }
447
448 /* Return the number of changes made */
449 return Changes;
450}
451
452
453
454unsigned OptAdd5 (CodeSeg* S)
455/* Search for a call to incaxn and replace it by an 8 bit add if the X register
456** is not used later.
457*/
458{
459 unsigned Changes = 0;
460
461 /* Walk over the entries */
462 unsigned I = 0;
463 while (I < CS_GetEntryCount (S)) {
464
465 CodeEntry* E;
466
467 /* Get next entry */
468 E = CS_GetEntry (S, I);
469
470 /* Check for the sequence */
471 if (E->OPC == OP65_JSR &&
472 strncmp (E->Arg, "incax", 5) == 0 &&
473 IsDigit (E->Arg[5]) &&
474 E->Arg[6] == '\0' &&
475 !RegXUsed (S, I+1)) {
476
477 CodeEntry* X;
478 const char* Arg;
479
480 /* Insert new code behind the sequence */
481 X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, E->LI);
482 CS_InsertEntry (S, X, I+1);
483
484 Arg = MakeHexArg (E->Arg[5] - '0');
485 X = NewCodeEntry (OP65_ADC, AM65_IMM, Arg, 0, E->LI);
486 CS_InsertEntry (S, X, I+2);
487
488 /* Delete the old code */
489 CS_DelEntry (S, I);
490
491 /* Remember, we had changes */
492 ++Changes;
493
494 }
495
496 /* Next entry */
497 ++I;
498
499 }
500
501 /* Return the number of changes made */
502 return Changes;
503}
504
505
506
507unsigned OptAdd6 (CodeSeg* S)
508/* Search for the sequence
509**
510** adc ...
511** bcc L
512** inx
513** L:
514**
515** and remove the handling of the high byte if X is not used later.
516*/
517{
518 unsigned Changes = 0;
519
520 /* Walk over the entries */
521 unsigned I = 0;
522 while (I < CS_GetEntryCount (S)) {
523
524 CodeEntry* L[3];
525
526 /* Get next entry */
527 CodeEntry* E = CS_GetEntry (S, I);
528
529 /* Check for the sequence */
530 if (E->OPC == OP65_ADC &&
531 CS_GetEntries (S, L, I+1, 3) &&
532 (L[0]->OPC == OP65_BCC || L[0]->OPC == OP65_JCC) &&
533 L[0]->JumpTo != 0 &&
534 !CE_HasLabel (L[0]) &&
535 L[1]->OPC == OP65_INX &&
536 !CE_HasLabel (L[1]) &&
537 L[0]->JumpTo->Owner == L[2] &&
538 !RegXUsed (S, I+3)) {
539
540 /* Remove the bcs/dex */
541 CS_DelEntries (S, I+1, 2);
542
543 /* Remember, we had changes */
544 ++Changes;
545
546 }
547
548 /* Next entry */
549 ++I;
550
551 }
552
553 /* Return the number of changes made */
554 return Changes;
555}
556