struct / union in initializer, RFE #901.
[sdcc.git] / sdcc / src / mos6502 / gen.c
blobad923ebfa86349103c1be092bdf45d42ab92a8e5
1 /*-------------------------------------------------------------------------
2 gen.c - source file for code generation for the MOS6502
4 Copyright (C) 1998, Sandeep Dutta . sandeep.dutta@usa.net
5 Copyright (C) 1999, Jean-Louis VERN.jlvern@writeme.com
6 Bug Fixes - Wojciech Stryjewski wstryj1@tiger.lsu.edu (1999 v2.1.9a)
7 Hacked for the HC08:
8 Copyright (C) 2003, Erik Petrich
9 Hacked for the MOS6502:
10 Copyright (C) 2020, Steven Hugg hugg@fasterlight.com
11 Copyright (C) 2021-2024, Gabriele Gorla
13 This program is free software; you can redistribute it and/or modify it
14 under the terms of the GNU General Public License as published by the
15 Free Software Foundation; either version 2, or (at your option) any
16 later version.
18 This program is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26 -------------------------------------------------------------------------*/
28 #include "m6502.h"
29 #include "ralloc.h"
30 #include "gen.h"
31 #include "dbuf_string.h"
33 enum debug_messages {
34 ALWAYS=0x01,
35 VASM=0x02,
36 TRACEGEN=0x04,
37 DEEPTRACE=0x08,
38 COST=0x10,
39 REGALLOC=0x20,
40 REGOPS=0x40,
41 TRACE_AOP=0x80,
42 TRACE_STACK=0x100,
43 VVDBG=0x80000000,
44 DEBUG_ALL=0x7fffffff
47 //#define DBG_MSG (REGALLOC)
48 #define DBG_MSG (REGALLOC|TRACEGEN/*|COST*/)
49 //#define DBG_MSG (DEBUG_ALL/*|VVDBG*/)
50 //#define DBG_MSG ((DEBUG_ALL|VVDBG)&~COST)
52 #define DEBUG_UNIMPLEMENTED
54 extern int allocInfo;
56 static bool regalloc_dry_run;
57 static unsigned int regalloc_dry_run_cost_bytes;
58 static float regalloc_dry_run_cost_cycles;
60 struct attr_t
62 bool isLiteral;
63 unsigned char literalValue;
66 // keep this in sync with _temp.s in the library
67 #define NUM_TEMP_REGS 8
69 static struct
71 int stackOfs;
72 int funcHasBasePtr;
73 int stackPushes;
74 int tsxStackPushes;
75 // int baseStackPushes;
76 set *sendSet;
77 int tempOfs;
78 unsigned carryValid:1;
79 unsigned carry:1;
80 int lastflag;
81 struct attr_t tempAttr[NUM_TEMP_REGS];
82 struct attr_t DPTRAttr[2];
83 } _G;
85 extern int m6502_ptrRegReq;
86 extern int m6502_nRegs;
87 extern struct dbuf_s *codeOutBuf;
89 static void pushReg (reg_info * reg, bool freereg);
90 static bool pushRegIfUsed (reg_info *reg);
91 static bool pushRegIfSurv (reg_info *reg);
92 static void pullReg (reg_info * reg);
93 static void pullOrFreeReg (reg_info * reg, bool needpull);
95 static void storeRegTempi (reg_info * reg, bool freereg, bool force);
96 static void storeRegTemp (reg_info * reg, bool freereg);
97 static void storeRegTempAlways (reg_info * reg, bool freereg);
98 static bool storeRegTempIfUsed (reg_info *reg);
99 static bool storeRegTempIfSurv (reg_info *reg);
100 static void loadRegTemp (reg_info * reg);
101 static void loadOrFreeRegTemp (reg_info * reg, bool needload);
102 static void emitRegTempOp(char *op, int offset);
105 static void adjustStack (int n);
106 static void loadRegFromConst (reg_info * reg, int c);
108 static void transferAopAop (asmop * srcaop, int srcofs, asmop * dstaop, int dstofs);
109 static bool operandsEqu (operand * op1, operand * op2);
110 static asmop *newAsmop (short type);
111 static void aopAdrPrepare (asmop * aop, int loffset);
112 static const char *aopAdrStr (asmop * aop, int loffset, bool bit16);
113 static void aopAdrUnprepare (asmop * aop, int loffset);
114 static void updateiTempRegisterUse (operand * op);
115 static void rmwWithReg (char *rmwop, reg_info * reg);
116 static void doTSX(void);
117 static char * aopName (asmop * aop);
119 static asmop *m6502_aop_pass[8];
120 static asmop tsxaop;
122 static char *IMMDFMT = "#0x%02x";
123 static char *TEMPFMT = "*(REGTEMP+%d)";
124 static char *TEMPFMT_IND = "[REGTEMP+%d]";
125 //static char *TEMPFMT_IY = "[REGTEMP+%d],y";
127 //static char *IDX_FMT = "0x%x,x";
128 //static char *TEMPFMT_IX = "[(REGTEMP+%d),x]";
129 static char *DPTRFMT = "*(DPTR+%d)";
130 static char *INDFMT_IY = "[DPTR],y";
133 const int STACK_TOP = 0x100;
136 #define RESULTONSTACK(x) \
137 (IC_RESULT(x) && IC_RESULT(x)->aop && \
138 IC_RESULT(x)->aop->type == AOP_STK )
139 #define IS_AOP_A(x) ((x)->regmask == M6502MASK_A)
140 #define IS_AOP_X(x) ((x)->regmask == M6502MASK_X)
141 #define IS_AOP_Y(x) ((x)->regmask == M6502MASK_Y)
142 #define IS_AOP_XA(x) ((x)->regmask == M6502MASK_XA)
143 #define IS_AOP_YX(x) ((x)->regmask == M6502MASK_YX)
144 #define IS_AOP_WITH_A(x) (((x)->regmask & M6502MASK_A) != 0)
145 #define IS_AOP_WITH_X(x) (((x)->regmask & M6502MASK_X) != 0)
146 #define IS_AOP_WITH_Y(x) (((x)->regmask & M6502MASK_Y) != 0)
148 #define IS_AOPOFS_A(x,o) (((x)->type == AOP_REG) && ((x)->aopu.aop_reg[o]->mask == M6502MASK_A))
149 #define IS_AOPOFS_X(x,o) (((x)->type == AOP_REG) && ((x)->aopu.aop_reg[o]->mask == M6502MASK_X))
150 #define IS_AOPOFS_Y(x,o) (((x)->type == AOP_REG) && ((x)->aopu.aop_reg[o]->mask == M6502MASK_Y))
152 #define safeNewiTempLabel(a) ((regalloc_dry_run)?0:newiTempLabel(a))
153 #define safeEmitLabel(a) do { if(!regalloc_dry_run && a) emitLabel(a); _G.lastflag=-1; _G.carryValid=0; } while(0)
156 #define LSB 0
157 #define MSB16 1
158 #define MSB24 2
159 #define MSB32 3
161 #define AOP(op) op->aop
162 #define AOP_TYPE(op) AOP(op)->type
163 #define AOP_SIZE(op) AOP(op)->size
164 #define AOP_OP(aop) aop->op
166 /**************************************************************************
167 * Returns the number for the label
169 * @param a pointer to the label symbol
170 * @return label number
171 *************************************************************************/
173 safeLabelNum(symbol * a)
175 if(regalloc_dry_run) return 0;
177 if(a) return labelKey2num(a->key);
179 emitcode("ERROR"," %s : called with NULL symbol", __func__ );
180 return 0;
183 /**************************************************************************
184 * Returns the cycle count for the instruction
186 * @param opcode pointer to opcode entry in the opcode table
187 * @param arg string constant with the opcode argument
188 * @return minimum number of cycles for the instruction
189 *************************************************************************/
191 m6502_opcodeCycles(const m6502opcodedata *opcode, const char *arg)
193 int lastpos;
195 lastpos=(*arg)?strlen(arg)-1:0;
197 switch (opcode->type)
199 case M6502OP_INH: /* Inherent addressing mode */
200 case M6502OP_IDD:
201 case M6502OP_IDI:
202 case M6502OP_BR: /* Branch (1 byte signed offset) */
203 if(opcode->name[0]=='r'&&opcode->name[1]=='t') // rti and rts
204 return 6;
205 return 2;
206 case M6502OP_SPH:
207 return 3;
208 case M6502OP_SPL:
209 return 4;
210 case M6502OP_BBR: /* Branch on bit (1 byte signed offset) */
211 return 3;
212 case M6502OP_RMW: /* read/modify/write instructions */
213 if (!strcmp(arg, "a")) /* accumulator */
214 return 2;
215 if (arg[0] == '*') /* Zero page */
216 return 5;
217 if(lastpos>2 && arg[lastpos-1]!=',' && arg[lastpos]=='x' )
218 return 7;
219 return 6; /* absolute */
221 case M6502OP_REG: /* standard instruction */
222 case M6502OP_CMP:
223 case M6502OP_LD:
224 if (arg[0] == '#') /* Immediate addressing mode */
225 return 2;
226 if (arg[0] == '*') { /* Zero page */
227 if(arg[lastpos]=='x' || arg[lastpos]=='y')
228 return 4;
229 return 3;
231 if (arg[0] == '[') { /* indirect */
232 if(arg[lastpos]==']')
233 return 6;
234 return 5;
236 return 4; /* Otherwise, must be absolute addressing mode */
238 case M6502OP_ST:
239 if (arg[0] == '*') { /* Zero page */
240 if(arg[lastpos]=='x' || arg[lastpos]=='y')
241 return 4;
242 return 3;
244 if (arg[0] == '[') /* indirect */
245 return 6;
246 if(arg[lastpos]=='x' || arg[lastpos]=='y')
247 return 5;
248 return 4;
250 case M6502OP_JMP:
251 if(opcode->name[1]=='s') return 6;
252 if(arg[0]=='[') return 5;
253 return 3;
254 default:
255 werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "unknown instruction type in m6502_opcodeSize");
256 return 3;
260 /**************************************************************************
261 * Emits comments and debug messages
263 * @param level bitfield based on enum debug_messages
264 * @param fmt string constant or printf style format string
265 *************************************************************************/
266 void
267 emitComment (unsigned int level, const char *fmt, ...)
269 bool print=false;
270 va_list ap;
272 va_start (ap, fmt);
274 if ( level&DBG_MSG ) {
275 if(!(level&VVDBG)) print=true;
276 else if(DBG_MSG&VVDBG) print=true;
278 if (level==VASM && options.verboseAsm) print=true;
279 if (level==ALWAYS) print=true;
281 if(print) va_emitcode (";", fmt, ap);
282 va_end (ap);
285 /**************************************************************************
286 * Returns register state as a string
288 * @return
289 *************************************************************************/
290 const char * regInfoStr()
292 static char outstr[40];
293 char regstring[3][10];
294 char *flagreg;
296 if(m6502_reg_a->isLitConst) snprintf(regstring[0],10,"A:%c%c:#%02x ",
297 (m6502_reg_a->isFree)?'-':'U',
298 (m6502_reg_a->isDead)?'-':'L',
299 m6502_reg_a->litConst );
300 else if(m6502_reg_a->aop == &tsxaop) snprintf(regstring[0],10,"A:%c%c:S%+-3d",
301 (m6502_reg_a->isFree)?'-':'U',
302 (m6502_reg_a->isDead)?'-':'L',
303 _G.tsxStackPushes - _G.stackPushes );
304 else snprintf(regstring[0],10,"A:%c%c:??? ",
305 (m6502_reg_a->isFree)?'-':'U',
306 (m6502_reg_a->isDead)?'-':'L');
308 if(m6502_reg_x->isLitConst) snprintf(regstring[1],10,"X:%c%c:#%02x ",
309 (m6502_reg_x->isFree)?'-':'U',
310 (m6502_reg_x->isDead)?'-':'L',
311 m6502_reg_x->litConst );
312 else if(m6502_reg_x->aop == &tsxaop) snprintf(regstring[1],10,"X:%c%c:S%+-3d",
313 (m6502_reg_x->isFree)?'-':'U',
314 (m6502_reg_x->isDead)?'-':'L',
315 _G.tsxStackPushes - _G.stackPushes );
316 else snprintf(regstring[1],10,"X:%c%c:??? ",
317 (m6502_reg_x->isFree)?'-':'U',
318 (m6502_reg_x->isDead)?'-':'L');
320 if(m6502_reg_y->isLitConst) snprintf(regstring[2],10,"Y:%c%c:#%02x ",
321 (m6502_reg_y->isFree)?'-':'U',
322 (m6502_reg_y->isDead)?'-':'L',
323 m6502_reg_y->litConst );
324 else if(m6502_reg_y->aop == &tsxaop) snprintf(regstring[2],10,"Y:%c%c:S%+-3d",
325 (m6502_reg_y->isFree)?'-':'U',
326 (m6502_reg_y->isDead)?'-':'L',
327 _G.tsxStackPushes - _G.stackPushes );
328 else snprintf(regstring[2],10,"Y:%c%c:??? ",
329 (m6502_reg_y->isFree)?'-':'U',
330 (m6502_reg_y->isDead)?'-':'L');
332 flagreg = (_G.lastflag>=0)?m6502_regWithIdx(_G.lastflag)->name:"?";
334 char carry = (_G.carryValid)?((_G.carry)?'1':'0'):'?';
336 snprintf(outstr, 40, "%s %s %s F:%s C:%c",
337 regstring[0], regstring[1], regstring[2], flagreg, carry );
339 return outstr;
342 /**************************************************************************
343 * Returns operand information in the passed string
345 * @return
346 *************************************************************************/
347 char *
348 opInfo(char str[64], operand *op)
350 int size = 0;
351 char *type = "";
353 if(op) {
354 if(AOP(op)) size=AOP_SIZE(op);
355 if(AOP(op)) type=aopName(AOP(op));
358 if(op==0) {
359 snprintf(str, 64, "---");
360 } else if(IS_SYMOP(op)) {
361 if (snprintf(str, 64, "SYM:%s(%s:%d)", op->svt.symOperand->rname, type, size) >= 64) {
362 str[63] = 0; // ridiculous workaround to silence GCC warning ‘%s’ directive output may be truncated
364 } else if(IS_VALOP(op)) {
365 snprintf(str, 64, "VAL(%s:%d)", type, size);
366 } else if(IS_TYPOP(op)) {
367 snprintf(str, 64, "TYP");
368 } else {
369 snprintf(str, 64, "???");
372 return str;
375 /**************************************************************************
376 * Prints iCode debug information
378 * @return
379 *************************************************************************/
380 void
381 printIC(iCode *ic)
383 operand *left, *right, *result;
384 char tmpstr[3][64];
386 left = IC_LEFT (ic);
387 right = IC_RIGHT (ic);
388 result = IC_RESULT (ic);
390 opInfo(tmpstr[0], result);
391 opInfo(tmpstr[1], left);
392 opInfo(tmpstr[2], right);
394 emitComment (TRACEGEN|VVDBG, " [%s] = [%s] %s [%s]", tmpstr[0],
395 tmpstr[1], getTableEntry (ic->op)->printName, tmpstr[2]);
398 /**************************************************************************
399 * emit6502op - emits opcopdes, updates cost and register state
401 * @param inst string containing the opcode
402 * @param fmt string operands or printf style format string
403 *************************************************************************/
404 void
405 emit6502op (const char *inst, const char *fmt, ...)
407 static char verboseFmt[512];
408 va_list ap;
409 int isize = 0;
410 float cycles = 0;
411 float probability=1;
413 const m6502opcodedata *opcode = m6502_getOpcodeData(inst);
415 if(fmt==0) werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "NULL fmt in emit6502op");
418 if(opcode)
420 isize = m6502_opcodeSize(opcode, fmt);
421 cycles = m6502_opcodeCycles(opcode, fmt);
422 reg_info *dst_reg = (opcode->dest>=0)?m6502_regWithIdx(opcode->dest):NULL;
424 if(opcode->flags&0x82) // zero and negative flags
425 _G.lastflag=opcode->dest;
426 if(opcode->flags&0x01)
427 { // carry flag
428 _G.carryValid=0;
432 // mark the destination register dirty as necessary
433 // transfers are handled in the instruction generator
434 switch (opcode->type)
436 case M6502OP_LD:
437 if(fmt[0]!='#' || !isdigit(fmt[1]))
438 m6502_dirtyReg(dst_reg);
439 break;
440 case M6502OP_REG: // target is accumulator
441 #if 1
442 if(dst_reg && dst_reg->isLitConst &&
443 fmt[0]=='#' && isdigit(fmt[1]))
445 unsigned char b=strtol(&fmt[1],NULL,0);
446 if(!strcmp(inst,"and")) {
447 dst_reg->litConst&=b;
448 break;
450 if(!strcmp(inst,"ora"))
452 dst_reg->litConst|=b;
453 break;
455 if(!strcmp(inst,"eor"))
457 dst_reg->litConst^=b;
458 break;
461 #endif
462 if(dst_reg) m6502_dirtyReg (dst_reg);
463 break;
464 case M6502OP_CMP:
465 if(!strcmp(fmt,"#0x00"))
467 if(inst[2]=='p') _G.lastflag=A_IDX;
468 else if(inst[2]=='x') _G.lastflag=X_IDX;
469 else if(inst[2]=='y') _G.lastflag=Y_IDX;
470 _G.carryValid=1;
471 _G.carry=1;
473 break;
474 case M6502OP_INH:
475 if(!strcmp(inst,"clc"))
477 _G.carryValid=1;
478 _G.carry=0;
479 break;
481 if(!strcmp(inst,"sec"))
483 _G.carryValid=1;
484 _G.carry=1;
485 break;
487 break;
488 case M6502OP_RMW: // target is accumulator
489 if (!strcmp(fmt, "a"))
491 m6502_dirtyReg (m6502_reg_a);
492 _G.lastflag=A_IDX;
494 // FIXME: add 65c02 INC/DEC A literal
495 break;
496 case M6502OP_SPL: // stack pull
497 _G.stackPushes--;
498 break;
499 case M6502OP_SPH: // stack push
500 _G.stackPushes++;
501 break;
502 case M6502OP_IDD: // index decrement
503 if(dst_reg->isLitConst)
504 dst_reg->litConst--;
505 if(dst_reg->aop==&tsxaop)
506 _G.tsxStackPushes++;
507 break;
508 case M6502OP_IDI: // index increment
509 if(dst_reg->isLitConst)
510 dst_reg->litConst++;
511 if(dst_reg->aop==&tsxaop)
512 _G.tsxStackPushes--;
513 break;
514 case M6502OP_BR: // add penalty for taken branches
515 // this assumes:
516 // 50% not taken (2 cycles)
517 // 40% taken with target in the same page (3 cycles)
518 // 10% taken with target in a different page (4 cycles)
519 cycles += (0.4 * 1) + (0.1 * 2);
520 break;
521 case M6502OP_ST:
522 case M6502OP_JMP:
523 case M6502OP_BBR:
524 break;
527 else
529 emitcode("ERROR","Unimplemented opcode %s", inst);
530 isize=10;
531 //werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "NULL opcode in emit6502op");
534 regalloc_dry_run_cost_bytes += isize;
535 regalloc_dry_run_cost_cycles += cycles * probability;
537 va_start (ap, fmt);
538 if (options.verboseAsm)
540 char dstring[3][64];
541 dstring[0][0]=0;
542 dstring[1][0]=0;
543 dstring[2][0]=0;
545 if (DBG_MSG&COST)
547 snprintf(dstring[0], 64, "sz=%d cl=%f p=%f",
548 isize, cycles, probability);
551 if (DBG_MSG&REGALLOC)
553 snprintf(dstring[1], 64, "%s",
554 regInfoStr() );
556 if (DBG_MSG&TRACE_STACK)
558 snprintf(dstring[2], 64, "stkpush=%d",
559 _G.stackPushes );
562 snprintf(verboseFmt, 512, "%s \t; %s %s %s",
563 fmt, dstring[0], dstring[1], dstring[2]);
564 va_emitcode (inst, verboseFmt, ap);
566 else
568 va_emitcode (inst, fmt, ap);
570 va_end (ap);
573 /**************************************************************************
574 * m6502_unimplemented
576 *************************************************************************/
577 static void
578 m6502_unimplemented(const char *msg)
580 emitcode("ERROR","Unimplemented %s", msg);
581 #ifndef DEBUG_UNIMPLEMENTED
582 regalloc_dry_run_cost_bytes += 500;
583 regalloc_dry_run_cost_cycles += 500;
584 #else
585 regalloc_dry_run_cost_bytes = 0;
586 regalloc_dry_run_cost_cycles = 0;
587 #endif
590 /**************************************************************************
591 * emitSignedBranch
593 *************************************************************************/
594 static void
595 emitSignedBranch (bool gt, bool eq, symbol * tlbl)
597 symbol *tlbl2 = safeNewiTempLabel (NULL);
598 symbol *tlbl3 = safeNewiTempLabel (NULL);
600 if (eq && !gt)
601 emit6502op ("beq", "%05d$", safeLabelNum (tlbl));
602 if (!eq && gt)
603 emit6502op ("beq", "%05d$", safeLabelNum (tlbl2));
604 emit6502op (gt ? "bvs" : "bvc", "%05d$", safeLabelNum (tlbl2));
605 emit6502op ("bpl", "%05d$", safeLabelNum (tlbl));
606 emit6502op ("bmi", "%05d$", safeLabelNum (tlbl3));
607 safeEmitLabel (tlbl2);
608 emit6502op ("bmi", "%05d$", safeLabelNum (tlbl));
609 safeEmitLabel (tlbl3);
612 /**************************************************************************
613 * emitUnsignedBranch
615 *************************************************************************/
616 static void
617 emitUnsignedBranch (bool gt, bool eq, symbol * tlbl)
619 symbol *tlbl2 = safeNewiTempLabel (NULL);
621 if (eq && !gt)
622 emit6502op ("beq", "%05d$", safeLabelNum (tlbl));
623 if (!eq && gt)
624 emit6502op ("beq", "%05d$", safeLabelNum (tlbl2));
625 emit6502op (gt ? "bcs" : "bcc", "%05d$", safeLabelNum (tlbl));
626 safeEmitLabel (tlbl2);
629 /**************************************************************************
630 * emitBranch
632 *************************************************************************/
633 static void
634 emitBranch (char *branchop, symbol * tlbl)
636 if (!strcmp("bls", branchop))
638 emitUnsignedBranch(0, 1, tlbl);
640 else if (!strcmp("bhi", branchop))
642 emitUnsignedBranch(1, 0, tlbl);
644 else if (!strcmp("blt", branchop))
646 emitSignedBranch(0, 0, tlbl);
648 else if (!strcmp("bgt", branchop))
650 emitSignedBranch(1, 0, tlbl);
652 else if (!strcmp("ble", branchop))
654 emitSignedBranch(0, 1, tlbl);
656 else if (!strcmp("bge", branchop))
658 emitSignedBranch(1, 1, tlbl);
660 else
662 if (!IS_MOS65C02 && !strcmp(branchop, "bra"))
663 branchop = "jmp";
664 emit6502op (branchop, "%05d$", safeLabelNum (tlbl));
668 /**************************************************************************
669 * emitSetCarry - emit CLC/SEC if necessary
671 * @param c carry value to set
672 *************************************************************************/
673 static void
674 emitSetCarry(int c)
676 if(_G.carryValid && _G.carry==c)
677 return;
678 if(c)
679 emit6502op("sec", "");
680 else
681 emit6502op ("clc", "");
684 /**************************************************************************
685 * emitCpz - emit CMP/CPX/CPY with #0x00 if necessary
687 * @param reg_idx register index
688 * @return return true if the instruction was necessary
689 *************************************************************************/
690 static bool
691 emitCpz (int reg_idx)
693 if(_G.lastflag==reg_idx) return false;
695 switch(reg_idx)
697 case A_IDX:
698 emit6502op("cmp", "#0x00");
699 break;
700 case X_IDX:
701 emit6502op("cpx", "#0x00");
702 break;
703 case Y_IDX:
704 emit6502op("cpy", "#0x00");
705 break;
706 default:
707 emitcode ("ERROR", "illegal %d reg_idx in emitCpz", reg_idx);
708 break;
710 return true;
713 /**************************************************************************
714 * Adjust register by n bytes if possible.
716 * @param reg pointer to the register to adjust
717 * @param n amount of adjustment (can be positive or negative)
718 * @return return true if the ajdust was performed
719 *************************************************************************/
720 bool
721 smallAdjustReg (reg_info *reg, int n)
723 int regidx = reg->rIdx;
724 emitComment (REGOPS, __func__ );
726 if( (regidx!=X_IDX) && (regidx!=Y_IDX) && !IS_MOS65C02)
727 return false;
729 if (n <= -4 || n >= 4)
731 return false;
734 while (n < 0)
736 rmwWithReg ("dec", reg); /* 1 byte, 2 cycles */
737 n++;
739 while (n > 0)
741 rmwWithReg ("inc", reg); /* 1 byte, 2 cycles */
742 n--;
744 return true;
747 /**************************************************************************
748 * Associate the current code location with a debugger symbol
749 *************************************************************************/
750 void
751 m6502_emitDebuggerSymbol (const char *debugSym)
753 genLine.lineElement.isDebug = 1;
754 emitcode ("", "%s ==.", debugSym);
755 genLine.lineElement.isDebug = 0;
758 /**************************************************************************
759 * Transfer from register(s) sreg to register(s) dreg.
760 * If freesrc is true, sreg is marked free and available for reuse.
761 * sreg and dreg must be of equal size
763 * @param sreg pointer to the source register
764 * @param dreg pointer to the destination register
765 * @param freesrc free the source register if true
766 *************************************************************************/
767 static void
768 transferRegReg (reg_info *sreg, reg_info *dreg, bool freesrc)
770 int srcidx;
771 int dstidx;
772 char error = 0;
774 /* Nothing to do if no destination. */
775 if (!dreg)
776 return;
778 /* But it's definitely an error if there's no source. */
779 if (!sreg)
781 // emitcode("ERROR","%s: src reg is null", __func__);
782 werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "NULL sreg in transferRegReg");
783 return;
786 emitComment (REGOPS, " %s(%s,%s)", __func__, sreg->name, dreg->name);
787 emitComment (REGOPS, " %s %s", __func__, regInfoStr() );
789 srcidx = sreg->rIdx;
790 dstidx = dreg->rIdx;
792 if (srcidx == dstidx)
794 emitComment (REGOPS|VVDBG, " %s: sameregs", __func__);
795 m6502_useReg (dreg);
796 return;
799 // TODO: make sure regs are killed if clobbered
800 switch (dstidx)
802 case A_IDX:
803 switch (srcidx) {
804 case Y_IDX: /* Y to A */
805 emit6502op ("tya", "");
806 break;
807 case X_IDX: /* X to A */
808 emit6502op ("txa", "");
809 break;
810 default:
811 error = 1;
813 break;
814 case X_IDX:
815 switch (srcidx)
817 case A_IDX: /* A to X */
818 emit6502op ("tax", "");
819 break;
820 case Y_IDX: /* Y to X */
821 if(m6502_reg_y->isLitConst) {
822 loadRegFromConst (m6502_reg_x, m6502_reg_y->litConst);
823 } else if(m6502_reg_a->isFree) {
824 emit6502op ("tya", "");
825 emit6502op ("tax", "");
826 } else {
827 if (IS_MOS65C02) {
828 emit6502op ("phy", "");
829 emit6502op ("plx", "");
830 } else {
831 storeRegTemp (m6502_reg_y, false);
832 loadRegTemp (m6502_reg_x);
835 break;
836 default:
837 error = 1;
839 break;
840 case Y_IDX:
841 switch (srcidx)
843 case A_IDX: /* A to Y */
844 emit6502op ("tay", "");
845 break;
846 case X_IDX: /* X to Y */
847 if(m6502_reg_x->isLitConst) {
848 loadRegFromConst (m6502_reg_y, m6502_reg_x->litConst);
850 else if(m6502_reg_a->isFree)
852 emit6502op ("txa", "");
853 emit6502op ("tay", "");
855 else
857 if (IS_MOS65C02)
859 emit6502op ("phx", "");
860 emit6502op ("ply", "");
862 else
864 storeRegTemp (m6502_reg_x, false);
865 loadRegTemp (m6502_reg_y);
868 break;
869 default:
870 error = 1;
872 break;
873 case XA_IDX:
874 switch (srcidx)
876 case YX_IDX: /* YX to XA */
877 transferRegReg(m6502_reg_x, m6502_reg_a, true);
878 storeRegTemp (m6502_reg_y, true);
879 loadRegTemp (m6502_reg_x);
880 break;
881 default:
882 error = 1;
884 break;
885 case YX_IDX:
886 switch (srcidx)
888 case XA_IDX: /* XA to YX */
889 storeRegTemp (m6502_reg_x, false);
890 transferRegReg(m6502_reg_a, m6502_reg_x, true);
891 loadRegTemp (m6502_reg_y);
892 break;
893 default:
894 error = 1;
896 break;
897 default:
898 error = 1;
901 if(error) emitcode("ERROR", "bad combo in transferRegReg 0x%02x -> 0x%02x", srcidx, dstidx);
903 m6502_useReg (dreg);
905 if(sreg->isLitConst)
907 dreg->isLitConst = sreg->isLitConst;
908 dreg->litConst = sreg->litConst;
910 else
912 m6502_dirtyReg (dreg);
915 dreg->aop = sreg->aop;
916 dreg->aopofs = sreg->aopofs;
918 if (freesrc)
919 m6502_freeReg (sreg);
922 /**************************************************************************
923 * updateCFA - update the debugger information to reflect the current
924 * canonical frame address relative to the stack pointer
925 *************************************************************************/
926 static void
927 updateCFA (void)
929 /* there is no frame unless there is a function */
930 if (!currFunc)
931 return;
933 if (options.debug && !regalloc_dry_run)
934 debugFile->writeFrameAddress (NULL, m6502_reg_sp, 1 + _G.stackOfs + _G.stackPushes);
937 static void
938 emitRegTempOp( char *op, int offset)
940 // emitcode(";", "%s : op %s at ofs %d isLit %d const = 0x%02x",
941 // __func__, op, offset, _G.tempAttr[offset].isLiteral,
942 // _G.tempAttr[offset].literalValue );
944 if(!_G.tempAttr[offset].isLiteral)
946 emit6502op(op, TEMPFMT, offset);
948 else
950 emit6502op(op, IMMDFMT, _G.tempAttr[offset].literalValue );
954 static void
955 storeRegTemp (reg_info * reg, bool freereg)
957 storeRegTempi (reg, freereg, false);
960 static void
961 storeRegTempAlways (reg_info * reg, bool freereg)
963 storeRegTempi (reg, freereg, true);
966 /**************************************************************************
967 * Store register onto the REGTEMP stack. If freereg is true,
968 * reg is marked free and available for reuse. If force is true,
969 * no literal optimizations are performed.
971 * @param reg pointer for the register to save
972 * @param freereg free the register if true
973 *************************************************************************/
974 static void
975 storeRegTempi (reg_info * reg, bool freereg, bool force)
977 emitComment (REGOPS, " storeRegTemp(%s) %s", reg ? reg->name : "-", freereg ? "free" : "");
979 int regidx = reg->rIdx;
980 char storeOp[4] = "st?";
982 switch (regidx)
984 case A_IDX:
985 case X_IDX:
986 case Y_IDX:
987 storeOp[2]=reg->name[0];
988 _G.tempAttr[_G.tempOfs].isLiteral=reg->isLitConst;
989 _G.tempAttr[_G.tempOfs].literalValue=reg->litConst;
990 if(!reg->isLitConst || force)
991 emit6502op (storeOp, TEMPFMT, _G.tempOfs);
992 _G.tempOfs++;
993 break;
994 case XA_IDX:
995 storeRegTempi (m6502_reg_a, freereg, force);
996 storeRegTempi (m6502_reg_x, freereg, force);
997 break;
998 case YX_IDX:
999 storeRegTempi (m6502_reg_x, freereg, force);
1000 storeRegTempi (m6502_reg_y, freereg, force);
1001 break;
1002 default:
1003 emitcode("ERROR", "%s : bad reg %02x (%s)", __func__, regidx, reg->name);
1004 break;
1007 if (freereg)
1008 m6502_freeReg (reg);
1010 if(_G.tempOfs > NUM_TEMP_REGS)
1011 emitcode("ERROR", "storeRegTemp(): overflow");
1014 /**************************************************************************
1015 * Store register onto the REGTEMP stack if register is alive
1017 * @param reg pointer for the register to save
1018 * @return true if the register was saved
1019 *************************************************************************/
1020 static bool
1021 storeRegTempIfSurv (reg_info *reg)
1023 if (!reg->isDead)
1025 storeRegTemp (reg, true);
1026 return true;
1028 return false;
1031 /**************************************************************************
1032 * Store register onto the REGTEMP stack if register is in use
1034 * @param reg pointer for the register to save
1035 * @return true if the register was saved
1036 *************************************************************************/
1037 static bool
1038 storeRegTempIfUsed (reg_info *reg)
1040 if (!reg->isFree)
1042 storeRegTemp (reg, true);
1043 return true;
1045 return false;
1048 /**************************************************************************
1049 * Load register from the REGTEMP stack at an arbitrary offset
1051 * @param reg pointer for the register to save
1052 *************************************************************************/
1053 static void
1054 loadRegTempAt (reg_info * reg, int offset)
1056 int regidx = reg->rIdx;
1057 char loadOp[4] = "ld?";
1059 if(_G.tempAttr[offset].isLiteral)
1061 loadRegFromConst(reg, _G.tempAttr[offset].literalValue);
1062 return;
1065 switch (regidx)
1067 case A_IDX:
1068 case X_IDX:
1069 case Y_IDX:
1070 loadOp[2]=reg->name[0];
1071 emit6502op (loadOp, TEMPFMT, offset);
1072 break;
1073 default:
1074 emitcode("ERROR","loadRegTempAt called with illegal regidx %d", regidx);
1077 m6502_useReg (reg);
1078 m6502_dirtyReg (reg);
1081 /**************************************************************************
1082 * Load register from the REGTEMP stack.
1084 * @param reg pointer for the register to save
1085 *************************************************************************/
1086 static void
1087 loadRegTemp (reg_info * reg)
1089 // pop off stack, unused
1090 if (reg == NULL)
1092 _G.tempOfs--;
1093 return;
1096 switch (reg->rIdx)
1098 case A_IDX:
1099 case X_IDX:
1100 case Y_IDX:
1101 loadRegTempAt(reg, --_G.tempOfs);
1102 return;
1103 case XA_IDX:
1104 loadRegTemp(m6502_reg_x);
1105 loadRegTemp(m6502_reg_a);
1106 break;
1107 case YX_IDX:
1108 loadRegTemp(m6502_reg_y);
1109 loadRegTemp(m6502_reg_x);
1110 break;
1111 default:
1112 emitcode("ERROR", "bad reg in loadRegTemp()");
1113 break;
1116 // FIXME: figure out if register pairs are literals
1118 m6502_useReg (reg);
1119 m6502_dirtyReg (reg);
1122 /**************************************************************************
1123 * Conditionally load a register from the REGTEMP stack.
1125 * @param reg pointer for the register to load
1126 * @param load register if true otherwise free the register
1127 *************************************************************************/
1128 static void
1129 loadOrFreeRegTemp (reg_info * reg, bool needpull)
1131 if (needpull)
1132 loadRegTemp (reg);
1133 else
1134 m6502_freeReg (reg);
1137 /**************************************************************************
1138 * Conditionally load a register from the REGTEMP stack without
1139 * affecting condition flags
1141 * @param reg pointer for the register to load
1142 * @param load register if true otherwise free the register
1143 *************************************************************************/
1144 static void
1145 loadRegTempNoFlags (reg_info * reg, bool needpull)
1147 if (needpull)
1149 int tempflag=_G.lastflag;
1150 emit6502op("php", "");
1151 loadRegTemp (reg);
1152 emit6502op("plp", "");
1153 _G.lastflag=tempflag;
1155 else
1157 m6502_freeReg (reg);
1161 static void
1162 dirtyRegTemp (int temp_reg_idx)
1164 _G.tempAttr[temp_reg_idx].isLiteral=false;
1167 /**************************************************************************
1168 * pushReg - Push register reg onto the stack. If freereg is true, reg is
1169 * marked free and available for reuse.
1170 *************************************************************************/
1171 static void
1172 pushReg (reg_info * reg, bool freereg)
1174 int regidx = reg->rIdx;
1176 emitComment (REGOPS, " pushReg(%s) %s %s", reg->name, reg->isFree?"free":"", reg->isDead?"dead":"");
1178 switch (regidx)
1180 case A_IDX:
1181 emit6502op ("pha", "");
1182 updateCFA ();
1183 break;
1184 case X_IDX:
1185 if (IS_MOS65C02)
1187 emit6502op ("phx", "");
1189 else
1191 bool needloada = storeRegTempIfUsed (m6502_reg_a);
1192 transferRegReg (m6502_reg_x, m6502_reg_a, false);
1193 pushReg (m6502_reg_a, true);
1194 loadOrFreeRegTemp (m6502_reg_a, needloada);
1196 updateCFA ();
1197 break;
1198 case Y_IDX:
1199 if (IS_MOS65C02)
1201 emit6502op ("phy", "");
1203 else
1205 bool needloada = storeRegTempIfUsed (m6502_reg_a);
1206 transferRegReg (m6502_reg_y, m6502_reg_a, true);
1207 pushReg (m6502_reg_a, true);
1208 loadOrFreeRegTemp (m6502_reg_a, needloada);
1210 updateCFA ();
1211 break;
1212 // little-endian order
1213 case XA_IDX:
1214 pushReg(m6502_reg_x, freereg);
1215 pushReg(m6502_reg_a, freereg);
1216 break;
1217 case YX_IDX:
1218 pushReg(m6502_reg_y, freereg);
1219 pushReg(m6502_reg_x, freereg);
1220 break;
1221 default:
1222 emitcode("ERROR", " %s: bad reg idx: 0x%02x", __func__, regidx);
1223 break;
1225 if (freereg)
1226 m6502_freeReg (reg);
1229 /**************************************************************************
1230 * pullReg - Pull register reg off the stack.
1231 *************************************************************************/
1232 static void
1233 pullReg (reg_info * reg)
1235 int regidx = reg->rIdx;
1237 emitComment (REGOPS, __func__ );
1239 switch (regidx) {
1240 case A_IDX:
1241 emit6502op ("pla", "");
1242 updateCFA ();
1243 break;
1244 case X_IDX:
1245 if (IS_MOS65C02)
1247 emit6502op ("plx", "");
1249 else
1251 // FIXME: saving A makes regression fail
1252 // bool needloada = storeRegTempIfUsed (m6502_reg_a);
1253 pullReg (m6502_reg_a);
1254 transferRegReg (m6502_reg_a, m6502_reg_x, true);
1255 // loadOrFreeRegTemp (m6502_reg_a, needloada);
1257 updateCFA ();
1258 break;
1259 case Y_IDX:
1260 if (IS_MOS65C02)
1262 emit6502op ("ply", "");
1264 else
1266 // FIXME: saving A makes regression fail
1267 // bool needloada = storeRegTempIfUsed (m6502_reg_a);
1268 pullReg (m6502_reg_a);
1269 transferRegReg (m6502_reg_a, m6502_reg_y, true);
1270 // loadOrFreeRegTemp (m6502_reg_a, needloada);
1272 updateCFA ();
1273 break;
1274 // little-endian order
1275 case XA_IDX:
1276 pullReg(m6502_reg_a);
1277 pullReg(m6502_reg_x);
1278 break;
1279 case YX_IDX:
1280 pullReg(m6502_reg_x);
1281 pullReg(m6502_reg_y);
1282 break;
1283 default:
1284 emitcode("ERROR", " %s: bad reg idx: 0x%02x", __func__, regidx);
1285 break;
1287 m6502_useReg (reg);
1288 m6502_dirtyReg (reg);
1291 /**************************************************************************
1292 * pullNull - Discard n bytes off the top of the stack
1293 *************************************************************************/
1294 static void
1295 pullNull (int n)
1297 emitComment (REGOPS, __func__ );
1298 if(n < 0) emitcode("ERROR", "pullNull called with negative parameter");
1300 adjustStack (n);
1303 /**************************************************************************
1304 * pushRegIfUsed - Push register reg if marked in use. Returns true if the
1305 * push was performed, false otherwise.
1306 *************************************************************************/
1307 static bool
1308 pushRegIfUsed (reg_info *reg)
1310 if (!reg->isFree)
1312 pushReg (reg, true);
1313 return true;
1315 else
1316 return false;
1319 /**************************************************************************
1320 * pushRegIfSurv - Push register reg if marked surviving. Returns true if
1321 * the push was performed, false otherwise.
1322 *************************************************************************/
1323 static bool
1324 pushRegIfSurv (reg_info *reg)
1326 if (!reg->isDead)
1328 pushReg (reg, true);
1329 return true;
1330 } else
1331 return false;
1334 /**************************************************************************
1335 * pullOrFreeReg - If needpull is true, register reg is pulled from the
1336 * stack. Otherwise register reg is marked as free.
1337 *************************************************************************/
1338 static void
1339 pullOrFreeReg (reg_info * reg, bool needpull)
1341 if (needpull)
1342 pullReg (reg);
1343 else
1344 m6502_freeReg (reg);
1347 /**************************************************************************
1348 * adjustStack - Adjust the stack pointer by n bytes.
1349 *************************************************************************/
1350 // TODO: optimize for 65C02
1351 static void
1352 adjustStack (int n)
1354 emitComment (REGOPS, __func__ );
1355 emitComment (REGOPS, " %s reg: %s", __func__, regInfoStr());
1357 if (n <= -8 || n >= 8)
1359 // TODO: too big, consider subroutine
1360 storeRegTemp(m6502_reg_xa, true);
1361 // bool needloada=storeRegTempIfUsed(m6502_reg_xa);
1362 emit6502op ("tsx", "");
1363 transferRegReg (m6502_reg_x, m6502_reg_a, true);
1364 emitSetCarry(0);
1365 emit6502op ("adc", IMMDFMT, n&0xff);
1366 transferRegReg (m6502_reg_a, m6502_reg_x, true);
1367 emit6502op ("txs", "");
1368 _G.stackPushes -= n;
1369 n = 0;
1370 loadRegTemp(m6502_reg_xa);
1371 // loadOrFreeRegTemp(m6502_reg_xa, needloada);
1373 while (n < 0)
1375 emit6502op ("pha", ""); /* 1 byte, 3 cycles */
1376 n++;
1379 if (n > 0)
1381 // FIXME: A is incorrectly marked free and makes many regression fail
1382 bool needloada=storeRegTempIfUsed (m6502_reg_a);
1383 // bool needloada=storeRegTempIfSurv(m6502_reg_a);
1384 // bool needloada = true;
1385 // storeRegTemp(m6502_reg_a, true);
1387 while (n > 0)
1389 emit6502op ("pla", ""); /* 1 byte, 4 cycles */
1390 n--;
1392 loadOrFreeRegTemp(m6502_reg_a, needloada);
1394 updateCFA ();
1397 /**************************************************************************
1398 * Return a string with debugging information about an asmop.
1399 *************************************************************************/
1400 static char *
1401 aopName (asmop * aop)
1403 static char buffer[276];
1404 char *buf = buffer;
1406 if (!aop)
1407 return "(asmop*)NULL";
1409 switch (aop->type)
1411 case AOP_IMMD:
1412 sprintf (buf, "IMMD(%s)", aop->aopu.aop_immd);
1413 return buf;
1414 case AOP_LIT:
1415 sprintf (buf, "LIT(%s)", aopLiteral (aop->aopu.aop_lit, 0));
1416 return buf;
1417 case AOP_DIR:
1418 sprintf (buf, "DIR(%s)", aop->aopu.aop_dir);
1419 return buf;
1420 case AOP_EXT:
1421 sprintf (buf, "EXT(%s)", aop->aopu.aop_dir);
1422 return buf;
1423 case AOP_SOF:
1424 sprintf (buf, "SOF(%s@%d)", OP_SYMBOL (aop->op)->name, aop->aopu.aop_stk);
1425 return buf;
1426 case AOP_REG:
1427 sprintf (buf, "REG(%s%s%s%s)",
1428 aop->aopu.aop_reg[3] ? aop->aopu.aop_reg[3]->name : "",
1429 aop->aopu.aop_reg[2] ? aop->aopu.aop_reg[2]->name : "",
1430 aop->aopu.aop_reg[1] ? aop->aopu.aop_reg[1]->name : "",
1431 aop->aopu.aop_reg[0] ? aop->aopu.aop_reg[0]->name : "-");
1432 return buf;
1433 case AOP_STK:
1434 return "STK";
1435 default:
1436 sprintf (buf, "?%d", aop->type);
1437 return buf;
1440 return "?";
1443 // can we BIT aop ?
1444 static bool
1445 canBitOp (const operand* aop)
1447 switch (AOP_TYPE(aop))
1449 // bit aa, bit aaaa
1450 case AOP_DIR:
1451 case AOP_EXT:
1452 return true;
1453 // bit #aa
1454 case AOP_LIT:
1455 return IS_MOS65C02;
1456 // TODO: ind,x for 65c02?
1457 default:
1458 break;
1460 return false;
1463 /**************************************************************************
1464 * Load register reg from logical offset loffset of aop.
1465 * For multi-byte registers, loffset is of the lsb reg.
1466 *************************************************************************/
1467 static void
1468 loadRegFromAop (reg_info * reg, asmop * aop, int loffset)
1470 int regidx = reg->rIdx;
1472 emitComment (REGOPS, " loadRegFromAop (%s, %s, %d)", reg->name, aopName (aop), loffset);
1474 if (aop->stacked && aop->stk_aop[loffset])
1476 loadRegFromAop (reg, aop->stk_aop[loffset], 0);
1477 return;
1480 #if 0
1481 /* If operand is volatile, we cannot optimize. */
1482 if (!aop->op || isOperandVolatile (aop->op, false))
1483 goto forceload;
1486 /* If this register already has this offset of the operand
1487 then we need only mark it as in use. */
1488 if (reg->aop && reg->aop->op && aop->op && operandsEqu (reg->aop->op, aop->op) && (reg->aopofs == loffset))
1490 m6502_useReg (reg);
1491 emitComment (REGOPS, " already had correct value for %s", reg->name);
1492 return;
1495 /* TODO: check to see if we can transfer from another register */
1497 if (m6502_reg_hy>aop && m6502_reg_y->aop->op && aop->op
1498 && operandsEqu (m6502_reg_y->aop->op, aop->op) && (m6502_reg_y->aopofs == loffset))
1500 emitComment (REGOPS, " found correct value for %s in h", reg->name);
1501 transferRegReg (m6502_reg_y, reg, false);
1502 m6502_useReg (reg);
1503 return;
1507 if (m6502_reg_x->aop && m6502_reg_x->aop->op && aop->op
1508 && operandsEqu (m6502_reg_x->aop->op, aop->op) && (m6502_reg_x->aopofs == loffset))
1510 emitComment (REGOPS, " found correct value for %s in x", reg->name);
1511 transferRegReg (m6502_reg_x, reg, false);
1512 m6502_useReg (reg);
1513 return;
1516 if (m6502_reg_a->aop && m6502_reg_a->aop->op && aop->op
1517 && operandsEqu (m6502_reg_a->aop->op, aop->op) && (m6502_reg_a->aopofs == loffset))
1519 emitComment (REGOPS, " found correct value for %s in a", reg->name);
1520 transferRegReg (m6502_reg_a, reg, false);
1521 m6502_useReg (reg);
1522 return;
1525 forceload:
1526 #endif
1528 switch (regidx) {
1529 case A_IDX:
1530 case X_IDX:
1531 case Y_IDX:
1532 if (aop->type == AOP_REG)
1534 if (loffset < aop->size)
1535 transferRegReg (aop->aopu.aop_reg[loffset], reg, false);
1536 else
1537 loadRegFromConst (reg, 0); /* TODO: handle sign extension */
1539 else if (aop->type == AOP_LIT)
1541 loadRegFromConst (reg, byteOfVal (aop->aopu.aop_lit, loffset));
1543 // no such thing as ldx aa,x
1544 else if (aop->type == AOP_SOF && regidx != A_IDX)
1546 bool needloada = storeRegTempIfUsed(m6502_reg_a);
1547 loadRegFromAop(m6502_reg_a, aop, loffset);
1548 transferRegReg(m6502_reg_a, reg, false);
1549 loadOrFreeRegTemp(m6502_reg_a,needloada);
1550 // break;
1552 else
1554 if(aop->type == AOP_SOF)
1555 emitComment (TRACE_STACK|VVDBG, " loadRegFromAop: A [%d, %d]",aop->aopu.aop_stk, loffset);
1556 aopAdrPrepare(aop, loffset);
1557 const char *l = aopAdrStr (aop, loffset, false);
1558 emit6502op (regidx == A_IDX ? "lda" : regidx == X_IDX ? "ldx" : "ldy", l);
1559 aopAdrUnprepare(aop, loffset);
1560 m6502_dirtyReg (reg);
1562 break;
1563 case XA_IDX:
1564 if (IS_AOP_XA (aop))
1565 break;
1566 else if (IS_AOP_YX (aop))
1567 transferRegReg (m6502_reg_yx, m6502_reg_xa, false);
1568 else
1570 emitComment (REGOPS, "XA");
1571 if(IS_AOP_X(aop))
1573 transferRegReg(m6502_reg_x, m6502_reg_a, false);
1574 loadRegFromConst (m6502_reg_x, 0);
1576 else if(aop->type == AOP_SOF)
1578 loadRegFromAop (m6502_reg_a, aop, loffset);
1579 loadRegFromAop (m6502_reg_x, aop, loffset + 1);
1581 else
1583 loadRegFromAop (m6502_reg_x, aop, loffset + 1);
1584 loadRegFromAop (m6502_reg_a, aop, loffset);
1587 break;
1588 case YX_IDX:
1589 if (IS_AOP_YX (aop))
1590 break;
1591 else if (IS_AOP_XA (aop))
1592 transferRegReg (m6502_reg_xa, m6502_reg_yx, false);
1593 else
1595 loadRegFromAop (m6502_reg_x, aop, loffset);
1596 loadRegFromAop (m6502_reg_y, aop, loffset + 1);
1598 break;
1601 m6502_useReg (reg);
1604 /**************************************************************************
1605 * Find any free 8-bit register
1606 *************************************************************************/
1607 static reg_info*
1608 getFreeByteReg()
1610 if (m6502_reg_a->isFree)
1611 return m6502_reg_a;
1612 else if (m6502_reg_x->isFree)
1613 return m6502_reg_x;
1614 else if (m6502_reg_y->isFree)
1615 return m6502_reg_y;
1616 else
1617 return NULL;
1620 // TODO: move more to this one?
1621 static reg_info*
1622 getDeadByteReg()
1624 if (m6502_reg_a->isDead)
1625 return m6502_reg_a;
1626 else if (m6502_reg_x->isDead)
1627 return m6502_reg_x;
1628 else if (m6502_reg_y->isDead)
1629 return m6502_reg_y;
1630 else
1631 return NULL;
1634 /**************************************************************************
1635 * storeRegToAop - Store register reg to logical offset loffset of aop.
1636 * For multi-byte registers, loffset is of the lsb reg.
1637 *************************************************************************/
1638 static void
1639 storeRegToAop (reg_info *reg, asmop * aop, int loffset)
1641 bool needpulla = false;
1642 bool needpullx = false;
1643 int regidx = reg->rIdx;
1645 emitComment (TRACE_AOP, " %s (%s, %s, %d), stacked=%d",
1646 __func__, reg->name, aopName (aop), loffset, aop->stacked);
1648 if (aop->type == AOP_DUMMY)
1649 return;
1651 if (aop->type == AOP_CRY) /* This can only happen if IFX was optimized */
1652 return; /* away, so just toss the result */
1654 // handle reg to reg
1655 if (aop->type == AOP_REG)
1657 switch(regidx)
1659 case A_IDX:
1660 case X_IDX:
1661 case Y_IDX:
1662 transferRegReg (reg, aop->aopu.aop_reg[loffset], true);
1663 break;
1664 case XA_IDX:
1665 if (IS_AOP_YX (aop))
1667 transferRegReg (reg, m6502_reg_yx, false);
1669 else
1671 if(!IS_AOP_XA(aop))
1672 emitcode("ERROR", "%s: unsupported reg in AOP (XA)", __func__);
1674 break;
1675 case YX_IDX:
1676 if (IS_AOP_XA (aop))
1678 transferRegReg (reg, m6502_reg_xa, false);
1680 else
1682 if(!IS_AOP_YX(aop))
1683 emitcode("ERROR", "%s: unsupported reg in AOP (YX)", __func__);
1685 break;
1687 return;
1690 // handle ZP and absolute addresses
1691 if (aop->type == AOP_DIR || aop->type == AOP_EXT)
1693 switch(regidx)
1695 case A_IDX:
1696 emit6502op ("sta", aopAdrStr (aop, loffset, true));
1697 break;
1698 case X_IDX:
1699 emit6502op ("stx", aopAdrStr (aop, loffset, true));
1700 break;
1701 case Y_IDX:
1702 emit6502op ("sty", aopAdrStr (aop, loffset, true));
1703 break;
1704 case XA_IDX:
1705 emit6502op ("sta", aopAdrStr (aop, loffset, true));
1706 emit6502op ("stx", aopAdrStr (aop, loffset+1, true));
1707 break;
1708 case YX_IDX:
1709 emit6502op ("stx", aopAdrStr (aop, loffset, true));
1710 emit6502op ("sty", aopAdrStr (aop, loffset+1, true));
1711 break;
1713 return;
1716 // handle stack
1717 if (aop->type == AOP_SOF)
1719 // int xofs = STACK_TOP + _G.stackOfs + _G.tsxStackPushes + aop->aopu.aop_stk + loffset + 1;
1721 switch (regidx)
1723 case A_IDX:
1724 emitComment (TRACE_STACK|VVDBG, " %s: A [%d, %d]",
1725 __func__, aop->aopu.aop_stk, loffset);
1726 needpullx = storeRegTempIfUsed(m6502_reg_x);
1727 doTSX();
1728 emit6502op ("sta", aopAdrStr (aop, loffset, false));
1729 loadOrFreeRegTemp(m6502_reg_x, needpullx);
1730 break;
1731 case X_IDX:
1732 case Y_IDX:
1733 // TODO: push if live
1734 needpulla = pushRegIfUsed (m6502_reg_a);
1735 transferRegReg (reg, m6502_reg_a, false);
1736 storeRegToAop (m6502_reg_a, aop, loffset);
1737 pullOrFreeReg (m6502_reg_a, needpulla);
1738 break;
1739 case XA_IDX:
1740 // options.stackAuto
1741 // pushReg(m6502_reg_a, true);
1742 needpullx = storeRegTempIfSurv(m6502_reg_x);
1743 storeRegTemp(m6502_reg_a, false);
1744 transferRegReg (m6502_reg_x, m6502_reg_a, true);
1745 doTSX();
1746 emit6502op ("sta", aopAdrStr (aop, loffset + 1, false));
1747 // pullReg(m6502_reg_a);
1748 loadRegTemp(m6502_reg_a);
1749 emit6502op ("sta", aopAdrStr (aop, loffset, false));
1750 loadOrFreeRegTemp(m6502_reg_x, needpullx);
1751 break;
1752 case YX_IDX:
1753 needpulla = pushRegIfSurv(m6502_reg_a);
1754 needpullx = storeRegTempIfSurv(m6502_reg_x);
1755 transferRegReg (m6502_reg_x, m6502_reg_a, true);
1756 doTSX();
1757 emit6502op ("sta", aopAdrStr (aop, loffset, false));
1758 transferRegReg (m6502_reg_y, m6502_reg_a, true);
1759 emit6502op ("sta", aopAdrStr (aop, loffset + 1, false));
1760 loadOrFreeRegTemp(m6502_reg_x, needpullx);
1761 pullOrFreeReg(m6502_reg_a, needpulla);
1762 break;
1763 default:
1764 emitcode("ERROR", "%s: bad reg 0x%02x", __func__, regidx);
1768 /* Disable the register tracking for now */
1769 #if 0
1770 //if (!reg->aop || (reg->aop && (reg->aop != aop)))
1772 //if (reg->aop!=aop)
1773 for (otheridx = 0; otheridx < m6502_nRegs; otheridx++)
1775 otherreg = m6502_regWithIdx (otheridx);
1776 if (otherreg && otherreg->aop
1777 && otherreg->aop->op && aop->op && operandsEqu (otherreg->aop->op, aop->op) && (otherreg->aopofs == loffset))
1779 emitComment (TRACE_AOP|VVDBG, " marking %s stale", otherreg->name);
1780 otherreg->aop = NULL;
1783 if ((!m6502_reg_x->aop || !m6502_reg_y->aop) && m6502_reg_yx->aop)
1785 m6502_reg_yx->aop = NULL;
1786 emitComment (TRACE_AOP|VVDBG, " marking yx stale");
1788 if ((!m6502_reg_x->aop || !m6502_reg_a->aop) && m6502_reg_xa->aop)
1790 m6502_reg_xa->aop = NULL;
1791 emitComment (TRACE_AOP|VVDBG, " marking xa stale");
1794 reg->aop = aop;
1795 reg->aopofs = loffset;
1797 #endif
1800 /**************************************************************************
1801 * loadRegFromConst - Load register reg from constant c.
1802 *************************************************************************/
1803 static void
1804 loadRegFromConst (reg_info * reg, int c)
1806 emitComment (REGOPS, __func__ );
1808 switch (reg->rIdx) {
1809 case A_IDX:
1810 c &= 0xff;
1811 if (reg->isLitConst && reg->litConst == c)
1812 break;
1814 if (m6502_reg_y->isLitConst && m6502_reg_y->litConst == c)
1815 transferRegReg (m6502_reg_y, reg, false);
1816 else if (m6502_reg_x->isLitConst && m6502_reg_x->litConst == c)
1817 transferRegReg (m6502_reg_x, reg, false);
1818 else {
1819 emit6502op ("lda", IMMDFMT, c);
1821 break;
1822 case X_IDX:
1823 c &= 0xff;
1824 if (reg->isLitConst) {
1825 if (reg->litConst == c)
1826 break;
1827 if (((reg->litConst + 1) & 0xff) == c)
1829 emit6502op ("inx", "");
1830 break;
1832 if (((reg->litConst - 1) & 0xff) == c)
1834 emit6502op ("dex", "");
1835 break;
1839 if (m6502_reg_a->isLitConst && m6502_reg_a->litConst == c)
1840 transferRegReg (m6502_reg_a, reg, false);
1842 TODO does not work for X<->Y
1843 else if (m6502_reg_y->isLitConst && m6502_reg_y->litConst == c)
1844 transferRegReg (m6502_reg_y, reg, false);
1846 else
1848 emit6502op ("ldx", IMMDFMT, c);
1850 break;
1851 case Y_IDX:
1852 c &= 0xff;
1853 if (reg->isLitConst)
1855 if (reg->litConst == c)
1856 break;
1857 if (((reg->litConst + 1) & 0xff) == c)
1859 emit6502op ("iny", "");
1860 break;
1862 if (((reg->litConst - 1) & 0xff) == c)
1864 emit6502op ("dey", "");
1865 break;
1869 if (m6502_reg_a->isLitConst && m6502_reg_a->litConst == c)
1870 transferRegReg (m6502_reg_a, reg, false);
1872 TODO does not work for X<->Y
1873 else if (m6502_reg_x->isLitConst && m6502_reg_x->litConst == c)
1874 transferRegReg (m6502_reg_x, reg, false);
1876 else
1878 emit6502op ("ldy", IMMDFMT, c);
1880 break;
1881 case XA_IDX:
1882 c &= 0xffff;
1883 loadRegFromConst (m6502_reg_x, c >> 8);
1884 loadRegFromConst (m6502_reg_a, c);
1885 break;
1886 case YX_IDX:
1887 c &= 0xffff;
1888 loadRegFromConst (m6502_reg_y, c >> 8);
1889 loadRegFromConst (m6502_reg_x, c);
1890 break;
1891 default:
1892 emitcode("ERROR", "bad reg 0x%02x in %s", reg->rIdx, __func__);
1893 return;
1896 m6502_dirtyReg (reg);
1897 reg->isLitConst = 1;
1898 reg->litConst = c;
1900 m6502_useReg (reg);
1903 /**************************************************************************
1904 * loadRegFromImm - Load register reg from immediate value c.
1905 *************************************************************************/
1906 static void
1907 loadRegFromImm (reg_info * reg, char * c)
1909 emitComment (REGOPS, __func__ );
1911 if(!c) {
1912 werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "loadRegFromImm called with a null arg pointer");
1915 if (*c == '#')
1916 c++;
1917 switch (reg->rIdx) {
1918 case A_IDX:
1919 emit6502op ("lda", "#%s", c);
1920 break;
1921 case X_IDX:
1922 emit6502op ("ldx", "#%s", c);
1923 break;
1924 case Y_IDX:
1925 emit6502op ("ldy", "#%s", c);
1926 break;
1927 case XA_IDX:
1928 emit6502op ("ldx", "#%s >> 8", c);
1929 emit6502op ("lda", "#%s", c);
1930 break;
1931 case YX_IDX:
1932 emit6502op ("ldy", "#%s >> 8", c);
1933 emit6502op ("ldx", "#%s", c);
1934 break;
1935 default:
1936 werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "Bad rIdx in loadRegFromConst");
1937 return;
1939 m6502_dirtyReg (reg);
1940 m6502_useReg (reg);
1943 /**************************************************************************
1944 * storeConstToAop - Store constant c to logical offset loffset of
1945 * asmop aop.
1946 *************************************************************************/
1947 static void
1948 storeConstToAop (int c, asmop * aop, int loffset)
1950 emitComment (REGOPS, __func__ );
1952 if (aop->stacked && aop->stk_aop[loffset])
1954 storeConstToAop (c, aop->stk_aop[loffset], 0);
1955 return;
1958 /* If the value needed is already in A, X or Y just store it */
1959 if (m6502_reg_a->isLitConst && m6502_reg_a->litConst == c)
1961 storeRegToAop (m6502_reg_a, aop, loffset);
1962 return;
1964 if (m6502_reg_x->isLitConst && m6502_reg_x->litConst == c)
1966 storeRegToAop (m6502_reg_x, aop, loffset);
1967 return;
1969 if (m6502_reg_y->isLitConst && m6502_reg_y->litConst == c)
1971 storeRegToAop (m6502_reg_y, aop, loffset);
1972 return;
1975 switch (aop->type)
1977 case AOP_REG:
1978 if (loffset > (aop->size - 1))
1979 break;
1980 loadRegFromConst (aop->aopu.aop_reg[loffset], c);
1981 break;
1982 case AOP_DUMMY:
1983 break;
1984 case AOP_DIR:
1985 case AOP_EXT:
1986 /* stz operates with read-modify-write cycles, so don't use if the */
1987 /* destination is volatile to avoid the read side-effect. */
1988 if (c==0 && IS_MOS65C02 && !(aop->op && isOperandVolatile (aop->op, false)))
1990 aopAdrPrepare(aop, loffset);
1991 emit6502op ("stz", "%s", aopAdrStr (aop, loffset, false));
1992 aopAdrUnprepare(aop, loffset);
1993 break;
1995 default:
1996 if (m6502_reg_x->isFree && aop->type != AOP_SOF)
1998 loadRegFromConst (m6502_reg_x, c);
1999 storeRegToAop (m6502_reg_x, aop, loffset);
2000 m6502_freeReg (m6502_reg_x);
2002 else if (m6502_reg_y->isFree && aop->type != AOP_SOF)
2004 loadRegFromConst (m6502_reg_y, c);
2005 storeRegToAop (m6502_reg_y, aop, loffset);
2006 m6502_freeReg (m6502_reg_y);
2008 else
2010 bool needpulla = pushRegIfUsed (m6502_reg_a);
2011 // pushReg (m6502_reg_a, true);
2012 loadRegFromConst (m6502_reg_a, c);
2013 storeRegToAop (m6502_reg_a, aop, loffset);
2014 // pullReg (m6502_reg_a);
2015 pullOrFreeReg (m6502_reg_a, needpulla);
2020 /**************************************************************************
2021 * storeImmToAop - Store immediate value c to logical offset
2022 * loffset of asmop aop.
2023 *************************************************************************/
2024 static void
2025 storeImmToAop (char *c, asmop * aop, int loffset)
2027 emitComment (TRACE_AOP, __func__ );
2029 if (aop->stacked && aop->stk_aop[loffset])
2031 storeImmToAop (c, aop->stk_aop[loffset], 0);
2032 return;
2035 switch (aop->type) {
2036 case AOP_REG:
2037 if (loffset > (aop->size - 1))
2038 break;
2039 loadRegFromImm (aop->aopu.aop_reg[loffset], c);
2040 break;
2041 case AOP_DUMMY:
2042 break;
2043 case AOP_DIR:
2044 /* clr operates with read-modify-write cycles, so don't use if the */
2045 /* destination is volatile to avoid the read side-effect. */
2046 if (!strcmp (c, "#0x00") && IS_MOS65C02 && !(aop->op && isOperandVolatile (aop->op, false)))
2048 aopAdrPrepare(aop, loffset);
2049 emit6502op ("stz", "%s", aopAdrStr (aop, loffset, false));
2050 aopAdrUnprepare(aop, loffset);
2051 break;
2053 default:
2054 if (m6502_reg_x->isFree)
2056 loadRegFromImm (m6502_reg_x, c);
2057 storeRegToAop (m6502_reg_x, aop, loffset);
2058 m6502_freeReg (m6502_reg_x);
2059 } else if (m6502_reg_y->isFree) {
2060 loadRegFromImm (m6502_reg_y, c);
2061 storeRegToAop (m6502_reg_y, aop, loffset);
2062 m6502_freeReg (m6502_reg_y);
2063 } else {
2064 bool needpulla = pushRegIfUsed (m6502_reg_a);
2065 loadRegFromImm (m6502_reg_a, c);
2066 storeRegToAop (m6502_reg_a, aop, loffset);
2067 pullOrFreeReg (m6502_reg_a, needpulla);
2072 static void
2073 signExtendA()
2075 emit6502op ("asl", "a");
2076 loadRegFromConst (m6502_reg_a, 0);
2077 emit6502op ("adc", "#0xff");
2078 emit6502op ("eor", "#0xff");
2081 /**************************************************************************
2082 * storeRegSignToUpperAop - If isSigned is true, the sign bit of register
2083 * reg is extended to fill logical offsets loffset
2084 * and above of asmop aop. Otherwise, logical
2085 * offsets loffset and above of asmop aop are
2086 * zeroed. reg must be an 8-bit register.
2087 *************************************************************************/
2088 static void
2089 storeRegSignToUpperAop (reg_info * reg, asmop * aop, int loffset, bool isSigned)
2091 emitComment (TRACE_AOP, __func__ );
2093 // int regidx = reg->rIdx;
2094 int size = aop->size;
2096 if (loffset >= size)
2097 return;
2099 if (!isSigned)
2101 /* Unsigned case */
2102 while (loffset < size)
2103 storeConstToAop (0, aop, loffset++);
2105 else
2107 /* Signed case */
2108 if(reg!=m6502_reg_a)
2110 symbol *tlbl = safeNewiTempLabel (NULL);
2111 if(reg==m6502_reg_x) emit6502op("cpx","#0x80");
2112 else emit6502op("cpy","#0x80");
2113 loadRegFromConst(reg, 0);
2114 emitBranch ("bcc", tlbl);
2115 loadRegFromConst(reg, 0xff);
2116 safeEmitLabel (tlbl);
2117 m6502_dirtyReg(reg);
2119 else
2121 signExtendA();
2122 m6502_useReg (m6502_reg_a);
2124 while (loffset < size)
2125 storeRegToAop (reg, aop, loffset++);
2126 m6502_freeReg (reg);
2130 /**************************************************************************
2131 * storeRegToFullAop - Store register reg to asmop aop with appropriate
2132 * padding and/or truncation as needed. If isSigned is
2133 * true, sign extension will take place in the padding.
2134 *************************************************************************/
2135 static void
2136 storeRegToFullAop (reg_info *reg, asmop *aop, bool isSigned)
2138 int regidx = reg->rIdx;
2139 int size = aop->size;
2141 emitComment (TRACE_AOP, __func__ );
2143 switch (regidx)
2145 case A_IDX:
2146 case X_IDX:
2147 case Y_IDX:
2148 #if 0
2149 if ( IS_AOP_XA(aop) && regidx==A_IDX )
2151 loadRegFromConst(m6502_reg_x,0);
2152 if(isSigned)
2154 symbol *tlbl = safeNewiTempLabel (NULL);
2155 emit6502op("cmp","#0x80");
2156 emitBranch ("bcc", tlbl);
2157 loadRegFromConst(m6502_reg_x, 0xff);
2158 safeEmitLabel (tlbl);
2161 else
2162 #endif
2164 storeRegToAop (reg, aop, 0);
2165 if (size > 1 && isSigned && aop->type == AOP_REG && aop->aopu.aop_reg[0]->rIdx == A_IDX)
2166 pushReg (m6502_reg_a, true);
2167 storeRegSignToUpperAop (reg, aop, 1, isSigned);
2168 if (size > 1 && isSigned && aop->type == AOP_REG && aop->aopu.aop_reg[0]->rIdx == A_IDX)
2169 pullReg (m6502_reg_a);
2171 break;
2172 case XA_IDX:
2173 if (size == 1) {
2174 storeRegToAop (m6502_reg_a, aop, 0);
2175 } else {
2176 storeRegToAop (reg, aop, 0);
2177 if(aop->type!=AOP_SOF) {
2178 storeRegSignToUpperAop (m6502_reg_x, aop, 2, isSigned);
2179 } else {
2180 loadRegFromAop(m6502_reg_a, aop, 1);
2181 storeRegSignToUpperAop (m6502_reg_a, aop, 2, isSigned);
2184 break;
2185 case YX_IDX:
2186 if (size == 1) {
2187 storeRegToAop (m6502_reg_x, aop, 0);
2188 } else {
2189 storeRegToAop (reg, aop, 0);
2190 storeRegSignToUpperAop (m6502_reg_y, aop, 2, isSigned);
2192 break;
2193 default:
2194 emitcode("ERROR", "bad reg 0x%02x in storeRegToFullAop()", regidx);
2198 /**************************************************************************
2199 * transferAopAop - Transfer the value at logical offset srcofs of asmop
2200 * srcaop to logical offset dstofs of asmop dstaop.
2201 *************************************************************************/
2202 static void
2203 transferAopAop (asmop *srcaop, int srcofs, asmop *dstaop, int dstofs)
2205 bool needpula = false;
2206 reg_info *reg = NULL;
2207 bool keepreg = false;
2209 emitComment (TRACE_AOP, __func__ );
2211 if(!srcaop || !dstaop)
2213 if(!srcaop) emitcode("ERROR", "srcaop is null");
2214 if(!dstaop) emitcode("ERROR", "dstaop is null");
2215 return;
2217 wassert (srcaop && dstaop);
2219 /* ignore transfers at the same byte, unless its volatile */
2220 if (srcaop->op && !isOperandVolatile (srcaop->op, false)
2221 && dstaop->op && !isOperandVolatile (dstaop->op, false)
2222 && operandsEqu (srcaop->op, dstaop->op) && srcofs == dstofs && dstaop->type == srcaop->type)
2223 return;
2225 if (srcaop->stacked && srcaop->stk_aop[srcofs])
2227 transferAopAop (srcaop->stk_aop[srcofs], 0, dstaop, dstofs);
2228 return;
2231 if (dstaop->stacked && dstaop->stk_aop[srcofs])
2233 transferAopAop (srcaop, srcofs, dstaop->stk_aop[dstofs], 0);
2234 return;
2237 emitComment (TRACE_AOP|VVDBG, " transferAopAop from (%s, %d, %x)",
2238 aopName (srcaop), srcofs, srcaop->regmask);
2239 emitComment (TRACE_AOP|VVDBG, " transferAopAop to (%s, %d, %x)",
2240 aopName (dstaop), dstofs, dstaop->regmask);
2242 if (dstofs >= dstaop->size)
2243 return;
2245 // same registers and offset, no transfer
2246 if (srcaop->type == AOP_REG && dstaop->type == AOP_REG)
2248 emitComment (TRACE_AOP|VVDBG, " %s: regreg", __func__);
2249 transferRegReg(srcaop->aopu.aop_reg[srcofs], dstaop->aopu.aop_reg[dstofs], false);
2250 return;
2253 if (srcaop->type == AOP_LIT)
2255 storeConstToAop (byteOfVal (srcaop->aopu.aop_lit, srcofs), dstaop, dstofs);
2256 return;
2258 if (dstaop->type == AOP_REG)
2260 reg = dstaop->aopu.aop_reg[dstofs];
2261 keepreg = true;
2263 else if ((srcaop->type == AOP_REG) && (srcaop->aopu.aop_reg[srcofs]))
2265 reg = srcaop->aopu.aop_reg[srcofs];
2266 keepreg = true;
2269 // TODO: pick reg based on if can load op?
2270 if (!reg)
2272 reg = getFreeByteReg();
2273 if (reg == NULL)
2275 pushReg (m6502_reg_a, true);
2276 needpula = true;
2277 reg = m6502_reg_a;
2281 emitComment (TRACE_AOP|VVDBG, " %s: general case", __func__);
2283 loadRegFromAop (reg, srcaop, srcofs);
2284 storeRegToAop (reg, dstaop, dstofs);
2286 if (!keepreg)
2287 pullOrFreeReg (reg, needpula);
2290 #if 0
2291 /**************************************************************************
2292 * forceStackedAop - Reserve space on the stack for asmop aop; when
2293 * freeAsmop is called with aop, the stacked data will
2294 * be copied to the original aop location.
2295 *************************************************************************/
2296 // TODO????
2297 static asmop * forceStackedAop (asmop * aop, bool copyOrig)
2299 reg_info *reg = NULL;
2300 int offset;
2301 bool needpula = false;
2302 asmop *newaop = newAsmop (AOP_DIR);
2303 memcpy (newaop, aop, sizeof (*newaop));
2304 newaop->aopu.aop_dir = "REGTEMP";
2306 emitComment (TRACE_AOP|VVDBG, " forcedStackedAop %s", aopName (aop));
2308 if (copyOrig) {
2309 reg = getFreeByteReg();
2310 if (reg == NULL) {
2311 reg = m6502_reg_a;
2312 storeRegTemp(reg, true);
2313 needpula = true;
2316 for (offset=0; offset<newaop->size; offset++) {
2317 asmop *aopsof = newAsmop (AOP_SOF);
2318 aopsof->size = 1;
2319 if (copyOrig) {
2320 loadRegFromAop (reg, aop, offset);
2321 pushReg (reg, false);
2322 } else {
2323 pushReg (m6502_reg_a, false);
2325 aopsof->aopu.aop_stk = -_G.stackOfs - _G.stackPushes;
2326 aopsof->op = aop->op;
2327 newaop->stk_aop[offset] = aopsof;
2330 if (!reg && copyOrig) {
2331 for (offset = 0; offset < newaop->size; offset++)
2333 transferAopAop (aop, offset, newaop, offset);
2336 newaop->stacked = 1;
2337 loadOrFreeRegTemp(reg, needpulla);
2338 return newaop;
2340 #endif
2342 // TODO: fix these
2343 /**************************************************************************
2344 * accopWithAop - Emit accumulator modifying instruction accop with
2345 * the byte at logical offset loffset of asmop aop.
2346 * Supports: adc, and, cmp, eor, ora, sbc
2347 *************************************************************************/
2348 static void
2349 accopWithAop (char *accop, asmop *aop, int loffset)
2351 bool ucp = false;
2352 emitComment (TRACE_AOP, __func__ );
2354 if ( !strcmp(accop, "ucp") ) {
2355 // special case for unsigned compare
2356 ucp=true;
2357 accop = "cmp";
2360 if (aop->stacked && aop->stk_aop[loffset]) {
2361 accopWithAop (accop, aop->stk_aop[loffset], 0);
2362 return;
2365 if (aop->type == AOP_DUMMY)
2366 return;
2368 if (aop->type == AOP_REG)
2370 if (loffset < aop->size)
2372 // TODO FIXME: does this need forcestore ?
2373 storeRegTemp (aop->aopu.aop_reg[loffset], true);
2374 emitRegTempOp( accop, _G.tempOfs - 1);
2375 loadRegTemp(NULL);
2376 } else {
2377 emit6502op (accop, "#0x00");
2379 } else {
2380 aopAdrPrepare(aop, loffset);
2381 // emit6502op (accop, aopAdrStr (aop, loffset, false));
2382 const char *arg = aopAdrStr (aop, loffset, false);
2383 if ( ucp && _G.lastflag==A_IDX && !strcmp(arg,"#0x00") )
2385 // do nothing
2386 } else
2387 emit6502op (accop, arg);
2388 aopAdrUnprepare(aop, loffset);
2392 /**************************************************************************
2393 * rmwWithReg - Emit read/modify/write instruction rmwop with register reg.
2394 * byte at logical offset loffset of asmop aop. Register reg
2395 * must be 8-bit.
2396 * Supports: com, dec, inc, lsl, lsr, neg, rol, ror
2397 *************************************************************************/
2398 static void
2399 rmwWithReg (char *rmwop, reg_info * reg)
2401 if (reg->rIdx == A_IDX)
2403 if (!strcmp(rmwop, "inc"))
2405 if (IS_MOS65C02)
2407 emit6502op (rmwop, "a");
2409 else
2411 emitSetCarry(0);
2412 emit6502op ("adc", "#0x01");
2415 else if (!strcmp(rmwop, "dec"))
2417 if (IS_MOS65C02)
2419 emit6502op (rmwop, "a");
2421 else
2423 emitSetCarry(1);
2424 emit6502op ("sbc", "#0x01");
2427 else if (!strcmp(rmwop, "com"))
2429 emit6502op ("eor", "#0xff");
2431 else if (!strcmp(rmwop, "neg"))
2433 emit6502op ("eor", "#0xff");
2434 emitSetCarry (0);
2435 emit6502op ("adc", "#0x01");
2437 else if (!strcmp(rmwop, "asr"))
2439 emit6502op ("cmp", "#0x80");
2440 emit6502op ("ror", "a");
2442 else if (!strcmp(rmwop, "bit"))
2443 { // TODO???
2444 emitcode("ERROR", " %s : called with unsupported opcode: %s", __func__, rmwop);
2446 else
2448 emit6502op (rmwop, "a");
2451 else if (reg->rIdx == X_IDX)
2453 if (!strcmp(rmwop, "inc"))
2455 emit6502op ("inx", "");
2457 else if (!strcmp(rmwop, "dec"))
2459 emit6502op ("dex", "");
2461 else
2463 bool needpulla = pushRegIfUsed (m6502_reg_a);
2464 transferRegReg (m6502_reg_x, m6502_reg_a, true);
2465 rmwWithReg (rmwop, m6502_reg_a);
2466 transferRegReg (m6502_reg_a, m6502_reg_x, true);
2467 pullOrFreeReg (m6502_reg_a, needpulla);
2470 else if (reg->rIdx == Y_IDX)
2472 if (!strcmp(rmwop, "inc"))
2474 emit6502op ("iny", "");
2476 else if (!strcmp(rmwop, "dec"))
2478 emit6502op ("dey", "");
2480 else
2482 bool needpulla = pushRegIfUsed (m6502_reg_a);
2483 transferRegReg (m6502_reg_y, m6502_reg_a, true);
2484 rmwWithReg (rmwop, m6502_reg_a);
2485 transferRegReg (m6502_reg_a, m6502_reg_y, true);
2486 pullOrFreeReg (m6502_reg_a, needpulla);
2489 else
2491 emitcode("ERROR", "bad reg in rmwWithReg()");
2493 // always dirty dest. register
2494 // m6502_dirtyReg (reg);
2497 /**************************************************************************
2498 * rmwWithAop - Emit read/modify/write instruction rmwop with the byte at
2499 * logical offset loffset of asmop aop.
2500 * Supports: bit, dec, inc, lsl, lsr, neg, rol, ror
2501 *************************************************************************/
2502 static void
2503 rmwWithAop (char *rmwop, asmop * aop, int loffset)
2505 bool needpull = false;
2506 emitComment (TRACE_AOP, __func__ );
2508 if (aop->stacked && aop->stk_aop[loffset])
2510 rmwWithAop (rmwop, aop->stk_aop[loffset], 0);
2511 return;
2514 switch (aop->type)
2516 case AOP_REG:
2517 rmwWithReg (rmwop, aop->aopu.aop_reg[loffset]);
2518 break;
2519 case AOP_DIR:
2520 case AOP_EXT:
2521 aopAdrPrepare(aop, loffset);
2522 // TODO: this sucks
2523 if (!strcmp("asr", rmwop))
2525 emit6502op("pha", "");
2526 emit6502op("lda", aopAdrStr(aop, loffset, false)); // load
2527 emit6502op("cmp", "#0x80");
2528 emit6502op("pla", "");
2529 rmwop = "ror";
2531 emit6502op (rmwop, aopAdrStr(aop, loffset, false));
2532 aopAdrUnprepare(aop, loffset);
2533 break;
2534 case AOP_DUMMY:
2535 break;
2536 case AOP_SOF:
2538 emitComment (TRACE_AOP, " rmwWithAop AOP_SOF");
2539 // TODO: does anything but A make sense here?
2540 reg_info * reg = getFreeByteReg();
2541 if (!reg) reg = m6502_reg_a;
2542 int offset = loffset; // SEH: aop->size - 1 - loffset;
2543 offset += _G.stackOfs + _G.stackPushes + aop->aopu.aop_stk + 1;
2544 // if ((offset > 0xff) || (offset < 0))
2546 emitComment (TRACE_AOP, " rmwWithAop large offset");
2547 /* Unfortunately, the rmw class of instructions only support a */
2548 /* single byte stack pointer offset and we need two. */
2549 needpull = pushRegIfUsed (reg);
2550 loadRegFromAop (reg, aop, loffset);
2551 rmwWithReg (rmwop, reg);
2552 if (strcmp ("tst", rmwop)) //TODO: no tst
2553 storeRegToAop (reg, aop, loffset);
2554 pullOrFreeReg (reg, needpull);
2555 break;
2557 /* If the offset is small enough, fall through to default case */
2559 default:
2560 emitComment (TRACE_AOP, " rmwWithAop small offset ");
2561 // TODO: [aa],y dosn't work with inc/dec
2562 // emitcode (rmwop, "%s ;type %d", aopAdrStr (aop, loffset, false), aop->type);
2563 emit6502op (rmwop, aopAdrStr (aop, loffset, false));
2568 /**************************************************************************
2569 * stores reg in DPTR at offset dofs
2570 *************************************************************************/
2571 static void
2572 storeRegToDPTR(reg_info *reg, int dofs)
2574 int regidx=reg->rIdx;
2576 if(reg->isLitConst && _G.DPTRAttr[dofs].isLiteral
2577 && reg->litConst == _G.DPTRAttr[dofs].literalValue )
2578 return;
2580 switch(regidx) {
2581 case A_IDX:
2582 emit6502op ("sta", DPTRFMT, dofs);
2583 break;
2584 case X_IDX:
2585 emit6502op ("stx", DPTRFMT, dofs);
2586 break;
2587 case Y_IDX:
2588 emit6502op ("sty", DPTRFMT, dofs);
2589 break;
2590 default:
2591 emitcode("ERROR"," %s: illegal register index %d", __func__, regidx);
2592 return;
2595 _G.DPTRAttr[dofs].isLiteral=reg->isLitConst;
2596 _G.DPTRAttr[dofs].literalValue=reg->litConst;
2598 m6502_freeReg(reg);
2601 /**************************************************************************
2602 * sets up DPTR for a indexed operation
2603 * clobbers A if savea==false and clobbers Y if savea==true
2604 *************************************************************************/
2605 static int
2606 setupDPTR(operand *op, int offset, char * rematOfs, bool savea)
2608 emitComment (TRACEGEN, __func__);
2610 /* The rematerialized offset may have a "#" prefix; skip over it */
2611 if (rematOfs && rematOfs[0] == '#')
2612 rematOfs++;
2613 if (rematOfs && !rematOfs[0])
2614 rematOfs = NULL;
2616 /* force offset to signed 16-bit range */
2617 offset &= 0xffff;
2618 if (offset & 0x8000)
2619 offset = 0x10000 - offset;
2620 // offset = offset - 0x10000;
2622 if(!op)
2624 emitcode("ERROR", " %s: op is null", __func__);
2625 return 0;
2628 if (!rematOfs && offset >= 0 && offset <= 0xff)
2630 // no remat and 8-bit offset
2631 if(AOP_TYPE(op) == AOP_REG) {
2632 emitComment (TRACEGEN|VVDBG, " %s: AOP_REG", __func__);
2633 storeRegToDPTR(AOP(op)->aopu.aop_reg[0], 0);
2634 storeRegToDPTR(AOP(op)->aopu.aop_reg[1], 1);
2636 else
2638 emitComment (TRACEGEN|VVDBG, " %s: not AOP_REG", __func__);
2639 if(savea) transferRegReg(m6502_reg_a, m6502_reg_y, true);
2640 loadRegFromAop(m6502_reg_a, AOP(op), 0);
2641 storeRegToDPTR(m6502_reg_a, 0);
2642 loadRegFromAop(m6502_reg_a, AOP(op), 1);
2643 storeRegToDPTR(m6502_reg_a, 1);
2644 if(savea) transferRegReg(m6502_reg_y, m6502_reg_a, true);
2645 else m6502_freeReg(m6502_reg_a);
2647 return offset;
2649 else
2651 // general case
2652 emitComment (TRACEGEN|VVDBG, " %s: general case", __func__);
2654 if(!rematOfs) rematOfs="0";
2656 if(savea) transferRegReg(m6502_reg_a, m6502_reg_y, true);
2658 emitSetCarry(0);
2659 loadRegFromAop(m6502_reg_a, AOP(op), 0);
2660 emit6502op ("adc", "#<(%s+%d)", rematOfs, offset);
2661 storeRegToDPTR(m6502_reg_a, 0);
2662 loadRegFromAop(m6502_reg_a, AOP(op), 1);
2663 emit6502op ("adc", "#>(%s+%d)", rematOfs, offset);
2664 storeRegToDPTR(m6502_reg_a, 1);
2665 if(savea) transferRegReg(m6502_reg_y, m6502_reg_a, true);
2666 else m6502_freeReg(m6502_reg_a);
2667 return 0;
2671 /**************************************************************************
2672 * newAsmop - creates a new asmOp
2673 *************************************************************************/
2674 static asmop *
2675 newAsmop (short type)
2677 asmop *aop;
2678 // TODO: are these ever freed?
2679 aop = Safe_calloc (1, sizeof (asmop));
2680 aop->type = type;
2681 aop->op = NULL;
2682 return aop;
2685 #if 0
2686 /**************************************************************************
2687 * operandConflictsWithYX - true if operand in y and/or x register
2688 *************************************************************************/
2689 static bool
2690 operandConflictsWithYX (operand *op)
2692 symbol *sym;
2693 int i;
2695 if (IS_ITEMP (op))
2697 sym = OP_SYMBOL (op);
2698 if (!sym->isspilt)
2700 for(i = 0; i < sym->nRegs; i++)
2701 if (sym->regs[i] == m6502_reg_y || sym->regs[i] == m6502_reg_x)
2702 return true;
2706 return false;
2709 /**************************************************************************
2710 * operandConflictsWithX - true if operand in x register
2711 *************************************************************************/
2712 static bool
2713 operandConflictsWithX (operand *op)
2715 symbol *sym;
2716 int i;
2718 if (IS_ITEMP (op))
2720 sym = OP_SYMBOL (op);
2721 if (!sym->isspilt)
2723 for(i = 0; i < sym->nRegs; i++)
2724 if (sym->regs[i] == m6502_reg_x)
2725 return true;
2729 return false;
2732 /**************************************************************************
2733 * operandOnStack - returns True if operand is on the stack
2734 *************************************************************************/
2735 static bool
2736 operandOnStack(operand *op)
2738 symbol *sym;
2740 if (!op || !IS_SYMOP (op))
2741 return false;
2742 sym = OP_SYMBOL (op);
2743 if (!sym->isspilt && sym->onStack)
2744 return true;
2745 if (sym->isspilt)
2747 sym = sym->usl.spillLoc;
2748 if (sym && sym->onStack)
2749 return true;
2751 return false;
2754 /**************************************************************************
2755 * tsxUseful - returns True if tsx could help at least one
2756 * anticipated stack references
2757 *************************************************************************/
2758 static bool
2759 tsxUseful(const iCode *ic)
2761 operand *right = IC_RIGHT(ic);
2762 operand *left = IC_LEFT(ic);
2763 operand *result = IC_RESULT(ic);
2764 int uses = 0;
2766 if (ic->op == CALL)
2768 if (result && operandSize (result) < 2 && operandOnStack (result))
2770 uses++;
2771 ic = ic->next;
2775 while (ic && uses < 1) {
2776 if (ic->op == IFX) {
2777 if (operandOnStack (IC_COND (ic)))
2778 uses += operandSize(IC_COND (ic));
2779 break;
2780 } else if (ic->op == JUMPTABLE) {
2781 if (operandOnStack (IC_JTCOND (ic)))
2782 uses++;
2783 break;
2784 } else if (ic->op == ADDRESS_OF) {
2785 if (operandOnStack (right))
2786 break;
2787 } else if (ic->op == LABEL || ic->op == GOTO || ic->op == CALL || ic->op == PCALL)
2788 break;
2789 else if (POINTER_SET (ic) || POINTER_GET (ic))
2790 break;
2791 else {
2792 if (operandConflictsWithYX (result))
2793 break;
2794 if (operandOnStack (left))
2795 uses += operandSize (left);
2796 if (operandOnStack (right))
2797 uses += operandSize (right);
2798 if (operandOnStack (result))
2799 uses += operandSize (result);
2802 ic = ic->next;
2805 return uses >= 1;
2807 #endif
2809 static void
2810 doTSX()
2812 emitComment (TRACE_STACK|VVDBG, "%s: stackOfs=%d tsx=%d stackpush=%d",
2813 __func__, _G.stackOfs, _G.tsxStackPushes, _G.stackPushes);
2815 // already did TSX
2816 if (m6502_reg_x->aop == &tsxaop)
2817 return;
2819 // put stack pointer in X
2820 if(!m6502_reg_x->isFree)
2821 emitcode("ERROR","doTSX called with X in use");
2822 emit6502op ("tsx", "");
2823 m6502_dirtyReg (m6502_reg_x);
2824 m6502_reg_x->aop = &tsxaop;
2825 _G.tsxStackPushes = _G.stackPushes;
2828 // TODO: make these subroutines
2829 static void saveBasePtr()
2831 #if 0
2832 storeRegTemp (m6502_reg_x, true); // TODO: only when used?
2833 // TODO: if X is free should we call doTSX() to mark X=S?
2834 doTSX();
2835 emit6502op ("stx", BASEPTR);
2836 _G.baseStackPushes = _G.stackPushes;
2837 loadRegTemp (m6502_reg_x);
2838 #endif
2841 static void
2842 restoreBasePtr()
2844 // we recompute with saveBasePtr() after each jsr
2847 /**************************************************************************
2848 * aopForSym - for a true symbol
2849 *************************************************************************/
2850 static asmop * aopForSym (const iCode * ic, symbol * sym)
2852 asmop *aop;
2853 memmap *space;
2855 wassertl (ic != NULL, "Got a null iCode");
2856 wassertl (sym != NULL, "Got a null symbol");
2858 emitComment (TRACE_AOP|VVDBG, "%s", __func__);
2860 space = SPEC_OCLS (sym->etype);
2862 /* if already has one */
2863 if (sym->aop) {
2864 return sym->aop;
2867 /* special case for a function */
2868 if (IS_FUNC (sym->type)) {
2869 sym->aop = aop = newAsmop (AOP_IMMD);
2870 aop->aopu.aop_immd = Safe_calloc (1, strlen (sym->rname) + 1 + 6);
2871 sprintf (aop->aopu.aop_immd, "(%s)", sym->rname); // function pointer; take back one for RTS
2872 aop->size = FARPTRSIZE;
2873 return aop;
2876 /* if it is on the stack */
2877 if (sym->onStack) {
2878 sym->aop = aop = newAsmop (AOP_SOF);
2879 aop->size = getSize (sym->type);
2880 aop->aopu.aop_stk = sym->stack;
2882 emitComment (TRACE_STACK|VVDBG, "%s: symbol %s: stack=%d size=%d",
2883 __func__, sym->name, sym->stack, aop->size);
2885 #if 0
2886 if (!regalloc_dry_run && m6502_reg_x->isFree && m6502_reg_x->aop != &tsxaop) {
2887 if (!m6502_reg_x->isDead)
2888 return aop;
2889 if (ic->op == IFX && operandConflictsWithX (IC_COND (ic)))
2890 return aop;
2891 else if (ic->op == JUMPTABLE && operandConflictsWithX (IC_JTCOND (ic)))
2892 return aop;
2893 else {
2894 // FIXME: this is likely incorrect as YX is not a adr register in the 6502
2895 /* If this is a pointer gen/set, then hx is definitely in use */
2896 if (POINTER_SET (ic) || POINTER_GET (ic))
2897 return aop;
2898 if (ic->op == ADDRESS_OF)
2899 return aop;
2900 if (operandConflictsWithX (IC_LEFT (ic)))
2901 return aop;
2902 if (operandConflictsWithX (IC_RIGHT (ic)))
2903 return aop;
2905 // TODO?
2906 /* It's safe to use tsx here. */
2907 if (!tsxUseful (ic))
2908 return aop;
2909 // transfer S to X
2910 doTSX();
2912 #endif
2913 return aop;
2916 /* if it is in direct space */
2917 if (IN_DIRSPACE (space)) {
2918 sym->aop = aop = newAsmop (AOP_DIR);
2919 aop->aopu.aop_dir = sym->rname;
2920 aop->size = getSize (sym->type);
2921 return aop;
2924 /* default to far space */
2925 sym->aop = aop = newAsmop (AOP_EXT);
2926 aop->aopu.aop_dir = sym->rname;
2927 aop->size = getSize (sym->type);
2928 return aop;
2931 /**************************************************************************
2932 * aopForRemat - rematerializes an object
2933 *************************************************************************/
2934 static asmop * aopForRemat (symbol * sym)
2936 iCode *ic = sym->rematiCode;
2937 asmop *aop = NULL;
2938 int val = 0;
2940 if (!ic) {
2941 fprintf (stderr, "Symbol %s to be rematerialized, but has no rematiCode.\n", sym->name);
2942 wassert (0);
2945 for (;;) {
2946 if (ic->op == '+')
2947 val += (int) operandLitValue (IC_RIGHT (ic));
2948 else if (ic->op == '-')
2949 val -= (int) operandLitValue (IC_RIGHT (ic));
2950 else if (IS_CAST_ICODE (ic)) {
2951 ic = OP_SYMBOL (IC_RIGHT (ic))->rematiCode;
2952 continue;
2953 } else
2954 break;
2956 ic = OP_SYMBOL (IC_LEFT (ic))->rematiCode;
2959 if (ic->op == ADDRESS_OF) {
2960 if (val) {
2961 SNPRINTF (buffer, sizeof (buffer),
2962 "(%s %c 0x%04x)", OP_SYMBOL (IC_LEFT (ic))->rname, val >= 0 ? '+' : '-', abs (val) & 0xffff);
2963 } else {
2964 strncpyz (buffer, OP_SYMBOL (IC_LEFT (ic))->rname, sizeof (buffer));
2967 aop = newAsmop (AOP_IMMD);
2968 aop->aopu.aop_immd = Safe_strdup (buffer);
2969 } else if (ic->op == '=') {
2970 val += (int) operandLitValue (IC_RIGHT (ic));
2971 val &= 0xffff;
2972 SNPRINTF (buffer, sizeof (buffer), "0x%04x", val);
2973 aop = newAsmop (AOP_LIT);
2974 aop->aopu.aop_lit = constVal (buffer);
2975 } else {
2976 werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "unexpected rematerialization");
2979 return aop;
2982 #if 0 // No longer used?
2983 /**************************************************************************
2984 * regsInCommon - two operands have some registers in common
2985 *************************************************************************/
2986 static bool regsInCommon (operand * op1, operand * op2)
2988 symbol *sym1, *sym2;
2989 int i;
2991 /* if they have registers in common */
2992 if (!IS_SYMOP (op1) || !IS_SYMOP (op2))
2993 return false;
2995 sym1 = OP_SYMBOL (op1);
2996 sym2 = OP_SYMBOL (op2);
2998 if (sym1->nRegs == 0 || sym2->nRegs == 0)
2999 return false;
3001 for (i = 0; i < sym1->nRegs; i++)
3003 int j;
3004 if (!sym1->regs[i])
3005 continue;
3007 for (j = 0; j < sym2->nRegs; j++)
3009 if (!sym2->regs[j])
3010 continue;
3012 if (sym2->regs[j] == sym1->regs[i])
3013 return true;
3017 return false;
3019 #endif
3021 /**************************************************************************
3022 * operandsEqu - equivalent
3023 *************************************************************************/
3024 static bool operandsEqu (operand *op1, operand *op2)
3026 symbol *sym1, *sym2;
3028 /* if they not symbols */
3029 if (!IS_SYMOP (op1) || !IS_SYMOP (op2))
3030 return false;
3032 sym1 = OP_SYMBOL (op1);
3033 sym2 = OP_SYMBOL (op2);
3035 /* if both are itemps & one is spilt
3036 and the other is not then false */
3037 if (IS_ITEMP (op1) && IS_ITEMP (op2) && sym1->isspilt != sym2->isspilt)
3038 return false;
3040 /* if they are the same */
3041 if (sym1 == sym2)
3042 return true;
3044 /* if they have the same rname */
3045 if (sym1->rname[0] && sym2->rname[0] && strcmp (sym1->rname, sym2->rname) == 0)
3046 return true;
3048 /* if left is a tmp & right is not */
3049 if (IS_ITEMP (op1) && !IS_ITEMP (op2) && sym1->isspilt && (sym1->usl.spillLoc == sym2))
3050 return true;
3052 if (IS_ITEMP (op2) && !IS_ITEMP (op1) && sym2->isspilt && sym1->level > 0 && (sym2->usl.spillLoc == sym1))
3053 return true;
3055 return false;
3058 /**************************************************************************
3059 * sameRegs - two asmops have the same registers
3060 *************************************************************************/
3061 static bool
3062 sameRegs (asmop * aop1, asmop * aop2)
3064 int i;
3066 if (aop1 == aop2)
3067 return true;
3069 // if (aop1->size != aop2->size)
3070 // return false;
3072 if (aop1->type == aop2->type) {
3073 switch (aop1->type) {
3074 case AOP_REG:
3075 for (i = 0; i < aop1->size; i++)
3076 if (aop1->aopu.aop_reg[i] != aop2->aopu.aop_reg[i])
3077 return false;
3078 return true;
3079 case AOP_SOF:
3080 return (aop1->aopu.aop_stk == aop2->aopu.aop_stk);
3081 case AOP_DIR:
3082 // if (regalloc_dry_run)
3083 // return false; // TODO: why?
3084 case AOP_EXT:
3085 return (!strcmp (aop1->aopu.aop_dir, aop2->aopu.aop_dir));
3086 default:
3087 break;
3091 return false;
3094 /**************************************************************************
3095 * aopCanIncDec - asmop is EXT or DIR or X/Y
3097 *************************************************************************/
3098 static bool
3099 aopCanIncDec (asmop * aop)
3101 switch (aop->type)
3103 case AOP_REG:
3104 if(aop->aopu.aop_reg[0]->rIdx == A_IDX) return IS_MOS65C02;
3105 case AOP_DIR:
3106 case AOP_EXT:
3107 return true;
3108 default:
3109 break;
3111 return false;
3114 /**************************************************************************
3115 * aopCanShift - asmop is EXT or DIR or A
3117 *************************************************************************/
3118 static bool aopCanShift (asmop * aop)
3120 switch (aop->type) {
3121 case AOP_REG:
3122 return ((aop->size == 1) && (aop->aopu.aop_reg[0]->rIdx == A_IDX));
3123 case AOP_DIR:
3124 case AOP_EXT:
3125 return true;
3126 default:
3127 break;
3129 return false;
3132 /**************************************************************************
3133 * aopOp - allocates an asmop for an operand
3134 *************************************************************************/
3135 static void
3136 aopOp (operand *op, const iCode * ic)
3138 asmop *aop = NULL;
3139 symbol *sym;
3140 int i;
3142 emitComment (TRACE_AOP, __func__);
3144 if (!op)
3145 return;
3147 /* if already has an asmop */
3148 if (op->aop)
3150 emitComment (VVDBG|TRACE_AOP, " %s: skip", __func__);
3151 if (IS_SYMOP (op) && OP_SYMBOL (op)->aop)
3153 if(op->aop->type==AOP_SOF) {
3154 emitComment (VVDBG|TRACE_AOP, " asmop symbol: %s [%d:%d] - %d",
3155 OP_SYMBOL (op)->name, OP_SYMBOL (op)->stack, op->aop->size,
3156 op->aop->aopu.aop_stk );
3157 // FIXME: ugly fix to correct stack offset for some symbols
3158 // Should find the source of the bug
3159 op->aop->aopu.aop_stk = OP_SYMBOL (op)->stack;
3162 return;
3165 // Is this a pointer set result?
3166 if ((op == IC_RESULT (ic)) && POINTER_SET (ic))
3168 emitComment (VVDBG|TRACE_AOP, " %s: POINTER_SET", __func__);
3171 /* if this a literal */
3172 if (IS_OP_LITERAL (op))
3174 emitComment (VVDBG|TRACE_AOP, " %s: LITERAL = 0x%x:%d",
3175 __func__, ulFromVal(OP_VALUE (op)), getSize(operandType(op)) );
3176 aop = newAsmop (AOP_LIT);
3177 aop->aopu.aop_lit = OP_VALUE (op);
3178 aop->size = getSize (operandType (op));
3179 op->aop = aop;
3180 aop->op = op; // asmopToBool needs the op to check thetype of the literal.
3181 return;
3185 // printf("checking underlying sym\n");
3186 /* if the underlying symbol has a aop */
3187 if (IS_SYMOP (op) && OP_SYMBOL (op)->aop)
3189 emitComment (VVDBG|TRACE_AOP, " %s: SYMOP", __func__);
3190 op->aop = aop = Safe_calloc (1, sizeof (*aop));
3191 memcpy (aop, OP_SYMBOL (op)->aop, sizeof (*aop));
3192 //op->aop = aop = OP_SYMBOL (op)->aop;
3193 aop->size = getSize (operandType (op));
3194 emitComment (VVDBG|TRACE_AOP, " symbol: %s [%d]",
3195 OP_SYMBOL (op)->name, OP_SYMBOL (op)->stack );
3196 //printf ("reusing underlying symbol %s\n",OP_SYMBOL (op)->name);
3197 //printf (" with size = %d\n", aop->size);
3199 aop->op = op;
3200 return;
3203 // printf("checking true sym\n");
3204 /* if this is a true symbol */
3205 if (IS_TRUE_SYMOP (op))
3207 emitComment (VVDBG|TRACE_AOP, " %s: TRUE_SYMOP", __func__);
3208 op->aop = aop = aopForSym (ic, OP_SYMBOL (op));
3209 aop->op = op;
3210 //printf ("new symbol %s\n", OP_SYMBOL (op)->name);
3211 //printf (" with size = %d\n", aop->size);
3212 return;
3215 /* this is a temporary : this has
3216 only five choices :
3217 a) register
3218 b) spillocation
3219 c) rematerialize
3220 d) conditional
3221 e) can be a return use only */
3223 if (!IS_SYMOP (op))
3224 piCode (ic, NULL);
3225 sym = OP_SYMBOL (op);
3227 // printf("checking conditional\n");
3228 /* if the type is a conditional */
3229 if (sym->regType == REG_CND)
3231 sym->aop = op->aop = aop = newAsmop (AOP_CRY);
3232 aop->size = 0;
3233 aop->op = op;
3234 return;
3237 // printf("checking spilt\n");
3238 /* if it is spilt then two situations
3239 a) is rematerialize
3240 b) has a spill location */
3241 if (sym->isspilt || sym->nRegs == 0)
3243 // printf("checking remat\n");
3244 /* rematerialize it NOW */
3245 if (sym->remat)
3247 sym->aop = op->aop = aop = aopForRemat (sym);
3248 aop->size = getSize (sym->type);
3249 aop->op = op;
3250 return;
3253 wassertl (!sym->ruonly, "sym->ruonly not supported");
3255 if (regalloc_dry_run)
3257 // Todo: Handle dummy iTemp correctly
3258 if (options.stackAuto || (currFunc && IFFUNC_ISREENT (currFunc->type)))
3260 sym->aop = op->aop = aop = newAsmop (AOP_SOF);
3261 aop->aopu.aop_stk = 8; /* bogus stack offset, high enough to prevent optimization */
3263 else
3265 sym->aop = op->aop = aop = newAsmop (AOP_DIR);
3266 aop->aopu.aop_dir = sym->name; //TODO? avoids crashing in sameRegs()
3268 aop->size = getSize (sym->type);
3269 aop->op = op;
3270 return;
3273 /* else spill location */
3274 if (sym->isspilt && sym->usl.spillLoc || regalloc_dry_run)
3276 asmop *oldAsmOp = NULL;
3278 if (sym->usl.spillLoc->aop && sym->usl.spillLoc->aop->size != getSize (sym->type))
3280 /* force a new aop if sizes differ */
3281 oldAsmOp = sym->usl.spillLoc->aop;
3282 sym->usl.spillLoc->aop = NULL;
3283 //printf ("forcing new aop\n");
3285 sym->aop = op->aop = aop = aopForSym (ic, sym->usl.spillLoc);
3286 if (sym->usl.spillLoc->aop->size != getSize (sym->type))
3288 /* Don't reuse the new aop, go with the last one */
3289 sym->usl.spillLoc->aop = oldAsmOp;
3291 aop->size = getSize (sym->type);
3292 aop->op = op;
3293 //printf ("spill symbol %s\n", OP_SYMBOL (op)->name);
3294 //printf (" with size = %d\n", aop->size);
3295 return;
3298 /* else must be a dummy iTemp */
3299 sym->aop = op->aop = aop = newAsmop (AOP_DUMMY);
3300 aop->size = getSize (sym->type);
3301 aop->op = op;
3302 return;
3305 // printf("assuming register\n");
3306 /* must be in a register */
3307 wassert (sym->nRegs);
3308 sym->aop = op->aop = aop = newAsmop (AOP_REG);
3309 aop->size = sym->nRegs;
3310 for (i = 0; i < sym->nRegs; i++)
3312 wassert (sym->regs[i] >= regsm6502 && sym->regs[i] < regsm6502 + 3);
3313 wassertl (sym->regs[i], "Symbol in register, but no register assigned.");
3314 aop->aopu.aop_reg[i] = sym->regs[i];
3315 aop->regmask |= sym->regs[i]->mask;
3317 // if ((sym->nRegs > 1) && (sym->regs[0]->mask > sym->regs[1]->mask))
3318 // aop->regmask |= M6502MASK_REV;
3319 aop->op = op;
3322 /**************************************************************************
3323 * freeAsmop - free up the asmop given to an operand
3324 *************************************************************************/
3325 static void
3326 freeAsmop (operand * op, asmop * aaop)
3328 asmop *aop;
3330 if (!op)
3331 aop = aaop;
3332 else
3333 aop = op->aop;
3335 if (!aop)
3336 return;
3338 if (aop->freed)
3339 goto dealloc;
3341 aop->freed = 1;
3343 if (aop->stacked) {
3344 int stackAdjust;
3345 int loffset;
3347 emitComment (TRACE_AOP, " freeAsmop restoring stacked %s", aopName (aop));
3348 aop->stacked = 0;
3349 stackAdjust = 0;
3350 for (loffset = 0; loffset < aop->size; loffset++)
3351 if (aop->stk_aop[loffset]) {
3352 transferAopAop (aop->stk_aop[loffset], 0, aop, loffset);
3353 stackAdjust++;
3355 pullNull (stackAdjust);
3358 dealloc:
3359 /* all other cases just dealloc */
3360 if (op) {
3361 op->aop = NULL;
3362 if (IS_SYMOP (op)) {
3363 OP_SYMBOL (op)->aop = NULL;
3364 /* if the symbol has a spill */
3365 if (SPIL_LOC (op))
3366 SPIL_LOC (op)->aop = NULL;
3372 /**************************************************************************
3373 * aopDerefAop - treating the aop parameter as a pointer, return an asmop
3374 * for the object it references
3375 *************************************************************************/
3376 static asmop * aopDerefAop (asmop * aop, int offset)
3378 int adr;
3379 asmop *newaop = NULL;
3380 sym_link *type, *etype;
3381 int p_type;
3382 struct dbuf_s dbuf;
3384 emitComment (TRACE_AOP, " aopDerefAop(%s)", aopName (aop));
3385 if (aop->op) {
3386 type = operandType (aop->op);
3387 etype = getSpec (type);
3388 /* if op is of type of pointer then it is simple */
3389 if (IS_PTR (type) && !IS_FUNC (type->next))
3390 p_type = DCL_TYPE (type);
3391 else
3393 /* we have to go by the storage class */
3394 p_type = PTR_TYPE (SPEC_OCLS (etype));
3397 else
3398 p_type = UPOINTER;
3400 switch (aop->type) {
3401 case AOP_IMMD:
3402 if (p_type == POINTER)
3403 newaop = newAsmop (AOP_DIR);
3404 else
3405 newaop = newAsmop (AOP_EXT);
3406 if (!offset)
3407 newaop->aopu.aop_dir = aop->aopu.aop_immd;
3408 else {
3409 dbuf_init (&dbuf, 64);
3410 dbuf_printf (&dbuf, "(%s+%d)", aop->aopu.aop_immd, offset);
3411 newaop->aopu.aop_dir = dbuf_detach_c_str (&dbuf);
3413 break;
3414 case AOP_LIT:
3415 adr = (int) ulFromVal (aop->aopu.aop_lit);
3416 if (p_type == POINTER)
3417 adr &= 0xff;
3418 adr = (adr + offset) & 0xffff;
3419 dbuf_init (&dbuf, 64);
3421 if (adr < 0x100) {
3422 newaop = newAsmop (AOP_DIR);
3423 dbuf_printf (&dbuf, "0x%02x", adr);
3424 } else {
3425 newaop = newAsmop (AOP_EXT);
3426 dbuf_printf (&dbuf, "0x%04x", adr);
3428 newaop->aopu.aop_dir = dbuf_detach_c_str (&dbuf);
3429 break;
3430 default:
3431 werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "unsupported asmop");
3432 return NULL;
3435 return newaop;
3438 /**************************************************************************
3439 * aopOpExtToIdx - attempt to convert AOP_EXT to AOP_IDX
3440 *************************************************************************/
3441 static void
3442 aopOpExtToIdx(asmop * result, asmop *left, asmop *right)
3444 #if 0
3445 int accesses=0;
3446 int resultAccesses=0;
3447 int leftAccesses=0;
3448 int rightAccesses=0;
3449 asmop * winner;
3450 int winnerAccesses;
3452 return; //TODO: makes things worse often
3454 if (!m6502_reg_x->isFree || !m6502_reg_y->isFree)
3455 return;
3457 /* Need to replace at least two extended mode accesses with indexed */
3458 /* to break even with the extra cost of loading YX. Do a quick check */
3459 /* to see if anything is using extended mode at all. */
3460 if (result && result->type == AOP_EXT)
3461 accesses += result->size;
3462 if (left && left->type == AOP_EXT)
3463 accesses += left->size;
3464 if (right && right->type == AOP_EXT)
3465 accesses += right->size;
3466 if (accesses<2)
3467 return;
3469 /* If an operand is already using or going to Y or X then we cannot */
3470 /* use indexed addressing mode at the same time. */
3471 if (result && (IS_AOP_WITH_Y (result) || IS_AOP_WITH_X (result)))
3472 return;
3473 if (left && (IS_AOP_WITH_Y (left) || IS_AOP_WITH_X (left)))
3474 return;
3475 if (right && (IS_AOP_WITH_Y (right) || IS_AOP_WITH_X (right)))
3476 return;
3478 /* Decide which is the best asmop to make indexed. */
3479 if (result && result->type == AOP_EXT) {
3480 resultAccesses = result->size;
3481 if (result->op && left && left->op && result->op->key == left->op->key)
3482 resultAccesses += result->size;
3483 if (result->op && right && right->op && result->op->key == right->op->key)
3484 resultAccesses += result->size;
3486 if (left && left->type == AOP_EXT) {
3487 leftAccesses = left->size;
3488 if (left->op && right && right->op && left->op->key == right->op->key)
3489 leftAccesses += left->size;
3491 if (right && right->type == AOP_EXT) {
3492 rightAccesses = right->size;
3495 winner = result; winnerAccesses = resultAccesses;
3496 if (leftAccesses > winnerAccesses) {
3497 winnerAccesses = leftAccesses;
3498 winner = left;
3500 if (rightAccesses > winnerAccesses) {
3501 winnerAccesses = rightAccesses;
3502 winner = right;
3505 /* Make sure there were enough accesses of a single variable to be worthwhile. */
3506 if (winnerAccesses < 2)
3507 return;
3509 if (winner->op && result && result->op && winner->op->key == result->op->key)
3510 result->type = AOP_IDX;
3511 if (winner->op && left && left->op && winner->op->key == left->op->key)
3512 left->type = AOP_IDX;
3513 if (winner->op && right && right->op && winner->op->key == right->op->key)
3514 right->type = AOP_IDX;
3515 loadRegFromImm (m6502_reg_yx, winner->aopu.aop_dir);
3516 #endif
3519 // is it safe to aopAdrStr?
3520 static bool isAddrSafe(operand* op, reg_info* reg)
3522 switch (AOP(op)->type) {
3523 case AOP_IMMD: // #nn
3524 case AOP_LIT:
3525 case AOP_DIR: // aa
3526 case AOP_EXT: // aaaa
3527 return true;
3528 case AOP_SOF: // (BASEPTR),y
3529 if (reg == m6502_reg_a && (m6502_reg_x->isFree || m6502_reg_y->isFree))
3530 return true;
3531 default:
3532 break;
3534 return false;
3537 static int aopPrepareStoreTemp = 0;
3538 static int aopPreparePreserveFlags = 0;
3540 // TODO: make sure this is called before/after aopAdrStr if indexing might be used
3541 static void aopAdrPrepare (asmop * aop, int loffset)
3543 aopPreparePreserveFlags = 0;
3544 if (loffset > (aop->size - 1))
3545 return;
3547 if (aop->type==AOP_SOF) {
3548 #if 0
3549 // code for lda [BASEPTR],y
3550 aopPrepareStoreTemp = storeRegTemp(m6502_reg_y, false);
3551 // FIXME: offset is wrong
3552 emitComment (TRACE_AOP, "ofs=%d base=%d tsx=%d push=%d stk=%d loffset=%d", _G.stackOfs, _G.baseStackPushes, _G.tsxStackPushes, _G.stackPushes, aop->aopu.aop_stk, loffset);
3553 loadRegFromConst(m6502_reg_y, _G.stackOfs + _G.baseStackPushes + aop->aopu.aop_stk + loffset + 1);
3554 // ORIG: loadRegFromConst(m6502_reg_y, _G.stackOfs - _G.baseStackPushes + aop->aopu.aop_stk + loffset + 1);
3555 m6502_reg_y->aop = &tsxaop;
3556 #else
3557 // can we get stack pointer?
3558 aopPrepareStoreTemp=0;
3559 if (!m6502_reg_x->isFree) {
3560 // FIXME: check if used/dead is ok
3561 // aopPrepareStoreTemp = storeRegTempIfSurv(m6502_reg_x);
3562 storeRegTemp(m6502_reg_x, true);
3563 aopPrepareStoreTemp = true;
3564 // m6502_reg_x->isFree=true;
3567 doTSX();
3568 #endif
3569 aopPreparePreserveFlags = 1; // TODO: also need to make sure flags are needed by caller
3573 static void aopAdrUnprepare (asmop * aop, int loffset)
3575 if (loffset > (aop->size - 1))
3576 return;
3578 if (aop->type==AOP_SOF) {
3579 if (aopPrepareStoreTemp) {
3580 if (aopPreparePreserveFlags)
3581 loadRegTempNoFlags(m6502_reg_x, true);
3582 else
3583 loadRegTemp(m6502_reg_x);
3585 aopPreparePreserveFlags = 0;
3586 aopPrepareStoreTemp = 0;
3592 /**************************************************************************
3593 * aopAdrStr - for referencing the address of the aop
3594 *************************************************************************/
3595 /* loffset seems to have a weird meaning here. It seems to be nonzero in some places where one would expect an offset to be zero */
3596 static const char * aopAdrStr (asmop * aop, int loffset, bool bit16)
3598 char *s = buffer;
3599 char *rs;
3600 int offset = loffset; // SEH: aop->size - 1 - loffset - (bit16 ? 1 : 0);
3601 int xofs;
3603 /* offset is greater than
3604 size then zero */
3605 if (loffset > (aop->size - 1) && aop->type != AOP_LIT)
3606 return "#0x00";
3608 /* depending on type */
3609 switch (aop->type) {
3610 case AOP_DUMMY:
3611 return "#0x00";
3613 case AOP_IMMD:
3614 if (loffset) {
3615 if (loffset > 1)
3616 sprintf (s, "#(%s >> %d)", aop->aopu.aop_immd, loffset * 8);
3617 else
3618 sprintf (s, "#>%s", aop->aopu.aop_immd);
3620 else
3621 sprintf (s, "#%s", aop->aopu.aop_immd);
3622 rs = Safe_calloc (1, strlen (s) + 1);
3623 strcpy (rs, s);
3624 return rs;
3626 case AOP_DIR:
3627 if (regalloc_dry_run)
3628 return "*dry";
3629 if (offset)
3630 sprintf (s, "*(%s + %d)", aop->aopu.aop_dir, offset);
3631 else
3632 sprintf (s, "*%s", aop->aopu.aop_dir);
3633 rs = Safe_calloc (1, strlen (s) + 1);
3634 strcpy (rs, s);
3635 return rs;
3637 case AOP_EXT:
3638 if (regalloc_dry_run)
3639 return "dry";
3640 if (offset)
3641 sprintf (s, "(%s + %d)", aop->aopu.aop_dir, offset);
3642 else
3643 sprintf (s, "%s", aop->aopu.aop_dir);
3644 rs = Safe_calloc (1, strlen (s) + 1);
3645 strcpy (rs, s);
3646 return rs;
3648 case AOP_REG:
3649 return aop->aopu.aop_reg[loffset]->name;
3651 case AOP_LIT:
3652 if (bit16)
3653 return aopLiteralLong (aop->aopu.aop_lit, loffset, 2);
3654 else
3655 return aopLiteral (aop->aopu.aop_lit, loffset);
3657 case AOP_SOF: // TODO?
3658 if (regalloc_dry_run) {
3659 return "0x100,x"; // fake result, not needed
3660 } else {
3661 // FIXME FIXME: force emit of TSX to avoid offset < 0x100
3662 // this is a workaround for the assembler incorrectly
3663 // generating ZP,x instead of ABS,x
3664 if((_G.stackOfs + _G.tsxStackPushes + aop->aopu.aop_stk + offset + 1)<0)
3666 m6502_dirtyReg(m6502_reg_x);
3668 doTSX();
3669 // hc08's tsx returns +1, ours returns +0
3670 //DD( emitcode( "", "; %d + %d + %d + %d + 1", _G.stackOfs, _G.tsxStackPushes, aop->aopu.aop_stk, offset ));
3671 xofs = STACK_TOP + _G.stackOfs + _G.tsxStackPushes + aop->aopu.aop_stk + offset + 1;
3672 emitComment(VVDBG|TRACE_STACK," op target: STACK_FRAME%+d (SP%+d [%d, %d, %d])",
3673 /*_G.tsxStackPushes + */aop->aopu.aop_stk + offset + 1,
3674 _G.stackOfs + aop->aopu.aop_stk + offset + 1,
3675 _G.tsxStackPushes, aop->aopu.aop_stk, offset);
3676 sprintf (s, "0x%x,x", xofs);
3677 rs = Safe_calloc (1, strlen (s) + 1);
3678 strcpy (rs, s);
3679 return rs;
3680 #if 0
3681 else if (m6502_reg_y->aop == &tsxaop) {
3682 return "[__BASEPTR],y";
3683 } else {
3684 // FIXME: unimplemented
3685 // loadRegFromConst(m6502_reg_x, offset);
3686 return "ERROR [__BASEPTR],y"; // TODO: is base ptr or Y loaded?
3688 #endif
3690 case AOP_IDX:
3691 #if 0
3692 xofs = offset; /* For now, assume yx points to the base address of operand */
3693 // TODO: slow
3694 storeRegTemp (m6502_reg_yx, true);
3695 if (m6502_reg_y->aop == &tsxaop) {
3696 loadRegFromConst(m6502_reg_y, offset);
3697 return "ERROR [REGTEMP],y"; // TODO: what if != 0 tempOfs?
3698 } else
3699 #endif
3700 return "ERROR"; // TODO: error
3701 default:
3702 break;
3705 werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "aopAdrStr got unsupported aop->type");
3706 exit (1);
3709 /**************************************************************************
3710 * asmopToBool - Emit code to convert an asmop to a boolean.
3711 * Result left in A (0=false, 1=true) if ResultInA,
3712 * otherwise result left in Z flag (1=false, 0=true)
3713 *************************************************************************/
3714 static void
3715 asmopToBool (asmop *aop, bool resultInA)
3717 symbol *tlbl = safeNewiTempLabel (NULL);
3718 sym_link *type;
3719 int size = aop->size;
3720 int offset = size - 1;
3721 bool needloada = false;
3722 bool isFloat;
3724 emitComment (TRACE_AOP, "asmopToBool resultinA %s", resultInA?"yes":"no");
3726 wassert (aop);
3727 type = operandType (AOP_OP (aop));
3728 isFloat = IS_FLOAT (type);
3730 if (resultInA)
3731 m6502_freeReg (m6502_reg_a);
3733 if (IS_BOOL (type))
3735 if (resultInA)
3737 // result -> A
3738 loadRegFromAop (m6502_reg_a, aop, 0);
3740 else
3742 // result -> flags
3743 if (aop->type==AOP_REG)
3745 emitCpz(aop->aopu.aop_reg[0]->rIdx);
3747 else
3749 reg_info* freereg = getDeadByteReg();
3750 if (freereg)
3752 loadRegFromAop (freereg, aop, 0);
3754 else
3756 // no choice, all regs are full
3757 storeRegTemp (m6502_reg_a, true);
3758 loadRegFromAop (m6502_reg_a, aop, 0);
3759 loadRegTempNoFlags (m6502_reg_a, true); // TODO?
3763 return;
3766 #if 0
3767 if (resultInA && size == 1 && !IS_AOP_A(aop) /*_G.lastflag!=A_IDX*/)
3769 loadRegFromAop (m6502_reg_a, aop, 0);
3770 emit6502op ("cmp", "#0x01");
3771 loadRegFromConst (m6502_reg_a, 0);
3772 rmwWithReg ("rol", m6502_reg_a);
3773 return;
3775 #endif
3777 if(resultInA && size==1) {
3778 if(IS_AOP_A(aop)) emitCpz(A_IDX);
3779 else loadRegFromAop (m6502_reg_a, aop, 0);
3780 goto end;
3783 switch (aop->type)
3785 case AOP_REG:
3786 if (size==1)
3787 { // A, X or Y
3788 emitCpz(aop->aopu.aop_reg[0]->rIdx);
3789 return;
3791 else if (IS_AOP_XA (aop))
3793 // FIXME: this optimization makes the code smaller (expected) and slower (unexpected)
3794 #if 0
3795 if(_G.lastflag==X_IDX)
3797 if (!(m6502_reg_x->isLitConst && m6502_reg_x->litConst==0 ) )
3798 emit6502op ("bne", "%05d$", safeLabelNum (tlbl));
3799 emit6502op ("cmp", "#0x00");
3801 else
3802 #endif
3804 emitCpz(A_IDX);
3805 if( !(m6502_reg_x->isLitConst && m6502_reg_x->litConst==0))
3807 emit6502op ("bne", "%05d$", safeLabelNum (tlbl));
3808 // FIXME: this optimization makes the code smaller (expected) and slower (unexpected)
3809 // if(m6502_reg_a->isDead)
3810 // transferRegReg(m6502_reg_x, m6502_reg_a, true);
3811 // else
3812 emitCpz(X_IDX);
3816 else if (IS_AOP_YX (aop))
3818 emitCpz(X_IDX);
3819 emit6502op ("bne", "%05d$", safeLabelNum (tlbl));
3820 emitCpz(Y_IDX);
3822 else
3824 emitcode("ERROR", "Bad %02x regmask in asmopToBool", (aop)->regmask);
3825 return;
3827 break;
3829 case AOP_LIT:
3830 /* Higher levels should optimize this case away but let's be safe */
3831 if (ulFromVal (aop->aopu.aop_lit))
3832 loadRegFromConst (m6502_reg_a, 1);
3833 else
3834 loadRegFromConst (m6502_reg_a, 0);
3835 m6502_freeReg (m6502_reg_a);
3836 break;
3838 case AOP_DIR:
3839 case AOP_EXT:
3840 emitComment (TRACE_AOP|VVDBG, "asmopToBool - AOP_DIR || AOP_EXT");
3842 #if 1
3843 if (!resultInA && (size == 1) && !IS_AOP_A (aop) && !m6502_reg_a->isFree && m6502_reg_x->isFree)
3845 loadRegFromAop (m6502_reg_x, aop, 0);
3846 return;
3848 #else
3849 if (!resultInA && (size == 1) )
3851 reg_info *reg=getFreeByteReg();
3852 if(reg)
3854 loadRegFromAop (reg, aop, 0);
3855 return;
3858 #endif
3860 default:
3861 if (!resultInA)
3862 needloada = storeRegTempIfSurv(m6502_reg_a);
3864 loadRegFromAop (m6502_reg_a, aop, offset--);
3865 if (isFloat)
3866 emit6502op ("and", "#0x7F");
3868 while (--size)
3869 accopWithAop ("ora", aop, offset--);
3871 if (!resultInA)
3873 loadRegTempNoFlags (m6502_reg_a, needloada);
3874 return;
3876 else
3878 m6502_freeReg (m6502_reg_a);
3880 break;
3883 end:
3884 if (resultInA)
3886 symbol *endlbl = safeNewiTempLabel (NULL);
3888 emitBranch ("beq", endlbl);
3889 safeEmitLabel (tlbl);
3890 loadRegFromConst (m6502_reg_a, 1);
3891 safeEmitLabel (endlbl);
3892 _G.lastflag=A_IDX;
3893 m6502_dirtyReg (m6502_reg_a);
3894 m6502_useReg (m6502_reg_a);
3896 else
3898 if(tlbl) safeEmitLabel (tlbl);
3902 /**************************************************************************
3903 * genCopy - Copy the value from one operand to another
3904 * The caller is responsible for aopOp and freeAsmop
3905 *************************************************************************/
3906 static void genCopy (operand * result, operand * source)
3908 int size = AOP_SIZE (result);
3909 int srcsize = AOP_SIZE (source);
3910 int offset = 0;
3912 emitComment (TRACEGEN, __func__);
3913 emitComment (TRACEGEN|VVDBG, " genCopy - size %d -> %d", srcsize, size);
3914 emitComment (TRACEGEN|VVDBG, " genCopy - regmask %02x -> %02x",
3915 AOP(source)->regmask, AOP(result)->regmask );
3917 /* if they are the same and not volatile */
3918 if (operandsEqu (result, source) && !isOperandVolatile (result, false) &&
3919 !isOperandVolatile (source, false))
3920 return;
3922 /* if they are the same registers */
3923 if (sameRegs (AOP (source), AOP (result)) && srcsize == size )
3924 return;
3926 if (IS_AOP_XA (AOP (source)) && size == 2)
3928 storeRegToAop (m6502_reg_xa, AOP (result), 0);
3929 return;
3932 if(IS_AOP_XA (AOP (result)) && IS_AOP_X (AOP (source)) )
3934 transferRegReg(m6502_reg_x, m6502_reg_a, true);
3935 loadRegFromConst(m6502_reg_x, 0);
3936 return;
3939 #if 0
3940 // FIXME: enabling this produces worse code.
3941 // likely something not optimal in loadRegFromAop
3942 if(IS_AOP_XA (AOP (result)))
3944 loadRegFromAop (m6502_reg_xa, AOP(source), 0);
3945 return;
3947 #endif
3949 // TODO?
3950 // if (IS_MOS6502 && (size > 2))
3951 // aopOpExtToIdx (AOP (result), NULL, AOP (source));
3953 /* general case */
3954 emitComment (TRACEGEN|VVDBG, " genCopy (general case)", "");
3956 #if 0
3957 while (srcsize && size)
3959 transferAopAop (AOP (source), offset, AOP (result), offset);
3960 offset++;
3961 srcsize--;
3962 size--;
3964 while (size)
3966 storeConstToAop (0, AOP (result), offset);
3967 offset++;
3968 size--;
3970 #else
3971 // reverse order
3972 offset=size-1;
3974 while ( offset >= srcsize )
3976 storeConstToAop (0, AOP (result), offset);
3977 offset--;
3979 while (offset>=0)
3981 transferAopAop (AOP (source), offset, AOP (result), offset);
3982 offset--;
3984 #endif
3987 /**************************************************************************
3988 * genNot - generate code for ! operation
3989 *************************************************************************/
3990 static void genNot (iCode * ic)
3992 operand *left = IC_LEFT (ic);
3993 operand *result = IC_RESULT (ic);
3994 bool needpulla;
3996 emitComment (TRACEGEN, __func__);
3997 printIC(ic);
3999 /* assign asmOps to operand & result */
4000 aopOp (left, ic);
4001 aopOp (result, ic);
4002 needpulla = pushRegIfSurv (m6502_reg_a);
4003 asmopToBool (AOP (left), true);
4005 emit6502op ("eor", "#0x01");
4006 storeRegToFullAop (m6502_reg_a, AOP (result), false);
4007 pullOrFreeReg (m6502_reg_a, needpulla);
4009 freeAsmop (result, NULL);
4010 freeAsmop (left, NULL);
4014 /**************************************************************************
4015 * genCpl - generate code for complement
4016 *************************************************************************/
4017 static void
4018 genCpl (iCode * ic)
4020 operand *left = IC_LEFT (ic);
4021 operand *result = IC_RESULT (ic);
4023 int offset = 0;
4024 int size;
4025 bool needpullreg;
4027 emitComment (TRACEGEN, __func__);
4028 printIC(ic);
4030 /* assign asmOps to operand & result */
4031 aopOp (left, ic);
4032 aopOp (result, ic);
4033 size = AOP_SIZE (result);
4035 emitComment (TRACEGEN|VVDBG, " %s - regmask %02x -> %02x",
4036 __func__, AOP(left)->regmask, AOP(result)->regmask );
4038 if (size==2 && AOP_TYPE (result) == AOP_REG && AOP_TYPE (left) == AOP_REG)
4040 if(IS_AOP_XA(AOP(left)) && IS_AOP_XA(AOP(result)))
4042 pushReg(m6502_reg_a, true);
4043 transferRegReg(m6502_reg_x, m6502_reg_a, true);
4044 rmwWithReg ("com", m6502_reg_a);
4045 transferRegReg(m6502_reg_a, m6502_reg_x, true);
4046 pullReg(m6502_reg_a);
4047 rmwWithReg ("com", m6502_reg_a);
4049 else if(IS_AOP_YX(AOP(left)) && IS_AOP_YX(AOP(result)))
4051 bool pa = pushRegIfSurv(m6502_reg_a);
4052 transferRegReg(m6502_reg_y, m6502_reg_a, true);
4053 rmwWithReg ("com", m6502_reg_a);
4054 transferRegReg(m6502_reg_a, m6502_reg_y, true);
4055 transferRegReg(m6502_reg_x, m6502_reg_a, true);
4056 rmwWithReg ("com", m6502_reg_a);
4057 transferRegReg(m6502_reg_a, m6502_reg_x, true);
4058 pullOrFreeReg(m6502_reg_a, pa);
4060 else if(IS_AOP_YX(AOP(left)) && IS_AOP_XA(AOP(result)))
4062 transferRegReg(m6502_reg_x, m6502_reg_a, true);
4063 rmwWithReg ("com", m6502_reg_a);
4064 pushReg(m6502_reg_a, true);
4065 transferRegReg(m6502_reg_y, m6502_reg_a, true);
4066 rmwWithReg ("com", m6502_reg_a);
4067 transferRegReg(m6502_reg_a, m6502_reg_x, true);
4068 pullReg(m6502_reg_a);
4070 else if(IS_AOP_XA(AOP(left)) && IS_AOP_YX(AOP(result)))
4072 pushReg(m6502_reg_a, true);
4073 transferRegReg(m6502_reg_x, m6502_reg_a, true);
4074 rmwWithReg ("com", m6502_reg_a);
4075 transferRegReg(m6502_reg_a, m6502_reg_y, true);
4076 pullReg(m6502_reg_a);
4077 rmwWithReg ("com", m6502_reg_a);
4078 transferRegReg(m6502_reg_a, m6502_reg_x, true);
4080 else
4082 m6502_unimplemented("unknown register pair in genCpl");
4085 goto release;
4088 if (AOP_TYPE (left) == AOP_REG && size==2)
4090 rmwWithReg ("com", m6502_reg_a);
4091 storeRegToAop (m6502_reg_a, AOP (result), 0);
4092 loadRegFromAop (m6502_reg_a, AOP (left), 1);
4093 rmwWithReg ("com", m6502_reg_a);
4094 storeRegToAop (m6502_reg_a, AOP (result), 1);
4095 goto release;
4098 needpullreg = pushRegIfSurv (m6502_reg_a);
4099 for(offset=size-1; offset>=0; offset--)
4101 emitComment (TRACEGEN|VVDBG, " %s: general case", __func__);
4102 loadRegFromAop (m6502_reg_a, AOP (left), offset);
4103 rmwWithReg ("com", m6502_reg_a);
4104 storeRegToAop (m6502_reg_a, AOP (result), offset);
4106 pullOrFreeReg (m6502_reg_a, needpullreg);
4108 /* release the aops */
4109 release:
4110 freeAsmop (result, NULL);
4111 freeAsmop (left, NULL);
4114 /**************************************************************************
4115 * genUminusFloat - unary minus for floating points
4116 *************************************************************************/
4117 static void
4118 genUminusFloat (operand * op, operand * result)
4120 int size, offset = 0;
4121 bool needpula;
4123 emitComment (TRACEGEN, __func__);
4125 /* for this we just copy and then flip the bit */
4127 size = AOP_SIZE (op) - 1;
4129 while (size--)
4131 transferAopAop (AOP (op), offset, AOP (result), offset);
4132 offset++;
4135 needpula = pushRegIfSurv (m6502_reg_a);
4136 loadRegFromAop (m6502_reg_a, AOP (op), offset);
4137 emit6502op ("eor", "#0x80");
4138 m6502_useReg (m6502_reg_a);
4139 storeRegToAop (m6502_reg_a, AOP (result), offset);
4140 pullOrFreeReg (m6502_reg_a, needpula);
4143 /**************************************************************************
4144 * genUminus - unary minus code generation
4145 *************************************************************************/
4146 static void genUminus (iCode * ic)
4148 operand *left = IC_LEFT (ic);
4149 operand *result = IC_RESULT (ic);
4151 int offset, size;
4152 sym_link *optype;
4153 bool carry = true;
4154 bool needpula;
4156 emitComment (TRACEGEN, __func__);
4157 printIC(ic);
4159 /* assign asmops */
4160 aopOp (left, ic);
4161 aopOp (result, ic);
4163 optype = operandType (left);
4165 sym_link *resulttype = operandType (IC_RESULT (ic));
4166 unsigned topbytemask = (IS_BITINT (resulttype) && SPEC_USIGN (resulttype) && (SPEC_BITINTWIDTH (resulttype) % 8)) ?
4167 (0xff >> (8 - SPEC_BITINTWIDTH (resulttype) % 8)) : 0xff;
4168 bool maskedtopbyte = (topbytemask != 0xff);
4170 /* if float then do float stuff */
4171 if (IS_FLOAT (optype))
4173 genUminusFloat (left, result);
4174 goto release;
4177 /* otherwise subtract from zero */
4178 size = AOP_SIZE (left);
4179 offset = 0;
4181 if (size == 1) {
4182 needpula = pushRegIfSurv (m6502_reg_a);
4183 loadRegFromAop (m6502_reg_a, AOP (left), 0);
4184 rmwWithReg ("neg", m6502_reg_a);
4185 if (maskedtopbyte)
4186 emit6502op ("and", IMMDFMT, topbytemask);
4187 m6502_freeReg (m6502_reg_a);
4188 storeRegToFullAop (m6502_reg_a, AOP (result), SPEC_USIGN (operandType (left)));
4189 pullOrFreeReg (m6502_reg_a, needpula);
4190 goto release;
4193 /* If either left or result are in registers, handle this carefully to */
4194 /* avoid prematurely overwriting register values. The 1 byte case was */
4195 /* handled above and there aren't enough registers to handle 4 byte values */
4196 /* so this case only needs to deal with 2 byte values. */
4197 if (AOP_TYPE (result) == AOP_REG || AOP_TYPE (left) == AOP_REG)
4199 reg_info *result0 = NULL;
4200 reg_info *left0 = NULL;
4201 reg_info *left1 = NULL;
4202 if (AOP_TYPE (left) == AOP_REG)
4204 left0 = AOP (left)->aopu.aop_reg[0];
4205 left1 = AOP (left)->aopu.aop_reg[1];
4207 if (AOP_TYPE (result) == AOP_REG)
4209 result0 = AOP (result)->aopu.aop_reg[0];
4211 needpula = pushRegIfSurv (m6502_reg_a);
4212 if (left1 == m6502_reg_a)
4213 pushReg (left1, true);
4215 if (left0 == m6502_reg_a) // TODO?
4216 rmwWithReg ("neg", m6502_reg_a);
4217 else {
4218 loadRegFromConst (m6502_reg_a, 0);
4219 emitSetCarry(1);
4220 accopWithAop ("sbc", AOP (left), 0);
4222 if (result0 == m6502_reg_a || (result0 && result0 == left1))
4223 pushReg (m6502_reg_a, true);
4224 else
4225 storeRegToAop (m6502_reg_a, AOP (result), 0);
4226 loadRegFromConst (m6502_reg_a, 0);
4227 if (left1 == m6502_reg_a) {
4228 // FIXME: unimplemented
4229 m6502_unimplemented("genUniminus with left1=A");
4230 m6502_dirtyReg (m6502_reg_a);
4231 } else {
4232 accopWithAop ("sbc", AOP (left), 1);
4234 storeRegToAop (m6502_reg_a, AOP (result), 1);
4235 if (result0 == m6502_reg_a || (result0 && result0 == left1))
4236 pullReg (result0);
4237 if (left1 == m6502_reg_a)
4238 pullNull (1);
4239 pullOrFreeReg (m6502_reg_a, needpula);
4240 goto release;
4243 needpula = pushRegIfSurv (m6502_reg_a);
4244 while (size--) {
4245 loadRegFromConst (m6502_reg_a, 0);
4246 if (carry) {
4247 emitSetCarry(1);
4249 accopWithAop ("sbc", AOP (left), offset);
4250 storeRegToAop (m6502_reg_a, AOP(result), offset++);
4251 carry = false;
4253 // storeRegSignToUpperAop (m6502_reg_a, AOP(result), offset, SPEC_USIGN (operandType (left)));
4254 pullOrFreeReg (m6502_reg_a, needpula);
4256 release:
4257 /* release the aops */
4258 freeAsmop (result, NULL);
4259 freeAsmop (left, NULL);
4262 /**************************************************************************
4263 * saveRegisters - will look for a call and save the registers
4264 *************************************************************************/
4265 static void saveRegisters (iCode *lic)
4267 int i;
4268 iCode *ic;
4270 /* look for call */
4271 for (ic = lic; ic; ic = ic->next)
4272 if (ic->op == CALL || ic->op == PCALL)
4273 break;
4275 if (!ic) {
4276 fprintf (stderr, "found parameter push with no function call\n");
4277 return;
4280 /* if the registers have been saved already or don't need to be then
4281 do nothing */
4282 if (ic->regsSaved)
4283 return;
4284 if (IS_SYMOP (IC_LEFT (ic)) &&
4285 (IFFUNC_CALLEESAVES (OP_SYMBOL (IC_LEFT (ic))->type) || IFFUNC_ISNAKED (OP_SYM_TYPE (IC_LEFT (ic)))))
4286 return;
4288 if (!regalloc_dry_run)
4289 ic->regsSaved = 1;
4291 emitComment (REGOPS, " saveRegisters");
4293 // make sure not to clobber A
4294 // TODO: why does isUsed not set?
4295 // TODO: only clobbered if m6502_reg_a->isFree
4296 // TODO: 65C02
4297 bool clobbers_a = !IS_MOS65C02
4298 && (bitVectBitValue(ic->rSurv, X_IDX) || bitVectBitValue(ic->rSurv, Y_IDX))
4299 && !bitVectBitValue(ic->rSurv, A_IDX);
4300 if (clobbers_a)
4301 storeRegTemp (m6502_reg_a, true);
4302 for (i = A_IDX; i <= Y_IDX; i++) {
4303 if (bitVectBitValue (ic->rSurv, i))
4304 pushReg (m6502_regWithIdx (i), false);
4306 if (clobbers_a)
4307 loadRegTemp (m6502_reg_a);
4310 /**************************************************************************
4311 * unsaveRegisters - pop the pushed registers
4312 *************************************************************************/
4313 static void unsaveRegisters (iCode *ic)
4315 int i;
4317 emitComment (REGOPS, "; unsaveRegisters");
4319 // TODO: only clobbered if m6502_reg_a->isFree
4320 // TODO: 65C02
4321 bool clobbers_a = !IS_MOS65C02
4322 && (bitVectBitValue(ic->rSurv, X_IDX) || bitVectBitValue(ic->rSurv, Y_IDX))
4323 && !bitVectBitValue(ic->rSurv, A_IDX);
4324 if (clobbers_a)
4325 storeRegTemp (m6502_reg_a, true);
4326 for (i = Y_IDX; i >= A_IDX; i--) {
4327 if (bitVectBitValue (ic->rSurv, i))
4328 pullReg (m6502_regWithIdx (i));
4330 if (clobbers_a)
4331 loadRegTemp (m6502_reg_a);
4334 /**************************************************************************
4335 * pushSide
4336 *************************************************************************/
4337 static void pushSide (operand *oper, int size, iCode *ic)
4339 int offset = 0;
4340 // bool xIsFree = m6502_reg_x->isFree;
4342 aopOp (oper, ic);
4344 if (AOP_TYPE (oper) == AOP_REG) {
4345 /* The operand is in registers; we can push them directly */
4346 storeRegTemp(AOP (oper)->aopu.aop_reg[0], true);
4347 storeRegTemp(AOP (oper)->aopu.aop_reg[1], true);
4348 } else {
4349 // push A if not free
4350 // TODO: consider other regs for 65C02
4351 bool needloada = pushRegIfUsed(m6502_reg_a);
4352 bool needloadx = false;
4353 if(AOP_TYPE(oper)==AOP_SOF) needloadx=pushRegIfUsed(m6502_reg_x);
4354 /* A is free, so piecewise load operand into a and push A */
4355 for (offset=0; offset<size; offset++) {
4356 loadRegFromAop (m6502_reg_a, AOP (oper), offset);
4357 storeRegTemp (m6502_reg_a, true);
4359 pullOrFreeReg(m6502_reg_x, needloadx);
4360 pullOrFreeReg(m6502_reg_a, needloada);
4363 freeAsmop (oper, NULL);
4364 // if (xIsFree)
4365 // m6502_freeReg (m6502_reg_x);
4368 /**************************************************************************
4369 * assignResultValue
4370 *************************************************************************/
4371 static void
4372 assignResultValue (operand * oper)
4374 int size = AOP_SIZE (oper);
4375 int offset = 0;
4377 emitComment (TRACEGEN, __func__);
4379 if(size>8)
4381 emitcode("ERROR","assignresultvalue return struct size: %d\n",size);
4382 return;
4385 if(AOP_TYPE (oper) == AOP_REG)
4387 if(size==1)
4389 transferRegReg(m6502_reg_a, (AOP(oper))->aopu.aop_reg[0], true);
4391 else
4393 if(IS_AOP_YX(AOP(oper)))
4394 transferRegReg(m6502_reg_xa, m6502_reg_yx, true);
4396 return;
4399 while (size--)
4401 transferAopAop (m6502_aop_pass[offset], 0, AOP (oper), offset);
4402 if (m6502_aop_pass[offset]->type == AOP_REG)
4403 m6502_freeReg (m6502_aop_pass[offset]->aopu.aop_reg[0]);
4404 offset++;
4408 /**************************************************************************
4409 * genIpush - generate code for pushing this gets a little complex
4410 *************************************************************************/
4411 static void genIpush (iCode * ic)
4413 operand *left = IC_LEFT (ic);
4415 int size, offset = 0;
4417 emitComment (TRACEGEN, __func__);
4419 /* if this is not a parm push : ie. it is spill push
4420 and spill push is always done on the local stack */
4421 if (!ic->parmPush) {
4422 /* and the item is spilt then do nothing */
4423 if (OP_SYMBOL (left)->isspilt)
4424 return;
4426 aopOp (left, ic);
4427 size = AOP_SIZE (left);
4428 /* push it on the stack */
4429 for (offset=size-1; offset>=0; offset--) {
4430 loadRegFromAop (m6502_reg_a, AOP (left), offset);
4431 pushReg (m6502_reg_a, true);
4433 return;
4436 /* this is a parameter push: in this case we call
4437 the routine to find the call and save those
4438 registers that need to be saved */
4439 if (!regalloc_dry_run) /* Cost for saving registers is counted at CALL or PCALL */
4440 saveRegisters (ic);
4442 /* then do the push */
4443 aopOp (left, ic);
4445 // pushSide(IC_LEFT(ic), AOP_SIZE(IC_LEFT(ic)));
4446 size = AOP_SIZE (left);
4448 // l = aopGet (AOP (left), 0, false, true);
4449 if (AOP_TYPE (left) == AOP_IMMD || AOP_TYPE (left) == AOP_LIT ||IS_AOP_YX (AOP (left))) {
4450 if ((size == 2) && m6502_reg_yx->isDead || IS_AOP_YX (AOP (left))) {
4451 loadRegFromAop (m6502_reg_yx, AOP (left), 0);
4452 pushReg (m6502_reg_yx, true);
4453 goto release;
4457 if (AOP_TYPE (left) == AOP_REG) {
4458 for (offset=size-1; offset>=0; offset--)
4459 pushReg (AOP (left)->aopu.aop_reg[offset], true);
4460 goto release;
4463 for (offset=size-1; offset>=0; offset--) {
4464 // printf("loading %d\n", offset);
4465 loadRegFromAop (m6502_reg_a, AOP (left), offset);
4466 // printf("pushing \n");
4467 pushReg (m6502_reg_a, true);
4470 release:
4471 freeAsmop (left, NULL);
4474 /**************************************************************************
4475 * genPointerPush - generate code for pushing
4476 *************************************************************************/
4477 static void genPointerPush (iCode *ic)
4479 operand *left = IC_LEFT (ic);
4480 int yoff;
4482 emitComment (TRACEGEN, __func__);
4484 aopOp (left, ic);
4486 wassertl (IC_RIGHT (ic), "IPUSH_VALUE_AT_ADDRESS without right operand");
4487 wassertl (IS_OP_LITERAL (IC_RIGHT (ic)), "IPUSH_VALUE_AT_ADDRESS with non-literal right operand");
4488 wassertl (!operandLitValue (IC_RIGHT(ic)), "IPUSH_VALUE_AT_ADDRESS with non-zero right operand");
4490 yoff = setupDPTR(left, 0, NULL, false);
4493 int size = getSize (operandType (left)->next);
4494 while (size--) {
4495 loadRegFromConst(m6502_reg_y, yoff+size);
4496 emit6502op("lda", INDFMT_IY);
4497 pushReg (m6502_reg_a, true);
4500 freeAsmop (left, NULL);
4504 /**************************************************************************
4505 * genSend - gen code for SEND
4506 *************************************************************************/
4507 static void
4508 genSend (set *sendSet)
4510 iCode *send1;
4511 iCode *send2;
4513 emitComment (TRACEGEN, __func__);
4515 /* case 1: single parameter in A
4516 * case 2: single parameter in XA
4517 * case 3: first parameter in A, second parameter in X
4519 send1 = setFirstItem (sendSet);
4520 send2 = setNextItem (sendSet);
4522 if (!send2)
4524 int size;
4525 /* case 1 or 2, this is fairly easy */
4526 aopOp (IC_LEFT (send1), send1);
4527 size = AOP_SIZE (IC_LEFT (send1));
4528 wassert (size <= 2);
4529 if (size == 1)
4531 loadRegFromAop (send1->argreg == 2 ? m6502_reg_x : m6502_reg_a, AOP (IC_LEFT (send1)), 0);
4533 else if (AOP (IC_LEFT (send1))->type == AOP_REG)
4535 loadRegFromAop (m6502_reg_xa, AOP (IC_LEFT (send1)), 0);
4537 else if (isOperandVolatile (IC_LEFT (send1), false))
4539 /* use lsb to msb order for volatile operands */
4540 loadRegFromAop (m6502_reg_a, AOP (IC_LEFT (send1)), 0);
4541 loadRegFromAop (m6502_reg_x, AOP (IC_LEFT (send1)), 1);
4543 else
4545 /* otherwise perfer to load a last */
4546 loadRegFromAop (m6502_reg_x, AOP (IC_LEFT (send1)), 1);
4547 loadRegFromAop (m6502_reg_a, AOP (IC_LEFT (send1)), 0);
4550 freeAsmop (IC_LEFT (send1), NULL);
4552 else
4554 /* case 3 */
4555 /* make sure send1 is the first argument and swap with send2 if not */
4556 if (send1->argreg > send2->argreg)
4558 iCode * sic = send1;
4559 send1 = send2;
4560 send2 = sic;
4562 aopOp (IC_LEFT (send1), send1);
4563 aopOp (IC_LEFT (send2), send2);
4564 if (IS_AOP_A (AOP (IC_LEFT (send2))))
4566 loadRegFromAop (m6502_reg_x, AOP (IC_LEFT (send2)), 0);
4567 loadRegFromAop (m6502_reg_a, AOP (IC_LEFT (send1)), 0);
4569 else
4571 loadRegFromAop (m6502_reg_a, AOP (IC_LEFT (send1)), 0);
4572 loadRegFromAop (m6502_reg_x, AOP (IC_LEFT (send2)), 0);
4574 freeAsmop (IC_LEFT (send2), NULL);
4575 freeAsmop (IC_LEFT (send1), NULL);
4579 /**************************************************************************
4580 * genCall - generates a call statement
4581 *************************************************************************/
4582 static void
4583 genCall (iCode * ic)
4585 operand *left = IC_LEFT (ic);
4586 operand *result = IC_RESULT (ic);
4588 sym_link *dtype;
4589 sym_link *etype;
4590 // bool restoreBank = false;
4591 // bool swapBanks = false;
4593 emitComment (TRACEGEN, __func__);
4594 printIC(ic);
4596 /* if caller saves & we have not saved then */
4597 if (!ic->regsSaved)
4598 saveRegisters (ic);
4600 dtype = operandType (left);
4601 etype = getSpec (dtype);
4602 /* if send set is not empty then assign */
4603 if (_G.sendSet && !regalloc_dry_run) {
4604 if (IFFUNC_ISREENT (dtype)) {
4605 /* need to reverse the send set */
4606 //genSend (_G.sendSet);
4607 genSend (reverseSet (_G.sendSet));
4608 } else {
4609 genSend (_G.sendSet);
4611 _G.sendSet = NULL;
4614 /* make the call */
4615 if (IS_LITERAL (etype)) {
4616 emit6502op ("jsr", "0x%04X", ulFromVal (OP_VALUE (left)));
4617 } else {
4618 bool jump = (!ic->parmBytes && IFFUNC_ISNORETURN (OP_SYMBOL (left)->type));
4620 emit6502op (jump ? "jmp" : "jsr", "%s", (OP_SYMBOL (left)->rname[0] ?
4621 OP_SYMBOL (left)->rname : OP_SYMBOL (left)->name));
4624 m6502_dirtyReg (m6502_reg_a);
4625 m6502_dirtyReg (m6502_reg_x);
4626 m6502_dirtyReg (m6502_reg_y);
4627 _G.lastflag=-1;
4628 _G.carryValid=0;
4631 /* do we need to recompute the base ptr? */
4632 if (_G.funcHasBasePtr) {
4633 saveBasePtr();
4636 /* if we need assign a result value */
4637 if ((IS_ITEMP (result) &&
4638 (OP_SYMBOL (result)->nRegs || OP_SYMBOL (result)->spildir)) || IS_TRUE_SYMOP (result)) {
4639 m6502_useReg (m6502_reg_a);
4640 if (operandSize (result) > 1)
4641 m6502_useReg (m6502_reg_x);
4642 aopOp (result, ic);
4644 assignResultValue (result);
4646 freeAsmop (result, NULL);
4649 /* adjust the stack for parameters if required */
4650 if (ic->parmBytes) {
4651 pullNull (ic->parmBytes);
4654 /* if we had saved some registers then unsave them */
4655 if (ic->regsSaved && !IFFUNC_CALLEESAVES (dtype))
4656 unsaveRegisters (ic);
4659 /**************************************************************************
4660 * genPcall - generates a call by pointer statement
4661 *************************************************************************/
4662 static void
4663 genPcall (iCode * ic)
4665 operand *left = IC_LEFT (ic);
4666 operand *result = IC_RESULT (ic);
4668 sym_link *dtype;
4669 sym_link *etype;
4670 iCode * sendic;
4672 emitComment (TRACEGEN, __func__);
4673 printIC(ic);
4675 dtype = operandType (left)->next;
4676 etype = getSpec (dtype);
4677 /* if caller saves & we have not saved then */
4678 if (!ic->regsSaved)
4679 saveRegisters (ic);
4681 /* Go through the send set and mark any registers used by iTemps as */
4682 /* in use so we don't clobber them while setting up the return address */
4683 for (sendic = setFirstItem (_G.sendSet); sendic; sendic = setNextItem (_G.sendSet)) {
4684 updateiTempRegisterUse (IC_LEFT (sendic));
4687 // TODO: handle DIR/EXT with jmp [aa] or jmp [aaaa]
4689 if (!IS_LITERAL (etype)) {
4690 updateCFA ();
4691 /* compute the function address */
4692 pushSide (left, FARPTRSIZE, ic); // -1 is baked into initialization
4695 /* if send set is not empty then assign */
4696 if (_G.sendSet && !regalloc_dry_run) {
4697 genSend (reverseSet (_G.sendSet));
4698 _G.sendSet = NULL;
4701 /* make the call */
4702 if (!IS_LITERAL (etype)) {
4703 #if 1
4704 emit6502op("jsr","__sdcc_indirect_jsr");
4705 #else
4706 symbol *rlbl = safeNewiTempLabel (NULL);
4707 symbol *tlbl = safeNewiTempLabel (NULL);
4709 emitBranch ("jsr", tlbl);
4710 emitBranch ("bra", rlbl);
4711 safeEmitLabel (tlbl);
4712 emit6502op("jmp", "[REGTEMP]");
4713 safeEmitLabel (rlbl);
4714 #endif
4715 // _G.tempOfs -= 2;
4716 loadRegTemp (NULL);
4717 loadRegTemp (NULL);
4718 updateCFA ();
4719 } else {
4720 emit6502op ("jsr", "0x%04X", ulFromVal (OP_VALUE (left)));
4723 m6502_dirtyReg (m6502_reg_a);
4724 m6502_dirtyReg (m6502_reg_x);
4725 m6502_dirtyReg (m6502_reg_y);
4726 _G.lastflag=-1;
4727 _G.carryValid=0;
4729 /* do we need to recompute the base ptr? */
4730 if (_G.funcHasBasePtr) {
4731 saveBasePtr();
4734 /* if we need assign a result value */
4735 if ((IS_ITEMP (result) &&
4736 (OP_SYMBOL (result)->nRegs || OP_SYMBOL (result)->spildir)) || IS_TRUE_SYMOP (result)) {
4737 m6502_useReg (m6502_reg_a);
4738 if (operandSize (result) > 1)
4739 m6502_useReg (m6502_reg_x);
4740 aopOp (result, ic);
4742 assignResultValue (result);
4744 freeAsmop (result, NULL);
4747 /* adjust the stack for parameters if required */
4748 if (ic->parmBytes) {
4749 pullNull (ic->parmBytes);
4752 /* if we had saved some registers then unsave them */
4753 if (ic->regsSaved && !IFFUNC_CALLEESAVES (dtype))
4754 unsaveRegisters (ic);
4757 /**************************************************************************
4758 * resultRemat - result is rematerializable
4759 *************************************************************************/
4760 static int resultRemat (iCode * ic)
4762 operand *result = IC_RESULT (ic);
4764 if (SKIP_IC (ic) || ic->op == IFX)
4765 return 0;
4767 if (result && IS_ITEMP (result)) {
4768 symbol *sym = OP_SYMBOL (result);
4769 if (sym->remat && !POINTER_SET (ic))
4770 return 1;
4773 return 0;
4776 /**************************************************************************
4777 * inExcludeList - return 1 if the string is in exclude Reg list
4778 *************************************************************************/
4779 static int regsCmp (void *p1, void *p2)
4781 return (STRCASECMP ((char *) p1, (char *) (p2)) == 0);
4784 static bool inExcludeList (char *s)
4786 const char *p = setFirstItem (options.excludeRegsSet);
4788 if (p == NULL || STRCASECMP (p, "none") == 0)
4789 return false;
4792 return isinSetWith (options.excludeRegsSet, s, regsCmp);
4795 /**************************************************************************
4796 * genFunction - generated code for function entry
4797 *************************************************************************/
4798 static void genFunction (iCode * ic)
4800 symbol *sym = OP_SYMBOL (IC_LEFT (ic));
4801 sym_link *ftype;
4802 iCode *ric = (ic->next && ic->next->op == RECEIVE) ? ic->next : NULL;
4803 int stackAdjust = sym->stack;
4804 // int accIsFree = sym->recvSize == 0;
4806 /* create the function header */
4807 emitComment (ALWAYS, "-----------------------------------------");
4808 emitComment (ALWAYS, " function %s", sym->name);
4809 emitComment (ALWAYS, "-----------------------------------------");
4810 emitComment (ALWAYS, m6502_assignment_optimal ? "Register assignment is optimal." : "Register assignment might be sub-optimal.");
4811 emitComment (ALWAYS, "Stack space usage: %d bytes.", sym->stack);
4813 emitcode ("", "%s:", sym->rname);
4814 genLine.lineCurr->isLabel = 1;
4815 ftype = operandType (IC_LEFT (ic));
4818 if(ric && ric->argreg)
4820 m6502_useReg(m6502_reg_a);
4821 m6502_reg_a->isDead=0;
4822 // FIXME: check if X is a parameter
4823 m6502_useReg(m6502_reg_x);
4824 m6502_reg_x->isDead=0;
4828 _G.stackOfs = 0;
4829 _G.stackPushes = 0;
4830 _G.tsxStackPushes = 0;
4831 _G.lastflag=-1;
4832 _G.carryValid=0;
4834 if (options.debug && !regalloc_dry_run)
4835 debugFile->writeFrameAddress (NULL, m6502_reg_sp, 0);
4837 if (IFFUNC_ISNAKED (ftype))
4839 emitComment (ALWAYS, "naked function: no prologue.");
4840 return;
4843 /* if this is an interrupt service routine then
4844 save a, x & y */
4845 if (IFFUNC_ISISR (sym->type))
4847 if (!inExcludeList ("a"))
4848 pushReg (m6502_reg_a, true);
4849 if (!inExcludeList ("x"))
4850 pushReg (m6502_reg_x, true);
4851 if (!inExcludeList ("y"))
4852 pushReg (m6502_reg_y, true);
4855 /* For some cases it is worthwhile to perform a RECEIVE iCode */
4856 /* before setting up the stack frame completely. */
4857 int numStackParams = 0;
4858 while (ric && ric->next && ric->next->op == RECEIVE)
4859 ric = ric->next;
4860 while (ric && IC_RESULT (ric))
4862 symbol *rsym = OP_SYMBOL (IC_RESULT (ric));
4863 int rsymSize = rsym ? getSize (rsym->type) : 0;
4865 if (rsym->isitmp)
4867 if (rsym && rsym->regType == REG_CND)
4868 rsym = NULL;
4869 if (rsym && (/*rsym->accuse ||*/ rsym->ruonly))
4870 rsym = NULL;
4871 if (rsym && (rsym->isspilt || rsym->nRegs == 0) && rsym->usl.spillLoc)
4872 rsym = rsym->usl.spillLoc;
4875 /* If the RECEIVE operand immediately spills to the first entry on the */
4876 /* stack, we can push it directly rather than use an sp relative store. */
4877 if (rsym && rsym->onStack && rsym->stack == -_G.stackPushes - rsymSize)
4879 int ofs;
4881 genLine.lineElement.ic = ric;
4882 emitComment (TRACEGEN, "genReceive: size=%d", rsymSize);
4883 // for (ofs = 0; ofs < rsymSize; ofs++)
4884 m6502_reg_a->isFree=false;
4885 for (ofs = rsymSize-1; ofs >=0; ofs--)
4887 reg_info *reg = m6502_aop_pass[ofs + (ric->argreg - 1)]->aopu.aop_reg[0];
4888 emitComment (TRACEGEN, "pushreg: ofs=%d", ofs);
4889 pushReg (reg, true);
4890 // if (reg->rIdx == A_IDX)
4891 // accIsFree = 1;
4892 stackAdjust--;
4894 genLine.lineElement.ic = ic;
4895 ric->generated = 1;
4897 ric = (ric->prev && ric->prev->op == RECEIVE) ? ric->prev : NULL;
4900 /* adjust the stack for the function */
4901 if (stackAdjust)
4902 adjustStack (-stackAdjust);
4904 _G.stackOfs = sym->stack;
4905 _G.stackPushes = 0;
4906 _G.funcHasBasePtr = 0;
4907 // TODO: how to see if needed? how to count params?
4908 if ( stackAdjust || sym->stack || numStackParams || IFFUNC_ISREENT(sym->type) )
4910 saveBasePtr();
4911 _G.funcHasBasePtr = 1;
4914 /* if critical function then turn interrupts off */
4915 if (IFFUNC_ISCRITICAL (ftype))
4917 emit6502op ("php", "");
4918 emit6502op ("sei", "");
4922 /**************************************************************************
4923 * genEndFunction - generates epilogue for functions
4924 *************************************************************************/
4925 static void
4926 genEndFunction (iCode * ic)
4928 symbol *sym = OP_SYMBOL (IC_LEFT (ic));
4930 emitComment (TRACEGEN, __func__);
4931 emitComment (REGOPS, " %s %s", __func__, regInfoStr() );
4933 if (IFFUNC_ISNAKED (sym->type))
4935 emitComment (ALWAYS, "naked function: no epilogue.");
4936 if (options.debug && currFunc && !regalloc_dry_run)
4937 debugFile->writeEndFunction (currFunc, ic, 0);
4938 return;
4941 if (IFFUNC_ISCRITICAL (sym->type))
4943 emit6502op ("plp", "");
4946 if (IFFUNC_ISREENT (sym->type) || options.stackAuto)
4950 if (_G.funcHasBasePtr)
4951 restoreBasePtr();
4953 if (_G.stackPushes)
4954 emitcode("ERROR","_G.stackPushes=%d in genEndFunction", _G.stackPushes);
4956 if (sym->stack)
4958 // if (sym->regsUsed && sym->regsUsed->size)
4959 m6502_useReg(m6502_reg_a);
4960 _G.stackPushes += sym->stack;
4961 adjustStack (sym->stack);
4964 if (IFFUNC_ISISR (sym->type))
4966 if (!inExcludeList ("y"))
4967 pullReg (m6502_reg_y);
4968 if (!inExcludeList ("x"))
4969 pullReg (m6502_reg_x);
4970 if (!inExcludeList ("a"))
4971 pullReg (m6502_reg_a);
4973 /* if debug then send end of function */
4974 if (options.debug && currFunc && !regalloc_dry_run)
4976 debugFile->writeEndFunction (currFunc, ic, 1);
4979 emit6502op ("rti", "");
4981 else
4983 if (IFFUNC_CALLEESAVES (sym->type))
4985 int i;
4987 /* if any registers used */
4988 if (sym->regsUsed)
4990 /* save the registers used */
4991 for (i = sym->regsUsed->size; i >= 0; i--)
4993 if (bitVectBitValue (sym->regsUsed, i) || (m6502_ptrRegReq && (i == YX_IDX || i == YX_IDX)))
4995 // FIXME
4996 emitcode ("pop", "%s", m6502_regWithIdx (i)->name); /* Todo: Cost. Can't find this instruction in manual! */
5002 /* if debug then send end of function */
5003 if (options.debug && currFunc && !regalloc_dry_run)
5005 debugFile->writeEndFunction (currFunc, ic, 1);
5008 emit6502op ("rts", "");
5012 /**************************************************************************
5013 * genRet - generate code for return statement
5014 *************************************************************************/
5015 static void genRet (iCode * ic)
5017 operand *left = IC_LEFT (ic);
5019 int size, offset = 0;
5020 // int pushed = 0;
5021 bool delayed_x = false;
5023 emitComment (TRACEGEN, __func__);
5024 emitComment (REGOPS, " %s %s", __func__, regInfoStr() );
5027 /* if we have no return value then
5028 just generate the "ret" */
5029 if (!left)
5030 goto jumpret;
5032 /* we have something to return then
5033 move the return value into place */
5034 aopOp (left, ic);
5035 size = AOP_SIZE (left);
5036 const bool bigreturn = IS_STRUCT (operandType (left));
5038 if (bigreturn) {
5039 // FIXME: only up to size 8 is supported
5040 if(size>8)
5042 if (!regalloc_dry_run)
5043 werror ( E_FUNC_AGGR);
5044 goto jumpret;
5047 while (size--) {
5048 transferAopAop (AOP (left), size, m6502_aop_pass[size], 0);
5049 // offset--;
5051 // emitcode("ERROR","*** end return");
5053 goto jumpret;
5056 if (AOP_TYPE (left) == AOP_LIT) {
5057 /* If returning a literal, we can load the bytes of the return value */
5058 /* in any order. By loading A and X first, any other bytes that match */
5059 /* can use the shorter sta and stx instructions. */
5060 offset = 0;
5061 while (size--) {
5062 transferAopAop (AOP (left), offset, m6502_aop_pass[offset], 0);
5063 offset++;
5065 } else {
5066 /* Take care when swapping a and x */
5067 if (AOP_TYPE (left) == AOP_REG && size > 1 && AOP (left)->aopu.aop_reg[0]->rIdx == X_IDX) {
5068 delayed_x = true;
5069 pushReg (m6502_reg_x, true);
5072 offset = size - 1;
5073 while (size--) {
5074 if (!(delayed_x && !offset))
5075 transferAopAop (AOP (left), offset, m6502_aop_pass[offset], 0);
5076 offset--;
5079 if (delayed_x)
5080 pullReg (m6502_reg_a);
5083 freeAsmop (left, NULL);
5085 jumpret:
5086 /* generate a jump to the return label
5087 if the next is not the return statement */
5088 if (!(ic->next && ic->next->op == LABEL && IC_LABEL (ic->next) == returnLabel)) {
5089 emit6502op ("jmp", "%05d$", safeLabelNum (returnLabel));
5093 /**************************************************************************
5094 * genLabel - generates a label
5095 *************************************************************************/
5096 static void genLabel (iCode * ic)
5098 int i;
5100 emitComment (TRACEGEN, __func__);
5101 emitComment (REGOPS, " %s %s", __func__, regInfoStr() );
5104 for(i=0;i<NUM_TEMP_REGS;i++)
5105 _G.tempAttr[i].isLiteral=0;
5107 _G.DPTRAttr[0].isLiteral=0;
5108 _G.DPTRAttr[1].isLiteral=0;
5110 _G.lastflag=-1;
5111 _G.carryValid=0;
5113 /* special case never generate */
5114 if (IC_LABEL (ic) == entryLabel)
5115 return;
5117 /* For the high level labels we cannot depend on any */
5118 /* register's contents. Amnesia time. */
5119 for (i = A_IDX; i < SP_IDX; i++) {
5120 m6502_dirtyReg (m6502_regWithIdx (i));
5121 m6502_useReg (m6502_regWithIdx (i));
5124 if (options.debug && !regalloc_dry_run)
5125 debugFile->writeLabel (IC_LABEL (ic), ic);
5127 safeEmitLabel (IC_LABEL (ic));
5130 /**************************************************************************
5131 * genGoto - generates a jmp
5132 *************************************************************************/
5133 static void genGoto (iCode * ic)
5135 emitComment (TRACEGEN, __func__);
5136 emit6502op ("jmp", "%05d$", safeLabelNum (IC_LABEL (ic)));
5140 /**************************************************************************
5141 * genPlusIncr :- does addition with increment if possible
5142 *************************************************************************/
5143 static bool genPlusIncr (iCode * ic)
5145 operand *right = IC_RIGHT (ic);
5146 operand *left = IC_LEFT (ic);
5147 operand *result = IC_RESULT (ic);
5149 int icount;
5150 bool needpula;
5151 unsigned int size = AOP_SIZE (result);
5152 unsigned int offset;
5153 symbol *tlbl = NULL;
5155 emitComment (TRACEGEN, __func__);
5157 /* will try to generate an increment */
5158 /* if the right side is not a literal
5159 we cannot */
5160 if (AOP_TYPE (right) != AOP_LIT)
5161 return false;
5163 icount = (int) ulFromVal (AOP (right)->aopu.aop_lit);
5165 emitComment (TRACEGEN|VVDBG, " %s: icount = %d, sameRegs=%d",
5166 __func__, icount, sameRegs (AOP (left), AOP (result)));
5168 if (icount > 255)
5169 return false;
5171 #if 1
5172 if(IS_AOP_XA (AOP (result)) && icount >=0 ) {
5173 loadRegFromAop (m6502_reg_xa, AOP (left), 0);
5174 if(icount) {
5175 tlbl = safeNewiTempLabel (NULL);
5176 emitSetCarry(0);
5177 accopWithAop ("adc", AOP (right), 0);
5178 emitBranch ("bcc", tlbl);
5179 emit6502op ("inx", "");
5180 safeEmitLabel (tlbl);
5181 m6502_dirtyReg(m6502_reg_x);
5183 return true;
5185 #endif
5187 if (!sameRegs (AOP (left), AOP (result)))
5188 return false;
5190 // TODO: can inc blah,x
5191 if (!aopCanIncDec (AOP (result)))
5192 return false;
5194 emitComment (TRACEGEN|VVDBG, " genPlusIncr");
5196 if (size==1 && AOP(result)->type==AOP_REG) {
5197 // if it's in a 8-bit register try to do small adjust
5198 if(smallAdjustReg(AOP(result)->aopu.aop_reg[0], icount)) return true;
5201 if(icount < 0 )
5202 return false;
5204 aopOpExtToIdx (AOP (result), AOP (left), NULL);
5206 if (size > 1)
5207 tlbl = safeNewiTempLabel (NULL);
5209 if (icount == 1) {
5210 needpula = false;
5211 rmwWithAop ("inc", AOP (result), 0);
5212 if (size > 1)
5213 emitBranch ("bne", tlbl);
5214 } else {
5215 if (!IS_AOP_A (AOP (result)) && !IS_AOP_XA (AOP (result)))
5216 needpula = pushRegIfUsed (m6502_reg_a);
5217 else
5218 needpula = false;
5219 loadRegFromAop (m6502_reg_a, AOP (result), 0);
5220 emitSetCarry(0);
5221 accopWithAop ("adc", AOP (right), 0);
5222 storeRegToAop (m6502_reg_a, AOP (result), 0);
5223 if (size > 1)
5224 emitBranch ("bcc", tlbl);
5226 for (offset = 1; offset < size; offset++) {
5227 rmwWithAop ("inc", AOP (result), offset);
5228 if(AOP(result)->type==AOP_REG) m6502_dirtyReg(AOP(result)->aopu.aop_reg[offset]);
5229 if ((offset + 1) < size)
5230 emitBranch ("bne", tlbl);
5233 if (size > 1)
5234 safeEmitLabel (tlbl);
5236 pullOrFreeReg (m6502_reg_a, needpula);
5238 return true;
5241 /**************************************************************************
5242 * genPlus - generates code for addition
5243 *************************************************************************/
5244 static void genPlus (iCode * ic)
5246 operand *right = IC_RIGHT (ic);
5247 operand *left = IC_LEFT (ic);
5248 operand *result = IC_RESULT (ic);
5250 int size, offset = 0;
5251 bool clc = true;
5252 bool needpulla;
5253 bool earlystore = false;
5254 bool delayedstore = false;
5255 bool mayskip = true;
5256 bool skip = false;
5257 sym_link *resulttype = operandType (IC_RESULT (ic));
5258 unsigned topbytemask = (IS_BITINT (resulttype) && SPEC_USIGN (resulttype) && (SPEC_BITINTWIDTH (resulttype) % 8)) ?
5259 (0xff >> (8 - SPEC_BITINTWIDTH (resulttype) % 8)) : 0xff;
5260 bool maskedtopbyte = (topbytemask != 0xff);
5262 /* special cases :- */
5264 emitComment (TRACEGEN, __func__);
5266 aopOp (left, ic);
5267 aopOp (right, ic);
5268 aopOp (result, ic);
5270 printIC(ic);
5272 /* we want registers on the left and literals on the right */
5273 if ((AOP_TYPE (left) == AOP_LIT) || (AOP_TYPE (right) == AOP_REG && !IS_AOP_WITH_A (AOP (left))))
5275 operand *t = right;
5276 right = left;
5277 left = t;
5280 /* if I can do an increment instead
5281 of add then GOOD for ME */
5282 if (!maskedtopbyte && genPlusIncr (ic))
5283 goto release;
5285 emitComment (TRACEGEN|VVDBG, " %s - Can't Inc", __func__);
5288 aopOpExtToIdx (AOP (result), AOP (left), AOP (right));
5290 size = AOP_SIZE (result);
5292 offset = 0;
5294 // FIXME: should make this more general
5295 if ( size==2 && AOP_TYPE (right) == AOP_LIT
5296 && operandLitValue (right) >= 0
5297 && operandLitValue (right) <= 255
5298 && AOP_TYPE(result) != AOP_SOF )
5301 if (sameRegs(AOP(result),AOP(left)))
5303 symbol *skipInc = safeNewiTempLabel (NULL);
5304 needpulla = pushRegIfSurv (m6502_reg_a);
5305 emitSetCarry(0);
5306 loadRegFromAop (m6502_reg_a, AOP(left), 0);
5307 accopWithAop ("adc", AOP(right), 0);
5308 storeRegToAop (m6502_reg_a, AOP (result), 0);
5309 emitBranch ("bcc", skipInc);
5310 rmwWithAop ("inc", AOP(result), 1);
5311 if(IS_AOP_WITH_X(AOP(result)))
5312 m6502_dirtyReg(m6502_reg_x);
5313 if(IS_AOP_WITH_Y(AOP(result)))
5314 m6502_dirtyReg(m6502_reg_y);
5315 safeEmitLabel (skipInc);
5316 pullOrFreeReg (m6502_reg_a, needpulla);
5317 goto release;
5321 needpulla = pushRegIfSurv (m6502_reg_a);
5323 while (size--) {
5324 if (earlystore && offset == 1)
5325 pullReg (m6502_reg_a);
5326 else
5327 loadRegFromAop (m6502_reg_a, AOP(left), offset);
5328 if (clc)
5329 emitSetCarry(0);
5330 if (!mayskip || AOP_TYPE (right) != AOP_LIT || (byteOfVal (AOP (right)->aopu.aop_lit, offset) != 0x00) ) {
5331 accopWithAop ("adc", AOP(right), offset);
5332 if (!size && maskedtopbyte)
5333 emit6502op ("and", IMMDFMT, topbytemask);
5334 mayskip = false;
5335 skip = false;
5336 } else {
5337 skip = true;
5339 if (size && AOP_TYPE (result) == AOP_REG && AOP (result)->aopu.aop_reg[offset]->rIdx == A_IDX) {
5340 pushReg (m6502_reg_a, true);
5341 delayedstore = true;
5342 } else {
5343 storeRegToAop (m6502_reg_a, AOP (result), offset);
5345 offset++;
5346 m6502_freeReg (m6502_reg_a);
5347 if (!skip)
5348 clc = false; /* further adds must propagate carry */
5350 if (delayedstore)
5351 pullReg (m6502_reg_a);
5352 pullOrFreeReg (m6502_reg_a, needpulla);
5354 wassert (!earlystore || !delayedstore);
5356 release:
5357 freeAsmop (result, NULL);
5358 freeAsmop (right, NULL);
5359 freeAsmop (left, NULL);
5362 /**************************************************************************
5363 * genMinusDec :- does subtraction with decrement if possible
5364 *************************************************************************/
5365 static bool genMinusDec (iCode * ic)
5367 operand *right = IC_RIGHT (ic);
5368 operand *left = IC_LEFT (ic);
5369 operand *result = IC_RESULT (ic);
5371 int icount;
5372 unsigned int size = AOP_SIZE (result);
5373 // int offset;
5374 symbol *tlbl;
5376 emitComment (TRACEGEN, __func__);
5378 /* will try to generate an increment */
5379 /* if the right side is not a literal
5380 we cannot */
5381 if (AOP_TYPE (right) != AOP_LIT)
5382 return false;
5384 icount = (unsigned int) ulFromVal (AOP (right)->aopu.aop_lit);
5385 // TODO: genPlusIncr has a lot more, can merge?
5387 if(icount>255) return false;
5389 #if 1
5390 if(IS_AOP_XA (AOP (result)) && icount >=0 ) {
5391 loadRegFromAop (m6502_reg_xa, AOP (left), 0);
5392 if(icount) {
5393 tlbl = safeNewiTempLabel (NULL);
5394 emitSetCarry (1);
5395 accopWithAop ("sbc", AOP (right), 0);
5396 emitBranch ("bcs", tlbl);
5397 emit6502op ("dex", "");
5398 safeEmitLabel (tlbl);
5399 m6502_dirtyReg(m6502_reg_x);
5401 return true;
5403 #endif
5405 if (!sameRegs (AOP (left), AOP (result)))
5406 return false;
5408 if (size != 1)
5409 return false;
5411 // TODO: can inc blah,x
5412 if (!aopCanIncDec (AOP (result)))
5413 return false;
5415 // do dex/dey and inx/iny if icount is negative
5416 if (/*size==1 && */ AOP(result)->type==AOP_REG) {
5417 if(smallAdjustReg(AOP(result)->aopu.aop_reg[0],-icount))
5418 return true;
5421 if ((icount > 1) || (icount < 0))
5422 return false;
5424 emitComment (TRACEGEN|VVDBG, " genMinusDec");
5426 aopOpExtToIdx (AOP (result), AOP (left), NULL);
5428 rmwWithAop ("dec", AOP (result), 0);
5430 return true;
5433 /**************************************************************************
5434 * addSign - complete with sign
5435 *************************************************************************/
5436 static void addSign (operand * result, int offset, int sign)
5438 int size = (AOP_SIZE (result) - offset);
5439 if (size > 0) {
5440 if (sign) {
5441 signExtendA();
5442 while (size--)
5443 storeRegToAop (m6502_reg_a, AOP (result), offset++);
5444 } else
5445 while (size--)
5446 storeConstToAop (0, AOP (result), offset++);
5451 /**************************************************************************
5452 * genMinus - generates code for subtraction
5453 *************************************************************************/
5454 static void
5455 genMinus (iCode * ic)
5457 operand *right = IC_RIGHT (ic);
5458 operand *left = IC_LEFT (ic);
5459 operand *result = IC_RESULT (ic);
5461 bool carry = true;
5462 int size, offset = 0;
5463 bool needpulla = false;
5464 bool earlystore = false;
5465 bool delayedstore = false;
5467 sym_link *resulttype = operandType (IC_RESULT (ic));
5468 unsigned topbytemask = (IS_BITINT (resulttype) && SPEC_USIGN (resulttype) && (SPEC_BITINTWIDTH (resulttype) % 8)) ?
5469 (0xff >> (8 - SPEC_BITINTWIDTH (resulttype) % 8)) : 0xff;
5470 bool maskedtopbyte = (topbytemask != 0xff);
5472 asmop *leftOp, *rightOp;
5474 emitComment (TRACEGEN, __func__);
5476 aopOp (left, ic);
5477 aopOp (right, ic);
5478 aopOp (result, ic);
5480 printIC(ic);
5483 /* special cases :- */
5484 /* if I can do an decrement instead
5485 of subtract then GOOD for ME */
5486 if (!maskedtopbyte && genMinusDec (ic))
5487 goto release;
5489 emitComment (TRACEGEN|VVDBG, " %s - Can't Dec", __func__);
5490 aopOpExtToIdx (AOP (result), AOP (left), AOP (right));
5492 size = AOP_SIZE (result);
5495 leftOp = AOP (left);
5496 rightOp = AOP (right);
5497 offset = 0;
5499 if ( size==2 && AOP_TYPE (right) == AOP_LIT && !maskedtopbyte
5500 && operandLitValue (right) >= 0
5501 && operandLitValue (right) <= 255
5502 && sameRegs(AOP(result),AOP(left))
5503 && AOP_TYPE(result) != AOP_SOF ) {
5504 symbol *skipDec = safeNewiTempLabel (NULL);
5506 needpulla = pushRegIfSurv (m6502_reg_a);
5507 emitSetCarry(1);
5508 loadRegFromAop (m6502_reg_a, AOP(left), 0);
5509 accopWithAop ("sbc", AOP(right), 0);
5510 storeRegToAop (m6502_reg_a, AOP (result), 0);
5511 emitBranch ("bcs", skipDec);
5512 rmwWithAop ("dec", AOP(result), 1);
5513 if(IS_AOP_WITH_X(AOP(result)))
5514 m6502_dirtyReg(m6502_reg_x);
5515 if(IS_AOP_WITH_Y(AOP(result)))
5516 m6502_dirtyReg(m6502_reg_y);
5517 safeEmitLabel (skipDec);
5518 pullOrFreeReg (m6502_reg_a, needpulla);
5519 goto release;
5523 if (IS_AOP_A (rightOp)) {
5524 // op - a = neg(a - op) = not(a - op) + 1 = not(a - op - 1)
5525 needpulla = pushRegIfSurv (m6502_reg_a);
5526 emitSetCarry(0);
5527 accopWithAop ("sbc", leftOp, offset);
5528 emit6502op("eor", "#0xff");
5529 if (maskedtopbyte)
5530 emit6502op ("and", IMMDFMT, topbytemask);
5531 storeRegToAop (m6502_reg_a, AOP (result), offset);
5532 pullOrFreeReg (m6502_reg_a, needpulla);
5533 goto release;
5536 needpulla = storeRegTempIfSurv (m6502_reg_a);
5538 while (size--) {
5539 if (earlystore &&
5540 (AOP_TYPE (left) == AOP_REG && AOP (left)->aopu.aop_reg[offset]->rIdx == A_IDX ||
5541 AOP_TYPE (right) == AOP_REG && AOP (right)->aopu.aop_reg[offset]->rIdx == A_IDX))
5542 pullReg (m6502_reg_a);
5543 if (AOP_TYPE (right) == AOP_REG && AOP (right)->aopu.aop_reg[offset]->rIdx == A_IDX) {
5544 storeRegTemp (m6502_reg_a, true);
5545 loadRegFromAop (m6502_reg_a, leftOp, offset);
5546 if (carry) {
5547 emitSetCarry(1);
5549 emitRegTempOp("sbc", _G.tempOfs-1 );
5550 loadRegTemp (NULL);
5551 } else {
5552 emitComment (TRACEGEN|VVDBG, " - default path");
5554 loadRegFromAop (m6502_reg_a, leftOp, offset);
5555 if (carry) {
5556 emitSetCarry(1);
5558 accopWithAop ("sbc", rightOp, offset);
5560 if (!size && maskedtopbyte)
5561 emit6502op ("and", IMMDFMT, topbytemask);
5562 if (size && AOP_TYPE (result) == AOP_REG && AOP (result)->aopu.aop_reg[offset]->rIdx == A_IDX) {
5563 emitComment (TRACEGEN|VVDBG, " - push");
5564 pushReg (m6502_reg_a, true);
5565 delayedstore = true;
5566 } else {
5567 emitComment (TRACEGEN|VVDBG, " - store");
5568 storeRegToAop (m6502_reg_a, AOP (result), offset);
5570 offset++;
5571 carry = false;
5573 if(delayedstore)
5574 pullReg (m6502_reg_a);
5575 loadOrFreeRegTemp (m6502_reg_a, needpulla);
5577 wassert (!earlystore || !delayedstore);
5579 release:
5580 freeAsmop (left, NULL);
5581 freeAsmop (right, NULL);
5582 freeAsmop (result, NULL);
5586 /**************************************************************************
5587 * genMult - generates code for multiplication
5588 *************************************************************************/
5589 static void genMult (iCode * ic)
5591 /* Shouldn't occur - all done through function calls */
5592 wassertl (0, "Multiplication is handled through support function calls");
5595 /**************************************************************************
5596 * genDiv - generates code for division
5597 *************************************************************************/
5598 static void genDiv (iCode * ic)
5600 /* Shouldn't occur - all done through function calls */
5601 wassertl (0, "Division is handled through support function calls");
5604 /**************************************************************************
5605 * genMod - generates code for division
5606 *************************************************************************/
5607 static void genMod (iCode * ic)
5609 /* Shouldn't occur - all done through function calls */
5610 wassertl (0, "Division is handled through support function calls");
5613 /**************************************************************************
5614 * genIfxJump :- will create a jump depending on the ifx
5615 *************************************************************************/
5616 static void
5617 genIfxJump (iCode * ic, char *jval)
5619 symbol *jlbl;
5620 symbol *tlbl = safeNewiTempLabel (NULL);
5621 char *inst = "ERROR";
5623 emitComment (TRACEGEN, "%s : %s", __func__, jval);
5625 /* if true label then we jump if condition
5626 supplied is true */
5627 if (IC_TRUE (ic)) {
5628 jlbl = IC_TRUE (ic);
5629 if (!strcmp (jval, "z"))
5630 inst = "beq";
5631 else if (!strcmp (jval, "c"))
5632 inst = "bcc";
5633 else if (!strcmp (jval, "n"))
5634 inst = "bpl";
5635 else if (!strcmp (jval, "v"))
5636 inst = "bvc";
5637 else
5638 emitcode("ERROR", "; %s IC_TRUE: Unimplemented jval (%s)", __func__, jval );
5639 // inst = "bge";
5640 } else {
5641 /* false label is present */
5642 jlbl = IC_FALSE (ic);
5643 if (!strcmp (jval, "z"))
5644 inst = "bne";
5645 else if (!strcmp (jval, "c"))
5646 inst = "bcs";
5647 else if (!strcmp (jval, "n"))
5648 inst = "bmi";
5649 else if (!strcmp (jval, "v"))
5650 inst = "bvs";
5651 else
5652 emitcode("ERROR", "; %s IC_FALSE: Unimplemented jval (%s)", __func__, jval );
5653 // inst = "blt";
5655 emitBranch (inst, tlbl);
5656 emitBranch ("jmp", jlbl);
5657 safeEmitLabel (tlbl);
5659 /* mark the icode as generated */
5660 ic->generated = 1;
5664 /**************************************************************************
5665 * exchangedCmp : returns the opcode need if the two operands are
5666 * exchanged in a comparison
5667 *************************************************************************/
5668 static int exchangedCmp (int opcode)
5670 switch (opcode) {
5671 case '<':
5672 return '>';
5673 case '>':
5674 return '<';
5675 case LE_OP:
5676 return GE_OP;
5677 case GE_OP:
5678 return LE_OP;
5679 case NE_OP:
5680 return NE_OP;
5681 case EQ_OP:
5682 return EQ_OP;
5683 default:
5684 werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "opcode not a comparison");
5686 return EQ_OP; /* shouldn't happen, but need to return something */
5689 /**************************************************************************
5690 * negatedCmp : returns the equivalent opcode for when a comparison
5691 * if not true
5692 *************************************************************************/
5693 static int negatedCmp (int opcode)
5695 switch (opcode) {
5696 case '<':
5697 return GE_OP;
5698 case '>':
5699 return LE_OP;
5700 case LE_OP:
5701 return '>';
5702 case GE_OP:
5703 return '<';
5704 case NE_OP:
5705 return EQ_OP;
5706 case EQ_OP:
5707 return NE_OP;
5708 default:
5709 werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "opcode not a comparison");
5711 return EQ_OP; /* shouldn't happen, but need to return something */
5714 /**************************************************************************
5715 * nameCmp : helper function for human readable debug output
5716 *************************************************************************/
5717 static char * nameCmp (int opcode)
5719 switch (opcode) {
5720 case '<':
5721 return "<";
5722 case '>':
5723 return ">";
5724 case LE_OP:
5725 return "<=";
5726 case GE_OP:
5727 return ">=";
5728 case NE_OP:
5729 return "!=";
5730 case EQ_OP:
5731 return "==";
5732 default:
5733 return "invalid";
5737 /**************************************************************************
5738 * branchInstCmp : returns the conditional branch instruction that
5739 * will branch if the comparison is true
5740 *************************************************************************/
5741 static char * branchInstCmp (int opcode, int sign)
5743 switch (opcode) {
5744 case '<':
5745 if (sign)
5746 return "blt";
5747 else
5748 return "bcc";
5749 case '>':
5750 if (sign)
5751 return "bgt";
5752 else
5753 return "bhi";
5754 case LE_OP:
5755 if (sign)
5756 return "ble";
5757 else
5758 return "bls";
5759 case GE_OP:
5760 if (sign)
5761 return "bge";
5762 else
5763 return "bcs";
5764 case NE_OP:
5765 return "bne";
5766 case EQ_OP:
5767 return "beq";
5768 default:
5769 werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "opcode not a comparison");
5771 return "brn";
5775 /**************************************************************************
5776 * genCmp :- greater or less than (and maybe with equal) comparison
5777 *************************************************************************/
5778 static void
5779 genCmp (iCode * ic, iCode * ifx)
5781 operand *right = IC_RIGHT (ic);
5782 operand *left = IC_LEFT (ic);
5783 operand *result = IC_RESULT (ic);
5785 sym_link *letype, *retype;
5786 int sign, opcode;
5787 int size, offset = 0;
5788 unsigned long long lit = 0ull;
5789 char *sub;
5790 symbol *jlbl = NULL;
5791 bool needloada = false;
5792 bool bmi = false;
5793 bool bit = false;
5795 emitComment (TRACEGEN, __func__);
5797 opcode = ic->op;
5799 // TODO: optimize for cmp regs with 0 or constant
5801 sign = 0;
5802 // TODO: don't use signed when unsigned will do
5803 if (IS_SPEC (operandType (left)) && IS_SPEC (operandType (right)))
5805 letype = getSpec (operandType (left));
5806 retype = getSpec (operandType (right));
5807 sign = !(SPEC_USIGN (letype) | SPEC_USIGN (retype));
5810 /* assign the amsops */
5811 aopOp (left, ic);
5812 aopOp (right, ic);
5813 aopOp (result, ic);
5814 printIC(ic);
5816 /* need register operand on left, prefer literal operand on right */
5817 if ((AOP_TYPE (right) == AOP_REG) || AOP_TYPE (left) == AOP_LIT)
5819 operand *temp = left;
5820 left = right;
5821 right = temp;
5822 opcode = exchangedCmp (opcode);
5824 // TODO: special case for compare with 0
5826 if (ifx)
5828 if (IC_TRUE (ifx))
5830 jlbl = IC_TRUE (ifx);
5831 opcode = negatedCmp (opcode);
5833 else
5835 /* false label is present */
5836 jlbl = IC_FALSE (ifx);
5840 size = max (AOP_SIZE (left), AOP_SIZE (right));
5842 emitComment (TRACEGEN|VVDBG, " genCmp (%s, size %d, sign %d)", nameCmp (opcode), size, sign);
5844 if (sign && (AOP_TYPE (right) == AOP_LIT) && opcode=='<' && ullFromVal (AOP (right)->aopu.aop_lit) ==0 && canBitOp(left) )
5846 accopWithAop ("bit", AOP (left), size-1);
5847 bit=true;
5848 bmi=true;
5850 else if (sign && (AOP_TYPE (right) == AOP_LIT) && opcode==GE_OP && ullFromVal (AOP (right)->aopu.aop_lit) ==0 && canBitOp(left) )
5852 accopWithAop ("bit", AOP (left), size-1);
5853 bit=true;
5854 bmi=false;
5856 else if (!sign && size == 1 && IS_AOP_X (AOP (left)) && isAddrSafe(right, m6502_reg_x) )
5858 // if (AOP_TYPE (right) == AOP_LIT && ullFromVal (AOP (right)->aopu.aop_lit) == 0)
5859 // emitCpz(X_IDX);
5860 // else
5861 accopWithAop ("cpx", AOP (right), offset);
5863 else if (!sign && size == 1 && IS_AOP_Y (AOP (left)) && isAddrSafe(right, m6502_reg_y) )
5865 // if (AOP_TYPE (right) == AOP_LIT && ullFromVal (AOP (right)->aopu.aop_lit) == 0)
5866 // emitCpz(Y_IDX);
5867 // else
5868 accopWithAop ("cpy", AOP (right), offset);
5870 else if (!sign && size == 1 && IS_AOP_A (AOP (left)) && isAddrSafe(right, m6502_reg_a))
5872 if (AOP_TYPE (right) == AOP_LIT && ullFromVal (AOP (right)->aopu.aop_lit) == 0 && opcode=='>')
5874 // FIXME: this is unsafe in some corner cases as the branches need carry
5875 // even if it's always set.
5876 if(!emitCpz(A_IDX))
5877 emit6502op("sec","");
5879 else
5881 accopWithAop ("cmp", AOP (right), offset);
5884 else
5886 offset = 0;
5887 // need V flag for signed compare
5888 // FIXME: is this covered above?
5889 if (size == 1 && sign == 0)
5891 sub = "cmp";
5893 else
5895 sub = "sub";
5897 /* These conditions depend on the Z flag bit, but Z is */
5898 /* only valid for the last byte of the comparison, not */
5899 /* the whole value. So exchange the operands to get a */
5900 /* comparison that doesn't depend on Z. (This is safe */
5901 /* to do here since ralloc won't assign multi-byte */
5902 /* operands to registers for comparisons) */
5903 if ((opcode == '>') || (opcode == LE_OP))
5905 operand *temp = left;
5906 left = right;
5907 right = temp;
5908 opcode = exchangedCmp (opcode);
5911 if ((AOP_TYPE (right) == AOP_LIT) && !isOperandVolatile (left, false))
5913 lit = ullFromVal (AOP (right)->aopu.aop_lit);
5914 while ((size > 1) && (((lit >> (8 * offset)) & 0xff) == 0))
5916 offset++;
5917 size--;
5921 needloada = storeRegTempIfSurv (m6502_reg_a);
5922 while (size--)
5924 emitComment (TRACEGEN|VVDBG, " GenCmp - size counter = %d", size);
5926 if (AOP_TYPE (right) == AOP_REG && AOP(right)->aopu.aop_reg[offset]->rIdx == A_IDX)
5928 storeRegTemp(m6502_reg_a, true);
5929 loadRegFromAop (m6502_reg_a, AOP (left), offset);
5930 if (!strcmp(sub, "sub")) {
5931 emitSetCarry(1);
5932 emitRegTempOp("sbc", _G.tempOfs-1 );
5933 } else {
5934 emitRegTempOp(sub, _G.tempOfs-1 );
5936 loadRegTemp(NULL);
5938 else
5940 loadRegFromAop (m6502_reg_a, AOP (left), offset);
5941 if (!strcmp(sub, "sub")) {
5942 emitSetCarry(1);
5943 accopWithAop ("sbc", AOP (right), offset);
5944 } else {
5945 accopWithAop (sub, AOP (right), offset);
5948 m6502_freeReg (m6502_reg_a);
5949 offset++;
5950 sub = "sbc";
5954 if (ifx)
5956 symbol *tlbl = safeNewiTempLabel (NULL);
5957 char *inst;
5959 if(needloada)
5960 loadRegTempNoFlags(m6502_reg_a, needloada);
5961 else
5962 m6502_freeReg(m6502_reg_a);
5964 // freeAsmop (result, NULL);
5966 if (!bit)
5968 inst = branchInstCmp (opcode, sign);
5969 emitBranch (inst, tlbl);
5971 else
5973 if(bmi) emitBranch ("bmi", tlbl);
5974 else emitBranch ("bpl", tlbl);
5976 emitBranch ("jmp", jlbl);
5977 safeEmitLabel (tlbl);
5979 /* mark the icode as generated */
5980 ifx->generated = 1;
5982 else
5984 symbol *true_lbl = safeNewiTempLabel (NULL);
5985 symbol *tlbl2 = safeNewiTempLabel (NULL);
5987 if (!needloada)
5988 needloada = storeRegTempIfSurv (m6502_reg_a);
5990 if(!bit)
5992 emitBranch (branchInstCmp (opcode, sign), true_lbl);
5994 else
5996 if(bmi) emitBranch ("bmi", true_lbl);
5997 else emitBranch ("bpl", true_lbl);
5999 loadRegFromConst (m6502_reg_a, 0);
6000 emitBranch ("bra", tlbl2);
6001 safeEmitLabel (true_lbl);
6002 loadRegFromConst (m6502_reg_a, 1);
6003 safeEmitLabel (tlbl2);
6004 m6502_dirtyReg (m6502_reg_a);
6005 storeRegToFullAop (m6502_reg_a, AOP (result), false);
6006 loadOrFreeRegTemp (m6502_reg_a, needloada);
6008 freeAsmop (right, NULL);
6009 freeAsmop (left, NULL);
6010 freeAsmop (result, NULL);
6014 /**************************************************************************
6015 * genCmpEQorNE - equal or not equal comparison
6016 *************************************************************************/
6017 static void genCmpEQorNE (iCode * ic, iCode * ifx)
6019 operand *right = IC_RIGHT (ic);
6020 operand *left = IC_LEFT (ic);
6021 operand *result = IC_RESULT (ic);
6022 int opcode;
6023 int size;
6024 symbol *jlbl = NULL;
6025 symbol *tlbl_NE = NULL;
6026 symbol *tlbl_EQ = NULL;
6027 bool needloada = false;
6028 int offset = 0;
6030 emitComment (TRACEGEN, __func__);
6032 opcode = ic->op;
6034 emitComment (TRACEGEN|VVDBG, " genCmpEQorNE (%s)", nameCmp (opcode));
6036 /* assign the amsops */
6037 aopOp (left, ic);
6038 aopOp (right, ic);
6039 aopOp (result, ic);
6040 printIC(ic);
6042 /* need register operand on left, prefer literal operand on right */
6043 if ((AOP_TYPE (right) == AOP_REG) || AOP_TYPE (left) == AOP_LIT)
6045 // don't swap if left is A
6046 if(!((AOP_TYPE (left) == AOP_REG) && AOP (left)->aopu.aop_reg[0]->rIdx == A_IDX))
6048 operand *temp = left;
6049 left = right;
6050 right = temp;
6051 opcode = exchangedCmp (opcode);
6055 size = max (AOP_SIZE (left), AOP_SIZE (right));
6057 if (ifx) {
6058 if (IC_TRUE (ifx)) {
6059 jlbl = IC_TRUE (ifx);
6060 opcode = negatedCmp (opcode);
6061 } else {
6062 /* false label is present */
6063 jlbl = IC_FALSE (ifx);
6067 if(AOP_TYPE (left) == AOP_REG)
6068 emitComment (TRACEGEN|VVDBG, " genCmpEQorNE left is reg: %s",AOP (left)->aopu.aop_reg[offset]->name);
6069 else
6070 emitComment (TRACEGEN|VVDBG, " genCmpEQorNE left is not not a reg");
6072 // TODO: could clobber A if reg = XA?
6074 offset = 0;
6075 while (size--) {
6076 if (AOP_TYPE (left) == AOP_REG && AOP (left)->aopu.aop_reg[offset]->rIdx == X_IDX && isAddrSafe(right, m6502_reg_x))
6077 accopWithAop ("cpx", AOP (right), offset);
6078 else if (AOP_TYPE (left) == AOP_REG && AOP (left)->aopu.aop_reg[offset]->rIdx == Y_IDX && isAddrSafe(right, m6502_reg_y))
6079 accopWithAop ("cpy", AOP (right), offset);
6080 else {
6081 emitComment (TRACEGEN|VVDBG, " genCmpEQorNE can't cpx or cpy");
6083 // TODO? why do we push when we could cpx?
6084 if (!(AOP_TYPE (left) == AOP_REG && AOP (left)->aopu.aop_reg[offset]->rIdx == A_IDX)) {
6085 if(AOP_TYPE(right) == AOP_REG) {
6086 emitComment (TRACEGEN|VVDBG, " genCmpEQorNE right is reg: %s",AOP (right)->aopu.aop_reg[offset]->name);
6089 // FIXME: always?
6090 // storeRegTemp (m6502_reg_a, true);
6091 // needloada = true;
6092 needloada = storeRegTempIfSurv(m6502_reg_a);
6093 loadRegFromAop (m6502_reg_a, AOP (left), offset);
6095 accopWithAop ("ucp", AOP (right), offset);
6096 loadRegTempNoFlags (m6502_reg_a, needloada);
6097 needloada = false;
6099 if (size) {
6100 symbol *tmp_label = safeNewiTempLabel (NULL);;
6101 if (!tlbl_NE)
6102 tlbl_NE = safeNewiTempLabel (NULL);
6103 if (!needloada && !ifx)
6104 needloada = storeRegTempIfSurv (m6502_reg_a);
6106 emitBranch ("beq", tmp_label);
6107 emitBranch ("jmp", tlbl_NE);
6108 safeEmitLabel (tmp_label);
6110 loadOrFreeRegTemp (m6502_reg_a, needloada);
6111 needloada = false;
6113 offset++;
6116 freeAsmop (right, NULL);
6117 freeAsmop (left, NULL);
6119 if (ifx) {
6120 freeAsmop (result, NULL);
6122 if (opcode == EQ_OP) {
6123 if (!tlbl_EQ)
6124 tlbl_EQ = safeNewiTempLabel (NULL);
6125 emitBranch ("beq", tlbl_EQ);
6126 if (tlbl_NE)
6127 safeEmitLabel (tlbl_NE);
6128 emitBranch ("jmp", jlbl);
6129 safeEmitLabel (tlbl_EQ);
6130 } else {
6131 if (!tlbl_NE)
6132 tlbl_NE = safeNewiTempLabel (NULL);
6133 emitBranch ("bne", tlbl_NE);
6134 emitBranch ("jmp", jlbl);
6135 safeEmitLabel (tlbl_NE);
6138 /* mark the icode as generated */
6139 ifx->generated = 1;
6140 } else {
6141 symbol *tlbl = safeNewiTempLabel (NULL);
6143 if (!needloada)
6144 needloada = storeRegTempIfSurv (m6502_reg_a);
6145 if (opcode == EQ_OP) {
6146 if (!tlbl_EQ)
6147 tlbl_EQ = safeNewiTempLabel (NULL);
6148 emitBranch ("beq", tlbl_EQ);
6149 safeEmitLabel (tlbl_NE);
6150 loadRegFromConst (m6502_reg_a, 0);
6151 emitBranch ("bra", tlbl);
6152 safeEmitLabel (tlbl_EQ);
6153 loadRegFromConst (m6502_reg_a, 1);
6154 } else {
6155 if (!tlbl_NE)
6156 tlbl_NE = safeNewiTempLabel (NULL);
6157 emitBranch ("bne", tlbl_NE);
6158 loadRegFromConst (m6502_reg_a, 0);
6159 emitBranch ("bra", tlbl);
6160 safeEmitLabel (tlbl_NE);
6161 loadRegFromConst (m6502_reg_a, 1);
6164 safeEmitLabel (tlbl);
6165 m6502_dirtyReg (m6502_reg_a);
6166 storeRegToFullAop (m6502_reg_a, AOP (result), false);
6167 loadOrFreeRegTemp (m6502_reg_a, needloada);
6168 freeAsmop (result, NULL);
6172 /**************************************************************************
6173 * genAndOp - for && operation
6174 *************************************************************************/
6175 static void genAndOp (iCode * ic)
6177 operand *right = IC_RIGHT (ic);
6178 operand *left = IC_LEFT (ic);
6179 operand *result = IC_RESULT (ic);
6181 symbol *tlbl, *tlbl0;
6182 bool needpulla;
6184 emitComment (TRACEGEN, __func__);
6186 // TODO: optimize & 0xff as cast when signed
6188 /* note here that && operations that are in an
6189 if statement are taken away by backPatchLabels
6190 only those used in arthmetic operations remain */
6191 aopOp (left, ic);
6192 aopOp (right, ic);
6193 aopOp (result, ic);
6194 printIC(ic);
6196 tlbl = safeNewiTempLabel (NULL);
6197 tlbl0 = safeNewiTempLabel (NULL);
6199 needpulla = pushRegIfSurv (m6502_reg_a);
6200 asmopToBool (AOP (left), false);
6201 emitBranch ("beq", tlbl0);
6202 asmopToBool (AOP (right), false);
6203 emitBranch ("beq", tlbl0);
6204 loadRegFromConst (m6502_reg_a, 1);
6205 emitBranch ("bra", tlbl);
6206 safeEmitLabel (tlbl0);
6207 // m6502_dirtyReg (m6502_reg_a);
6208 loadRegFromConst (m6502_reg_a, 0);
6209 safeEmitLabel (tlbl);
6210 m6502_dirtyReg (m6502_reg_a);
6212 // m6502_useReg (m6502_reg_a);
6213 // m6502_freeReg (m6502_reg_a);
6215 storeRegToFullAop (m6502_reg_a, AOP (result), false);
6216 pullOrFreeReg(m6502_reg_a, needpulla);
6218 freeAsmop (left, NULL);
6219 freeAsmop (right, NULL);
6220 freeAsmop (result, NULL);
6223 /**************************************************************************
6224 * genOrOp - for || operation
6225 *************************************************************************/
6226 static void genOrOp (iCode * ic)
6228 operand *right = IC_RIGHT (ic);
6229 operand *left = IC_LEFT (ic);
6230 operand *result = IC_RESULT (ic);
6232 symbol *tlbl, *tlbl0;
6233 bool needpulla;
6235 emitComment (TRACEGEN, __func__);
6237 /* note here that || operations that are in an
6238 if statement are taken away by backPatchLabels
6239 only those used in arthmetic operations remain */
6240 aopOp (left, ic);
6241 aopOp (right, ic);
6242 aopOp (result, ic);
6243 printIC(ic);
6245 tlbl = safeNewiTempLabel (NULL);
6246 tlbl0 = safeNewiTempLabel (NULL);
6248 needpulla = pushRegIfSurv (m6502_reg_a);
6249 asmopToBool (AOP (left), false);
6250 emitBranch ("bne", tlbl0);
6251 asmopToBool (AOP (right), false);
6252 emitBranch ("bne", tlbl0);
6253 loadRegFromConst (m6502_reg_a, 0);
6254 emitBranch ("bra", tlbl);
6255 safeEmitLabel (tlbl0);
6256 // m6502_dirtyReg (m6502_reg_a);
6257 loadRegFromConst (m6502_reg_a, 1);
6258 safeEmitLabel (tlbl);
6259 m6502_dirtyReg (m6502_reg_a);
6261 // m6502_useReg (m6502_reg_a);
6262 // m6502_freeReg (m6502_reg_a);
6264 storeRegToFullAop (m6502_reg_a, AOP (result), false);
6265 pullOrFreeReg(m6502_reg_a, needpulla);
6267 freeAsmop (left, NULL);
6268 freeAsmop (right, NULL);
6269 freeAsmop (result, NULL);
6272 /**************************************************************************
6273 * isLiteralBit - test if lit == 2^n
6274 *************************************************************************/
6275 static int
6276 isLiteralBit (unsigned long long lit)
6278 int idx;
6280 for (idx = 0; idx < 64; idx++)
6281 if (lit == 1ull<<idx)
6282 return idx + 1;
6283 return 0;
6286 /**************************************************************************
6287 * litmask - return the mask based on the operand size
6288 *************************************************************************/
6289 static unsigned long long
6290 litmask (int size)
6292 unsigned long long ret = 0xffffffffffffffffull;
6293 if (size == 1)
6294 ret = 0xffull;
6295 else if (size == 2)
6296 ret= 0xffffull;
6297 else if (size == 4)
6298 ret = 0xffffffffull;
6299 return ret;
6302 bool isAndTrivial(operand *op, unsigned char mask)
6304 if(AOP_TYPE(op)==AOP_LIT && (mask==0x00 || mask==0xff))
6305 return true;
6306 return false;
6309 bool isOrTrivial(operand *op, unsigned char mask)
6311 if(AOP_TYPE(op)==AOP_LIT && (mask==0x00 || mask==0xff ) )
6312 return true;
6313 return false;
6316 bool isXorTrivial(operand *op, unsigned char mask)
6318 if(AOP_TYPE(op)==AOP_LIT && mask==0x00)
6319 return true;
6320 return false;
6323 /**************************************************************************
6324 * genAnd - code for and
6325 *************************************************************************/
6326 static void
6327 genAnd (iCode * ic, iCode * ifx)
6329 operand *right = IC_RIGHT (ic);
6330 operand *left = IC_LEFT (ic);
6331 operand *result = IC_RESULT (ic);
6333 int size, offset = 0;
6334 bool needpulla = false;
6335 bool isLit = false;
6336 unsigned long long lit = 0ull;
6337 unsigned int bytemask;
6338 int bitpos = -1;
6340 emitComment (TRACEGEN, __func__);
6342 aopOp (left, ic);
6343 aopOp (right, ic);
6344 aopOp (result, ic);
6345 printIC(ic);
6347 /* force literal on the right and reg on the left */
6348 if (AOP_TYPE (left) == AOP_LIT || AOP_TYPE (right) == AOP_REG)
6350 if(!IS_AOP_WITH_A (AOP (left)))
6352 operand *tmp = right;
6353 right = left;
6354 left = tmp;
6358 size = (AOP_SIZE (left) >= AOP_SIZE (right)) ? AOP_SIZE (left) : AOP_SIZE (right);
6360 isLit = (AOP_TYPE (right) == AOP_LIT);
6362 if (isLit)
6364 lit = ullFromVal (AOP (right)->aopu.aop_lit);
6365 lit &= litmask(size);
6366 bitpos = isLiteralBit (lit) - 1;
6367 emitComment (TRACEGEN|VVDBG, " %s: lit=%04x bitpos=%d", __func__, lit, bitpos);
6370 #if 0
6371 // Rockwell and WDC only - unimplemented
6372 if (IS_MOS65C02 && ifx
6373 && AOP_TYPE (result) == AOP_CRY
6374 && AOP_TYPE (right) == AOP_LIT
6375 && AOP_TYPE (left) == AOP_DIR && bitpos >= 0)
6377 symbol *tlbl = safeNewiTempLabel (NULL);
6378 if (IC_TRUE (ifx))
6380 // FIXME: unimplemented
6381 emit6502op ("brclr", "#%d,%s,%05d$", bitpos & 7, aopAdrStr (AOP (left), bitpos >> 3, false), safeLabelKey2num ((tlbl->key)));
6382 emitBranch ("jmp", IC_TRUE (ifx));
6383 safeEmitLabel (tlbl);
6384 if (IC_FALSE (ifx))
6385 emitBranch ("jmp", IC_FALSE (ifx));
6387 else
6389 // FIXME: unimplemented
6390 if (!regalloc_dry_run)
6391 emit6502op ("brset", "#%d,%s,%05d$", bitpos & 7, aopAdrStr (AOP (left), bitpos >> 3, false), safeLabelKey2num ((tlbl->key)));
6392 emitBranch ("jmp", IC_FALSE (ifx));
6393 safeEmitLabel (tlbl);
6395 ifx->generated = true;
6396 goto release;
6398 #endif
6400 // special case for bit 7 and 6
6401 if (AOP_TYPE (result) == AOP_CRY && isLit && canBitOp(left))
6403 emitComment (TRACEGEN|VVDBG, " %s: special case bit %d", __func__, bitpos);
6405 if (bitpos >= 0 && (bitpos & 7) == 7)
6407 rmwWithAop ("bit", AOP (left), bitpos >> 3);
6408 genIfxJump (ifx, "n");
6409 goto release;
6411 if (bitpos >= 0 && (bitpos & 7) == 6)
6413 rmwWithAop ("bit", AOP (left), bitpos >> 3);
6414 genIfxJump (ifx, "v");
6415 goto release;
6419 // test A for flags only
6420 if (AOP_TYPE (result) == AOP_CRY && IS_AOP_A (AOP (left)))
6422 emitComment (TRACEGEN|VVDBG, " %s: test A for flags", __func__);
6424 if (m6502_reg_a->isDead)
6425 accopWithAop ("and", AOP (right), 0);
6426 else if (canBitOp(right))
6427 accopWithAop ("bit", AOP (right), 0);
6428 else
6430 if(isLit && bitpos==7)
6432 emitCpz(A_IDX);
6433 genIfxJump (ifx, "n");
6434 goto release;
6437 if(isLit && bitpos==6)
6439 // FIXME: this can be improved for 65C02
6440 storeRegTempAlways(m6502_reg_a, false);
6441 emit6502op ("bit", TEMPFMT, _G.tempOfs - 1);
6442 loadRegTemp(NULL);
6443 genIfxJump(ifx, "v");
6444 goto release;
6447 reg_info* freereg = getDeadByteReg();
6448 if (freereg)
6450 // FIXME: this can be improved for 65C02
6451 loadRegFromAop (freereg, AOP(right), 0);
6452 storeRegTempAlways(freereg, true);
6453 emit6502op ("bit", TEMPFMT, _G.tempOfs - 1);
6454 loadRegTemp(NULL);
6456 else
6458 storeRegTemp(m6502_reg_a, true);
6459 accopWithAop ("and", AOP(right), 0);
6460 loadRegTempNoFlags(m6502_reg_a, true); // preserve flags
6463 genIfxJump (ifx, "z");
6464 goto release;
6467 // test for flags only (general case)
6468 if (AOP_TYPE (result) == AOP_CRY)
6470 symbol *tlbl = safeNewiTempLabel (NULL);
6471 needpulla = storeRegTempIfSurv (m6502_reg_a);
6472 offset = 0;
6474 if ( isLit && lit == 0)
6476 loadRegFromConst (m6502_reg_a, 0x00);
6478 else
6479 while (size--)
6481 bytemask = (isLit) ? (lit >> (offset * 8)) & 0xff : 0x100;
6483 if (bytemask != 0x00)
6485 loadRegFromAop (m6502_reg_a, AOP(left), offset);
6486 if (bytemask != 0xff)
6487 accopWithAop ("and", AOP(right), offset);
6488 if (size)
6489 emitBranch ("bne", tlbl);
6491 offset++;
6493 m6502_freeReg (m6502_reg_a);
6494 safeEmitLabel (tlbl);
6496 // TODO: better way to preserve flags?
6497 if (ifx)
6499 loadRegTempNoFlags (m6502_reg_a, needpulla);
6500 genIfxJump (ifx, "z");
6502 else
6504 if (needpulla) loadRegTemp (NULL);
6506 goto release;
6509 size = AOP_SIZE (result);
6511 #if 0
6512 // Rockwell and WDC only - works but limited usefulness
6513 if (IS_MOS65C02 && AOP_TYPE (right) == AOP_LIT)
6515 unsigned long long litinv = (~lit) & litmask(size);
6516 if (sameRegs (AOP (left), AOP (result)) && (AOP_TYPE (left) == AOP_DIR)
6517 && isLiteralBit (litinv))
6519 char inst[5] = "rmbx";
6520 inst[3] = '0' + (bitpos & 7);
6521 emit6502op (inst, "%s", aopAdrStr (AOP (left), bitpos >> 3, false));
6522 goto release;
6525 #endif
6527 bool savea = false;
6528 unsigned int bmask0 = (isLit) ? ((lit >> (0 * 8)) & 0xff) : 0x100;
6529 unsigned int bmask1 = (isLit) ? ((lit >> (1 * 8)) & 0xff) : 0x100;
6531 if (IS_AOP_XA(AOP(left)))
6533 if(m6502_reg_a->isLitConst) bmask0 &= m6502_reg_a->litConst;
6534 if(m6502_reg_x->isLitConst) bmask1 &= m6502_reg_x->litConst;
6537 if(IS_AOP_XA(AOP(result)))
6539 emitComment (TRACEGEN|VVDBG, " %s: XA", __func__);
6541 // if(/*AOP_TYPE(right)==AOP_LIT &&*/ AOP_TYPE(left)!=AOP_SOF)
6543 if (IS_AOP_A(AOP(left)))
6544 storeConstToAop(0x00, AOP(result), 1);
6545 else if(bmask1==0x00)
6546 storeConstToAop(0x00, AOP(result), 1);
6547 else if(IS_AOP_XA(AOP(left)) && m6502_reg_x->isLitConst && m6502_reg_x->litConst==0xff)
6548 transferAopAop(AOP(right), 1, AOP(result), 1);
6549 else if (bmask1==0xff)
6550 transferAopAop(AOP(left), 1, AOP(result), 1);
6551 else
6553 if(IS_AOP_XA(AOP(left)))
6555 if(bmask0!=0x00)
6557 if (bmask0!=0xff)
6558 accopWithAop ("and", AOP (right), 0);
6559 storeRegTemp(m6502_reg_a, true);
6560 needpulla=true;
6563 loadRegFromAop (m6502_reg_a, AOP (left), 1);
6564 accopWithAop ("and", AOP (right), 1);
6565 storeRegToAop (m6502_reg_a, AOP (result), 1);
6567 if(bmask0==0x00)
6568 storeConstToAop(0x00, AOP(result), 0);
6569 else
6571 if(needpulla) loadRegTemp(m6502_reg_a);
6572 else
6574 loadRegFromAop (m6502_reg_a, AOP (left), 0);
6575 if (bmask0!=0xff)
6576 accopWithAop ("and", AOP (right), 0);
6579 goto release;
6584 int i;
6585 needpulla = !m6502_reg_a->isDead;
6586 if(needpulla) storeRegTemp(m6502_reg_a, true);
6588 emitComment (TRACEGEN|VVDBG, " %s: general path", __func__);
6590 for(i=0; i<size; i++)
6592 bytemask = (isLit) ? ((lit >> (i * 8)) & 0xff) : 0x100;
6593 if ( bytemask==0x00 )
6595 storeConstToAop(0x00, AOP(result), i);
6597 else if ( bytemask==0xff )
6599 transferAopAop(AOP(left), i, AOP(result), i);
6601 else
6603 loadRegFromAop (m6502_reg_a, AOP (left), i);
6604 accopWithAop ("and", AOP (right), i);
6605 storeRegToAop (m6502_reg_a, AOP (result), i);
6610 if(needpulla)
6612 loadRegTemp(m6502_reg_a);
6614 else
6616 if(savea) loadRegTemp(NULL);
6617 m6502_freeReg(m6502_reg_a);
6620 release:
6621 freeAsmop (left, NULL);
6622 freeAsmop (right, NULL);
6623 freeAsmop (result, NULL);
6626 /**************************************************************************
6627 * genOr - code for or
6628 *************************************************************************/
6629 static void
6630 genOr (iCode * ic, iCode * ifx)
6632 operand *right = IC_RIGHT (ic);
6633 operand *left = IC_LEFT (ic);
6634 operand *result = IC_RESULT (ic);
6636 int size, offset = 0;
6637 bool needpulla = false;
6638 bool isLit = false;
6639 unsigned long long lit = 0ull;
6640 unsigned int bytemask;
6641 int bitpos = -1;
6643 emitComment (TRACEGEN, __func__);
6645 aopOp (left, ic);
6646 aopOp (right, ic);
6647 aopOp (result, ic);
6648 printIC(ic);
6650 /* force literal on the right and reg on the left */
6651 if (AOP_TYPE (left) == AOP_LIT || AOP_TYPE (right) == AOP_REG)
6653 if(!IS_AOP_WITH_A (AOP (left)))
6655 operand *tmp = right;
6656 right = left;
6657 left = tmp;
6661 size = (AOP_SIZE (left) >= AOP_SIZE (right)) ? AOP_SIZE (left) : AOP_SIZE (right);
6663 isLit = (AOP_TYPE (right) == AOP_LIT);
6665 if (isLit)
6667 lit = ullFromVal (AOP (right)->aopu.aop_lit);
6668 lit &= litmask(size);
6669 bitpos = isLiteralBit (lit) - 1;
6670 emitComment (TRACEGEN|VVDBG, " %s: lit=%04x bitpos=%d", __func__, lit, bitpos);
6673 // test A for flags only
6674 if (AOP_TYPE (result) == AOP_CRY && IS_AOP_A (AOP (left)))
6676 emitComment (TRACEGEN|VVDBG, " %s: test A for flags", __func__);
6678 if( isLit && lit==0x00 )
6680 emitCpz(A_IDX);
6681 genIfxJump (ifx, "z");
6682 goto release;
6686 // test for flags only (general case)
6687 if (AOP_TYPE (result) == AOP_CRY)
6689 symbol *tlbl = safeNewiTempLabel (NULL);
6691 #if 0
6692 // FIXME: good optmization but currently not working
6693 if (IS_MOS65C02 && isLit && lit!=0)
6695 emit6502op("bit","#0x00");
6696 genIfxJump (ifx, "z");
6697 goto release;
6699 #endif
6701 needpulla = storeRegTempIfSurv (m6502_reg_a);
6702 offset = 0;
6704 if (isLit && lit != 0)
6706 loadRegFromConst (m6502_reg_a, 0xff);
6708 else
6709 while (size--)
6711 bytemask = (isLit) ? (lit >> (offset * 8)) & 0xff : 0x100;
6713 if(offset==0)
6714 loadRegFromAop (m6502_reg_a, AOP (left), offset);
6715 else
6716 accopWithAop ("ora", AOP(left), offset);
6718 if (bytemask != 0x00)
6719 accopWithAop ("ora", AOP(right), offset);
6720 if (size && size%2)
6721 emitBranch ("bne", tlbl);
6722 offset++;
6724 m6502_freeReg (m6502_reg_a);
6725 safeEmitLabel (tlbl);
6727 // TODO: better way to preserve flags?
6728 if (ifx)
6730 loadRegTempNoFlags (m6502_reg_a, needpulla);
6731 genIfxJump (ifx, "z");
6733 else
6735 if (needpulla) loadRegTemp (NULL);
6737 goto release;
6740 size = AOP_SIZE (result);
6742 #if 0
6743 // Rockwell and WDC only - works but limited usefulness
6744 if (IS_MOS65C02 && AOP_TYPE (right) == AOP_LIT)
6746 if (sameRegs (AOP (left), AOP (result)) && (AOP_TYPE (left) == AOP_DIR)
6747 && isLiteralBit (lit))
6749 char inst[5] = "smbx";
6750 inst[3] = '0' + (bitpos & 7);
6751 emit6502op (inst, "%s", aopAdrStr (AOP (left), bitpos >> 3, false));
6752 goto release;
6755 #endif
6757 bool savea = false;
6758 unsigned int bmask0 = (isLit) ? ((lit >> (0 * 8)) & 0xff) : 0x100;
6759 unsigned int bmask1 = (isLit) ? ((lit >> (1 * 8)) & 0xff) : 0x100;
6761 if (IS_AOP_XA(AOP(left)))
6763 // if(m6502_reg_a->isLitConst) bmask0 |= m6502_reg_a->litConst;
6764 // if(m6502_reg_x->isLitConst && m6502_reg_x->litConst==0) bmask1=0x00;
6767 if(IS_AOP_XA(AOP(result)))
6769 emitComment (TRACEGEN|VVDBG, " %s: XA", __func__);
6771 // if(/*AOP_TYPE(right)==AOP_LIT &&*/ AOP_TYPE(left)!=AOP_SOF)
6773 if (IS_AOP_A(AOP(left)))
6774 storeConstToAop(0x00, AOP(result), 1);
6775 else if(bmask1==0xff)
6776 storeConstToAop(0xff, AOP(result), 1);
6777 else if(IS_AOP_XA(AOP(left)) && m6502_reg_x->isLitConst && m6502_reg_x->litConst==0)
6778 transferAopAop(AOP(right), 1, AOP(result), 1);
6779 else if (bmask1==0x00)
6780 transferAopAop(AOP(left), 1, AOP(result), 1);
6781 else
6783 if(IS_AOP_XA(AOP(left)))
6785 if(bmask0!=0xff)
6787 if (bmask0!=0x00)
6788 accopWithAop ("ora", AOP (right), 0);
6789 storeRegTemp(m6502_reg_a, true);
6790 needpulla=true;
6793 loadRegFromAop (m6502_reg_a, AOP (left), 1);
6794 accopWithAop ("ora", AOP (right), 1);
6795 storeRegToAop (m6502_reg_a, AOP (result), 1);
6797 if(bmask0==0xff)
6798 storeConstToAop(0xff, AOP(result), 0);
6799 else {
6800 if(needpulla) loadRegTemp(m6502_reg_a);
6801 else
6803 loadRegFromAop (m6502_reg_a, AOP (left), 0);
6804 if (bmask0!=0x00)
6805 accopWithAop ("ora", AOP (right), 0);
6808 goto release;
6813 int i;
6814 needpulla = !m6502_reg_a->isDead;
6815 if(needpulla) storeRegTemp(m6502_reg_a, true);
6817 emitComment (TRACEGEN|VVDBG, " %s: general path", __func__);
6819 for(i=0; i<size; i++)
6821 bytemask = (isLit) ? ((lit >> (i * 8)) & 0xff) : 0x100;
6822 if ( bytemask==0xff )
6824 storeConstToAop(0xff, AOP(result), i);
6826 else if ( bytemask==0x00 )
6828 transferAopAop(AOP(left), i, AOP(result), i);
6830 else
6832 loadRegFromAop (m6502_reg_a, AOP (left), i);
6833 accopWithAop ("ora", AOP (right), i);
6834 storeRegToAop (m6502_reg_a, AOP (result), i);
6839 if(needpulla)
6841 loadRegTemp(m6502_reg_a);
6843 else
6845 if(savea) loadRegTemp(NULL);
6846 m6502_freeReg(m6502_reg_a);
6849 release:
6850 freeAsmop (left, NULL);
6851 freeAsmop (right, NULL);
6852 freeAsmop (result, NULL);
6855 /**************************************************************************
6856 * genXor - code for Exclusive or
6857 *************************************************************************/
6858 static void
6859 genXor (iCode * ic, iCode * ifx)
6861 operand *right = IC_RIGHT (ic);
6862 operand *left = IC_LEFT (ic);
6863 operand *result = IC_RESULT (ic);
6865 int size, offset = 0;
6866 bool needpulla = false;
6867 bool isLit = false;
6868 unsigned long long lit = 0ull;
6869 unsigned int bytemask;
6870 int bitpos = -1;
6872 emitComment (TRACEGEN, __func__);
6874 aopOp (left, ic);
6875 aopOp (right, ic);
6876 aopOp (result, ic);
6877 printIC(ic);
6879 /* force literal on the right and reg on the left */
6880 if (AOP_TYPE (left) == AOP_LIT || AOP_TYPE (right) == AOP_REG)
6882 if(!IS_AOP_WITH_A (AOP (left)))
6884 operand *tmp = right;
6885 right = left;
6886 left = tmp;
6890 size = (AOP_SIZE (left) >= AOP_SIZE (right)) ? AOP_SIZE (left) : AOP_SIZE (right);
6892 isLit = (AOP_TYPE (right) == AOP_LIT);
6894 if (isLit)
6896 lit = ullFromVal (AOP (right)->aopu.aop_lit);
6897 lit &= litmask(size);
6898 bitpos = isLiteralBit (lit) - 1;
6899 emitComment (TRACEGEN|VVDBG, " %s: lit=%04x bitpos=%d", __func__, lit, bitpos);
6902 // test A for flags only
6903 if (AOP_TYPE (result) == AOP_CRY && IS_AOP_A (AOP (left)))
6905 emitComment (TRACEGEN|VVDBG, " %s: test A for flags", __func__);
6907 if( isLit && lit==0x00 )
6909 emitCpz(A_IDX);
6910 genIfxJump (ifx, "z");
6911 goto release;
6915 // test for flags only (general case)
6916 if (AOP_TYPE (result) == AOP_CRY)
6918 symbol *tlbl = safeNewiTempLabel (NULL);
6919 needpulla = storeRegTempIfSurv (m6502_reg_a);
6920 offset = 0;
6922 while (size--)
6924 bytemask = (isLit) ? (lit >> (offset * 8)) & 0xff : 0x100;
6926 loadRegFromAop (m6502_reg_a, AOP (left), offset);
6927 if (bytemask == 0x00)
6929 emitCpz(A_IDX);
6931 else
6933 accopWithAop ("eor", AOP (right), offset);
6935 if (size)
6936 emitBranch ("bne", tlbl);
6937 offset++;
6939 // FIXME: check bug1875933.c
6940 m6502_freeReg (m6502_reg_a);
6941 safeEmitLabel (tlbl);
6943 // TODO: better way to preserve flags?
6944 if (ifx)
6946 loadRegTempNoFlags (m6502_reg_a, needpulla);
6947 genIfxJump (ifx, "z");
6949 else
6951 if (needpulla) loadRegTemp (NULL);
6953 goto release;
6956 size = AOP_SIZE (result);
6958 bool savea = false;
6959 unsigned int bmask0 = (isLit) ? ((lit >> (0 * 8)) & 0xff) : 0x100;
6960 unsigned int bmask1 = (isLit) ? ((lit >> (1 * 8)) & 0xff) : 0x100;
6962 if(IS_AOP_XA(AOP(result)))
6964 emitComment (TRACEGEN|VVDBG, " %s: XA", __func__);
6966 // if(/*AOP_TYPE(right)==AOP_LIT &&*/ AOP_TYPE(left)!=AOP_SOF)
6968 if (IS_AOP_A(AOP(left)))
6969 storeConstToAop(0x00, AOP(result), 1);
6970 else if(IS_AOP_XA(AOP(left)) && m6502_reg_x->isLitConst && m6502_reg_x->litConst==0)
6971 transferAopAop(AOP(right), 1, AOP(result), 1);
6972 else if(bmask1==0x00)
6973 transferAopAop(AOP(left), 1, AOP(result), 1);
6974 else
6976 if(IS_AOP_XA(AOP(left))) {
6977 // if(bmask0!=0x00)
6979 if(bmask0!=0x00)
6980 accopWithAop ("eor", AOP (right), 0);
6981 storeRegTemp(m6502_reg_a, true);
6982 needpulla=true;
6985 loadRegFromAop (m6502_reg_a, AOP (left), 1);
6986 accopWithAop ("eor", AOP (right), 1);
6987 storeRegToAop (m6502_reg_a, AOP (result), 1);
6991 if(needpulla) loadRegTemp(m6502_reg_a);
6992 else
6994 loadRegFromAop (m6502_reg_a, AOP (left), 0);
6995 if(bmask0!=0x00)
6996 accopWithAop ("eor", AOP (right), 0);
6999 goto release;
7004 int i;
7005 needpulla = !m6502_reg_a->isDead;
7006 if(needpulla) storeRegTemp(m6502_reg_a, true);
7008 emitComment (TRACEGEN|VVDBG, " %s: general path", __func__);
7010 for(i=0; i<size; i++)
7012 bytemask = (isLit) ? ((lit >> (i * 8)) & 0xff) : 0x100;
7013 if ( bytemask==0x00 )
7015 transferAopAop(AOP(left), i, AOP(result), i);
7017 else
7019 loadRegFromAop (m6502_reg_a, AOP (left), i);
7020 accopWithAop ("eor", AOP (right), i);
7021 storeRegToAop (m6502_reg_a, AOP (result), i);
7026 if(needpulla)
7028 loadRegTemp(m6502_reg_a);
7030 else
7032 if(savea) loadRegTemp(NULL);
7033 m6502_freeReg(m6502_reg_a);
7036 release:
7037 freeAsmop (left, NULL);
7038 freeAsmop (right, NULL);
7039 freeAsmop (result, NULL);
7042 static const char * expand_symbols (iCode * ic, const char *inlin)
7044 const char *begin = NULL, *p = inlin;
7045 bool inIdent = false;
7046 struct dbuf_s dbuf;
7048 dbuf_init (&dbuf, 128);
7050 while (*p) {
7051 if (inIdent) {
7052 if ('_' == *p || isalnum (*p))
7053 /* in the middle of identifier */
7054 ++p;
7055 else {
7056 /* end of identifier */
7057 symbol *sym, *tempsym;
7058 char *symname = Safe_strndup (p + 1, p - begin - 1);
7060 inIdent = 0;
7062 tempsym = newSymbol (symname, ic->level);
7063 tempsym->block = ic->block;
7064 sym = (symbol *) findSymWithLevel (SymbolTab, tempsym);
7065 if (!sym) {
7066 dbuf_append (&dbuf, begin, p - begin);
7067 } else {
7068 asmop *aop = aopForSym (ic, sym);
7069 const char *l = aopAdrStr (aop, aop->size - 1, true);
7071 if ('#' == *l)
7072 l++;
7073 sym->isref = 1;
7074 if (sym->level && !sym->allocreq && !sym->ismyparm) {
7075 werror (E_ID_UNDEF, sym->name);
7076 werror (W_CONTINUE,
7077 " Add 'volatile' to the variable declaration so that it\n"
7078 " can be referenced within inline assembly");
7080 dbuf_append_str (&dbuf, l);
7082 Safe_free (symname);
7083 begin = p++;
7085 } else if ('_' == *p) {
7086 /* begin of identifier */
7087 inIdent = true;
7088 if (begin)
7089 dbuf_append (&dbuf, begin, p - begin);
7090 begin = p++;
7091 } else {
7092 if (!begin)
7093 begin = p;
7094 p++;
7098 if (begin)
7099 dbuf_append (&dbuf, begin, p - begin);
7101 return dbuf_detach_c_str (&dbuf);
7104 /**************************************************************************
7105 * genInline - write the inline code out
7106 *************************************************************************/
7107 static void m6502_genInline (iCode * ic)
7109 char *buf, *bp, *begin;
7110 const char *expanded;
7111 bool inComment = false;
7113 emitComment (TRACEGEN, __func__);
7115 genLine.lineElement.isInline += (!options.asmpeep);
7117 buf = bp = begin = Safe_strdup (IC_INLINE (ic));
7119 /* emit each line as a code */
7120 while (*bp) {
7121 switch (*bp) {
7122 case ';':
7123 inComment = true;
7124 ++bp;
7125 break;
7127 case '\x87':
7128 case '\n':
7129 inComment = false;
7130 *bp++ = '\0';
7131 expanded = expand_symbols (ic, begin);
7132 emitcode (expanded, NULL);
7133 dbuf_free (expanded);
7134 begin = bp;
7135 break;
7137 default:
7138 /* Add \n for labels, not dirs such as c:\mydir */
7139 if (!inComment && (*bp == ':') && (isspace ((unsigned char) bp[1]))) {
7140 ++bp;
7141 *bp = '\0';
7142 ++bp;
7143 emitcode (begin, NULL);
7144 begin = bp;
7146 else
7147 ++bp;
7148 break;
7151 if (begin != bp) {
7152 expanded = expand_symbols (ic, begin);
7153 emitcode (expanded, NULL);
7154 dbuf_free (expanded);
7157 Safe_free (buf);
7159 /* consumed; we can free it here */
7160 dbuf_free (IC_INLINE (ic));
7162 genLine.lineElement.isInline -= (!options.asmpeep);
7165 /**************************************************************************
7166 * genGetByte - generates code to get a single byte
7167 *************************************************************************/
7168 static void genGetByte (const iCode *ic)
7170 operand *left = IC_LEFT (ic);
7171 operand *right = IC_RIGHT (ic);
7172 operand *result = IC_RESULT (ic);
7173 int offset;
7175 emitComment (TRACEGEN, __func__);
7177 aopOp (left, ic);
7178 aopOp (right, ic);
7179 aopOp (result, ic);
7181 offset = (int) ulFromVal (right->aop->aopu.aop_lit) / 8;
7182 transferAopAop(left->aop, offset, result->aop, 0);
7184 freeAsmop (result, NULL);
7185 freeAsmop (right, NULL);
7186 freeAsmop (left, NULL);
7189 /**************************************************************************
7190 * genGetWord - generates code to get a 16-bit word
7191 *************************************************************************/
7192 static void genGetWord (const iCode *ic)
7194 operand *left = IC_LEFT (ic);
7195 operand *right = IC_RIGHT (ic);
7196 operand *result = IC_RESULT (ic);
7197 int offset;
7199 emitComment (TRACEGEN, __func__);
7201 aopOp (left, ic);
7202 aopOp (right, ic);
7203 aopOp (result, ic);
7205 offset = (int) ulFromVal (right->aop->aopu.aop_lit) / 8;
7206 transferAopAop(left->aop, offset, result->aop, 0);
7207 transferAopAop(left->aop, offset+1, result->aop, 1);
7209 freeAsmop (result, NULL);
7210 freeAsmop (right, NULL);
7211 freeAsmop (left, NULL);
7214 /**************************************************************************
7215 * genRRC - rotate right with carry
7216 *************************************************************************/
7217 static void genRRC (iCode * ic)
7219 operand *left = IC_LEFT (ic);
7220 operand *result = IC_RESULT (ic);
7222 int size, offset;
7223 bool resultInA = false;
7224 char *shift;
7226 emitComment (TRACEGEN, __func__);
7228 /* rotate right with carry */
7229 aopOp (left, ic);
7230 aopOp (result, ic);
7231 printIC(ic);
7233 if(IS_AOP_WITH_A(AOP(result))) resultInA=true;
7234 size = AOP_SIZE (result);
7235 offset = size - 1;
7237 shift = "lsr";
7238 if(IS_AOP_XA(AOP(result))&&IS_AOP_XA(AOP(left)))
7240 storeRegTempAlways(m6502_reg_x, true);
7241 emit6502op("lsr", TEMPFMT, _G.tempOfs-1);
7242 emit6502op("ror", "a");
7243 storeRegTemp(m6502_reg_a, true);
7244 loadRegFromConst(m6502_reg_a, 0);
7245 emit6502op("ror", "a");
7246 emit6502op ("ora", TEMPFMT, _G.tempOfs-2);
7247 transferRegReg(m6502_reg_a, m6502_reg_x, true);
7248 loadRegTemp(m6502_reg_a);
7249 loadRegTemp(NULL);
7250 goto release;
7252 else if(IS_AOP_XA(AOP(left)))
7254 // TODO: optimize if the result is in DIR or EXT
7255 storeRegTempAlways(m6502_reg_x, true);
7256 emit6502op("lsr", TEMPFMT, _G.tempOfs-1);
7257 emit6502op("ror", "a");
7258 storeRegTemp(m6502_reg_a, true);
7259 loadRegFromConst(m6502_reg_a, 0);
7260 emit6502op("ror", "a");
7261 emit6502op ("ora", TEMPFMT, _G.tempOfs-2);
7262 storeRegToAop(m6502_reg_a, AOP(result), 1);
7263 loadRegTemp(m6502_reg_a);
7264 storeRegToAop(m6502_reg_a, AOP(result), 0);
7265 loadRegTemp(NULL);
7266 goto release;
7268 else if (sameRegs (AOP (left), AOP (result)))
7270 while (size--)
7272 rmwWithAop (shift, AOP (result), offset--);
7273 shift = "ror";
7276 else
7278 while (size--)
7280 loadRegFromAop (m6502_reg_a, AOP (left), offset);
7281 rmwWithReg (shift, m6502_reg_a);
7282 storeRegToAop (m6502_reg_a, AOP (result), offset);
7283 shift = "ror";
7284 offset--;
7288 if(resultInA) storeRegTemp(m6502_reg_a, true);
7290 /* now we need to put the carry into the
7291 highest order byte of the result */
7292 offset = AOP_SIZE (result) - 1;
7293 loadRegFromConst(m6502_reg_a, 0);
7294 emit6502op ("ror", "a");
7295 accopWithAop ("ora", AOP (result), offset);
7296 storeRegToAop (m6502_reg_a, AOP (result), offset);
7298 if(resultInA) loadRegTemp(m6502_reg_a);
7300 release:
7301 // pullOrFreeReg (m6502_reg_a, needpula);
7303 freeAsmop (left, NULL);
7304 freeAsmop (result, NULL);
7307 /**************************************************************************
7308 * genRLC - generate code for rotate left with carry
7309 *************************************************************************/
7310 static void genRLC (iCode * ic)
7312 operand *left = IC_LEFT (ic);
7313 operand *result = IC_RESULT (ic);
7315 int size, offset;
7316 char *shift;
7317 bool resultInA = false;
7318 // bool needpula = false;
7320 emitComment (TRACEGEN, __func__);
7322 /* rotate right with carry */
7323 aopOp (left, ic);
7324 aopOp (result, ic);
7325 printIC(ic);
7327 if(IS_AOP_WITH_A(AOP(result))) resultInA=true;
7328 size = AOP_SIZE (result);
7329 offset = 0;
7331 shift = "asl";
7332 if (!resultInA && sameRegs (AOP (left), AOP (result)))
7334 while (size--)
7336 rmwWithAop (shift, AOP (result), offset++);
7337 shift = "rol";
7340 else
7342 while (size--)
7344 loadRegFromAop (m6502_reg_a, AOP (left), offset);
7345 rmwWithReg (shift, m6502_reg_a);
7346 if(offset==0 && resultInA) storeRegTemp (m6502_reg_a, true);
7347 else storeRegToAop (m6502_reg_a, AOP (result), offset);
7348 shift = "rol";
7349 offset++;
7353 /* now we need to put the carry into the
7354 lowest order byte of the result */
7355 offset = 0;
7356 loadRegFromConst(m6502_reg_a, 0);
7357 emit6502op ("rol", "a");
7358 if (resultInA)
7360 emit6502op("ora", TEMPFMT, _G.tempOfs-1);
7361 loadRegTemp(NULL);
7363 else
7365 accopWithAop ("ora", AOP (result), offset);
7367 storeRegToAop (m6502_reg_a, AOP (result), offset);
7369 // pullOrFreeReg (m6502_reg_a, needpula);
7371 freeAsmop (left, NULL);
7372 freeAsmop (result, NULL);
7375 /**************************************************************************
7376 * AccLsh - left shift accumulator by known count
7377 *************************************************************************/
7378 static void AccLsh (int shCount)
7380 int i;
7382 shCount &= 0x0007; // shCount : 0..7
7384 /* For shift counts of 6 and 7, the unrolled loop is never optimal. */
7385 switch (shCount) {
7386 case 6:
7387 emit6502op ("ror", "a");
7388 emit6502op ("ror", "a");
7389 emit6502op ("ror", "a");
7390 emit6502op ("and", "#0xc0");
7391 /* total: 8 cycles, 5 bytes */
7392 return;
7393 case 7:
7394 emit6502op ("ror", "a");
7395 loadRegFromConst(m6502_reg_a, 0);
7396 emit6502op ("ror", "a");
7397 /* total: 6 cycles, 4 bytes */
7398 return;
7401 /* asl a is 2 cycles and 1 byte, so an unrolled loop is the */
7402 /* fastest and shortest (shCount<6). */
7403 for (i = 0; i < shCount; i++)
7404 emit6502op ("asl", "a");
7407 /**************************************************************************
7408 * AccSRsh - signed right shift accumulator by known count
7409 *************************************************************************/
7410 static void AccSRsh (int shCount)
7412 int i;
7414 shCount &= 0x0007; // shCount : 0..7
7416 if (shCount == 7)
7418 emit6502op ("rol", "a");
7419 loadRegFromConst(m6502_reg_a, 0);
7420 emit6502op ("adc", "#0xff");
7421 emit6502op ("eor", "#0xff");
7422 return;
7424 if (shCount == 6) {
7425 symbol *tlbl = safeNewiTempLabel (NULL);
7426 emit6502op ("ora", "#0x3f");
7427 emitSetCarry(1);
7428 emit6502op ("bmi", "%05d$", safeLabelNum (tlbl));
7429 emit6502op ("and", "#0xc0");
7430 emitSetCarry(0);
7431 safeEmitLabel(tlbl);
7432 emit6502op ("rol", "a");
7433 emit6502op ("rol", "a");
7434 emit6502op ("rol", "a");
7435 return;
7437 // TODO: optimize? (asr?)
7438 for (i = 0; i < shCount; i++) {
7439 emit6502op ("cmp", "#0x80");
7440 emit6502op ("ror", "a");
7444 /**************************************************************************
7445 * AccRsh - right shift accumulator by known count
7446 *************************************************************************/
7447 static void AccRsh (int shCount, bool sign)
7449 int i;
7451 if (sign) {
7452 AccSRsh (shCount);
7453 return;
7456 shCount &= 0x0007; // shCount : 0..7
7458 /* For shift counts of 6 and 7, the unrolled loop is never optimal. */
7459 switch (shCount) {
7460 case 6:
7461 emit6502op ("rol", "a");
7462 emit6502op ("rol", "a");
7463 emit6502op ("rol", "a");
7464 emit6502op ("and", "#0x03");
7465 /* total: 8 cycles, 5 bytes */
7466 return;
7467 case 7:
7468 emit6502op ("rol", "a");
7469 loadRegFromConst(m6502_reg_a, 0);
7470 emit6502op ("rol", "a");
7471 /* total: 6 cycles, 4 bytes */
7472 return;
7475 /* lsra is 2 cycles and 1 byte, so an unrolled loop is the */
7476 /* the fastest and shortest (shCount<6). */
7477 for (i = 0; i < shCount; i++)
7478 emit6502op ("lsr", "a");
7481 /**************************************************************************
7482 * XAccLsh - left shift register pair XA by known count
7483 *************************************************************************/
7484 static void reg16Lsh (reg_info *msb_reg, int shCount)
7486 int i;
7488 shCount &= 0x000f; // shCount : 0..15
7490 if (shCount >= 8) {
7491 AccLsh (shCount - 8);
7492 transferRegReg (m6502_reg_a, msb_reg, false);
7493 loadRegFromConst (m6502_reg_a, 0);
7494 return;
7497 /* if we can beat 2n cycles or bytes for some special case, do it here */
7498 switch (shCount) {
7499 case 7:
7500 storeRegTempAlways(msb_reg, true);
7501 emit6502op ("lsr", TEMPFMT, _G.tempOfs - 1);
7502 dirtyRegTemp(_G.tempOfs - 1);
7503 rmwWithReg ("ror", m6502_reg_a);
7504 transferRegReg (m6502_reg_a, msb_reg, false);
7505 loadRegFromConst (m6502_reg_a, 0);
7506 rmwWithReg ("ror", m6502_reg_a);
7507 loadRegTemp(NULL);
7508 return;
7509 case 0:
7510 return;
7511 default:
7512 /* lsla/rolx is only 2 cycles and bytes, so an unrolled loop is often */
7513 /* the fastest and shortest. */
7514 storeRegTempAlways(msb_reg, true);
7516 for (i = 0; i < shCount; i++) {
7517 rmwWithReg ("asl", m6502_reg_a);
7518 emit6502op ("rol", TEMPFMT, _G.tempOfs - 1);
7519 dirtyRegTemp(_G.tempOfs - 1);
7521 loadRegTemp(msb_reg);
7526 /**************************************************************************
7527 * XAccSRsh - signed right shift register pair XA by known count
7528 *************************************************************************/
7529 static void XAccSRsh (int shCount)
7531 symbol *tlbl;
7532 int i;
7534 shCount &= 0x000f; // shCount : 0..7
7536 /* if we can beat 2n cycles or bytes for some special case, do it here */
7537 switch (shCount) { // TODO
7538 case 15:
7539 case 14:
7540 case 13:
7541 case 12:
7542 case 11:
7543 case 10:
7544 case 9:
7545 transferRegReg (m6502_reg_x, m6502_reg_a, false);
7546 loadRegFromConst (m6502_reg_x, 0);
7547 AccSRsh (shCount - 8);
7548 tlbl = safeNewiTempLabel (NULL);
7549 emit6502op ("bpl", "%05d$", safeLabelNum (tlbl));
7550 loadRegFromConst (m6502_reg_x, 0xff);
7551 safeEmitLabel(tlbl);
7552 break;
7553 case 8:
7554 transferRegReg (m6502_reg_x, m6502_reg_a, false);
7555 loadRegFromConst (m6502_reg_x, 0);
7556 emit6502op("cmp","#0x00");
7557 tlbl = safeNewiTempLabel (NULL);
7558 emit6502op ("bpl", "%05d$", safeLabelNum (tlbl));
7559 loadRegFromConst (m6502_reg_x, 0xff);
7560 safeEmitLabel(tlbl);
7561 break;
7563 default:
7564 /* asrx/rora is only 2 cycles and bytes, so an unrolled loop is often */
7565 /* the fastest and shortest. */
7566 storeRegTempAlways(m6502_reg_x, true);
7567 for (i = 0; i < shCount; i++)
7569 // TODO: this is so bad
7570 emit6502op ("cpx", "#0x80");
7571 emit6502op ("ror", TEMPFMT, _G.tempOfs - 1);
7572 dirtyRegTemp(_G.tempOfs - 1);
7573 rmwWithReg ("ror", m6502_reg_a);
7575 loadRegTemp(m6502_reg_x);
7579 /**************************************************************************
7580 * XAccRsh - right shift register pair XA by known count
7581 *************************************************************************/
7582 static void XAccRsh (int shCount, bool sign)
7584 int i;
7586 if (sign) {
7587 XAccSRsh (shCount);
7588 return;
7591 shCount &= 0x000f; // shCount : 0..f
7593 /* if we can beat 2n cycles or bytes for some special case, do it here */
7594 switch (shCount) {
7595 case 15:
7596 case 14:
7597 case 13:
7598 case 12:
7599 case 11:
7600 case 10:
7601 case 9:
7602 case 8:
7603 transferRegReg(m6502_reg_x, m6502_reg_a, true);
7604 AccRsh (shCount - 8, false);
7605 loadRegFromConst (m6502_reg_x, 0);
7606 break;
7607 case 7:
7608 storeRegTempAlways(m6502_reg_x, true);
7609 rmwWithReg ("rol", m6502_reg_a);
7610 emit6502op ("rol", TEMPFMT, _G.tempOfs - 1);
7611 dirtyRegTemp(_G.tempOfs - 1);
7612 loadRegFromConst (m6502_reg_a, 0);
7613 rmwWithReg ("rol", m6502_reg_a);
7614 transferRegReg(m6502_reg_a, m6502_reg_x, true);
7615 loadRegTemp(m6502_reg_a);
7616 break;
7617 case 0:
7618 break;
7619 default:
7620 /* lsrx/rora is only 2 cycles and bytes, so an unrolled loop is often */
7621 /* the fastest and shortest. */
7622 storeRegTempAlways(m6502_reg_x, true);
7623 for (i = 0; i < shCount; i++)
7625 emit6502op ("lsr", TEMPFMT, _G.tempOfs - 1);
7626 dirtyRegTemp(_G.tempOfs - 1);
7627 rmwWithReg ("ror", m6502_reg_a);
7629 loadRegTemp(m6502_reg_x);
7633 /**************************************************************************
7634 * shiftL1Left2Result - shift left one byte from left to result
7635 *************************************************************************/
7636 static void shiftL1Left2Result (operand * left, int offl, operand * result, int offr, int shCount)
7638 sym_link *resulttype = operandType (result);
7639 unsigned bytemask = (IS_BITINT (resulttype) && SPEC_USIGN (resulttype) && (SPEC_BITINTWIDTH (resulttype) % 8)) ?
7640 (0xff >> (8 - SPEC_BITINTWIDTH (resulttype) % 8)) : 0xff;
7641 bool maskedbyte = (bytemask != 0xff);
7643 // TODO: shift > 2?
7644 if (sameRegs (AOP (left), AOP (result)) && aopCanShift(AOP(left)) && offr == offl && !maskedbyte) {
7645 while (shCount--)
7646 rmwWithAop ("asl", AOP (result), 0);
7647 } else {
7648 bool needpulla = pushRegIfSurv (m6502_reg_a);
7649 loadRegFromAop (m6502_reg_a, AOP (left), offl);
7650 /* shift left accumulator */
7651 AccLsh (shCount);
7652 if (maskedbyte)
7653 emit6502op ("and", IMMDFMT, bytemask);
7654 storeRegToAop (m6502_reg_a, AOP (result), offr);
7655 pullOrFreeReg (m6502_reg_a, needpulla);
7659 /**************************************************************************
7660 * movLeft2Result - move byte from left to result
7661 *************************************************************************/
7662 static void movLeft2Result (operand * left, int offl, operand * result, int offr, int sign)
7664 if (!sameRegs (AOP (left), AOP (result)) || (offl != offr)) {
7665 transferAopAop (AOP (left), offl, AOP (result), offr);
7670 /**************************************************************************
7671 * shiftL2Left2Result - shift left two bytes from left to result
7672 *************************************************************************/
7673 static void shiftL2Left2Result (operand * left, int offl, operand * result, int offr, int shCount)
7675 int i;
7676 bool needpula = false;
7677 bool needpulx = false;
7679 if (!IS_AOP_XA (AOP (left)) && !IS_AOP_A (AOP (left)))
7680 needpula = pushRegIfUsed (m6502_reg_a);
7681 else
7682 needpula = false;
7683 if (!IS_AOP_XA (AOP (left)))
7684 needpulx = pushRegIfUsed (m6502_reg_x);
7685 else
7686 needpulx = false;
7688 loadRegFromAop (m6502_reg_xa, AOP (left), offl);
7690 switch (shCount) {
7691 case 7:
7692 rmwWithReg ("lsr", m6502_reg_x);
7693 rmwWithReg ("ror", m6502_reg_a);
7694 transferRegReg (m6502_reg_a, m6502_reg_x, false);
7695 loadRegFromConst (m6502_reg_a, 0);
7696 rmwWithReg ("ror", m6502_reg_a);
7697 break;
7698 default:
7699 for (i = 0; i < shCount; i++) {
7700 rmwWithReg ("asl", m6502_reg_a);
7701 rmwWithReg ("rol", m6502_reg_x);
7704 storeRegToAop (m6502_reg_xa, AOP (result), offr);
7706 pullOrFreeReg (m6502_reg_x, needpulx);
7707 pullOrFreeReg (m6502_reg_a, needpula);
7713 /**************************************************************************
7714 * shiftRLeftOrResult - shift right one byte from left,or to result
7715 *************************************************************************/
7716 static void shiftRLeftOrResult (operand * left, int offl, operand * result, int offr, int shCount)
7718 loadRegFromAop (m6502_reg_a, AOP (left), offl);
7719 /* shift left accumulator */
7720 AccRsh (shCount, false);
7721 /* or with result */
7722 accopWithAop ("ora", AOP (result), offr);
7723 /* back to result */
7724 storeRegToAop (m6502_reg_a, AOP (result), offr);
7725 m6502_freeReg (m6502_reg_a);
7728 /**************************************************************************
7729 * genlshOne - left shift a one byte quantity by known count
7730 *************************************************************************/
7731 static void genlshOne (operand * result, operand * left, int shCount)
7733 emitComment (TRACEGEN, __func__);
7735 shiftL1Left2Result (left, LSB, result, LSB, shCount);
7738 /**************************************************************************
7739 * genlshTwo - left shift two bytes by known amount != 0
7740 *************************************************************************/
7741 static void genlshTwo (operand * result, operand * left, int shCount)
7743 bool needpulla, needpullx;
7745 sym_link *resulttype = operandType (result);
7746 unsigned topbytemask = (IS_BITINT (resulttype) && SPEC_USIGN (resulttype) && (SPEC_BITINTWIDTH (resulttype) % 8)) ?
7747 (0xff >> (8 - SPEC_BITINTWIDTH (resulttype) % 8)) : 0xff;
7748 bool maskedtopbyte = (topbytemask != 0xff);
7750 emitComment (TRACEGEN, __func__);
7752 /* if shCount >= 8 */
7753 if (shCount >= 8) {
7754 shCount -= 8;
7755 // TODO
7756 needpulla = pushRegIfSurv (m6502_reg_a);
7757 loadRegFromAop (m6502_reg_a, AOP (left), 0);
7758 AccLsh (shCount);
7759 if (maskedtopbyte)
7760 emit6502op ("and", IMMDFMT, topbytemask);
7761 storeRegToAop (m6502_reg_a, AOP (result), 1);
7762 storeConstToAop (0, AOP (result), LSB);
7763 pullOrFreeReg (m6502_reg_a, needpulla);
7764 goto done; // Top byte is 0, doesn't need masking.
7766 else if(IS_AOP_XA(AOP(result)))
7768 /* 1 <= shCount <= 7 */
7769 // TODO: count > 2 efficient?
7770 loadRegFromAop (m6502_reg_xa, AOP (left), 0);
7771 reg16Lsh (m6502_reg_x, shCount);
7773 else if(aopCanShift(AOP(result)) && shCount <= 4)
7775 if( sameRegs (AOP (left), AOP (result))) {
7776 while (shCount--) {
7777 rmwWithAop ("asl", AOP (result), 0);
7778 rmwWithAop ("rol", AOP (result), 1);
7780 } else {
7781 needpulla = storeRegTempIfSurv (m6502_reg_a);
7782 transferAopAop (AOP (left), 1, AOP(result), 1);
7783 loadRegFromAop (m6502_reg_a, AOP (left), 0);
7784 while(shCount--) {
7785 emit6502op ("asl", "a");
7786 rmwWithAop ("rol", AOP (result), 1);
7788 storeRegToAop (m6502_reg_a, AOP (result), 0);
7789 loadOrFreeRegTemp (m6502_reg_a, needpulla);
7791 } else {
7792 reg_info *reg;
7793 reg=m6502_reg_x;
7794 needpulla = storeRegTempIfSurv (m6502_reg_a);
7795 needpullx = storeRegTempIfSurv (reg);
7796 loadRegFromAop (m6502_reg_xa, AOP (left), 0);
7797 reg16Lsh (reg, shCount);
7798 storeRegToFullAop (m6502_reg_xa, AOP (result), 0);
7799 loadOrFreeRegTemp (reg, needpullx);
7800 loadOrFreeRegTemp (m6502_reg_a, needpulla);
7803 if (maskedtopbyte) {
7804 bool in_a = (result->aop->type == AOP_REG && result->aop->aopu.aop_reg[1]->rIdx == A_IDX);
7805 bool needpull = false;
7806 if (!in_a) {
7807 needpull = pushRegIfUsed (m6502_reg_a);
7808 loadRegFromAop (m6502_reg_a, result->aop, 1);
7810 emit6502op ("and", IMMDFMT, topbytemask);
7811 if (!in_a) {
7812 storeRegToAop (m6502_reg_a, result->aop, 1);
7813 pullOrFreeReg (m6502_reg_a, needpull);
7816 done:
7820 /**************************************************************************
7821 * shiftLLong - shift left one long from left to result
7822 * offr = LSB or MSB16
7823 *************************************************************************/
7824 static void shiftLLong (operand * left, operand * result, int offr)
7826 bool needpulla = false;
7827 bool needloadx = false;
7829 needpulla = pushRegIfUsed (m6502_reg_a);
7831 switch(offr) {
7832 case LSB:
7833 loadRegFromAop (m6502_reg_a, AOP (left), 0);
7834 rmwWithReg ("asl", m6502_reg_a);
7835 storeRegToAop (m6502_reg_a, AOP (result), 0);
7836 loadRegFromAop (m6502_reg_a, AOP (left), 1);
7837 rmwWithReg ("rol", m6502_reg_a);
7838 storeRegToAop (m6502_reg_a, AOP (result), 1);
7839 loadRegFromAop (m6502_reg_a, AOP (left), 2);
7840 rmwWithReg ("rol", m6502_reg_a);
7841 storeRegToAop (m6502_reg_a, AOP (result), 2);
7842 loadRegFromAop (m6502_reg_a, AOP (left), 3);
7843 rmwWithReg ("rol", m6502_reg_a);
7844 storeRegToAop (m6502_reg_a, AOP (result), 3);
7845 break;
7846 case MSB16:
7847 needloadx = storeRegTempIfUsed (m6502_reg_x);
7848 loadRegFromAop (m6502_reg_a, AOP (left), 0);
7849 rmwWithReg ("asl", m6502_reg_a);
7850 loadRegFromAop (m6502_reg_x, AOP (left), 1);
7851 storeRegToAop (m6502_reg_a, AOP (result), 1);
7852 transferRegReg (m6502_reg_x, m6502_reg_a, true);
7853 rmwWithReg ("rol", m6502_reg_a);
7854 loadRegFromAop (m6502_reg_x, AOP (left), 2);
7855 storeRegToAop (m6502_reg_a, AOP (result), 2);
7856 transferRegReg (m6502_reg_x, m6502_reg_a, true);
7857 rmwWithReg ("rol", m6502_reg_a);
7858 storeRegToAop (m6502_reg_a, AOP (result), 3);
7859 storeConstToAop (0, AOP (result), 0);
7860 break;
7863 loadOrFreeRegTemp (m6502_reg_x, needloadx);
7864 pullOrFreeReg (m6502_reg_a, needpulla);
7867 /**************************************************************************
7868 * genlshFour - shift four byte by a known amount != 0
7869 *************************************************************************/
7870 static void genlshFour (operand * result, operand * left, int shCount)
7872 emitComment (TRACEGEN, __func__);
7873 emitComment (TRACEGEN, " %s - shift=%d", __func__, shCount);
7875 sym_link *resulttype = operandType (result);
7876 unsigned topbytemask = (IS_BITINT (resulttype) && SPEC_USIGN (resulttype) && (SPEC_BITINTWIDTH (resulttype) % 8)) ?
7877 (0xff >> (8 - SPEC_BITINTWIDTH (resulttype) % 8)) : 0xff;
7878 bool maskedtopbyte = (topbytemask != 0xff);
7880 if(AOP_SIZE (result)!=4) abort();
7882 /* TODO: deal with the &result == &left case */
7884 /* if shifting more that 3 bytes */
7885 if (shCount >= 24) {
7886 shCount -= 24;
7887 if (shCount)
7888 /* lowest order of left goes to the highest
7889 order of the destination */
7890 shiftL1Left2Result (left, LSB, result, MSB32, shCount);
7891 else
7892 movLeft2Result (left, LSB, result, MSB32, 0);
7893 storeConstToAop (0, AOP (result), LSB);
7894 storeConstToAop (0, AOP (result), MSB16);
7895 storeConstToAop (0, AOP (result), MSB24);
7896 return;
7897 } else if (shCount >= 16) {
7898 /* more than two bytes */
7899 /* lower order two bytes goes to higher order two bytes */
7900 shCount -= 16;
7901 /* if some more remaining */
7902 if (shCount)
7903 shiftL2Left2Result (left, LSB, result, MSB24, shCount);
7904 else {
7905 movLeft2Result (left, MSB16, result, MSB32, 0);
7906 movLeft2Result (left, LSB, result, MSB24, 0);
7908 storeConstToAop (0, AOP (result), LSB);
7909 storeConstToAop (0, AOP (result), MSB16);
7910 return;
7913 /* if more than 1 byte */
7914 else if (shCount >= 8)
7916 /* lower order three bytes goes to higher order three bytes */
7917 shCount -= 8;
7918 if (shCount == 0)
7920 movLeft2Result (left, MSB24, result, MSB32, 0);
7921 movLeft2Result (left, MSB16, result, MSB24, 0);
7922 movLeft2Result (left, LSB, result, MSB16, 0);
7923 storeConstToAop (0, AOP (result), LSB);
7925 else if (shCount == 1)
7926 shiftLLong (left, result, MSB16);
7927 else
7929 shiftL2Left2Result (left, MSB16, result, MSB24, shCount);
7930 shiftL1Left2Result (left, LSB, result, MSB16, shCount);
7931 shiftRLeftOrResult (left, LSB, result, MSB24, 8 - shCount);
7932 storeConstToAop (0, AOP (result), LSB);
7934 return;
7936 else if (shCount <= 2)
7938 /* 1 <= shCount <= 2 */
7939 shiftLLong (left, result, LSB);
7940 if (shCount == 2)
7941 shiftLLong (result, result, LSB);
7943 else
7945 /* 3 <= shCount <= 7, optimize */
7946 #if 1
7947 shiftLLong (left, result, LSB);
7948 while(--shCount)
7949 shiftLLong (result , result, LSB);
7950 #else
7951 // FIXME: bug 2825
7952 shiftL2Left2Result (left, MSB24, result, MSB24, shCount);
7953 shiftRLeftOrResult (left, MSB16, result, MSB24, 8 - shCount);
7954 shiftL2Left2Result (left, LSB, result, LSB, shCount);
7955 #endif
7957 if (maskedtopbyte)
7959 bool in_a = (result->aop->type == AOP_REG && result->aop->aopu.aop_reg[1]->rIdx == A_IDX);
7960 bool needpull = false;
7961 if (!in_a)
7963 needpull = pushRegIfUsed (m6502_reg_a);
7964 loadRegFromAop (m6502_reg_a, result->aop, 1);
7966 emit6502op ("and", IMMDFMT, topbytemask);
7967 if (!in_a)
7969 storeRegToAop (m6502_reg_a, result->aop, 1);
7970 pullOrFreeReg (m6502_reg_a, needpull);
7975 /**************************************************************************
7976 * genRot - generates code for rotation
7977 *************************************************************************/
7978 static void genRot (iCode *ic)
7980 operand *left = IC_LEFT (ic);
7981 operand *right = IC_RIGHT (ic);
7982 unsigned int lbits = bitsForType (operandType (left));
7983 if (IS_OP_LITERAL (right) && operandLitValueUll (right) % lbits == 1)
7984 genRLC (ic);
7985 else if (IS_OP_LITERAL (right) && operandLitValueUll (right) % lbits == lbits - 1)
7986 genRRC (ic);
7987 else
7988 wassertl (0, "Unsupported rotation.");
7991 /**************************************************************************
7992 * genLeftShiftLiteral - left shifting by known count
7993 *************************************************************************/
7994 static void genLeftShiftLiteral (operand * left, operand * right, operand * result, iCode * ic)
7996 int shCount = (int) ulFromVal (AOP (right)->aopu.aop_lit);
7997 int size;
7999 emitComment (TRACEGEN, __func__);
8001 freeAsmop (right, NULL);
8003 aopOp (left, ic);
8004 aopOp (result, ic);
8006 size = AOP_SIZE (result);
8008 #if VIEW_SIZE
8009 emitComment (TRACEGEN|VVDBG, " shift left ", "result %d, left %d", size, AOP_SIZE (left));
8010 #endif
8012 if (shCount == 0) {
8013 genCopy (result, left);
8014 } else if (shCount >= (size * 8)) {
8015 while (size--)
8016 storeConstToAop (0, AOP (result), size);
8017 } else {
8018 switch (size) {
8019 case 1:
8020 genlshOne (result, left, shCount);
8021 break;
8022 case 2:
8023 genlshTwo (result, left, shCount);
8024 break;
8025 case 4:
8026 genlshFour (result, left, shCount);
8027 break;
8028 default:
8029 werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "*** ack! mystery literal shift!\n");
8030 break;
8033 freeAsmop (left, NULL);
8034 freeAsmop (result, NULL);
8037 /**************************************************************************
8038 * genLeftShift - generates code for left shifting
8039 *************************************************************************/
8040 static void genLeftShift (iCode * ic)
8042 operand *right = IC_RIGHT (ic);
8043 operand *left = IC_LEFT (ic);
8044 operand *result = IC_RESULT (ic);
8046 int size, offset;
8047 symbol *tlbl, *tlbl1;
8048 char *shift;
8049 asmop *aopResult;
8050 reg_info *countreg = NULL;
8051 int count_offset = 0;
8053 emitComment (TRACEGEN, __func__);
8055 aopOp (right, ic);
8057 sym_link *resulttype = operandType (result);
8058 unsigned topbytemask = (IS_BITINT (resulttype) && SPEC_USIGN (resulttype) && (SPEC_BITINTWIDTH (resulttype) % 8)) ?
8059 (0xff >> (8 - SPEC_BITINTWIDTH (resulttype) % 8)) : 0xff;
8060 bool maskedtopbyte = (topbytemask != 0xff);
8062 /* if the shift count is known then do it
8063 as efficiently as possible */
8064 if (AOP_TYPE (right) == AOP_LIT &&
8065 (getSize (operandType (result)) == 1 || getSize (operandType (result)) == 2 || getSize (operandType (result)) == 4))
8067 genLeftShiftLiteral (left, right, result, ic);
8068 return;
8071 /* shift count is unknown then we have to form
8072 a loop get the loop count in X : Note: we take
8073 only the lower order byte since shifting
8074 more that 32 bits make no sense anyway, ( the
8075 largest size of an object can be only 32 bits ) */
8077 aopOp (result, ic);
8078 aopOp (left, ic);
8079 aopResult = AOP (result);
8081 // TODO
8082 #if 0
8083 if (sameRegs (AOP (right), AOP (result)) || regsInCommon (right, result) || IS_AOP_XA (AOP (result)) || isOperandVolatile (result, false))
8084 aopResult = forceZeropageAop (AOP (result), sameRegs (AOP (left), AOP (result)));
8085 #endif
8087 /* load the count register */
8088 if (m6502_reg_y->isDead && !IS_AOP_WITH_Y (AOP (result)) && !IS_AOP_WITH_Y (AOP (left)))
8089 countreg = m6502_reg_y;
8090 else if (m6502_reg_x->isDead && !IS_AOP_WITH_X (AOP (result)) && !IS_AOP_WITH_X (AOP (left)))
8091 countreg = m6502_reg_x;
8092 else if (m6502_reg_a->isDead && !IS_AOP_WITH_A (AOP (result)) && !IS_AOP_WITH_A (AOP (left)))
8093 countreg = m6502_reg_a;
8095 if(countreg) {
8096 countreg->isFree = false;
8097 emitComment (TRACEGEN|VVDBG, " load countreg");
8098 loadRegFromAop (countreg, AOP (right), 0);
8099 } else {
8100 // FIXME FIXME : something odd with load/store/pull reg
8101 emitComment (TRACEGEN|VVDBG, " count is not a register");
8102 bool needpully = pushRegIfUsed (m6502_reg_y);
8103 loadRegFromAop (m6502_reg_y, AOP (right), 0);
8104 count_offset=_G.tempOfs;
8105 storeRegTemp (m6502_reg_y, true);
8106 pullOrFreeReg(m6502_reg_y, needpully);
8109 /* now move the left to the result if they are not the
8110 same */
8111 if (IS_AOP_YX (AOP (result)))
8112 loadRegFromAop (m6502_reg_yx, AOP (left), 0);
8113 else if (!sameRegs (AOP (left), aopResult))
8115 size = AOP_SIZE (result);
8116 offset = 0;
8117 while (size--)
8119 transferAopAop (AOP (left), offset, aopResult, offset);
8120 offset++;
8123 freeAsmop (left, NULL);
8124 AOP (result) = aopResult;
8126 tlbl = safeNewiTempLabel (NULL);
8127 size = AOP_SIZE (result);
8128 offset = 0;
8129 tlbl1 = safeNewiTempLabel (NULL);
8131 if (countreg) {
8132 emitCpz(countreg->rIdx);
8133 emitBranch ("beq", tlbl1);
8134 } else {
8135 emit6502op ("dec", TEMPFMT, count_offset);
8136 // FIXME: could keep it literal
8137 dirtyRegTemp(_G.tempOfs - 1);
8138 emitBranch ("bmi", tlbl1);
8141 safeEmitLabel (tlbl);
8143 shift = "asl";
8144 for (offset = 0; offset < size; offset++) {
8145 rmwWithAop (shift, AOP (result), offset);
8146 shift = "rol";
8149 if (countreg) {
8150 rmwWithReg("dec", countreg);
8151 emit6502op("bne", "%05d$", safeLabelNum (tlbl));
8152 } else {
8153 emit6502op("dec", TEMPFMT, count_offset );
8154 // FIXME: could keep it literal
8155 dirtyRegTemp(_G.tempOfs - 1);
8156 emit6502op("bpl", "%05d$", safeLabelNum (tlbl));
8159 safeEmitLabel (tlbl1);
8161 // After loop, countreg is 0
8162 if (countreg) {
8163 countreg->isLitConst = 1;
8164 countreg->litConst = 0;
8167 if (!countreg) {
8168 emitComment (TRACEGEN|VVDBG, " pull null (1) ");
8169 loadRegTemp(NULL);
8172 if (maskedtopbyte) {
8173 bool in_a = (result->aop->type == AOP_REG && result->aop->aopu.aop_reg[size - 1]->rIdx == A_IDX);
8174 bool needpull = false;
8175 if (!in_a) {
8176 needpull = pushRegIfUsed (m6502_reg_a);
8177 loadRegFromAop (m6502_reg_a, result->aop, size - 1);
8179 emit6502op ("and", IMMDFMT, topbytemask);
8180 if (!in_a) {
8181 storeRegToAop (m6502_reg_a, result->aop, size - 1);
8182 pullOrFreeReg (m6502_reg_a, needpull);
8186 freeAsmop (result, NULL);
8187 freeAsmop (right, NULL);
8190 /**************************************************************************
8191 * genrshOne - right shift a one byte quantity by known count
8192 *************************************************************************/
8193 static void genrshOne (operand * result, operand * left, int shCount, int sign)
8195 bool needpulla;
8196 emitComment (TRACEGEN, __func__);
8197 if (shCount==0) return;
8198 needpulla = pushRegIfSurv (m6502_reg_a);
8199 loadRegFromAop (m6502_reg_a, AOP (left), 0);
8200 AccRsh (shCount, sign);
8201 storeRegToFullAop (m6502_reg_a, AOP (result), sign);
8202 pullOrFreeReg (m6502_reg_a, needpulla);
8205 /**************************************************************************
8206 * genrshTwo - right shift two bytes by known amount != 0
8207 *************************************************************************/
8208 static void genrshTwo (operand * result, operand * left, int shCount, int sign)
8210 bool needpulla, needpullx;
8211 emitComment (TRACEGEN, __func__);
8213 /* if shCount >= 8 */
8214 if (shCount >= 8) {
8215 if (shCount != 8 || sign) {
8216 needpulla = pushRegIfSurv (m6502_reg_a);
8217 loadRegFromAop (m6502_reg_a, AOP (left), 1);
8218 AccRsh (shCount - 8, sign);
8219 storeRegToFullAop (m6502_reg_a, AOP (result), sign);
8220 pullOrFreeReg (m6502_reg_a, needpulla);
8221 } else {
8222 transferAopAop (AOP (left), 1, AOP (result), 0);
8223 storeConstToAop (0, AOP (result), 1);
8225 } else {
8226 /* 1 <= shCount <= 7 */
8227 needpulla = storeRegTempIfSurv (m6502_reg_a);
8228 needpullx = storeRegTempIfSurv (m6502_reg_x);
8229 loadRegFromAop (m6502_reg_xa, AOP (left), 0);
8230 XAccRsh (shCount, sign);
8231 storeRegToAop (m6502_reg_xa, AOP (result), 0);
8232 loadOrFreeRegTemp (m6502_reg_x, needpullx);
8233 loadOrFreeRegTemp (m6502_reg_a, needpulla);
8237 /**************************************************************************
8238 * shiftRLong - shift right one long from left to result
8239 * offl = LSB or MSB16
8240 *************************************************************************/
8241 static void
8242 shiftRLong (operand * left, int offl, operand * result, int sign)
8244 bool needpulla = pushRegIfSurv (m6502_reg_a);
8245 bool needloadx = false;
8247 switch(offl)
8249 case LSB:
8250 loadRegFromAop (m6502_reg_a, AOP (left), 3);
8251 if(sign)
8253 emit6502op("cmp","#0x80");
8254 rmwWithReg ("ror", m6502_reg_a);
8256 else
8257 rmwWithReg ("lsr", m6502_reg_a);
8259 storeRegToAop (m6502_reg_a, AOP (result), 3);
8260 loadRegFromAop (m6502_reg_a, AOP (left), 2);
8261 rmwWithReg ("ror", m6502_reg_a);
8262 storeRegToAop (m6502_reg_a, AOP (result), 2);
8263 loadRegFromAop (m6502_reg_a, AOP (left), 1);
8264 rmwWithReg ("ror", m6502_reg_a);
8265 storeRegToAop (m6502_reg_a, AOP (result), 1);
8266 loadRegFromAop (m6502_reg_a, AOP (left), 0);
8267 rmwWithReg ("ror", m6502_reg_a);
8268 storeRegToAop (m6502_reg_a, AOP (result), 0);
8269 break;
8270 case MSB16:
8271 needloadx = storeRegTempIfSurv (m6502_reg_x);
8273 loadRegFromConst(m6502_reg_x,0);
8274 loadRegFromAop (m6502_reg_a, AOP (left), 3);
8275 if(sign) {
8276 symbol *tlbl = safeNewiTempLabel (NULL);
8277 emitSetCarry(0);
8278 emit6502op("bpl","%05d$", safeLabelNum (tlbl));
8279 emitSetCarry(1);
8280 loadRegFromConst(m6502_reg_x,0xff);
8281 safeEmitLabel(tlbl);
8282 rmwWithReg ("ror", m6502_reg_a);
8283 } else {
8284 rmwWithReg ("lsr", m6502_reg_a);
8286 storeRegToAop (m6502_reg_x, AOP (result), 3);
8288 loadRegFromAop (m6502_reg_x, AOP (left), 2);
8289 storeRegToAop (m6502_reg_a, AOP (result), 2);
8290 transferRegReg(m6502_reg_x, m6502_reg_a, true);
8291 rmwWithReg ("ror", m6502_reg_a);
8293 loadRegFromAop (m6502_reg_x, AOP (left), 1);
8294 storeRegToAop (m6502_reg_a, AOP (result), 1);
8295 transferRegReg(m6502_reg_x, m6502_reg_a, true);
8296 rmwWithReg ("ror", m6502_reg_a);
8297 storeRegToAop (m6502_reg_a, AOP (result), 0);
8298 break;
8299 // default:
8300 // error
8302 loadOrFreeRegTemp (m6502_reg_x, needloadx);
8303 pullOrFreeReg (m6502_reg_a, needpulla);
8306 /**************************************************************************
8307 * genrshFour - shift four byte by a known amount != 0
8308 *************************************************************************/
8309 static void genrshFour (operand * result, operand * left, int shCount, int sign)
8311 bool needpulla = false;
8312 bool needpullx = false;
8314 /* TODO: handle cases where left == result */
8316 emitComment (TRACEGEN, __func__);
8317 emitComment (TRACEGEN, " %s - shift=%d", __func__, shCount);
8319 /* if shifting more that 3 bytes */
8320 if (shCount >= 24) {
8321 needpulla = storeRegTempIfSurv (m6502_reg_a);
8322 loadRegFromAop (m6502_reg_a, AOP (left), 3);
8323 AccRsh (shCount - 24, sign);
8324 storeRegToFullAop (m6502_reg_a, AOP (result), sign);
8325 } else if (shCount >= 16) {
8326 needpulla = storeRegTempIfSurv (m6502_reg_a);
8327 needpullx = storeRegTempIfSurv (m6502_reg_x);
8328 loadRegFromAop (m6502_reg_xa, AOP (left), 2);
8329 XAccRsh (shCount - 16, sign);
8330 storeRegToFullAop (m6502_reg_xa, AOP (result), sign);
8332 else if (shCount >= 8)
8334 if (shCount == 8)
8336 needpulla = storeRegTempIfSurv (m6502_reg_a);
8337 transferAopAop (AOP (left), 1, AOP (result), 0);
8338 transferAopAop (AOP (left), 2, AOP (result), 1);
8339 loadRegFromAop (m6502_reg_a, AOP (left), 3);
8340 storeRegToAop (m6502_reg_a, AOP (result), 2);
8341 storeRegSignToUpperAop (m6502_reg_a, AOP (result), 3, sign);
8343 else if (shCount == 9)
8345 shiftRLong (left, MSB16, result, sign);
8346 return;
8348 else
8350 needpulla = storeRegTempIfSurv (m6502_reg_a);
8351 needpullx = storeRegTempIfSurv (m6502_reg_x);
8352 loadRegFromAop (m6502_reg_xa, AOP (left), 1);
8353 XAccRsh (shCount - 8, false);
8354 storeRegToAop (m6502_reg_xa, AOP (result), 0);
8355 loadRegFromAop (m6502_reg_x, AOP (left), 3);
8356 loadRegFromConst (m6502_reg_a, 0);
8357 XAccRsh (shCount - 8, sign);
8358 accopWithAop ("ora", AOP (result), 1);
8359 storeRegToAop (m6502_reg_xa, AOP (result), 1);
8360 storeRegSignToUpperAop (m6502_reg_x, AOP (result), 3, sign);
8363 else
8365 /* 1 <= shCount <= 7 */
8366 if (shCount == 1)
8368 shiftRLong (left, LSB, result, sign);
8369 return;
8371 else
8373 needpulla = storeRegTempIfSurv (m6502_reg_a);
8374 needpullx = storeRegTempIfSurv (m6502_reg_x);
8375 loadRegFromAop (m6502_reg_xa, AOP (left), 0);
8376 XAccRsh (shCount, false);
8377 storeRegToAop (m6502_reg_xa, AOP (result), 0);
8378 loadRegFromAop (m6502_reg_a, AOP (left), 2);
8379 AccLsh (8 - shCount);
8380 accopWithAop ("ora", AOP (result), 1);
8381 storeRegToAop (m6502_reg_a, AOP (result), 1);
8382 loadRegFromAop (m6502_reg_xa, AOP (left), 2);
8383 XAccRsh (shCount, sign);
8384 storeRegToAop (m6502_reg_xa, AOP (result), 2);
8387 loadOrFreeRegTemp (m6502_reg_x, needpullx);
8388 loadOrFreeRegTemp (m6502_reg_a, needpulla);
8391 /**************************************************************************
8392 * genRightShiftLiteral - right shifting by known count
8393 *************************************************************************/
8394 static void genRightShiftLiteral (operand * left, operand * right, operand * result, iCode * ic, int sign)
8396 int shCount = (int) ulFromVal (AOP (right)->aopu.aop_lit);
8397 int size;
8399 emitComment (TRACEGEN, __func__);
8401 freeAsmop (right, NULL);
8403 aopOp (left, ic);
8404 aopOp (result, ic);
8406 #if VIEW_SIZE
8407 emitComment (TRACEGEN|VVDBG, " shift right ", "result %d, left %d", AOP_SIZE (result), AOP_SIZE (left));
8408 #endif
8410 size = AOP_SIZE (left);
8411 /* test the LEFT size !!! */
8413 /* I suppose that the left size >= result size */
8414 if (shCount == 0) {
8415 genCopy (result, left);
8416 } else if (shCount >= (size * 8)) {
8417 bool needpulla = pushRegIfSurv (m6502_reg_a);
8418 if (sign) {
8419 /* get sign in acc.7 */
8420 loadRegFromAop (m6502_reg_a, AOP (left), size - 1);
8422 addSign (result, LSB, sign);
8423 pullOrFreeReg (m6502_reg_a, needpulla);
8424 } else {
8425 switch (size) {
8426 case 1:
8427 genrshOne (result, left, shCount, sign);
8428 break;
8430 case 2:
8431 genrshTwo (result, left, shCount, sign);
8432 break;
8434 case 4:
8435 genrshFour (result, left, shCount, sign);
8436 break;
8437 default:
8438 wassertl (0, "Invalid operand size in right shift.");
8439 break;
8442 freeAsmop (left, NULL);
8443 freeAsmop (result, NULL);
8446 // TODO: can refactor lots of left shift with right shift
8448 /**************************************************************************
8449 * genRightShift - generate code for right shifting
8450 *************************************************************************/
8451 static void genRightShift (iCode * ic)
8453 operand *right = IC_RIGHT (ic);
8454 operand *left = IC_LEFT (ic);
8455 operand *result = IC_RESULT (ic);
8457 int size, offset;
8458 symbol *tlbl, *tlbl1;
8459 char *shift;
8460 bool sign;
8461 asmop *aopResult;
8462 reg_info *countreg = NULL;
8463 int count_offset=0;
8465 emitComment (TRACEGEN, __func__);
8467 /* if signed then we do it the hard way preserve the
8468 sign bit moving it inwards */
8469 sign = !SPEC_USIGN (getSpec (operandType (left)));
8471 /* signed & unsigned types are treated the same : i.e. the
8472 signed is NOT propagated inwards : quoting from the
8473 ANSI - standard : "for E1 >> E2, is equivalent to division
8474 by 2**E2 if unsigned or if it has a non-negative value,
8475 otherwise the result is implementation defined ", MY definition
8476 is that the sign does not get propagated */
8478 aopOp (right, ic);
8480 /* if the shift count is known then do it
8481 as efficiently as possible */
8482 if (AOP_TYPE (right) == AOP_LIT &&
8483 (getSize (operandType (result)) == 1 || getSize (operandType (result)) == 2 || getSize (operandType (result)) == 4))
8485 genRightShiftLiteral (left, right, result, ic, sign);
8486 return;
8489 /* shift count is unknown then we have to form
8490 a loop get the loop count in X : Note: we take
8491 only the lower order byte since shifting
8492 more that 32 bits make no sense anyway, ( the
8493 largest size of an object can be only 32 bits ) */
8495 aopOp (result, ic);
8496 aopOp (left, ic);
8497 aopResult = AOP (result);
8498 printIC(ic);
8500 // TODO
8501 #if 0
8502 if (sameRegs (AOP (right), AOP (result)) || regsInCommon (right, result) || IS_AOP_XA (AOP (result)) || isOperandVolatile (result, false))
8503 aopResult = forceZeropageAop (AOP (result), sameRegs (AOP (left), AOP (result)));
8504 #endif
8506 /* load the count register */
8507 if (m6502_reg_y->isDead && !IS_AOP_WITH_Y (AOP (result)) && !IS_AOP_WITH_Y (AOP (left)))
8508 countreg = m6502_reg_y;
8509 else if (m6502_reg_x->isDead && !IS_AOP_WITH_X (AOP (result)) && !IS_AOP_WITH_X (AOP (left)))
8510 countreg = m6502_reg_x;
8511 else if (m6502_reg_a->isDead && !IS_AOP_WITH_A (AOP (result)) && !IS_AOP_WITH_A (AOP (left)))
8512 countreg = m6502_reg_a;
8514 if(countreg) {
8515 countreg->isFree = false;
8516 loadRegFromAop (countreg, AOP (right), 0);
8517 } else {
8518 // FIXME FIXME
8519 emitComment (TRACEGEN|VVDBG, " count is not a register");
8520 bool needpully = pushRegIfUsed (m6502_reg_y);
8521 loadRegFromAop (m6502_reg_y, AOP (right), 0);
8522 count_offset=_G.tempOfs;
8523 storeRegTemp (m6502_reg_y, true);
8524 pullOrFreeReg(m6502_reg_y, needpully);
8527 /* now move the left to the result if they are not the
8528 same */
8529 // TODO: can we keep it in A?
8530 if (IS_AOP_YX (AOP (result))) {
8531 loadRegFromAop (m6502_reg_yx, AOP (left), 0);
8532 } else if (!sameRegs (AOP (left), aopResult)) {
8533 size = AOP_SIZE (result);
8534 offset = 0;
8535 while (size--) {
8536 transferAopAop (AOP (left), offset, aopResult, offset);
8537 offset++;
8540 freeAsmop (left, NULL);
8541 AOP (result) = aopResult;
8543 tlbl = safeNewiTempLabel (NULL);
8544 size = AOP_SIZE (result);
8545 offset = 0;
8546 tlbl1 = safeNewiTempLabel (NULL);
8548 if (countreg) {
8549 emitCpz(countreg->rIdx);
8550 emitBranch ("beq", tlbl1);
8551 } else {
8552 emit6502op ("dec", TEMPFMT, count_offset);
8553 // could keep it literal
8554 dirtyRegTemp(_G.tempOfs - 1);
8555 emitBranch ("bmi", tlbl1);
8558 safeEmitLabel (tlbl);
8560 shift = sign ? "asr" : "lsr";
8561 for (offset = size - 1; offset >= 0; offset--) {
8562 rmwWithAop (shift, AOP (result), offset);
8563 shift = "ror";
8566 if (countreg) {
8567 rmwWithReg("dec", countreg);
8568 emit6502op("bne", "%05d$", safeLabelNum (tlbl));
8569 } else {
8570 emit6502op("dec", TEMPFMT, count_offset );
8571 // FIXME: could keep it literal
8572 dirtyRegTemp(_G.tempOfs - 1);
8573 emit6502op("bpl", "%05d$", safeLabelNum (tlbl));
8576 safeEmitLabel (tlbl1);
8578 // After loop, countreg is 0
8579 if (countreg)
8581 countreg->isLitConst = 1;
8582 countreg->litConst = 0;
8585 if (!countreg)
8586 loadRegTemp(NULL);
8588 freeAsmop (result, NULL);
8589 freeAsmop (right, NULL);
8592 /**************************************************************************
8593 * decodePointerOffset - decode a pointer offset operand into a
8594 * literal offset and a rematerializable offset
8595 *************************************************************************/
8596 static void decodePointerOffset (operand * opOffset, int * litOffset, char ** rematOffset)
8598 *litOffset = 0;
8599 *rematOffset = NULL;
8601 if (!opOffset)
8602 return;
8604 if (IS_OP_LITERAL (opOffset)) {
8605 *litOffset = (int)operandLitValue (opOffset);
8606 } else if (IS_ITEMP (opOffset) && OP_SYMBOL (opOffset)->remat) {
8607 asmop * aop = aopForRemat (OP_SYMBOL (opOffset));
8609 if (aop->type == AOP_LIT)
8610 *litOffset = (int) floatFromVal (aop->aopu.aop_lit);
8611 else if (aop->type == AOP_IMMD)
8612 *rematOffset = aop->aopu.aop_immd;
8614 else
8615 wassertl (0, "Pointer get/set with non-constant offset");
8618 /**************************************************************************
8619 * does a BIT A with a constant, even for non-65C02
8620 *************************************************************************/
8621 // TODO: lookup table for each new const?
8622 static void bitAConst(int val)
8624 wassertl (val >= 0 && val <= 0xff, "bitAConst()");
8625 if (IS_MOS65C02)
8627 emit6502op ("bit", IMMDFMT, val);
8629 else
8631 reg_info *reg=getFreeByteReg();
8632 if(reg)
8634 loadRegFromConst(reg,val);
8635 storeRegTempAlways (reg, true);
8636 emit6502op ("bit", TEMPFMT, _G.tempOfs-1);
8637 loadRegTemp(NULL);
8639 else
8641 storeRegTemp (m6502_reg_a, true);
8642 emit6502op ("and", IMMDFMT, val);
8643 loadRegTempNoFlags (m6502_reg_a, true);
8648 /**************************************************************************
8649 * genUnpackBits - generates code for unpacking bits
8650 *************************************************************************/
8651 static void genUnpackBits (operand * result, operand * left, operand * right, iCode * ifx)
8653 int offset = 0; /* result byte offset */
8654 int rsize; /* result size */
8655 int rlen = 0; /* remaining bitfield length */
8656 sym_link *etype; /* bitfield type information */
8657 unsigned blen; /* bitfield length */
8658 unsigned bstr; /* bitfield starting bit within byte */
8659 bool needpulla = false;
8660 bool needpully = false;
8661 bool needpullx = false;
8662 int litOffset = 0;
8663 char * rematOffset = NULL;
8665 emitComment (TRACEGEN, __func__);
8667 decodePointerOffset (right, &litOffset, &rematOffset);
8668 etype = getSpec (operandType (result));
8669 rsize = getSize (operandType (result));
8670 blen = SPEC_BLEN (etype);
8671 bstr = SPEC_BSTR (etype);
8673 needpulla = pushRegIfSurv (m6502_reg_a);
8675 if (!IS_AOP_YX (AOP (left)))
8677 needpullx = pushRegIfSurv (m6502_reg_x);
8678 needpully = pushRegIfSurv (m6502_reg_y);
8681 int yoff= setupDPTR(left, litOffset, rematOffset, false);
8683 /* dptr now contains the address */
8685 if (ifx && blen <= 8) {
8686 loadRegFromConst(m6502_reg_y, yoff);
8687 emit6502op("lda", INDFMT_IY);
8688 if (blen < 8) {
8689 emit6502op ("and", IMMDFMT, (((unsigned char) - 1) >> (8 - blen)) << bstr);
8691 // emit6502op("php", "");//TODO
8692 pullOrFreeReg (m6502_reg_y, needpully);
8693 pullOrFreeReg (m6502_reg_x, needpullx);
8694 pullOrFreeReg (m6502_reg_a, needpulla);
8695 // emit6502op("plp", "");
8696 genIfxJump (ifx, "z");
8697 return;
8699 wassert (!ifx);
8701 /* If the bitfield length is less than a byte */
8702 if (blen < 8) {
8703 loadRegFromConst(m6502_reg_y, yoff);
8704 emit6502op("lda", INDFMT_IY);
8705 AccRsh (bstr, false);
8706 emit6502op ("and", IMMDFMT, ((unsigned char) - 1) >> (8 - blen));
8707 m6502_reg_a->isFree=false;
8708 if (!SPEC_USIGN (etype)) {
8709 /* signed bitfield */
8710 symbol *tlbl = safeNewiTempLabel (NULL);
8712 bitAConst(1 << (blen - 1));
8713 emit6502op ("beq", "%05d$", safeLabelNum (tlbl));
8714 emit6502op ("ora", IMMDFMT, (unsigned char) (0xff << blen));
8715 safeEmitLabel (tlbl);
8717 storeRegToAop (m6502_reg_a, AOP (result), offset++);
8718 goto finish;
8721 /* Bit field did not fit in a byte. Copy all
8722 but the partial byte at the end. */
8723 for (rlen = blen; rlen >= 8; rlen -= 8) {
8724 loadRegFromConst(m6502_reg_y, yoff + offset);
8725 emit6502op("lda", INDFMT_IY);
8726 if (rlen > 8 && AOP_TYPE (result) == AOP_REG)
8727 pushReg (m6502_reg_a, true);
8728 else
8729 storeRegToAop (m6502_reg_a, AOP (result), offset);
8730 offset++;
8733 /* Handle the partial byte at the end */
8734 if (rlen) {
8735 loadRegFromConst(m6502_reg_y, yoff + offset);
8736 emit6502op("lda", INDFMT_IY);
8737 emit6502op ("and", IMMDFMT, ((unsigned char) - 1) >> (8 - rlen));
8738 if (!SPEC_USIGN (etype)) {
8739 /* signed bitfield */
8740 symbol *tlbl = safeNewiTempLabel (NULL);
8741 // FIXME: works but very ugly
8742 bitAConst(1 << (rlen - 1));
8743 emit6502op ("beq", "%05d$", safeLabelNum (tlbl));
8744 emit6502op ("ora", IMMDFMT, (unsigned char) (0xff << rlen));
8745 safeEmitLabel (tlbl);
8747 storeRegToAop (m6502_reg_a, AOP (result), offset++);
8749 if (blen > 8 && AOP_TYPE (result) == AOP_REG) {
8750 pullReg (AOP (result)->aopu.aop_reg[0]);
8753 finish:
8754 if (offset < rsize) {
8755 rsize -= offset;
8756 if (SPEC_USIGN (etype)) {
8757 while (rsize--)
8758 storeConstToAop (0, AOP (result), offset++);
8759 } else {
8760 /* signed bitfield: sign extension with 0x00 or 0xff */
8761 signExtendA();
8762 while (rsize--)
8763 storeRegToAop (m6502_reg_a, AOP (result), offset++);
8766 pullOrFreeReg (m6502_reg_y, needpully);
8767 pullOrFreeReg (m6502_reg_x, needpullx);
8768 pullOrFreeReg (m6502_reg_a, needpulla);
8771 /**************************************************************************
8772 * genUnpackBitsImmed - generates code for unpacking bits
8773 *************************************************************************/
8774 static void genUnpackBitsImmed (operand * left, operand *right, operand * result, iCode * ic, iCode * ifx)
8776 int size;
8777 int offset = 0; /* result byte offset */
8778 int litOffset = 0;
8779 char * rematOffset = NULL;
8780 int rsize; /* result size */
8781 int rlen = 0; /* remaining bitfield length */
8782 sym_link *etype; /* bitfield type information */
8783 unsigned blen; /* bitfield length */
8784 unsigned bstr; /* bitfield starting bit within byte */
8785 asmop *derefaop;
8786 bool delayed_a = false;
8787 bool assigned_a = false;
8788 bool needpulla = false;
8790 emitComment (TRACEGEN, __func__);
8792 decodePointerOffset (right, &litOffset, &rematOffset);
8793 wassert (rematOffset==NULL);
8795 aopOp (result, ic);
8796 size = AOP_SIZE (result);
8798 derefaop = aopDerefAop (AOP (left), litOffset);
8799 freeAsmop (left, NULL);
8800 derefaop->size = size;
8802 etype = getSpec (operandType (result));
8803 rsize = getSize (operandType (result));
8804 blen = SPEC_BLEN (etype);
8805 bstr = SPEC_BSTR (etype);
8807 needpulla = pushRegIfSurv (m6502_reg_a);
8809 /* if the bitfield is a single bit in the direct page */
8810 if (blen == 1 && derefaop->type == AOP_DIR) {
8811 if (!ifx && bstr) {
8812 symbol *tlbl = safeNewiTempLabel (NULL);
8814 // FIXME: unimplemented
8815 loadRegFromConst (m6502_reg_a, 0);
8816 m6502_unimplemented("genUnpackBitsImmed");
8817 // emit6502op ("brclr", "#%d,%s,%05d$", bstr, aopAdrStr (derefaop, 0, false), safeLabelNum ((tlbl)));
8818 if (SPEC_USIGN (etype))
8819 rmwWithReg ("inc", m6502_reg_a);
8820 else
8821 rmwWithReg ("dec", m6502_reg_a);
8822 safeEmitLabel (tlbl);
8823 storeRegToAop (m6502_reg_a, AOP (result), offset);
8824 if (AOP_TYPE (result) == AOP_REG && AOP(result)->aopu.aop_reg[offset]->rIdx == A_IDX)
8825 assigned_a = true;
8826 m6502_freeReg (m6502_reg_a);
8827 offset++;
8828 goto finish;
8829 } else if (ifx) {
8830 symbol *tlbl = safeNewiTempLabel (NULL);
8831 symbol *jlbl;
8832 char *inst;
8834 // FIXME
8835 if (IC_TRUE (ifx)) {
8836 jlbl = IC_TRUE (ifx);
8837 inst = "brclr";
8838 } else {
8839 jlbl = IC_FALSE (ifx);
8840 inst = "brset";
8842 emit6502op (inst, "#%d,%s,%05d$", bstr, aopAdrStr (derefaop, 0, false), safeLabelNum ((tlbl)));
8843 emitBranch ("jmp", jlbl);
8844 safeEmitLabel (tlbl);
8845 ifx->generated = 1;
8846 offset++;
8847 goto finish;
8851 /* If the bitfield length is less than a byte */
8852 if (blen < 8) {
8853 loadRegFromAop (m6502_reg_a, derefaop, 0);
8854 if (!ifx) {
8855 // TODO: inefficient if just getting flags
8856 AccRsh (bstr, false);
8857 emit6502op ("and", IMMDFMT, ((unsigned char) - 1) >> (8 - blen));
8858 if (!SPEC_USIGN (etype)) {
8859 /* signed bitfield */
8860 symbol *tlbl = safeNewiTempLabel (NULL);
8861 bitAConst(1 << (blen - 1));
8862 emit6502op ("beq", "%05d$", safeLabelNum (tlbl));
8863 emit6502op ("ora", IMMDFMT, (unsigned char) (0xff << blen));
8864 safeEmitLabel (tlbl);
8866 storeRegToAop (m6502_reg_a, AOP (result), offset);
8867 if (AOP_TYPE (result) == AOP_REG && AOP(result)->aopu.aop_reg[offset]->rIdx == A_IDX)
8868 assigned_a = true;
8869 } else {
8870 emit6502op ("and", IMMDFMT, (((unsigned char) - 1) >> (8 - blen)) << bstr);
8872 offset++;
8873 goto finish;
8876 /* Bit field did not fit in a byte. Copy all
8877 but the partial byte at the end. */
8878 for (rlen = blen; rlen >= 8; rlen -= 8) {
8879 if (assigned_a && !delayed_a) {
8880 pushReg (m6502_reg_a, true);
8881 delayed_a = true;
8883 loadRegFromAop (m6502_reg_a, derefaop, offset);
8884 if (!ifx) {
8885 storeRegToAop (m6502_reg_a, AOP (result), offset);
8886 if (AOP_TYPE (result) == AOP_REG && AOP(result)->aopu.aop_reg[offset]->rIdx == A_IDX)
8887 assigned_a = true;
8888 } else {
8889 emitCpz(A_IDX);
8891 offset++;
8894 /* Handle the partial byte at the end */
8895 if (rlen) {
8896 if (assigned_a && !delayed_a) {
8897 pushReg (m6502_reg_a, true);
8898 delayed_a = true;
8900 loadRegFromAop (m6502_reg_a, derefaop, offset);
8901 emit6502op ("and", IMMDFMT, ((unsigned char) - 1) >> (8 - rlen));
8902 if (!SPEC_USIGN (etype)) {
8903 /* signed bitfield */
8904 symbol *tlbl = safeNewiTempLabel (NULL);
8906 bitAConst (1 << (rlen - 1));
8907 emit6502op ("beq", "%05d$", safeLabelNum (tlbl));
8908 emit6502op ("ora", IMMDFMT, (unsigned char) (0xff << rlen));
8909 safeEmitLabel (tlbl);
8911 storeRegToAop (m6502_reg_a, AOP (result), offset);
8912 if (AOP_TYPE (result) == AOP_REG && AOP(result)->aopu.aop_reg[offset]->rIdx == A_IDX)
8913 assigned_a = true;
8914 offset++;
8917 finish:
8918 if (offset < rsize) {
8919 rsize -= offset;
8920 if (SPEC_USIGN (etype)) {
8921 while (rsize--)
8922 storeConstToAop (0, AOP (result), offset++);
8923 } else {
8924 if (assigned_a && !delayed_a) {
8925 pushReg (m6502_reg_a, true);
8926 delayed_a = true;
8929 /* signed bitfield: sign extension with 0x00 or 0xff */
8930 signExtendA();
8931 while (rsize--)
8932 storeRegToAop (m6502_reg_a, AOP (result), offset++);
8936 freeAsmop (NULL, derefaop);
8937 freeAsmop (result, NULL);
8939 if (ifx && !ifx->generated) {
8940 genIfxJump (ifx, "z");
8942 if (delayed_a)
8943 pullReg (m6502_reg_a);
8945 // TODO? wrong plac?
8946 pullOrFreeReg (m6502_reg_a, needpulla);
8949 /**************************************************************************
8950 * genDataPointerGet - generates code when ptr offset is known
8951 *************************************************************************/
8952 static void genDataPointerGet (operand * left, operand * right, operand * result, iCode * ic, iCode * ifx)
8954 int size;
8955 int litOffset = 0;
8956 char * rematOffset = NULL;
8957 asmop *derefaop;
8958 bool needpulla = false;
8960 emitComment (TRACEGEN, __func__);
8962 decodePointerOffset (right, &litOffset, &rematOffset);
8963 wassert (rematOffset==NULL);
8965 aopOp (result, ic);
8966 size = AOP_SIZE (result);
8968 // TODO: aopDerefAop(IMMD(_ftest_a_65536_8)), why?
8969 derefaop = aopDerefAop (AOP (left), litOffset);
8970 freeAsmop (left, NULL);
8971 derefaop->size = size;
8973 if (ifx)
8974 needpulla = storeRegTempIfSurv (m6502_reg_a);
8976 if (IS_AOP_YX (AOP (result)))
8977 loadRegFromAop (m6502_reg_yx, derefaop, 0);
8978 else while (size--) {
8979 if (!ifx)
8980 transferAopAop (derefaop, size, AOP (result), size);
8981 else
8982 loadRegFromAop (m6502_reg_a, derefaop, size);
8985 freeAsmop (NULL, derefaop);
8986 freeAsmop (result, NULL);
8988 if (ifx && !ifx->generated) {
8989 loadRegTempNoFlags (m6502_reg_a, needpulla);
8990 genIfxJump (ifx, "z");
8991 } else {
8992 if (needpulla) loadRegTemp (NULL);
8997 /**************************************************************************
8998 * genPointerGet - generate code for pointer get
8999 *************************************************************************/
9000 static void genPointerGet (iCode * ic, iCode * ifx)
9002 operand *right = IC_RIGHT (ic);
9003 operand *left = IC_LEFT (ic);
9004 operand *result = IC_RESULT (ic);
9005 int size, offset;
9006 int litOffset = 0;
9007 char * rematOffset = NULL;
9008 sym_link *retype = getSpec (operandType (result));
9009 bool needpulla = false;
9011 emitComment (TRACEGEN, __func__);
9012 // result = right (remat+literal_offset) + left (register offset)
9014 size = getSize (operandType (result));
9015 if (size > 1)
9016 ifx = NULL;
9018 aopOp (left, ic);
9020 /* if left is rematerialisable */
9021 if (AOP_TYPE (left) == AOP_IMMD || AOP_TYPE (left) == AOP_LIT) {
9022 /* if result is not bit variable type */
9023 if (!IS_BITVAR (retype))
9024 genDataPointerGet (left, right, result, ic, ifx);
9025 else
9026 genUnpackBitsImmed (left, right, result, ic, ifx);
9027 return;
9030 aopOp (result, ic);
9032 /* if bit then unpack */
9033 if (IS_BITVAR (retype)) {
9034 genUnpackBits (result, left, right, ifx);
9035 goto release;
9038 // TODO?
9039 aopOp (right, ic);
9041 decodePointerOffset (right, &litOffset, &rematOffset);
9043 printIC(ic);
9045 /* force offset to signed 16-bit range */
9046 litOffset &= 0xffff;
9047 if (litOffset & 0x8000)
9048 litOffset = 0x10000 - litOffset;
9050 emitComment (TRACEGEN|VVDBG, " genPointerGet (%s) size=%d loff=%d rmoff=%s",
9051 aopName(AOP(left)), size, litOffset, rematOffset );
9054 if (AOP_TYPE (left) == AOP_DIR
9055 && !rematOffset && litOffset >= 0 && litOffset <= 256-size)
9057 // pointer is already in zero page & 8-bit offset
9058 emitComment (TRACEGEN|VVDBG, " %s - pointer already in zp", __func__);
9059 bool needloady = storeRegTempIfSurv(m6502_reg_y);
9061 #if 0
9062 // seem to make perf worse
9063 if (size == 1 && litOffset == 0
9064 && ( /*m6502_reg_x->isDead || */ (m6502_reg_x->isLitConst && m6502_reg_x->litConst == 0) ) ) {
9065 // [aa,x] x == 0
9066 loadRegFromConst(m6502_reg_x,0);
9067 emit6502op ("lda", "[%s,x]", aopAdrStr ( AOP(left), 0, true ) );
9068 storeRegToAop (m6502_reg_a, AOP (result), 0);
9069 goto release;
9071 #endif
9073 if (sameRegs(AOP(left), AOP(result)) ) {
9074 // pointer and destination is the same - need avoid overwriting
9075 emitComment (TRACEGEN|VVDBG, " %s - sameregs", __func__);
9076 needpulla = storeRegTempIfSurv (m6502_reg_a);
9077 for (int i=size-1; i>=0; i--) {
9078 loadRegFromConst(m6502_reg_y, litOffset + i);
9079 emit6502op ("lda", "[*%s],y", AOP(left)->aopu.aop_dir);
9080 if(i>1) {
9081 storeRegToAop (m6502_reg_a, AOP (result), i);
9082 } else if(i==1) {
9083 pushReg(m6502_reg_a, false);
9084 } else if(i==0) {
9085 storeRegToAop (m6502_reg_a, AOP (result), 0);
9086 if(size>1) {
9087 pullReg(m6502_reg_a);
9088 storeRegToAop (m6502_reg_a, AOP (result), 1);
9092 } else {
9093 // otherwise use [aa],y
9094 if (IS_AOP_XA(AOP(result))) {
9095 // reverse order so A is last
9096 emitComment (TRACEGEN|VVDBG, " %s: dest XA", __func__);
9097 for (int i=size-1; i>=0; i--) {
9098 loadRegFromConst(m6502_reg_y, litOffset + i);
9099 emit6502op ("lda", "[%s],y", aopAdrStr ( AOP(left), 0, true ) );
9100 storeRegToAop (m6502_reg_a, AOP (result), i);
9102 } else {
9103 // forward order
9104 emitComment (TRACEGEN|VVDBG, " %s: dest generic", __func__);
9105 if (!IS_AOP_WITH_A(AOP(result))) needpulla = storeRegTempIfSurv (m6502_reg_a);
9106 for (int i=0; i<size; i++) {
9107 loadRegFromConst(m6502_reg_y, litOffset + i);
9108 emit6502op ("lda", "[%s],y", aopAdrStr ( AOP(left), 0, true ) );
9109 storeRegToAop (m6502_reg_a, AOP (result), i);
9113 loadOrFreeRegTemp (m6502_reg_a, needpulla);
9114 loadOrFreeRegTemp(m6502_reg_y, needloady);
9115 goto release;
9118 // try absolute indexed
9119 #if 0
9120 // allow index to be in memory
9121 if (rematOffset
9122 && ( AOP_SIZE(left)==1
9123 || ( AOP_TYPE(left) == AOP_REG && AOP(left)->aopu.aop_reg[1]->isLitConst ) ) )
9124 #else
9125 // index can only be a register
9126 if (rematOffset && AOP_TYPE(left) == AOP_REG &&
9127 (AOP_SIZE(left) == 1|| AOP(left)->aopu.aop_reg[1]->isLitConst ))
9128 #endif
9130 emitComment (TRACEGEN|VVDBG," %s - absolute with 8-bit index", __func__);
9131 unsigned int hi_offset=0;
9132 char *dst_reg;
9133 char idx_reg;
9135 if(AOP_SIZE(left)==2)
9136 hi_offset=(AOP(left)->aopu.aop_reg[1]->litConst)<<8;
9138 if(AOP_TYPE(left)==AOP_REG) {
9139 switch(AOP(left)->aopu.aop_reg[0]->rIdx) {
9140 case X_IDX: idx_reg='x'; break;
9141 case Y_IDX: idx_reg='y'; break;
9142 case A_IDX: idx_reg='A'; break;
9143 default: idx_reg='E'; break;
9145 } else {
9146 idx_reg='M';
9149 if(AOP_TYPE(result)==AOP_REG) {
9150 switch(AOP(result)->aopu.aop_reg[0]->rIdx) {
9151 case A_IDX: dst_reg="lda"; break;
9152 case X_IDX: dst_reg="ldx"; break;
9153 case Y_IDX: dst_reg="ldy"; break;
9154 default: dst_reg="ERROR"; break;
9156 } else {
9157 dst_reg="MEM";
9160 bool px = false;
9161 bool py = false;
9162 bool pa = false;
9164 if(idx_reg=='A' || idx_reg=='M') {
9165 if(dst_reg[2]=='y') {
9166 px = storeRegTempIfSurv(m6502_reg_x);
9167 loadRegFromAop(m6502_reg_x, AOP(left), 0 );
9168 idx_reg='x';
9169 } else if(dst_reg[2]=='x') {
9170 py = storeRegTempIfSurv(m6502_reg_y);
9171 loadRegFromAop(m6502_reg_y, AOP(left), 0 );
9172 idx_reg='y';
9173 } else {
9174 // FIXME: should check for a free reg to avoid saving if possible
9175 py = storeRegTempIfSurv(m6502_reg_y);
9176 loadRegFromAop(m6502_reg_y, AOP(left), 0 );
9177 idx_reg='y';
9182 if(dst_reg[2] == idx_reg || dst_reg[0]=='M') {
9183 // loadRegFromAop (m6502_reg_a, AOP (right), 0);
9184 // dst_reg="lda";
9185 pa = storeRegTempIfSurv(m6502_reg_a);
9186 emit6502op("lda", "(%s+%d+0x%04x),%c",
9187 rematOffset, litOffset, hi_offset, idx_reg );
9189 storeRegToAop (m6502_reg_a, AOP (result), 0);
9190 loadOrFreeRegTemp(m6502_reg_a,pa);
9191 } else {
9192 emit6502op(dst_reg, "(%s+%d+0x%04x),%c",
9193 rematOffset, litOffset, hi_offset, idx_reg );
9196 loadOrFreeRegTemp(m6502_reg_x,px);
9197 loadOrFreeRegTemp(m6502_reg_y,py);
9198 loadOrFreeRegTemp (m6502_reg_a, needpulla);
9199 goto release;
9202 needpulla = storeRegTempIfSurv (m6502_reg_a);
9203 bool needloady = storeRegTempIfSurv(m6502_reg_y);
9204 bool needloadx = storeRegTempIfSurv(m6502_reg_x);
9206 int yoff = setupDPTR(left, litOffset, rematOffset, false);
9208 emitComment (TRACEGEN|VVDBG, " %s: generic path", __func__);
9210 if (IS_AOP_XA (AOP (result))) {
9211 loadRegFromConst(m6502_reg_y, yoff + 1);
9212 emit6502op ("lda", INDFMT_IY);
9213 transferRegReg(m6502_reg_a, m6502_reg_x, true);
9214 loadRegFromConst(m6502_reg_y, yoff + 0);
9215 emit6502op ("lda", INDFMT_IY);
9217 else
9219 for (offset=0; offset<size; offset++)
9221 loadRegFromConst(m6502_reg_y, yoff + offset);
9222 emit6502op ("lda", INDFMT_IY);
9223 storeRegToAop (m6502_reg_a, AOP (result), offset);
9226 loadOrFreeRegTemp (m6502_reg_x, needloadx);
9227 loadOrFreeRegTemp (m6502_reg_y, needloady);
9228 loadOrFreeRegTemp (m6502_reg_a, needpulla);
9230 release:
9231 freeAsmop (left, NULL);
9232 freeAsmop (result, NULL);
9234 if (ifx && !ifx->generated) {
9235 genIfxJump (ifx, "z");
9239 /**************************************************************************
9240 * genPackBits - generates code for packed bit storage
9241 *************************************************************************/
9242 static void genPackBits (operand * result, operand * left, sym_link * etype, operand * right)
9244 int offset = 0; /* source byte offset */
9245 int rlen = 0; /* remaining bitfield length */
9246 unsigned blen; /* bitfield length */
9247 unsigned bstr; /* bitfield starting bit within byte */
9248 unsigned long long litval; /* source literal value (if AOP_LIT) */
9249 unsigned char mask; /* bitmask within current byte */
9250 int litOffset = 0;
9251 char *rematOffset = NULL;
9252 bool needpulla;
9254 emitComment (TRACEGEN, __func__);
9256 decodePointerOffset (left, &litOffset, &rematOffset);
9257 blen = SPEC_BLEN (etype);
9258 bstr = SPEC_BSTR (etype);
9260 needpulla = pushRegIfSurv (m6502_reg_a);
9261 if (AOP_TYPE (right) == AOP_REG)
9263 /* Not optimal, but works for any register sources. */
9264 /* Just push the source values onto the stack and */
9265 /* pull them off any needed. Better optimzed would */
9266 /* be to do some of the shifting/masking now and */
9267 /* push the intermediate result. */
9268 if (blen > 8)
9269 pushReg (AOP (right)->aopu.aop_reg[1], true);
9270 pushReg (AOP (right)->aopu.aop_reg[0], true);
9273 int yoff= setupDPTR(result, litOffset, rematOffset, false);
9275 /* If the bitfield length is less than a byte */
9276 if (blen < 8)
9278 mask = ((unsigned char) (0xFF << (blen + bstr)) | (unsigned char) (0xFF >> (8 - bstr)));
9280 if (AOP_TYPE (right) == AOP_LIT)
9282 /* Case with a bitfield length <8 and literal source
9284 litval = ullFromVal (AOP (right)->aopu.aop_lit);
9285 litval <<= bstr;
9286 litval &= (~mask) & 0xff;
9288 loadRegFromConst(m6502_reg_y, yoff + offset);
9289 emit6502op ("lda", INDFMT_IY);
9290 if ((mask | litval) != 0xff) {
9291 emit6502op ("and", IMMDFMT, mask);
9293 if (litval)
9295 emit6502op ("ora", IMMDFMT, litval);
9297 loadRegFromConst(m6502_reg_y, yoff + offset);
9298 emit6502op ("sta", INDFMT_IY);
9299 pullOrFreeReg (m6502_reg_a, needpulla);
9300 return;
9303 /* Case with a bitfield length < 8 and arbitrary source
9305 if (AOP_TYPE (right) == AOP_REG)
9306 pullReg (m6502_reg_a);
9307 else
9308 loadRegFromAop (m6502_reg_a, AOP (right), 0);
9309 /* shift and mask source value */
9310 AccLsh (bstr);
9311 emit6502op ("and", IMMDFMT, (~mask) & 0xff);
9312 storeRegTemp (m6502_reg_a, true);
9314 loadRegFromConst(m6502_reg_y, yoff + offset);
9315 emit6502op("lda", INDFMT_IY);
9316 emit6502op("and", IMMDFMT, mask);
9317 emit6502op ("ora", TEMPFMT, _G.tempOfs-1);
9318 loadRegFromConst(m6502_reg_y, yoff + offset);
9319 emit6502op ("sta", INDFMT_IY);
9320 // loadRegTemp (m6502_reg_a);
9321 loadRegTemp (NULL);
9322 // TODO? redundant?
9323 pullOrFreeReg (m6502_reg_a, needpulla);
9324 return;
9327 /* Bit length is greater than 7 bits. In this case, copy */
9328 /* all except the partial byte at the end */
9329 for (rlen = blen; rlen >= 8; rlen -= 8)
9331 if (AOP_TYPE (right) == AOP_REG)
9332 pullReg (m6502_reg_a);
9333 else
9334 loadRegFromAop (m6502_reg_a, AOP (right), offset);
9336 // storeRegIndexed (m6502_reg_a, litOffset+offset, rematOffset);
9337 loadRegFromConst(m6502_reg_y, yoff + offset);
9338 emit6502op ("sta", INDFMT_IY);
9339 offset++;
9342 /* If there was a partial byte at the end */
9343 if (rlen)
9345 mask = (((unsigned char) - 1 << rlen) & 0xff);
9347 if (AOP_TYPE (right) == AOP_LIT) {
9348 /* Case with partial byte and literal source
9350 litval = (int) ulFromVal (AOP (right)->aopu.aop_lit);
9351 litval >>= (blen - rlen);
9352 litval &= (~mask) & 0xff;
9353 // loadRegIndexed (m6502_reg_a, litOffset+offset, rematOffset);
9354 loadRegFromConst(m6502_reg_y, yoff + offset);
9355 emit6502op ("lda", INDFMT_IY);
9356 if ((mask | litval) != 0xff) {
9357 emit6502op ("and", IMMDFMT, mask);
9359 if (litval) {
9360 emit6502op ("ora", IMMDFMT, litval);
9362 m6502_dirtyReg (m6502_reg_a);
9363 // storeRegIndexed (m6502_reg_a, litOffset+offset, rematOffset);
9364 loadRegFromConst(m6502_reg_y, yoff + offset);
9365 emit6502op ("sta", INDFMT_IY);
9366 pullOrFreeReg (m6502_reg_a, needpulla);
9367 return;
9370 /* Case with partial byte and arbitrary source
9372 if (AOP_TYPE (right) == AOP_REG)
9373 pullReg (m6502_reg_a);
9374 else
9375 loadRegFromAop (m6502_reg_a, AOP (right), offset);
9376 emit6502op ("and", IMMDFMT, (~mask) & 0xff);
9377 pushReg (m6502_reg_a, true);
9379 // FIXME: works but ugly
9380 // loadRegIndexed(m6502_reg_a, litOffset+offset, rematOffset);
9381 loadRegFromConst(m6502_reg_y, yoff + offset);
9382 emit6502op ("lda", INDFMT_IY);
9383 emit6502op ("and", IMMDFMT, mask);
9384 // emit6502op ("ora19", "1,s");
9385 storeRegTemp(m6502_reg_a, true);
9386 emit6502op("pla","");
9387 emit6502op("pha","");
9388 emit6502op("ora", TEMPFMT, _G.tempOfs-1);
9389 loadRegTemp(NULL);
9390 // storeRegIndexed (m6502_reg_a, litOffset+offset, rematOffset);
9391 loadRegFromConst(m6502_reg_y, yoff + offset);
9392 emit6502op ("sta", INDFMT_IY);
9393 pullReg (m6502_reg_a);
9396 pullOrFreeReg (m6502_reg_a, needpulla);
9399 /**************************************************************************
9400 * genPackBitsImmed - generates code for packed bit storage
9401 *************************************************************************/
9402 static void genPackBitsImmed (operand * result, operand * left, sym_link * etype, operand * right, iCode * ic)
9404 asmop *derefaop;
9405 int size;
9406 int offset = 0; /* source byte offset */
9407 int rlen = 0; /* remaining bitfield length */
9408 unsigned blen; /* bitfield length */
9409 unsigned bstr; /* bitfield starting bit within byte */
9410 unsigned long long int litval;/* source literal value (if AOP_LIT) */
9411 unsigned char mask; /* bitmask within current byte */
9412 bool needpulla;
9413 int litOffset = 0;
9414 char *rematOffset = NULL;
9416 emitComment (TRACEGEN, __func__);
9418 blen = SPEC_BLEN (etype);
9419 bstr = SPEC_BSTR (etype);
9421 aopOp (right, ic);
9422 size = AOP_SIZE (right);
9423 decodePointerOffset (left, &litOffset, &rematOffset);
9424 wassert (!rematOffset);
9426 derefaop = aopDerefAop (AOP (result), litOffset);
9427 freeAsmop (result, NULL);
9428 derefaop->size = size;
9430 /* if the bitfield is a single bit in the direct page */
9431 if (blen == 1 && derefaop->type == AOP_DIR) {
9432 if (AOP_TYPE (right) == AOP_LIT) {
9433 litval = ullFromVal (AOP (right)->aopu.aop_lit);
9434 // FIXME: unimplemented
9435 m6502_unimplemented("genPackBitsImmed 1");
9436 //emit6502op ((litval & 1) ? "bset" : "bclr", "#%d,%s", bstr, aopAdrStr (derefaop, 0, false));
9437 } else {
9438 symbol *tlbl1 = safeNewiTempLabel (NULL);
9439 symbol *tlbl2 = safeNewiTempLabel (NULL);
9441 // FIXME: unimplemented
9442 m6502_unimplemented("genPackBitsImmed 2");
9443 needpulla = pushRegIfSurv (m6502_reg_a);
9444 loadRegFromAop (m6502_reg_a, AOP (right), 0);
9445 emit6502op ("lsr", "a");
9446 emitBranch ("bcs", tlbl1);
9447 emit6502op ("bclr", "#%d,%s", bstr, aopAdrStr (derefaop, 0, false));
9448 emitBranch ("bra", tlbl2);
9449 safeEmitLabel (tlbl1);
9450 emit6502op ("bset", "#%d,%s", bstr, aopAdrStr (derefaop, 0, false));
9451 safeEmitLabel (tlbl2);
9452 pullOrFreeReg (m6502_reg_a, needpulla);
9454 goto release;
9457 /* If the bitfield length is less than a byte */
9458 if (blen < 8) {
9459 mask = ((unsigned char) (0xFF << (blen + bstr)) | (unsigned char) (0xFF >> (8 - bstr)));
9461 if (AOP_TYPE (right) == AOP_LIT) {
9462 /* Case with a bitfield length <8 and literal source
9464 litval = (int) ulFromVal (AOP (right)->aopu.aop_lit);
9465 litval <<= bstr;
9466 litval &= (~mask) & 0xff;
9468 needpulla = pushRegIfSurv (m6502_reg_a);
9469 loadRegFromAop (m6502_reg_a, derefaop, 0);
9470 if ((mask | litval) != 0xff) {
9471 emit6502op ("and", IMMDFMT, mask);
9473 if (litval) {
9474 emit6502op ("ora", IMMDFMT, litval);
9476 m6502_dirtyReg (m6502_reg_a);
9477 storeRegToAop (m6502_reg_a, derefaop, 0);
9479 pullOrFreeReg (m6502_reg_a, needpulla);
9480 goto release;
9483 /* Case with a bitfield length < 8 and arbitrary source
9485 needpulla = pushRegIfSurv (m6502_reg_a);
9486 loadRegFromAop (m6502_reg_a, AOP (right), 0);
9487 /* shift and mask source value */
9488 AccLsh (bstr);
9489 emit6502op ("and", IMMDFMT, (~mask) & 0xff);
9490 storeRegTemp(m6502_reg_a, true);
9492 loadRegFromAop (m6502_reg_a, derefaop, 0);
9493 emit6502op("and", IMMDFMT, mask);
9494 emit6502op ("ora", TEMPFMT, _G.tempOfs - 1);
9495 storeRegToAop (m6502_reg_a, derefaop, 0);
9497 pullOrFreeReg (m6502_reg_a, needpulla);
9498 loadRegTemp(NULL);
9499 goto release;
9502 /* Bit length is greater than 7 bits. In this case, copy */
9503 /* all except the partial byte at the end */
9504 for (rlen = blen; rlen >= 8; rlen -= 8) {
9505 transferAopAop (AOP (right), offset, derefaop, offset);
9506 offset++;
9509 /* If there was a partial byte at the end */
9510 if (rlen) {
9511 mask = (((unsigned char) - 1 << rlen) & 0xff);
9513 if (AOP_TYPE (right) == AOP_LIT) {
9514 /* Case with partial byte and literal source
9516 litval = (int) ulFromVal (AOP (right)->aopu.aop_lit);
9517 litval >>= (blen - rlen);
9518 litval &= (~mask) & 0xff;
9519 needpulla = pushRegIfSurv (m6502_reg_a);
9520 loadRegFromAop (m6502_reg_a, derefaop, offset);
9521 if ((mask | litval) != 0xff) {
9522 emit6502op ("and", IMMDFMT, mask);
9524 if (litval) {
9525 emit6502op ("ora", IMMDFMT, litval);
9527 m6502_dirtyReg (m6502_reg_a);
9528 storeRegToAop (m6502_reg_a, derefaop, offset);
9529 m6502_dirtyReg (m6502_reg_a);
9530 pullOrFreeReg (m6502_reg_a, needpulla);
9531 goto release;
9534 /* Case with partial byte and arbitrary source
9536 needpulla = pushRegIfSurv (m6502_reg_a);
9537 loadRegFromAop (m6502_reg_a, AOP (right), offset);
9538 emit6502op ("and", IMMDFMT, (~mask) & 0xff);
9539 storeRegTemp (m6502_reg_a, true);
9541 loadRegFromAop (m6502_reg_a, derefaop, offset);
9542 emit6502op("and", IMMDFMT, mask);
9543 emit6502op ("ora", TEMPFMT, _G.tempOfs - 1);
9544 storeRegToAop (m6502_reg_a, derefaop, offset);
9545 pullOrFreeReg (m6502_reg_a, needpulla);
9546 loadRegTemp (NULL);
9549 m6502_freeReg (m6502_reg_a);
9551 release:
9552 freeAsmop (right, NULL);
9553 freeAsmop (NULL, derefaop);
9556 /**************************************************************************
9557 * genDataPointerSet - remat pointer to data space
9558 *************************************************************************/
9559 static void genDataPointerSet (operand * left, operand * right, operand * result, iCode * ic)
9561 int size;
9562 asmop *derefaop;
9563 int litOffset = 0;
9564 char *rematOffset = NULL;
9566 emitComment (TRACEGEN, __func__);
9568 aopOp (right, ic);
9569 size = AOP_SIZE (right);
9570 decodePointerOffset (left, &litOffset, &rematOffset);
9571 wassert (!rematOffset);
9573 derefaop = aopDerefAop (AOP (result), litOffset);
9574 freeAsmop (result, NULL);
9575 derefaop->size = size;
9577 while (size--) {
9578 transferAopAop (AOP (right), size, derefaop, size);
9581 freeAsmop (right, NULL);
9582 freeAsmop (NULL, derefaop);
9585 /**************************************************************************
9586 * genPointerSet - stores the value into a pointer location
9587 *************************************************************************/
9588 static void genPointerSet (iCode * ic)
9590 operand *right = IC_RIGHT (ic);
9591 operand *left = IC_LEFT (ic);
9592 operand *result = IC_RESULT (ic);
9593 int size, offset;
9594 bool needpulla = false;
9595 bool needpullx = false;
9596 bool needpully = false;
9597 bool deadA = false;
9598 int litOffset = 0;
9599 char *rematOffset = NULL;
9600 wassert (operandType (result)->next);
9601 bool bit_field = IS_BITVAR (operandType (result)->next);
9603 emitComment (TRACEGEN, __func__);
9605 // *(result (reg) + left (rematoffset+litoffset) = right
9608 aopOp (result, ic);
9610 /* if the result is rematerializable */
9611 if (AOP_TYPE (result) == AOP_IMMD || AOP_TYPE (result) == AOP_LIT) {
9612 if (!bit_field)
9613 genDataPointerSet (left, right, result, ic);
9614 else
9615 genPackBitsImmed (result, left, operandType (result)->next, right, ic);
9616 return;
9619 aopOp (right, ic);
9620 //aopOp (left, ic);
9622 printIC(ic);
9625 size = AOP_SIZE (right);
9627 decodePointerOffset (left, &litOffset, &rematOffset);
9629 emitComment (TRACEGEN|VVDBG, " genPointerSet (%s), size=%d, litoffset=%d, rematoffset=%s",
9630 aopName(AOP(right)), size, litOffset, rematOffset );
9632 // shortcut for [aa],y (or [aa,x]) if already in zero-page
9633 // and we're not storing to the same pointer location
9635 if (!bit_field
9636 && AOP_TYPE (result) == AOP_DIR && !rematOffset && litOffset >= 0 && litOffset <= 256-size
9637 && !sameRegs(AOP(right), AOP(result)) ) {
9639 #if 0
9640 if (size == 1 && litOffset == 0 && m6502_reg_x->isLitConst && m6502_reg_x->litConst == 0) {
9641 // use [aa,x] if only 1 byte and offset is 0
9642 loadRegFromAop (m6502_reg_a, AOP (right), 0);
9643 emit6502op ("sta", "[%s,x]", aopAdrStr ( AOP(result), 0, true ) );
9644 } else
9646 #endif
9647 needpulla = storeRegTempIfSurv (m6502_reg_a);
9648 needpully = storeRegTempIfUsed (m6502_reg_y);
9650 emitComment (TRACEGEN|VVDBG," %s - ptr already in zp ", __func__);
9652 if (IS_AOP_YX(AOP(right)))
9654 // reverse order so Y is first
9655 for (int i=size-1; i>=0; i--)
9657 loadRegFromAop (m6502_reg_a, AOP (right), i);
9658 loadRegFromConst(m6502_reg_y, litOffset + i);
9659 emit6502op ("sta", "[%s],y", aopAdrStr ( AOP(result), 0, true ) );
9662 else
9664 // forward order
9665 for (int i=0; i<size; i++)
9667 loadRegFromAop (m6502_reg_a, AOP (right), i);
9668 loadRegFromConst(m6502_reg_y, litOffset + i);
9669 emit6502op ("sta", "[%s],y", aopAdrStr ( AOP(result), 0, true ) );
9672 goto release;
9675 #if 1
9676 // abs,x or abs,y with index in register or memory
9677 if (rematOffset
9678 && ( AOP_SIZE(result)==1
9679 || ( AOP_TYPE(result) == AOP_REG && AOP(result)->aopu.aop_reg[1]->isLitConst ) ) )
9680 #else
9681 // abs,x or abs,y with index in register
9682 if (rematOffset && AOP_TYPE(result)== AOP_REG
9683 && ( AOP_SIZE(result) == 1 || AOP(result)->aopu.aop_reg[1]->isLitConst ) )
9684 #endif
9686 emitComment (TRACEGEN|VVDBG," %s - absolute with 8-bit index", __func__);
9687 emitComment(TRACEGEN|VVDBG," reg : %d size:%d", AOP(result)->aopu.aop_reg[0]->rIdx, AOP_SIZE(result) );
9689 emitComment (TRACEGEN|VVDBG,"AOP TYPE(result)=%d",AOP_TYPE (result));
9690 emitComment (TRACEGEN|VVDBG,"AOP(result) reg=%d",AOP(result)->aopu.aop_reg[0]->rIdx);
9691 unsigned int hi_offset=0;
9692 bool src_reg_is_y = false;
9693 char idx_reg;
9694 bool px = false;
9695 bool py = false;
9696 bool pa = false;
9698 pa=pushRegIfSurv(m6502_reg_a);
9701 if(AOP_SIZE(result)==2)
9702 hi_offset=(AOP(result)->aopu.aop_reg[1]->litConst)<<8;
9704 // if ( ( AOP_TYPE(result) == AOP_REG && AOP(result)->aopu.aop_reg[0]->isLitConst ) )
9705 // emitcode("ERROR","");
9708 if(AOP_TYPE(result)==AOP_REG) {
9709 switch(AOP(result)->aopu.aop_reg[0]->rIdx) {
9710 case X_IDX: idx_reg='x'; break;
9711 case Y_IDX: idx_reg='y'; break;
9712 case A_IDX: idx_reg='A'; break;
9713 default: idx_reg='E'; break;
9715 } else {
9716 idx_reg='M';
9719 if(AOP_TYPE(right)==AOP_REG
9720 && AOP(right)->aopu.aop_reg[0]->rIdx == Y_IDX )
9721 src_reg_is_y = true;
9723 if(idx_reg=='A' || idx_reg=='M') {
9724 if(src_reg_is_y) {
9725 px = storeRegTempIfSurv(m6502_reg_x);
9726 loadRegFromAop(m6502_reg_x, AOP(result), 0 );
9727 idx_reg='x';
9728 } else {
9729 py = storeRegTempIfSurv(m6502_reg_y);
9730 loadRegFromAop(m6502_reg_y, AOP(result), 0 );
9731 idx_reg='y';
9735 loadRegFromAop (m6502_reg_a, AOP (right), 0);
9737 emit6502op("sta", "(%s+%d+0x%04x),%c",
9738 rematOffset, litOffset, hi_offset, idx_reg );
9740 pullOrFreeReg(m6502_reg_a, pa);
9741 loadOrFreeRegTemp(m6502_reg_x,px);
9742 loadOrFreeRegTemp(m6502_reg_y,py);
9744 goto release;
9747 // general case
9748 emitComment (TRACEGEN|VVDBG," %s - general case ", __func__);
9749 int aloc, xloc, yloc;
9750 deadA = m6502_reg_a->isDead;
9752 aloc = _G.tempOfs;
9753 if(IS_AOP_WITH_A(AOP(right))) needpulla = storeRegTempIfUsed (m6502_reg_a);
9754 else needpulla = storeRegTempIfSurv (m6502_reg_a);
9755 xloc = _G.tempOfs;
9756 if(IS_AOP_WITH_X(AOP(right)) && AOP_TYPE(result)==AOP_SOF ) needpullx = storeRegTempIfUsed (m6502_reg_x);
9757 else needpullx = storeRegTempIfSurv (m6502_reg_x);
9758 yloc = _G.tempOfs;
9759 if(IS_AOP_WITH_Y(AOP(right))) needpully = storeRegTempIfUsed (m6502_reg_y);
9760 else needpully = storeRegTempIfSurv (m6502_reg_y);
9762 /* if bit-field then pack */
9763 if (bit_field)
9765 emitComment (TRACEGEN|VVDBG," %s : bitvar", __func__ );
9767 if(needpulla && IS_AOP_WITH_A (AOP(right)))
9768 loadRegTempAt(m6502_reg_a, aloc);
9769 genPackBits (result, left, operandType (result)->next, right);
9770 goto release;
9773 #if 0
9774 bool savea = false;
9775 if(!m6502_reg_a->isFree) {
9776 savea = true;
9777 transferRegReg(m6502_reg_a, m6502_reg_y, true);
9779 #endif
9781 int yoff = setupDPTR(result, litOffset, rematOffset, false);
9782 if(IS_AOP_WITH_A (AOP(right))) {
9783 loadRegTempAt(m6502_reg_a, aloc);
9785 if(IS_AOP_WITH_X (AOP(right))) {
9786 if(needpullx) loadRegTempAt(m6502_reg_x, xloc);
9788 if(IS_AOP_WITH_Y (AOP(right))) {
9789 if(needpully) loadRegTempAt(m6502_reg_y, yloc);
9792 for (offset=0; offset<size; offset++) {
9793 loadRegFromAop (m6502_reg_a, AOP (right), offset);
9794 loadRegFromConst(m6502_reg_y, yoff + offset);
9795 emit6502op("sta", INDFMT_IY);
9798 release:
9799 freeAsmop (result, NULL);
9800 freeAsmop (right, NULL);
9802 loadOrFreeRegTemp (m6502_reg_y, needpully);
9803 loadOrFreeRegTemp (m6502_reg_x, needpullx);
9805 if(deadA) {
9806 if(needpulla) loadRegTemp(NULL);
9807 m6502_freeReg(m6502_reg_a);
9808 } else {
9809 loadOrFreeRegTemp (m6502_reg_a, needpulla);
9814 // TODO: genIfx sometimes does a cmp #0 but has flags already, peephole might fix
9815 /**************************************************************************
9816 * genIfx - generate code for Ifx statement
9817 *************************************************************************/
9818 static void genIfx (iCode * ic, iCode * popIc)
9820 operand *cond = IC_COND (ic);
9822 emitComment (TRACEGEN, __func__);
9824 aopOp (cond, ic);
9826 /* If the condition is a literal, we can just do an unconditional */
9827 /* branch or no branch */
9828 if (AOP_TYPE (cond) == AOP_LIT) {
9829 unsigned long long lit = ullFromVal (AOP (cond)->aopu.aop_lit);
9830 freeAsmop (cond, NULL);
9832 if (lit) {
9833 if (IC_TRUE (ic))
9834 emitBranch ("jmp", IC_TRUE (ic));
9835 } else {
9836 if (IC_FALSE (ic))
9837 emitBranch ("jmp", IC_FALSE (ic));
9839 ic->generated = 1;
9840 return;
9843 /* evaluate the operand */
9844 if (AOP_TYPE (cond) != AOP_CRY) {
9845 emitComment (TRACEGEN|VVDBG, " genIfx - !AOP_CRY");
9846 asmopToBool (AOP (cond), false);
9848 /* the result is now in the z flag bit */
9849 freeAsmop (cond, NULL);
9851 // TODO: redundant bne/beq
9852 emitComment (TRACEGEN|VVDBG, " genIfx - call jump");
9853 genIfxJump (ic, "z");
9855 ic->generated = 1;
9858 /**************************************************************************
9859 * genAddrOf - generates code for address of
9860 *************************************************************************/
9861 static void genAddrOf (iCode * ic)
9863 operand *result = IC_RESULT (ic);
9864 symbol *sym = OP_SYMBOL (IC_LEFT (ic));
9865 int size, offset;
9866 bool needpulla, needpullx;
9867 struct dbuf_s dbuf;
9869 emitComment (TRACEGEN, __func__);
9871 aopOp (result, ic);
9873 /* if the operand is on the stack then we
9874 need to get the stack offset of this
9875 variable */
9876 if (sym->onStack)
9878 needpulla = pushRegIfSurv (m6502_reg_a);
9879 needpullx = pushRegIfSurv (m6502_reg_x);
9880 /* if it has an offset then we need to compute it */
9881 doTSX();
9882 offset = _G.stackOfs + _G.tsxStackPushes + _G.stackPushes + sym->stack + 1;
9883 if(smallAdjustReg(m6502_reg_x, offset))
9884 offset=0;
9885 transferRegReg (m6502_reg_x, m6502_reg_a, true);
9886 if (offset)
9888 emitSetCarry(0);
9889 emit6502op ("adc", IMMDFMT, offset&0xff);
9891 if(IS_AOP_XA(AOP(result)))
9893 loadRegFromConst(m6502_reg_x, 0x01); // stack top = 0x100
9895 else
9897 storeRegToAop (m6502_reg_a, AOP (result), 0);
9898 loadRegFromConst(m6502_reg_a, 0x01); // stack top = 0x100
9899 storeRegToAop (m6502_reg_a, AOP (result), 1);
9901 pullOrFreeReg (m6502_reg_x, needpullx);
9902 pullOrFreeReg (m6502_reg_a, needpulla);
9903 goto release;
9906 /* object not on stack then we need the name */
9907 size = AOP_SIZE (result);
9908 offset = 0;
9910 while (size--)
9912 dbuf_init (&dbuf, 64);
9913 switch (offset)
9915 case 0:
9916 dbuf_printf (&dbuf, "#%s", sym->rname);
9917 break;
9918 case 1:
9919 dbuf_printf (&dbuf, "#>%s", sym->rname);
9920 break;
9921 default:
9922 dbuf_printf (&dbuf, "#0");
9924 storeImmToAop (dbuf_detach_c_str (&dbuf), AOP (result), offset++);
9927 release:
9928 freeAsmop (result, NULL);
9931 /**************************************************************************
9932 * genAssignLit - Try to generate code for literal assignment.
9933 * result and right should already be asmOped
9934 *************************************************************************/
9935 static bool genAssignLit (operand * result, operand * right)
9937 char assigned[8];
9938 unsigned char value[sizeof(assigned)];
9939 int size;
9940 int offset,offset2;
9942 emitComment (TRACEGEN, __func__);
9944 /* Make sure this is a literal assignment */
9945 if (AOP_TYPE (right) != AOP_LIT)
9946 return false;
9948 /* The general case already handles register assignment well */
9949 if (AOP_TYPE (result) == AOP_REG)
9950 return false;
9952 /* Some hardware registers require MSB to LSB assignment order */
9953 /* so don't optimize the assignment order if volatile */
9954 if (isOperandVolatile (result, false))
9955 return false;
9957 /* Make sure the assignment is not larger than we can handle */
9958 size = AOP_SIZE (result);
9959 if (size > sizeof(assigned))
9960 return false;
9962 for (offset=0; offset<size; offset++)
9964 assigned[offset] = 0;
9965 value[offset] = byteOfVal (AOP (right)->aopu.aop_lit, offset);
9968 for (offset=0; offset<size; offset++)
9970 if (assigned[offset])
9971 continue;
9972 storeConstToAop (value[offset], AOP (result), offset);
9973 assigned[offset] = 1;
9974 // look for duplicates
9975 if ((AOP_TYPE (result) != AOP_DIR )) {
9976 for(offset2=offset+1; offset2<size; offset2++)
9978 if(value[offset]==value[offset2])
9980 storeConstToAop (value[offset], AOP (result), offset2);
9981 assigned[offset2] = 1;
9987 return true;
9990 /**************************************************************************
9991 * genAssign - generate code for assignment
9992 *************************************************************************/
9993 static void
9994 genAssign (iCode * ic)
9996 operand *result, *right;
9998 emitComment (TRACEGEN, __func__);
10000 result = IC_RESULT (ic);
10001 right = IC_RIGHT (ic);
10003 aopOp (right, ic);
10004 aopOp (result, ic);
10005 printIC(ic);
10008 if (!genAssignLit (result, right))
10009 genCopy (result, right);
10011 freeAsmop (right, NULL);
10012 freeAsmop (result, NULL);
10015 /**************************************************************************
10016 * genJumpTab - generates code for jump table
10017 *************************************************************************/
10018 static void genJumpTab (iCode * ic)
10020 symbol *jtab;
10021 symbol *jtablo = safeNewiTempLabel (NULL);
10022 symbol *jtabhi = safeNewiTempLabel (NULL);
10024 emitComment (TRACEGEN, __func__);
10026 aopOp (IC_JTCOND (ic), ic);
10028 // TODO
10030 bool needpulla = pushRegIfSurv (m6502_reg_a);
10031 // use X or Y for index?
10032 bool needpullind = false;
10033 reg_info* indreg;
10034 if (IS_AOP_X (AOP (IC_JTCOND (ic)))) {
10035 indreg = m6502_reg_x;
10036 } else if (IS_AOP_Y (AOP (IC_JTCOND (ic)))) {
10037 indreg = m6502_reg_y;
10038 } else {
10039 indreg = m6502_reg_x->isFree ? m6502_reg_x : m6502_reg_y;
10040 needpullind = pushRegIfSurv (indreg);
10041 /* get the condition into indreg */
10042 loadRegFromAop (indreg, AOP (IC_JTCOND (ic)), 0);
10044 freeAsmop (IC_JTCOND (ic), NULL);
10046 if (indreg == m6502_reg_x)
10048 emit6502op ("lda", "%05d$,x", safeLabelNum (jtablo));
10049 storeRegTemp (m6502_reg_a, true);
10050 emit6502op ("lda", "%05d$,x", safeLabelNum (jtabhi));
10051 storeRegTemp (m6502_reg_a, true);
10053 else
10055 emit6502op ("lda", "%05d$,y", safeLabelNum (jtablo));
10056 storeRegTemp (m6502_reg_a, true);
10057 emit6502op ("lda", "%05d$,y", safeLabelNum (jtabhi));
10058 storeRegTemp (m6502_reg_a, true);
10060 loadRegTemp(NULL);
10061 loadRegTemp(NULL);
10062 if (needpullind) pullReg(indreg);
10063 if (needpulla) pullReg(m6502_reg_a);
10064 emit6502op ("jmp", TEMPFMT_IND, _G.tempOfs);
10066 m6502_dirtyReg (m6502_reg_a);
10067 m6502_dirtyReg (m6502_reg_x);
10068 m6502_dirtyReg (m6502_reg_y);
10069 m6502_freeReg (m6502_reg_a);
10070 m6502_freeReg (m6502_reg_x);
10071 m6502_freeReg (m6502_reg_y);
10074 /* now generate the jump labels */
10075 safeEmitLabel (jtablo);
10076 // FIXME: add this to gen6502op
10077 for (jtab = setFirstItem (IC_JTLABELS (ic)); jtab; jtab = setNextItem (IC_JTLABELS (ic)))
10079 emitcode (".db", "%05d$", labelKey2num (jtab->key));
10080 regalloc_dry_run_cost_bytes++;
10082 safeEmitLabel (jtabhi);
10083 for (jtab = setFirstItem (IC_JTLABELS (ic)); jtab; jtab = setNextItem (IC_JTLABELS (ic)))
10085 emitcode (".db", ">%05d$", labelKey2num (jtab->key));
10086 regalloc_dry_run_cost_bytes++;
10090 /**************************************************************************
10091 * genCast - generate code for casting
10092 *************************************************************************/
10093 static void genCast (iCode * ic)
10095 operand *right = IC_RIGHT (ic);
10096 operand *result = IC_RESULT (ic);
10097 sym_link *resulttype = operandType (result);
10098 sym_link *righttype = operandType (right);
10099 int size, offset;
10100 bool signExtend;
10101 bool save_a;
10103 emitComment (TRACEGEN, __func__);
10105 /* if they are equivalent then do nothing */
10106 if (operandsEqu (result, right))
10107 return;
10109 unsigned topbytemask = (IS_BITINT (resulttype) && (SPEC_BITINTWIDTH (resulttype) % 8)) ?
10110 (0xff >> (8 - SPEC_BITINTWIDTH (resulttype) % 8)) : 0xff;
10112 aopOp (right, ic);
10113 aopOp (result, ic);
10114 printIC(ic);
10116 emitComment (TRACEGEN|VVDBG, " genCast - size %d -> %d", right?AOP_SIZE(right):0, result?AOP_SIZE(result):0);
10118 // Cast to _BitInt can require mask of top byte.
10119 if (IS_BITINT (resulttype) && (SPEC_BITINTWIDTH (resulttype) % 8) && bitsForType (resulttype) < bitsForType (righttype)) {
10120 save_a = false;
10121 genCopy (result, right);
10122 if (result->aop->type != AOP_REG || result->aop->aopu.aop_reg[result->aop->size - 1] != m6502_reg_a) {
10123 save_a = result->aop->aopu.aop_reg[0] == m6502_reg_a || !m6502_reg_a->isDead;
10124 if (save_a)
10125 pushReg(m6502_reg_a, false);
10126 loadRegFromAop (m6502_reg_a, result->aop, result->aop->size - 1);
10128 emit6502op ("and", IMMDFMT, topbytemask);
10129 if (!SPEC_USIGN (resulttype))
10131 // sign extend
10132 symbol *tlbl = safeNewiTempLabel (NULL);
10133 pushReg (m6502_reg_a, true);
10134 emit6502op ("and", IMMDFMT, 1u << (SPEC_BITINTWIDTH (resulttype) % 8 - 1));
10135 emitBranch ("beq", tlbl);
10136 pullReg (m6502_reg_a);
10137 emit6502op ("ora", IMMDFMT, ~topbytemask & 0xff);
10138 pushReg (m6502_reg_a, true);
10139 safeEmitLabel (tlbl);
10140 pullReg (m6502_reg_a);
10142 storeRegToAop (m6502_reg_a, result->aop, result->aop->size - 1);
10143 if (save_a)
10144 pullReg (m6502_reg_a);
10145 goto release;
10148 if (IS_BOOL (operandType (result)))
10150 bool needpulla = pushRegIfSurv (m6502_reg_a);
10151 asmopToBool (AOP (right), true);
10152 storeRegToAop (m6502_reg_a, AOP (result), 0);
10153 pullOrFreeReg (m6502_reg_a, needpulla);
10154 goto release;
10157 // if the result size is <= source just copy
10158 // FIXME: investigate special case SOF as it genereates worse code
10159 if(AOP_SIZE (result) <= AOP_SIZE (right)
10160 && !(AOP_TYPE (result) == AOP_SOF && AOP_TYPE (result) == AOP_SOF) )
10162 genCopy (result, right);
10163 goto release;
10166 signExtend = AOP_SIZE (result) > AOP_SIZE (right) && !IS_BOOL (righttype) && IS_SPEC (righttype) && !SPEC_USIGN (righttype);
10167 bool masktopbyte = IS_BITINT (resulttype) && (SPEC_BITINTWIDTH (resulttype) % 8) && SPEC_USIGN (resulttype);
10169 if(IS_AOP_XA(AOP(right)))
10171 storeRegToFullAop (m6502_reg_xa, AOP (result), signExtend);
10172 goto release;
10175 if (IS_AOP_XA (AOP (result) ) && AOP_SIZE (right) == 1 )
10177 genCopy (result, right);
10178 if (signExtend)
10180 symbol *tlbl = safeNewiTempLabel (NULL);
10181 emitCpz(A_IDX);
10182 emitBranch ("bpl", tlbl);
10183 storeConstToAop (0xff, AOP (result), 1);
10184 safeEmitLabel (tlbl);
10185 m6502_dirtyReg(m6502_reg_x);
10187 goto release;
10190 wassert (AOP (result)->type != AOP_REG);
10192 save_a = !m6502_reg_a->isDead && signExtend;
10193 if (save_a)
10194 pushReg(m6502_reg_a, true);
10196 offset = 0;
10197 size = AOP_SIZE (right);
10198 if (AOP_SIZE (result) < size)
10199 size = AOP_SIZE (result);
10200 while (size) {
10201 if (size == 1 && signExtend) {
10202 loadRegFromAop (m6502_reg_a, AOP (right), offset);
10203 storeRegToAop (m6502_reg_a, AOP (result), offset);
10204 offset++;
10205 size--;
10206 } else if ((size > 2 || size >= 2 && !signExtend) && m6502_reg_y->isDead && m6502_reg_x->isDead &&
10207 (AOP_TYPE (right) == AOP_IMMD || IS_MOS65C02 && AOP_TYPE (right) == AOP_EXT) &&
10208 (AOP_TYPE (result) == AOP_DIR || IS_MOS65C02 && AOP_TYPE (result) == AOP_EXT)) {
10209 // FIXME: the above exception for 65C02 is likely incorrect
10210 loadRegFromAop (m6502_reg_yx, AOP (right), offset);
10211 storeRegToAop (m6502_reg_yx, AOP (result), offset);
10212 offset += 2;
10213 size -= 2;
10214 } else {
10215 transferAopAop (AOP (right), offset, AOP (result), offset);
10216 offset++;
10217 size--;
10221 size = AOP_SIZE (result) - offset;
10222 if (size && !signExtend)
10224 while (size--)
10225 storeConstToAop (0, AOP (result), offset++);
10227 else if (size)
10229 signExtendA();
10230 while (size--)
10232 if (!size && masktopbyte)
10233 emit6502op ("and", IMMDFMT, topbytemask);
10234 storeRegToAop (m6502_reg_a, AOP (result), offset++);
10238 if (save_a)
10239 pullReg(m6502_reg_a);
10241 release:
10242 freeAsmop (right, NULL);
10243 freeAsmop (result, NULL);
10246 /**************************************************************************
10247 * genReceive - generate code for a receive iCode
10248 *************************************************************************/
10249 static void genReceive (iCode * ic)
10251 operand *result = IC_RESULT (ic);
10252 int size;
10253 int offset;
10254 bool delayed_x = false;
10256 emitComment (TRACEGEN, __func__);
10258 aopOp (result, ic);
10259 size = AOP_SIZE (result);
10260 offset = 0;
10262 emitComment (TRACEGEN|VVDBG, " %s: size=%d regmask=%x",
10263 __func__, size, AOP (result)->regmask );
10265 if (ic->argreg && IS_AOP_YX (AOP (result)) && (offset + (ic->argreg - 1)) == 0)
10267 transferRegReg (m6502_reg_xa, m6502_reg_yx, true);
10269 else if (ic->argreg)
10271 while (size--)
10273 if (AOP_TYPE (result) == AOP_REG && !(offset + (ic->argreg - 1))
10274 && AOP (result)->aopu.aop_reg[0]->rIdx == X_IDX && size)
10276 storeRegTemp (m6502_reg_a, true);
10277 delayed_x = true;
10279 else
10280 transferAopAop (m6502_aop_pass[offset + (ic->argreg - 1)], 0, AOP (result), offset);
10281 // FIXME: this freereg is likely wrong
10282 if (m6502_aop_pass[offset]->type == AOP_REG)
10283 m6502_freeReg (m6502_aop_pass[offset]->aopu.aop_reg[0]);
10284 offset++;
10288 if (delayed_x)
10289 loadRegTemp (m6502_reg_x);
10291 freeAsmop (result, NULL);
10294 // support routine for genDummyRead
10295 static void dummyRead (iCode* ic, operand* op, reg_info* reg)
10297 if (op && IS_SYMOP (op)) {
10298 aopOp (op, ic);
10299 int size = AOP_SIZE (op);
10300 for (int offset=0; offset<size; offset++) {
10301 loadRegFromAop (reg, AOP (op), offset);
10303 freeAsmop (op, NULL);
10307 /**************************************************************************
10308 * genDummyRead - generate code for dummy read of volatiles
10309 *************************************************************************/
10310 static void genDummyRead (iCode * ic)
10312 bool needpulla = false;
10314 emitComment (TRACEGEN, __func__);
10316 reg_info* reg = getFreeByteReg();
10317 if (!reg) {
10318 needpulla = pushRegIfSurv (m6502_reg_a);
10319 reg = m6502_reg_a;
10322 // TODO: use BIT? STA?
10323 dummyRead(ic, IC_RIGHT(ic), reg);
10324 dummyRead(ic, IC_LEFT(ic), reg);
10326 pullOrFreeReg (reg, needpulla);
10329 /**************************************************************************
10330 * genCritical - generate code for start of a critical sequence
10331 *************************************************************************/
10332 static void genCritical (iCode * ic)
10334 operand *result = IC_RESULT (ic);
10335 emitComment (TRACEGEN, __func__);
10337 if (result)
10338 aopOp (result, ic);
10340 emit6502op ("php", "");
10341 emit6502op ("sei", "");
10343 if (result) {
10344 emit6502op ("plp", "");
10345 m6502_dirtyReg (m6502_reg_a);
10346 storeRegToAop (m6502_reg_a, AOP (result), 0);
10349 m6502_freeReg (m6502_reg_a);
10350 if (result)
10351 freeAsmop (result, NULL);
10354 /**************************************************************************
10355 * genEndCritical - generate code for end of a critical sequence
10356 *************************************************************************/
10357 static void genEndCritical (iCode * ic)
10359 operand *right = IC_RIGHT (ic);
10360 emitComment (TRACEGEN, __func__);
10362 if (right)
10364 aopOp (right, ic);
10365 loadRegFromAop (m6502_reg_a, AOP (right), 0);
10366 emit6502op ("pha", "");
10367 m6502_freeReg (m6502_reg_a);
10368 freeAsmop (right, NULL);
10370 emit6502op ("plp", "");
10373 static void updateiTempRegisterUse (operand * op)
10375 symbol *sym;
10377 if (IS_ITEMP (op)) {
10378 sym = OP_SYMBOL (op);
10379 if (!sym->isspilt)
10381 /* If only used by IFX, there might not be any register assigned */
10382 int i;
10383 for(i = 0; i < sym->nRegs; i++)
10384 if (sym->regs[i])
10385 m6502_useReg (sym->regs[i]);
10390 /**************************************************************************
10391 * genm6502iCode - generate code for M6502 based controllers for a
10392 * single iCode instruction
10393 *************************************************************************/
10394 static void genm6502iCode (iCode *ic)
10396 operand *right = IC_RIGHT (ic);
10397 operand *left = IC_LEFT (ic);
10398 operand *result = IC_RESULT (ic);
10400 int i;
10401 reg_info *reg;
10403 initGenLineElement ();
10404 genLine.lineElement.ic = ic;
10406 #if 0
10407 if (!regalloc_dry_run)
10408 printf ("ic %d op %d stack pushed %d\n", ic->key, ic->op, G.stack.pushed);
10409 #endif
10411 if (resultRemat (ic))
10413 if (!regalloc_dry_run)
10414 emitComment(TRACEGEN, "skipping iCode since result will be rematerialized");
10415 return;
10418 if (ic->generated)
10420 if (!regalloc_dry_run)
10421 emitComment(TRACEGEN, "skipping generated iCode");
10422 return;
10425 for (i = 0; i < m6502_nRegs; i++)
10427 reg = m6502_regWithIdx (i);
10428 m6502_freeReg (reg);
10429 // if (reg->aop)
10430 // emitcode ("", "; %s = %s offset %d", reg->name, aopName (reg->aop), reg->aopofs);
10431 // FIXME: removing the following generates worse code
10432 if (regalloc_dry_run)
10433 m6502_dirtyReg (reg);
10434 // reg->isLitConst = 0; //
10437 if (ic->op == IFX)
10438 updateiTempRegisterUse (IC_COND (ic));
10439 else if (ic->op == JUMPTABLE)
10440 updateiTempRegisterUse (IC_JTCOND (ic));
10441 else if (ic->op == RECEIVE)
10443 // FIXME: should add entry icode to this.
10444 m6502_useReg (m6502_reg_a);
10445 m6502_useReg (m6502_reg_x); // TODO: x really is free if function only receives 1 byte
10447 else
10449 if (POINTER_SET (ic))
10450 updateiTempRegisterUse (result);
10451 updateiTempRegisterUse (left);
10452 updateiTempRegisterUse (right);
10455 // FIXME: this is broken when reordering registers
10456 for (i = A_IDX; i <= Y_IDX; i++)
10458 if (bitVectBitValue (ic->rSurv, i))
10460 m6502_regWithIdx (i)->isDead = false;
10461 m6502_regWithIdx (i)->isFree = false;
10462 } else
10463 m6502_regWithIdx (i)->isDead = true;
10466 /* depending on the operation */
10467 switch (ic->op)
10469 case '!':
10470 genNot (ic);
10471 break;
10473 case '~':
10474 genCpl (ic);
10475 break;
10477 case UNARYMINUS:
10478 genUminus (ic);
10479 break;
10481 case IPUSH:
10482 genIpush (ic);
10483 break;
10485 case IPUSH_VALUE_AT_ADDRESS:
10486 genPointerPush (ic);
10487 break;
10489 case CALL:
10490 genCall (ic);
10491 break;
10493 case PCALL:
10494 genPcall (ic);
10495 break;
10497 case FUNCTION:
10498 genFunction (ic);
10499 break;
10501 case ENDFUNCTION:
10502 genEndFunction (ic);
10503 break;
10505 case RETURN:
10506 genRet (ic);
10507 break;
10509 case LABEL:
10510 genLabel (ic);
10511 break;
10513 case GOTO:
10514 genGoto (ic);
10515 break;
10517 case '+':
10518 genPlus (ic);
10519 break;
10521 case '-':
10522 genMinus (ic);
10523 break;
10525 case '*':
10526 genMult (ic);
10527 break;
10529 case '/':
10530 genDiv (ic);
10531 break;
10533 case '%':
10534 genMod (ic);
10535 break;
10537 case '>':
10538 case '<':
10539 case LE_OP:
10540 case GE_OP:
10541 genCmp (ic, ifxForOp (result, ic));
10542 break;
10544 case NE_OP:
10545 case EQ_OP:
10546 genCmpEQorNE (ic, ifxForOp (result, ic));
10547 break;
10549 case AND_OP:
10550 genAndOp (ic);
10551 break;
10553 case OR_OP:
10554 genOrOp (ic);
10555 break;
10557 case '^':
10558 genXor (ic, ifxForOp (result, ic));
10559 break;
10561 case '|':
10562 genOr (ic, ifxForOp (result, ic));
10563 break;
10565 case BITWISEAND:
10566 genAnd (ic, ifxForOp (result, ic));
10567 break;
10569 case INLINEASM:
10570 m6502_genInline (ic);
10571 break;
10573 case GETABIT:
10574 wassertl (0, "Unimplemented iCode: GETABIT");
10575 break;
10577 case GETBYTE:
10578 genGetByte(ic);
10579 break;
10581 case GETWORD:
10582 genGetWord(ic);
10583 break;
10585 case ROT:
10586 genRot (ic);
10587 break;
10589 case LEFT_OP:
10590 genLeftShift (ic);
10591 break;
10593 case RIGHT_OP:
10594 genRightShift (ic);
10595 break;
10597 case GET_VALUE_AT_ADDRESS:
10598 genPointerGet (ic, NULL); // TODO? ifxForOp (result, ic));
10599 break;
10601 case SET_VALUE_AT_ADDRESS:
10602 genPointerSet (ic);
10603 break;
10605 case '=':
10606 if (POINTER_SET (ic))
10607 genPointerSet (ic);
10608 else
10609 genAssign (ic);
10610 break;
10612 case IFX:
10613 genIfx (ic, NULL);
10614 break;
10616 case ADDRESS_OF:
10617 genAddrOf (ic);
10618 break;
10620 case JUMPTABLE:
10621 genJumpTab (ic);
10622 break;
10624 case CAST:
10625 genCast (ic);
10626 break;
10628 case RECEIVE:
10629 genReceive (ic);
10630 break;
10632 case SEND:
10633 if (!regalloc_dry_run)
10634 addSet (&_G.sendSet, ic);
10635 else
10637 set * sendSet = NULL;
10638 addSet (&sendSet, ic);
10639 genSend (sendSet);
10640 deleteSet (&sendSet);
10642 break;
10644 case DUMMY_READ_VOLATILE:
10645 genDummyRead (ic);
10646 break;
10648 case CRITICAL:
10649 genCritical (ic);
10650 break;
10652 case ENDCRITICAL:
10653 genEndCritical (ic);
10654 break;
10656 default:
10657 emitcode("ERROR", "; Unimplemented iCode (%x)", ic->op);
10658 printIC(ic);
10659 // wassertl (0, "Unknown iCode");
10663 static void init_aop_pass(void)
10665 if (m6502_aop_pass[0])
10666 return;
10668 m6502_aop_pass[0] = newAsmop (AOP_REG);
10669 m6502_aop_pass[0]->size = 1;
10670 m6502_aop_pass[0]->aopu.aop_reg[0] = m6502_reg_a;
10671 m6502_aop_pass[1] = newAsmop (AOP_REG);
10672 m6502_aop_pass[1]->size = 1;
10673 m6502_aop_pass[1]->aopu.aop_reg[0] = m6502_reg_x;
10674 m6502_aop_pass[2] = newAsmop (AOP_DIR);
10675 m6502_aop_pass[2]->size = 1;
10676 m6502_aop_pass[2]->aopu.aop_dir = "___SDCC_m6502_ret2";
10677 m6502_aop_pass[3] = newAsmop (AOP_DIR);
10678 m6502_aop_pass[3]->size = 1;
10679 m6502_aop_pass[3]->aopu.aop_dir = "___SDCC_m6502_ret3";
10680 m6502_aop_pass[4] = newAsmop (AOP_DIR);
10681 m6502_aop_pass[4]->size = 1;
10682 m6502_aop_pass[4]->aopu.aop_dir = "___SDCC_m6502_ret4";
10683 m6502_aop_pass[5] = newAsmop (AOP_DIR);
10684 m6502_aop_pass[5]->size = 1;
10685 m6502_aop_pass[5]->aopu.aop_dir = "___SDCC_m6502_ret5";
10686 m6502_aop_pass[6] = newAsmop (AOP_DIR);
10687 m6502_aop_pass[6]->size = 1;
10688 m6502_aop_pass[6]->aopu.aop_dir = "___SDCC_m6502_ret6";
10689 m6502_aop_pass[7] = newAsmop (AOP_DIR);
10690 m6502_aop_pass[7]->size = 1;
10691 m6502_aop_pass[7]->aopu.aop_dir = "___SDCC_m6502_ret7";
10694 float drym6502iCode (iCode *ic)
10696 regalloc_dry_run = true;
10697 regalloc_dry_run_cost_bytes = 0;
10698 regalloc_dry_run_cost_cycles = 0;
10700 init_aop_pass();
10702 genm6502iCode (ic);
10704 destroy_line_list ();
10705 /*freeTrace (&_G.trace.aops);*/
10707 wassert (regalloc_dry_run);
10709 int byte_cost_weight = 1;
10710 if(optimize.codeSize) byte_cost_weight*=2;
10711 if(!optimize.codeSpeed) byte_cost_weight*=4;
10713 return ((float)regalloc_dry_run_cost_bytes * byte_cost_weight + regalloc_dry_run_cost_cycles * ic->count);
10716 /**************************************************************************
10717 * genm6502Code - generate code for for a block of instructions
10718 *************************************************************************/
10719 void
10720 genm6502Code (iCode *lic)
10722 iCode *ic;
10723 int cln = 0;
10724 int clevel = 0;
10725 int cblock = 0;
10727 regalloc_dry_run = false;
10729 m6502_dirtyReg (m6502_reg_a);
10730 m6502_dirtyReg (m6502_reg_y);
10731 m6502_dirtyReg (m6502_reg_x);
10732 _G.tempOfs = 0;
10734 /* print the allocation information */
10735 if (allocInfo && currFunc)
10736 printAllocInfo (currFunc, codeOutBuf);
10737 /* if debug information required */
10738 if (options.debug && currFunc && !regalloc_dry_run)
10739 debugFile->writeFunction (currFunc, lic);
10741 if (options.debug && !regalloc_dry_run)
10742 debugFile->writeFrameAddress (NULL, NULL, 0); /* have no idea where frame is now */
10744 init_aop_pass();
10746 /* Generate Code for all instructions */
10747 for (ic = lic; ic; ic = ic->next) {
10748 initGenLineElement ();
10750 genLine.lineElement.ic = ic;
10752 if (ic->level != clevel || ic->block != cblock) {
10753 if (options.debug)
10754 debugFile->writeScope (ic);
10755 clevel = ic->level;
10756 cblock = ic->block;
10759 if (ic->lineno && cln != ic->lineno) {
10760 if (options.debug)
10761 debugFile->writeCLine (ic);
10763 if (!options.noCcodeInAsm)
10764 emitComment (ALWAYS, "%s: %d: %s", ic->filename, ic->lineno, printCLine (ic->filename, ic->lineno));
10765 cln = ic->lineno;
10768 regalloc_dry_run_cost_bytes = 0;
10769 regalloc_dry_run_cost_cycles = 0;
10771 if (options.iCodeInAsm)
10773 char regsSurv[4];
10774 const char *iLine;
10776 regsSurv[0] = (bitVectBitValue (ic->rSurv, A_IDX)) ? 'a' : '-';
10777 regsSurv[1] = (bitVectBitValue (ic->rSurv, Y_IDX)) ? 'y' : '-';
10778 regsSurv[2] = (bitVectBitValue (ic->rSurv, X_IDX)) ? 'x' : '-';
10779 regsSurv[3] = 0;
10780 iLine = printILine (ic);
10781 emitComment (ALWAYS, " [%s] ic:%d: %s", regsSurv, ic->key, iLine);
10782 dbuf_free (iLine);
10785 genm6502iCode(ic);
10786 emitComment (TRACEGEN, "Raw cost for generated ic %d : (%d, %f) count=%f", ic->key, regalloc_dry_run_cost_bytes, regalloc_dry_run_cost_cycles, ic->count);
10788 // TODO: should be asserts?
10790 if (!m6502_reg_a->isFree)
10791 emitComment (REGOPS|VVDBG, " forgot to free a");
10792 if (!m6502_reg_x->isFree)
10793 emitComment (REGOPS|VVDBG, " forgot to free x");
10794 if (!m6502_reg_y->isFree)
10795 emitComment (REGOPS|VVDBG, " forgot to free y");
10796 if (!m6502_reg_yx->isFree)
10797 emitComment (REGOPS|VVDBG, " forgot to free yx");
10798 if (!m6502_reg_xa->isFree)
10799 emitComment (REGOPS|VVDBG, " forgot to free xa");
10802 if (_G.tempOfs != 0)
10803 emitcode("ERROR", "; forgot to free temp stack (%d)", _G.tempOfs);
10806 if (options.debug)
10807 debugFile->writeFrameAddress (NULL, NULL, 0); /* have no idea where frame is now */
10809 /* now we are ready to call the
10810 peep hole optimizer */
10811 if (!options.nopeep)
10812 peepHole (&genLine.lineHead);
10814 /* now do the actual printing */
10815 printLine (genLine.lineHead, codeOutBuf);
10817 /* destroy the line list */
10818 destroy_line_list ();