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)
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
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 -------------------------------------------------------------------------*/
31 #include "dbuf_string.h"
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
56 static bool regalloc_dry_run
;
57 static unsigned int regalloc_dry_run_cost_bytes
;
58 static float regalloc_dry_run_cost_cycles
;
63 unsigned char literalValue
;
66 // keep this in sync with _temp.s in the library
67 #define NUM_TEMP_REGS 8
75 // int baseStackPushes;
78 unsigned carryValid
:1;
81 struct attr_t tempAttr
[NUM_TEMP_REGS
];
82 struct attr_t DPTRAttr
[2];
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];
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)
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__
);
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
)
195 lastpos
=(*arg
)?strlen(arg
)-1:0;
197 switch (opcode
->type
)
199 case M6502OP_INH
: /* Inherent addressing mode */
202 case M6502OP_BR
: /* Branch (1 byte signed offset) */
203 if(opcode
->name
[0]=='r'&&opcode
->name
[1]=='t') // rti and rts
210 case M6502OP_BBR
: /* Branch on bit (1 byte signed offset) */
212 case M6502OP_RMW
: /* read/modify/write instructions */
213 if (!strcmp(arg
, "a")) /* accumulator */
215 if (arg
[0] == '*') /* Zero page */
217 if(lastpos
>2 && arg
[lastpos
-1]!=',' && arg
[lastpos
]=='x' )
219 return 6; /* absolute */
221 case M6502OP_REG
: /* standard instruction */
224 if (arg
[0] == '#') /* Immediate addressing mode */
226 if (arg
[0] == '*') { /* Zero page */
227 if(arg
[lastpos
]=='x' || arg
[lastpos
]=='y')
231 if (arg
[0] == '[') { /* indirect */
232 if(arg
[lastpos
]==']')
236 return 4; /* Otherwise, must be absolute addressing mode */
239 if (arg
[0] == '*') { /* Zero page */
240 if(arg
[lastpos
]=='x' || arg
[lastpos
]=='y')
244 if (arg
[0] == '[') /* indirect */
246 if(arg
[lastpos
]=='x' || arg
[lastpos
]=='y')
251 if(opcode
->name
[1]=='s') return 6;
252 if(arg
[0]=='[') return 5;
255 werror (E_INTERNAL_ERROR
, __FILE__
, __LINE__
, "unknown instruction type in m6502_opcodeSize");
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 *************************************************************************/
267 emitComment (unsigned int level
, const char *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
);
285 /**************************************************************************
286 * Returns register state as a string
289 *************************************************************************/
290 const char * regInfoStr()
292 static char outstr
[40];
293 char regstring
[3][10];
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
);
342 /**************************************************************************
343 * Returns operand information in the passed string
346 *************************************************************************/
348 opInfo(char str
[64], operand
*op
)
354 if(AOP(op
)) size
=AOP_SIZE(op
);
355 if(AOP(op
)) type
=aopName(AOP(op
));
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");
369 snprintf(str
, 64, "???");
375 /**************************************************************************
376 * Prints iCode debug information
379 *************************************************************************/
383 operand
*left
, *right
, *result
;
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 *************************************************************************/
405 emit6502op (const char *inst
, const char *fmt
, ...)
407 static char verboseFmt
[512];
413 const m6502opcodedata
*opcode
= m6502_getOpcodeData(inst
);
415 if(fmt
==0) werror (E_INTERNAL_ERROR
, __FILE__
, __LINE__
, "NULL fmt in emit6502op");
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)
432 // mark the destination register dirty as necessary
433 // transfers are handled in the instruction generator
434 switch (opcode
->type
)
437 if(fmt
[0]!='#' || !isdigit(fmt
[1]))
438 m6502_dirtyReg(dst_reg
);
440 case M6502OP_REG
: // target is accumulator
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
;
450 if(!strcmp(inst
,"ora"))
452 dst_reg
->litConst
|=b
;
455 if(!strcmp(inst
,"eor"))
457 dst_reg
->litConst
^=b
;
462 if(dst_reg
) m6502_dirtyReg (dst_reg
);
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
;
475 if(!strcmp(inst
,"clc"))
481 if(!strcmp(inst
,"sec"))
488 case M6502OP_RMW
: // target is accumulator
489 if (!strcmp(fmt
, "a"))
491 m6502_dirtyReg (m6502_reg_a
);
494 // FIXME: add 65c02 INC/DEC A literal
496 case M6502OP_SPL
: // stack pull
499 case M6502OP_SPH
: // stack push
502 case M6502OP_IDD
: // index decrement
503 if(dst_reg
->isLitConst
)
505 if(dst_reg
->aop
==&tsxaop
)
508 case M6502OP_IDI
: // index increment
509 if(dst_reg
->isLitConst
)
511 if(dst_reg
->aop
==&tsxaop
)
514 case M6502OP_BR
: // add penalty for taken branches
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);
529 emitcode("ERROR","Unimplemented opcode %s", inst
);
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
;
538 if (options
.verboseAsm
)
547 snprintf(dstring
[0], 64, "sz=%d cl=%f p=%f",
548 isize
, cycles
, probability
);
551 if (DBG_MSG
®ALLOC
)
553 snprintf(dstring
[1], 64, "%s",
556 if (DBG_MSG
&TRACE_STACK
)
558 snprintf(dstring
[2], 64, "stkpush=%d",
562 snprintf(verboseFmt
, 512, "%s \t; %s %s %s",
563 fmt
, dstring
[0], dstring
[1], dstring
[2]);
564 va_emitcode (inst
, verboseFmt
, ap
);
568 va_emitcode (inst
, fmt
, ap
);
573 /**************************************************************************
574 * m6502_unimplemented
576 *************************************************************************/
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;
585 regalloc_dry_run_cost_bytes
= 0;
586 regalloc_dry_run_cost_cycles
= 0;
590 /**************************************************************************
593 *************************************************************************/
595 emitSignedBranch (bool gt
, bool eq
, symbol
* tlbl
)
597 symbol
*tlbl2
= safeNewiTempLabel (NULL
);
598 symbol
*tlbl3
= safeNewiTempLabel (NULL
);
601 emit6502op ("beq", "%05d$", safeLabelNum (tlbl
));
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 /**************************************************************************
615 *************************************************************************/
617 emitUnsignedBranch (bool gt
, bool eq
, symbol
* tlbl
)
619 symbol
*tlbl2
= safeNewiTempLabel (NULL
);
622 emit6502op ("beq", "%05d$", safeLabelNum (tlbl
));
624 emit6502op ("beq", "%05d$", safeLabelNum (tlbl2
));
625 emit6502op (gt
? "bcs" : "bcc", "%05d$", safeLabelNum (tlbl
));
626 safeEmitLabel (tlbl2
);
629 /**************************************************************************
632 *************************************************************************/
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
);
662 if (!IS_MOS65C02
&& !strcmp(branchop
, "bra"))
664 emit6502op (branchop
, "%05d$", safeLabelNum (tlbl
));
668 /**************************************************************************
669 * emitSetCarry - emit CLC/SEC if necessary
671 * @param c carry value to set
672 *************************************************************************/
676 if(_G
.carryValid
&& _G
.carry
==c
)
679 emit6502op("sec", "");
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 *************************************************************************/
691 emitCpz (int reg_idx
)
693 if(_G
.lastflag
==reg_idx
) return false;
698 emit6502op("cmp", "#0x00");
701 emit6502op("cpx", "#0x00");
704 emit6502op("cpy", "#0x00");
707 emitcode ("ERROR", "illegal %d reg_idx in emitCpz", reg_idx
);
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 *************************************************************************/
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
)
729 if (n
<= -4 || n
>= 4)
736 rmwWithReg ("dec", reg
); /* 1 byte, 2 cycles */
741 rmwWithReg ("inc", reg
); /* 1 byte, 2 cycles */
747 /**************************************************************************
748 * Associate the current code location with a debugger symbol
749 *************************************************************************/
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 *************************************************************************/
768 transferRegReg (reg_info
*sreg
, reg_info
*dreg
, bool freesrc
)
774 /* Nothing to do if no destination. */
778 /* But it's definitely an error if there's no source. */
781 // emitcode("ERROR","%s: src reg is null", __func__);
782 werror (E_INTERNAL_ERROR
, __FILE__
, __LINE__
, "NULL sreg in transferRegReg");
786 emitComment (REGOPS
, " %s(%s,%s)", __func__
, sreg
->name
, dreg
->name
);
787 emitComment (REGOPS
, " %s %s", __func__
, regInfoStr() );
792 if (srcidx
== dstidx
)
794 emitComment (REGOPS
|VVDBG
, " %s: sameregs", __func__
);
799 // TODO: make sure regs are killed if clobbered
804 case Y_IDX
: /* Y to A */
805 emit6502op ("tya", "");
807 case X_IDX
: /* X to A */
808 emit6502op ("txa", "");
817 case A_IDX
: /* A to X */
818 emit6502op ("tax", "");
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", "");
828 emit6502op ("phy", "");
829 emit6502op ("plx", "");
831 storeRegTemp (m6502_reg_y
, false);
832 loadRegTemp (m6502_reg_x
);
843 case A_IDX
: /* A to Y */
844 emit6502op ("tay", "");
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", "");
859 emit6502op ("phx", "");
860 emit6502op ("ply", "");
864 storeRegTemp (m6502_reg_x
, false);
865 loadRegTemp (m6502_reg_y
);
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
);
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
);
901 if(error
) emitcode("ERROR", "bad combo in transferRegReg 0x%02x -> 0x%02x", srcidx
, dstidx
);
907 dreg
->isLitConst
= sreg
->isLitConst
;
908 dreg
->litConst
= sreg
->litConst
;
912 m6502_dirtyReg (dreg
);
915 dreg
->aop
= sreg
->aop
;
916 dreg
->aopofs
= sreg
->aopofs
;
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 *************************************************************************/
929 /* there is no frame unless there is a function */
933 if (options
.debug
&& !regalloc_dry_run
)
934 debugFile
->writeFrameAddress (NULL
, m6502_reg_sp
, 1 + _G
.stackOfs
+ _G
.stackPushes
);
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
);
950 emit6502op(op
, IMMDFMT
, _G
.tempAttr
[offset
].literalValue
);
955 storeRegTemp (reg_info
* reg
, bool freereg
)
957 storeRegTempi (reg
, freereg
, false);
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 *************************************************************************/
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?";
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
);
995 storeRegTempi (m6502_reg_a
, freereg
, force
);
996 storeRegTempi (m6502_reg_x
, freereg
, force
);
999 storeRegTempi (m6502_reg_x
, freereg
, force
);
1000 storeRegTempi (m6502_reg_y
, freereg
, force
);
1003 emitcode("ERROR", "%s : bad reg %02x (%s)", __func__
, regidx
, reg
->name
);
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 *************************************************************************/
1021 storeRegTempIfSurv (reg_info
*reg
)
1025 storeRegTemp (reg
, true);
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 *************************************************************************/
1038 storeRegTempIfUsed (reg_info
*reg
)
1042 storeRegTemp (reg
, true);
1048 /**************************************************************************
1049 * Load register from the REGTEMP stack at an arbitrary offset
1051 * @param reg pointer for the register to save
1052 *************************************************************************/
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
);
1070 loadOp
[2]=reg
->name
[0];
1071 emit6502op (loadOp
, TEMPFMT
, offset
);
1074 emitcode("ERROR","loadRegTempAt called with illegal regidx %d", regidx
);
1078 m6502_dirtyReg (reg
);
1081 /**************************************************************************
1082 * Load register from the REGTEMP stack.
1084 * @param reg pointer for the register to save
1085 *************************************************************************/
1087 loadRegTemp (reg_info
* reg
)
1089 // pop off stack, unused
1101 loadRegTempAt(reg
, --_G
.tempOfs
);
1104 loadRegTemp(m6502_reg_x
);
1105 loadRegTemp(m6502_reg_a
);
1108 loadRegTemp(m6502_reg_y
);
1109 loadRegTemp(m6502_reg_x
);
1112 emitcode("ERROR", "bad reg in loadRegTemp()");
1116 // FIXME: figure out if register pairs are literals
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 *************************************************************************/
1129 loadOrFreeRegTemp (reg_info
* reg
, bool needpull
)
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 *************************************************************************/
1145 loadRegTempNoFlags (reg_info
* reg
, bool needpull
)
1149 int tempflag
=_G
.lastflag
;
1150 emit6502op("php", "");
1152 emit6502op("plp", "");
1153 _G
.lastflag
=tempflag
;
1157 m6502_freeReg (reg
);
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 *************************************************************************/
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":"");
1181 emit6502op ("pha", "");
1187 emit6502op ("phx", "");
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
);
1201 emit6502op ("phy", "");
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
);
1212 // little-endian order
1214 pushReg(m6502_reg_x
, freereg
);
1215 pushReg(m6502_reg_a
, freereg
);
1218 pushReg(m6502_reg_y
, freereg
);
1219 pushReg(m6502_reg_x
, freereg
);
1222 emitcode("ERROR", " %s: bad reg idx: 0x%02x", __func__
, regidx
);
1226 m6502_freeReg (reg
);
1229 /**************************************************************************
1230 * pullReg - Pull register reg off the stack.
1231 *************************************************************************/
1233 pullReg (reg_info
* reg
)
1235 int regidx
= reg
->rIdx
;
1237 emitComment (REGOPS
, __func__
);
1241 emit6502op ("pla", "");
1247 emit6502op ("plx", "");
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);
1262 emit6502op ("ply", "");
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);
1274 // little-endian order
1276 pullReg(m6502_reg_a
);
1277 pullReg(m6502_reg_x
);
1280 pullReg(m6502_reg_x
);
1281 pullReg(m6502_reg_y
);
1284 emitcode("ERROR", " %s: bad reg idx: 0x%02x", __func__
, regidx
);
1288 m6502_dirtyReg (reg
);
1291 /**************************************************************************
1292 * pullNull - Discard n bytes off the top of the stack
1293 *************************************************************************/
1297 emitComment (REGOPS
, __func__
);
1298 if(n
< 0) emitcode("ERROR", "pullNull called with negative parameter");
1303 /**************************************************************************
1304 * pushRegIfUsed - Push register reg if marked in use. Returns true if the
1305 * push was performed, false otherwise.
1306 *************************************************************************/
1308 pushRegIfUsed (reg_info
*reg
)
1312 pushReg (reg
, true);
1319 /**************************************************************************
1320 * pushRegIfSurv - Push register reg if marked surviving. Returns true if
1321 * the push was performed, false otherwise.
1322 *************************************************************************/
1324 pushRegIfSurv (reg_info
*reg
)
1328 pushReg (reg
, true);
1334 /**************************************************************************
1335 * pullOrFreeReg - If needpull is true, register reg is pulled from the
1336 * stack. Otherwise register reg is marked as free.
1337 *************************************************************************/
1339 pullOrFreeReg (reg_info
* reg
, bool needpull
)
1344 m6502_freeReg (reg
);
1347 /**************************************************************************
1348 * adjustStack - Adjust the stack pointer by n bytes.
1349 *************************************************************************/
1350 // TODO: optimize for 65C02
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);
1365 emit6502op ("adc", IMMDFMT
, n
&0xff);
1366 transferRegReg (m6502_reg_a
, m6502_reg_x
, true);
1367 emit6502op ("txs", "");
1368 _G
.stackPushes
-= n
;
1370 loadRegTemp(m6502_reg_xa
);
1371 // loadOrFreeRegTemp(m6502_reg_xa, needloada);
1375 emit6502op ("pha", ""); /* 1 byte, 3 cycles */
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);
1389 emit6502op ("pla", ""); /* 1 byte, 4 cycles */
1392 loadOrFreeRegTemp(m6502_reg_a
, needloada
);
1397 /**************************************************************************
1398 * Return a string with debugging information about an asmop.
1399 *************************************************************************/
1401 aopName (asmop
* aop
)
1403 static char buffer
[276];
1407 return "(asmop*)NULL";
1412 sprintf (buf
, "IMMD(%s)", aop
->aopu
.aop_immd
);
1415 sprintf (buf
, "LIT(%s)", aopLiteral (aop
->aopu
.aop_lit
, 0));
1418 sprintf (buf
, "DIR(%s)", aop
->aopu
.aop_dir
);
1421 sprintf (buf
, "EXT(%s)", aop
->aopu
.aop_dir
);
1424 sprintf (buf
, "SOF(%s@%d)", OP_SYMBOL (aop
->op
)->name
, aop
->aopu
.aop_stk
);
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
: "-");
1436 sprintf (buf
, "?%d", aop
->type
);
1445 canBitOp (const operand
* aop
)
1447 switch (AOP_TYPE(aop
))
1456 // TODO: ind,x for 65c02?
1463 /**************************************************************************
1464 * Load register reg from logical offset loffset of aop.
1465 * For multi-byte registers, loffset is of the lsb reg.
1466 *************************************************************************/
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);
1481 /* If operand is volatile, we cannot optimize. */
1482 if (!aop
->op
|| isOperandVolatile (aop
->op
, false))
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
))
1491 emitComment (REGOPS
, " already had correct value for %s", reg
->name
);
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);
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);
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);
1532 if (aop
->type
== AOP_REG
)
1534 if (loffset
< aop
->size
)
1535 transferRegReg (aop
->aopu
.aop_reg
[loffset
], reg
, false);
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
);
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
);
1564 if (IS_AOP_XA (aop
))
1566 else if (IS_AOP_YX (aop
))
1567 transferRegReg (m6502_reg_yx
, m6502_reg_xa
, false);
1570 emitComment (REGOPS
, "XA");
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);
1583 loadRegFromAop (m6502_reg_x
, aop
, loffset
+ 1);
1584 loadRegFromAop (m6502_reg_a
, aop
, loffset
);
1589 if (IS_AOP_YX (aop
))
1591 else if (IS_AOP_XA (aop
))
1592 transferRegReg (m6502_reg_xa
, m6502_reg_yx
, false);
1595 loadRegFromAop (m6502_reg_x
, aop
, loffset
);
1596 loadRegFromAop (m6502_reg_y
, aop
, loffset
+ 1);
1604 /**************************************************************************
1605 * Find any free 8-bit register
1606 *************************************************************************/
1610 if (m6502_reg_a
->isFree
)
1612 else if (m6502_reg_x
->isFree
)
1614 else if (m6502_reg_y
->isFree
)
1620 // TODO: move more to this one?
1624 if (m6502_reg_a
->isDead
)
1626 else if (m6502_reg_x
->isDead
)
1628 else if (m6502_reg_y
->isDead
)
1634 /**************************************************************************
1635 * storeRegToAop - Store register reg to logical offset loffset of aop.
1636 * For multi-byte registers, loffset is of the lsb reg.
1637 *************************************************************************/
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
)
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
)
1662 transferRegReg (reg
, aop
->aopu
.aop_reg
[loffset
], true);
1665 if (IS_AOP_YX (aop
))
1667 transferRegReg (reg
, m6502_reg_yx
, false);
1672 emitcode("ERROR", "%s: unsupported reg in AOP (XA)", __func__
);
1676 if (IS_AOP_XA (aop
))
1678 transferRegReg (reg
, m6502_reg_xa
, false);
1683 emitcode("ERROR", "%s: unsupported reg in AOP (YX)", __func__
);
1690 // handle ZP and absolute addresses
1691 if (aop
->type
== AOP_DIR
|| aop
->type
== AOP_EXT
)
1696 emit6502op ("sta", aopAdrStr (aop
, loffset
, true));
1699 emit6502op ("stx", aopAdrStr (aop
, loffset
, true));
1702 emit6502op ("sty", aopAdrStr (aop
, loffset
, true));
1705 emit6502op ("sta", aopAdrStr (aop
, loffset
, true));
1706 emit6502op ("stx", aopAdrStr (aop
, loffset
+1, true));
1709 emit6502op ("stx", aopAdrStr (aop
, loffset
, true));
1710 emit6502op ("sty", aopAdrStr (aop
, loffset
+1, true));
1717 if (aop
->type
== AOP_SOF
)
1719 // int xofs = STACK_TOP + _G.stackOfs + _G.tsxStackPushes + aop->aopu.aop_stk + loffset + 1;
1724 emitComment (TRACE_STACK
|VVDBG
, " %s: A [%d, %d]",
1725 __func__
, aop
->aopu
.aop_stk
, loffset
);
1726 needpullx
= storeRegTempIfUsed(m6502_reg_x
);
1728 emit6502op ("sta", aopAdrStr (aop
, loffset
, false));
1729 loadOrFreeRegTemp(m6502_reg_x
, needpullx
);
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
);
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);
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
);
1753 needpulla
= pushRegIfSurv(m6502_reg_a
);
1754 needpullx
= storeRegTempIfSurv(m6502_reg_x
);
1755 transferRegReg (m6502_reg_x
, m6502_reg_a
, true);
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
);
1764 emitcode("ERROR", "%s: bad reg 0x%02x", __func__
, regidx
);
1768 /* Disable the register tracking for now */
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");
1795 reg
->aopofs
= loffset
;
1800 /**************************************************************************
1801 * loadRegFromConst - Load register reg from constant c.
1802 *************************************************************************/
1804 loadRegFromConst (reg_info
* reg
, int c
)
1806 emitComment (REGOPS
, __func__
);
1808 switch (reg
->rIdx
) {
1811 if (reg
->isLitConst
&& reg
->litConst
== c
)
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);
1819 emit6502op ("lda", IMMDFMT
, c
);
1824 if (reg
->isLitConst
) {
1825 if (reg
->litConst
== c
)
1827 if (((reg
->litConst
+ 1) & 0xff) == c
)
1829 emit6502op ("inx", "");
1832 if (((reg
->litConst
- 1) & 0xff) == c
)
1834 emit6502op ("dex", "");
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);
1848 emit6502op ("ldx", IMMDFMT
, c
);
1853 if (reg
->isLitConst
)
1855 if (reg
->litConst
== c
)
1857 if (((reg
->litConst
+ 1) & 0xff) == c
)
1859 emit6502op ("iny", "");
1862 if (((reg
->litConst
- 1) & 0xff) == c
)
1864 emit6502op ("dey", "");
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);
1878 emit6502op ("ldy", IMMDFMT
, c
);
1883 loadRegFromConst (m6502_reg_x
, c
>> 8);
1884 loadRegFromConst (m6502_reg_a
, c
);
1888 loadRegFromConst (m6502_reg_y
, c
>> 8);
1889 loadRegFromConst (m6502_reg_x
, c
);
1892 emitcode("ERROR", "bad reg 0x%02x in %s", reg
->rIdx
, __func__
);
1896 m6502_dirtyReg (reg
);
1897 reg
->isLitConst
= 1;
1903 /**************************************************************************
1904 * loadRegFromImm - Load register reg from immediate value c.
1905 *************************************************************************/
1907 loadRegFromImm (reg_info
* reg
, char * c
)
1909 emitComment (REGOPS
, __func__
);
1912 werror (E_INTERNAL_ERROR
, __FILE__
, __LINE__
, "loadRegFromImm called with a null arg pointer");
1917 switch (reg
->rIdx
) {
1919 emit6502op ("lda", "#%s", c
);
1922 emit6502op ("ldx", "#%s", c
);
1925 emit6502op ("ldy", "#%s", c
);
1928 emit6502op ("ldx", "#%s >> 8", c
);
1929 emit6502op ("lda", "#%s", c
);
1932 emit6502op ("ldy", "#%s >> 8", c
);
1933 emit6502op ("ldx", "#%s", c
);
1936 werror (E_INTERNAL_ERROR
, __FILE__
, __LINE__
, "Bad rIdx in loadRegFromConst");
1939 m6502_dirtyReg (reg
);
1943 /**************************************************************************
1944 * storeConstToAop - Store constant c to logical offset loffset of
1946 *************************************************************************/
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);
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
);
1964 if (m6502_reg_x
->isLitConst
&& m6502_reg_x
->litConst
== c
)
1966 storeRegToAop (m6502_reg_x
, aop
, loffset
);
1969 if (m6502_reg_y
->isLitConst
&& m6502_reg_y
->litConst
== c
)
1971 storeRegToAop (m6502_reg_y
, aop
, loffset
);
1978 if (loffset
> (aop
->size
- 1))
1980 loadRegFromConst (aop
->aopu
.aop_reg
[loffset
], c
);
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
);
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
);
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 *************************************************************************/
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);
2035 switch (aop
->type
) {
2037 if (loffset
> (aop
->size
- 1))
2039 loadRegFromImm (aop
->aopu
.aop_reg
[loffset
], c
);
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
);
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
);
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
);
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 *************************************************************************/
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
)
2102 while (loffset
< size
)
2103 storeConstToAop (0, aop
, loffset
++);
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
);
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 *************************************************************************/
2136 storeRegToFullAop (reg_info
*reg
, asmop
*aop
, bool isSigned
)
2138 int regidx
= reg
->rIdx
;
2139 int size
= aop
->size
;
2141 emitComment (TRACE_AOP
, __func__
);
2149 if ( IS_AOP_XA(aop
) && regidx
==A_IDX
)
2151 loadRegFromConst(m6502_reg_x
,0);
2154 symbol
*tlbl
= safeNewiTempLabel (NULL
);
2155 emit6502op("cmp","#0x80");
2156 emitBranch ("bcc", tlbl
);
2157 loadRegFromConst(m6502_reg_x
, 0xff);
2158 safeEmitLabel (tlbl
);
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
);
2174 storeRegToAop (m6502_reg_a
, aop
, 0);
2176 storeRegToAop (reg
, aop
, 0);
2177 if(aop
->type
!=AOP_SOF
) {
2178 storeRegSignToUpperAop (m6502_reg_x
, aop
, 2, isSigned
);
2180 loadRegFromAop(m6502_reg_a
, aop
, 1);
2181 storeRegSignToUpperAop (m6502_reg_a
, aop
, 2, isSigned
);
2187 storeRegToAop (m6502_reg_x
, aop
, 0);
2189 storeRegToAop (reg
, aop
, 0);
2190 storeRegSignToUpperAop (m6502_reg_y
, aop
, 2, isSigned
);
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 *************************************************************************/
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");
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
)
2225 if (srcaop
->stacked
&& srcaop
->stk_aop
[srcofs
])
2227 transferAopAop (srcaop
->stk_aop
[srcofs
], 0, dstaop
, dstofs
);
2231 if (dstaop
->stacked
&& dstaop
->stk_aop
[srcofs
])
2233 transferAopAop (srcaop
, srcofs
, dstaop
->stk_aop
[dstofs
], 0);
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
)
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);
2253 if (srcaop
->type
== AOP_LIT
)
2255 storeConstToAop (byteOfVal (srcaop
->aopu
.aop_lit
, srcofs
), dstaop
, dstofs
);
2258 if (dstaop
->type
== AOP_REG
)
2260 reg
= dstaop
->aopu
.aop_reg
[dstofs
];
2263 else if ((srcaop
->type
== AOP_REG
) && (srcaop
->aopu
.aop_reg
[srcofs
]))
2265 reg
= srcaop
->aopu
.aop_reg
[srcofs
];
2269 // TODO: pick reg based on if can load op?
2272 reg
= getFreeByteReg();
2275 pushReg (m6502_reg_a
, true);
2281 emitComment (TRACE_AOP
|VVDBG
, " %s: general case", __func__
);
2283 loadRegFromAop (reg
, srcaop
, srcofs
);
2284 storeRegToAop (reg
, dstaop
, dstofs
);
2287 pullOrFreeReg (reg
, needpula
);
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 *************************************************************************/
2297 static asmop
* forceStackedAop (asmop
* aop
, bool copyOrig
)
2299 reg_info
*reg
= NULL
;
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
));
2309 reg
= getFreeByteReg();
2312 storeRegTemp(reg
, true);
2316 for (offset
=0; offset
<newaop
->size
; offset
++) {
2317 asmop
*aopsof
= newAsmop (AOP_SOF
);
2320 loadRegFromAop (reg
, aop
, offset
);
2321 pushReg (reg
, false);
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
);
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 *************************************************************************/
2349 accopWithAop (char *accop
, asmop
*aop
, int loffset
)
2352 emitComment (TRACE_AOP
, __func__
);
2354 if ( !strcmp(accop
, "ucp") ) {
2355 // special case for unsigned compare
2360 if (aop
->stacked
&& aop
->stk_aop
[loffset
]) {
2361 accopWithAop (accop
, aop
->stk_aop
[loffset
], 0);
2365 if (aop
->type
== AOP_DUMMY
)
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);
2377 emit6502op (accop
, "#0x00");
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") )
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
2396 * Supports: com, dec, inc, lsl, lsr, neg, rol, ror
2397 *************************************************************************/
2399 rmwWithReg (char *rmwop
, reg_info
* reg
)
2401 if (reg
->rIdx
== A_IDX
)
2403 if (!strcmp(rmwop
, "inc"))
2407 emit6502op (rmwop
, "a");
2412 emit6502op ("adc", "#0x01");
2415 else if (!strcmp(rmwop
, "dec"))
2419 emit6502op (rmwop
, "a");
2424 emit6502op ("sbc", "#0x01");
2427 else if (!strcmp(rmwop
, "com"))
2429 emit6502op ("eor", "#0xff");
2431 else if (!strcmp(rmwop
, "neg"))
2433 emit6502op ("eor", "#0xff");
2435 emit6502op ("adc", "#0x01");
2437 else if (!strcmp(rmwop
, "asr"))
2439 emit6502op ("cmp", "#0x80");
2440 emit6502op ("ror", "a");
2442 else if (!strcmp(rmwop
, "bit"))
2444 emitcode("ERROR", " %s : called with unsupported opcode: %s", __func__
, rmwop
);
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", "");
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", "");
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
);
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 *************************************************************************/
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);
2517 rmwWithReg (rmwop
, aop
->aopu
.aop_reg
[loffset
]);
2521 aopAdrPrepare(aop
, loffset
);
2523 if (!strcmp("asr", rmwop
))
2525 emit6502op("pha", "");
2526 emit6502op("lda", aopAdrStr(aop
, loffset
, false)); // load
2527 emit6502op("cmp", "#0x80");
2528 emit6502op("pla", "");
2531 emit6502op (rmwop
, aopAdrStr(aop
, loffset
, false));
2532 aopAdrUnprepare(aop
, loffset
);
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
);
2557 /* If the offset is small enough, fall through to default case */
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 *************************************************************************/
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
)
2582 emit6502op ("sta", DPTRFMT
, dofs
);
2585 emit6502op ("stx", DPTRFMT
, dofs
);
2588 emit6502op ("sty", DPTRFMT
, dofs
);
2591 emitcode("ERROR"," %s: illegal register index %d", __func__
, regidx
);
2595 _G
.DPTRAttr
[dofs
].isLiteral
=reg
->isLitConst
;
2596 _G
.DPTRAttr
[dofs
].literalValue
=reg
->litConst
;
2601 /**************************************************************************
2602 * sets up DPTR for a indexed operation
2603 * clobbers A if savea==false and clobbers Y if savea==true
2604 *************************************************************************/
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] == '#')
2613 if (rematOfs
&& !rematOfs
[0])
2616 /* force offset to signed 16-bit range */
2618 if (offset
& 0x8000)
2619 offset
= 0x10000 - offset
;
2620 // offset = offset - 0x10000;
2624 emitcode("ERROR", " %s: op is null", __func__
);
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);
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
);
2652 emitComment (TRACEGEN
|VVDBG
, " %s: general case", __func__
);
2654 if(!rematOfs
) rematOfs
="0";
2656 if(savea
) transferRegReg(m6502_reg_a
, m6502_reg_y
, true);
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
);
2671 /**************************************************************************
2672 * newAsmop - creates a new asmOp
2673 *************************************************************************/
2675 newAsmop (short type
)
2678 // TODO: are these ever freed?
2679 aop
= Safe_calloc (1, sizeof (asmop
));
2686 /**************************************************************************
2687 * operandConflictsWithYX - true if operand in y and/or x register
2688 *************************************************************************/
2690 operandConflictsWithYX (operand
*op
)
2697 sym
= OP_SYMBOL (op
);
2700 for(i
= 0; i
< sym
->nRegs
; i
++)
2701 if (sym
->regs
[i
] == m6502_reg_y
|| sym
->regs
[i
] == m6502_reg_x
)
2709 /**************************************************************************
2710 * operandConflictsWithX - true if operand in x register
2711 *************************************************************************/
2713 operandConflictsWithX (operand
*op
)
2720 sym
= OP_SYMBOL (op
);
2723 for(i
= 0; i
< sym
->nRegs
; i
++)
2724 if (sym
->regs
[i
] == m6502_reg_x
)
2732 /**************************************************************************
2733 * operandOnStack - returns True if operand is on the stack
2734 *************************************************************************/
2736 operandOnStack(operand
*op
)
2740 if (!op
|| !IS_SYMOP (op
))
2742 sym
= OP_SYMBOL (op
);
2743 if (!sym
->isspilt
&& sym
->onStack
)
2747 sym
= sym
->usl
.spillLoc
;
2748 if (sym
&& sym
->onStack
)
2754 /**************************************************************************
2755 * tsxUseful - returns True if tsx could help at least one
2756 * anticipated stack references
2757 *************************************************************************/
2759 tsxUseful(const iCode
*ic
)
2761 operand
*right
= IC_RIGHT(ic
);
2762 operand
*left
= IC_LEFT(ic
);
2763 operand
*result
= IC_RESULT(ic
);
2768 if (result
&& operandSize (result
) < 2 && operandOnStack (result
))
2775 while (ic
&& uses
< 1) {
2776 if (ic
->op
== IFX
) {
2777 if (operandOnStack (IC_COND (ic
)))
2778 uses
+= operandSize(IC_COND (ic
));
2780 } else if (ic
->op
== JUMPTABLE
) {
2781 if (operandOnStack (IC_JTCOND (ic
)))
2784 } else if (ic
->op
== ADDRESS_OF
) {
2785 if (operandOnStack (right
))
2787 } else if (ic
->op
== LABEL
|| ic
->op
== GOTO
|| ic
->op
== CALL
|| ic
->op
== PCALL
)
2789 else if (POINTER_SET (ic
) || POINTER_GET (ic
))
2792 if (operandConflictsWithYX (result
))
2794 if (operandOnStack (left
))
2795 uses
+= operandSize (left
);
2796 if (operandOnStack (right
))
2797 uses
+= operandSize (right
);
2798 if (operandOnStack (result
))
2799 uses
+= operandSize (result
);
2812 emitComment (TRACE_STACK
|VVDBG
, "%s: stackOfs=%d tsx=%d stackpush=%d",
2813 __func__
, _G
.stackOfs
, _G
.tsxStackPushes
, _G
.stackPushes
);
2816 if (m6502_reg_x
->aop
== &tsxaop
)
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()
2832 storeRegTemp (m6502_reg_x
, true); // TODO: only when used?
2833 // TODO: if X is free should we call doTSX() to mark X=S?
2835 emit6502op ("stx", BASEPTR
);
2836 _G
.baseStackPushes
= _G
.stackPushes
;
2837 loadRegTemp (m6502_reg_x
);
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
)
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 */
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
;
2876 /* if it is on the stack */
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
);
2886 if (!regalloc_dry_run
&& m6502_reg_x
->isFree
&& m6502_reg_x
->aop
!= &tsxaop
) {
2887 if (!m6502_reg_x
->isDead
)
2889 if (ic
->op
== IFX
&& operandConflictsWithX (IC_COND (ic
)))
2891 else if (ic
->op
== JUMPTABLE
&& operandConflictsWithX (IC_JTCOND (ic
)))
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
))
2898 if (ic
->op
== ADDRESS_OF
)
2900 if (operandConflictsWithX (IC_LEFT (ic
)))
2902 if (operandConflictsWithX (IC_RIGHT (ic
)))
2906 /* It's safe to use tsx here. */
2907 if (!tsxUseful (ic
))
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
);
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
);
2931 /**************************************************************************
2932 * aopForRemat - rematerializes an object
2933 *************************************************************************/
2934 static asmop
* aopForRemat (symbol
* sym
)
2936 iCode
*ic
= sym
->rematiCode
;
2941 fprintf (stderr
, "Symbol %s to be rematerialized, but has no rematiCode.\n", sym
->name
);
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
;
2956 ic
= OP_SYMBOL (IC_LEFT (ic
))->rematiCode
;
2959 if (ic
->op
== ADDRESS_OF
) {
2961 SNPRINTF (buffer
, sizeof (buffer
),
2962 "(%s %c 0x%04x)", OP_SYMBOL (IC_LEFT (ic
))->rname
, val
>= 0 ? '+' : '-', abs (val
) & 0xffff);
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
));
2972 SNPRINTF (buffer
, sizeof (buffer
), "0x%04x", val
);
2973 aop
= newAsmop (AOP_LIT
);
2974 aop
->aopu
.aop_lit
= constVal (buffer
);
2976 werror (E_INTERNAL_ERROR
, __FILE__
, __LINE__
, "unexpected rematerialization");
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
;
2991 /* if they have registers in common */
2992 if (!IS_SYMOP (op1
) || !IS_SYMOP (op2
))
2995 sym1
= OP_SYMBOL (op1
);
2996 sym2
= OP_SYMBOL (op2
);
2998 if (sym1
->nRegs
== 0 || sym2
->nRegs
== 0)
3001 for (i
= 0; i
< sym1
->nRegs
; i
++)
3007 for (j
= 0; j
< sym2
->nRegs
; j
++)
3012 if (sym2
->regs
[j
] == sym1
->regs
[i
])
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
))
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
)
3040 /* if they are the same */
3044 /* if they have the same rname */
3045 if (sym1
->rname
[0] && sym2
->rname
[0] && strcmp (sym1
->rname
, sym2
->rname
) == 0)
3048 /* if left is a tmp & right is not */
3049 if (IS_ITEMP (op1
) && !IS_ITEMP (op2
) && sym1
->isspilt
&& (sym1
->usl
.spillLoc
== sym2
))
3052 if (IS_ITEMP (op2
) && !IS_ITEMP (op1
) && sym2
->isspilt
&& sym1
->level
> 0 && (sym2
->usl
.spillLoc
== sym1
))
3058 /**************************************************************************
3059 * sameRegs - two asmops have the same registers
3060 *************************************************************************/
3062 sameRegs (asmop
* aop1
, asmop
* aop2
)
3069 // if (aop1->size != aop2->size)
3072 if (aop1
->type
== aop2
->type
) {
3073 switch (aop1
->type
) {
3075 for (i
= 0; i
< aop1
->size
; i
++)
3076 if (aop1
->aopu
.aop_reg
[i
] != aop2
->aopu
.aop_reg
[i
])
3080 return (aop1
->aopu
.aop_stk
== aop2
->aopu
.aop_stk
);
3082 // if (regalloc_dry_run)
3083 // return false; // TODO: why?
3085 return (!strcmp (aop1
->aopu
.aop_dir
, aop2
->aopu
.aop_dir
));
3094 /**************************************************************************
3095 * aopCanIncDec - asmop is EXT or DIR or X/Y
3097 *************************************************************************/
3099 aopCanIncDec (asmop
* aop
)
3104 if(aop
->aopu
.aop_reg
[0]->rIdx
== A_IDX
) return IS_MOS65C02
;
3114 /**************************************************************************
3115 * aopCanShift - asmop is EXT or DIR or A
3117 *************************************************************************/
3118 static bool aopCanShift (asmop
* aop
)
3120 switch (aop
->type
) {
3122 return ((aop
->size
== 1) && (aop
->aopu
.aop_reg
[0]->rIdx
== A_IDX
));
3132 /**************************************************************************
3133 * aopOp - allocates an asmop for an operand
3134 *************************************************************************/
3136 aopOp (operand
*op
, const iCode
* ic
)
3142 emitComment (TRACE_AOP
, __func__
);
3147 /* if already has an asmop */
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
;
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
));
3180 aop
->op
= op
; // asmopToBool needs the op to check thetype of the literal.
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);
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
));
3210 //printf ("new symbol %s\n", OP_SYMBOL (op)->name);
3211 //printf (" with size = %d\n", aop->size);
3215 /* this is a temporary : this has
3221 e) can be a return use only */
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
);
3237 // printf("checking spilt\n");
3238 /* if it is spilt then two situations
3240 b) has a spill location */
3241 if (sym
->isspilt
|| sym
->nRegs
== 0)
3243 // printf("checking remat\n");
3244 /* rematerialize it NOW */
3247 sym
->aop
= op
->aop
= aop
= aopForRemat (sym
);
3248 aop
->size
= getSize (sym
->type
);
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 */
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
);
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
);
3293 //printf ("spill symbol %s\n", OP_SYMBOL (op)->name);
3294 //printf (" with size = %d\n", aop->size);
3298 /* else must be a dummy iTemp */
3299 sym
->aop
= op
->aop
= aop
= newAsmop (AOP_DUMMY
);
3300 aop
->size
= getSize (sym
->type
);
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;
3322 /**************************************************************************
3323 * freeAsmop - free up the asmop given to an operand
3324 *************************************************************************/
3326 freeAsmop (operand
* op
, asmop
* aaop
)
3347 emitComment (TRACE_AOP
, " freeAsmop restoring stacked %s", aopName (aop
));
3350 for (loffset
= 0; loffset
< aop
->size
; loffset
++)
3351 if (aop
->stk_aop
[loffset
]) {
3352 transferAopAop (aop
->stk_aop
[loffset
], 0, aop
, loffset
);
3355 pullNull (stackAdjust
);
3359 /* all other cases just dealloc */
3362 if (IS_SYMOP (op
)) {
3363 OP_SYMBOL (op
)->aop
= NULL
;
3364 /* if the symbol has a spill */
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
)
3379 asmop
*newaop
= NULL
;
3380 sym_link
*type
, *etype
;
3384 emitComment (TRACE_AOP
, " aopDerefAop(%s)", aopName (aop
));
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
);
3393 /* we have to go by the storage class */
3394 p_type
= PTR_TYPE (SPEC_OCLS (etype
));
3400 switch (aop
->type
) {
3402 if (p_type
== POINTER
)
3403 newaop
= newAsmop (AOP_DIR
);
3405 newaop
= newAsmop (AOP_EXT
);
3407 newaop
->aopu
.aop_dir
= aop
->aopu
.aop_immd
;
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
);
3415 adr
= (int) ulFromVal (aop
->aopu
.aop_lit
);
3416 if (p_type
== POINTER
)
3418 adr
= (adr
+ offset
) & 0xffff;
3419 dbuf_init (&dbuf
, 64);
3422 newaop
= newAsmop (AOP_DIR
);
3423 dbuf_printf (&dbuf
, "0x%02x", adr
);
3425 newaop
= newAsmop (AOP_EXT
);
3426 dbuf_printf (&dbuf
, "0x%04x", adr
);
3428 newaop
->aopu
.aop_dir
= dbuf_detach_c_str (&dbuf
);
3431 werror (E_INTERNAL_ERROR
, __FILE__
, __LINE__
, "unsupported asmop");
3438 /**************************************************************************
3439 * aopOpExtToIdx - attempt to convert AOP_EXT to AOP_IDX
3440 *************************************************************************/
3442 aopOpExtToIdx(asmop
* result
, asmop
*left
, asmop
*right
)
3446 int resultAccesses
=0;
3448 int rightAccesses
=0;
3452 return; //TODO: makes things worse often
3454 if (!m6502_reg_x
->isFree
|| !m6502_reg_y
->isFree
)
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
;
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
)))
3473 if (left
&& (IS_AOP_WITH_Y (left
) || IS_AOP_WITH_X (left
)))
3475 if (right
&& (IS_AOP_WITH_Y (right
) || IS_AOP_WITH_X (right
)))
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
;
3500 if (rightAccesses
> winnerAccesses
) {
3501 winnerAccesses
= rightAccesses
;
3505 /* Make sure there were enough accesses of a single variable to be worthwhile. */
3506 if (winnerAccesses
< 2)
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
);
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
3526 case AOP_EXT
: // aaaa
3528 case AOP_SOF
: // (BASEPTR),y
3529 if (reg
== m6502_reg_a
&& (m6502_reg_x
->isFree
|| m6502_reg_y
->isFree
))
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))
3547 if (aop
->type
==AOP_SOF
) {
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
;
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;
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))
3578 if (aop
->type
==AOP_SOF
) {
3579 if (aopPrepareStoreTemp
) {
3580 if (aopPreparePreserveFlags
)
3581 loadRegTempNoFlags(m6502_reg_x
, true);
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
)
3600 int offset
= loffset
; // SEH: aop->size - 1 - loffset - (bit16 ? 1 : 0);
3603 /* offset is greater than
3605 if (loffset
> (aop
->size
- 1) && aop
->type
!= AOP_LIT
)
3608 /* depending on type */
3609 switch (aop
->type
) {
3616 sprintf (s
, "#(%s >> %d)", aop
->aopu
.aop_immd
, loffset
* 8);
3618 sprintf (s
, "#>%s", aop
->aopu
.aop_immd
);
3621 sprintf (s
, "#%s", aop
->aopu
.aop_immd
);
3622 rs
= Safe_calloc (1, strlen (s
) + 1);
3627 if (regalloc_dry_run
)
3630 sprintf (s
, "*(%s + %d)", aop
->aopu
.aop_dir
, offset
);
3632 sprintf (s
, "*%s", aop
->aopu
.aop_dir
);
3633 rs
= Safe_calloc (1, strlen (s
) + 1);
3638 if (regalloc_dry_run
)
3641 sprintf (s
, "(%s + %d)", aop
->aopu
.aop_dir
, offset
);
3643 sprintf (s
, "%s", aop
->aopu
.aop_dir
);
3644 rs
= Safe_calloc (1, strlen (s
) + 1);
3649 return aop
->aopu
.aop_reg
[loffset
]->name
;
3653 return aopLiteralLong (aop
->aopu
.aop_lit
, loffset
, 2);
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
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
);
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);
3681 else if (m6502_reg_y
->aop
== &tsxaop
) {
3682 return "[__BASEPTR],y";
3684 // FIXME: unimplemented
3685 // loadRegFromConst(m6502_reg_x, offset);
3686 return "ERROR [__BASEPTR],y"; // TODO: is base ptr or Y loaded?
3692 xofs
= offset
; /* For now, assume yx points to the base address of operand */
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?
3700 return "ERROR"; // TODO: error
3705 werror (E_INTERNAL_ERROR
, __FILE__
, __LINE__
, "aopAdrStr got unsupported aop->type");
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 *************************************************************************/
3715 asmopToBool (asmop
*aop
, bool resultInA
)
3717 symbol
*tlbl
= safeNewiTempLabel (NULL
);
3719 int size
= aop
->size
;
3720 int offset
= size
- 1;
3721 bool needloada
= false;
3724 emitComment (TRACE_AOP
, "asmopToBool resultinA %s", resultInA
?"yes":"no");
3727 type
= operandType (AOP_OP (aop
));
3728 isFloat
= IS_FLOAT (type
);
3731 m6502_freeReg (m6502_reg_a
);
3738 loadRegFromAop (m6502_reg_a
, aop
, 0);
3743 if (aop
->type
==AOP_REG
)
3745 emitCpz(aop
->aopu
.aop_reg
[0]->rIdx
);
3749 reg_info
* freereg
= getDeadByteReg();
3752 loadRegFromAop (freereg
, aop
, 0);
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?
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
);
3777 if(resultInA
&& size
==1) {
3778 if(IS_AOP_A(aop
)) emitCpz(A_IDX
);
3779 else loadRegFromAop (m6502_reg_a
, aop
, 0);
3788 emitCpz(aop
->aopu
.aop_reg
[0]->rIdx
);
3791 else if (IS_AOP_XA (aop
))
3793 // FIXME: this optimization makes the code smaller (expected) and slower (unexpected)
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");
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);
3816 else if (IS_AOP_YX (aop
))
3819 emit6502op ("bne", "%05d$", safeLabelNum (tlbl
));
3824 emitcode("ERROR", "Bad %02x regmask in asmopToBool", (aop
)->regmask
);
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);
3834 loadRegFromConst (m6502_reg_a
, 0);
3835 m6502_freeReg (m6502_reg_a
);
3840 emitComment (TRACE_AOP
|VVDBG
, "asmopToBool - AOP_DIR || AOP_EXT");
3843 if (!resultInA
&& (size
== 1) && !IS_AOP_A (aop
) && !m6502_reg_a
->isFree
&& m6502_reg_x
->isFree
)
3845 loadRegFromAop (m6502_reg_x
, aop
, 0);
3849 if (!resultInA
&& (size
== 1) )
3851 reg_info
*reg
=getFreeByteReg();
3854 loadRegFromAop (reg
, aop
, 0);
3862 needloada
= storeRegTempIfSurv(m6502_reg_a
);
3864 loadRegFromAop (m6502_reg_a
, aop
, offset
--);
3866 emit6502op ("and", "#0x7F");
3869 accopWithAop ("ora", aop
, offset
--);
3873 loadRegTempNoFlags (m6502_reg_a
, needloada
);
3878 m6502_freeReg (m6502_reg_a
);
3886 symbol
*endlbl
= safeNewiTempLabel (NULL
);
3888 emitBranch ("beq", endlbl
);
3889 safeEmitLabel (tlbl
);
3890 loadRegFromConst (m6502_reg_a
, 1);
3891 safeEmitLabel (endlbl
);
3893 m6502_dirtyReg (m6502_reg_a
);
3894 m6502_useReg (m6502_reg_a
);
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
);
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))
3922 /* if they are the same registers */
3923 if (sameRegs (AOP (source
), AOP (result
)) && srcsize
== size
)
3926 if (IS_AOP_XA (AOP (source
)) && size
== 2)
3928 storeRegToAop (m6502_reg_xa
, AOP (result
), 0);
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);
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);
3950 // if (IS_MOS6502 && (size > 2))
3951 // aopOpExtToIdx (AOP (result), NULL, AOP (source));
3954 emitComment (TRACEGEN
|VVDBG
, " genCopy (general case)", "");
3957 while (srcsize
&& size
)
3959 transferAopAop (AOP (source
), offset
, AOP (result
), offset
);
3966 storeConstToAop (0, AOP (result
), offset
);
3974 while ( offset
>= srcsize
)
3976 storeConstToAop (0, AOP (result
), offset
);
3981 transferAopAop (AOP (source
), offset
, AOP (result
), offset
);
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
);
3996 emitComment (TRACEGEN
, __func__
);
3999 /* assign asmOps to operand & result */
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 *************************************************************************/
4020 operand
*left
= IC_LEFT (ic
);
4021 operand
*result
= IC_RESULT (ic
);
4027 emitComment (TRACEGEN
, __func__
);
4030 /* assign asmOps to operand & result */
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);
4082 m6502_unimplemented("unknown register pair in genCpl");
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);
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 */
4110 freeAsmop (result
, NULL
);
4111 freeAsmop (left
, NULL
);
4114 /**************************************************************************
4115 * genUminusFloat - unary minus for floating points
4116 *************************************************************************/
4118 genUminusFloat (operand
* op
, operand
* result
)
4120 int size
, offset
= 0;
4123 emitComment (TRACEGEN
, __func__
);
4125 /* for this we just copy and then flip the bit */
4127 size
= AOP_SIZE (op
) - 1;
4131 transferAopAop (AOP (op
), offset
, AOP (result
), 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
);
4156 emitComment (TRACEGEN
, __func__
);
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
);
4177 /* otherwise subtract from zero */
4178 size
= AOP_SIZE (left
);
4182 needpula
= pushRegIfSurv (m6502_reg_a
);
4183 loadRegFromAop (m6502_reg_a
, AOP (left
), 0);
4184 rmwWithReg ("neg", m6502_reg_a
);
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
);
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
);
4218 loadRegFromConst (m6502_reg_a
, 0);
4220 accopWithAop ("sbc", AOP (left
), 0);
4222 if (result0
== m6502_reg_a
|| (result0
&& result0
== left1
))
4223 pushReg (m6502_reg_a
, true);
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
);
4232 accopWithAop ("sbc", AOP (left
), 1);
4234 storeRegToAop (m6502_reg_a
, AOP (result
), 1);
4235 if (result0
== m6502_reg_a
|| (result0
&& result0
== left1
))
4237 if (left1
== m6502_reg_a
)
4239 pullOrFreeReg (m6502_reg_a
, needpula
);
4243 needpula
= pushRegIfSurv (m6502_reg_a
);
4245 loadRegFromConst (m6502_reg_a
, 0);
4249 accopWithAop ("sbc", AOP (left
), offset
);
4250 storeRegToAop (m6502_reg_a
, AOP(result
), offset
++);
4253 // storeRegSignToUpperAop (m6502_reg_a, AOP(result), offset, SPEC_USIGN (operandType (left)));
4254 pullOrFreeReg (m6502_reg_a
, needpula
);
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
)
4271 for (ic
= lic
; ic
; ic
= ic
->next
)
4272 if (ic
->op
== CALL
|| ic
->op
== PCALL
)
4276 fprintf (stderr
, "found parameter push with no function call\n");
4280 /* if the registers have been saved already or don't need to be then
4284 if (IS_SYMOP (IC_LEFT (ic
)) &&
4285 (IFFUNC_CALLEESAVES (OP_SYMBOL (IC_LEFT (ic
))->type
) || IFFUNC_ISNAKED (OP_SYM_TYPE (IC_LEFT (ic
)))))
4288 if (!regalloc_dry_run
)
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
4297 bool clobbers_a
= !IS_MOS65C02
4298 && (bitVectBitValue(ic
->rSurv
, X_IDX
) || bitVectBitValue(ic
->rSurv
, Y_IDX
))
4299 && !bitVectBitValue(ic
->rSurv
, A_IDX
);
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);
4307 loadRegTemp (m6502_reg_a
);
4310 /**************************************************************************
4311 * unsaveRegisters - pop the pushed registers
4312 *************************************************************************/
4313 static void unsaveRegisters (iCode
*ic
)
4317 emitComment (REGOPS
, "; unsaveRegisters");
4319 // TODO: only clobbered if m6502_reg_a->isFree
4321 bool clobbers_a
= !IS_MOS65C02
4322 && (bitVectBitValue(ic
->rSurv
, X_IDX
) || bitVectBitValue(ic
->rSurv
, Y_IDX
))
4323 && !bitVectBitValue(ic
->rSurv
, A_IDX
);
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
));
4331 loadRegTemp (m6502_reg_a
);
4334 /**************************************************************************
4336 *************************************************************************/
4337 static void pushSide (operand
*oper
, int size
, iCode
*ic
)
4340 // bool xIsFree = m6502_reg_x->isFree;
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);
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
);
4365 // m6502_freeReg (m6502_reg_x);
4368 /**************************************************************************
4370 *************************************************************************/
4372 assignResultValue (operand
* oper
)
4374 int size
= AOP_SIZE (oper
);
4377 emitComment (TRACEGEN
, __func__
);
4381 emitcode("ERROR","assignresultvalue return struct size: %d\n",size
);
4385 if(AOP_TYPE (oper
) == AOP_REG
)
4389 transferRegReg(m6502_reg_a
, (AOP(oper
))->aopu
.aop_reg
[0], true);
4393 if(IS_AOP_YX(AOP(oper
)))
4394 transferRegReg(m6502_reg_xa
, m6502_reg_yx
, true);
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]);
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
)
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);
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 */
4442 /* then do the push */
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);
4457 if (AOP_TYPE (left
) == AOP_REG
) {
4458 for (offset
=size
-1; offset
>=0; offset
--)
4459 pushReg (AOP (left
)->aopu
.aop_reg
[offset
], true);
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);
4471 freeAsmop (left
, NULL
);
4474 /**************************************************************************
4475 * genPointerPush - generate code for pushing
4476 *************************************************************************/
4477 static void genPointerPush (iCode
*ic
)
4479 operand
*left
= IC_LEFT (ic
);
4482 emitComment (TRACEGEN
, __func__
);
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
);
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 *************************************************************************/
4508 genSend (set
*sendSet
)
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
);
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);
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);
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
);
4555 /* make sure send1 is the first argument and swap with send2 if not */
4556 if (send1
->argreg
> send2
->argreg
)
4558 iCode
* sic
= send1
;
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);
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 *************************************************************************/
4583 genCall (iCode
* ic
)
4585 operand
*left
= IC_LEFT (ic
);
4586 operand
*result
= IC_RESULT (ic
);
4590 // bool restoreBank = false;
4591 // bool swapBanks = false;
4593 emitComment (TRACEGEN
, __func__
);
4596 /* if caller saves & we have not saved then */
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
));
4609 genSend (_G
.sendSet
);
4615 if (IS_LITERAL (etype
)) {
4616 emit6502op ("jsr", "0x%04X", ulFromVal (OP_VALUE (left
)));
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
);
4631 /* do we need to recompute the base ptr? */
4632 if (_G
.funcHasBasePtr
) {
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
);
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 *************************************************************************/
4663 genPcall (iCode
* ic
)
4665 operand
*left
= IC_LEFT (ic
);
4666 operand
*result
= IC_RESULT (ic
);
4672 emitComment (TRACEGEN
, __func__
);
4675 dtype
= operandType (left
)->next
;
4676 etype
= getSpec (dtype
);
4677 /* if caller saves & we have not saved then */
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
)) {
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
));
4702 if (!IS_LITERAL (etype
)) {
4704 emit6502op("jsr","__sdcc_indirect_jsr");
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
);
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
);
4729 /* do we need to recompute the base ptr? */
4730 if (_G
.funcHasBasePtr
) {
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
);
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
)
4767 if (result
&& IS_ITEMP (result
)) {
4768 symbol
*sym
= OP_SYMBOL (result
);
4769 if (sym
->remat
&& !POINTER_SET (ic
))
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)
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
));
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;
4830 _G
.tsxStackPushes
= 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.");
4843 /* if this is an interrupt service routine then
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
)
4860 while (ric
&& IC_RESULT (ric
))
4862 symbol
*rsym
= OP_SYMBOL (IC_RESULT (ric
));
4863 int rsymSize
= rsym
? getSize (rsym
->type
) : 0;
4867 if (rsym
&& rsym
->regType
== REG_CND
)
4869 if (rsym
&& (/*rsym->accuse ||*/ rsym
->ruonly
))
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
)
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)
4894 genLine
.lineElement
.ic
= ic
;
4897 ric
= (ric
->prev
&& ric
->prev
->op
== RECEIVE
) ? ric
->prev
: NULL
;
4900 /* adjust the stack for the function */
4902 adjustStack (-stackAdjust
);
4904 _G
.stackOfs
= sym
->stack
;
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
) )
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 *************************************************************************/
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);
4941 if (IFFUNC_ISCRITICAL (sym
->type
))
4943 emit6502op ("plp", "");
4946 if (IFFUNC_ISREENT (sym
->type
) || options
.stackAuto
)
4950 if (_G
.funcHasBasePtr
)
4954 emitcode("ERROR","_G.stackPushes=%d in genEndFunction", _G
.stackPushes
);
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", "");
4983 if (IFFUNC_CALLEESAVES (sym
->type
))
4987 /* if any registers used */
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
)))
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;
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" */
5032 /* we have something to return then
5033 move the return value into place */
5035 size
= AOP_SIZE (left
);
5036 const bool bigreturn
= IS_STRUCT (operandType (left
));
5039 // FIXME: only up to size 8 is supported
5042 if (!regalloc_dry_run
)
5043 werror ( E_FUNC_AGGR
);
5048 transferAopAop (AOP (left
), size
, m6502_aop_pass
[size
], 0);
5051 // emitcode("ERROR","*** end return");
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. */
5062 transferAopAop (AOP (left
), offset
, m6502_aop_pass
[offset
], 0);
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
) {
5069 pushReg (m6502_reg_x
, true);
5074 if (!(delayed_x
&& !offset
))
5075 transferAopAop (AOP (left
), offset
, m6502_aop_pass
[offset
], 0);
5080 pullReg (m6502_reg_a
);
5083 freeAsmop (left
, NULL
);
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
)
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;
5113 /* special case never generate */
5114 if (IC_LABEL (ic
) == entryLabel
)
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
);
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
5160 if (AOP_TYPE (right
) != AOP_LIT
)
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
)));
5172 if(IS_AOP_XA (AOP (result
)) && icount
>=0 ) {
5173 loadRegFromAop (m6502_reg_xa
, AOP (left
), 0);
5175 tlbl
= safeNewiTempLabel (NULL
);
5177 accopWithAop ("adc", AOP (right
), 0);
5178 emitBranch ("bcc", tlbl
);
5179 emit6502op ("inx", "");
5180 safeEmitLabel (tlbl
);
5181 m6502_dirtyReg(m6502_reg_x
);
5187 if (!sameRegs (AOP (left
), AOP (result
)))
5190 // TODO: can inc blah,x
5191 if (!aopCanIncDec (AOP (result
)))
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;
5204 aopOpExtToIdx (AOP (result
), AOP (left
), NULL
);
5207 tlbl
= safeNewiTempLabel (NULL
);
5211 rmwWithAop ("inc", AOP (result
), 0);
5213 emitBranch ("bne", tlbl
);
5215 if (!IS_AOP_A (AOP (result
)) && !IS_AOP_XA (AOP (result
)))
5216 needpula
= pushRegIfUsed (m6502_reg_a
);
5219 loadRegFromAop (m6502_reg_a
, AOP (result
), 0);
5221 accopWithAop ("adc", AOP (right
), 0);
5222 storeRegToAop (m6502_reg_a
, AOP (result
), 0);
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
);
5234 safeEmitLabel (tlbl
);
5236 pullOrFreeReg (m6502_reg_a
, needpula
);
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;
5253 bool earlystore
= false;
5254 bool delayedstore
= false;
5255 bool mayskip
= true;
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__
);
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
))))
5280 /* if I can do an increment instead
5281 of add then GOOD for ME */
5282 if (!maskedtopbyte
&& genPlusIncr (ic
))
5285 emitComment (TRACEGEN
|VVDBG
, " %s - Can't Inc", __func__
);
5288 aopOpExtToIdx (AOP (result
), AOP (left
), AOP (right
));
5290 size
= AOP_SIZE (result
);
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
);
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
);
5321 needpulla
= pushRegIfSurv (m6502_reg_a
);
5324 if (earlystore
&& offset
== 1)
5325 pullReg (m6502_reg_a
);
5327 loadRegFromAop (m6502_reg_a
, AOP(left
), offset
);
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
);
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;
5343 storeRegToAop (m6502_reg_a
, AOP (result
), offset
);
5346 m6502_freeReg (m6502_reg_a
);
5348 clc
= false; /* further adds must propagate carry */
5351 pullReg (m6502_reg_a
);
5352 pullOrFreeReg (m6502_reg_a
, needpulla
);
5354 wassert (!earlystore
|| !delayedstore
);
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
);
5372 unsigned int size
= AOP_SIZE (result
);
5376 emitComment (TRACEGEN
, __func__
);
5378 /* will try to generate an increment */
5379 /* if the right side is not a literal
5381 if (AOP_TYPE (right
) != AOP_LIT
)
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;
5390 if(IS_AOP_XA (AOP (result
)) && icount
>=0 ) {
5391 loadRegFromAop (m6502_reg_xa
, AOP (left
), 0);
5393 tlbl
= safeNewiTempLabel (NULL
);
5395 accopWithAop ("sbc", AOP (right
), 0);
5396 emitBranch ("bcs", tlbl
);
5397 emit6502op ("dex", "");
5398 safeEmitLabel (tlbl
);
5399 m6502_dirtyReg(m6502_reg_x
);
5405 if (!sameRegs (AOP (left
), AOP (result
)))
5411 // TODO: can inc blah,x
5412 if (!aopCanIncDec (AOP (result
)))
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
))
5421 if ((icount
> 1) || (icount
< 0))
5424 emitComment (TRACEGEN
|VVDBG
, " genMinusDec");
5426 aopOpExtToIdx (AOP (result
), AOP (left
), NULL
);
5428 rmwWithAop ("dec", AOP (result
), 0);
5433 /**************************************************************************
5434 * addSign - complete with sign
5435 *************************************************************************/
5436 static void addSign (operand
* result
, int offset
, int sign
)
5438 int size
= (AOP_SIZE (result
) - offset
);
5443 storeRegToAop (m6502_reg_a
, AOP (result
), offset
++);
5446 storeConstToAop (0, AOP (result
), offset
++);
5451 /**************************************************************************
5452 * genMinus - generates code for subtraction
5453 *************************************************************************/
5455 genMinus (iCode
* ic
)
5457 operand
*right
= IC_RIGHT (ic
);
5458 operand
*left
= IC_LEFT (ic
);
5459 operand
*result
= IC_RESULT (ic
);
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__
);
5483 /* special cases :- */
5484 /* if I can do an decrement instead
5485 of subtract then GOOD for ME */
5486 if (!maskedtopbyte
&& genMinusDec (ic
))
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
);
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
);
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
);
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
);
5527 accopWithAop ("sbc", leftOp
, offset
);
5528 emit6502op("eor", "#0xff");
5530 emit6502op ("and", IMMDFMT
, topbytemask
);
5531 storeRegToAop (m6502_reg_a
, AOP (result
), offset
);
5532 pullOrFreeReg (m6502_reg_a
, needpulla
);
5536 needpulla
= storeRegTempIfSurv (m6502_reg_a
);
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
);
5549 emitRegTempOp("sbc", _G
.tempOfs
-1 );
5552 emitComment (TRACEGEN
|VVDBG
, " - default path");
5554 loadRegFromAop (m6502_reg_a
, leftOp
, offset
);
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;
5567 emitComment (TRACEGEN
|VVDBG
, " - store");
5568 storeRegToAop (m6502_reg_a
, AOP (result
), offset
);
5574 pullReg (m6502_reg_a
);
5575 loadOrFreeRegTemp (m6502_reg_a
, needpulla
);
5577 wassert (!earlystore
|| !delayedstore
);
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 *************************************************************************/
5617 genIfxJump (iCode
* ic
, char *jval
)
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
5628 jlbl
= IC_TRUE (ic
);
5629 if (!strcmp (jval
, "z"))
5631 else if (!strcmp (jval
, "c"))
5633 else if (!strcmp (jval
, "n"))
5635 else if (!strcmp (jval
, "v"))
5638 emitcode("ERROR", "; %s IC_TRUE: Unimplemented jval (%s)", __func__
, jval
);
5641 /* false label is present */
5642 jlbl
= IC_FALSE (ic
);
5643 if (!strcmp (jval
, "z"))
5645 else if (!strcmp (jval
, "c"))
5647 else if (!strcmp (jval
, "n"))
5649 else if (!strcmp (jval
, "v"))
5652 emitcode("ERROR", "; %s IC_FALSE: Unimplemented jval (%s)", __func__
, jval
);
5655 emitBranch (inst
, tlbl
);
5656 emitBranch ("jmp", jlbl
);
5657 safeEmitLabel (tlbl
);
5659 /* mark the icode as generated */
5664 /**************************************************************************
5665 * exchangedCmp : returns the opcode need if the two operands are
5666 * exchanged in a comparison
5667 *************************************************************************/
5668 static int exchangedCmp (int opcode
)
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
5692 *************************************************************************/
5693 static int negatedCmp (int opcode
)
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
)
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
)
5769 werror (E_INTERNAL_ERROR
, __FILE__
, __LINE__
, "opcode not a comparison");
5775 /**************************************************************************
5776 * genCmp :- greater or less than (and maybe with equal) comparison
5777 *************************************************************************/
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
;
5787 int size
, offset
= 0;
5788 unsigned long long lit
= 0ull;
5790 symbol
*jlbl
= NULL
;
5791 bool needloada
= false;
5795 emitComment (TRACEGEN
, __func__
);
5799 // TODO: optimize for cmp regs with 0 or constant
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 */
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
;
5822 opcode
= exchangedCmp (opcode
);
5824 // TODO: special case for compare with 0
5830 jlbl
= IC_TRUE (ifx
);
5831 opcode
= negatedCmp (opcode
);
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);
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);
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)
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)
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.
5877 emit6502op("sec","");
5881 accopWithAop ("cmp", AOP (right
), offset
);
5887 // need V flag for signed compare
5888 // FIXME: is this covered above?
5889 if (size
== 1 && sign
== 0)
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
;
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))
5921 needloada
= storeRegTempIfSurv (m6502_reg_a
);
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")) {
5932 emitRegTempOp("sbc", _G
.tempOfs
-1 );
5934 emitRegTempOp(sub
, _G
.tempOfs
-1 );
5940 loadRegFromAop (m6502_reg_a
, AOP (left
), offset
);
5941 if (!strcmp(sub
, "sub")) {
5943 accopWithAop ("sbc", AOP (right
), offset
);
5945 accopWithAop (sub
, AOP (right
), offset
);
5948 m6502_freeReg (m6502_reg_a
);
5956 symbol
*tlbl
= safeNewiTempLabel (NULL
);
5960 loadRegTempNoFlags(m6502_reg_a
, needloada
);
5962 m6502_freeReg(m6502_reg_a
);
5964 // freeAsmop (result, NULL);
5968 inst
= branchInstCmp (opcode
, sign
);
5969 emitBranch (inst
, tlbl
);
5973 if(bmi
) emitBranch ("bmi", tlbl
);
5974 else emitBranch ("bpl", tlbl
);
5976 emitBranch ("jmp", jlbl
);
5977 safeEmitLabel (tlbl
);
5979 /* mark the icode as generated */
5984 symbol
*true_lbl
= safeNewiTempLabel (NULL
);
5985 symbol
*tlbl2
= safeNewiTempLabel (NULL
);
5988 needloada
= storeRegTempIfSurv (m6502_reg_a
);
5992 emitBranch (branchInstCmp (opcode
, sign
), true_lbl
);
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
);
6024 symbol
*jlbl
= NULL
;
6025 symbol
*tlbl_NE
= NULL
;
6026 symbol
*tlbl_EQ
= NULL
;
6027 bool needloada
= false;
6030 emitComment (TRACEGEN
, __func__
);
6034 emitComment (TRACEGEN
|VVDBG
, " genCmpEQorNE (%s)", nameCmp (opcode
));
6036 /* assign the amsops */
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
;
6051 opcode
= exchangedCmp (opcode
);
6055 size
= max (AOP_SIZE (left
), AOP_SIZE (right
));
6058 if (IC_TRUE (ifx
)) {
6059 jlbl
= IC_TRUE (ifx
);
6060 opcode
= negatedCmp (opcode
);
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
);
6070 emitComment (TRACEGEN
|VVDBG
, " genCmpEQorNE left is not not a reg");
6072 // TODO: could clobber A if reg = XA?
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
);
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
);
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
);
6100 symbol
*tmp_label
= safeNewiTempLabel (NULL
);;
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
);
6116 freeAsmop (right
, NULL
);
6117 freeAsmop (left
, NULL
);
6120 freeAsmop (result
, NULL
);
6122 if (opcode
== EQ_OP
) {
6124 tlbl_EQ
= safeNewiTempLabel (NULL
);
6125 emitBranch ("beq", tlbl_EQ
);
6127 safeEmitLabel (tlbl_NE
);
6128 emitBranch ("jmp", jlbl
);
6129 safeEmitLabel (tlbl_EQ
);
6132 tlbl_NE
= safeNewiTempLabel (NULL
);
6133 emitBranch ("bne", tlbl_NE
);
6134 emitBranch ("jmp", jlbl
);
6135 safeEmitLabel (tlbl_NE
);
6138 /* mark the icode as generated */
6141 symbol
*tlbl
= safeNewiTempLabel (NULL
);
6144 needloada
= storeRegTempIfSurv (m6502_reg_a
);
6145 if (opcode
== EQ_OP
) {
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);
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
;
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 */
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
;
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 */
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 *************************************************************************/
6276 isLiteralBit (unsigned long long lit
)
6280 for (idx
= 0; idx
< 64; idx
++)
6281 if (lit
== 1ull<<idx
)
6286 /**************************************************************************
6287 * litmask - return the mask based on the operand size
6288 *************************************************************************/
6289 static unsigned long long
6292 unsigned long long ret
= 0xffffffffffffffffull
;
6298 ret
= 0xffffffffull
;
6302 bool isAndTrivial(operand
*op
, unsigned char mask
)
6304 if(AOP_TYPE(op
)==AOP_LIT
&& (mask
==0x00 || mask
==0xff))
6309 bool isOrTrivial(operand
*op
, unsigned char mask
)
6311 if(AOP_TYPE(op
)==AOP_LIT
&& (mask
==0x00 || mask
==0xff ) )
6316 bool isXorTrivial(operand
*op
, unsigned char mask
)
6318 if(AOP_TYPE(op
)==AOP_LIT
&& mask
==0x00)
6323 /**************************************************************************
6324 * genAnd - code for and
6325 *************************************************************************/
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;
6336 unsigned long long lit
= 0ull;
6337 unsigned int bytemask
;
6340 emitComment (TRACEGEN
, __func__
);
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
;
6358 size
= (AOP_SIZE (left
) >= AOP_SIZE (right
)) ? AOP_SIZE (left
) : AOP_SIZE (right
);
6360 isLit
= (AOP_TYPE (right
) == AOP_LIT
);
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
);
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
);
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
);
6385 emitBranch ("jmp", IC_FALSE (ifx
));
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;
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");
6411 if (bitpos
>= 0 && (bitpos
& 7) == 6)
6413 rmwWithAop ("bit", AOP (left
), bitpos
>> 3);
6414 genIfxJump (ifx
, "v");
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);
6430 if(isLit
&& bitpos
==7)
6433 genIfxJump (ifx
, "n");
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);
6443 genIfxJump(ifx
, "v");
6447 reg_info
* freereg
= getDeadByteReg();
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);
6458 storeRegTemp(m6502_reg_a
, true);
6459 accopWithAop ("and", AOP(right
), 0);
6460 loadRegTempNoFlags(m6502_reg_a
, true); // preserve flags
6463 genIfxJump (ifx
, "z");
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
);
6474 if ( isLit
&& lit
== 0)
6476 loadRegFromConst (m6502_reg_a
, 0x00);
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
);
6489 emitBranch ("bne", tlbl
);
6493 m6502_freeReg (m6502_reg_a
);
6494 safeEmitLabel (tlbl
);
6496 // TODO: better way to preserve flags?
6499 loadRegTempNoFlags (m6502_reg_a
, needpulla
);
6500 genIfxJump (ifx
, "z");
6504 if (needpulla
) loadRegTemp (NULL
);
6509 size
= AOP_SIZE (result
);
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));
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);
6553 if(IS_AOP_XA(AOP(left
)))
6558 accopWithAop ("and", AOP (right
), 0);
6559 storeRegTemp(m6502_reg_a
, true);
6563 loadRegFromAop (m6502_reg_a
, AOP (left
), 1);
6564 accopWithAop ("and", AOP (right
), 1);
6565 storeRegToAop (m6502_reg_a
, AOP (result
), 1);
6568 storeConstToAop(0x00, AOP(result
), 0);
6571 if(needpulla
) loadRegTemp(m6502_reg_a
);
6574 loadRegFromAop (m6502_reg_a
, AOP (left
), 0);
6576 accopWithAop ("and", AOP (right
), 0);
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
);
6603 loadRegFromAop (m6502_reg_a
, AOP (left
), i
);
6604 accopWithAop ("and", AOP (right
), i
);
6605 storeRegToAop (m6502_reg_a
, AOP (result
), i
);
6612 loadRegTemp(m6502_reg_a
);
6616 if(savea
) loadRegTemp(NULL
);
6617 m6502_freeReg(m6502_reg_a
);
6621 freeAsmop (left
, NULL
);
6622 freeAsmop (right
, NULL
);
6623 freeAsmop (result
, NULL
);
6626 /**************************************************************************
6627 * genOr - code for or
6628 *************************************************************************/
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;
6639 unsigned long long lit
= 0ull;
6640 unsigned int bytemask
;
6643 emitComment (TRACEGEN
, __func__
);
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
;
6661 size
= (AOP_SIZE (left
) >= AOP_SIZE (right
)) ? AOP_SIZE (left
) : AOP_SIZE (right
);
6663 isLit
= (AOP_TYPE (right
) == AOP_LIT
);
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 )
6681 genIfxJump (ifx
, "z");
6686 // test for flags only (general case)
6687 if (AOP_TYPE (result
) == AOP_CRY
)
6689 symbol
*tlbl
= safeNewiTempLabel (NULL
);
6692 // FIXME: good optmization but currently not working
6693 if (IS_MOS65C02
&& isLit
&& lit
!=0)
6695 emit6502op("bit","#0x00");
6696 genIfxJump (ifx
, "z");
6701 needpulla
= storeRegTempIfSurv (m6502_reg_a
);
6704 if (isLit
&& lit
!= 0)
6706 loadRegFromConst (m6502_reg_a
, 0xff);
6711 bytemask
= (isLit
) ? (lit
>> (offset
* 8)) & 0xff : 0x100;
6714 loadRegFromAop (m6502_reg_a
, AOP (left
), offset
);
6716 accopWithAop ("ora", AOP(left
), offset
);
6718 if (bytemask
!= 0x00)
6719 accopWithAop ("ora", AOP(right
), offset
);
6721 emitBranch ("bne", tlbl
);
6724 m6502_freeReg (m6502_reg_a
);
6725 safeEmitLabel (tlbl
);
6727 // TODO: better way to preserve flags?
6730 loadRegTempNoFlags (m6502_reg_a
, needpulla
);
6731 genIfxJump (ifx
, "z");
6735 if (needpulla
) loadRegTemp (NULL
);
6740 size
= AOP_SIZE (result
);
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));
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);
6783 if(IS_AOP_XA(AOP(left
)))
6788 accopWithAop ("ora", AOP (right
), 0);
6789 storeRegTemp(m6502_reg_a
, true);
6793 loadRegFromAop (m6502_reg_a
, AOP (left
), 1);
6794 accopWithAop ("ora", AOP (right
), 1);
6795 storeRegToAop (m6502_reg_a
, AOP (result
), 1);
6798 storeConstToAop(0xff, AOP(result
), 0);
6800 if(needpulla
) loadRegTemp(m6502_reg_a
);
6803 loadRegFromAop (m6502_reg_a
, AOP (left
), 0);
6805 accopWithAop ("ora", AOP (right
), 0);
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
);
6832 loadRegFromAop (m6502_reg_a
, AOP (left
), i
);
6833 accopWithAop ("ora", AOP (right
), i
);
6834 storeRegToAop (m6502_reg_a
, AOP (result
), i
);
6841 loadRegTemp(m6502_reg_a
);
6845 if(savea
) loadRegTemp(NULL
);
6846 m6502_freeReg(m6502_reg_a
);
6850 freeAsmop (left
, NULL
);
6851 freeAsmop (right
, NULL
);
6852 freeAsmop (result
, NULL
);
6855 /**************************************************************************
6856 * genXor - code for Exclusive or
6857 *************************************************************************/
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;
6868 unsigned long long lit
= 0ull;
6869 unsigned int bytemask
;
6872 emitComment (TRACEGEN
, __func__
);
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
;
6890 size
= (AOP_SIZE (left
) >= AOP_SIZE (right
)) ? AOP_SIZE (left
) : AOP_SIZE (right
);
6892 isLit
= (AOP_TYPE (right
) == AOP_LIT
);
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 )
6910 genIfxJump (ifx
, "z");
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
);
6924 bytemask
= (isLit
) ? (lit
>> (offset
* 8)) & 0xff : 0x100;
6926 loadRegFromAop (m6502_reg_a
, AOP (left
), offset
);
6927 if (bytemask
== 0x00)
6933 accopWithAop ("eor", AOP (right
), offset
);
6936 emitBranch ("bne", tlbl
);
6939 // FIXME: check bug1875933.c
6940 m6502_freeReg (m6502_reg_a
);
6941 safeEmitLabel (tlbl
);
6943 // TODO: better way to preserve flags?
6946 loadRegTempNoFlags (m6502_reg_a
, needpulla
);
6947 genIfxJump (ifx
, "z");
6951 if (needpulla
) loadRegTemp (NULL
);
6956 size
= AOP_SIZE (result
);
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);
6976 if(IS_AOP_XA(AOP(left
))) {
6980 accopWithAop ("eor", AOP (right
), 0);
6981 storeRegTemp(m6502_reg_a
, 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
);
6994 loadRegFromAop (m6502_reg_a
, AOP (left
), 0);
6996 accopWithAop ("eor", AOP (right
), 0);
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
);
7019 loadRegFromAop (m6502_reg_a
, AOP (left
), i
);
7020 accopWithAop ("eor", AOP (right
), i
);
7021 storeRegToAop (m6502_reg_a
, AOP (result
), i
);
7028 loadRegTemp(m6502_reg_a
);
7032 if(savea
) loadRegTemp(NULL
);
7033 m6502_freeReg(m6502_reg_a
);
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;
7048 dbuf_init (&dbuf
, 128);
7052 if ('_' == *p
|| isalnum (*p
))
7053 /* in the middle of identifier */
7056 /* end of identifier */
7057 symbol
*sym
, *tempsym
;
7058 char *symname
= Safe_strndup (p
+ 1, p
- begin
- 1);
7062 tempsym
= newSymbol (symname
, ic
->level
);
7063 tempsym
->block
= ic
->block
;
7064 sym
= (symbol
*) findSymWithLevel (SymbolTab
, tempsym
);
7066 dbuf_append (&dbuf
, begin
, p
- begin
);
7068 asmop
*aop
= aopForSym (ic
, sym
);
7069 const char *l
= aopAdrStr (aop
, aop
->size
- 1, true);
7074 if (sym
->level
&& !sym
->allocreq
&& !sym
->ismyparm
) {
7075 werror (E_ID_UNDEF
, sym
->name
);
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
);
7085 } else if ('_' == *p
) {
7086 /* begin of identifier */
7089 dbuf_append (&dbuf
, begin
, p
- 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 */
7131 expanded
= expand_symbols (ic
, begin
);
7132 emitcode (expanded
, NULL
);
7133 dbuf_free (expanded
);
7138 /* Add \n for labels, not dirs such as c:\mydir */
7139 if (!inComment
&& (*bp
== ':') && (isspace ((unsigned char) bp
[1]))) {
7143 emitcode (begin
, NULL
);
7152 expanded
= expand_symbols (ic
, begin
);
7153 emitcode (expanded
, NULL
);
7154 dbuf_free (expanded
);
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
);
7175 emitComment (TRACEGEN
, __func__
);
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
);
7199 emitComment (TRACEGEN
, __func__
);
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
);
7223 bool resultInA
= false;
7226 emitComment (TRACEGEN
, __func__
);
7228 /* rotate right with carry */
7233 if(IS_AOP_WITH_A(AOP(result
))) resultInA
=true;
7234 size
= AOP_SIZE (result
);
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
);
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);
7268 else if (sameRegs (AOP (left
), AOP (result
)))
7272 rmwWithAop (shift
, AOP (result
), offset
--);
7280 loadRegFromAop (m6502_reg_a
, AOP (left
), offset
);
7281 rmwWithReg (shift
, m6502_reg_a
);
7282 storeRegToAop (m6502_reg_a
, AOP (result
), 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
);
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
);
7317 bool resultInA
= false;
7318 // bool needpula = false;
7320 emitComment (TRACEGEN
, __func__
);
7322 /* rotate right with carry */
7327 if(IS_AOP_WITH_A(AOP(result
))) resultInA
=true;
7328 size
= AOP_SIZE (result
);
7332 if (!resultInA
&& sameRegs (AOP (left
), AOP (result
)))
7336 rmwWithAop (shift
, AOP (result
), offset
++);
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
);
7353 /* now we need to put the carry into the
7354 lowest order byte of the result */
7356 loadRegFromConst(m6502_reg_a
, 0);
7357 emit6502op ("rol", "a");
7360 emit6502op("ora", TEMPFMT
, _G
.tempOfs
-1);
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
)
7382 shCount
&= 0x0007; // shCount : 0..7
7384 /* For shift counts of 6 and 7, the unrolled loop is never optimal. */
7387 emit6502op ("ror", "a");
7388 emit6502op ("ror", "a");
7389 emit6502op ("ror", "a");
7390 emit6502op ("and", "#0xc0");
7391 /* total: 8 cycles, 5 bytes */
7394 emit6502op ("ror", "a");
7395 loadRegFromConst(m6502_reg_a
, 0);
7396 emit6502op ("ror", "a");
7397 /* total: 6 cycles, 4 bytes */
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
)
7414 shCount
&= 0x0007; // shCount : 0..7
7418 emit6502op ("rol", "a");
7419 loadRegFromConst(m6502_reg_a
, 0);
7420 emit6502op ("adc", "#0xff");
7421 emit6502op ("eor", "#0xff");
7425 symbol
*tlbl
= safeNewiTempLabel (NULL
);
7426 emit6502op ("ora", "#0x3f");
7428 emit6502op ("bmi", "%05d$", safeLabelNum (tlbl
));
7429 emit6502op ("and", "#0xc0");
7431 safeEmitLabel(tlbl
);
7432 emit6502op ("rol", "a");
7433 emit6502op ("rol", "a");
7434 emit6502op ("rol", "a");
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
)
7456 shCount
&= 0x0007; // shCount : 0..7
7458 /* For shift counts of 6 and 7, the unrolled loop is never optimal. */
7461 emit6502op ("rol", "a");
7462 emit6502op ("rol", "a");
7463 emit6502op ("rol", "a");
7464 emit6502op ("and", "#0x03");
7465 /* total: 8 cycles, 5 bytes */
7468 emit6502op ("rol", "a");
7469 loadRegFromConst(m6502_reg_a
, 0);
7470 emit6502op ("rol", "a");
7471 /* total: 6 cycles, 4 bytes */
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
)
7488 shCount
&= 0x000f; // shCount : 0..15
7491 AccLsh (shCount
- 8);
7492 transferRegReg (m6502_reg_a
, msb_reg
, false);
7493 loadRegFromConst (m6502_reg_a
, 0);
7497 /* if we can beat 2n cycles or bytes for some special case, do it here */
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
);
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
)
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
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
);
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
);
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
)
7591 shCount
&= 0x000f; // shCount : 0..f
7593 /* if we can beat 2n cycles or bytes for some special case, do it here */
7603 transferRegReg(m6502_reg_x
, m6502_reg_a
, true);
7604 AccRsh (shCount
- 8, false);
7605 loadRegFromConst (m6502_reg_x
, 0);
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
);
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);
7644 if (sameRegs (AOP (left
), AOP (result
)) && aopCanShift(AOP(left
)) && offr
== offl
&& !maskedbyte
) {
7646 rmwWithAop ("asl", AOP (result
), 0);
7648 bool needpulla
= pushRegIfSurv (m6502_reg_a
);
7649 loadRegFromAop (m6502_reg_a
, AOP (left
), offl
);
7650 /* shift left accumulator */
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
)
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
);
7683 if (!IS_AOP_XA (AOP (left
)))
7684 needpulx
= pushRegIfUsed (m6502_reg_x
);
7688 loadRegFromAop (m6502_reg_xa
, AOP (left
), offl
);
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
);
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 */
7756 needpulla
= pushRegIfSurv (m6502_reg_a
);
7757 loadRegFromAop (m6502_reg_a
, AOP (left
), 0);
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
))) {
7777 rmwWithAop ("asl", AOP (result
), 0);
7778 rmwWithAop ("rol", AOP (result
), 1);
7781 needpulla
= storeRegTempIfSurv (m6502_reg_a
);
7782 transferAopAop (AOP (left
), 1, AOP(result
), 1);
7783 loadRegFromAop (m6502_reg_a
, AOP (left
), 0);
7785 emit6502op ("asl", "a");
7786 rmwWithAop ("rol", AOP (result
), 1);
7788 storeRegToAop (m6502_reg_a
, AOP (result
), 0);
7789 loadOrFreeRegTemp (m6502_reg_a
, needpulla
);
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;
7807 needpull
= pushRegIfUsed (m6502_reg_a
);
7808 loadRegFromAop (m6502_reg_a
, result
->aop
, 1);
7810 emit6502op ("and", IMMDFMT
, topbytemask
);
7812 storeRegToAop (m6502_reg_a
, result
->aop
, 1);
7813 pullOrFreeReg (m6502_reg_a
, needpull
);
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
);
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);
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);
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) {
7888 /* lowest order of left goes to the highest
7889 order of the destination */
7890 shiftL1Left2Result (left
, LSB
, result
, MSB32
, shCount
);
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
);
7897 } else if (shCount
>= 16) {
7898 /* more than two bytes */
7899 /* lower order two bytes goes to higher order two bytes */
7901 /* if some more remaining */
7903 shiftL2Left2Result (left
, LSB
, result
, MSB24
, shCount
);
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
);
7913 /* if more than 1 byte */
7914 else if (shCount
>= 8)
7916 /* lower order three bytes goes to higher order three bytes */
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
);
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
);
7936 else if (shCount
<= 2)
7938 /* 1 <= shCount <= 2 */
7939 shiftLLong (left
, result
, LSB
);
7941 shiftLLong (result
, result
, LSB
);
7945 /* 3 <= shCount <= 7, optimize */
7947 shiftLLong (left
, result
, LSB
);
7949 shiftLLong (result
, result
, LSB
);
7952 shiftL2Left2Result (left
, MSB24
, result
, MSB24
, shCount
);
7953 shiftRLeftOrResult (left
, MSB16
, result
, MSB24
, 8 - shCount
);
7954 shiftL2Left2Result (left
, LSB
, result
, LSB
, shCount
);
7959 bool in_a
= (result
->aop
->type
== AOP_REG
&& result
->aop
->aopu
.aop_reg
[1]->rIdx
== A_IDX
);
7960 bool needpull
= false;
7963 needpull
= pushRegIfUsed (m6502_reg_a
);
7964 loadRegFromAop (m6502_reg_a
, result
->aop
, 1);
7966 emit6502op ("and", IMMDFMT
, topbytemask
);
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)
7985 else if (IS_OP_LITERAL (right
) && operandLitValueUll (right
) % lbits
== lbits
- 1)
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
);
7999 emitComment (TRACEGEN
, __func__
);
8001 freeAsmop (right
, NULL
);
8006 size
= AOP_SIZE (result
);
8009 emitComment (TRACEGEN
|VVDBG
, " shift left ", "result %d, left %d", size
, AOP_SIZE (left
));
8013 genCopy (result
, left
);
8014 } else if (shCount
>= (size
* 8)) {
8016 storeConstToAop (0, AOP (result
), size
);
8020 genlshOne (result
, left
, shCount
);
8023 genlshTwo (result
, left
, shCount
);
8026 genlshFour (result
, left
, shCount
);
8029 werror (E_INTERNAL_ERROR
, __FILE__
, __LINE__
, "*** ack! mystery literal shift!\n");
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
);
8047 symbol
*tlbl
, *tlbl1
;
8050 reg_info
*countreg
= NULL
;
8051 int count_offset
= 0;
8053 emitComment (TRACEGEN
, __func__
);
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
);
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 ) */
8079 aopResult
= AOP (result
);
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
)));
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
;
8096 countreg
->isFree
= false;
8097 emitComment (TRACEGEN
|VVDBG
, " load countreg");
8098 loadRegFromAop (countreg
, AOP (right
), 0);
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
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
);
8119 transferAopAop (AOP (left
), offset
, aopResult
, offset
);
8123 freeAsmop (left
, NULL
);
8124 AOP (result
) = aopResult
;
8126 tlbl
= safeNewiTempLabel (NULL
);
8127 size
= AOP_SIZE (result
);
8129 tlbl1
= safeNewiTempLabel (NULL
);
8132 emitCpz(countreg
->rIdx
);
8133 emitBranch ("beq", tlbl1
);
8135 emit6502op ("dec", TEMPFMT
, count_offset
);
8136 // FIXME: could keep it literal
8137 dirtyRegTemp(_G
.tempOfs
- 1);
8138 emitBranch ("bmi", tlbl1
);
8141 safeEmitLabel (tlbl
);
8144 for (offset
= 0; offset
< size
; offset
++) {
8145 rmwWithAop (shift
, AOP (result
), offset
);
8150 rmwWithReg("dec", countreg
);
8151 emit6502op("bne", "%05d$", safeLabelNum (tlbl
));
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
8163 countreg
->isLitConst
= 1;
8164 countreg
->litConst
= 0;
8168 emitComment (TRACEGEN
|VVDBG
, " pull null (1) ");
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;
8176 needpull
= pushRegIfUsed (m6502_reg_a
);
8177 loadRegFromAop (m6502_reg_a
, result
->aop
, size
- 1);
8179 emit6502op ("and", IMMDFMT
, topbytemask
);
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
)
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 */
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
);
8222 transferAopAop (AOP (left
), 1, AOP (result
), 0);
8223 storeConstToAop (0, AOP (result
), 1);
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 *************************************************************************/
8242 shiftRLong (operand
* left
, int offl
, operand
* result
, int sign
)
8244 bool needpulla
= pushRegIfSurv (m6502_reg_a
);
8245 bool needloadx
= false;
8250 loadRegFromAop (m6502_reg_a
, AOP (left
), 3);
8253 emit6502op("cmp","#0x80");
8254 rmwWithReg ("ror", m6502_reg_a
);
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);
8271 needloadx
= storeRegTempIfSurv (m6502_reg_x
);
8273 loadRegFromConst(m6502_reg_x
,0);
8274 loadRegFromAop (m6502_reg_a
, AOP (left
), 3);
8276 symbol
*tlbl
= safeNewiTempLabel (NULL
);
8278 emit6502op("bpl","%05d$", safeLabelNum (tlbl
));
8280 loadRegFromConst(m6502_reg_x
,0xff);
8281 safeEmitLabel(tlbl
);
8282 rmwWithReg ("ror", m6502_reg_a
);
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);
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)
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
);
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
);
8365 /* 1 <= shCount <= 7 */
8368 shiftRLong (left
, LSB
, result
, sign
);
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
);
8399 emitComment (TRACEGEN
, __func__
);
8401 freeAsmop (right
, NULL
);
8407 emitComment (TRACEGEN
|VVDBG
, " shift right ", "result %d, left %d", AOP_SIZE (result
), AOP_SIZE (left
));
8410 size
= AOP_SIZE (left
);
8411 /* test the LEFT size !!! */
8413 /* I suppose that the left size >= result size */
8415 genCopy (result
, left
);
8416 } else if (shCount
>= (size
* 8)) {
8417 bool needpulla
= pushRegIfSurv (m6502_reg_a
);
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
);
8427 genrshOne (result
, left
, shCount
, sign
);
8431 genrshTwo (result
, left
, shCount
, sign
);
8435 genrshFour (result
, left
, shCount
, sign
);
8438 wassertl (0, "Invalid operand size in right shift.");
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
);
8458 symbol
*tlbl
, *tlbl1
;
8462 reg_info
*countreg
= NULL
;
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 */
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
);
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 ) */
8497 aopResult
= AOP (result
);
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
)));
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
;
8515 countreg
->isFree
= false;
8516 loadRegFromAop (countreg
, AOP (right
), 0);
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
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
);
8536 transferAopAop (AOP (left
), offset
, aopResult
, offset
);
8540 freeAsmop (left
, NULL
);
8541 AOP (result
) = aopResult
;
8543 tlbl
= safeNewiTempLabel (NULL
);
8544 size
= AOP_SIZE (result
);
8546 tlbl1
= safeNewiTempLabel (NULL
);
8549 emitCpz(countreg
->rIdx
);
8550 emitBranch ("beq", tlbl1
);
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
);
8567 rmwWithReg("dec", countreg
);
8568 emit6502op("bne", "%05d$", safeLabelNum (tlbl
));
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
8581 countreg
->isLitConst
= 1;
8582 countreg
->litConst
= 0;
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
)
8599 *rematOffset
= NULL
;
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
;
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()");
8627 emit6502op ("bit", IMMDFMT
, val
);
8631 reg_info
*reg
=getFreeByteReg();
8634 loadRegFromConst(reg
,val
);
8635 storeRegTempAlways (reg
, true);
8636 emit6502op ("bit", TEMPFMT
, _G
.tempOfs
-1);
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;
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
);
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");
8701 /* If the bitfield length is less than a byte */
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
++);
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);
8729 storeRegToAop (m6502_reg_a
, AOP (result
), offset
);
8733 /* Handle the partial byte at the end */
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]);
8754 if (offset
< rsize
) {
8756 if (SPEC_USIGN (etype
)) {
8758 storeConstToAop (0, AOP (result
), offset
++);
8760 /* signed bitfield: sign extension with 0x00 or 0xff */
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
)
8777 int offset
= 0; /* result byte offset */
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 */
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
);
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
) {
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
);
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
)
8826 m6502_freeReg (m6502_reg_a
);
8830 symbol
*tlbl
= safeNewiTempLabel (NULL
);
8835 if (IC_TRUE (ifx
)) {
8836 jlbl
= IC_TRUE (ifx
);
8839 jlbl
= IC_FALSE (ifx
);
8842 emit6502op (inst
, "#%d,%s,%05d$", bstr
, aopAdrStr (derefaop
, 0, false), safeLabelNum ((tlbl
)));
8843 emitBranch ("jmp", jlbl
);
8844 safeEmitLabel (tlbl
);
8851 /* If the bitfield length is less than a byte */
8853 loadRegFromAop (m6502_reg_a
, derefaop
, 0);
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
)
8870 emit6502op ("and", IMMDFMT
, (((unsigned char) - 1) >> (8 - blen
)) << bstr
);
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);
8883 loadRegFromAop (m6502_reg_a
, derefaop
, offset
);
8885 storeRegToAop (m6502_reg_a
, AOP (result
), offset
);
8886 if (AOP_TYPE (result
) == AOP_REG
&& AOP(result
)->aopu
.aop_reg
[offset
]->rIdx
== A_IDX
)
8894 /* Handle the partial byte at the end */
8896 if (assigned_a
&& !delayed_a
) {
8897 pushReg (m6502_reg_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
)
8918 if (offset
< rsize
) {
8920 if (SPEC_USIGN (etype
)) {
8922 storeConstToAop (0, AOP (result
), offset
++);
8924 if (assigned_a
&& !delayed_a
) {
8925 pushReg (m6502_reg_a
, true);
8929 /* signed bitfield: sign extension with 0x00 or 0xff */
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");
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
)
8956 char * rematOffset
= NULL
;
8958 bool needpulla
= false;
8960 emitComment (TRACEGEN
, __func__
);
8962 decodePointerOffset (right
, &litOffset
, &rematOffset
);
8963 wassert (rematOffset
==NULL
);
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
;
8974 needpulla
= storeRegTempIfSurv (m6502_reg_a
);
8976 if (IS_AOP_YX (AOP (result
)))
8977 loadRegFromAop (m6502_reg_yx
, derefaop
, 0);
8978 else while (size
--) {
8980 transferAopAop (derefaop
, size
, AOP (result
), size
);
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");
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
);
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
));
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
);
9026 genUnpackBitsImmed (left
, right
, result
, ic
, ifx
);
9032 /* if bit then unpack */
9033 if (IS_BITVAR (retype
)) {
9034 genUnpackBits (result
, left
, right
, ifx
);
9041 decodePointerOffset (right
, &litOffset
, &rematOffset
);
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
);
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) ) ) {
9066 loadRegFromConst(m6502_reg_x
,0);
9067 emit6502op ("lda", "[%s,x]", aopAdrStr ( AOP(left
), 0, true ) );
9068 storeRegToAop (m6502_reg_a
, AOP (result
), 0);
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
);
9081 storeRegToAop (m6502_reg_a
, AOP (result
), i
);
9083 pushReg(m6502_reg_a
, false);
9085 storeRegToAop (m6502_reg_a
, AOP (result
), 0);
9087 pullReg(m6502_reg_a
);
9088 storeRegToAop (m6502_reg_a
, AOP (result
), 1);
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
);
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
);
9118 // try absolute indexed
9120 // allow index to be in memory
9122 && ( AOP_SIZE(left
)==1
9123 || ( AOP_TYPE(left
) == AOP_REG
&& AOP(left
)->aopu
.aop_reg
[1]->isLitConst
) ) )
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
))
9130 emitComment (TRACEGEN
|VVDBG
," %s - absolute with 8-bit index", __func__
);
9131 unsigned int hi_offset
=0;
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;
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;
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 );
9169 } else if(dst_reg
[2]=='x') {
9170 py
= storeRegTempIfSurv(m6502_reg_y
);
9171 loadRegFromAop(m6502_reg_y
, AOP(left
), 0 );
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 );
9182 if(dst_reg
[2] == idx_reg
|| dst_reg
[0]=='M') {
9183 // loadRegFromAop (m6502_reg_a, AOP (right), 0);
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
);
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
);
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
);
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
);
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 */
9251 char *rematOffset
= NULL
;
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. */
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 */
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
);
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
);
9295 emit6502op ("ora", IMMDFMT
, litval
);
9297 loadRegFromConst(m6502_reg_y
, yoff
+ offset
);
9298 emit6502op ("sta", INDFMT_IY
);
9299 pullOrFreeReg (m6502_reg_a
, needpulla
);
9303 /* Case with a bitfield length < 8 and arbitrary source
9305 if (AOP_TYPE (right
) == AOP_REG
)
9306 pullReg (m6502_reg_a
);
9308 loadRegFromAop (m6502_reg_a
, AOP (right
), 0);
9309 /* shift and mask source value */
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);
9323 pullOrFreeReg (m6502_reg_a
, needpulla
);
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
);
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
);
9342 /* If there was a partial byte at the end */
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
);
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
);
9370 /* Case with partial byte and arbitrary source
9372 if (AOP_TYPE (right
) == AOP_REG
)
9373 pullReg (m6502_reg_a
);
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);
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
)
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 */
9414 char *rematOffset
= NULL
;
9416 emitComment (TRACEGEN
, __func__
);
9418 blen
= SPEC_BLEN (etype
);
9419 bstr
= SPEC_BSTR (etype
);
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));
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
);
9457 /* If the bitfield length is less than a byte */
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
);
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
);
9474 emit6502op ("ora", IMMDFMT
, litval
);
9476 m6502_dirtyReg (m6502_reg_a
);
9477 storeRegToAop (m6502_reg_a
, derefaop
, 0);
9479 pullOrFreeReg (m6502_reg_a
, needpulla
);
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 */
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
);
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
);
9509 /* If there was a partial byte at the end */
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
);
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
);
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
);
9549 m6502_freeReg (m6502_reg_a
);
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
)
9564 char *rematOffset
= NULL
;
9566 emitComment (TRACEGEN
, __func__
);
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
;
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
);
9594 bool needpulla
= false;
9595 bool needpullx
= false;
9596 bool needpully
= false;
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
9610 /* if the result is rematerializable */
9611 if (AOP_TYPE (result
) == AOP_IMMD
|| AOP_TYPE (result
) == AOP_LIT
) {
9613 genDataPointerSet (left
, right
, result
, ic
);
9615 genPackBitsImmed (result
, left
, operandType (result
)->next
, right
, 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
9636 && AOP_TYPE (result
) == AOP_DIR
&& !rematOffset
&& litOffset
>= 0 && litOffset
<= 256-size
9637 && !sameRegs(AOP(right
), AOP(result
)) ) {
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 ) );
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 ) );
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 ) );
9676 // abs,x or abs,y with index in register or memory
9678 && ( AOP_SIZE(result
)==1
9679 || ( AOP_TYPE(result
) == AOP_REG
&& AOP(result
)->aopu
.aop_reg
[1]->isLitConst
) ) )
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
) )
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;
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;
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') {
9725 px
= storeRegTempIfSurv(m6502_reg_x
);
9726 loadRegFromAop(m6502_reg_x
, AOP(result
), 0 );
9729 py
= storeRegTempIfSurv(m6502_reg_y
);
9730 loadRegFromAop(m6502_reg_y
, AOP(result
), 0 );
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
);
9748 emitComment (TRACEGEN
|VVDBG
," %s - general case ", __func__
);
9749 int aloc
, xloc
, yloc
;
9750 deadA
= m6502_reg_a
->isDead
;
9753 if(IS_AOP_WITH_A(AOP(right
))) needpulla
= storeRegTempIfUsed (m6502_reg_a
);
9754 else needpulla
= storeRegTempIfSurv (m6502_reg_a
);
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
);
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 */
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
);
9775 if(!m6502_reg_a
->isFree
) {
9777 transferRegReg(m6502_reg_a
, m6502_reg_y
, true);
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
);
9799 freeAsmop (result
, NULL
);
9800 freeAsmop (right
, NULL
);
9802 loadOrFreeRegTemp (m6502_reg_y
, needpully
);
9803 loadOrFreeRegTemp (m6502_reg_x
, needpullx
);
9806 if(needpulla
) loadRegTemp(NULL
);
9807 m6502_freeReg(m6502_reg_a
);
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__
);
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
);
9834 emitBranch ("jmp", IC_TRUE (ic
));
9837 emitBranch ("jmp", IC_FALSE (ic
));
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");
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
));
9866 bool needpulla
, needpullx
;
9869 emitComment (TRACEGEN
, __func__
);
9873 /* if the operand is on the stack then we
9874 need to get the stack offset of this
9878 needpulla
= pushRegIfSurv (m6502_reg_a
);
9879 needpullx
= pushRegIfSurv (m6502_reg_x
);
9880 /* if it has an offset then we need to compute it */
9882 offset
= _G
.stackOfs
+ _G
.tsxStackPushes
+ _G
.stackPushes
+ sym
->stack
+ 1;
9883 if(smallAdjustReg(m6502_reg_x
, offset
))
9885 transferRegReg (m6502_reg_x
, m6502_reg_a
, true);
9889 emit6502op ("adc", IMMDFMT
, offset
&0xff);
9891 if(IS_AOP_XA(AOP(result
)))
9893 loadRegFromConst(m6502_reg_x
, 0x01); // stack top = 0x100
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
);
9906 /* object not on stack then we need the name */
9907 size
= AOP_SIZE (result
);
9912 dbuf_init (&dbuf
, 64);
9916 dbuf_printf (&dbuf
, "#%s", sym
->rname
);
9919 dbuf_printf (&dbuf
, "#>%s", sym
->rname
);
9922 dbuf_printf (&dbuf
, "#0");
9924 storeImmToAop (dbuf_detach_c_str (&dbuf
), AOP (result
), offset
++);
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
)
9938 unsigned char value
[sizeof(assigned
)];
9942 emitComment (TRACEGEN
, __func__
);
9944 /* Make sure this is a literal assignment */
9945 if (AOP_TYPE (right
) != AOP_LIT
)
9948 /* The general case already handles register assignment well */
9949 if (AOP_TYPE (result
) == AOP_REG
)
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))
9957 /* Make sure the assignment is not larger than we can handle */
9958 size
= AOP_SIZE (result
);
9959 if (size
> sizeof(assigned
))
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
])
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;
9990 /**************************************************************************
9991 * genAssign - generate code for assignment
9992 *************************************************************************/
9994 genAssign (iCode
* ic
)
9996 operand
*result
, *right
;
9998 emitComment (TRACEGEN
, __func__
);
10000 result
= IC_RESULT (ic
);
10001 right
= IC_RIGHT (ic
);
10004 aopOp (result
, 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
)
10021 symbol
*jtablo
= safeNewiTempLabel (NULL
);
10022 symbol
*jtabhi
= safeNewiTempLabel (NULL
);
10024 emitComment (TRACEGEN
, __func__
);
10026 aopOp (IC_JTCOND (ic
), ic
);
10030 bool needpulla
= pushRegIfSurv (m6502_reg_a
);
10031 // use X or Y for index?
10032 bool needpullind
= false;
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
;
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);
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);
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
);
10103 emitComment (TRACEGEN
, __func__
);
10105 /* if they are equivalent then do nothing */
10106 if (operandsEqu (result
, right
))
10109 unsigned topbytemask
= (IS_BITINT (resulttype
) && (SPEC_BITINTWIDTH (resulttype
) % 8)) ?
10110 (0xff >> (8 - SPEC_BITINTWIDTH (resulttype
) % 8)) : 0xff;
10113 aopOp (result
, 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
)) {
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
;
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
))
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);
10144 pullReg (m6502_reg_a
);
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
);
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
);
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
);
10175 if (IS_AOP_XA (AOP (result
) ) && AOP_SIZE (right
) == 1 )
10177 genCopy (result
, right
);
10180 symbol
*tlbl
= safeNewiTempLabel (NULL
);
10182 emitBranch ("bpl", tlbl
);
10183 storeConstToAop (0xff, AOP (result
), 1);
10184 safeEmitLabel (tlbl
);
10185 m6502_dirtyReg(m6502_reg_x
);
10190 wassert (AOP (result
)->type
!= AOP_REG
);
10192 save_a
= !m6502_reg_a
->isDead
&& signExtend
;
10194 pushReg(m6502_reg_a
, true);
10197 size
= AOP_SIZE (right
);
10198 if (AOP_SIZE (result
) < size
)
10199 size
= AOP_SIZE (result
);
10201 if (size
== 1 && signExtend
) {
10202 loadRegFromAop (m6502_reg_a
, AOP (right
), offset
);
10203 storeRegToAop (m6502_reg_a
, AOP (result
), offset
);
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
);
10215 transferAopAop (AOP (right
), offset
, AOP (result
), offset
);
10221 size
= AOP_SIZE (result
) - offset
;
10222 if (size
&& !signExtend
)
10225 storeConstToAop (0, AOP (result
), offset
++);
10232 if (!size
&& masktopbyte
)
10233 emit6502op ("and", IMMDFMT
, topbytemask
);
10234 storeRegToAop (m6502_reg_a
, AOP (result
), offset
++);
10239 pullReg(m6502_reg_a
);
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
);
10254 bool delayed_x
= false;
10256 emitComment (TRACEGEN
, __func__
);
10258 aopOp (result
, ic
);
10259 size
= AOP_SIZE (result
);
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
)
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);
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]);
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
)) {
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();
10318 needpulla
= pushRegIfSurv (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__
);
10338 aopOp (result
, ic
);
10340 emit6502op ("php", "");
10341 emit6502op ("sei", "");
10344 emit6502op ("plp", "");
10345 m6502_dirtyReg (m6502_reg_a
);
10346 storeRegToAop (m6502_reg_a
, AOP (result
), 0);
10349 m6502_freeReg (m6502_reg_a
);
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__
);
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
)
10377 if (IS_ITEMP (op
)) {
10378 sym
= OP_SYMBOL (op
);
10381 /* If only used by IFX, there might not be any register assigned */
10383 for(i
= 0; i
< sym
->nRegs
; 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
);
10403 initGenLineElement ();
10404 genLine
.lineElement
.ic
= ic
;
10407 if (!regalloc_dry_run
)
10408 printf ("ic %d op %d stack pushed %d\n", ic
->key
, ic
->op
, G
.stack
.pushed
);
10411 if (resultRemat (ic
))
10413 if (!regalloc_dry_run
)
10414 emitComment(TRACEGEN
, "skipping iCode since result will be rematerialized");
10420 if (!regalloc_dry_run
)
10421 emitComment(TRACEGEN
, "skipping generated iCode");
10425 for (i
= 0; i
< m6502_nRegs
; i
++)
10427 reg
= m6502_regWithIdx (i
);
10428 m6502_freeReg (reg
);
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; //
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
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;
10463 m6502_regWithIdx (i
)->isDead
= true;
10466 /* depending on the operation */
10485 case IPUSH_VALUE_AT_ADDRESS
:
10486 genPointerPush (ic
);
10502 genEndFunction (ic
);
10541 genCmp (ic
, ifxForOp (result
, ic
));
10546 genCmpEQorNE (ic
, ifxForOp (result
, ic
));
10558 genXor (ic
, ifxForOp (result
, ic
));
10562 genOr (ic
, ifxForOp (result
, ic
));
10566 genAnd (ic
, ifxForOp (result
, ic
));
10570 m6502_genInline (ic
);
10574 wassertl (0, "Unimplemented iCode: GETABIT");
10594 genRightShift (ic
);
10597 case GET_VALUE_AT_ADDRESS
:
10598 genPointerGet (ic
, NULL
); // TODO? ifxForOp (result, ic));
10601 case SET_VALUE_AT_ADDRESS
:
10602 genPointerSet (ic
);
10606 if (POINTER_SET (ic
))
10607 genPointerSet (ic
);
10633 if (!regalloc_dry_run
)
10634 addSet (&_G
.sendSet
, ic
);
10637 set
* sendSet
= NULL
;
10638 addSet (&sendSet
, ic
);
10640 deleteSet (&sendSet
);
10644 case DUMMY_READ_VOLATILE
:
10653 genEndCritical (ic
);
10657 emitcode("ERROR", "; Unimplemented iCode (%x)", ic
->op
);
10659 // wassertl (0, "Unknown iCode");
10663 static void init_aop_pass(void)
10665 if (m6502_aop_pass
[0])
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;
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 *************************************************************************/
10720 genm6502Code (iCode
*lic
)
10727 regalloc_dry_run
= false;
10729 m6502_dirtyReg (m6502_reg_a
);
10730 m6502_dirtyReg (m6502_reg_y
);
10731 m6502_dirtyReg (m6502_reg_x
);
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 */
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
) {
10754 debugFile
->writeScope (ic
);
10755 clevel
= ic
->level
;
10756 cblock
= ic
->block
;
10759 if (ic
->lineno
&& cln
!= ic
->lineno
) {
10761 debugFile
->writeCLine (ic
);
10763 if (!options
.noCcodeInAsm
)
10764 emitComment (ALWAYS
, "%s: %d: %s", ic
->filename
, ic
->lineno
, printCLine (ic
->filename
, ic
->lineno
));
10768 regalloc_dry_run_cost_bytes
= 0;
10769 regalloc_dry_run_cost_cycles
= 0;
10771 if (options
.iCodeInAsm
)
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' : '-';
10780 iLine
= printILine (ic
);
10781 emitComment (ALWAYS
, " [%s] ic:%d: %s", regsSurv
, ic
->key
, iLine
);
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
);
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 ();