1 /*****************************************************************************/
5 /* 6502 code generator */
9 /* (C) 1998-2001 Ullrich von Bassewitz */
11 /* D-70597 Stuttgart */
12 /* EMail: uz@musoftware.de */
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, "L%04X+%u", (unsigned)(label & 0xFFFF), 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 (""));
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_usecode (void)
202 /* Switch to the code segment */
209 void g_userodata (void)
210 /* Switch to the read only data segment */
217 void g_usedata (void)
218 /* Switch to the data segment */
226 /* Switch to the bss segment */
233 static void SegName (segment_t Seg, const char* Name)
234 /* Set the name of a segment */
236 /* Free the old name and set a new one */
237 NewSegName (Seg, Name);
239 /* If the new segment is the current segment, emit a segment directive
243 CurSeg = SEG_INV; /* Invalidate */
250 void g_codename (const char* Name)
251 /* Set the name of the CODE segment */
253 SegName (SEG_CODE, Name);
258 void g_rodataname (const char* Name)
259 /* Set the name of the RODATA segment */
261 SegName (SEG_RODATA, Name);
266 void g_dataname (const char* Name)
267 /* Set the name of the DATA segment */
269 SegName (SEG_DATA, Name);
274 void g_bssname (const char* Name)
275 /* Set the name of the BSS segment */
277 SegName (SEG_BSS, Name);
282 /*****************************************************************************/
284 /*****************************************************************************/
288 unsigned sizeofarg (unsigned flags)
289 /* Return the size of a function argument type that is encoded in flags */
291 switch (flags & CF_TYPE) {
294 return (flags & CF_FORCECHAR)? 1 : 2;
311 int pop (unsigned flags)
312 /* Pop an argument of the given size */
314 return oursp += sizeofarg (flags);
319 int push (unsigned flags)
320 /* Push an argument of the given size */
322 return oursp -= sizeofarg (flags);
327 static unsigned MakeByteOffs (unsigned Flags, unsigned Offs)
328 /* The value in Offs is an offset to an address in a/x. Make sure, an object
329 * of the type given in Flags can be loaded or stored into this address by
330 * adding part of the offset to the address in ax, so that the remaining
331 * offset fits into an index register. Return the remaining offset.
334 /* If the offset is too large for a byte register, add the high byte
335 * of the offset to the primary. Beware: We need a special correction
336 * if the offset in the low byte will overflow in the operation.
338 unsigned O = Offs & ~0xFFU;
339 if ((Offs & 0xFF) > 256 - sizeofarg (Flags)) {
340 /* We need to add the low byte also */
344 /* Do the correction if we need one */
346 g_inc (CF_INT | CF_CONST, O);
350 /* Return the new offset */
356 /*****************************************************************************/
357 /* Functions handling local labels */
358 /*****************************************************************************/
362 void g_defloclabel (unsigned label)
363 /* Define a local label */
365 if (CurSeg == SEG_CODE) {
366 AddLocCodeLabel (CS, LocalLabelName (label));
368 AddDataSegLine (DS, "%s:", LocalLabelName (label));
374 /*****************************************************************************/
375 /* Functions handling global labels */
376 /*****************************************************************************/
380 void g_defgloblabel (const char* Name)
381 /* Define a global label with the given name */
383 if (CurSeg == SEG_CODE) {
386 xsprintf (Buf, sizeof (Buf), "_%s", Name);
387 AddExtCodeLabel (CS, Buf);
389 AddDataSegLine (DS, "_%s:", Name);
395 void g_defexport (const char* Name, int ZP)
396 /* Export the given label */
399 AddDataSegLine (DS, "\t.exportzp\t_%s", Name);
401 AddDataSegLine (DS, "\t.export\t\t_%s", Name);
407 void g_defimport (const char* Name, int ZP)
408 /* Import the given label */
411 AddDataSegLine (DS, "\t.importzp\t_%s", Name);
413 AddDataSegLine (DS, "\t.import\t\t_%s", Name);
419 /*****************************************************************************/
420 /* Load functions for various registers */
421 /*****************************************************************************/
425 static void ldaconst (unsigned val)
426 /* Load a with a constant */
428 AddCodeSegLine (CS, "lda #$%02X", val & 0xFF);
433 static void ldxconst (unsigned val)
434 /* Load x with a constant */
436 AddCodeSegLine (CS, "ldx #$%02X", val & 0xFF);
441 static void ldyconst (unsigned val)
442 /* Load y with a constant */
444 AddCodeSegLine (CS, "ldy #$%02X", val & 0xFF);
449 /*****************************************************************************/
450 /* Function entry and exit */
451 /*****************************************************************************/
455 /* Remember the argument size of a function. The variable is set by g_enter
456 * and used by g_leave. If the functions gets its argument size by the caller
457 * (variable param list or function without prototype), g_enter will set the
463 void g_enter (unsigned flags, unsigned argsize)
464 /* Function prologue */
466 if ((flags & CF_FIXARGC) != 0) {
467 /* Just remember the argument size for the leave */
471 AddCodeSegLine (CS, "jsr enter");
477 void g_leave (int flags, int val)
478 /* Function epilogue */
483 /* CF_REG is set if we're returning a value from the function */
484 if ((flags & CF_REG) == 0) {
489 /* How many bytes of locals do we have to drop? */
492 /* If we didn't have a variable argument list, don't call leave */
495 /* Load a function return code if needed */
496 if ((flags & CF_CONST) != 0) {
497 g_getimmed (flags, val, 0);
500 /* Drop stackframe or leave with rts */
503 AddCodeHint ("y:-"); /* Y register no longer used */
504 AddCodeSegLine (CS, "rts");
506 AddCodeHint ("y:-"); /* Y register no longer used */
507 AddCodeSegLine (CS, "jmp incsp%d", k);
511 AddCodeSegLine (CS, "jmp addysp");
516 strcpy (buf, "\tjmp\tleave");
518 /* We've a stack frame to drop */
522 /* Y register no longer used */
525 if (flags & CF_CONST) {
526 if ((flags & CF_TYPE) != CF_LONG) {
527 /* Constant int sized value given for return code */
529 /* Special case: return 0 */
531 } else if (((val >> 8) & 0xFF) == 0) {
532 /* Special case: constant with high byte zero */
533 ldaconst (val); /* Load low byte */
536 /* Others: arbitrary constant value */
537 g_getimmed (flags, val, 0); /* Load value */
540 /* Constant long value: No shortcut possible */
541 g_getimmed (flags, val, 0);
545 /* Output the jump */
546 AddCodeSegLine (CS, buf);
552 /*****************************************************************************/
553 /* Register variables */
554 /*****************************************************************************/
558 void g_save_regvars (int RegOffs, unsigned Bytes)
559 /* Save register variables */
561 /* Don't loop for up to two bytes */
564 AddCodeSegLine (CS, "lda regbank%+d", RegOffs);
565 AddCodeSegLine (CS, "jsr pusha");
567 } else if (Bytes == 2) {
569 AddCodeSegLine (CS, "lda regbank%+d", RegOffs);
570 AddCodeSegLine (CS, "ldx regbank%+d", RegOffs+1);
571 AddCodeSegLine (CS, "jsr pushax");
575 /* More than two bytes - loop */
576 unsigned Label = GetLocalLabel ();
578 ldyconst (Bytes - 1);
580 g_defloclabel (Label);
581 AddCodeSegLine (CS, "lda regbank%+d,x", RegOffs-1);
582 AddCodeSegLine (CS, "sta (sp),y");
583 AddCodeSegLine (CS, "dey");
584 AddCodeSegLine (CS, "dex");
585 AddCodeSegLine (CS, "bne L%04X", Label);
589 /* We pushed stuff, correct the stack pointer */
595 void g_restore_regvars (int StackOffs, int RegOffs, unsigned Bytes)
596 /* Restore register variables */
598 /* Calculate the actual stack offset and check it */
600 CheckLocalOffs (StackOffs);
602 /* Don't loop for up to two bytes */
605 ldyconst (StackOffs);
606 AddCodeSegLine (CS, "lda (sp),y");
607 AddCodeSegLine (CS, "sta regbank%+d", RegOffs);
609 } else if (Bytes == 2) {
611 ldyconst (StackOffs);
612 AddCodeSegLine (CS, "lda (sp),y");
613 AddCodeSegLine (CS, "sta regbank%+d", RegOffs);
614 AddCodeSegLine (CS, "iny");
615 AddCodeSegLine (CS, "lda (sp),y");
616 AddCodeSegLine (CS, "sta regbank%+d", RegOffs+1);
620 /* More than two bytes - loop */
621 unsigned Label = GetLocalLabel ();
622 ldyconst (StackOffs+Bytes-1);
624 g_defloclabel (Label);
625 AddCodeSegLine (CS, "lda (sp),y");
626 AddCodeSegLine (CS, "sta regbank%+d,x", RegOffs-1);
627 AddCodeSegLine (CS, "dey");
628 AddCodeSegLine (CS, "dex");
629 AddCodeSegLine (CS, "bne L%04X", Label);
636 /*****************************************************************************/
637 /* Fetching memory cells */
638 /*****************************************************************************/
642 void g_getimmed (unsigned flags, unsigned long val, unsigned offs)
643 /* Load a constant into the primary register */
645 if ((flags & CF_CONST) != 0) {
647 /* Numeric constant */
648 switch (flags & CF_TYPE) {
651 if ((flags & CF_FORCECHAR) != 0) {
657 ldxconst ((val >> 8) & 0xFF);
658 ldaconst (val & 0xFF);
663 AddCodeSegLine (CS, "ldx #$00");
664 AddCodeSegLine (CS, "stx sreg+1");
665 AddCodeSegLine (CS, "stx sreg");
666 AddCodeSegLine (CS, "lda #$%02X", (unsigned char) val);
667 } else if ((val & 0xFFFF00FF) == 0) {
668 AddCodeSegLine (CS, "lda #$00");
669 AddCodeSegLine (CS, "sta sreg+1");
670 AddCodeSegLine (CS, "sta sreg");
671 AddCodeSegLine (CS, "ldx #$%02X", (unsigned char) (val >> 8));
672 } else if ((val & 0xFFFF0000) == 0 && CodeSizeFactor > 140) {
673 AddCodeSegLine (CS, "lda #$00");
674 AddCodeSegLine (CS, "sta sreg+1");
675 AddCodeSegLine (CS, "sta sreg");
676 AddCodeSegLine (CS, "lda #$%02X", (unsigned char) val);
677 AddCodeSegLine (CS, "ldx #$%02X", (unsigned char) (val >> 8));
678 } else if ((val & 0xFFFFFF00) == 0xFFFFFF00) {
679 AddCodeSegLine (CS, "ldx #$FF");
680 AddCodeSegLine (CS, "stx sreg+1");
681 AddCodeSegLine (CS, "stx sreg");
682 if ((val & 0xFF) == 0xFF) {
683 AddCodeSegLine (CS, "txa");
685 AddCodeSegLine (CS, "lda #$%02X", (unsigned char) val);
687 } else if ((val & 0xFFFF00FF) == 0xFFFF00FF) {
688 AddCodeSegLine (CS, "lda #$FF");
689 AddCodeSegLine (CS, "sta sreg+1");
690 AddCodeSegLine (CS, "sta sreg");
691 AddCodeSegLine (CS, "ldx #$%02X", (unsigned char) (val >> 8));
693 /* Call a subroutine that will load following value */
694 AddCodeSegLine (CS, "jsr ldeax");
695 AddCodeSegLine (CS, ".dword $%08lX", val & 0xFFFFFFFF);
707 /* Some sort of label */
708 const char* Label = GetLabelName (flags, val, offs);
710 /* Load the address into the primary */
711 AddCodeSegLine (CS, "lda #<(%s)", Label);
712 AddCodeSegLine (CS, "ldx #>(%s)", Label);
719 void g_getstatic (unsigned flags, unsigned long label, unsigned offs)
720 /* Fetch an static memory cell into the primary register */
722 /* Create the correct label name */
723 char* lbuf = GetLabelName (flags, label, offs);
725 /* Check the size and generate the correct load operation */
726 switch (flags & CF_TYPE) {
729 if ((flags & CF_FORCECHAR) || (flags & CF_TEST)) {
730 AddCodeSegLine (CS, "lda %s", lbuf); /* load A from the label */
733 AddCodeSegLine (CS, "lda %s", lbuf); /* load A from the label */
734 if (!(flags & CF_UNSIGNED)) {
735 /* Must sign extend */
736 AddCodeSegLine (CS, "bpl *+3");
737 AddCodeSegLine (CS, "dex");
738 AddCodeHint ("x:!"); /* X is invalid now */
744 AddCodeSegLine (CS, "lda %s", lbuf);
745 if (flags & CF_TEST) {
746 AddCodeSegLine (CS, "ora %s+1", lbuf);
748 AddCodeSegLine (CS, "ldx %s+1", lbuf);
753 if (flags & CF_TEST) {
754 AddCodeSegLine (CS, "lda %s+3", lbuf);
755 AddCodeSegLine (CS, "ora %s+2", lbuf);
756 AddCodeSegLine (CS, "ora %s+1", lbuf);
757 AddCodeSegLine (CS, "ora %s+0", lbuf);
759 AddCodeSegLine (CS, "lda %s+3", lbuf);
760 AddCodeSegLine (CS, "sta sreg+1");
761 AddCodeSegLine (CS, "lda %s+2", lbuf);
762 AddCodeSegLine (CS, "sta sreg");
763 AddCodeSegLine (CS, "ldx %s+1", lbuf);
764 AddCodeSegLine (CS, "lda %s", lbuf);
776 void g_getlocal (unsigned flags, int offs)
777 /* Fetch specified local object (local var). */
780 CheckLocalOffs (offs);
781 switch (flags & CF_TYPE) {
784 if ((flags & CF_FORCECHAR) || (flags & CF_TEST)) {
785 if (CPU == CPU_65C02 && offs == 0) {
786 AddCodeSegLine (CS, "lda (sp)");
789 AddCodeSegLine (CS, "lda (sp),y");
793 AddCodeSegLine (CS, "ldx #$00");
794 AddCodeSegLine (CS, "lda (sp,x)");
797 AddCodeSegLine (CS, "ldx #$00");
798 AddCodeSegLine (CS, "lda (sp),y");
800 if ((flags & CF_UNSIGNED) == 0) {
801 AddCodeSegLine (CS, "bpl *+3");
802 AddCodeSegLine (CS, "dex");
803 AddCodeHint ("x:!"); /* X is invalid now */
809 CheckLocalOffs (offs + 1);
810 if (flags & CF_TEST) {
812 AddCodeSegLine (CS, "lda (sp),y");
813 AddCodeSegLine (CS, "dey");
814 AddCodeSegLine (CS, "ora (sp),y");
816 if (CodeSizeFactor > 180) {
818 AddCodeSegLine (CS, "lda (sp),y");
819 AddCodeSegLine (CS, "tax");
820 AddCodeSegLine (CS, "dey");
821 AddCodeSegLine (CS, "lda (sp),y");
825 AddCodeSegLine (CS, "jsr ldaxysp");
827 AddCodeSegLine (CS, "jsr ldax0sp");
836 AddCodeSegLine (CS, "jsr ldeaxysp");
838 AddCodeSegLine (CS, "jsr ldeax0sp");
849 void g_getind (unsigned flags, unsigned offs)
850 /* Fetch the specified object type indirect through the primary register
851 * into the primary register
854 /* If the offset is greater than 255, add the part that is > 255 to
855 * the primary. This way we get an easy addition and use the low byte
858 offs = MakeByteOffs (flags, offs);
860 /* Handle the indirect fetch */
861 switch (flags & CF_TYPE) {
864 /* Character sized */
867 if (flags & CF_UNSIGNED) {
868 AddCodeSegLine (CS, "jsr ldauidx");
870 AddCodeSegLine (CS, "jsr ldaidx");
873 if (flags & CF_UNSIGNED) {
874 if (CodeSizeFactor > 250) {
875 AddCodeSegLine (CS, "sta ptr1");
876 AddCodeSegLine (CS, "stx ptr1+1");
877 AddCodeSegLine (CS, "ldx #$00");
878 AddCodeSegLine (CS, "lda (ptr1,x)");
880 AddCodeSegLine (CS, "jsr ldaui");
883 AddCodeSegLine (CS, "jsr ldai");
889 if (flags & CF_TEST) {
891 AddCodeSegLine (CS, "sta ptr1");
892 AddCodeSegLine (CS, "stx ptr1+1");
893 AddCodeSegLine (CS, "lda (ptr1),y");
894 AddCodeSegLine (CS, "iny");
895 AddCodeSegLine (CS, "ora (ptr1),y");
898 AddCodeSegLine (CS, "jsr ldaxi");
901 AddCodeSegLine (CS, "jsr ldaxidx");
908 AddCodeSegLine (CS, "jsr ldeaxi");
911 AddCodeSegLine (CS, "jsr ldeaxidx");
913 if (flags & CF_TEST) {
914 AddCodeSegLine (CS, "jsr tsteax");
926 void g_leasp (int offs)
927 /* Fetch the address of the specified symbol into the primary register */
929 /* Calculate the offset relative to sp */
932 /* For value 0 we do direct code */
934 AddCodeSegLine (CS, "lda sp");
935 AddCodeSegLine (CS, "ldx sp+1");
937 if (CodeSizeFactor < 300) {
938 ldaconst (offs); /* Load A with offset value */
939 AddCodeSegLine (CS, "jsr leaasp"); /* Load effective address */
941 if (CPU == CPU_65C02 && offs == 1) {
942 AddCodeSegLine (CS, "lda sp");
943 AddCodeSegLine (CS, "ldx sp+1");
944 AddCodeSegLine (CS, "ina");
945 AddCodeSegLine (CS, "bne *+3");
946 AddCodeSegLine (CS, "inx");
947 AddCodeHint ("x:!"); /* Invalidate X */
950 AddCodeSegLine (CS, "clc");
951 AddCodeSegLine (CS, "ldx sp+1");
952 AddCodeSegLine (CS, "adc sp");
953 AddCodeSegLine (CS, "bcc *+3");
954 AddCodeSegLine (CS, "inx");
955 AddCodeHint ("x:!"); /* Invalidate X */
963 void g_leavariadic (int Offs)
964 /* Fetch the address of a parameter in a variadic function into the primary
968 unsigned ArgSizeOffs;
970 /* Calculate the offset relative to sp */
973 /* Get the offset of the parameter which is stored at sp+0 on function
974 * entry and check if this offset is reachable with a byte offset.
977 ArgSizeOffs = -oursp;
978 CheckLocalOffs (ArgSizeOffs);
980 /* Get the size of all parameters. */
981 if (ArgSizeOffs == 0 && CPU == CPU_65C02) {
982 AddCodeSegLine (CS, "lda (sp)");
984 ldyconst (ArgSizeOffs);
985 AddCodeSegLine (CS, "lda (sp),y");
988 /* Add the value of the stackpointer */
989 if (CodeSizeFactor > 250) {
990 AddCodeSegLine (CS, "ldx sp+1");
991 AddCodeSegLine (CS, "clc");
992 AddCodeSegLine (CS, "adc sp");
993 AddCodeSegLine (CS, "bcc *+3");
994 AddCodeSegLine (CS, "inx");
995 AddCodeHint ("x:!"); /* Invalidate X */
997 AddCodeSegLine (CS, "jsr leaasp");
1000 /* Add the offset to the primary */
1002 g_inc (CF_INT | CF_CONST, Offs);
1003 } else if (Offs < 0) {
1004 g_dec (CF_INT | CF_CONST, -Offs);
1010 /*****************************************************************************/
1011 /* Store into memory */
1012 /*****************************************************************************/
1016 void g_putstatic (unsigned flags, unsigned long label, unsigned offs)
1017 /* Store the primary register into the specified static memory cell */
1019 /* Create the correct label name */
1020 char* lbuf = GetLabelName (flags, label, offs);
1022 /* Check the size and generate the correct store operation */
1023 switch (flags & CF_TYPE) {
1026 AddCodeSegLine (CS, "sta %s", lbuf);
1030 AddCodeSegLine (CS, "sta %s", lbuf);
1031 AddCodeSegLine (CS, "stx %s+1", lbuf);
1035 AddCodeSegLine (CS, "sta %s", lbuf);
1036 AddCodeSegLine (CS, "stx %s+1", lbuf);
1037 AddCodeSegLine (CS, "ldy sreg");
1038 AddCodeSegLine (CS, "sty %s+2", lbuf);
1039 AddCodeSegLine (CS, "ldy sreg+1");
1040 AddCodeSegLine (CS, "sty %s+3", lbuf);
1051 void g_putlocal (unsigned Flags, int Offs, long Val)
1052 /* Put data into local object. */
1055 CheckLocalOffs (Offs);
1056 switch (Flags & CF_TYPE) {
1059 if (Flags & CF_CONST) {
1060 AddCodeSegLine (CS, "lda #$%02X", (unsigned char) Val);
1062 if (CPU == CPU_65C02 && Offs == 0) {
1063 AddCodeSegLine (CS, "sta (sp)");
1066 AddCodeSegLine (CS, "sta (sp),y");
1071 if (Flags & CF_CONST) {
1073 AddCodeSegLine (CS, "lda #$%02X", (unsigned char) (Val >> 8));
1074 AddCodeSegLine (CS, "sta (sp),y");
1075 if ((Flags & CF_NOKEEP) == 0) {
1076 /* Place high byte into X */
1077 AddCodeSegLine (CS, "tax");
1079 if (CPU == CPU_65C02 && Offs == 0) {
1080 AddCodeSegLine (CS, "lda #$%02X", (unsigned char) Val);
1081 AddCodeSegLine (CS, "sta (sp)");
1083 if ((Val & 0xFF) == Offs+1) {
1084 /* The value we need is already in Y */
1085 AddCodeSegLine (CS, "tya");
1086 AddCodeSegLine (CS, "dey");
1088 AddCodeSegLine (CS, "dey");
1089 AddCodeSegLine (CS, "lda #$%02X", (unsigned char) Val);
1091 AddCodeSegLine (CS, "sta (sp),y");
1094 if ((Flags & CF_NOKEEP) == 0 || CodeSizeFactor < 160) {
1097 AddCodeSegLine (CS, "jsr staxysp");
1099 AddCodeSegLine (CS, "jsr stax0sp");
1102 if (CPU == CPU_65C02 && Offs == 0) {
1103 AddCodeSegLine (CS, "sta (sp)");
1105 AddCodeSegLine (CS, "txa");
1106 AddCodeSegLine (CS, "sta (sp),y");
1109 AddCodeSegLine (CS, "sta (sp),y");
1110 AddCodeSegLine (CS, "iny");
1111 AddCodeSegLine (CS, "txa");
1112 AddCodeSegLine (CS, "sta (sp),y");
1119 if (Flags & CF_CONST) {
1120 g_getimmed (Flags, Val, 0);
1124 AddCodeSegLine (CS, "jsr steaxysp");
1126 AddCodeSegLine (CS, "jsr steax0sp");
1138 void g_putind (unsigned Flags, unsigned Offs)
1139 /* Store the specified object type in the primary register at the address
1140 * on the top of the stack
1143 /* We can handle offsets below $100 directly, larger offsets must be added
1144 * to the address. Since a/x is in use, best code is achieved by adding
1145 * just the high byte. Be sure to check if the low byte will overflow while
1148 if ((Offs & 0xFF) > 256 - sizeofarg (Flags | CF_FORCECHAR)) {
1150 /* Overflow - we need to add the low byte also */
1151 AddCodeSegLine (CS, "ldy #$00");
1152 AddCodeSegLine (CS, "clc");
1153 AddCodeSegLine (CS, "pha");
1154 AddCodeSegLine (CS, "lda #$%02X", Offs & 0xFF);
1155 AddCodeSegLine (CS, "adc (sp),y");
1156 AddCodeSegLine (CS, "sta (sp),y");
1157 AddCodeSegLine (CS, "iny");
1158 AddCodeSegLine (CS, "lda #$%02X", (Offs >> 8) & 0xFF);
1159 AddCodeSegLine (CS, "adc (sp),y");
1160 AddCodeSegLine (CS, "sta (sp),y");
1161 AddCodeSegLine (CS, "pla");
1163 /* Complete address is on stack, new offset is zero */
1166 } else if ((Offs & 0xFF00) != 0) {
1168 /* We can just add the high byte */
1169 AddCodeSegLine (CS, "ldy #$01");
1170 AddCodeSegLine (CS, "clc");
1171 AddCodeSegLine (CS, "pha");
1172 AddCodeSegLine (CS, "lda #$%02X", (Offs >> 8) & 0xFF);
1173 AddCodeSegLine (CS, "adc (sp),y");
1174 AddCodeSegLine (CS, "sta (sp),y");
1175 AddCodeSegLine (CS, "pla");
1177 /* Offset is now just the low byte */
1181 /* Check the size and determine operation */
1182 switch (Flags & CF_TYPE) {
1187 AddCodeSegLine (CS, "jsr staspidx");
1189 AddCodeSegLine (CS, "jsr staspp");
1196 AddCodeSegLine (CS, "jsr staxspidx");
1198 AddCodeSegLine (CS, "jsr staxspp");
1205 AddCodeSegLine (CS, "jsr steaxspidx");
1207 AddCodeSegLine (CS, "jsr steaxspp");
1216 /* Pop the argument which is always a pointer */
1222 /*****************************************************************************/
1223 /* type conversion and similiar stuff */
1224 /*****************************************************************************/
1228 void g_toslong (unsigned flags)
1229 /* Make sure, the value on TOS is a long. Convert if necessary */
1231 switch (flags & CF_TYPE) {
1235 if (flags & CF_UNSIGNED) {
1236 AddCodeSegLine (CS, "jsr tosulong");
1238 AddCodeSegLine (CS, "jsr toslong");
1253 void g_tosint (unsigned flags)
1254 /* Make sure, the value on TOS is an int. Convert if necessary */
1256 switch (flags & CF_TYPE) {
1263 AddCodeSegLine (CS, "jsr tosint");
1274 void g_reglong (unsigned flags)
1275 /* Make sure, the value in the primary register a long. Convert if necessary */
1277 switch (flags & CF_TYPE) {
1281 if (flags & CF_UNSIGNED) {
1282 if (CodeSizeFactor >= 200) {
1284 AddCodeSegLine (CS, "sty sreg");
1285 AddCodeSegLine (CS, "sty sreg+1");
1287 AddCodeSegLine (CS, "jsr axulong");
1290 AddCodeSegLine (CS, "jsr axlong");
1304 unsigned g_typeadjust (unsigned lhs, unsigned rhs)
1305 /* Adjust the integer operands before doing a binary operation. lhs is a flags
1306 * value, that corresponds to the value on TOS, rhs corresponds to the value
1307 * in (e)ax. The return value is the the flags value for the resulting type.
1310 unsigned ltype, rtype;
1313 /* Get the type spec from the flags */
1314 ltype = lhs & CF_TYPE;
1315 rtype = rhs & CF_TYPE;
1317 /* Check if a conversion is needed */
1318 if (ltype == CF_LONG && rtype != CF_LONG && (rhs & CF_CONST) == 0) {
1319 /* We must promote the primary register to long */
1321 /* Get the new rhs type */
1322 rhs = (rhs & ~CF_TYPE) | CF_LONG;
1324 } else if (ltype != CF_LONG && (lhs & CF_CONST) == 0 && rtype == CF_LONG) {
1325 /* We must promote the lhs to long */
1331 /* Get the new rhs type */
1332 lhs = (lhs & ~CF_TYPE) | CF_LONG;
1336 /* Determine the result type for the operation:
1337 * - The result is const if both operands are const.
1338 * - The result is unsigned if one of the operands is unsigned.
1339 * - The result is long if one of the operands is long.
1340 * - Otherwise the result is int sized.
1342 result = (lhs & CF_CONST) & (rhs & CF_CONST);
1343 result |= (lhs & CF_UNSIGNED) | (rhs & CF_UNSIGNED);
1344 if (rtype == CF_LONG || ltype == CF_LONG) {
1354 unsigned g_typecast (unsigned lhs, unsigned rhs)
1355 /* Cast the value in the primary register to the operand size that is flagged
1356 * by the lhs value. Return the result value.
1359 unsigned ltype, rtype;
1361 /* Get the type spec from the flags */
1362 ltype = lhs & CF_TYPE;
1363 rtype = rhs & CF_TYPE;
1365 /* Check if a conversion is needed */
1366 if (ltype == CF_LONG && rtype != CF_LONG && (rhs & CF_CONST) == 0) {
1367 /* We must promote the primary register to long */
1371 /* Do not need any other action. If the left type is int, and the primary
1372 * register is long, it will be automagically truncated. If the right hand
1373 * side is const, it is not located in the primary register and handled by
1374 * the expression parser code.
1377 /* Result is const if the right hand side was const */
1378 lhs |= (rhs & CF_CONST);
1380 /* The resulting type is that of the left hand side (that's why you called
1388 void g_scale (unsigned flags, long val)
1389 /* Scale the value in the primary register by the given value. If val is positive,
1390 * scale up, is val is negative, scale down. This function is used to scale
1391 * the operands or results of pointer arithmetic by the size of the type, the
1392 * pointer points to.
1397 /* Value may not be zero */
1399 Internal ("Data type has no size");
1400 } else if (val > 0) {
1403 if ((p2 = powerof2 (val)) > 0 && p2 <= 3) {
1405 /* Factor is 2, 4 or 8, use special function */
1406 switch (flags & CF_TYPE) {
1409 if (flags & CF_FORCECHAR) {
1411 AddCodeSegLine (CS, "asl a");
1418 if (CodeSizeFactor >= (p2+1)*130U) {
1419 AddCodeSegLine (CS, "stx tmp1");
1421 AddCodeSegLine (CS, "asl a");
1422 AddCodeSegLine (CS, "rol tmp1");
1424 AddCodeSegLine (CS, "ldx tmp1");
1426 if (flags & CF_UNSIGNED) {
1427 AddCodeSegLine (CS, "jsr shlax%d", p2);
1429 AddCodeSegLine (CS, "jsr aslax%d", p2);
1435 if (flags & CF_UNSIGNED) {
1436 AddCodeSegLine (CS, "jsr shleax%d", p2);
1438 AddCodeSegLine (CS, "jsr asleax%d", p2);
1447 } else if (val != 1) {
1449 /* Use a multiplication instead */
1450 g_mul (flags | CF_CONST, val);
1458 if ((p2 = powerof2 (val)) > 0 && p2 <= 3) {
1460 /* Factor is 2, 4 or 8, use special function */
1461 switch (flags & CF_TYPE) {
1464 if (flags & CF_FORCECHAR) {
1465 if (flags & CF_UNSIGNED) {
1467 AddCodeSegLine (CS, "lsr a");
1470 } else if (p2 <= 2) {
1471 AddCodeSegLine (CS, "cmp #$80");
1472 AddCodeSegLine (CS, "ror a");
1479 if (flags & CF_UNSIGNED) {
1480 if (CodeSizeFactor >= (p2+1)*130U) {
1481 AddCodeSegLine (CS, "stx tmp1");
1483 AddCodeSegLine (CS, "lsr tmp1");
1484 AddCodeSegLine (CS, "ror a");
1486 AddCodeSegLine (CS, "ldx tmp1");
1488 AddCodeSegLine (CS, "jsr lsrax%d", p2);
1491 if (CodeSizeFactor >= (p2+1)*150U) {
1492 AddCodeSegLine (CS, "stx tmp1");
1494 AddCodeSegLine (CS, "cpx #$80");
1495 AddCodeSegLine (CS, "ror tmp1");
1496 AddCodeSegLine (CS, "ror a");
1498 AddCodeSegLine (CS, "ldx tmp1");
1500 AddCodeSegLine (CS, "jsr asrax%d", p2);
1506 if (flags & CF_UNSIGNED) {
1507 AddCodeSegLine (CS, "jsr lsreax%d", p2);
1509 AddCodeSegLine (CS, "jsr asreax%d", p2);
1518 } else if (val != 1) {
1520 /* Use a division instead */
1521 g_div (flags | CF_CONST, val);
1529 /*****************************************************************************/
1530 /* Adds and subs of variables fix a fixed address */
1531 /*****************************************************************************/
1535 void g_addlocal (unsigned flags, int offs)
1536 /* Add a local variable to ax */
1538 /* Correct the offset and check it */
1540 CheckLocalOffs (offs);
1542 switch (flags & CF_TYPE) {
1545 AddCodeSegLine (CS, "ldy #$%02X", offs & 0xFF);
1546 AddCodeSegLine (CS, "clc");
1547 AddCodeSegLine (CS, "adc (sp),y");
1548 AddCodeSegLine (CS, "bcc *+3");
1549 AddCodeSegLine (CS, "inx");
1550 AddCodeHint ("x:!");
1554 AddCodeSegLine (CS, "ldy #$%02X", offs & 0xFF);
1555 AddCodeSegLine (CS, "clc");
1556 AddCodeSegLine (CS, "adc (sp),y");
1557 AddCodeSegLine (CS, "pha");
1558 AddCodeSegLine (CS, "txa");
1559 AddCodeSegLine (CS, "iny");
1560 AddCodeSegLine (CS, "adc (sp),y");
1561 AddCodeSegLine (CS, "tax");
1562 AddCodeSegLine (CS, "pla");
1566 /* Do it the old way */
1568 g_getlocal (flags, offs);
1580 void g_addstatic (unsigned flags, unsigned long label, unsigned offs)
1581 /* Add a static variable to ax */
1583 /* Create the correct label name */
1584 char* lbuf = GetLabelName (flags, label, offs);
1586 switch (flags & CF_TYPE) {
1589 AddCodeSegLine (CS, "clc");
1590 AddCodeSegLine (CS, "adc %s", lbuf);
1591 AddCodeSegLine (CS, "bcc *+3");
1592 AddCodeSegLine (CS, "inx");
1593 AddCodeHint ("x:!");
1597 AddCodeSegLine (CS, "clc");
1598 AddCodeSegLine (CS, "adc %s", lbuf);
1599 AddCodeSegLine (CS, "tay");
1600 AddCodeSegLine (CS, "txa");
1601 AddCodeSegLine (CS, "adc %s+1", lbuf);
1602 AddCodeSegLine (CS, "tax");
1603 AddCodeSegLine (CS, "tya");
1607 /* Do it the old way */
1609 g_getstatic (flags, label, offs);
1621 /*****************************************************************************/
1622 /* Compares of ax with a variable with fixed address */
1623 /*****************************************************************************/
1627 void g_cmplocal (unsigned flags, int offs)
1628 /* Compare a local variable to ax */
1630 Internal ("g_cmplocal not implemented");
1635 void g_cmpstatic (unsigned flags, unsigned label, unsigned offs)
1636 /* Compare a static variable to ax */
1638 Internal ("g_cmpstatic not implemented");
1643 /*****************************************************************************/
1644 /* Special op= functions */
1645 /*****************************************************************************/
1649 void g_addeqstatic (unsigned flags, unsigned long label, unsigned offs,
1651 /* Emit += for a static variable */
1653 /* Create the correct label name */
1654 char* lbuf = GetLabelName (flags, label, offs);
1656 /* Check the size and determine operation */
1657 switch (flags & CF_TYPE) {
1660 if (flags & CF_FORCECHAR) {
1661 AddCodeSegLine (CS, "ldx #$00");
1662 if (flags & CF_CONST) {
1664 AddCodeSegLine (CS, "inc %s", lbuf);
1665 AddCodeSegLine (CS, "lda %s", lbuf);
1667 AddCodeSegLine (CS, "lda #$%02X", (int)(val & 0xFF));
1668 AddCodeSegLine (CS, "clc");
1669 AddCodeSegLine (CS, "adc %s", lbuf);
1670 AddCodeSegLine (CS, "sta %s", lbuf);
1673 AddCodeSegLine (CS, "clc");
1674 AddCodeSegLine (CS, "adc %s", lbuf);
1675 AddCodeSegLine (CS, "sta %s", lbuf);
1677 if ((flags & CF_UNSIGNED) == 0) {
1678 AddCodeSegLine (CS, "bpl *+3");
1679 AddCodeSegLine (CS, "dex");
1680 AddCodeHint ("x:!"); /* Invalidate X */
1687 if (flags & CF_CONST) {
1689 label = GetLocalLabel ();
1690 AddCodeSegLine (CS, "inc %s", lbuf);
1691 AddCodeSegLine (CS, "bne L%04X", (int)label);
1692 AddCodeSegLine (CS, "inc %s+1", lbuf);
1693 g_defloclabel (label);
1694 AddCodeSegLine (CS, "lda %s", lbuf); /* Hmmm... */
1695 AddCodeSegLine (CS, "ldx %s+1", lbuf);
1697 AddCodeSegLine (CS, "lda #$%02X", (int)(val & 0xFF));
1698 AddCodeSegLine (CS, "clc");
1699 AddCodeSegLine (CS, "adc %s", lbuf);
1700 AddCodeSegLine (CS, "sta %s", lbuf);
1702 label = GetLocalLabel ();
1703 AddCodeSegLine (CS, "bcc L%04X", (int)label);
1704 AddCodeSegLine (CS, "inc %s+1", lbuf);
1705 g_defloclabel (label);
1706 AddCodeSegLine (CS, "ldx %s+1", lbuf);
1708 AddCodeSegLine (CS, "lda #$%02X", (unsigned char)(val >> 8));
1709 AddCodeSegLine (CS, "adc %s+1", lbuf);
1710 AddCodeSegLine (CS, "sta %s+1", lbuf);
1711 AddCodeSegLine (CS, "tax");
1712 AddCodeSegLine (CS, "lda %s", lbuf);
1716 AddCodeSegLine (CS, "clc");
1717 AddCodeSegLine (CS, "adc %s", lbuf);
1718 AddCodeSegLine (CS, "sta %s", lbuf);
1719 AddCodeSegLine (CS, "txa");
1720 AddCodeSegLine (CS, "adc %s+1", lbuf);
1721 AddCodeSegLine (CS, "sta %s+1", lbuf);
1722 AddCodeSegLine (CS, "tax");
1723 AddCodeSegLine (CS, "lda %s", lbuf);
1728 if (flags & CF_CONST) {
1730 AddCodeSegLine (CS, "ldy #<(%s)", lbuf);
1731 AddCodeSegLine (CS, "sty ptr1");
1732 AddCodeSegLine (CS, "ldy #>(%s+1)", lbuf);
1734 AddCodeSegLine (CS, "jsr laddeq1");
1736 AddCodeSegLine (CS, "lda #$%02X", (int)(val & 0xFF));
1737 AddCodeSegLine (CS, "jsr laddeqa");
1740 g_getstatic (flags, label, offs);
1742 g_putstatic (flags, label, offs);
1745 AddCodeSegLine (CS, "ldy #<(%s)", lbuf);
1746 AddCodeSegLine (CS, "sty ptr1");
1747 AddCodeSegLine (CS, "ldy #>(%s+1)", lbuf);
1748 AddCodeSegLine (CS, "jsr laddeq");
1759 void g_addeqlocal (unsigned flags, int offs, unsigned long val)
1760 /* Emit += for a local variable */
1762 /* Calculate the true offset, check it, load it into Y */
1764 CheckLocalOffs (offs);
1766 /* Check the size and determine operation */
1767 switch (flags & CF_TYPE) {
1770 if (flags & CF_FORCECHAR) {
1772 AddCodeSegLine (CS, "ldx #$00");
1773 if (flags & CF_CONST) {
1774 AddCodeSegLine (CS, "clc");
1775 AddCodeSegLine (CS, "lda #$%02X", (int)(val & 0xFF));
1776 AddCodeSegLine (CS, "adc (sp,x)");
1777 AddCodeSegLine (CS, "sta (sp,x)");
1779 AddCodeSegLine (CS, "clc");
1780 AddCodeSegLine (CS, "adc (sp,x)");
1781 AddCodeSegLine (CS, "sta (sp,x)");
1785 AddCodeSegLine (CS, "ldx #$00");
1786 if (flags & CF_CONST) {
1787 AddCodeSegLine (CS, "clc");
1788 AddCodeSegLine (CS, "lda #$%02X", (int)(val & 0xFF));
1789 AddCodeSegLine (CS, "adc (sp),y");
1790 AddCodeSegLine (CS, "sta (sp),y");
1792 AddCodeSegLine (CS, "clc");
1793 AddCodeSegLine (CS, "adc (sp),y");
1794 AddCodeSegLine (CS, "sta (sp),y");
1797 if ((flags & CF_UNSIGNED) == 0) {
1798 AddCodeSegLine (CS, "bpl *+3");
1799 AddCodeSegLine (CS, "dex");
1800 AddCodeHint ("x:!"); /* Invalidate X */
1807 if (flags & CF_CONST) {
1808 g_getimmed (flags, val, 0);
1811 AddCodeSegLine (CS, "jsr addeq0sp");
1814 AddCodeSegLine (CS, "jsr addeqysp");
1819 if (flags & CF_CONST) {
1820 g_getimmed (flags, val, 0);
1823 AddCodeSegLine (CS, "jsr laddeq0sp");
1826 AddCodeSegLine (CS, "jsr laddeqysp");
1837 void g_addeqind (unsigned flags, unsigned offs, unsigned long val)
1838 /* Emit += for the location with address in ax */
1840 /* If the offset is too large for a byte register, add the high byte
1841 * of the offset to the primary. Beware: We need a special correction
1842 * if the offset in the low byte will overflow in the operation.
1844 offs = MakeByteOffs (flags, offs);
1846 /* Check the size and determine operation */
1847 switch (flags & CF_TYPE) {
1850 AddCodeSegLine (CS, "sta ptr1");
1851 AddCodeSegLine (CS, "stx ptr1+1");
1853 AddCodeSegLine (CS, "ldx #$00");
1854 AddCodeSegLine (CS, "lda #$%02X", (int)(val & 0xFF));
1855 AddCodeSegLine (CS, "clc");
1856 AddCodeSegLine (CS, "adc (ptr1,x)");
1857 AddCodeSegLine (CS, "sta (ptr1,x)");
1859 AddCodeSegLine (CS, "ldy #$%02X", offs);
1860 AddCodeSegLine (CS, "ldx #$00");
1861 AddCodeSegLine (CS, "lda #$%02X", (int)(val & 0xFF));
1862 AddCodeSegLine (CS, "clc");
1863 AddCodeSegLine (CS, "adc (ptr1),y");
1864 AddCodeSegLine (CS, "sta (ptr1),y");
1869 if (CodeSizeFactor >= 200) {
1870 /* Lots of code, use only if size is not important */
1871 AddCodeSegLine (CS, "sta ptr1");
1872 AddCodeSegLine (CS, "stx ptr1+1");
1873 AddCodeSegLine (CS, "ldy #$%02X", offs);
1874 AddCodeSegLine (CS, "lda #$%02X", (int)(val & 0xFF));
1875 AddCodeSegLine (CS, "clc");
1876 AddCodeSegLine (CS, "adc (ptr1),y");
1877 AddCodeSegLine (CS, "sta (ptr1),y");
1878 AddCodeSegLine (CS, "pha");
1879 AddCodeSegLine (CS, "iny");
1880 AddCodeSegLine (CS, "lda #$%02X", (unsigned char)(val >> 8));
1881 AddCodeSegLine (CS, "adc (ptr1),y");
1882 AddCodeSegLine (CS, "sta (ptr1),y");
1883 AddCodeSegLine (CS, "tax");
1884 AddCodeSegLine (CS, "pla");
1890 AddCodeSegLine (CS, "jsr pushax"); /* Push the address */
1891 push (flags); /* Correct the internal sp */
1892 g_getind (flags, offs); /* Fetch the value */
1893 g_inc (flags, val); /* Increment value in primary */
1894 g_putind (flags, offs); /* Store the value back */
1904 void g_subeqstatic (unsigned flags, unsigned long label, unsigned offs,
1906 /* Emit -= for a static variable */
1908 /* Create the correct label name */
1909 char* lbuf = GetLabelName (flags, label, offs);
1911 /* Check the size and determine operation */
1912 switch (flags & CF_TYPE) {
1915 if (flags & CF_FORCECHAR) {
1916 AddCodeSegLine (CS, "ldx #$00");
1917 if (flags & CF_CONST) {
1919 AddCodeSegLine (CS, "dec %s", lbuf);
1920 AddCodeSegLine (CS, "lda %s", lbuf);
1922 AddCodeSegLine (CS, "sec");
1923 AddCodeSegLine (CS, "lda %s", lbuf);
1924 AddCodeSegLine (CS, "sbc #$%02X", (int)(val & 0xFF));
1925 AddCodeSegLine (CS, "sta %s", lbuf);
1928 AddCodeSegLine (CS, "sec");
1929 AddCodeSegLine (CS, "sta tmp1");
1930 AddCodeSegLine (CS, "lda %s", lbuf);
1931 AddCodeSegLine (CS, "sbc tmp1");
1932 AddCodeSegLine (CS, "sta %s", lbuf);
1934 if ((flags & CF_UNSIGNED) == 0) {
1935 AddCodeSegLine (CS, "bpl *+3");
1936 AddCodeSegLine (CS, "dex");
1937 AddCodeHint ("x:!"); /* Invalidate X */
1944 AddCodeSegLine (CS, "sec");
1945 if (flags & CF_CONST) {
1946 AddCodeSegLine (CS, "lda %s", lbuf);
1947 AddCodeSegLine (CS, "sbc #$%02X", (unsigned char)val);
1948 AddCodeSegLine (CS, "sta %s", lbuf);
1950 label = GetLocalLabel ();
1951 AddCodeSegLine (CS, "bcs L%04X", (unsigned)label);
1952 AddCodeSegLine (CS, "dec %s+1", lbuf);
1953 g_defloclabel (label);
1954 AddCodeSegLine (CS, "ldx %s+1", lbuf);
1956 AddCodeSegLine (CS, "lda %s+1", lbuf);
1957 AddCodeSegLine (CS, "sbc #$%02X", (unsigned char)(val >> 8));
1958 AddCodeSegLine (CS, "sta %s+1", lbuf);
1959 AddCodeSegLine (CS, "tax");
1960 AddCodeSegLine (CS, "lda %s", lbuf);
1963 AddCodeSegLine (CS, "sta tmp1");
1964 AddCodeSegLine (CS, "lda %s", lbuf);
1965 AddCodeSegLine (CS, "sbc tmp1");
1966 AddCodeSegLine (CS, "sta %s", lbuf);
1967 AddCodeSegLine (CS, "stx tmp1");
1968 AddCodeSegLine (CS, "lda %s+1", lbuf);
1969 AddCodeSegLine (CS, "sbc tmp1");
1970 AddCodeSegLine (CS, "sta %s+1", lbuf);
1971 AddCodeSegLine (CS, "tax");
1972 AddCodeSegLine (CS, "lda %s", lbuf);
1977 if (flags & CF_CONST) {
1979 AddCodeSegLine (CS, "ldy #<(%s)", lbuf);
1980 AddCodeSegLine (CS, "sty ptr1");
1981 AddCodeSegLine (CS, "ldy #>(%s+1)", lbuf);
1983 AddCodeSegLine (CS, "jsr lsubeq1");
1985 AddCodeSegLine (CS, "lda #$%02X", (unsigned char)val);
1986 AddCodeSegLine (CS, "jsr lsubeqa");
1989 g_getstatic (flags, label, offs);
1991 g_putstatic (flags, label, offs);
1994 AddCodeSegLine (CS, "ldy #<(%s)", lbuf);
1995 AddCodeSegLine (CS, "sty ptr1");
1996 AddCodeSegLine (CS, "ldy #>(%s+1)", lbuf);
1997 AddCodeSegLine (CS, "jsr lsubeq");
2008 void g_subeqlocal (unsigned flags, int offs, unsigned long val)
2009 /* Emit -= for a local variable */
2011 /* Calculate the true offset, check it, load it into Y */
2013 CheckLocalOffs (offs);
2015 /* Check the size and determine operation */
2016 switch (flags & CF_TYPE) {
2019 if (flags & CF_FORCECHAR) {
2021 AddCodeSegLine (CS, "ldx #$00");
2022 AddCodeSegLine (CS, "sec");
2023 if (flags & CF_CONST) {
2024 AddCodeSegLine (CS, "lda (sp),y");
2025 AddCodeSegLine (CS, "sbc #$%02X", (unsigned char)val);
2027 AddCodeSegLine (CS, "sta tmp1");
2028 AddCodeSegLine (CS, "lda (sp),y");
2029 AddCodeSegLine (CS, "sbc tmp1");
2031 AddCodeSegLine (CS, "sta (sp),y");
2032 if ((flags & CF_UNSIGNED) == 0) {
2033 AddCodeSegLine (CS, "bpl *+3");
2034 AddCodeSegLine (CS, "dex");
2035 AddCodeHint ("x:!"); /* Invalidate X */
2042 if (flags & CF_CONST) {
2043 g_getimmed (flags, val, 0);
2046 AddCodeSegLine (CS, "jsr subeq0sp");
2049 AddCodeSegLine (CS, "jsr subeqysp");
2054 if (flags & CF_CONST) {
2055 g_getimmed (flags, val, 0);
2058 AddCodeSegLine (CS, "jsr lsubeq0sp");
2061 AddCodeSegLine (CS, "jsr lsubeqysp");
2072 void g_subeqind (unsigned flags, unsigned offs, unsigned long val)
2073 /* Emit -= for the location with address in ax */
2075 /* If the offset is too large for a byte register, add the high byte
2076 * of the offset to the primary. Beware: We need a special correction
2077 * if the offset in the low byte will overflow in the operation.
2079 offs = MakeByteOffs (flags, offs);
2081 /* Check the size and determine operation */
2082 switch (flags & CF_TYPE) {
2085 AddCodeSegLine (CS, "sta ptr1");
2086 AddCodeSegLine (CS, "stx ptr1+1");
2088 AddCodeSegLine (CS, "ldx #$00");
2089 AddCodeSegLine (CS, "lda (ptr1,x)");
2090 AddCodeSegLine (CS, "sec");
2091 AddCodeSegLine (CS, "sbc #$%02X", (unsigned char)val);
2092 AddCodeSegLine (CS, "sta (ptr1,x)");
2094 AddCodeSegLine (CS, "ldy #$%02X", offs);
2095 AddCodeSegLine (CS, "ldx #$00");
2096 AddCodeSegLine (CS, "lda (ptr1),y");
2097 AddCodeSegLine (CS, "sec");
2098 AddCodeSegLine (CS, "sbc #$%02X", (unsigned char)val);
2099 AddCodeSegLine (CS, "sta (ptr1),y");
2104 if (CodeSizeFactor >= 200) {
2105 /* Lots of code, use only if size is not important */
2106 AddCodeSegLine (CS, "sta ptr1");
2107 AddCodeSegLine (CS, "stx ptr1+1");
2108 AddCodeSegLine (CS, "ldy #$%02X", offs);
2109 AddCodeSegLine (CS, "lda (ptr1),y");
2110 AddCodeSegLine (CS, "sec");
2111 AddCodeSegLine (CS, "sbc #$%02X", (unsigned char)val);
2112 AddCodeSegLine (CS, "sta (ptr1),y");
2113 AddCodeSegLine (CS, "pha");
2114 AddCodeSegLine (CS, "iny");
2115 AddCodeSegLine (CS, "lda (ptr1),y");
2116 AddCodeSegLine (CS, "sbc #$%02X", (unsigned char)(val >> 8));
2117 AddCodeSegLine (CS, "sta (ptr1),y");
2118 AddCodeSegLine (CS, "tax");
2119 AddCodeSegLine (CS, "pla");
2125 AddCodeSegLine (CS, "jsr pushax"); /* Push the address */
2126 push (flags); /* Correct the internal sp */
2127 g_getind (flags, offs); /* Fetch the value */
2128 g_dec (flags, val); /* Increment value in primary */
2129 g_putind (flags, offs); /* Store the value back */
2139 /*****************************************************************************/
2140 /* Add a variable address to the value in ax */
2141 /*****************************************************************************/
2145 void g_addaddr_local (unsigned flags, int offs)
2146 /* Add the address of a local variable to ax */
2148 /* Add the offset */
2151 /* We cannot address more then 256 bytes of locals anyway */
2152 CheckLocalOffs (offs);
2153 AddCodeSegLine (CS, "clc");
2154 AddCodeSegLine (CS, "adc #$%02X", offs & 0xFF);
2155 AddCodeSegLine (CS, "bcc *+4"); /* Do also skip the CLC insn below */
2156 AddCodeSegLine (CS, "inx");
2157 AddCodeHint ("x:!"); /* Invalidate X */
2160 /* Add the current stackpointer value */
2161 AddCodeSegLine (CS, "clc");
2162 AddCodeSegLine (CS, "adc sp");
2163 AddCodeSegLine (CS, "tay");
2164 AddCodeSegLine (CS, "txa");
2165 AddCodeSegLine (CS, "adc sp+1");
2166 AddCodeSegLine (CS, "tax");
2167 AddCodeSegLine (CS, "tya");
2172 void g_addaddr_static (unsigned flags, unsigned long label, unsigned offs)
2173 /* Add the address of a static variable to ax */
2175 /* Create the correct label name */
2176 char* lbuf = GetLabelName (flags, label, offs);
2178 /* Add the address to the current ax value */
2179 AddCodeSegLine (CS, "clc");
2180 AddCodeSegLine (CS, "adc #<(%s)", lbuf);
2181 AddCodeSegLine (CS, "tay");
2182 AddCodeSegLine (CS, "txa");
2183 AddCodeSegLine (CS, "adc #>(%s)", lbuf);
2184 AddCodeSegLine (CS, "tax");
2185 AddCodeSegLine (CS, "tya");
2190 /*****************************************************************************/
2192 /*****************************************************************************/
2196 void g_save (unsigned flags)
2197 /* Copy primary register to hold register. */
2199 /* Check the size and determine operation */
2200 switch (flags & CF_TYPE) {
2203 if (flags & CF_FORCECHAR) {
2204 AddCodeSegLine (CS, "pha");
2210 AddCodeSegLine (CS, "sta regsave");
2211 AddCodeSegLine (CS, "stx regsave+1");
2215 AddCodeSegLine (CS, "jsr saveeax");
2225 void g_restore (unsigned flags)
2226 /* Copy hold register to P. */
2228 /* Check the size and determine operation */
2229 switch (flags & CF_TYPE) {
2232 if (flags & CF_FORCECHAR) {
2233 AddCodeSegLine (CS, "pla");
2239 AddCodeSegLine (CS, "lda regsave");
2240 AddCodeSegLine (CS, "ldx regsave+1");
2244 AddCodeSegLine (CS, "jsr resteax");
2254 void g_cmp (unsigned flags, unsigned long val)
2255 /* Immidiate compare. The primary register will not be changed, Z flag
2259 /* Check the size and determine operation */
2260 switch (flags & CF_TYPE) {
2263 if (flags & CF_FORCECHAR) {
2264 AddCodeSegLine (CS, "cmp #$%02X", (unsigned char)val);
2270 AddCodeSegLine (CS, "cmp #$%02X", (unsigned char)val);
2271 AddCodeSegLine (CS, "bne *+4");
2272 AddCodeSegLine (CS, "cpx #$%02X", (unsigned char)(val >> 8));
2276 Internal ("g_cmp: Long compares not implemented");
2286 static void oper (unsigned flags, unsigned long val, char** subs)
2287 /* Encode a binary operation. subs is a pointer to four groups of three
2289 * 0-2 --> Operate on ints
2290 * 3-5 --> Operate on unsigneds
2291 * 6-8 --> Operate on longs
2292 * 9-11 --> Operate on unsigned longs
2294 * The first subroutine names in each string group is used to encode an
2295 * operation with a zero constant, the second to encode an operation with
2296 * a 8 bit constant, and the third is used in all other cases.
2301 /* Determine the offset into the array */
2302 offs = (flags & CF_UNSIGNED)? 3 : 0;
2303 switch (flags & CF_TYPE) {
2316 /* Encode the operation */
2317 if (flags & CF_CONST) {
2318 /* Constant value given */
2319 if (val == 0 && subs [offs+0]) {
2320 /* Special case: constant with value zero */
2321 AddCodeSegLine (CS, "jsr %s", subs [offs+0]);
2322 } else if (val < 0x100 && subs [offs+1]) {
2323 /* Special case: constant with high byte zero */
2324 ldaconst (val); /* Load low byte */
2325 AddCodeSegLine (CS, "jsr %s", subs [offs+1]);
2327 /* Others: arbitrary constant value */
2328 g_getimmed (flags, val, 0); /* Load value */
2329 AddCodeSegLine (CS, "jsr %s", subs [offs+2]);
2332 /* Value not constant (is already in (e)ax) */
2333 AddCodeSegLine (CS, "jsr %s", subs [offs+2]);
2336 /* The operation will pop it's argument */
2342 void g_test (unsigned flags)
2343 /* Force a test to set cond codes right */
2345 switch (flags & CF_TYPE) {
2348 if (flags & CF_FORCECHAR) {
2349 AddCodeSegLine (CS, "tax");
2355 AddCodeSegLine (CS, "stx tmp1");
2356 AddCodeSegLine (CS, "ora tmp1");
2360 if (flags & CF_UNSIGNED) {
2361 AddCodeSegLine (CS, "jsr utsteax");
2363 AddCodeSegLine (CS, "jsr tsteax");
2375 void g_push (unsigned flags, unsigned long val)
2376 /* Push the primary register or a constant value onto the stack */
2380 if (flags & CF_CONST && (flags & CF_TYPE) != CF_LONG) {
2382 /* We have a constant 8 or 16 bit value */
2383 if ((flags & CF_TYPE) == CF_CHAR && (flags & CF_FORCECHAR)) {
2385 /* Handle as 8 bit value */
2386 if (CodeSizeFactor >= 165 || val > 2) {
2388 AddCodeSegLine (CS, "jsr pusha");
2390 AddCodeSegLine (CS, "jsr pushc%d", (int) val);
2395 /* Handle as 16 bit value */
2396 hi = (unsigned char) (val >> 8);
2398 AddCodeSegLine (CS, "jsr push%u", (unsigned) val);
2399 } else if (hi == 0 || hi == 0xFF) {
2400 /* Use special function */
2402 AddCodeSegLine (CS, "jsr %s", (hi == 0)? "pusha0" : "pushaFF");
2405 g_getimmed (flags, val, 0);
2406 AddCodeSegLine (CS, "jsr pushax");
2412 /* Value is not 16 bit or not constant */
2413 if (flags & CF_CONST) {
2414 /* Constant 32 bit value, load into eax */
2415 g_getimmed (flags, val, 0);
2418 /* Push the primary register */
2419 switch (flags & CF_TYPE) {
2422 if (flags & CF_FORCECHAR) {
2423 /* Handle as char */
2424 AddCodeSegLine (CS, "jsr pusha");
2429 AddCodeSegLine (CS, "jsr pushax");
2433 AddCodeSegLine (CS, "jsr pusheax");
2443 /* Adjust the stack offset */
2449 void g_swap (unsigned flags)
2450 /* Swap the primary register and the top of the stack. flags give the type
2451 * of *both* values (must have same size).
2454 switch (flags & CF_TYPE) {
2458 AddCodeSegLine (CS, "jsr swapstk");
2462 AddCodeSegLine (CS, "jsr swapestk");
2473 void g_call (unsigned Flags, const char* Label, unsigned ArgSize)
2474 /* Call the specified subroutine name */
2476 if ((Flags & CF_FIXARGC) == 0) {
2477 /* Pass the argument count */
2480 AddCodeSegLine (CS, "jsr _%s", Label);
2481 oursp += ArgSize; /* callee pops args */
2486 void g_callind (unsigned Flags, unsigned ArgSize)
2487 /* Call subroutine with address in AX */
2489 if ((Flags & CF_FIXARGC) == 0) {
2490 /* Pass arg count */
2493 AddCodeSegLine (CS, "jsr callax"); /* do the call */
2494 oursp += ArgSize; /* callee pops args */
2499 void g_jump (unsigned Label)
2500 /* Jump to specified internal label number */
2502 AddCodeSegLine (CS, "jmp L%04X", Label);
2507 void g_switch (unsigned Flags)
2508 /* Output switch statement preamble */
2510 switch (Flags & CF_TYPE) {
2514 AddCodeSegLine (CS, "jsr switch");
2518 AddCodeSegLine (CS, "jsr lswitch");
2529 void g_case (unsigned flags, unsigned label, unsigned long val)
2530 /* Create table code for one case selector */
2532 switch (flags & CF_TYPE) {
2536 AddCodeSegLine (CS, ".word $%04X, L%04X",
2537 (unsigned)(val & 0xFFFF),
2538 (unsigned)(label & 0xFFFF));
2542 AddCodeSegLine (CS, ".dword $%08lX", val);
2543 AddCodeSegLine (CS, ".word L%04X", label & 0xFFFF);
2554 void g_truejump (unsigned flags, unsigned label)
2555 /* Jump to label if zero flag clear */
2557 if (flags & CF_SHORT) {
2558 AddCodeSegLine (CS, "bne L%04X", label);
2560 AddCodeSegLine (CS, "jne L%04X", label);
2566 void g_falsejump (unsigned flags, unsigned label)
2567 /* Jump to label if zero flag set */
2569 if (flags & CF_SHORT) {
2570 AddCodeSegLine (CS, "beq L%04X", label);
2572 AddCodeSegLine (CS, "jeq L%04X", label);
2578 static void mod_internal (int k, char* verb1, char* verb2)
2581 AddCodeSegLine (CS, "jsr %ssp%c", verb1, k + '0');
2585 AddCodeSegLine (CS, "jsr %ssp", verb2);
2591 void g_space (int space)
2592 /* Create or drop space on the stack */
2595 mod_internal (-space, "inc", "addy");
2596 } else if (space > 0) {
2597 mod_internal (space, "dec", "suby");
2603 void g_cstackcheck (void)
2604 /* Check for a C stack overflow */
2606 AddCodeSegLine (CS, "jsr cstkchk");
2611 void g_stackcheck (void)
2612 /* Check for a stack overflow */
2614 AddCodeSegLine (CS, "jsr stkchk");
2619 void g_add (unsigned flags, unsigned long val)
2620 /* Primary = TOS + Primary */
2622 static char* ops [12] = {
2623 0, "tosadda0", "tosaddax",
2624 0, "tosadda0", "tosaddax",
2629 if (flags & CF_CONST) {
2630 flags &= ~CF_FORCECHAR; /* Handle chars as ints */
2631 g_push (flags & ~CF_CONST, 0);
2633 oper (flags, val, ops);
2638 void g_sub (unsigned flags, unsigned long val)
2639 /* Primary = TOS - Primary */
2641 static char* ops [12] = {
2642 0, "tossuba0", "tossubax",
2643 0, "tossuba0", "tossubax",
2648 if (flags & CF_CONST) {
2649 flags &= ~CF_FORCECHAR; /* Handle chars as ints */
2650 g_push (flags & ~CF_CONST, 0);
2652 oper (flags, val, ops);
2657 void g_rsub (unsigned flags, unsigned long val)
2658 /* Primary = Primary - TOS */
2660 static char* ops [12] = {
2661 0, "tosrsuba0", "tosrsubax",
2662 0, "tosrsuba0", "tosrsubax",
2666 oper (flags, val, ops);
2671 void g_mul (unsigned flags, unsigned long val)
2672 /* Primary = TOS * Primary */
2674 static char* ops [12] = {
2675 0, "tosmula0", "tosmulax",
2676 0, "tosumula0", "tosumulax",
2683 /* Do strength reduction if the value is constant and a power of two */
2684 if (flags & CF_CONST && (p2 = powerof2 (val)) >= 0) {
2685 /* Generate a shift instead */
2690 /* If the right hand side is const, the lhs is not on stack but still
2691 * in the primary register.
2693 if (flags & CF_CONST) {
2695 switch (flags & CF_TYPE) {
2698 if (flags & CF_FORCECHAR) {
2699 /* Handle some special cases */
2703 AddCodeSegLine (CS, "sta tmp1");
2704 AddCodeSegLine (CS, "asl a");
2705 AddCodeSegLine (CS, "clc");
2706 AddCodeSegLine (CS, "adc tmp1");
2710 AddCodeSegLine (CS, "sta tmp1");
2711 AddCodeSegLine (CS, "asl a");
2712 AddCodeSegLine (CS, "asl a");
2713 AddCodeSegLine (CS, "clc");
2714 AddCodeSegLine (CS, "adc tmp1");
2718 AddCodeSegLine (CS, "sta tmp1");
2719 AddCodeSegLine (CS, "asl a");
2720 AddCodeSegLine (CS, "asl a");
2721 AddCodeSegLine (CS, "clc");
2722 AddCodeSegLine (CS, "adc tmp1");
2723 AddCodeSegLine (CS, "asl a");
2739 /* If we go here, we didn't emit code. Push the lhs on stack and fall
2740 * into the normal, non-optimized stuff.
2742 flags &= ~CF_FORCECHAR; /* Handle chars as ints */
2743 g_push (flags & ~CF_CONST, 0);
2747 /* Use long way over the stack */
2748 oper (flags, val, ops);
2753 void g_div (unsigned flags, unsigned long val)
2754 /* Primary = TOS / Primary */
2756 static char* ops [12] = {
2757 0, "tosdiva0", "tosdivax",
2758 0, "tosudiva0", "tosudivax",
2763 /* Do strength reduction if the value is constant and a power of two */
2765 if ((flags & CF_CONST) && (p2 = powerof2 (val)) >= 0) {
2766 /* Generate a shift instead */
2769 /* Generate a division */
2770 if (flags & CF_CONST) {
2771 /* lhs is not on stack */
2772 flags &= ~CF_FORCECHAR; /* Handle chars as ints */
2773 g_push (flags & ~CF_CONST, 0);
2775 oper (flags, val, ops);
2781 void g_mod (unsigned flags, unsigned long val)
2782 /* Primary = TOS % Primary */
2784 static char* ops [12] = {
2785 0, "tosmoda0", "tosmodax",
2786 0, "tosumoda0", "tosumodax",
2792 /* Check if we can do some cost reduction */
2793 if ((flags & CF_CONST) && (flags & CF_UNSIGNED) && val != 0xFFFFFFFF && (p2 = powerof2 (val)) >= 0) {
2794 /* We can do that with an AND operation */
2795 g_and (flags, val - 1);
2797 /* Do it the hard way... */
2798 if (flags & CF_CONST) {
2799 /* lhs is not on stack */
2800 flags &= ~CF_FORCECHAR; /* Handle chars as ints */
2801 g_push (flags & ~CF_CONST, 0);
2803 oper (flags, val, ops);
2809 void g_or (unsigned flags, unsigned long val)
2810 /* Primary = TOS | Primary */
2812 static char* ops [12] = {
2813 0, "tosora0", "tosorax",
2814 0, "tosora0", "tosorax",
2819 /* If the right hand side is const, the lhs is not on stack but still
2820 * in the primary register.
2822 if (flags & CF_CONST) {
2824 switch (flags & CF_TYPE) {
2827 if (flags & CF_FORCECHAR) {
2828 if ((val & 0xFF) != 0xFF) {
2829 AddCodeSegLine (CS, "ora #$%02X", (unsigned char)val);
2837 AddCodeSegLine (CS, "ora #$%02X", (unsigned char)val);
2844 AddCodeSegLine (CS, "ora #$%02X", (unsigned char)val);
2853 /* If we go here, we didn't emit code. Push the lhs on stack and fall
2854 * into the normal, non-optimized stuff.
2856 g_push (flags & ~CF_CONST, 0);
2860 /* Use long way over the stack */
2861 oper (flags, val, ops);
2866 void g_xor (unsigned flags, unsigned long val)
2867 /* Primary = TOS ^ Primary */
2869 static char* ops [12] = {
2870 0, "tosxora0", "tosxorax",
2871 0, "tosxora0", "tosxorax",
2877 /* If the right hand side is const, the lhs is not on stack but still
2878 * in the primary register.
2880 if (flags & CF_CONST) {
2882 switch (flags & CF_TYPE) {
2885 if (flags & CF_FORCECHAR) {
2886 if ((val & 0xFF) != 0) {
2887 AddCodeSegLine (CS, "eor #$%02X", (unsigned char)val);
2896 AddCodeSegLine (CS, "eor #$%02X", (unsigned char)val);
2899 } else if ((val & 0xFF) == 0) {
2900 AddCodeSegLine (CS, "pha");
2901 AddCodeSegLine (CS, "txa");
2902 AddCodeSegLine (CS, "eor #$%02X", (unsigned char)(val >> 8));
2903 AddCodeSegLine (CS, "tax");
2904 AddCodeSegLine (CS, "pla");
2912 AddCodeSegLine (CS, "eor #$%02X", (unsigned char)val);
2922 /* If we go here, we didn't emit code. Push the lhs on stack and fall
2923 * into the normal, non-optimized stuff.
2925 g_push (flags & ~CF_CONST, 0);
2929 /* Use long way over the stack */
2930 oper (flags, val, ops);
2935 void g_and (unsigned flags, unsigned long val)
2936 /* Primary = TOS & Primary */
2938 static char* ops [12] = {
2939 0, "tosanda0", "tosandax",
2940 0, "tosanda0", "tosandax",
2945 /* If the right hand side is const, the lhs is not on stack but still
2946 * in the primary register.
2948 if (flags & CF_CONST) {
2950 switch (flags & CF_TYPE) {
2953 if (flags & CF_FORCECHAR) {
2954 AddCodeSegLine (CS, "and #$%02X", (unsigned char)val);
2959 if ((val & 0xFFFF) != 0xFFFF) {
2964 } else if (val != 0xFF) {
2965 AddCodeSegLine (CS, "and #$%02X", (unsigned char)val);
2967 } else if ((val & 0xFF00) == 0xFF00) {
2968 AddCodeSegLine (CS, "and #$%02X", (unsigned char)val);
2969 } else if ((val & 0x00FF) == 0x0000) {
2970 AddCodeSegLine (CS, "txa");
2971 AddCodeSegLine (CS, "and #$%02X", (unsigned char)(val >> 8));
2972 AddCodeSegLine (CS, "tax");
2975 AddCodeSegLine (CS, "tay");
2976 AddCodeSegLine (CS, "txa");
2977 AddCodeSegLine (CS, "and #$%02X", (unsigned char)(val >> 8));
2978 AddCodeSegLine (CS, "tax");
2979 AddCodeSegLine (CS, "tya");
2980 if ((val & 0x00FF) != 0x00FF) {
2981 AddCodeSegLine (CS, "and #$%02X", (unsigned char)val);
2990 AddCodeSegLine (CS, "stx sreg+1");
2991 AddCodeSegLine (CS, "stx sreg");
2992 if ((val & 0xFF) != 0xFF) {
2993 AddCodeSegLine (CS, "and #$%02X", (unsigned char)val);
2996 } else if (val == 0xFF00) {
2998 AddCodeSegLine (CS, "sta sreg+1");
2999 AddCodeSegLine (CS, "sta sreg");
3008 /* If we go here, we didn't emit code. Push the lhs on stack and fall
3009 * into the normal, non-optimized stuff.
3011 g_push (flags & ~CF_CONST, 0);
3015 /* Use long way over the stack */
3016 oper (flags, val, ops);
3021 void g_asr (unsigned flags, unsigned long val)
3022 /* Primary = TOS >> Primary */
3024 static char* ops [12] = {
3025 0, "tosasra0", "tosasrax",
3026 0, "tosshra0", "tosshrax",
3031 /* If the right hand side is const, the lhs is not on stack but still
3032 * in the primary register.
3034 if (flags & CF_CONST) {
3036 switch (flags & CF_TYPE) {
3040 if (val >= 1 && val <= 3) {
3041 if (flags & CF_UNSIGNED) {
3042 AddCodeSegLine (CS, "jsr shrax%ld", val);
3044 AddCodeSegLine (CS, "jsr asrax%ld", val);
3047 } else if (val == 8 && (flags & CF_UNSIGNED)) {
3048 AddCodeSegLine (CS, "txa");
3055 if (val >= 1 && val <= 3) {
3056 if (flags & CF_UNSIGNED) {
3057 AddCodeSegLine (CS, "jsr shreax%ld", val);
3059 AddCodeSegLine (CS, "jsr asreax%ld", val);
3062 } else if (val == 8 && (flags & CF_UNSIGNED)) {
3063 AddCodeSegLine (CS, "txa");
3064 AddCodeSegLine (CS, "ldx sreg");
3065 AddCodeSegLine (CS, "ldy sreg+1");
3066 AddCodeSegLine (CS, "sty sreg");
3067 AddCodeSegLine (CS, "ldy #$00");
3068 AddCodeSegLine (CS, "sty sreg+1");
3070 } else if (val == 16) {
3071 AddCodeSegLine (CS, "ldy #$00");
3072 AddCodeSegLine (CS, "ldx sreg+1");
3073 if ((flags & CF_UNSIGNED) == 0) {
3074 AddCodeSegLine (CS, "bpl *+3");
3075 AddCodeSegLine (CS, "dey");
3076 AddCodeHint ("y:!");
3078 AddCodeSegLine (CS, "lda sreg");
3079 AddCodeSegLine (CS, "sty sreg+1");
3080 AddCodeSegLine (CS, "sty sreg");
3089 /* If we go here, we didn't emit code. Push the lhs on stack and fall
3090 * into the normal, non-optimized stuff.
3092 g_push (flags & ~CF_CONST, 0);
3096 /* Use long way over the stack */
3097 oper (flags, val, ops);
3102 void g_asl (unsigned flags, unsigned long val)
3103 /* Primary = TOS << Primary */
3105 static char* ops [12] = {
3106 0, "tosasla0", "tosaslax",
3107 0, "tosshla0", "tosshlax",
3113 /* If the right hand side is const, the lhs is not on stack but still
3114 * in the primary register.
3116 if (flags & CF_CONST) {
3118 switch (flags & CF_TYPE) {
3122 if (val >= 1 && val <= 3) {
3123 if (flags & CF_UNSIGNED) {
3124 AddCodeSegLine (CS, "jsr shlax%ld", val);
3126 AddCodeSegLine (CS, "jsr aslax%ld", val);
3129 } else if (val == 8) {
3130 AddCodeSegLine (CS, "tax");
3131 AddCodeSegLine (CS, "lda #$00");
3137 if (val >= 1 && val <= 3) {
3138 if (flags & CF_UNSIGNED) {
3139 AddCodeSegLine (CS, "jsr shleax%ld", val);
3141 AddCodeSegLine (CS, "jsr asleax%ld", val);
3144 } else if (val == 8) {
3145 AddCodeSegLine (CS, "ldy sreg");
3146 AddCodeSegLine (CS, "sty sreg+1");
3147 AddCodeSegLine (CS, "stx sreg");
3148 AddCodeSegLine (CS, "tax");
3149 AddCodeSegLine (CS, "lda #$00");
3151 } else if (val == 16) {
3152 AddCodeSegLine (CS, "stx sreg+1");
3153 AddCodeSegLine (CS, "sta sreg");
3154 AddCodeSegLine (CS, "lda #$00");
3155 AddCodeSegLine (CS, "tax");
3164 /* If we go here, we didn't emit code. Push the lhs on stack and fall
3165 * into the normal, non-optimized stuff.
3167 g_push (flags & ~CF_CONST, 0);
3171 /* Use long way over the stack */
3172 oper (flags, val, ops);
3177 void g_neg (unsigned flags)
3178 /* Primary = -Primary */
3180 switch (flags & CF_TYPE) {
3184 AddCodeSegLine (CS, "jsr negax");
3188 AddCodeSegLine (CS, "jsr negeax");
3198 void g_bneg (unsigned flags)
3199 /* Primary = !Primary */
3201 switch (flags & CF_TYPE) {
3204 AddCodeSegLine (CS, "jsr bnega");
3208 AddCodeSegLine (CS, "jsr bnegax");
3212 AddCodeSegLine (CS, "jsr bnegeax");
3222 void g_com (unsigned flags)
3223 /* Primary = ~Primary */
3225 switch (flags & CF_TYPE) {
3229 AddCodeSegLine (CS, "jsr complax");
3233 AddCodeSegLine (CS, "jsr compleax");
3243 void g_inc (unsigned flags, unsigned long val)
3244 /* Increment the primary register by a given number */
3246 /* Don't inc by zero */
3251 /* Generate code for the supported types */
3253 switch (flags & CF_TYPE) {
3256 if (flags & CF_FORCECHAR) {
3257 if (CPU == CPU_65C02 && val <= 2) {
3259 AddCodeSegLine (CS, "ina");
3262 AddCodeSegLine (CS, "clc");
3263 AddCodeSegLine (CS, "adc #$%02X", (unsigned char)val);
3270 if (CPU == CPU_65C02 && val == 1) {
3271 AddCodeSegLine (CS, "ina");
3272 AddCodeSegLine (CS, "bne *+3");
3273 AddCodeSegLine (CS, "inx");
3274 /* Tell the optimizer that the X register may be invalid */
3275 AddCodeHint ("x:!");
3276 } else if (CodeSizeFactor < 200) {
3279 AddCodeSegLine (CS, "jsr incax%lu", val);
3280 } else if (val <= 255) {
3282 AddCodeSegLine (CS, "jsr incaxy");
3284 g_add (flags | CF_CONST, val);
3287 /* Inline the code */
3289 if ((val & 0xFF) != 0) {
3290 AddCodeSegLine (CS, "clc");
3291 AddCodeSegLine (CS, "adc #$%02X", (unsigned char) val);
3292 AddCodeSegLine (CS, "bcc *+3");
3293 AddCodeSegLine (CS, "inx");
3294 /* Tell the optimizer that the X register may be invalid */
3295 AddCodeHint ("x:!");
3298 AddCodeSegLine (CS, "inx");
3301 AddCodeSegLine (CS, "inx");
3304 AddCodeSegLine (CS, "clc");
3305 if ((val & 0xFF) != 0) {
3306 AddCodeSegLine (CS, "adc #$%02X", (unsigned char) val);
3307 /* Tell the optimizer that the X register may be invalid */
3308 AddCodeHint ("x:!");
3310 AddCodeSegLine (CS, "pha");
3311 AddCodeSegLine (CS, "txa");
3312 AddCodeSegLine (CS, "adc #$%02X", (unsigned char) (val >> 8));
3313 AddCodeSegLine (CS, "tax");
3314 AddCodeSegLine (CS, "pla");
3322 AddCodeSegLine (CS, "jsr inceaxy");
3324 g_add (flags | CF_CONST, val);
3336 void g_dec (unsigned flags, unsigned long val)
3337 /* Decrement the primary register by a given number */
3339 /* Don't dec by zero */
3344 /* Generate code for the supported types */
3346 switch (flags & CF_TYPE) {
3349 if (flags & CF_FORCECHAR) {
3350 if (CPU == CPU_65C02 && val <= 2) {
3352 AddCodeSegLine (CS, "dea");
3355 AddCodeSegLine (CS, "sec");
3356 AddCodeSegLine (CS, "sbc #$%02X", (unsigned char)val);
3363 if (CodeSizeFactor < 200) {
3364 /* Use subroutines */
3366 AddCodeSegLine (CS, "jsr decax%d", (int) val);
3367 } else if (val <= 255) {
3369 AddCodeSegLine (CS, "jsr decaxy");
3371 g_sub (flags | CF_CONST, val);
3374 /* Inline the code */
3376 if ((val & 0xFF) != 0) {
3377 AddCodeSegLine (CS, "sec");
3378 AddCodeSegLine (CS, "sbc #$%02X", (unsigned char) val);
3379 AddCodeSegLine (CS, "bcs *+3");
3380 AddCodeSegLine (CS, "dex");
3381 /* Tell the optimizer that the X register may be invalid */
3382 AddCodeHint ("x:!");
3385 AddCodeSegLine (CS, "dex");
3388 AddCodeSegLine (CS, "dex");
3391 AddCodeSegLine (CS, "sec");
3392 if ((val & 0xFF) != 0) {
3393 AddCodeSegLine (CS, "sbc #$%02X", (unsigned char) val);
3394 /* Tell the optimizer that the X register may be invalid */
3395 AddCodeHint ("x:!");
3397 AddCodeSegLine (CS, "pha");
3398 AddCodeSegLine (CS, "txa");
3399 AddCodeSegLine (CS, "sbc #$%02X", (unsigned char) (val >> 8));
3400 AddCodeSegLine (CS, "tax");
3401 AddCodeSegLine (CS, "pla");
3409 AddCodeSegLine (CS, "jsr deceaxy");
3411 g_sub (flags | CF_CONST, val);
3424 * Following are the conditional operators. They compare the TOS against
3425 * the primary and put a literal 1 in the primary if the condition is
3426 * true, otherwise they clear the primary register
3431 void g_eq (unsigned flags, unsigned long val)
3432 /* Test for equal */
3434 static char* ops [12] = {
3435 "toseq00", "toseqa0", "toseqax",
3436 "toseq00", "toseqa0", "toseqax",
3441 /* If the right hand side is const, the lhs is not on stack but still
3442 * in the primary register.
3444 if (flags & CF_CONST) {
3446 switch (flags & CF_TYPE) {
3449 if (flags & CF_FORCECHAR) {
3450 AddCodeSegLine (CS, "cmp #$%02X", (unsigned char)val);
3451 AddCodeSegLine (CS, "jsr booleq");
3457 AddCodeSegLine (CS, "cpx #$%02X", (unsigned char)(val >> 8));
3458 AddCodeSegLine (CS, "bne *+4");
3459 AddCodeSegLine (CS, "cmp #$%02X", (unsigned char)val);
3460 AddCodeSegLine (CS, "jsr booleq");
3470 /* If we go here, we didn't emit code. Push the lhs on stack and fall
3471 * into the normal, non-optimized stuff.
3473 g_push (flags & ~CF_CONST, 0);
3477 /* Use long way over the stack */
3478 oper (flags, val, ops);
3483 void g_ne (unsigned flags, unsigned long val)
3484 /* Test for not equal */
3486 static char* ops [12] = {
3487 "tosne00", "tosnea0", "tosneax",
3488 "tosne00", "tosnea0", "tosneax",
3494 /* If the right hand side is const, the lhs is not on stack but still
3495 * in the primary register.
3497 if (flags & CF_CONST) {
3499 switch (flags & CF_TYPE) {
3502 if (flags & CF_FORCECHAR) {
3503 AddCodeSegLine (CS, "cmp #$%02X", (unsigned char)val);
3504 AddCodeSegLine (CS, "jsr boolne");
3510 AddCodeSegLine (CS, "cpx #$%02X", (unsigned char)(val >> 8));
3511 AddCodeSegLine (CS, "bne *+4");
3512 AddCodeSegLine (CS, "cmp #$%02X", (unsigned char)val);
3513 AddCodeSegLine (CS, "jsr boolne");
3523 /* If we go here, we didn't emit code. Push the lhs on stack and fall
3524 * into the normal, non-optimized stuff.
3526 g_push (flags & ~CF_CONST, 0);
3530 /* Use long way over the stack */
3531 oper (flags, val, ops);
3536 void g_lt (unsigned flags, unsigned long val)
3537 /* Test for less than */
3539 static char* ops [12] = {
3540 "toslt00", "toslta0", "tosltax",
3541 "tosult00", "tosulta0", "tosultax",
3546 /* If the right hand side is const, the lhs is not on stack but still
3547 * in the primary register.
3549 if (flags & CF_CONST) {
3551 /* Give a warning in some special cases */
3552 if ((flags & CF_UNSIGNED) && val == 0) {
3553 Warning ("Condition is never true");
3556 /* Look at the type */
3557 switch (flags & CF_TYPE) {
3560 if (flags & CF_FORCECHAR) {
3561 AddCodeSegLine (CS, "cmp #$%02X", (unsigned char)val);
3562 if (flags & CF_UNSIGNED) {
3563 AddCodeSegLine (CS, "jsr boolult");
3565 AddCodeSegLine (CS, "jsr boollt");
3572 if ((flags & CF_UNSIGNED) == 0 && val == 0) {
3573 /* If we have a signed compare against zero, we only need to
3574 * test the high byte.
3576 AddCodeSegLine (CS, "txa");
3577 AddCodeSegLine (CS, "jsr boollt");
3580 /* Direct code only for unsigned data types */
3581 if (flags & CF_UNSIGNED) {
3582 AddCodeSegLine (CS, "cpx #$%02X", (unsigned char)(val >> 8));
3583 AddCodeSegLine (CS, "bne *+4");
3584 AddCodeSegLine (CS, "cmp #$%02X", (unsigned char)val);
3585 AddCodeSegLine (CS, "jsr boolult");
3597 /* If we go here, we didn't emit code. Push the lhs on stack and fall
3598 * into the normal, non-optimized stuff.
3600 g_push (flags & ~CF_CONST, 0);
3604 /* Use long way over the stack */
3605 oper (flags, val, ops);
3610 void g_le (unsigned flags, unsigned long val)
3611 /* Test for less than or equal to */
3613 static char* ops [12] = {
3614 "tosle00", "toslea0", "tosleax",
3615 "tosule00", "tosulea0", "tosuleax",
3621 /* If the right hand side is const, the lhs is not on stack but still
3622 * in the primary register.
3624 if (flags & CF_CONST) {
3626 /* Look at the type */
3627 switch (flags & CF_TYPE) {
3630 if (flags & CF_FORCECHAR) {
3631 AddCodeSegLine (CS, "cmp #$%02X", (unsigned char)val);
3632 if (flags & CF_UNSIGNED) {
3633 AddCodeSegLine (CS, "jsr boolule");
3635 AddCodeSegLine (CS, "jsr boolle");
3642 if (flags & CF_UNSIGNED) {
3643 AddCodeSegLine (CS, "cpx #$%02X", (unsigned char)(val >> 8));
3644 AddCodeSegLine (CS, "bne *+4");
3645 AddCodeSegLine (CS, "cmp #$%02X", (unsigned char)val);
3646 AddCodeSegLine (CS, "jsr boolule");
3658 /* If we go here, we didn't emit code. Push the lhs on stack and fall
3659 * into the normal, non-optimized stuff.
3661 g_push (flags & ~CF_CONST, 0);
3665 /* Use long way over the stack */
3666 oper (flags, val, ops);
3671 void g_gt (unsigned flags, unsigned long val)
3672 /* Test for greater than */
3674 static char* ops [12] = {
3675 "tosgt00", "tosgta0", "tosgtax",
3676 "tosugt00", "tosugta0", "tosugtax",
3682 /* If the right hand side is const, the lhs is not on stack but still
3683 * in the primary register.
3685 if (flags & CF_CONST) {
3687 /* Look at the type */
3688 switch (flags & CF_TYPE) {
3691 if (flags & CF_FORCECHAR) {
3692 AddCodeSegLine (CS, "cmp #$%02X", (unsigned char)val);
3693 if (flags & CF_UNSIGNED) {
3694 /* If we have a compare > 0, we will replace it by
3695 * != 0 here, since both are identical but the latter
3696 * is easier to optimize.
3699 AddCodeSegLine (CS, "jsr boolugt");
3701 AddCodeSegLine (CS, "jsr boolne");
3704 AddCodeSegLine (CS, "jsr boolgt");
3711 if (flags & CF_UNSIGNED) {
3712 /* If we have a compare > 0, we will replace it by
3713 * != 0 here, since both are identical but the latter
3714 * is easier to optimize.
3716 if ((val & 0xFFFF) == 0) {
3717 AddCodeSegLine (CS, "stx tmp1");
3718 AddCodeSegLine (CS, "ora tmp1");
3719 AddCodeSegLine (CS, "jsr boolne");
3721 AddCodeSegLine (CS, "cpx #$%02X", (unsigned char)(val >> 8));
3722 AddCodeSegLine (CS, "bne *+4");
3723 AddCodeSegLine (CS, "cmp #$%02X", (unsigned char)val);
3724 AddCodeSegLine (CS, "jsr boolugt");
3737 /* If we go here, we didn't emit code. Push the lhs on stack and fall
3738 * into the normal, non-optimized stuff.
3740 g_push (flags & ~CF_CONST, 0);
3744 /* Use long way over the stack */
3745 oper (flags, val, ops);
3750 void g_ge (unsigned flags, unsigned long val)
3751 /* Test for greater than or equal to */
3753 static char* ops [12] = {
3754 "tosge00", "tosgea0", "tosgeax",
3755 "tosuge00", "tosugea0", "tosugeax",
3761 /* If the right hand side is const, the lhs is not on stack but still
3762 * in the primary register.
3764 if (flags & CF_CONST) {
3766 /* Give a warning in some special cases */
3767 if ((flags & CF_UNSIGNED) && val == 0) {
3768 Warning ("Condition is always true");
3771 /* Look at the type */
3772 switch (flags & CF_TYPE) {
3775 if (flags & CF_FORCECHAR) {
3776 AddCodeSegLine (CS, "cmp #$%02X", (unsigned char)val);
3777 if (flags & CF_UNSIGNED) {
3778 AddCodeSegLine (CS, "jsr booluge");
3780 AddCodeSegLine (CS, "jsr boolge");
3787 if (flags & CF_UNSIGNED) {
3788 AddCodeSegLine (CS, "cpx #$%02X", (unsigned char)(val >> 8));
3789 AddCodeSegLine (CS, "bne *+4");
3790 AddCodeSegLine (CS, "cmp #$%02X", (unsigned char)val);
3791 AddCodeSegLine (CS, "jsr booluge");
3803 /* If we go here, we didn't emit code. Push the lhs on stack and fall
3804 * into the normal, non-optimized stuff.
3806 g_push (flags & ~CF_CONST, 0);
3810 /* Use long way over the stack */
3811 oper (flags, val, ops);
3816 /*****************************************************************************/
3817 /* Allocating static storage */
3818 /*****************************************************************************/
3822 void g_res (unsigned n)
3823 /* Reserve static storage, n bytes */
3825 AddDataSegLine (DS, "\t.res\t%u,$00", n);
3830 void g_defdata (unsigned flags, unsigned long val, unsigned offs)
3831 /* Define data with the size given in flags */
3833 if (flags & CF_CONST) {
3835 /* Numeric constant */
3836 switch (flags & CF_TYPE) {
3839 AddDataSegLine (DS, "\t.byte\t$%02lX", val & 0xFF);
3843 AddDataSegLine (DS, "\t.word\t$%04lX", val & 0xFFFF);
3847 AddDataSegLine (DS, "\t.dword\t$%08lX", val & 0xFFFFFFFF);
3858 /* Create the correct label name */
3859 const char* Label = GetLabelName (flags, val, offs);
3861 /* Labels are always 16 bit */
3862 AddDataSegLine (DS, "\t.word\t%s", Label);
3869 void g_defbytes (const void* Bytes, unsigned Count)
3870 /* Output a row of bytes as a constant */
3876 /* Cast the buffer pointer */
3877 const unsigned char* Data = (const unsigned char*) Bytes;
3879 /* Output the stuff */
3882 /* How many go into this line? */
3883 if ((Chunk = Count) > 16) {
3888 /* Output one line */
3889 strcpy (Buf, "\t.byte\t");
3892 B += sprintf (B, "$%02X", *Data++);
3898 /* Output the line */
3899 AddCodeSegLine (CS, Buf);
3905 void g_zerobytes (unsigned n)
3906 /* Output n bytes of data initialized with zero */
3908 AddDataSegLine (DS, "\t.res\t%u,$00", n);
3913 /*****************************************************************************/
3914 /* User supplied assembler code */
3915 /*****************************************************************************/
3919 void g_asmcode (const char* Line, int Len)
3920 /* Output one line of assembler code. If Len is greater than zero, it is used
3921 * as the maximum number of characters to use from Line.
3925 AddCodeSegLine (CS, "%.*s", Len, Line);
3927 AddCodeSegLine (CS, "%s", Line);
3933 /*****************************************************************************/
3934 /* Inlined known functions */
3935 /*****************************************************************************/
3939 void g_strlen (unsigned flags, unsigned long val, unsigned offs)
3940 /* Inline the strlen() function */
3942 /* We need a label in both cases */
3943 unsigned label = GetLocalLabel ();
3945 /* Two different encodings */
3946 if (flags & CF_CONST) {
3948 /* The address of the string is constant. Create the correct label name */
3949 char* lbuf = GetLabelName (flags, val, offs);
3951 /* Generate the strlen code */
3952 AddCodeSegLine (CS, "ldy #$FF");
3953 g_defloclabel (label);
3954 AddCodeSegLine (CS, "iny");
3955 AddCodeSegLine (CS, "lda %s,y", lbuf);
3956 AddCodeSegLine (CS, "bne L%04X", label);
3957 AddCodeSegLine (CS, "tax");
3958 AddCodeSegLine (CS, "tya");
3962 /* Address not constant but in primary */
3963 if (CodeSizeFactor < 400) {
3964 /* This is too much code, so call strlen instead of inlining */
3965 AddCodeSegLine (CS, "jsr _strlen");
3967 /* Inline the function */
3968 AddCodeSegLine (CS, "sta ptr1");
3969 AddCodeSegLine (CS, "stx ptr1+1");
3970 AddCodeSegLine (CS, "ldy #$FF");
3971 g_defloclabel (label);
3972 AddCodeSegLine (CS, "iny");
3973 AddCodeSegLine (CS, "lda (ptr1),y");
3974 AddCodeSegLine (CS, "bne L%04X", label);
3975 AddCodeSegLine (CS, "tax");
3976 AddCodeSegLine (CS, "tya");