1 | /*****************************************************************************/ |
2 | /* */ |
3 | /* codeent.c */ |
4 | /* */ |
5 | /* Code segment entry */ |
6 | /* */ |
7 | /* */ |
8 | /* */ |
9 | /* (C) 2001-2009, 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 <stdlib.h> |
37 | |
38 | /* common */ |
39 | #include "chartype.h" |
40 | #include "check.h" |
41 | #include "debugflag.h" |
42 | #include "xmalloc.h" |
43 | #include "xsprintf.h" |
44 | |
45 | /* cc65 */ |
46 | #include "codeent.h" |
47 | #include "codeinfo.h" |
48 | #include "error.h" |
49 | #include "global.h" |
50 | #include "codelab.h" |
51 | #include "opcodes.h" |
52 | #include "output.h" |
53 | |
54 | |
55 | |
56 | /*****************************************************************************/ |
57 | /* Data */ |
58 | /*****************************************************************************/ |
59 | |
60 | |
61 | |
62 | /* Empty argument */ |
63 | static char EmptyArg[] = "" ; |
64 | |
65 | |
66 | |
67 | /*****************************************************************************/ |
68 | /* Helper functions */ |
69 | /*****************************************************************************/ |
70 | |
71 | |
72 | |
73 | static void FreeArg (char* Arg) |
74 | /* Free a code entry argument */ |
75 | { |
76 | if (Arg != EmptyArg) { |
77 | xfree (Arg); |
78 | } |
79 | } |
80 | |
81 | |
82 | |
83 | static char* GetArgCopy (const char* Arg) |
84 | /* Create an argument copy for assignment */ |
85 | { |
86 | if (Arg && Arg[0] != '\0') { |
87 | /* Create a copy */ |
88 | return xstrdup (Arg); |
89 | } else { |
90 | /* Use the empty argument string */ |
91 | return EmptyArg; |
92 | } |
93 | } |
94 | |
95 | |
96 | |
97 | static int NumArg (const char* Arg, unsigned long* Num) |
98 | /* If the given argument is numerical, convert it and return true. Otherwise |
99 | ** set Num to zero and return false. |
100 | */ |
101 | { |
102 | char* End; |
103 | unsigned long Val; |
104 | |
105 | /* Determine the base */ |
106 | int Base = 10; |
107 | if (*Arg == '$') { |
108 | ++Arg; |
109 | Base = 16; |
110 | } else if (*Arg == '%') { |
111 | ++Arg; |
112 | Base = 2; |
113 | } |
114 | |
115 | /* Convert the value. strtol is not exactly what we want here, but it's |
116 | ** cheap and may be replaced by something fancier later. |
117 | */ |
118 | Val = strtoul (Arg, &End, Base); |
119 | |
120 | /* Check if the conversion was successful */ |
121 | if (*End != '\0') { |
122 | |
123 | /* Could not convert */ |
124 | *Num = 0; |
125 | return 0; |
126 | |
127 | } else { |
128 | |
129 | /* Conversion ok */ |
130 | *Num = Val; |
131 | return 1; |
132 | |
133 | } |
134 | } |
135 | |
136 | |
137 | |
138 | static void SetUseChgInfo (CodeEntry* E, const OPCDesc* D) |
139 | /* Set the Use and Chg in E */ |
140 | { |
141 | const ZPInfo* Info; |
142 | |
143 | /* If this is a subroutine call, or a jump to an external function, |
144 | ** lookup the information about this function and use it. The jump itself |
145 | ** does not change any registers, so we don't need to use the data from D. |
146 | */ |
147 | if ((E->Info & (OF_UBRA | OF_CALL)) != 0 && E->JumpTo == 0) { |
148 | /* A subroutine call or jump to external symbol (function exit) */ |
149 | GetFuncInfo (E->Arg, &E->Use, &E->Chg); |
150 | } else { |
151 | /* Some other instruction. Use the values from the opcode description |
152 | ** plus addressing mode info. |
153 | */ |
154 | E->Use = D->Use | GetAMUseInfo (E->AM); |
155 | E->Chg = D->Chg; |
156 | |
157 | /* Check for special zero page registers used */ |
158 | switch (E->AM) { |
159 | |
160 | case AM65_ACC: |
161 | if (E->OPC == OP65_ASL || E->OPC == OP65_DEC || |
162 | E->OPC == OP65_INC || E->OPC == OP65_LSR || |
163 | E->OPC == OP65_ROL || E->OPC == OP65_ROR) { |
164 | /* A is changed by these insns */ |
165 | E->Chg |= REG_A; |
166 | } |
167 | break; |
168 | |
169 | case AM65_ZP: |
170 | case AM65_ABS: |
171 | /* Be conservative: */ |
172 | case AM65_ZPX: |
173 | case AM65_ABSX: |
174 | case AM65_ABSY: |
175 | Info = GetZPInfo (E->Arg); |
176 | if (Info && Info->ByteUse != REG_NONE) { |
177 | if (E->OPC == OP65_ASL || E->OPC == OP65_DEC || |
178 | E->OPC == OP65_INC || E->OPC == OP65_LSR || |
179 | E->OPC == OP65_ROL || E->OPC == OP65_ROR || |
180 | E->OPC == OP65_TRB || E->OPC == OP65_TSB) { |
181 | /* The zp loc is both, input and output */ |
182 | E->Chg |= Info->ByteUse; |
183 | E->Use |= Info->ByteUse; |
184 | } else if ((E->Info & OF_STORE) != 0) { |
185 | /* Just output */ |
186 | E->Chg |= Info->ByteUse; |
187 | } else { |
188 | /* Input only */ |
189 | E->Use |= Info->ByteUse; |
190 | } |
191 | } |
192 | break; |
193 | |
194 | case AM65_ZPX_IND: |
195 | case AM65_ZP_INDY: |
196 | case AM65_ZP_IND: |
197 | Info = GetZPInfo (E->Arg); |
198 | if (Info && Info->ByteUse != REG_NONE) { |
199 | /* These addressing modes will never change the zp loc */ |
200 | E->Use |= Info->WordUse; |
201 | } |
202 | break; |
203 | |
204 | default: |
205 | /* Keep gcc silent */ |
206 | break; |
207 | } |
208 | } |
209 | } |
210 | |
211 | |
212 | |
213 | /*****************************************************************************/ |
214 | /* Code */ |
215 | /*****************************************************************************/ |
216 | |
217 | |
218 | |
219 | const char* MakeHexArg (unsigned Num) |
220 | /* Convert Num into a string in the form $XY, suitable for passing it as an |
221 | ** argument to NewCodeEntry, and return a pointer to the string. |
222 | ** BEWARE: The function returns a pointer to a static buffer, so the value is |
223 | ** gone if you call it twice (and apart from that it's not thread and signal |
224 | ** safe). |
225 | */ |
226 | { |
227 | static char Buf[16]; |
228 | xsprintf (Buf, sizeof (Buf), "$%02X" , (unsigned char) Num); |
229 | return Buf; |
230 | } |
231 | |
232 | |
233 | |
234 | CodeEntry* NewCodeEntry (opc_t OPC, am_t AM, const char* Arg, |
235 | CodeLabel* JumpTo, LineInfo* LI) |
236 | /* Create a new code entry, initialize and return it */ |
237 | { |
238 | /* Get the opcode description */ |
239 | const OPCDesc* D = GetOPCDesc (OPC); |
240 | |
241 | /* Allocate memory */ |
242 | CodeEntry* E = xmalloc (sizeof (CodeEntry)); |
243 | |
244 | /* Initialize the fields */ |
245 | E->OPC = D->OPC; |
246 | E->AM = AM; |
247 | E->Size = GetInsnSize (E->OPC, E->AM); |
248 | E->Arg = GetArgCopy (Arg); |
249 | E->Flags = NumArg (E->Arg, &E->Num)? CEF_NUMARG : 0; /* Needs E->Arg */ |
250 | E->Info = D->Info; |
251 | E->JumpTo = JumpTo; |
252 | E->LI = UseLineInfo (LI); |
253 | E->RI = 0; |
254 | SetUseChgInfo (E, D); |
255 | InitCollection (&E->Labels); |
256 | |
257 | /* If we have a label given, add this entry to the label */ |
258 | if (JumpTo) { |
259 | CollAppend (&JumpTo->JumpFrom, E); |
260 | } |
261 | |
262 | /* Return the initialized struct */ |
263 | return E; |
264 | } |
265 | |
266 | |
267 | |
268 | void FreeCodeEntry (CodeEntry* E) |
269 | /* Free the given code entry */ |
270 | { |
271 | /* Free the string argument if we have one */ |
272 | FreeArg (E->Arg); |
273 | |
274 | /* Cleanup the collection */ |
275 | DoneCollection (&E->Labels); |
276 | |
277 | /* Release the line info */ |
278 | ReleaseLineInfo (E->LI); |
279 | |
280 | /* Delete the register info */ |
281 | CE_FreeRegInfo (E); |
282 | |
283 | /* Free the entry */ |
284 | xfree (E); |
285 | } |
286 | |
287 | |
288 | |
289 | void CE_ReplaceOPC (CodeEntry* E, opc_t OPC) |
290 | /* Replace the opcode of the instruction. This will also replace related info, |
291 | ** Size, Use and Chg, but it will NOT update any arguments or labels. |
292 | */ |
293 | { |
294 | /* Get the opcode descriptor */ |
295 | const OPCDesc* D = GetOPCDesc (OPC); |
296 | |
297 | /* Replace the opcode */ |
298 | E->OPC = OPC; |
299 | E->Info = D->Info; |
300 | E->Size = GetInsnSize (E->OPC, E->AM); |
301 | SetUseChgInfo (E, D); |
302 | } |
303 | |
304 | |
305 | |
306 | int CodeEntriesAreEqual (const CodeEntry* E1, const CodeEntry* E2) |
307 | /* Check if both code entries are equal */ |
308 | { |
309 | return (E1->OPC == E2->OPC && E1->AM == E2->AM && strcmp (E1->Arg, E2->Arg) == 0); |
310 | } |
311 | |
312 | |
313 | |
314 | void CE_AttachLabel (CodeEntry* E, CodeLabel* L) |
315 | /* Attach the label to the entry */ |
316 | { |
317 | /* Add it to the entries label list */ |
318 | CollAppend (&E->Labels, L); |
319 | |
320 | /* Tell the label about it's owner */ |
321 | L->Owner = E; |
322 | } |
323 | |
324 | |
325 | |
326 | void CE_ClearJumpTo (CodeEntry* E) |
327 | /* Clear the JumpTo entry and the argument (which contained the name of the |
328 | ** label). Note: The function will not clear the backpointer from the label, |
329 | ** so use it with care. |
330 | */ |
331 | { |
332 | /* Clear the JumpTo entry */ |
333 | E->JumpTo = 0; |
334 | |
335 | /* Clear the argument and assign the empty one */ |
336 | FreeArg (E->Arg); |
337 | E->Arg = EmptyArg; |
338 | } |
339 | |
340 | |
341 | |
342 | void CE_MoveLabel (CodeLabel* L, CodeEntry* E) |
343 | /* Move the code label L from it's former owner to the code entry E. */ |
344 | { |
345 | /* Delete the label from the owner */ |
346 | CollDeleteItem (&L->Owner->Labels, L); |
347 | |
348 | /* Set the new owner */ |
349 | CollAppend (&E->Labels, L); |
350 | L->Owner = E; |
351 | } |
352 | |
353 | |
354 | |
355 | void CE_SetArg (CodeEntry* E, const char* Arg) |
356 | /* Replace the argument by the new one. */ |
357 | { |
358 | /* Free the old argument */ |
359 | FreeArg (E->Arg); |
360 | |
361 | /* Assign the new one */ |
362 | E->Arg = GetArgCopy (Arg); |
363 | } |
364 | |
365 | |
366 | |
367 | void CE_SetNumArg (CodeEntry* E, long Num) |
368 | /* Set a new numeric argument for the given code entry that must already |
369 | ** have a numeric argument. |
370 | */ |
371 | { |
372 | char Buf[16]; |
373 | |
374 | /* Check that the entry has a numerical argument */ |
375 | CHECK (E->Flags & CEF_NUMARG); |
376 | |
377 | /* Make the new argument string */ |
378 | if (E->Size == 2) { |
379 | Num &= 0xFF; |
380 | xsprintf (Buf, sizeof (Buf), "$%02X" , (unsigned) Num); |
381 | } else if (E->Size == 3) { |
382 | Num &= 0xFFFF; |
383 | xsprintf (Buf, sizeof (Buf), "$%04X" , (unsigned) Num); |
384 | } else { |
385 | Internal ("Invalid instruction size in CE_SetNumArg" ); |
386 | } |
387 | |
388 | /* Replace the argument by the new one */ |
389 | CE_SetArg (E, Buf); |
390 | |
391 | /* Use the new numerical value */ |
392 | E->Num = Num; |
393 | } |
394 | |
395 | |
396 | |
397 | int CE_IsConstImm (const CodeEntry* E) |
398 | /* Return true if the argument of E is a constant immediate value */ |
399 | { |
400 | return (E->AM == AM65_IMM && CE_HasNumArg (E)); |
401 | } |
402 | |
403 | |
404 | |
405 | int CE_IsKnownImm (const CodeEntry* E, unsigned long Num) |
406 | /* Return true if the argument of E is a constant immediate value that is |
407 | ** equal to Num. |
408 | */ |
409 | { |
410 | return (E->AM == AM65_IMM && CE_HasNumArg (E) && E->Num == Num); |
411 | } |
412 | |
413 | |
414 | |
415 | int CE_UseLoadFlags (CodeEntry* E) |
416 | /* Return true if the instruction uses any flags that are set by a load of |
417 | ** a register (N and Z). |
418 | */ |
419 | { |
420 | /* Follow unconditional branches, but beware of endless loops. After this, |
421 | ** E will point to the first entry that is not a branch. |
422 | */ |
423 | if (E->Info & OF_UBRA) { |
424 | Collection C = AUTO_COLLECTION_INITIALIZER; |
425 | |
426 | /* Follow the chain */ |
427 | while (E->Info & OF_UBRA) { |
428 | |
429 | /* Remember the entry so we can detect loops */ |
430 | CollAppend (&C, E); |
431 | |
432 | /* Check the target */ |
433 | if (E->JumpTo == 0 || CollIndex (&C, E->JumpTo->Owner) >= 0) { |
434 | /* Unconditional jump to external symbol, or endless loop. */ |
435 | DoneCollection (&C); |
436 | return 0; /* Flags not used */ |
437 | } |
438 | |
439 | /* Follow the chain */ |
440 | E = E->JumpTo->Owner; |
441 | } |
442 | |
443 | /* Delete the collection */ |
444 | DoneCollection (&C); |
445 | } |
446 | |
447 | /* A branch will use the flags */ |
448 | if (E->Info & OF_FBRA) { |
449 | return 1; |
450 | } |
451 | |
452 | /* Call of a boolean transformer routine will also use the flags */ |
453 | if (E->OPC == OP65_JSR) { |
454 | /* Get the condition that is evaluated and check it */ |
455 | switch (FindBoolCmpCond (E->Arg)) { |
456 | case CMP_EQ: |
457 | case CMP_NE: |
458 | case CMP_GT: |
459 | case CMP_GE: |
460 | case CMP_LT: |
461 | case CMP_LE: |
462 | case CMP_UGT: |
463 | case CMP_ULE: |
464 | /* Will use the N or Z flags */ |
465 | return 1; |
466 | |
467 | |
468 | case CMP_UGE: /* Uses only carry */ |
469 | case CMP_ULT: /* Dito */ |
470 | default: /* No bool transformer subroutine */ |
471 | return 0; |
472 | } |
473 | } |
474 | |
475 | /* Anything else */ |
476 | return 0; |
477 | } |
478 | |
479 | |
480 | |
481 | void CE_FreeRegInfo (CodeEntry* E) |
482 | /* Free an existing register info struct */ |
483 | { |
484 | if (E->RI) { |
485 | FreeRegInfo (E->RI); |
486 | E->RI = 0; |
487 | } |
488 | } |
489 | |
490 | |
491 | |
492 | void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) |
493 | /* Generate register info for this instruction. If an old info exists, it is |
494 | ** overwritten. |
495 | */ |
496 | { |
497 | /* Pointers to the register contents */ |
498 | RegContents* In; |
499 | RegContents* Out; |
500 | |
501 | /* Function register usage */ |
502 | unsigned short Use, Chg; |
503 | |
504 | /* If we don't have a register info struct, allocate one. */ |
505 | if (E->RI == 0) { |
506 | E->RI = NewRegInfo (InputRegs); |
507 | } else { |
508 | if (InputRegs) { |
509 | E->RI->In = *InputRegs; |
510 | } else { |
511 | RC_Invalidate (&E->RI->In); |
512 | } |
513 | E->RI->Out2 = E->RI->Out = E->RI->In; |
514 | } |
515 | |
516 | /* Get pointers to the register contents */ |
517 | In = &E->RI->In; |
518 | Out = &E->RI->Out; |
519 | |
520 | /* Handle the different instructions */ |
521 | switch (E->OPC) { |
522 | |
523 | case OP65_ADC: |
524 | /* We don't know the value of the carry, so the result is |
525 | ** always unknown. |
526 | */ |
527 | Out->RegA = UNKNOWN_REGVAL; |
528 | break; |
529 | |
530 | case OP65_AND: |
531 | if (RegValIsKnown (In->RegA)) { |
532 | if (CE_IsConstImm (E)) { |
533 | Out->RegA = In->RegA & (short) E->Num; |
534 | } else if (E->AM == AM65_ZP) { |
535 | switch (GetKnownReg (E->Use & REG_ZP, In)) { |
536 | case REG_TMP1: |
537 | Out->RegA = In->RegA & In->Tmp1; |
538 | break; |
539 | case REG_PTR1_LO: |
540 | Out->RegA = In->RegA & In->Ptr1Lo; |
541 | break; |
542 | case REG_PTR1_HI: |
543 | Out->RegA = In->RegA & In->Ptr1Hi; |
544 | break; |
545 | case REG_SREG_LO: |
546 | Out->RegA = In->RegA & In->SRegLo; |
547 | break; |
548 | case REG_SREG_HI: |
549 | Out->RegA = In->RegA & In->SRegHi; |
550 | break; |
551 | default: |
552 | Out->RegA = UNKNOWN_REGVAL; |
553 | break; |
554 | } |
555 | } else { |
556 | Out->RegA = UNKNOWN_REGVAL; |
557 | } |
558 | } else if (CE_IsKnownImm (E, 0)) { |
559 | /* A and $00 does always give zero */ |
560 | Out->RegA = 0; |
561 | } |
562 | break; |
563 | |
564 | case OP65_ASL: |
565 | if (E->AM == AM65_ACC && RegValIsKnown (In->RegA)) { |
566 | Out->RegA = (In->RegA << 1) & 0xFF; |
567 | } else if (E->AM == AM65_ZP) { |
568 | switch (GetKnownReg (E->Chg & REG_ZP, In)) { |
569 | case REG_TMP1: |
570 | Out->Tmp1 = (In->Tmp1 << 1) & 0xFF; |
571 | break; |
572 | case REG_PTR1_LO: |
573 | Out->Ptr1Lo = (In->Ptr1Lo << 1) & 0xFF; |
574 | break; |
575 | case REG_PTR1_HI: |
576 | Out->Ptr1Hi = (In->Ptr1Hi << 1) & 0xFF; |
577 | break; |
578 | case REG_SREG_LO: |
579 | Out->SRegLo = (In->SRegLo << 1) & 0xFF; |
580 | break; |
581 | case REG_SREG_HI: |
582 | Out->SRegHi = (In->SRegHi << 1) & 0xFF; |
583 | break; |
584 | } |
585 | } else if (E->AM == AM65_ZPX) { |
586 | /* Invalidates all ZP registers */ |
587 | RC_InvalidateZP (Out); |
588 | } |
589 | break; |
590 | |
591 | case OP65_BCC: |
592 | break; |
593 | |
594 | case OP65_BCS: |
595 | break; |
596 | |
597 | case OP65_BEQ: |
598 | break; |
599 | |
600 | case OP65_BIT: |
601 | break; |
602 | |
603 | case OP65_BMI: |
604 | break; |
605 | |
606 | case OP65_BNE: |
607 | break; |
608 | |
609 | case OP65_BPL: |
610 | break; |
611 | |
612 | case OP65_BRA: |
613 | break; |
614 | |
615 | case OP65_BRK: |
616 | break; |
617 | |
618 | case OP65_BVC: |
619 | break; |
620 | |
621 | case OP65_BVS: |
622 | break; |
623 | |
624 | case OP65_CLC: |
625 | break; |
626 | |
627 | case OP65_CLD: |
628 | break; |
629 | |
630 | case OP65_CLI: |
631 | break; |
632 | |
633 | case OP65_CLV: |
634 | break; |
635 | |
636 | case OP65_CMP: |
637 | break; |
638 | |
639 | case OP65_CPX: |
640 | break; |
641 | |
642 | case OP65_CPY: |
643 | break; |
644 | |
645 | case OP65_DEA: |
646 | if (RegValIsKnown (In->RegA)) { |
647 | Out->RegA = (In->RegA - 1) & 0xFF; |
648 | } |
649 | break; |
650 | |
651 | case OP65_DEC: |
652 | if (E->AM == AM65_ACC && RegValIsKnown (In->RegA)) { |
653 | Out->RegA = (In->RegA - 1) & 0xFF; |
654 | } else if (E->AM == AM65_ZP) { |
655 | switch (GetKnownReg (E->Chg & REG_ZP, In)) { |
656 | case REG_TMP1: |
657 | Out->Tmp1 = (In->Tmp1 - 1) & 0xFF; |
658 | break; |
659 | case REG_PTR1_LO: |
660 | Out->Ptr1Lo = (In->Ptr1Lo - 1) & 0xFF; |
661 | break; |
662 | case REG_PTR1_HI: |
663 | Out->Ptr1Hi = (In->Ptr1Hi - 1) & 0xFF; |
664 | break; |
665 | case REG_SREG_LO: |
666 | Out->SRegLo = (In->SRegLo - 1) & 0xFF; |
667 | break; |
668 | case REG_SREG_HI: |
669 | Out->SRegHi = (In->SRegHi - 1) & 0xFF; |
670 | break; |
671 | } |
672 | } else if (E->AM == AM65_ZPX) { |
673 | /* Invalidates all ZP registers */ |
674 | RC_InvalidateZP (Out); |
675 | } |
676 | break; |
677 | |
678 | case OP65_DEX: |
679 | if (RegValIsKnown (In->RegX)) { |
680 | Out->RegX = (In->RegX - 1) & 0xFF; |
681 | } |
682 | break; |
683 | |
684 | case OP65_DEY: |
685 | if (RegValIsKnown (In->RegY)) { |
686 | Out->RegY = (In->RegY - 1) & 0xFF; |
687 | } |
688 | break; |
689 | |
690 | case OP65_EOR: |
691 | if (RegValIsKnown (In->RegA)) { |
692 | if (CE_IsConstImm (E)) { |
693 | Out->RegA = In->RegA ^ (short) E->Num; |
694 | } else if (E->AM == AM65_ZP) { |
695 | switch (GetKnownReg (E->Use & REG_ZP, In)) { |
696 | case REG_TMP1: |
697 | Out->RegA = In->RegA ^ In->Tmp1; |
698 | break; |
699 | case REG_PTR1_LO: |
700 | Out->RegA = In->RegA ^ In->Ptr1Lo; |
701 | break; |
702 | case REG_PTR1_HI: |
703 | Out->RegA = In->RegA ^ In->Ptr1Hi; |
704 | break; |
705 | case REG_SREG_LO: |
706 | Out->RegA = In->RegA ^ In->SRegLo; |
707 | break; |
708 | case REG_SREG_HI: |
709 | Out->RegA = In->RegA ^ In->SRegHi; |
710 | break; |
711 | default: |
712 | Out->RegA = UNKNOWN_REGVAL; |
713 | break; |
714 | } |
715 | } else { |
716 | Out->RegA = UNKNOWN_REGVAL; |
717 | } |
718 | } |
719 | break; |
720 | |
721 | case OP65_INA: |
722 | if (RegValIsKnown (In->RegA)) { |
723 | Out->RegA = (In->RegA + 1) & 0xFF; |
724 | } |
725 | break; |
726 | |
727 | case OP65_INC: |
728 | if (E->AM == AM65_ACC && RegValIsKnown (In->RegA)) { |
729 | Out->RegA = (In->RegA + 1) & 0xFF; |
730 | } else if (E->AM == AM65_ZP) { |
731 | switch (GetKnownReg (E->Chg & REG_ZP, In)) { |
732 | case REG_TMP1: |
733 | Out->Tmp1 = (In->Tmp1 + 1) & 0xFF; |
734 | break; |
735 | case REG_PTR1_LO: |
736 | Out->Ptr1Lo = (In->Ptr1Lo + 1) & 0xFF; |
737 | break; |
738 | case REG_PTR1_HI: |
739 | Out->Ptr1Hi = (In->Ptr1Hi + 1) & 0xFF; |
740 | break; |
741 | case REG_SREG_LO: |
742 | Out->SRegLo = (In->SRegLo + 1) & 0xFF; |
743 | break; |
744 | case REG_SREG_HI: |
745 | Out->SRegHi = (In->SRegHi + 1) & 0xFF; |
746 | break; |
747 | } |
748 | } else if (E->AM == AM65_ZPX) { |
749 | /* Invalidates all ZP registers */ |
750 | RC_InvalidateZP (Out); |
751 | } |
752 | break; |
753 | |
754 | case OP65_INX: |
755 | if (RegValIsKnown (In->RegX)) { |
756 | Out->RegX = (In->RegX + 1) & 0xFF; |
757 | } |
758 | break; |
759 | |
760 | case OP65_INY: |
761 | if (RegValIsKnown (In->RegY)) { |
762 | Out->RegY = (In->RegY + 1) & 0xFF; |
763 | } |
764 | break; |
765 | |
766 | case OP65_JCC: |
767 | break; |
768 | |
769 | case OP65_JCS: |
770 | break; |
771 | |
772 | case OP65_JEQ: |
773 | break; |
774 | |
775 | case OP65_JMI: |
776 | break; |
777 | |
778 | case OP65_JMP: |
779 | break; |
780 | |
781 | case OP65_JNE: |
782 | break; |
783 | |
784 | case OP65_JPL: |
785 | break; |
786 | |
787 | case OP65_JSR: |
788 | /* Get the code info for the function */ |
789 | GetFuncInfo (E->Arg, &Use, &Chg); |
790 | if (Chg & REG_A) { |
791 | Out->RegA = UNKNOWN_REGVAL; |
792 | } |
793 | if (Chg & REG_X) { |
794 | Out->RegX = UNKNOWN_REGVAL; |
795 | } |
796 | if (Chg & REG_Y) { |
797 | Out->RegY = UNKNOWN_REGVAL; |
798 | } |
799 | if (Chg & REG_TMP1) { |
800 | Out->Tmp1 = UNKNOWN_REGVAL; |
801 | } |
802 | if (Chg & REG_PTR1_LO) { |
803 | Out->Ptr1Lo = UNKNOWN_REGVAL; |
804 | } |
805 | if (Chg & REG_PTR1_HI) { |
806 | Out->Ptr1Hi = UNKNOWN_REGVAL; |
807 | } |
808 | if (Chg & REG_SREG_LO) { |
809 | Out->SRegLo = UNKNOWN_REGVAL; |
810 | } |
811 | if (Chg & REG_SREG_HI) { |
812 | Out->SRegHi = UNKNOWN_REGVAL; |
813 | } |
814 | /* ## FIXME: Quick hack for some known functions: */ |
815 | if (strcmp (E->Arg, "complax" ) == 0) { |
816 | if (RegValIsKnown (In->RegA)) { |
817 | Out->RegA = (In->RegA ^ 0xFF); |
818 | } |
819 | if (RegValIsKnown (In->RegX)) { |
820 | Out->RegX = (In->RegX ^ 0xFF); |
821 | } |
822 | } else if (strcmp (E->Arg, "tosandax" ) == 0) { |
823 | if (In->RegA == 0) { |
824 | Out->RegA = 0; |
825 | } |
826 | if (In->RegX == 0) { |
827 | Out->RegX = 0; |
828 | } |
829 | } else if (strcmp (E->Arg, "tosaslax" ) == 0) { |
830 | if (RegValIsKnown (In->RegA) && (In->RegA & 0x0F) >= 8) { |
831 | printf ("Hey!\n" ); |
832 | Out->RegA = 0; |
833 | } |
834 | } else if (strcmp (E->Arg, "tosorax" ) == 0) { |
835 | if (In->RegA == 0xFF) { |
836 | Out->RegA = 0xFF; |
837 | } |
838 | if (In->RegX == 0xFF) { |
839 | Out->RegX = 0xFF; |
840 | } |
841 | } else if (strcmp (E->Arg, "tosshlax" ) == 0) { |
842 | if ((In->RegA & 0x0F) >= 8) { |
843 | Out->RegA = 0; |
844 | } |
845 | } else if (FindBoolCmpCond (E->Arg) != CMP_INV || |
846 | FindTosCmpCond (E->Arg) != CMP_INV) { |
847 | /* Result is boolean value, so X is zero on output */ |
848 | Out->RegX = 0; |
849 | } |
850 | break; |
851 | |
852 | case OP65_JVC: |
853 | break; |
854 | |
855 | case OP65_JVS: |
856 | break; |
857 | |
858 | case OP65_LDA: |
859 | if (CE_IsConstImm (E)) { |
860 | Out->RegA = (unsigned char) E->Num; |
861 | } else if (E->AM == AM65_ZP) { |
862 | switch (GetKnownReg (E->Use & REG_ZP, In)) { |
863 | case REG_TMP1: |
864 | Out->RegA = In->Tmp1; |
865 | break; |
866 | case REG_PTR1_LO: |
867 | Out->RegA = In->Ptr1Lo; |
868 | break; |
869 | case REG_PTR1_HI: |
870 | Out->RegA = In->Ptr1Hi; |
871 | break; |
872 | case REG_SREG_LO: |
873 | Out->RegA = In->SRegLo; |
874 | break; |
875 | case REG_SREG_HI: |
876 | Out->RegA = In->SRegHi; |
877 | break; |
878 | default: |
879 | Out->RegA = UNKNOWN_REGVAL; |
880 | break; |
881 | } |
882 | } else { |
883 | /* A is now unknown */ |
884 | Out->RegA = UNKNOWN_REGVAL; |
885 | } |
886 | break; |
887 | |
888 | case OP65_LDX: |
889 | if (CE_IsConstImm (E)) { |
890 | Out->RegX = (unsigned char) E->Num; |
891 | } else if (E->AM == AM65_ZP) { |
892 | switch (GetKnownReg (E->Use & REG_ZP, In)) { |
893 | case REG_TMP1: |
894 | Out->RegX = In->Tmp1; |
895 | break; |
896 | case REG_PTR1_LO: |
897 | Out->RegX = In->Ptr1Lo; |
898 | break; |
899 | case REG_PTR1_HI: |
900 | Out->RegX = In->Ptr1Hi; |
901 | break; |
902 | case REG_SREG_LO: |
903 | Out->RegX = In->SRegLo; |
904 | break; |
905 | case REG_SREG_HI: |
906 | Out->RegX = In->SRegHi; |
907 | break; |
908 | default: |
909 | Out->RegX = UNKNOWN_REGVAL; |
910 | break; |
911 | } |
912 | } else { |
913 | /* X is now unknown */ |
914 | Out->RegX = UNKNOWN_REGVAL; |
915 | } |
916 | break; |
917 | |
918 | case OP65_LDY: |
919 | if (CE_IsConstImm (E)) { |
920 | Out->RegY = (unsigned char) E->Num; |
921 | } else if (E->AM == AM65_ZP) { |
922 | switch (GetKnownReg (E->Use & REG_ZP, In)) { |
923 | case REG_TMP1: |
924 | Out->RegY = In->Tmp1; |
925 | break; |
926 | case REG_PTR1_LO: |
927 | Out->RegY = In->Ptr1Lo; |
928 | break; |
929 | case REG_PTR1_HI: |
930 | Out->RegY = In->Ptr1Hi; |
931 | break; |
932 | case REG_SREG_LO: |
933 | Out->RegY = In->SRegLo; |
934 | break; |
935 | case REG_SREG_HI: |
936 | Out->RegY = In->SRegHi; |
937 | break; |
938 | default: |
939 | Out->RegY = UNKNOWN_REGVAL; |
940 | break; |
941 | } |
942 | } else { |
943 | /* Y is now unknown */ |
944 | Out->RegY = UNKNOWN_REGVAL; |
945 | } |
946 | break; |
947 | |
948 | case OP65_LSR: |
949 | if (E->AM == AM65_ACC && RegValIsKnown (In->RegA)) { |
950 | Out->RegA = (In->RegA >> 1) & 0xFF; |
951 | } else if (E->AM == AM65_ZP) { |
952 | switch (GetKnownReg (E->Chg & REG_ZP, In)) { |
953 | case REG_TMP1: |
954 | Out->Tmp1 = (In->Tmp1 >> 1) & 0xFF; |
955 | break; |
956 | case REG_PTR1_LO: |
957 | Out->Ptr1Lo = (In->Ptr1Lo >> 1) & 0xFF; |
958 | break; |
959 | case REG_PTR1_HI: |
960 | Out->Ptr1Hi = (In->Ptr1Hi >> 1) & 0xFF; |
961 | break; |
962 | case REG_SREG_LO: |
963 | Out->SRegLo = (In->SRegLo >> 1) & 0xFF; |
964 | break; |
965 | case REG_SREG_HI: |
966 | Out->SRegHi = (In->SRegHi >> 1) & 0xFF; |
967 | break; |
968 | } |
969 | } else if (E->AM == AM65_ZPX) { |
970 | /* Invalidates all ZP registers */ |
971 | RC_InvalidateZP (Out); |
972 | } |
973 | break; |
974 | |
975 | case OP65_NOP: |
976 | break; |
977 | |
978 | case OP65_ORA: |
979 | if (RegValIsKnown (In->RegA)) { |
980 | if (CE_IsConstImm (E)) { |
981 | Out->RegA = In->RegA | (short) E->Num; |
982 | } else if (E->AM == AM65_ZP) { |
983 | switch (GetKnownReg (E->Use & REG_ZP, In)) { |
984 | case REG_TMP1: |
985 | Out->RegA = In->RegA | In->Tmp1; |
986 | break; |
987 | case REG_PTR1_LO: |
988 | Out->RegA = In->RegA | In->Ptr1Lo; |
989 | break; |
990 | case REG_PTR1_HI: |
991 | Out->RegA = In->RegA | In->Ptr1Hi; |
992 | break; |
993 | case REG_SREG_LO: |
994 | Out->RegA = In->RegA | In->SRegLo; |
995 | break; |
996 | case REG_SREG_HI: |
997 | Out->RegA = In->RegA | In->SRegHi; |
998 | break; |
999 | default: |
1000 | Out->RegA = UNKNOWN_REGVAL; |
1001 | break; |
1002 | } |
1003 | } else { |
1004 | /* A is now unknown */ |
1005 | Out->RegA = UNKNOWN_REGVAL; |
1006 | } |
1007 | } else if (CE_IsKnownImm (E, 0xFF)) { |
1008 | /* ORA with 0xFF does always give 0xFF */ |
1009 | Out->RegA = 0xFF; |
1010 | } |
1011 | break; |
1012 | |
1013 | case OP65_PHA: |
1014 | break; |
1015 | |
1016 | case OP65_PHP: |
1017 | break; |
1018 | |
1019 | case OP65_PHX: |
1020 | break; |
1021 | |
1022 | case OP65_PHY: |
1023 | break; |
1024 | |
1025 | case OP65_PLA: |
1026 | Out->RegA = UNKNOWN_REGVAL; |
1027 | break; |
1028 | |
1029 | case OP65_PLP: |
1030 | break; |
1031 | |
1032 | case OP65_PLX: |
1033 | Out->RegX = UNKNOWN_REGVAL; |
1034 | break; |
1035 | |
1036 | case OP65_PLY: |
1037 | Out->RegY = UNKNOWN_REGVAL; |
1038 | break; |
1039 | |
1040 | case OP65_ROL: |
1041 | /* We don't know the value of the carry bit */ |
1042 | if (E->AM == AM65_ACC) { |
1043 | Out->RegA = UNKNOWN_REGVAL; |
1044 | } else if (E->AM == AM65_ZP) { |
1045 | switch (GetKnownReg (E->Chg & REG_ZP, In)) { |
1046 | case REG_TMP1: |
1047 | Out->Tmp1 = UNKNOWN_REGVAL; |
1048 | break; |
1049 | case REG_PTR1_LO: |
1050 | Out->Ptr1Lo = UNKNOWN_REGVAL; |
1051 | break; |
1052 | case REG_PTR1_HI: |
1053 | Out->Ptr1Hi = UNKNOWN_REGVAL; |
1054 | break; |
1055 | case REG_SREG_LO: |
1056 | Out->SRegLo = UNKNOWN_REGVAL; |
1057 | break; |
1058 | case REG_SREG_HI: |
1059 | Out->SRegHi = UNKNOWN_REGVAL; |
1060 | break; |
1061 | } |
1062 | } else if (E->AM == AM65_ZPX) { |
1063 | /* Invalidates all ZP registers */ |
1064 | RC_InvalidateZP (Out); |
1065 | } |
1066 | break; |
1067 | |
1068 | case OP65_ROR: |
1069 | /* We don't know the value of the carry bit */ |
1070 | if (E->AM == AM65_ACC) { |
1071 | Out->RegA = UNKNOWN_REGVAL; |
1072 | } else if (E->AM == AM65_ZP) { |
1073 | switch (GetKnownReg (E->Chg & REG_ZP, In)) { |
1074 | case REG_TMP1: |
1075 | Out->Tmp1 = UNKNOWN_REGVAL; |
1076 | break; |
1077 | case REG_PTR1_LO: |
1078 | Out->Ptr1Lo = UNKNOWN_REGVAL; |
1079 | break; |
1080 | case REG_PTR1_HI: |
1081 | Out->Ptr1Hi = UNKNOWN_REGVAL; |
1082 | break; |
1083 | case REG_SREG_LO: |
1084 | Out->SRegLo = UNKNOWN_REGVAL; |
1085 | break; |
1086 | case REG_SREG_HI: |
1087 | Out->SRegHi = UNKNOWN_REGVAL; |
1088 | break; |
1089 | } |
1090 | } else if (E->AM == AM65_ZPX) { |
1091 | /* Invalidates all ZP registers */ |
1092 | RC_InvalidateZP (Out); |
1093 | } |
1094 | break; |
1095 | |
1096 | case OP65_RTI: |
1097 | break; |
1098 | |
1099 | case OP65_RTS: |
1100 | break; |
1101 | |
1102 | case OP65_SBC: |
1103 | /* We don't know the value of the carry bit */ |
1104 | Out->RegA = UNKNOWN_REGVAL; |
1105 | break; |
1106 | |
1107 | case OP65_SEC: |
1108 | break; |
1109 | |
1110 | case OP65_SED: |
1111 | break; |
1112 | |
1113 | case OP65_SEI: |
1114 | break; |
1115 | |
1116 | case OP65_STA: |
1117 | if (E->AM == AM65_ZP) { |
1118 | switch (GetKnownReg (E->Chg & REG_ZP, 0)) { |
1119 | case REG_TMP1: |
1120 | Out->Tmp1 = In->RegA; |
1121 | break; |
1122 | case REG_PTR1_LO: |
1123 | Out->Ptr1Lo = In->RegA; |
1124 | break; |
1125 | case REG_PTR1_HI: |
1126 | Out->Ptr1Hi = In->RegA; |
1127 | break; |
1128 | case REG_SREG_LO: |
1129 | Out->SRegLo = In->RegA; |
1130 | break; |
1131 | case REG_SREG_HI: |
1132 | Out->SRegHi = In->RegA; |
1133 | break; |
1134 | } |
1135 | } else if (E->AM == AM65_ZPX) { |
1136 | /* Invalidates all ZP registers */ |
1137 | RC_InvalidateZP (Out); |
1138 | } |
1139 | break; |
1140 | |
1141 | case OP65_STX: |
1142 | if (E->AM == AM65_ZP) { |
1143 | switch (GetKnownReg (E->Chg & REG_ZP, 0)) { |
1144 | case REG_TMP1: |
1145 | Out->Tmp1 = In->RegX; |
1146 | break; |
1147 | case REG_PTR1_LO: |
1148 | Out->Ptr1Lo = In->RegX; |
1149 | break; |
1150 | case REG_PTR1_HI: |
1151 | Out->Ptr1Hi = In->RegX; |
1152 | break; |
1153 | case REG_SREG_LO: |
1154 | Out->SRegLo = In->RegX; |
1155 | break; |
1156 | case REG_SREG_HI: |
1157 | Out->SRegHi = In->RegX; |
1158 | break; |
1159 | } |
1160 | } else if (E->AM == AM65_ZPX) { |
1161 | /* Invalidates all ZP registers */ |
1162 | RC_InvalidateZP (Out); |
1163 | } |
1164 | break; |
1165 | |
1166 | case OP65_STY: |
1167 | if (E->AM == AM65_ZP) { |
1168 | switch (GetKnownReg (E->Chg & REG_ZP, 0)) { |
1169 | case REG_TMP1: |
1170 | Out->Tmp1 = In->RegY; |
1171 | break; |
1172 | case REG_PTR1_LO: |
1173 | Out->Ptr1Lo = In->RegY; |
1174 | break; |
1175 | case REG_PTR1_HI: |
1176 | Out->Ptr1Hi = In->RegY; |
1177 | break; |
1178 | case REG_SREG_LO: |
1179 | Out->SRegLo = In->RegY; |
1180 | break; |
1181 | case REG_SREG_HI: |
1182 | Out->SRegHi = In->RegY; |
1183 | break; |
1184 | } |
1185 | } else if (E->AM == AM65_ZPX) { |
1186 | /* Invalidates all ZP registers */ |
1187 | RC_InvalidateZP (Out); |
1188 | } |
1189 | break; |
1190 | |
1191 | case OP65_STZ: |
1192 | if (E->AM == AM65_ZP) { |
1193 | switch (GetKnownReg (E->Chg & REG_ZP, 0)) { |
1194 | case REG_TMP1: |
1195 | Out->Tmp1 = 0; |
1196 | break; |
1197 | case REG_PTR1_LO: |
1198 | Out->Ptr1Lo = 0; |
1199 | break; |
1200 | case REG_PTR1_HI: |
1201 | Out->Ptr1Hi = 0; |
1202 | break; |
1203 | case REG_SREG_LO: |
1204 | Out->SRegLo = 0; |
1205 | break; |
1206 | case REG_SREG_HI: |
1207 | Out->SRegHi = 0; |
1208 | break; |
1209 | } |
1210 | } else if (E->AM == AM65_ZPX) { |
1211 | /* Invalidates all ZP registers */ |
1212 | RC_InvalidateZP (Out); |
1213 | } |
1214 | break; |
1215 | |
1216 | case OP65_TAX: |
1217 | Out->RegX = In->RegA; |
1218 | break; |
1219 | |
1220 | case OP65_TAY: |
1221 | Out->RegY = In->RegA; |
1222 | break; |
1223 | |
1224 | case OP65_TRB: |
1225 | if (E->AM == AM65_ZPX) { |
1226 | /* Invalidates all ZP registers */ |
1227 | RC_InvalidateZP (Out); |
1228 | } else if (E->AM == AM65_ZP) { |
1229 | if (RegValIsKnown (In->RegA)) { |
1230 | switch (GetKnownReg (E->Chg & REG_ZP, In)) { |
1231 | case REG_TMP1: |
1232 | Out->Tmp1 &= ~In->RegA; |
1233 | break; |
1234 | case REG_PTR1_LO: |
1235 | Out->Ptr1Lo &= ~In->RegA; |
1236 | break; |
1237 | case REG_PTR1_HI: |
1238 | Out->Ptr1Hi &= ~In->RegA; |
1239 | break; |
1240 | case REG_SREG_LO: |
1241 | Out->SRegLo &= ~In->RegA; |
1242 | break; |
1243 | case REG_SREG_HI: |
1244 | Out->SRegHi &= ~In->RegA; |
1245 | break; |
1246 | } |
1247 | } else { |
1248 | switch (GetKnownReg (E->Chg & REG_ZP, In)) { |
1249 | case REG_TMP1: |
1250 | Out->Tmp1 = UNKNOWN_REGVAL; |
1251 | break; |
1252 | case REG_PTR1_LO: |
1253 | Out->Ptr1Lo = UNKNOWN_REGVAL; |
1254 | break; |
1255 | case REG_PTR1_HI: |
1256 | Out->Ptr1Hi = UNKNOWN_REGVAL; |
1257 | break; |
1258 | case REG_SREG_LO: |
1259 | Out->SRegLo = UNKNOWN_REGVAL; |
1260 | break; |
1261 | case REG_SREG_HI: |
1262 | Out->SRegHi = UNKNOWN_REGVAL; |
1263 | break; |
1264 | } |
1265 | } |
1266 | } |
1267 | break; |
1268 | |
1269 | case OP65_TSB: |
1270 | if (E->AM == AM65_ZPX) { |
1271 | /* Invalidates all ZP registers */ |
1272 | RC_InvalidateZP (Out); |
1273 | } else if (E->AM == AM65_ZP) { |
1274 | if (RegValIsKnown (In->RegA)) { |
1275 | switch (GetKnownReg (E->Chg & REG_ZP, In)) { |
1276 | case REG_TMP1: |
1277 | Out->Tmp1 |= In->RegA; |
1278 | break; |
1279 | case REG_PTR1_LO: |
1280 | Out->Ptr1Lo |= In->RegA; |
1281 | break; |
1282 | case REG_PTR1_HI: |
1283 | Out->Ptr1Hi |= In->RegA; |
1284 | break; |
1285 | case REG_SREG_LO: |
1286 | Out->SRegLo |= In->RegA; |
1287 | break; |
1288 | case REG_SREG_HI: |
1289 | Out->SRegHi |= In->RegA; |
1290 | break; |
1291 | } |
1292 | } else { |
1293 | switch (GetKnownReg (E->Chg & REG_ZP, In)) { |
1294 | case REG_TMP1: |
1295 | Out->Tmp1 = UNKNOWN_REGVAL; |
1296 | break; |
1297 | case REG_PTR1_LO: |
1298 | Out->Ptr1Lo = UNKNOWN_REGVAL; |
1299 | break; |
1300 | case REG_PTR1_HI: |
1301 | Out->Ptr1Hi = UNKNOWN_REGVAL; |
1302 | break; |
1303 | case REG_SREG_LO: |
1304 | Out->SRegLo = UNKNOWN_REGVAL; |
1305 | break; |
1306 | case REG_SREG_HI: |
1307 | Out->SRegHi = UNKNOWN_REGVAL; |
1308 | break; |
1309 | } |
1310 | } |
1311 | } |
1312 | break; |
1313 | |
1314 | case OP65_TSX: |
1315 | Out->RegX = UNKNOWN_REGVAL; |
1316 | break; |
1317 | |
1318 | case OP65_TXA: |
1319 | Out->RegA = In->RegX; |
1320 | break; |
1321 | |
1322 | case OP65_TXS: |
1323 | break; |
1324 | |
1325 | case OP65_TYA: |
1326 | Out->RegA = In->RegY; |
1327 | break; |
1328 | |
1329 | default: |
1330 | break; |
1331 | |
1332 | } |
1333 | } |
1334 | |
1335 | |
1336 | |
1337 | static char* RegInfoDesc (unsigned U, char* Buf) |
1338 | /* Return a string containing register info */ |
1339 | { |
1340 | Buf[0] = '\0'; |
1341 | |
1342 | strcat (Buf, U & REG_SREG_HI? "H" : "_" ); |
1343 | strcat (Buf, U & REG_SREG_LO? "L" : "_" ); |
1344 | strcat (Buf, U & REG_A? "A" : "_" ); |
1345 | strcat (Buf, U & REG_X? "X" : "_" ); |
1346 | strcat (Buf, U & REG_Y? "Y" : "_" ); |
1347 | strcat (Buf, U & REG_TMP1? "T1" : "__" ); |
1348 | strcat (Buf, U & REG_PTR1? "1" : "_" ); |
1349 | strcat (Buf, U & REG_PTR2? "2" : "_" ); |
1350 | strcat (Buf, U & REG_SAVE? "V" : "_" ); |
1351 | strcat (Buf, U & REG_SP? "S" : "_" ); |
1352 | |
1353 | return Buf; |
1354 | } |
1355 | |
1356 | |
1357 | |
1358 | static char* RegContentDesc (const RegContents* RC, char* Buf) |
1359 | /* Return a string containing register contents */ |
1360 | { |
1361 | char* B = Buf; |
1362 | |
1363 | if (RegValIsUnknown (RC->RegA)) { |
1364 | strcpy (B, "A:XX " ); |
1365 | } else { |
1366 | sprintf (B, "A:%02X " , RC->RegA); |
1367 | } |
1368 | B += 5; |
1369 | if (RegValIsUnknown (RC->RegX)) { |
1370 | strcpy (B, "X:XX " ); |
1371 | } else { |
1372 | sprintf (B, "X:%02X " , RC->RegX); |
1373 | } |
1374 | B += 5; |
1375 | if (RegValIsUnknown (RC->RegY)) { |
1376 | strcpy (B, "Y:XX" ); |
1377 | } else { |
1378 | sprintf (B, "Y:%02X" , RC->RegY); |
1379 | } |
1380 | B += 4; |
1381 | |
1382 | return Buf; |
1383 | } |
1384 | |
1385 | |
1386 | |
1387 | void CE_Output (const CodeEntry* E) |
1388 | /* Output the code entry to the output file */ |
1389 | { |
1390 | const OPCDesc* D; |
1391 | unsigned Chars; |
1392 | int Space; |
1393 | const char* Target; |
1394 | |
1395 | /* If we have a label, print that */ |
1396 | unsigned LabelCount = CollCount (&E->Labels); |
1397 | unsigned I; |
1398 | for (I = 0; I < LabelCount; ++I) { |
1399 | CL_Output (CollConstAt (&E->Labels, I)); |
1400 | } |
1401 | |
1402 | /* Get the opcode description */ |
1403 | D = GetOPCDesc (E->OPC); |
1404 | |
1405 | /* Print the mnemonic */ |
1406 | Chars = WriteOutput ("\t%s" , D->Mnemo); |
1407 | |
1408 | /* Space to leave before the operand */ |
1409 | Space = 9 - Chars; |
1410 | |
1411 | /* Print the operand */ |
1412 | switch (E->AM) { |
1413 | |
1414 | case AM65_IMP: |
1415 | /* implicit */ |
1416 | break; |
1417 | |
1418 | case AM65_ACC: |
1419 | /* accumulator */ |
1420 | Chars += WriteOutput ("%*sa" , Space, "" ); |
1421 | break; |
1422 | |
1423 | case AM65_IMM: |
1424 | /* immidiate */ |
1425 | Chars += WriteOutput ("%*s#%s" , Space, "" , E->Arg); |
1426 | break; |
1427 | |
1428 | case AM65_ZP: |
1429 | case AM65_ABS: |
1430 | /* zeropage and absolute */ |
1431 | Chars += WriteOutput ("%*s%s" , Space, "" , E->Arg); |
1432 | break; |
1433 | |
1434 | case AM65_ZPX: |
1435 | case AM65_ABSX: |
1436 | /* zeropage,X and absolute,X */ |
1437 | Chars += WriteOutput ("%*s%s,x" , Space, "" , E->Arg); |
1438 | break; |
1439 | |
1440 | case AM65_ABSY: |
1441 | /* absolute,Y */ |
1442 | Chars += WriteOutput ("%*s%s,y" , Space, "" , E->Arg); |
1443 | break; |
1444 | |
1445 | case AM65_ZPX_IND: |
1446 | /* (zeropage,x) */ |
1447 | Chars += WriteOutput ("%*s(%s,x)" , Space, "" , E->Arg); |
1448 | break; |
1449 | |
1450 | case AM65_ZP_INDY: |
1451 | /* (zeropage),y */ |
1452 | Chars += WriteOutput ("%*s(%s),y" , Space, "" , E->Arg); |
1453 | break; |
1454 | |
1455 | case AM65_ZP_IND: |
1456 | /* (zeropage) */ |
1457 | Chars += WriteOutput ("%*s(%s)" , Space, "" , E->Arg); |
1458 | break; |
1459 | |
1460 | case AM65_BRA: |
1461 | /* branch */ |
1462 | Target = E->JumpTo? E->JumpTo->Name : E->Arg; |
1463 | Chars += WriteOutput ("%*s%s" , Space, "" , Target); |
1464 | break; |
1465 | |
1466 | default: |
1467 | Internal ("Invalid addressing mode" ); |
1468 | |
1469 | } |
1470 | |
1471 | /* Print usage info if requested by the debugging flag */ |
1472 | if (Debug) { |
1473 | char Use [128]; |
1474 | char Chg [128]; |
1475 | WriteOutput ("%*s; USE: %-12s CHG: %-12s SIZE: %u" , |
1476 | (int)(30-Chars), "" , |
1477 | RegInfoDesc (E->Use, Use), |
1478 | RegInfoDesc (E->Chg, Chg), |
1479 | E->Size); |
1480 | |
1481 | if (E->RI) { |
1482 | char RegIn[32]; |
1483 | char RegOut[32]; |
1484 | WriteOutput (" In %s Out %s" , |
1485 | RegContentDesc (&E->RI->In, RegIn), |
1486 | RegContentDesc (&E->RI->Out, RegOut)); |
1487 | } |
1488 | } |
1489 | |
1490 | /* Terminate the line */ |
1491 | WriteOutput ("\n" ); |
1492 | } |
1493 | |