1 /*****************************************************************************/
5 /* 6502 code generator */
9 /* (C) 1998-2001 Ullrich von Bassewitz */
11 /* D-70597 Stuttgart */
12 /* EMail: uz@cc65.org */
15 /* This software is provided 'as-is', without any expressed or implied */
16 /* warranty. In no event will the authors be held liable for any damages */
17 /* arising from the use of this software. */
19 /* Permission is granted to anyone to use this software for any purpose, */
20 /* including commercial applications, and to alter it and redistribute it */
21 /* freely, subject to the following restrictions: */
23 /* 1. The origin of this software must not be misrepresented; you must not */
24 /* claim that you wrote the original software. If you use this software */
25 /* in a product, an acknowledgment in the product documentation would be */
26 /* appreciated but is not required. */
27 /* 2. Altered source versions must be plainly marked as such, and must not */
28 /* be misrepresented as being the original software. */
29 /* 3. This notice may not be removed or altered from any source */
32 /*****************************************************************************/
62 /*****************************************************************************/
64 /*****************************************************************************/
68 /* Compiler relative stack pointer */
72 segment_t CurSeg = SEG_INV;
76 /*****************************************************************************/
78 /*****************************************************************************/
82 static void typeerror (unsigned type)
83 /* Print an error message about an invalid operand type */
85 Internal ("Invalid type in CF flags: %04X, type = %u", type, type & CF_TYPE);
90 static void CheckLocalOffs (unsigned Offs)
91 /* Check the offset into the stack for 8bit range */
94 /* Too many local vars */
95 Error ("Too many local variables");
101 static char* GetLabelName (unsigned flags, unsigned long label, unsigned offs)
103 static char lbuf [128]; /* Label name */
105 /* Create the correct label name */
106 switch (flags & CF_ADDRMASK) {
109 /* Static memory cell */
110 sprintf (lbuf, "%s+%u", LocalLabelName (label), offs);
115 sprintf (lbuf, "_%s+%u", (char*) label, offs);
119 /* Absolute address */
120 sprintf (lbuf, "$%04X", (unsigned)((label+offs) & 0xFFFF));
124 /* Variable in register bank */
125 sprintf (lbuf, "regbank+%u", (unsigned)((label+offs) & 0xFFFF));
129 Internal ("Invalid address flags");
132 /* Return a pointer to the static buffer */
138 /*****************************************************************************/
139 /* Pre- and postamble */
140 /*****************************************************************************/
144 void g_preamble (void)
145 /* Generate the assembler code preamble */
147 /* Generate the global segments and push them */
148 PushCodeSeg (NewCodeSeg (SegmentNames[SEG_CODE], ""));
149 PushDataSeg (NewDataSeg (""));
151 /* Identify the compiler version */
152 AddDataSegLine (DS, "; File generated by cc65 v %u.%u.%u",
153 VER_MAJOR, VER_MINOR, VER_PATCH);
155 /* Insert some object file options */
156 AddDataSegLine (DS, ".fopt\t\tcompiler,\"cc65 v %u.%u.%u\"",
157 VER_MAJOR, VER_MINOR, VER_PATCH);
159 /* If we're producing code for some other CPU, switch the command set */
160 if (CPU == CPU_65C02) {
161 AddDataSegLine (DS, ".pc02");
164 /* Allow auto import for runtime library routines */
165 AddDataSegLine (DS, ".autoimport\ton");
167 /* Switch the assembler into case sensitive mode */
168 AddDataSegLine (DS, ".case\t\ton");
170 /* Tell the assembler if we want to generate debug info */
171 AddDataSegLine (DS, ".debuginfo\t%s", (DebugInfo != 0)? "on" : "off");
173 /* Import the stack pointer for direct auto variable access */
174 AddDataSegLine (DS, ".importzp\tsp, sreg, regsave, regbank, tmp1, ptr1");
176 /* Define long branch macros */
177 AddDataSegLine (DS, ".macpack\tlongbranch");
182 /*****************************************************************************/
183 /* Segment support */
184 /*****************************************************************************/
188 static void UseSeg (int NewSeg)
189 /* Switch to a specific segment */
191 if (CurSeg != NewSeg) {
192 CurSeg = (segment_t) NewSeg;
193 if (CurSeg != SEG_CODE) {
194 AddDataSegLine (DS, ".segment\t\"%s\"", SegmentNames [CurSeg]);
201 void g_pushseg (struct CodeSeg** FCS, struct DataSeg** FDS, const char* FuncName)
202 /* Push the current segments and generate new ones for the given function */
204 PushCodeSeg (NewCodeSeg (SegmentNames[SEG_CODE], FuncName));
206 PushDataSeg (NewDataSeg (FuncName));
213 /* Restore the old segments */
221 void g_userodata (void)
222 /* Switch to the read only data segment */
229 void g_usedata (void)
230 /* Switch to the data segment */
238 /* Switch to the bss segment */
245 static void SegName (segment_t Seg, const char* Name)
246 /* Set the name of a segment */
248 /* Free the old name and set a new one */
249 NewSegName (Seg, Name);
251 /* If the new segment is the current segment, emit a segment directive
255 CurSeg = SEG_INV; /* Invalidate */
262 void g_codename (const char* Name)
263 /* Set the name of the CODE segment */
265 SegName (SEG_CODE, Name);
270 void g_rodataname (const char* Name)
271 /* Set the name of the RODATA segment */
273 SegName (SEG_RODATA, Name);
278 void g_dataname (const char* Name)
279 /* Set the name of the DATA segment */
281 SegName (SEG_DATA, Name);
286 void g_bssname (const char* Name)
287 /* Set the name of the BSS segment */
289 SegName (SEG_BSS, Name);
294 /*****************************************************************************/
296 /*****************************************************************************/
300 unsigned sizeofarg (unsigned flags)
301 /* Return the size of a function argument type that is encoded in flags */
303 switch (flags & CF_TYPE) {
306 return (flags & CF_FORCECHAR)? 1 : 2;
323 int pop (unsigned flags)
324 /* Pop an argument of the given size */
326 return oursp += sizeofarg (flags);
331 int push (unsigned flags)
332 /* Push an argument of the given size */
334 return oursp -= sizeofarg (flags);
339 static unsigned MakeByteOffs (unsigned Flags, unsigned Offs)
340 /* The value in Offs is an offset to an address in a/x. Make sure, an object
341 * of the type given in Flags can be loaded or stored into this address by
342 * adding part of the offset to the address in ax, so that the remaining
343 * offset fits into an index register. Return the remaining offset.
346 /* If the offset is too large for a byte register, add the high byte
347 * of the offset to the primary. Beware: We need a special correction
348 * if the offset in the low byte will overflow in the operation.
350 unsigned O = Offs & ~0xFFU;
351 if ((Offs & 0xFF) > 256 - sizeofarg (Flags)) {
352 /* We need to add the low byte also */
356 /* Do the correction if we need one */
358 g_inc (CF_INT | CF_CONST, O);
362 /* Return the new offset */
368 /*****************************************************************************/
369 /* Functions handling local labels */
370 /*****************************************************************************/
374 void g_defcodelabel (unsigned label)
375 /* Define a local code label */
377 AddCodeLabel (CS, LocalLabelName (label));
382 void g_defdatalabel (unsigned label)
383 /* Define a local data label */
385 AddDataSegLine (DS, "%s:", LocalLabelName (label));
390 /*****************************************************************************/
391 /* Functions handling global labels */
392 /*****************************************************************************/
396 void g_defgloblabel (const char* Name)
397 /* Define a global label with the given name */
399 /* Global labels are always data labels */
400 AddDataSegLine (DS, "_%s:", Name);
405 void g_defexport (const char* Name, int ZP)
406 /* Export the given label */
409 AddDataSegLine (DS, "\t.exportzp\t_%s", Name);
411 AddDataSegLine (DS, "\t.export\t\t_%s", Name);
417 void g_defimport (const char* Name, int ZP)
418 /* Import the given label */
421 AddDataSegLine (DS, "\t.importzp\t_%s", Name);
423 AddDataSegLine (DS, "\t.import\t\t_%s", Name);
429 /*****************************************************************************/
430 /* Load functions for various registers */
431 /*****************************************************************************/
435 static void ldaconst (unsigned val)
436 /* Load a with a constant */
438 AddCodeSegLine (CS, "lda #$%02X", val & 0xFF);
443 static void ldxconst (unsigned val)
444 /* Load x with a constant */
446 AddCodeSegLine (CS, "ldx #$%02X", val & 0xFF);
451 static void ldyconst (unsigned val)
452 /* Load y with a constant */
454 AddCodeSegLine (CS, "ldy #$%02X", val & 0xFF);
459 /*****************************************************************************/
460 /* Function entry and exit */
461 /*****************************************************************************/
465 /* Remember the argument size of a function. The variable is set by g_enter
466 * and used by g_leave. If the functions gets its argument size by the caller
467 * (variable param list or function without prototype), g_enter will set the
473 void g_enter (unsigned flags, unsigned argsize)
474 /* Function prologue */
476 if ((flags & CF_FIXARGC) != 0) {
477 /* Just remember the argument size for the leave */
481 AddCodeSegLine (CS, "jsr enter");
487 void g_leave (int flags, int val)
488 /* Function epilogue */
493 /* CF_REG is set if we're returning a value from the function */
494 if ((flags & CF_REG) == 0) {
499 /* How many bytes of locals do we have to drop? */
502 /* If we didn't have a variable argument list, don't call leave */
505 /* Load a function return code if needed */
506 if ((flags & CF_CONST) != 0) {
507 g_getimmed (flags, val, 0);
510 /* Drop stackframe or leave with rts */
513 AddCodeHint ("y:-"); /* Y register no longer used */
514 AddCodeSegLine (CS, "rts");
516 AddCodeHint ("y:-"); /* Y register no longer used */
517 AddCodeSegLine (CS, "jmp incsp%d", k);
521 AddCodeSegLine (CS, "jmp addysp");
526 strcpy (buf, "\tjmp\tleave");
528 /* We've a stack frame to drop */
532 /* Y register no longer used */
535 if (flags & CF_CONST) {
536 if ((flags & CF_TYPE) != CF_LONG) {
537 /* Constant int sized value given for return code */
539 /* Special case: return 0 */
541 } else if (((val >> 8) & 0xFF) == 0) {
542 /* Special case: constant with high byte zero */
543 ldaconst (val); /* Load low byte */
546 /* Others: arbitrary constant value */
547 g_getimmed (flags, val, 0); /* Load value */
550 /* Constant long value: No shortcut possible */
551 g_getimmed (flags, val, 0);
555 /* Output the jump */
556 AddCodeSegLine (CS, buf);
562 /*****************************************************************************/
563 /* Register variables */
564 /*****************************************************************************/
568 void g_save_regvars (int RegOffs, unsigned Bytes)
569 /* Save register variables */
571 /* Don't loop for up to two bytes */
574 AddCodeSegLine (CS, "lda regbank%+d", RegOffs);
575 AddCodeSegLine (CS, "jsr pusha");
577 } else if (Bytes == 2) {
579 AddCodeSegLine (CS, "lda regbank%+d", RegOffs);
580 AddCodeSegLine (CS, "ldx regbank%+d", RegOffs+1);
581 AddCodeSegLine (CS, "jsr pushax");
585 /* More than two bytes - loop */
586 unsigned Label = GetLocalLabel ();
588 ldyconst (Bytes - 1);
590 g_defcodelabel (Label);
591 AddCodeSegLine (CS, "lda regbank%+d,x", RegOffs-1);
592 AddCodeSegLine (CS, "sta (sp),y");
593 AddCodeSegLine (CS, "dey");
594 AddCodeSegLine (CS, "dex");
595 AddCodeSegLine (CS, "bne %s", LocalLabelName (Label));
599 /* We pushed stuff, correct the stack pointer */
605 void g_restore_regvars (int StackOffs, int RegOffs, unsigned Bytes)
606 /* Restore register variables */
608 /* Calculate the actual stack offset and check it */
610 CheckLocalOffs (StackOffs);
612 /* Don't loop for up to two bytes */
615 ldyconst (StackOffs);
616 AddCodeSegLine (CS, "lda (sp),y");
617 AddCodeSegLine (CS, "sta regbank%+d", RegOffs);
619 } else if (Bytes == 2) {
621 ldyconst (StackOffs);
622 AddCodeSegLine (CS, "lda (sp),y");
623 AddCodeSegLine (CS, "sta regbank%+d", RegOffs);
624 AddCodeSegLine (CS, "iny");
625 AddCodeSegLine (CS, "lda (sp),y");
626 AddCodeSegLine (CS, "sta regbank%+d", RegOffs+1);
630 /* More than two bytes - loop */
631 unsigned Label = GetLocalLabel ();
632 ldyconst (StackOffs+Bytes-1);
634 g_defcodelabel (Label);
635 AddCodeSegLine (CS, "lda (sp),y");
636 AddCodeSegLine (CS, "sta regbank%+d,x", RegOffs-1);
637 AddCodeSegLine (CS, "dey");
638 AddCodeSegLine (CS, "dex");
639 AddCodeSegLine (CS, "bne %s", LocalLabelName (Label));
646 /*****************************************************************************/
647 /* Fetching memory cells */
648 /*****************************************************************************/
652 void g_getimmed (unsigned flags, unsigned long val, unsigned offs)
653 /* Load a constant into the primary register */
655 if ((flags & CF_CONST) != 0) {
657 /* Numeric constant */
658 switch (flags & CF_TYPE) {
661 if ((flags & CF_FORCECHAR) != 0) {
667 ldxconst ((val >> 8) & 0xFF);
668 ldaconst (val & 0xFF);
673 AddCodeSegLine (CS, "ldx #$00");
674 AddCodeSegLine (CS, "stx sreg+1");
675 AddCodeSegLine (CS, "stx sreg");
676 AddCodeSegLine (CS, "lda #$%02X", (unsigned char) val);
677 } else if ((val & 0xFFFF00FF) == 0) {
678 AddCodeSegLine (CS, "lda #$00");
679 AddCodeSegLine (CS, "sta sreg+1");
680 AddCodeSegLine (CS, "sta sreg");
681 AddCodeSegLine (CS, "ldx #$%02X", (unsigned char) (val >> 8));
682 } else if ((val & 0xFFFF0000) == 0 && CodeSizeFactor > 140) {
683 AddCodeSegLine (CS, "lda #$00");
684 AddCodeSegLine (CS, "sta sreg+1");
685 AddCodeSegLine (CS, "sta sreg");
686 AddCodeSegLine (CS, "lda #$%02X", (unsigned char) val);
687 AddCodeSegLine (CS, "ldx #$%02X", (unsigned char) (val >> 8));
688 } else if ((val & 0xFFFFFF00) == 0xFFFFFF00) {
689 AddCodeSegLine (CS, "ldx #$FF");
690 AddCodeSegLine (CS, "stx sreg+1");
691 AddCodeSegLine (CS, "stx sreg");
692 if ((val & 0xFF) == 0xFF) {
693 AddCodeSegLine (CS, "txa");
695 AddCodeSegLine (CS, "lda #$%02X", (unsigned char) val);
697 } else if ((val & 0xFFFF00FF) == 0xFFFF00FF) {
698 AddCodeSegLine (CS, "lda #$FF");
699 AddCodeSegLine (CS, "sta sreg+1");
700 AddCodeSegLine (CS, "sta sreg");
701 AddCodeSegLine (CS, "ldx #$%02X", (unsigned char) (val >> 8));
703 /* Call a subroutine that will load following value */
704 AddCodeSegLine (CS, "jsr ldeax");
705 AddCodeSegLine (CS, ".dword $%08lX", val & 0xFFFFFFFF);
717 /* Some sort of label */
718 const char* Label = GetLabelName (flags, val, offs);
720 /* Load the address into the primary */
721 AddCodeSegLine (CS, "lda #<(%s)", Label);
722 AddCodeSegLine (CS, "ldx #>(%s)", Label);
729 void g_getstatic (unsigned flags, unsigned long label, unsigned offs)
730 /* Fetch an static memory cell into the primary register */
732 /* Create the correct label name */
733 char* lbuf = GetLabelName (flags, label, offs);
735 /* Check the size and generate the correct load operation */
736 switch (flags & CF_TYPE) {
739 if ((flags & CF_FORCECHAR) || (flags & CF_TEST)) {
740 AddCodeSegLine (CS, "lda %s", lbuf); /* load A from the label */
743 AddCodeSegLine (CS, "lda %s", lbuf); /* load A from the label */
744 if (!(flags & CF_UNSIGNED)) {
745 /* Must sign extend */
746 AddCodeSegLine (CS, "bpl *+3");
747 AddCodeSegLine (CS, "dex");
748 AddCodeHint ("x:!"); /* X is invalid now */
754 AddCodeSegLine (CS, "lda %s", lbuf);
755 if (flags & CF_TEST) {
756 AddCodeSegLine (CS, "ora %s+1", lbuf);
758 AddCodeSegLine (CS, "ldx %s+1", lbuf);
763 if (flags & CF_TEST) {
764 AddCodeSegLine (CS, "lda %s+3", lbuf);
765 AddCodeSegLine (CS, "ora %s+2", lbuf);
766 AddCodeSegLine (CS, "ora %s+1", lbuf);
767 AddCodeSegLine (CS, "ora %s+0", lbuf);
769 AddCodeSegLine (CS, "lda %s+3", lbuf);
770 AddCodeSegLine (CS, "sta sreg+1");
771 AddCodeSegLine (CS, "lda %s+2", lbuf);
772 AddCodeSegLine (CS, "sta sreg");
773 AddCodeSegLine (CS, "ldx %s+1", lbuf);
774 AddCodeSegLine (CS, "lda %s", lbuf);
786 void g_getlocal (unsigned flags, int offs)
787 /* Fetch specified local object (local var). */
790 CheckLocalOffs (offs);
791 switch (flags & CF_TYPE) {
794 if ((flags & CF_FORCECHAR) || (flags & CF_TEST)) {
795 if (CPU == CPU_65C02 && offs == 0) {
796 AddCodeSegLine (CS, "lda (sp)");
799 AddCodeSegLine (CS, "lda (sp),y");
803 AddCodeSegLine (CS, "ldx #$00");
804 AddCodeSegLine (CS, "lda (sp,x)");
807 AddCodeSegLine (CS, "ldx #$00");
808 AddCodeSegLine (CS, "lda (sp),y");
810 if ((flags & CF_UNSIGNED) == 0) {
811 AddCodeSegLine (CS, "bpl *+3");
812 AddCodeSegLine (CS, "dex");
813 AddCodeHint ("x:!"); /* X is invalid now */
819 CheckLocalOffs (offs + 1);
820 if (flags & CF_TEST) {
822 AddCodeSegLine (CS, "lda (sp),y");
823 AddCodeSegLine (CS, "dey");
824 AddCodeSegLine (CS, "ora (sp),y");
826 if (CodeSizeFactor > 180) {
828 AddCodeSegLine (CS, "lda (sp),y");
829 AddCodeSegLine (CS, "tax");
830 AddCodeSegLine (CS, "dey");
831 AddCodeSegLine (CS, "lda (sp),y");
835 AddCodeSegLine (CS, "jsr ldaxysp");
837 AddCodeSegLine (CS, "jsr ldax0sp");
846 AddCodeSegLine (CS, "jsr ldeaxysp");
848 AddCodeSegLine (CS, "jsr ldeax0sp");
859 void g_getind (unsigned flags, unsigned offs)
860 /* Fetch the specified object type indirect through the primary register
861 * into the primary register
864 /* If the offset is greater than 255, add the part that is > 255 to
865 * the primary. This way we get an easy addition and use the low byte
868 offs = MakeByteOffs (flags, offs);
870 /* Handle the indirect fetch */
871 switch (flags & CF_TYPE) {
874 /* Character sized */
877 if (flags & CF_UNSIGNED) {
878 AddCodeSegLine (CS, "jsr ldauidx");
880 AddCodeSegLine (CS, "jsr ldaidx");
883 if (flags & CF_UNSIGNED) {
884 if (CodeSizeFactor > 250) {
885 AddCodeSegLine (CS, "sta ptr1");
886 AddCodeSegLine (CS, "stx ptr1+1");
887 AddCodeSegLine (CS, "ldx #$00");
888 AddCodeSegLine (CS, "lda (ptr1,x)");
890 AddCodeSegLine (CS, "jsr ldaui");
893 AddCodeSegLine (CS, "jsr ldai");
899 if (flags & CF_TEST) {
901 AddCodeSegLine (CS, "sta ptr1");
902 AddCodeSegLine (CS, "stx ptr1+1");
903 AddCodeSegLine (CS, "lda (ptr1),y");
904 AddCodeSegLine (CS, "iny");
905 AddCodeSegLine (CS, "ora (ptr1),y");
908 AddCodeSegLine (CS, "jsr ldaxi");
911 AddCodeSegLine (CS, "jsr ldaxidx");
918 AddCodeSegLine (CS, "jsr ldeaxi");
921 AddCodeSegLine (CS, "jsr ldeaxidx");
923 if (flags & CF_TEST) {
924 AddCodeSegLine (CS, "jsr tsteax");
936 void g_leasp (int offs)
937 /* Fetch the address of the specified symbol into the primary register */
939 /* Calculate the offset relative to sp */
942 /* For value 0 we do direct code */
944 AddCodeSegLine (CS, "lda sp");
945 AddCodeSegLine (CS, "ldx sp+1");
947 if (CodeSizeFactor < 300) {
948 ldaconst (offs); /* Load A with offset value */
949 AddCodeSegLine (CS, "jsr leaasp"); /* Load effective address */
951 if (CPU == CPU_65C02 && offs == 1) {
952 AddCodeSegLine (CS, "lda sp");
953 AddCodeSegLine (CS, "ldx sp+1");
954 AddCodeSegLine (CS, "ina");
955 AddCodeSegLine (CS, "bne *+3");
956 AddCodeSegLine (CS, "inx");
957 AddCodeHint ("x:!"); /* Invalidate X */
960 AddCodeSegLine (CS, "clc");
961 AddCodeSegLine (CS, "ldx sp+1");
962 AddCodeSegLine (CS, "adc sp");
963 AddCodeSegLine (CS, "bcc *+3");
964 AddCodeSegLine (CS, "inx");
965 AddCodeHint ("x:!"); /* Invalidate X */
973 void g_leavariadic (int Offs)
974 /* Fetch the address of a parameter in a variadic function into the primary
978 unsigned ArgSizeOffs;
980 /* Calculate the offset relative to sp */
983 /* Get the offset of the parameter which is stored at sp+0 on function
984 * entry and check if this offset is reachable with a byte offset.
987 ArgSizeOffs = -oursp;
988 CheckLocalOffs (ArgSizeOffs);
990 /* Get the size of all parameters. */
991 if (ArgSizeOffs == 0 && CPU == CPU_65C02) {
992 AddCodeSegLine (CS, "lda (sp)");
994 ldyconst (ArgSizeOffs);
995 AddCodeSegLine (CS, "lda (sp),y");
998 /* Add the value of the stackpointer */
999 if (CodeSizeFactor > 250) {
1000 AddCodeSegLine (CS, "ldx sp+1");
1001 AddCodeSegLine (CS, "clc");
1002 AddCodeSegLine (CS, "adc sp");
1003 AddCodeSegLine (CS, "bcc *+3");
1004 AddCodeSegLine (CS, "inx");
1005 AddCodeHint ("x:!"); /* Invalidate X */
1007 AddCodeSegLine (CS, "jsr leaasp");
1010 /* Add the offset to the primary */
1012 g_inc (CF_INT | CF_CONST, Offs);
1013 } else if (Offs < 0) {
1014 g_dec (CF_INT | CF_CONST, -Offs);
1020 /*****************************************************************************/
1021 /* Store into memory */
1022 /*****************************************************************************/
1026 void g_putstatic (unsigned flags, unsigned long label, unsigned offs)
1027 /* Store the primary register into the specified static memory cell */
1029 /* Create the correct label name */
1030 char* lbuf = GetLabelName (flags, label, offs);
1032 /* Check the size and generate the correct store operation */
1033 switch (flags & CF_TYPE) {
1036 AddCodeSegLine (CS, "sta %s", lbuf);
1040 AddCodeSegLine (CS, "sta %s", lbuf);
1041 AddCodeSegLine (CS, "stx %s+1", lbuf);
1045 AddCodeSegLine (CS, "sta %s", lbuf);
1046 AddCodeSegLine (CS, "stx %s+1", lbuf);
1047 AddCodeSegLine (CS, "ldy sreg");
1048 AddCodeSegLine (CS, "sty %s+2", lbuf);
1049 AddCodeSegLine (CS, "ldy sreg+1");
1050 AddCodeSegLine (CS, "sty %s+3", lbuf);
1061 void g_putlocal (unsigned Flags, int Offs, long Val)
1062 /* Put data into local object. */
1065 CheckLocalOffs (Offs);
1066 switch (Flags & CF_TYPE) {
1069 if (Flags & CF_CONST) {
1070 AddCodeSegLine (CS, "lda #$%02X", (unsigned char) Val);
1072 if (CPU == CPU_65C02 && Offs == 0) {
1073 AddCodeSegLine (CS, "sta (sp)");
1076 AddCodeSegLine (CS, "sta (sp),y");
1081 if (Flags & CF_CONST) {
1083 AddCodeSegLine (CS, "lda #$%02X", (unsigned char) (Val >> 8));
1084 AddCodeSegLine (CS, "sta (sp),y");
1085 if ((Flags & CF_NOKEEP) == 0) {
1086 /* Place high byte into X */
1087 AddCodeSegLine (CS, "tax");
1089 if (CPU == CPU_65C02 && Offs == 0) {
1090 AddCodeSegLine (CS, "lda #$%02X", (unsigned char) Val);
1091 AddCodeSegLine (CS, "sta (sp)");
1093 if ((Val & 0xFF) == Offs+1) {
1094 /* The value we need is already in Y */
1095 AddCodeSegLine (CS, "tya");
1096 AddCodeSegLine (CS, "dey");
1098 AddCodeSegLine (CS, "dey");
1099 AddCodeSegLine (CS, "lda #$%02X", (unsigned char) Val);
1101 AddCodeSegLine (CS, "sta (sp),y");
1104 if ((Flags & CF_NOKEEP) == 0 || CodeSizeFactor < 160) {
1107 AddCodeSegLine (CS, "jsr staxysp");
1109 AddCodeSegLine (CS, "jsr stax0sp");
1112 if (CPU == CPU_65C02 && Offs == 0) {
1113 AddCodeSegLine (CS, "sta (sp)");
1115 AddCodeSegLine (CS, "txa");
1116 AddCodeSegLine (CS, "sta (sp),y");
1119 AddCodeSegLine (CS, "sta (sp),y");
1120 AddCodeSegLine (CS, "iny");
1121 AddCodeSegLine (CS, "txa");
1122 AddCodeSegLine (CS, "sta (sp),y");
1129 if (Flags & CF_CONST) {
1130 g_getimmed (Flags, Val, 0);
1134 AddCodeSegLine (CS, "jsr steaxysp");
1136 AddCodeSegLine (CS, "jsr steax0sp");
1148 void g_putind (unsigned Flags, unsigned Offs)
1149 /* Store the specified object type in the primary register at the address
1150 * on the top of the stack
1153 /* We can handle offsets below $100 directly, larger offsets must be added
1154 * to the address. Since a/x is in use, best code is achieved by adding
1155 * just the high byte. Be sure to check if the low byte will overflow while
1158 if ((Offs & 0xFF) > 256 - sizeofarg (Flags | CF_FORCECHAR)) {
1160 /* Overflow - we need to add the low byte also */
1161 AddCodeSegLine (CS, "ldy #$00");
1162 AddCodeSegLine (CS, "clc");
1163 AddCodeSegLine (CS, "pha");
1164 AddCodeSegLine (CS, "lda #$%02X", Offs & 0xFF);
1165 AddCodeSegLine (CS, "adc (sp),y");
1166 AddCodeSegLine (CS, "sta (sp),y");
1167 AddCodeSegLine (CS, "iny");
1168 AddCodeSegLine (CS, "lda #$%02X", (Offs >> 8) & 0xFF);
1169 AddCodeSegLine (CS, "adc (sp),y");
1170 AddCodeSegLine (CS, "sta (sp),y");
1171 AddCodeSegLine (CS, "pla");
1173 /* Complete address is on stack, new offset is zero */
1176 } else if ((Offs & 0xFF00) != 0) {
1178 /* We can just add the high byte */
1179 AddCodeSegLine (CS, "ldy #$01");
1180 AddCodeSegLine (CS, "clc");
1181 AddCodeSegLine (CS, "pha");
1182 AddCodeSegLine (CS, "lda #$%02X", (Offs >> 8) & 0xFF);
1183 AddCodeSegLine (CS, "adc (sp),y");
1184 AddCodeSegLine (CS, "sta (sp),y");
1185 AddCodeSegLine (CS, "pla");
1187 /* Offset is now just the low byte */
1191 /* Check the size and determine operation */
1192 switch (Flags & CF_TYPE) {
1197 AddCodeSegLine (CS, "jsr staspidx");
1199 AddCodeSegLine (CS, "jsr staspp");
1206 AddCodeSegLine (CS, "jsr staxspidx");
1208 AddCodeSegLine (CS, "jsr staxspp");
1215 AddCodeSegLine (CS, "jsr steaxspidx");
1217 AddCodeSegLine (CS, "jsr steaxspp");
1226 /* Pop the argument which is always a pointer */
1232 /*****************************************************************************/
1233 /* type conversion and similiar stuff */
1234 /*****************************************************************************/
1238 void g_toslong (unsigned flags)
1239 /* Make sure, the value on TOS is a long. Convert if necessary */
1241 switch (flags & CF_TYPE) {
1245 if (flags & CF_UNSIGNED) {
1246 AddCodeSegLine (CS, "jsr tosulong");
1248 AddCodeSegLine (CS, "jsr toslong");
1263 void g_tosint (unsigned flags)
1264 /* Make sure, the value on TOS is an int. Convert if necessary */
1266 switch (flags & CF_TYPE) {
1273 AddCodeSegLine (CS, "jsr tosint");
1284 void g_reglong (unsigned flags)
1285 /* Make sure, the value in the primary register a long. Convert if necessary */
1287 switch (flags & CF_TYPE) {
1291 if (flags & CF_UNSIGNED) {
1292 if (CodeSizeFactor >= 200) {
1294 AddCodeSegLine (CS, "sty sreg");
1295 AddCodeSegLine (CS, "sty sreg+1");
1297 AddCodeSegLine (CS, "jsr axulong");
1300 AddCodeSegLine (CS, "jsr axlong");
1314 unsigned g_typeadjust (unsigned lhs, unsigned rhs)
1315 /* Adjust the integer operands before doing a binary operation. lhs is a flags
1316 * value, that corresponds to the value on TOS, rhs corresponds to the value
1317 * in (e)ax. The return value is the the flags value for the resulting type.
1320 unsigned ltype, rtype;
1323 /* Get the type spec from the flags */
1324 ltype = lhs & CF_TYPE;
1325 rtype = rhs & CF_TYPE;
1327 /* Check if a conversion is needed */
1328 if (ltype == CF_LONG && rtype != CF_LONG && (rhs & CF_CONST) == 0) {
1329 /* We must promote the primary register to long */
1331 /* Get the new rhs type */
1332 rhs = (rhs & ~CF_TYPE) | CF_LONG;
1334 } else if (ltype != CF_LONG && (lhs & CF_CONST) == 0 && rtype == CF_LONG) {
1335 /* We must promote the lhs to long */
1341 /* Get the new rhs type */
1342 lhs = (lhs & ~CF_TYPE) | CF_LONG;
1346 /* Determine the result type for the operation:
1347 * - The result is const if both operands are const.
1348 * - The result is unsigned if one of the operands is unsigned.
1349 * - The result is long if one of the operands is long.
1350 * - Otherwise the result is int sized.
1352 result = (lhs & CF_CONST) & (rhs & CF_CONST);
1353 result |= (lhs & CF_UNSIGNED) | (rhs & CF_UNSIGNED);
1354 if (rtype == CF_LONG || ltype == CF_LONG) {
1364 unsigned g_typecast (unsigned lhs, unsigned rhs)
1365 /* Cast the value in the primary register to the operand size that is flagged
1366 * by the lhs value. Return the result value.
1369 unsigned ltype, rtype;
1371 /* Get the type spec from the flags */
1372 ltype = lhs & CF_TYPE;
1373 rtype = rhs & CF_TYPE;
1375 /* Check if a conversion is needed */
1376 if (ltype == CF_LONG && rtype != CF_LONG && (rhs & CF_CONST) == 0) {
1377 /* We must promote the primary register to long */
1381 /* Do not need any other action. If the left type is int, and the primary
1382 * register is long, it will be automagically truncated. If the right hand
1383 * side is const, it is not located in the primary register and handled by
1384 * the expression parser code.
1387 /* Result is const if the right hand side was const */
1388 lhs |= (rhs & CF_CONST);
1390 /* The resulting type is that of the left hand side (that's why you called
1398 void g_scale (unsigned flags, long val)
1399 /* Scale the value in the primary register by the given value. If val is positive,
1400 * scale up, is val is negative, scale down. This function is used to scale
1401 * the operands or results of pointer arithmetic by the size of the type, the
1402 * pointer points to.
1407 /* Value may not be zero */
1409 Internal ("Data type has no size");
1410 } else if (val > 0) {
1413 if ((p2 = powerof2 (val)) > 0 && p2 <= 3) {
1415 /* Factor is 2, 4 or 8, use special function */
1416 switch (flags & CF_TYPE) {
1419 if (flags & CF_FORCECHAR) {
1421 AddCodeSegLine (CS, "asl a");
1428 if (CodeSizeFactor >= (p2+1)*130U) {
1429 AddCodeSegLine (CS, "stx tmp1");
1431 AddCodeSegLine (CS, "asl a");
1432 AddCodeSegLine (CS, "rol tmp1");
1434 AddCodeSegLine (CS, "ldx tmp1");
1436 if (flags & CF_UNSIGNED) {
1437 AddCodeSegLine (CS, "jsr shlax%d", p2);
1439 AddCodeSegLine (CS, "jsr aslax%d", p2);
1445 if (flags & CF_UNSIGNED) {
1446 AddCodeSegLine (CS, "jsr shleax%d", p2);
1448 AddCodeSegLine (CS, "jsr asleax%d", p2);
1457 } else if (val != 1) {
1459 /* Use a multiplication instead */
1460 g_mul (flags | CF_CONST, val);
1468 if ((p2 = powerof2 (val)) > 0 && p2 <= 3) {
1470 /* Factor is 2, 4 or 8, use special function */
1471 switch (flags & CF_TYPE) {
1474 if (flags & CF_FORCECHAR) {
1475 if (flags & CF_UNSIGNED) {
1477 AddCodeSegLine (CS, "lsr a");
1480 } else if (p2 <= 2) {
1481 AddCodeSegLine (CS, "cmp #$80");
1482 AddCodeSegLine (CS, "ror a");
1489 if (flags & CF_UNSIGNED) {
1490 if (CodeSizeFactor >= (p2+1)*130U) {
1491 AddCodeSegLine (CS, "stx tmp1");
1493 AddCodeSegLine (CS, "lsr tmp1");
1494 AddCodeSegLine (CS, "ror a");
1496 AddCodeSegLine (CS, "ldx tmp1");
1498 AddCodeSegLine (CS, "jsr lsrax%d", p2);
1501 if (CodeSizeFactor >= (p2+1)*150U) {
1502 AddCodeSegLine (CS, "stx tmp1");
1504 AddCodeSegLine (CS, "cpx #$80");
1505 AddCodeSegLine (CS, "ror tmp1");
1506 AddCodeSegLine (CS, "ror a");
1508 AddCodeSegLine (CS, "ldx tmp1");
1510 AddCodeSegLine (CS, "jsr asrax%d", p2);
1516 if (flags & CF_UNSIGNED) {
1517 AddCodeSegLine (CS, "jsr lsreax%d", p2);
1519 AddCodeSegLine (CS, "jsr asreax%d", p2);
1528 } else if (val != 1) {
1530 /* Use a division instead */
1531 g_div (flags | CF_CONST, val);
1539 /*****************************************************************************/
1540 /* Adds and subs of variables fix a fixed address */
1541 /*****************************************************************************/
1545 void g_addlocal (unsigned flags, int offs)
1546 /* Add a local variable to ax */
1548 /* Correct the offset and check it */
1550 CheckLocalOffs (offs);
1552 switch (flags & CF_TYPE) {
1555 AddCodeSegLine (CS, "ldy #$%02X", offs & 0xFF);
1556 AddCodeSegLine (CS, "clc");
1557 AddCodeSegLine (CS, "adc (sp),y");
1558 AddCodeSegLine (CS, "bcc *+3");
1559 AddCodeSegLine (CS, "inx");
1560 AddCodeHint ("x:!");
1564 AddCodeSegLine (CS, "ldy #$%02X", offs & 0xFF);
1565 AddCodeSegLine (CS, "clc");
1566 AddCodeSegLine (CS, "adc (sp),y");
1567 AddCodeSegLine (CS, "pha");
1568 AddCodeSegLine (CS, "txa");
1569 AddCodeSegLine (CS, "iny");
1570 AddCodeSegLine (CS, "adc (sp),y");
1571 AddCodeSegLine (CS, "tax");
1572 AddCodeSegLine (CS, "pla");
1576 /* Do it the old way */
1578 g_getlocal (flags, offs);
1590 void g_addstatic (unsigned flags, unsigned long label, unsigned offs)
1591 /* Add a static variable to ax */
1593 /* Create the correct label name */
1594 char* lbuf = GetLabelName (flags, label, offs);
1596 switch (flags & CF_TYPE) {
1599 AddCodeSegLine (CS, "clc");
1600 AddCodeSegLine (CS, "adc %s", lbuf);
1601 AddCodeSegLine (CS, "bcc *+3");
1602 AddCodeSegLine (CS, "inx");
1603 AddCodeHint ("x:!");
1607 AddCodeSegLine (CS, "clc");
1608 AddCodeSegLine (CS, "adc %s", lbuf);
1609 AddCodeSegLine (CS, "tay");
1610 AddCodeSegLine (CS, "txa");
1611 AddCodeSegLine (CS, "adc %s+1", lbuf);
1612 AddCodeSegLine (CS, "tax");
1613 AddCodeSegLine (CS, "tya");
1617 /* Do it the old way */
1619 g_getstatic (flags, label, offs);
1631 /*****************************************************************************/
1632 /* Compares of ax with a variable with fixed address */
1633 /*****************************************************************************/
1637 void g_cmplocal (unsigned flags, int offs)
1638 /* Compare a local variable to ax */
1640 Internal ("g_cmplocal not implemented");
1645 void g_cmpstatic (unsigned flags, unsigned label, unsigned offs)
1646 /* Compare a static variable to ax */
1648 Internal ("g_cmpstatic not implemented");
1653 /*****************************************************************************/
1654 /* Special op= functions */
1655 /*****************************************************************************/
1659 void g_addeqstatic (unsigned flags, unsigned long label, unsigned offs,
1661 /* Emit += for a static variable */
1663 /* Create the correct label name */
1664 char* lbuf = GetLabelName (flags, label, offs);
1666 /* Check the size and determine operation */
1667 switch (flags & CF_TYPE) {
1670 if (flags & CF_FORCECHAR) {
1671 AddCodeSegLine (CS, "ldx #$00");
1672 if (flags & CF_CONST) {
1674 AddCodeSegLine (CS, "inc %s", lbuf);
1675 AddCodeSegLine (CS, "lda %s", lbuf);
1677 AddCodeSegLine (CS, "lda #$%02X", (int)(val & 0xFF));
1678 AddCodeSegLine (CS, "clc");
1679 AddCodeSegLine (CS, "adc %s", lbuf);
1680 AddCodeSegLine (CS, "sta %s", lbuf);
1683 AddCodeSegLine (CS, "clc");
1684 AddCodeSegLine (CS, "adc %s", lbuf);
1685 AddCodeSegLine (CS, "sta %s", lbuf);
1687 if ((flags & CF_UNSIGNED) == 0) {
1688 AddCodeSegLine (CS, "bpl *+3");
1689 AddCodeSegLine (CS, "dex");
1690 AddCodeHint ("x:!"); /* Invalidate X */
1697 if (flags & CF_CONST) {
1699 unsigned L = GetLocalLabel ();
1700 AddCodeSegLine (CS, "inc %s", lbuf);
1701 AddCodeSegLine (CS, "bne %s", LocalLabelName (L));
1702 AddCodeSegLine (CS, "inc %s+1", lbuf);
1704 AddCodeSegLine (CS, "lda %s", lbuf); /* Hmmm... */
1705 AddCodeSegLine (CS, "ldx %s+1", lbuf);
1707 AddCodeSegLine (CS, "lda #$%02X", (int)(val & 0xFF));
1708 AddCodeSegLine (CS, "clc");
1709 AddCodeSegLine (CS, "adc %s", lbuf);
1710 AddCodeSegLine (CS, "sta %s", lbuf);
1712 unsigned L = GetLocalLabel ();
1713 AddCodeSegLine (CS, "bcc %s", LocalLabelName (L));
1714 AddCodeSegLine (CS, "inc %s+1", lbuf);
1716 AddCodeSegLine (CS, "ldx %s+1", lbuf);
1718 AddCodeSegLine (CS, "lda #$%02X", (unsigned char)(val >> 8));
1719 AddCodeSegLine (CS, "adc %s+1", lbuf);
1720 AddCodeSegLine (CS, "sta %s+1", lbuf);
1721 AddCodeSegLine (CS, "tax");
1722 AddCodeSegLine (CS, "lda %s", lbuf);
1726 AddCodeSegLine (CS, "clc");
1727 AddCodeSegLine (CS, "adc %s", lbuf);
1728 AddCodeSegLine (CS, "sta %s", lbuf);
1729 AddCodeSegLine (CS, "txa");
1730 AddCodeSegLine (CS, "adc %s+1", lbuf);
1731 AddCodeSegLine (CS, "sta %s+1", lbuf);
1732 AddCodeSegLine (CS, "tax");
1733 AddCodeSegLine (CS, "lda %s", lbuf);
1738 if (flags & CF_CONST) {
1740 AddCodeSegLine (CS, "ldy #<(%s)", lbuf);
1741 AddCodeSegLine (CS, "sty ptr1");
1742 AddCodeSegLine (CS, "ldy #>(%s+1)", lbuf);
1744 AddCodeSegLine (CS, "jsr laddeq1");
1746 AddCodeSegLine (CS, "lda #$%02X", (int)(val & 0xFF));
1747 AddCodeSegLine (CS, "jsr laddeqa");
1750 g_getstatic (flags, label, offs);
1752 g_putstatic (flags, label, offs);
1755 AddCodeSegLine (CS, "ldy #<(%s)", lbuf);
1756 AddCodeSegLine (CS, "sty ptr1");
1757 AddCodeSegLine (CS, "ldy #>(%s+1)", lbuf);
1758 AddCodeSegLine (CS, "jsr laddeq");
1769 void g_addeqlocal (unsigned flags, int offs, unsigned long val)
1770 /* Emit += for a local variable */
1772 /* Calculate the true offset, check it, load it into Y */
1774 CheckLocalOffs (offs);
1776 /* Check the size and determine operation */
1777 switch (flags & CF_TYPE) {
1780 if (flags & CF_FORCECHAR) {
1782 AddCodeSegLine (CS, "ldx #$00");
1783 if (flags & CF_CONST) {
1784 AddCodeSegLine (CS, "clc");
1785 AddCodeSegLine (CS, "lda #$%02X", (int)(val & 0xFF));
1786 AddCodeSegLine (CS, "adc (sp,x)");
1787 AddCodeSegLine (CS, "sta (sp,x)");
1789 AddCodeSegLine (CS, "clc");
1790 AddCodeSegLine (CS, "adc (sp,x)");
1791 AddCodeSegLine (CS, "sta (sp,x)");
1795 AddCodeSegLine (CS, "ldx #$00");
1796 if (flags & CF_CONST) {
1797 AddCodeSegLine (CS, "clc");
1798 AddCodeSegLine (CS, "lda #$%02X", (int)(val & 0xFF));
1799 AddCodeSegLine (CS, "adc (sp),y");
1800 AddCodeSegLine (CS, "sta (sp),y");
1802 AddCodeSegLine (CS, "clc");
1803 AddCodeSegLine (CS, "adc (sp),y");
1804 AddCodeSegLine (CS, "sta (sp),y");
1807 if ((flags & CF_UNSIGNED) == 0) {
1808 AddCodeSegLine (CS, "bpl *+3");
1809 AddCodeSegLine (CS, "dex");
1810 AddCodeHint ("x:!"); /* Invalidate X */
1817 if (flags & CF_CONST) {
1818 g_getimmed (flags, val, 0);
1821 AddCodeSegLine (CS, "jsr addeq0sp");
1824 AddCodeSegLine (CS, "jsr addeqysp");
1829 if (flags & CF_CONST) {
1830 g_getimmed (flags, val, 0);
1833 AddCodeSegLine (CS, "jsr laddeq0sp");
1836 AddCodeSegLine (CS, "jsr laddeqysp");
1847 void g_addeqind (unsigned flags, unsigned offs, unsigned long val)
1848 /* Emit += for the location with address in ax */
1850 /* If the offset is too large for a byte register, add the high byte
1851 * of the offset to the primary. Beware: We need a special correction
1852 * if the offset in the low byte will overflow in the operation.
1854 offs = MakeByteOffs (flags, offs);
1856 /* Check the size and determine operation */
1857 switch (flags & CF_TYPE) {
1860 AddCodeSegLine (CS, "sta ptr1");
1861 AddCodeSegLine (CS, "stx ptr1+1");
1863 AddCodeSegLine (CS, "ldx #$00");
1864 AddCodeSegLine (CS, "lda #$%02X", (int)(val & 0xFF));
1865 AddCodeSegLine (CS, "clc");
1866 AddCodeSegLine (CS, "adc (ptr1,x)");
1867 AddCodeSegLine (CS, "sta (ptr1,x)");
1869 AddCodeSegLine (CS, "ldy #$%02X", offs);
1870 AddCodeSegLine (CS, "ldx #$00");
1871 AddCodeSegLine (CS, "lda #$%02X", (int)(val & 0xFF));
1872 AddCodeSegLine (CS, "clc");
1873 AddCodeSegLine (CS, "adc (ptr1),y");
1874 AddCodeSegLine (CS, "sta (ptr1),y");
1879 if (CodeSizeFactor >= 200) {
1880 /* Lots of code, use only if size is not important */
1881 AddCodeSegLine (CS, "sta ptr1");
1882 AddCodeSegLine (CS, "stx ptr1+1");
1883 AddCodeSegLine (CS, "ldy #$%02X", offs);
1884 AddCodeSegLine (CS, "lda #$%02X", (int)(val & 0xFF));
1885 AddCodeSegLine (CS, "clc");
1886 AddCodeSegLine (CS, "adc (ptr1),y");
1887 AddCodeSegLine (CS, "sta (ptr1),y");
1888 AddCodeSegLine (CS, "pha");
1889 AddCodeSegLine (CS, "iny");
1890 AddCodeSegLine (CS, "lda #$%02X", (unsigned char)(val >> 8));
1891 AddCodeSegLine (CS, "adc (ptr1),y");
1892 AddCodeSegLine (CS, "sta (ptr1),y");
1893 AddCodeSegLine (CS, "tax");
1894 AddCodeSegLine (CS, "pla");
1900 AddCodeSegLine (CS, "jsr pushax"); /* Push the address */
1901 push (flags); /* Correct the internal sp */
1902 g_getind (flags, offs); /* Fetch the value */
1903 g_inc (flags, val); /* Increment value in primary */
1904 g_putind (flags, offs); /* Store the value back */
1914 void g_subeqstatic (unsigned flags, unsigned long label, unsigned offs,
1916 /* Emit -= for a static variable */
1918 /* Create the correct label name */
1919 char* lbuf = GetLabelName (flags, label, offs);
1921 /* Check the size and determine operation */
1922 switch (flags & CF_TYPE) {
1925 if (flags & CF_FORCECHAR) {
1926 AddCodeSegLine (CS, "ldx #$00");
1927 if (flags & CF_CONST) {
1929 AddCodeSegLine (CS, "dec %s", lbuf);
1930 AddCodeSegLine (CS, "lda %s", lbuf);
1932 AddCodeSegLine (CS, "sec");
1933 AddCodeSegLine (CS, "lda %s", lbuf);
1934 AddCodeSegLine (CS, "sbc #$%02X", (int)(val & 0xFF));
1935 AddCodeSegLine (CS, "sta %s", lbuf);
1938 AddCodeSegLine (CS, "sec");
1939 AddCodeSegLine (CS, "sta tmp1");
1940 AddCodeSegLine (CS, "lda %s", lbuf);
1941 AddCodeSegLine (CS, "sbc tmp1");
1942 AddCodeSegLine (CS, "sta %s", lbuf);
1944 if ((flags & CF_UNSIGNED) == 0) {
1945 AddCodeSegLine (CS, "bpl *+3");
1946 AddCodeSegLine (CS, "dex");
1947 AddCodeHint ("x:!"); /* Invalidate X */
1954 AddCodeSegLine (CS, "sec");
1955 if (flags & CF_CONST) {
1956 AddCodeSegLine (CS, "lda %s", lbuf);
1957 AddCodeSegLine (CS, "sbc #$%02X", (unsigned char)val);
1958 AddCodeSegLine (CS, "sta %s", lbuf);
1960 unsigned L = GetLocalLabel ();
1961 AddCodeSegLine (CS, "bcs %s", LocalLabelName (L));
1962 AddCodeSegLine (CS, "dec %s+1", lbuf);
1964 AddCodeSegLine (CS, "ldx %s+1", lbuf);
1966 AddCodeSegLine (CS, "lda %s+1", lbuf);
1967 AddCodeSegLine (CS, "sbc #$%02X", (unsigned char)(val >> 8));
1968 AddCodeSegLine (CS, "sta %s+1", lbuf);
1969 AddCodeSegLine (CS, "tax");
1970 AddCodeSegLine (CS, "lda %s", lbuf);
1973 AddCodeSegLine (CS, "sta tmp1");
1974 AddCodeSegLine (CS, "lda %s", lbuf);
1975 AddCodeSegLine (CS, "sbc tmp1");
1976 AddCodeSegLine (CS, "sta %s", lbuf);
1977 AddCodeSegLine (CS, "stx tmp1");
1978 AddCodeSegLine (CS, "lda %s+1", lbuf);
1979 AddCodeSegLine (CS, "sbc tmp1");
1980 AddCodeSegLine (CS, "sta %s+1", lbuf);
1981 AddCodeSegLine (CS, "tax");
1982 AddCodeSegLine (CS, "lda %s", lbuf);
1987 if (flags & CF_CONST) {
1989 AddCodeSegLine (CS, "ldy #<(%s)", lbuf);
1990 AddCodeSegLine (CS, "sty ptr1");
1991 AddCodeSegLine (CS, "ldy #>(%s+1)", lbuf);
1993 AddCodeSegLine (CS, "jsr lsubeq1");
1995 AddCodeSegLine (CS, "lda #$%02X", (unsigned char)val);
1996 AddCodeSegLine (CS, "jsr lsubeqa");
1999 g_getstatic (flags, label, offs);
2001 g_putstatic (flags, label, offs);
2004 AddCodeSegLine (CS, "ldy #<(%s)", lbuf);
2005 AddCodeSegLine (CS, "sty ptr1");
2006 AddCodeSegLine (CS, "ldy #>(%s+1)", lbuf);
2007 AddCodeSegLine (CS, "jsr lsubeq");
2018 void g_subeqlocal (unsigned flags, int offs, unsigned long val)
2019 /* Emit -= for a local variable */
2021 /* Calculate the true offset, check it, load it into Y */
2023 CheckLocalOffs (offs);
2025 /* Check the size and determine operation */
2026 switch (flags & CF_TYPE) {
2029 if (flags & CF_FORCECHAR) {
2031 AddCodeSegLine (CS, "ldx #$00");
2032 AddCodeSegLine (CS, "sec");
2033 if (flags & CF_CONST) {
2034 AddCodeSegLine (CS, "lda (sp),y");
2035 AddCodeSegLine (CS, "sbc #$%02X", (unsigned char)val);
2037 AddCodeSegLine (CS, "sta tmp1");
2038 AddCodeSegLine (CS, "lda (sp),y");
2039 AddCodeSegLine (CS, "sbc tmp1");
2041 AddCodeSegLine (CS, "sta (sp),y");
2042 if ((flags & CF_UNSIGNED) == 0) {
2043 AddCodeSegLine (CS, "bpl *+3");
2044 AddCodeSegLine (CS, "dex");
2045 AddCodeHint ("x:!"); /* Invalidate X */
2052 if (flags & CF_CONST) {
2053 g_getimmed (flags, val, 0);
2056 AddCodeSegLine (CS, "jsr subeq0sp");
2059 AddCodeSegLine (CS, "jsr subeqysp");
2064 if (flags & CF_CONST) {
2065 g_getimmed (flags, val, 0);
2068 AddCodeSegLine (CS, "jsr lsubeq0sp");
2071 AddCodeSegLine (CS, "jsr lsubeqysp");
2082 void g_subeqind (unsigned flags, unsigned offs, unsigned long val)
2083 /* Emit -= for the location with address in ax */
2085 /* If the offset is too large for a byte register, add the high byte
2086 * of the offset to the primary. Beware: We need a special correction
2087 * if the offset in the low byte will overflow in the operation.
2089 offs = MakeByteOffs (flags, offs);
2091 /* Check the size and determine operation */
2092 switch (flags & CF_TYPE) {
2095 AddCodeSegLine (CS, "sta ptr1");
2096 AddCodeSegLine (CS, "stx ptr1+1");
2098 AddCodeSegLine (CS, "ldx #$00");
2099 AddCodeSegLine (CS, "lda (ptr1,x)");
2100 AddCodeSegLine (CS, "sec");
2101 AddCodeSegLine (CS, "sbc #$%02X", (unsigned char)val);
2102 AddCodeSegLine (CS, "sta (ptr1,x)");
2104 AddCodeSegLine (CS, "ldy #$%02X", offs);
2105 AddCodeSegLine (CS, "ldx #$00");
2106 AddCodeSegLine (CS, "lda (ptr1),y");
2107 AddCodeSegLine (CS, "sec");
2108 AddCodeSegLine (CS, "sbc #$%02X", (unsigned char)val);
2109 AddCodeSegLine (CS, "sta (ptr1),y");
2114 if (CodeSizeFactor >= 200) {
2115 /* Lots of code, use only if size is not important */
2116 AddCodeSegLine (CS, "sta ptr1");
2117 AddCodeSegLine (CS, "stx ptr1+1");
2118 AddCodeSegLine (CS, "ldy #$%02X", offs);
2119 AddCodeSegLine (CS, "lda (ptr1),y");
2120 AddCodeSegLine (CS, "sec");
2121 AddCodeSegLine (CS, "sbc #$%02X", (unsigned char)val);
2122 AddCodeSegLine (CS, "sta (ptr1),y");
2123 AddCodeSegLine (CS, "pha");
2124 AddCodeSegLine (CS, "iny");
2125 AddCodeSegLine (CS, "lda (ptr1),y");
2126 AddCodeSegLine (CS, "sbc #$%02X", (unsigned char)(val >> 8));
2127 AddCodeSegLine (CS, "sta (ptr1),y");
2128 AddCodeSegLine (CS, "tax");
2129 AddCodeSegLine (CS, "pla");
2135 AddCodeSegLine (CS, "jsr pushax"); /* Push the address */
2136 push (flags); /* Correct the internal sp */
2137 g_getind (flags, offs); /* Fetch the value */
2138 g_dec (flags, val); /* Increment value in primary */
2139 g_putind (flags, offs); /* Store the value back */
2149 /*****************************************************************************/
2150 /* Add a variable address to the value in ax */
2151 /*****************************************************************************/
2155 void g_addaddr_local (unsigned flags, int offs)
2156 /* Add the address of a local variable to ax */
2158 /* Add the offset */
2161 /* We cannot address more then 256 bytes of locals anyway */
2162 CheckLocalOffs (offs);
2163 AddCodeSegLine (CS, "clc");
2164 AddCodeSegLine (CS, "adc #$%02X", offs & 0xFF);
2165 AddCodeSegLine (CS, "bcc *+4"); /* Do also skip the CLC insn below */
2166 AddCodeSegLine (CS, "inx");
2167 AddCodeHint ("x:!"); /* Invalidate X */
2170 /* Add the current stackpointer value */
2171 AddCodeSegLine (CS, "clc");
2172 AddCodeSegLine (CS, "adc sp");
2173 AddCodeSegLine (CS, "tay");
2174 AddCodeSegLine (CS, "txa");
2175 AddCodeSegLine (CS, "adc sp+1");
2176 AddCodeSegLine (CS, "tax");
2177 AddCodeSegLine (CS, "tya");
2182 void g_addaddr_static (unsigned flags, unsigned long label, unsigned offs)
2183 /* Add the address of a static variable to ax */
2185 /* Create the correct label name */
2186 char* lbuf = GetLabelName (flags, label, offs);
2188 /* Add the address to the current ax value */
2189 AddCodeSegLine (CS, "clc");
2190 AddCodeSegLine (CS, "adc #<(%s)", lbuf);
2191 AddCodeSegLine (CS, "tay");
2192 AddCodeSegLine (CS, "txa");
2193 AddCodeSegLine (CS, "adc #>(%s)", lbuf);
2194 AddCodeSegLine (CS, "tax");
2195 AddCodeSegLine (CS, "tya");
2200 /*****************************************************************************/
2202 /*****************************************************************************/
2206 void g_save (unsigned flags)
2207 /* Copy primary register to hold register. */
2209 /* Check the size and determine operation */
2210 switch (flags & CF_TYPE) {
2213 if (flags & CF_FORCECHAR) {
2214 AddCodeSegLine (CS, "pha");
2220 AddCodeSegLine (CS, "sta regsave");
2221 AddCodeSegLine (CS, "stx regsave+1");
2225 AddCodeSegLine (CS, "jsr saveeax");
2235 void g_restore (unsigned flags)
2236 /* Copy hold register to primary. */
2238 /* Check the size and determine operation */
2239 switch (flags & CF_TYPE) {
2242 if (flags & CF_FORCECHAR) {
2243 AddCodeSegLine (CS, "pla");
2249 AddCodeSegLine (CS, "lda regsave");
2250 AddCodeSegLine (CS, "ldx regsave+1");
2254 AddCodeSegLine (CS, "jsr resteax");
2264 void g_cmp (unsigned flags, unsigned long val)
2265 /* Immidiate compare. The primary register will not be changed, Z flag
2269 /* Check the size and determine operation */
2270 switch (flags & CF_TYPE) {
2273 if (flags & CF_FORCECHAR) {
2274 AddCodeSegLine (CS, "cmp #$%02X", (unsigned char)val);
2280 AddCodeSegLine (CS, "cmp #$%02X", (unsigned char)val);
2281 AddCodeSegLine (CS, "bne *+4");
2282 AddCodeSegLine (CS, "cpx #$%02X", (unsigned char)(val >> 8));
2286 Internal ("g_cmp: Long compares not implemented");
2296 static void oper (unsigned flags, unsigned long val, char** subs)
2297 /* Encode a binary operation. subs is a pointer to four groups of three
2299 * 0-2 --> Operate on ints
2300 * 3-5 --> Operate on unsigneds
2301 * 6-8 --> Operate on longs
2302 * 9-11 --> Operate on unsigned longs
2304 * The first subroutine names in each string group is used to encode an
2305 * operation with a zero constant, the second to encode an operation with
2306 * a 8 bit constant, and the third is used in all other cases.
2311 /* Determine the offset into the array */
2312 offs = (flags & CF_UNSIGNED)? 3 : 0;
2313 switch (flags & CF_TYPE) {
2326 /* Encode the operation */
2327 if (flags & CF_CONST) {
2328 /* Constant value given */
2329 if (val == 0 && subs [offs+0]) {
2330 /* Special case: constant with value zero */
2331 AddCodeSegLine (CS, "jsr %s", subs [offs+0]);
2332 } else if (val < 0x100 && subs [offs+1]) {
2333 /* Special case: constant with high byte zero */
2334 ldaconst (val); /* Load low byte */
2335 AddCodeSegLine (CS, "jsr %s", subs [offs+1]);
2337 /* Others: arbitrary constant value */
2338 g_getimmed (flags, val, 0); /* Load value */
2339 AddCodeSegLine (CS, "jsr %s", subs [offs+2]);
2342 /* Value not constant (is already in (e)ax) */
2343 AddCodeSegLine (CS, "jsr %s", subs [offs+2]);
2346 /* The operation will pop it's argument */
2352 void g_test (unsigned flags)
2353 /* Test the value in the primary and set the condition codes */
2355 switch (flags & CF_TYPE) {
2358 if (flags & CF_FORCECHAR) {
2359 AddCodeSegLine (CS, "tax");
2365 AddCodeSegLine (CS, "stx tmp1");
2366 AddCodeSegLine (CS, "ora tmp1");
2370 if (flags & CF_UNSIGNED) {
2371 AddCodeSegLine (CS, "jsr utsteax");
2373 AddCodeSegLine (CS, "jsr tsteax");
2385 void g_push (unsigned flags, unsigned long val)
2386 /* Push the primary register or a constant value onto the stack */
2390 if (flags & CF_CONST && (flags & CF_TYPE) != CF_LONG) {
2392 /* We have a constant 8 or 16 bit value */
2393 if ((flags & CF_TYPE) == CF_CHAR && (flags & CF_FORCECHAR)) {
2395 /* Handle as 8 bit value */
2396 if (CodeSizeFactor >= 165 || val > 2) {
2398 AddCodeSegLine (CS, "jsr pusha");
2400 AddCodeSegLine (CS, "jsr pushc%d", (int) val);
2405 /* Handle as 16 bit value */
2406 hi = (unsigned char) (val >> 8);
2408 AddCodeSegLine (CS, "jsr push%u", (unsigned) val);
2409 } else if (hi == 0 || hi == 0xFF) {
2410 /* Use special function */
2412 AddCodeSegLine (CS, "jsr %s", (hi == 0)? "pusha0" : "pushaFF");
2415 g_getimmed (flags, val, 0);
2416 AddCodeSegLine (CS, "jsr pushax");
2422 /* Value is not 16 bit or not constant */
2423 if (flags & CF_CONST) {
2424 /* Constant 32 bit value, load into eax */
2425 g_getimmed (flags, val, 0);
2428 /* Push the primary register */
2429 switch (flags & CF_TYPE) {
2432 if (flags & CF_FORCECHAR) {
2433 /* Handle as char */
2434 AddCodeSegLine (CS, "jsr pusha");
2439 AddCodeSegLine (CS, "jsr pushax");
2443 AddCodeSegLine (CS, "jsr pusheax");
2453 /* Adjust the stack offset */
2459 void g_swap (unsigned flags)
2460 /* Swap the primary register and the top of the stack. flags give the type
2461 * of *both* values (must have same size).
2464 switch (flags & CF_TYPE) {
2468 AddCodeSegLine (CS, "jsr swapstk");
2472 AddCodeSegLine (CS, "jsr swapestk");
2483 void g_call (unsigned Flags, const char* Label, unsigned ArgSize)
2484 /* Call the specified subroutine name */
2486 if ((Flags & CF_FIXARGC) == 0) {
2487 /* Pass the argument count */
2490 AddCodeSegLine (CS, "jsr _%s", Label);
2491 oursp += ArgSize; /* callee pops args */
2496 void g_callind (unsigned Flags, unsigned ArgSize)
2497 /* Call subroutine with address in AX */
2499 if ((Flags & CF_FIXARGC) == 0) {
2500 /* Pass arg count */
2503 AddCodeSegLine (CS, "jsr callax"); /* do the call */
2504 oursp += ArgSize; /* callee pops args */
2509 void g_jump (unsigned Label)
2510 /* Jump to specified internal label number */
2512 AddCodeSegLine (CS, "jmp %s", LocalLabelName (Label));
2517 void g_switch (unsigned Flags)
2518 /* Output switch statement preamble */
2520 switch (Flags & CF_TYPE) {
2524 AddCodeSegLine (CS, "jsr switch");
2528 AddCodeSegLine (CS, "jsr lswitch");
2539 void g_case (unsigned flags, unsigned label, unsigned long val)
2540 /* Create table code for one case selector */
2542 switch (flags & CF_TYPE) {
2546 AddCodeSegLine (CS, ".word $%04X, %s",
2547 (unsigned)(val & 0xFFFF),
2548 LocalLabelName (label));
2552 AddCodeSegLine (CS, ".dword $%08lX", val);
2553 AddCodeSegLine (CS, ".word %s", LocalLabelName (label));
2564 void g_truejump (unsigned flags, unsigned label)
2565 /* Jump to label if zero flag clear */
2567 AddCodeSegLine (CS, "jne %s", LocalLabelName (label));
2572 void g_falsejump (unsigned flags, unsigned label)
2573 /* Jump to label if zero flag set */
2575 AddCodeSegLine (CS, "jeq %s", LocalLabelName (label));
2580 static void mod_internal (int k, char* verb1, char* verb2)
2583 AddCodeSegLine (CS, "jsr %ssp%c", verb1, k + '0');
2587 AddCodeSegLine (CS, "jsr %ssp", verb2);
2593 void g_space (int space)
2594 /* Create or drop space on the stack */
2597 mod_internal (-space, "inc", "addy");
2598 } else if (space > 0) {
2599 mod_internal (space, "dec", "suby");
2605 void g_cstackcheck (void)
2606 /* Check for a C stack overflow */
2608 AddCodeSegLine (CS, "jsr cstkchk");
2613 void g_stackcheck (void)
2614 /* Check for a stack overflow */
2616 AddCodeSegLine (CS, "jsr stkchk");
2621 void g_add (unsigned flags, unsigned long val)
2622 /* Primary = TOS + Primary */
2624 static char* ops [12] = {
2625 0, "tosadda0", "tosaddax",
2626 0, "tosadda0", "tosaddax",
2631 if (flags & CF_CONST) {
2632 flags &= ~CF_FORCECHAR; /* Handle chars as ints */
2633 g_push (flags & ~CF_CONST, 0);
2635 oper (flags, val, ops);
2640 void g_sub (unsigned flags, unsigned long val)
2641 /* Primary = TOS - Primary */
2643 static char* ops [12] = {
2644 0, "tossuba0", "tossubax",
2645 0, "tossuba0", "tossubax",
2650 if (flags & CF_CONST) {
2651 flags &= ~CF_FORCECHAR; /* Handle chars as ints */
2652 g_push (flags & ~CF_CONST, 0);
2654 oper (flags, val, ops);
2659 void g_rsub (unsigned flags, unsigned long val)
2660 /* Primary = Primary - TOS */
2662 static char* ops [12] = {
2663 0, "tosrsuba0", "tosrsubax",
2664 0, "tosrsuba0", "tosrsubax",
2668 oper (flags, val, ops);
2673 void g_mul (unsigned flags, unsigned long val)
2674 /* Primary = TOS * Primary */
2676 static char* ops [12] = {
2677 0, "tosmula0", "tosmulax",
2678 0, "tosumula0", "tosumulax",
2685 /* Do strength reduction if the value is constant and a power of two */
2686 if (flags & CF_CONST && (p2 = powerof2 (val)) >= 0) {
2687 /* Generate a shift instead */
2692 /* If the right hand side is const, the lhs is not on stack but still
2693 * in the primary register.
2695 if (flags & CF_CONST) {
2697 switch (flags & CF_TYPE) {
2700 if (flags & CF_FORCECHAR) {
2701 /* Handle some special cases */
2705 AddCodeSegLine (CS, "sta tmp1");
2706 AddCodeSegLine (CS, "asl a");
2707 AddCodeSegLine (CS, "clc");
2708 AddCodeSegLine (CS, "adc tmp1");
2712 AddCodeSegLine (CS, "sta tmp1");
2713 AddCodeSegLine (CS, "asl a");
2714 AddCodeSegLine (CS, "asl a");
2715 AddCodeSegLine (CS, "clc");
2716 AddCodeSegLine (CS, "adc tmp1");
2720 AddCodeSegLine (CS, "sta tmp1");
2721 AddCodeSegLine (CS, "asl a");
2722 AddCodeSegLine (CS, "asl a");
2723 AddCodeSegLine (CS, "clc");
2724 AddCodeSegLine (CS, "adc tmp1");
2725 AddCodeSegLine (CS, "asl a");
2741 /* If we go here, we didn't emit code. Push the lhs on stack and fall
2742 * into the normal, non-optimized stuff.
2744 flags &= ~CF_FORCECHAR; /* Handle chars as ints */
2745 g_push (flags & ~CF_CONST, 0);
2749 /* Use long way over the stack */
2750 oper (flags, val, ops);
2755 void g_div (unsigned flags, unsigned long val)
2756 /* Primary = TOS / Primary */
2758 static char* ops [12] = {
2759 0, "tosdiva0", "tosdivax",
2760 0, "tosudiva0", "tosudivax",
2765 /* Do strength reduction if the value is constant and a power of two */
2767 if ((flags & CF_CONST) && (p2 = powerof2 (val)) >= 0) {
2768 /* Generate a shift instead */
2771 /* Generate a division */
2772 if (flags & CF_CONST) {
2773 /* lhs is not on stack */
2774 flags &= ~CF_FORCECHAR; /* Handle chars as ints */
2775 g_push (flags & ~CF_CONST, 0);
2777 oper (flags, val, ops);
2783 void g_mod (unsigned flags, unsigned long val)
2784 /* Primary = TOS % Primary */
2786 static char* ops [12] = {
2787 0, "tosmoda0", "tosmodax",
2788 0, "tosumoda0", "tosumodax",
2794 /* Check if we can do some cost reduction */
2795 if ((flags & CF_CONST) && (flags & CF_UNSIGNED) && val != 0xFFFFFFFF && (p2 = powerof2 (val)) >= 0) {
2796 /* We can do that with an AND operation */
2797 g_and (flags, val - 1);
2799 /* Do it the hard way... */
2800 if (flags & CF_CONST) {
2801 /* lhs is not on stack */
2802 flags &= ~CF_FORCECHAR; /* Handle chars as ints */
2803 g_push (flags & ~CF_CONST, 0);
2805 oper (flags, val, ops);
2811 void g_or (unsigned flags, unsigned long val)
2812 /* Primary = TOS | Primary */
2814 static char* ops [12] = {
2815 0, "tosora0", "tosorax",
2816 0, "tosora0", "tosorax",
2821 /* If the right hand side is const, the lhs is not on stack but still
2822 * in the primary register.
2824 if (flags & CF_CONST) {
2826 switch (flags & CF_TYPE) {
2829 if (flags & CF_FORCECHAR) {
2830 if ((val & 0xFF) != 0xFF) {
2831 AddCodeSegLine (CS, "ora #$%02X", (unsigned char)val);
2839 AddCodeSegLine (CS, "ora #$%02X", (unsigned char)val);
2846 AddCodeSegLine (CS, "ora #$%02X", (unsigned char)val);
2855 /* If we go here, we didn't emit code. Push the lhs on stack and fall
2856 * into the normal, non-optimized stuff.
2858 g_push (flags & ~CF_CONST, 0);
2862 /* Use long way over the stack */
2863 oper (flags, val, ops);
2868 void g_xor (unsigned flags, unsigned long val)
2869 /* Primary = TOS ^ Primary */
2871 static char* ops [12] = {
2872 0, "tosxora0", "tosxorax",
2873 0, "tosxora0", "tosxorax",
2879 /* If the right hand side is const, the lhs is not on stack but still
2880 * in the primary register.
2882 if (flags & CF_CONST) {
2884 switch (flags & CF_TYPE) {
2887 if (flags & CF_FORCECHAR) {
2888 if ((val & 0xFF) != 0) {
2889 AddCodeSegLine (CS, "eor #$%02X", (unsigned char)val);
2898 AddCodeSegLine (CS, "eor #$%02X", (unsigned char)val);
2901 } else if ((val & 0xFF) == 0) {
2902 AddCodeSegLine (CS, "pha");
2903 AddCodeSegLine (CS, "txa");
2904 AddCodeSegLine (CS, "eor #$%02X", (unsigned char)(val >> 8));
2905 AddCodeSegLine (CS, "tax");
2906 AddCodeSegLine (CS, "pla");
2914 AddCodeSegLine (CS, "eor #$%02X", (unsigned char)val);
2924 /* If we go here, we didn't emit code. Push the lhs on stack and fall
2925 * into the normal, non-optimized stuff.
2927 g_push (flags & ~CF_CONST, 0);
2931 /* Use long way over the stack */
2932 oper (flags, val, ops);
2937 void g_and (unsigned flags, unsigned long val)
2938 /* Primary = TOS & Primary */
2940 static char* ops [12] = {
2941 0, "tosanda0", "tosandax",
2942 0, "tosanda0", "tosandax",
2947 /* If the right hand side is const, the lhs is not on stack but still
2948 * in the primary register.
2950 if (flags & CF_CONST) {
2952 switch (flags & CF_TYPE) {
2955 if (flags & CF_FORCECHAR) {
2956 AddCodeSegLine (CS, "and #$%02X", (unsigned char)val);
2961 if ((val & 0xFFFF) != 0xFFFF) {
2966 } else if (val != 0xFF) {
2967 AddCodeSegLine (CS, "and #$%02X", (unsigned char)val);
2969 } else if ((val & 0xFF00) == 0xFF00) {
2970 AddCodeSegLine (CS, "and #$%02X", (unsigned char)val);
2971 } else if ((val & 0x00FF) == 0x0000) {
2972 AddCodeSegLine (CS, "txa");
2973 AddCodeSegLine (CS, "and #$%02X", (unsigned char)(val >> 8));
2974 AddCodeSegLine (CS, "tax");
2977 AddCodeSegLine (CS, "tay");
2978 AddCodeSegLine (CS, "txa");
2979 AddCodeSegLine (CS, "and #$%02X", (unsigned char)(val >> 8));
2980 AddCodeSegLine (CS, "tax");
2981 AddCodeSegLine (CS, "tya");
2982 if ((val & 0x00FF) != 0x00FF) {
2983 AddCodeSegLine (CS, "and #$%02X", (unsigned char)val);
2992 AddCodeSegLine (CS, "stx sreg+1");
2993 AddCodeSegLine (CS, "stx sreg");
2994 if ((val & 0xFF) != 0xFF) {
2995 AddCodeSegLine (CS, "and #$%02X", (unsigned char)val);
2998 } else if (val == 0xFF00) {
3000 AddCodeSegLine (CS, "sta sreg+1");
3001 AddCodeSegLine (CS, "sta sreg");
3010 /* If we go here, we didn't emit code. Push the lhs on stack and fall
3011 * into the normal, non-optimized stuff.
3013 g_push (flags & ~CF_CONST, 0);
3017 /* Use long way over the stack */
3018 oper (flags, val, ops);
3023 void g_asr (unsigned flags, unsigned long val)
3024 /* Primary = TOS >> Primary */
3026 static char* ops [12] = {
3027 0, "tosasra0", "tosasrax",
3028 0, "tosshra0", "tosshrax",
3033 /* If the right hand side is const, the lhs is not on stack but still
3034 * in the primary register.
3036 if (flags & CF_CONST) {
3038 switch (flags & CF_TYPE) {
3042 if (val >= 1 && val <= 3) {
3043 if (flags & CF_UNSIGNED) {
3044 AddCodeSegLine (CS, "jsr shrax%ld", val);
3046 AddCodeSegLine (CS, "jsr asrax%ld", val);
3049 } else if (val == 8 && (flags & CF_UNSIGNED)) {
3050 AddCodeSegLine (CS, "txa");
3057 if (val >= 1 && val <= 3) {
3058 if (flags & CF_UNSIGNED) {
3059 AddCodeSegLine (CS, "jsr shreax%ld", val);
3061 AddCodeSegLine (CS, "jsr asreax%ld", val);
3064 } else if (val == 8 && (flags & CF_UNSIGNED)) {
3065 AddCodeSegLine (CS, "txa");
3066 AddCodeSegLine (CS, "ldx sreg");
3067 AddCodeSegLine (CS, "ldy sreg+1");
3068 AddCodeSegLine (CS, "sty sreg");
3069 AddCodeSegLine (CS, "ldy #$00");
3070 AddCodeSegLine (CS, "sty sreg+1");
3072 } else if (val == 16) {
3073 AddCodeSegLine (CS, "ldy #$00");
3074 AddCodeSegLine (CS, "ldx sreg+1");
3075 if ((flags & CF_UNSIGNED) == 0) {
3076 AddCodeSegLine (CS, "bpl *+3");
3077 AddCodeSegLine (CS, "dey");
3078 AddCodeHint ("y:!");
3080 AddCodeSegLine (CS, "lda sreg");
3081 AddCodeSegLine (CS, "sty sreg+1");
3082 AddCodeSegLine (CS, "sty sreg");
3091 /* If we go here, we didn't emit code. Push the lhs on stack and fall
3092 * into the normal, non-optimized stuff.
3094 g_push (flags & ~CF_CONST, 0);
3098 /* Use long way over the stack */
3099 oper (flags, val, ops);
3104 void g_asl (unsigned flags, unsigned long val)
3105 /* Primary = TOS << Primary */
3107 static char* ops [12] = {
3108 0, "tosasla0", "tosaslax",
3109 0, "tosshla0", "tosshlax",
3115 /* If the right hand side is const, the lhs is not on stack but still
3116 * in the primary register.
3118 if (flags & CF_CONST) {
3120 switch (flags & CF_TYPE) {
3124 if (val >= 1 && val <= 3) {
3125 if (flags & CF_UNSIGNED) {
3126 AddCodeSegLine (CS, "jsr shlax%ld", val);
3128 AddCodeSegLine (CS, "jsr aslax%ld", val);
3131 } else if (val == 8) {
3132 AddCodeSegLine (CS, "tax");
3133 AddCodeSegLine (CS, "lda #$00");
3139 if (val >= 1 && val <= 3) {
3140 if (flags & CF_UNSIGNED) {
3141 AddCodeSegLine (CS, "jsr shleax%ld", val);
3143 AddCodeSegLine (CS, "jsr asleax%ld", val);
3146 } else if (val == 8) {
3147 AddCodeSegLine (CS, "ldy sreg");
3148 AddCodeSegLine (CS, "sty sreg+1");
3149 AddCodeSegLine (CS, "stx sreg");
3150 AddCodeSegLine (CS, "tax");
3151 AddCodeSegLine (CS, "lda #$00");
3153 } else if (val == 16) {
3154 AddCodeSegLine (CS, "stx sreg+1");
3155 AddCodeSegLine (CS, "sta sreg");
3156 AddCodeSegLine (CS, "lda #$00");
3157 AddCodeSegLine (CS, "tax");
3166 /* If we go here, we didn't emit code. Push the lhs on stack and fall
3167 * into the normal, non-optimized stuff.
3169 g_push (flags & ~CF_CONST, 0);
3173 /* Use long way over the stack */
3174 oper (flags, val, ops);
3179 void g_neg (unsigned flags)
3180 /* Primary = -Primary */
3182 switch (flags & CF_TYPE) {
3186 AddCodeSegLine (CS, "jsr negax");
3190 AddCodeSegLine (CS, "jsr negeax");
3200 void g_bneg (unsigned flags)
3201 /* Primary = !Primary */
3203 switch (flags & CF_TYPE) {
3206 AddCodeSegLine (CS, "jsr bnega");
3210 AddCodeSegLine (CS, "jsr bnegax");
3214 AddCodeSegLine (CS, "jsr bnegeax");
3224 void g_com (unsigned flags)
3225 /* Primary = ~Primary */
3227 switch (flags & CF_TYPE) {
3231 AddCodeSegLine (CS, "jsr complax");
3235 AddCodeSegLine (CS, "jsr compleax");
3245 void g_inc (unsigned flags, unsigned long val)
3246 /* Increment the primary register by a given number */
3248 /* Don't inc by zero */
3253 /* Generate code for the supported types */
3255 switch (flags & CF_TYPE) {
3258 if (flags & CF_FORCECHAR) {
3259 if (CPU == CPU_65C02 && val <= 2) {
3261 AddCodeSegLine (CS, "ina");
3264 AddCodeSegLine (CS, "clc");
3265 AddCodeSegLine (CS, "adc #$%02X", (unsigned char)val);
3272 if (CPU == CPU_65C02 && val == 1) {
3273 AddCodeSegLine (CS, "ina");
3274 AddCodeSegLine (CS, "bne *+3");
3275 AddCodeSegLine (CS, "inx");
3276 /* Tell the optimizer that the X register may be invalid */
3277 AddCodeHint ("x:!");
3278 } else if (CodeSizeFactor < 200) {
3281 AddCodeSegLine (CS, "jsr incax%lu", val);
3282 } else if (val <= 255) {
3284 AddCodeSegLine (CS, "jsr incaxy");
3286 g_add (flags | CF_CONST, val);
3289 /* Inline the code */
3291 if ((val & 0xFF) != 0) {
3292 AddCodeSegLine (CS, "clc");
3293 AddCodeSegLine (CS, "adc #$%02X", (unsigned char) val);
3294 AddCodeSegLine (CS, "bcc *+3");
3295 AddCodeSegLine (CS, "inx");
3296 /* Tell the optimizer that the X register may be invalid */
3297 AddCodeHint ("x:!");
3300 AddCodeSegLine (CS, "inx");
3303 AddCodeSegLine (CS, "inx");
3306 AddCodeSegLine (CS, "clc");
3307 if ((val & 0xFF) != 0) {
3308 AddCodeSegLine (CS, "adc #$%02X", (unsigned char) val);
3309 /* Tell the optimizer that the X register may be invalid */
3310 AddCodeHint ("x:!");
3312 AddCodeSegLine (CS, "pha");
3313 AddCodeSegLine (CS, "txa");
3314 AddCodeSegLine (CS, "adc #$%02X", (unsigned char) (val >> 8));
3315 AddCodeSegLine (CS, "tax");
3316 AddCodeSegLine (CS, "pla");
3324 AddCodeSegLine (CS, "jsr inceaxy");
3326 g_add (flags | CF_CONST, val);
3338 void g_dec (unsigned flags, unsigned long val)
3339 /* Decrement the primary register by a given number */
3341 /* Don't dec by zero */
3346 /* Generate code for the supported types */
3348 switch (flags & CF_TYPE) {
3351 if (flags & CF_FORCECHAR) {
3352 if (CPU == CPU_65C02 && val <= 2) {
3354 AddCodeSegLine (CS, "dea");
3357 AddCodeSegLine (CS, "sec");
3358 AddCodeSegLine (CS, "sbc #$%02X", (unsigned char)val);
3365 if (CodeSizeFactor < 200) {
3366 /* Use subroutines */
3368 AddCodeSegLine (CS, "jsr decax%d", (int) val);
3369 } else if (val <= 255) {
3371 AddCodeSegLine (CS, "jsr decaxy");
3373 g_sub (flags | CF_CONST, val);
3376 /* Inline the code */
3378 if ((val & 0xFF) != 0) {
3379 AddCodeSegLine (CS, "sec");
3380 AddCodeSegLine (CS, "sbc #$%02X", (unsigned char) val);
3381 AddCodeSegLine (CS, "bcs *+3");
3382 AddCodeSegLine (CS, "dex");
3383 /* Tell the optimizer that the X register may be invalid */
3384 AddCodeHint ("x:!");
3387 AddCodeSegLine (CS, "dex");
3390 AddCodeSegLine (CS, "dex");
3393 AddCodeSegLine (CS, "sec");
3394 if ((val & 0xFF) != 0) {
3395 AddCodeSegLine (CS, "sbc #$%02X", (unsigned char) val);
3396 /* Tell the optimizer that the X register may be invalid */
3397 AddCodeHint ("x:!");
3399 AddCodeSegLine (CS, "pha");
3400 AddCodeSegLine (CS, "txa");
3401 AddCodeSegLine (CS, "sbc #$%02X", (unsigned char) (val >> 8));
3402 AddCodeSegLine (CS, "tax");
3403 AddCodeSegLine (CS, "pla");
3411 AddCodeSegLine (CS, "jsr deceaxy");
3413 g_sub (flags | CF_CONST, val);
3426 * Following are the conditional operators. They compare the TOS against
3427 * the primary and put a literal 1 in the primary if the condition is
3428 * true, otherwise they clear the primary register
3433 void g_eq (unsigned flags, unsigned long val)
3434 /* Test for equal */
3436 static char* ops [12] = {
3437 "toseq00", "toseqa0", "toseqax",
3438 "toseq00", "toseqa0", "toseqax",
3443 /* If the right hand side is const, the lhs is not on stack but still
3444 * in the primary register.
3446 if (flags & CF_CONST) {
3448 switch (flags & CF_TYPE) {
3451 if (flags & CF_FORCECHAR) {
3452 AddCodeSegLine (CS, "cmp #$%02X", (unsigned char)val);
3453 AddCodeSegLine (CS, "jsr booleq");
3459 AddCodeSegLine (CS, "cpx #$%02X", (unsigned char)(val >> 8));
3460 AddCodeSegLine (CS, "bne *+4");
3461 AddCodeSegLine (CS, "cmp #$%02X", (unsigned char)val);
3462 AddCodeSegLine (CS, "jsr booleq");
3472 /* If we go here, we didn't emit code. Push the lhs on stack and fall
3473 * into the normal, non-optimized stuff.
3475 g_push (flags & ~CF_CONST, 0);
3479 /* Use long way over the stack */
3480 oper (flags, val, ops);
3485 void g_ne (unsigned flags, unsigned long val)
3486 /* Test for not equal */
3488 static char* ops [12] = {
3489 "tosne00", "tosnea0", "tosneax",
3490 "tosne00", "tosnea0", "tosneax",
3496 /* If the right hand side is const, the lhs is not on stack but still
3497 * in the primary register.
3499 if (flags & CF_CONST) {
3501 switch (flags & CF_TYPE) {
3504 if (flags & CF_FORCECHAR) {
3505 AddCodeSegLine (CS, "cmp #$%02X", (unsigned char)val);
3506 AddCodeSegLine (CS, "jsr boolne");
3512 AddCodeSegLine (CS, "cpx #$%02X", (unsigned char)(val >> 8));
3513 AddCodeSegLine (CS, "bne *+4");
3514 AddCodeSegLine (CS, "cmp #$%02X", (unsigned char)val);
3515 AddCodeSegLine (CS, "jsr boolne");
3525 /* If we go here, we didn't emit code. Push the lhs on stack and fall
3526 * into the normal, non-optimized stuff.
3528 g_push (flags & ~CF_CONST, 0);
3532 /* Use long way over the stack */
3533 oper (flags, val, ops);
3538 void g_lt (unsigned flags, unsigned long val)
3539 /* Test for less than */
3541 static char* ops [12] = {
3542 "toslt00", "toslta0", "tosltax",
3543 "tosult00", "tosulta0", "tosultax",
3548 /* If the right hand side is const, the lhs is not on stack but still
3549 * in the primary register.
3551 if (flags & CF_CONST) {
3553 /* Give a warning in some special cases */
3554 if ((flags & CF_UNSIGNED) && val == 0) {
3555 Warning ("Condition is never true");
3558 /* Look at the type */
3559 switch (flags & CF_TYPE) {
3562 if (flags & CF_FORCECHAR) {
3563 AddCodeSegLine (CS, "cmp #$%02X", (unsigned char)val);
3564 if (flags & CF_UNSIGNED) {
3565 AddCodeSegLine (CS, "jsr boolult");
3567 AddCodeSegLine (CS, "jsr boollt");
3574 if ((flags & CF_UNSIGNED) == 0 && val == 0) {
3575 /* If we have a signed compare against zero, we only need to
3576 * test the high byte.
3578 AddCodeSegLine (CS, "txa");
3579 AddCodeSegLine (CS, "jsr boollt");
3582 /* Direct code only for unsigned data types */
3583 if (flags & CF_UNSIGNED) {
3584 AddCodeSegLine (CS, "cpx #$%02X", (unsigned char)(val >> 8));
3585 AddCodeSegLine (CS, "bne *+4");
3586 AddCodeSegLine (CS, "cmp #$%02X", (unsigned char)val);
3587 AddCodeSegLine (CS, "jsr boolult");
3599 /* If we go here, we didn't emit code. Push the lhs on stack and fall
3600 * into the normal, non-optimized stuff.
3602 g_push (flags & ~CF_CONST, 0);
3606 /* Use long way over the stack */
3607 oper (flags, val, ops);
3612 void g_le (unsigned flags, unsigned long val)
3613 /* Test for less than or equal to */
3615 static char* ops [12] = {
3616 "tosle00", "toslea0", "tosleax",
3617 "tosule00", "tosulea0", "tosuleax",
3623 /* If the right hand side is const, the lhs is not on stack but still
3624 * in the primary register.
3626 if (flags & CF_CONST) {
3628 /* Look at the type */
3629 switch (flags & CF_TYPE) {
3632 if (flags & CF_FORCECHAR) {
3633 AddCodeSegLine (CS, "cmp #$%02X", (unsigned char)val);
3634 if (flags & CF_UNSIGNED) {
3635 AddCodeSegLine (CS, "jsr boolule");
3637 AddCodeSegLine (CS, "jsr boolle");
3644 if (flags & CF_UNSIGNED) {
3645 AddCodeSegLine (CS, "cpx #$%02X", (unsigned char)(val >> 8));
3646 AddCodeSegLine (CS, "bne *+4");
3647 AddCodeSegLine (CS, "cmp #$%02X", (unsigned char)val);
3648 AddCodeSegLine (CS, "jsr boolule");
3660 /* If we go here, we didn't emit code. Push the lhs on stack and fall
3661 * into the normal, non-optimized stuff.
3663 g_push (flags & ~CF_CONST, 0);
3667 /* Use long way over the stack */
3668 oper (flags, val, ops);
3673 void g_gt (unsigned flags, unsigned long val)
3674 /* Test for greater than */
3676 static char* ops [12] = {
3677 "tosgt00", "tosgta0", "tosgtax",
3678 "tosugt00", "tosugta0", "tosugtax",
3684 /* If the right hand side is const, the lhs is not on stack but still
3685 * in the primary register.
3687 if (flags & CF_CONST) {
3689 /* Look at the type */
3690 switch (flags & CF_TYPE) {
3693 if (flags & CF_FORCECHAR) {
3694 AddCodeSegLine (CS, "cmp #$%02X", (unsigned char)val);
3695 if (flags & CF_UNSIGNED) {
3696 /* If we have a compare > 0, we will replace it by
3697 * != 0 here, since both are identical but the latter
3698 * is easier to optimize.
3701 AddCodeSegLine (CS, "jsr boolugt");
3703 AddCodeSegLine (CS, "jsr boolne");
3706 AddCodeSegLine (CS, "jsr boolgt");
3713 if (flags & CF_UNSIGNED) {
3714 /* If we have a compare > 0, we will replace it by
3715 * != 0 here, since both are identical but the latter
3716 * is easier to optimize.
3718 if ((val & 0xFFFF) == 0) {
3719 AddCodeSegLine (CS, "stx tmp1");
3720 AddCodeSegLine (CS, "ora tmp1");
3721 AddCodeSegLine (CS, "jsr boolne");
3723 AddCodeSegLine (CS, "cpx #$%02X", (unsigned char)(val >> 8));
3724 AddCodeSegLine (CS, "bne *+4");
3725 AddCodeSegLine (CS, "cmp #$%02X", (unsigned char)val);
3726 AddCodeSegLine (CS, "jsr boolugt");
3739 /* If we go here, we didn't emit code. Push the lhs on stack and fall
3740 * into the normal, non-optimized stuff.
3742 g_push (flags & ~CF_CONST, 0);
3746 /* Use long way over the stack */
3747 oper (flags, val, ops);
3752 void g_ge (unsigned flags, unsigned long val)
3753 /* Test for greater than or equal to */
3755 static char* ops [12] = {
3756 "tosge00", "tosgea0", "tosgeax",
3757 "tosuge00", "tosugea0", "tosugeax",
3763 /* If the right hand side is const, the lhs is not on stack but still
3764 * in the primary register.
3766 if (flags & CF_CONST) {
3768 /* Give a warning in some special cases */
3769 if ((flags & CF_UNSIGNED) && val == 0) {
3770 Warning ("Condition is always true");
3773 /* Look at the type */
3774 switch (flags & CF_TYPE) {
3777 if (flags & CF_FORCECHAR) {
3778 AddCodeSegLine (CS, "cmp #$%02X", (unsigned char)val);
3779 if (flags & CF_UNSIGNED) {
3780 AddCodeSegLine (CS, "jsr booluge");
3782 AddCodeSegLine (CS, "jsr boolge");
3789 if (flags & CF_UNSIGNED) {
3790 AddCodeSegLine (CS, "cpx #$%02X", (unsigned char)(val >> 8));
3791 AddCodeSegLine (CS, "bne *+4");
3792 AddCodeSegLine (CS, "cmp #$%02X", (unsigned char)val);
3793 AddCodeSegLine (CS, "jsr booluge");
3805 /* If we go here, we didn't emit code. Push the lhs on stack and fall
3806 * into the normal, non-optimized stuff.
3808 g_push (flags & ~CF_CONST, 0);
3812 /* Use long way over the stack */
3813 oper (flags, val, ops);
3818 /*****************************************************************************/
3819 /* Allocating static storage */
3820 /*****************************************************************************/
3824 void g_res (unsigned n)
3825 /* Reserve static storage, n bytes */
3827 AddDataSegLine (DS, "\t.res\t%u,$00", n);
3832 void g_defdata (unsigned flags, unsigned long val, unsigned offs)
3833 /* Define data with the size given in flags */
3835 if (flags & CF_CONST) {
3837 /* Numeric constant */
3838 switch (flags & CF_TYPE) {
3841 AddDataSegLine (DS, "\t.byte\t$%02lX", val & 0xFF);
3845 AddDataSegLine (DS, "\t.word\t$%04lX", val & 0xFFFF);
3849 AddDataSegLine (DS, "\t.dword\t$%08lX", val & 0xFFFFFFFF);
3860 /* Create the correct label name */
3861 const char* Label = GetLabelName (flags, val, offs);
3863 /* Labels are always 16 bit */
3864 AddDataSegLine (DS, "\t.word\t%s", Label);
3871 void g_defbytes (const void* Bytes, unsigned Count)
3872 /* Output a row of bytes as a constant */
3878 /* Cast the buffer pointer */
3879 const unsigned char* Data = (const unsigned char*) Bytes;
3881 /* Output the stuff */
3884 /* How many go into this line? */
3885 if ((Chunk = Count) > 16) {
3890 /* Output one line */
3891 strcpy (Buf, "\t.byte\t");
3894 B += sprintf (B, "$%02X", *Data++);
3900 /* Output the line */
3901 AddDataSegLine (DS, Buf);
3907 void g_zerobytes (unsigned n)
3908 /* Output n bytes of data initialized with zero */
3910 AddDataSegLine (DS, "\t.res\t%u,$00", n);
3915 /*****************************************************************************/
3916 /* User supplied assembler code */
3917 /*****************************************************************************/
3921 void g_asmcode (const char* Line, int Len)
3922 /* Output one line of assembler code. If Len is greater than zero, it is used
3923 * as the maximum number of characters to use from Line.
3927 AddCodeSegLine (CS, "%.*s", Len, Line);
3929 AddCodeSegLine (CS, "%s", Line);
3935 /*****************************************************************************/
3936 /* Inlined known functions */
3937 /*****************************************************************************/
3941 void g_strlen (unsigned flags, unsigned long val, unsigned offs)
3942 /* Inline the strlen() function */
3944 /* We need a label in both cases */
3945 unsigned label = GetLocalLabel ();
3947 /* Two different encodings */
3948 if (flags & CF_CONST) {
3950 /* The address of the string is constant. Create the correct label name */
3951 char* lbuf = GetLabelName (flags, val, offs);
3953 /* Generate the strlen code */
3954 AddCodeSegLine (CS, "ldy #$FF");
3955 g_defcodelabel (label);
3956 AddCodeSegLine (CS, "iny");
3957 AddCodeSegLine (CS, "lda %s,y", lbuf);
3958 AddCodeSegLine (CS, "bne %s", LocalLabelName (label));
3959 AddCodeSegLine (CS, "tax");
3960 AddCodeSegLine (CS, "tya");
3964 /* Address not constant but in primary */
3965 if (CodeSizeFactor < 400) {
3966 /* This is too much code, so call strlen instead of inlining */
3967 AddCodeSegLine (CS, "jsr _strlen");
3969 /* Inline the function */
3970 AddCodeSegLine (CS, "sta ptr1");
3971 AddCodeSegLine (CS, "stx ptr1+1");
3972 AddCodeSegLine (CS, "ldy #$FF");
3973 g_defcodelabel (label);
3974 AddCodeSegLine (CS, "iny");
3975 AddCodeSegLine (CS, "lda (ptr1),y");
3976 AddCodeSegLine (CS, "bne %s", LocalLabelName (label));
3977 AddCodeSegLine (CS, "tax");
3978 AddCodeSegLine (CS, "tya");