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 | } |
759 | |