]> git.sur5r.net Git - cc65/blob - src/cc65/codegen.c
898ce17fb5a6f89b8fc65b8e09bc2788055cb2ca
[cc65] / src / cc65 / codegen.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                 codegen.c                                 */
4 /*                                                                           */
5 /*                            6502 code generator                            */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 1998-2001 Ullrich von Bassewitz                                       */
10 /*               Wacholderweg 14                                             */
11 /*               D-70597 Stuttgart                                           */
12 /* EMail:        uz@cc65.org                                                 */
13 /*                                                                           */
14 /*                                                                           */
15 /* This software is provided 'as-is', without any expressed or implied       */
16 /* warranty.  In no event will the authors be held liable for any damages    */
17 /* arising from the use of this software.                                    */
18 /*                                                                           */
19 /* Permission is granted to anyone to use this software for any purpose,     */
20 /* including commercial applications, and to alter it and redistribute it    */
21 /* freely, subject to the following restrictions:                            */
22 /*                                                                           */
23 /* 1. The origin of this software must not be misrepresented; you must not   */
24 /*    claim that you wrote the original software. If you use this software   */
25 /*    in a product, an acknowledgment in the product documentation would be  */
26 /*    appreciated but is not required.                                       */
27 /* 2. Altered source versions must be plainly marked as such, and must not   */
28 /*    be misrepresented as being the original software.                      */
29 /* 3. This notice may not be removed or altered from any source              */
30 /*    distribution.                                                          */
31 /*                                                                           */
32 /*****************************************************************************/
33
34
35
36 #include <stdio.h>
37 #include <string.h>
38 #include <stdarg.h>
39
40 /* common */
41 #include "check.h"
42 #include "version.h"
43 #include "xmalloc.h"
44 #include "xsprintf.h"
45
46 /* cc65 */
47 #include "asmcode.h"
48 #include "asmlabel.h"
49 #include "codeseg.h"
50 #include "cpu.h"
51 #include "dataseg.h"
52 #include "error.h"
53 #include "global.h"
54 #include "segments.h"
55 #include "util.h"
56 #include "codegen.h"
57
58
59
60 /*****************************************************************************/
61 /*                                   Data                                    */
62 /*****************************************************************************/
63
64
65
66 /* Compiler relative stack pointer */
67 int oursp       = 0;
68
69
70
71 /*****************************************************************************/
72 /*                                  Helpers                                  */
73 /*****************************************************************************/
74
75
76
77 static void typeerror (unsigned type)
78 /* Print an error message about an invalid operand type */
79 {
80     Internal ("Invalid type in CF flags: %04X, type = %u", type, type & CF_TYPE);
81 }
82
83
84
85 static void CheckLocalOffs (unsigned Offs)
86 /* Check the offset into the stack for 8bit range */
87 {
88     if (Offs >= 256) {
89         /* Too many local vars */
90         Error ("Too many local variables");
91     }
92 }
93
94
95
96 static char* GetLabelName (unsigned flags, unsigned long label, unsigned offs)
97 {
98     static char lbuf [128];             /* Label name */
99
100     /* Create the correct label name */
101     switch (flags & CF_ADDRMASK) {
102
103         case CF_STATIC:
104             /* Static memory cell */
105             sprintf (lbuf, "%s+%u", LocalLabelName (label), offs);
106             break;
107
108         case CF_EXTERNAL:
109             /* External label */
110             sprintf (lbuf, "_%s+%u", (char*) label, offs);
111             break;
112
113         case CF_ABSOLUTE:
114             /* Absolute address */
115             sprintf (lbuf, "$%04X", (unsigned)((label+offs) & 0xFFFF));
116             break;
117
118         case CF_REGVAR:
119             /* Variable in register bank */
120             sprintf (lbuf, "regbank+%u", (unsigned)((label+offs) & 0xFFFF));
121             break;
122
123         default:
124             Internal ("Invalid address flags");
125     }
126
127     /* Return a pointer to the static buffer */
128     return lbuf;
129 }
130
131
132
133 /*****************************************************************************/
134 /*                            Pre- and postamble                             */
135 /*****************************************************************************/
136
137
138
139 void g_preamble (void)
140 /* Generate the assembler code preamble */
141 {
142     /* Create a new segment list */
143     PushSegments (0);
144
145     /* Identify the compiler version */
146     AddTextLine ("; File generated by cc65 v %u.%u.%u",
147                  VER_MAJOR, VER_MINOR, VER_PATCH);
148
149     /* Insert some object file options */
150     AddTextLine (".fopt\t\tcompiler,\"cc65 v %u.%u.%u\"",
151                     VER_MAJOR, VER_MINOR, VER_PATCH);
152
153     /* If we're producing code for some other CPU, switch the command set */
154     if (CPU == CPU_65C02) {
155         AddTextLine (".pc02");
156     }
157
158     /* Allow auto import for runtime library routines */
159     AddTextLine (".autoimport\ton");
160
161     /* Switch the assembler into case sensitive mode */
162     AddTextLine (".case\t\ton");
163
164     /* Tell the assembler if we want to generate debug info */
165     AddTextLine (".debuginfo\t%s", (DebugInfo != 0)? "on" : "off");
166
167     /* Import the stack pointer for direct auto variable access */
168     AddTextLine (".importzp\tsp, sreg, regsave, regbank, tmp1, ptr1");
169
170     /* Define long branch macros */
171     AddTextLine (".macpack\tlongbranch");
172 }
173
174
175
176 /*****************************************************************************/
177 /*                              Segment support                              */
178 /*****************************************************************************/
179
180
181
182 void g_userodata (void)
183 /* Switch to the read only data segment */
184 {
185     UseDataSeg (SEG_RODATA);
186 }
187
188
189
190 void g_usedata (void)
191 /* Switch to the data segment */
192 {
193     UseDataSeg (SEG_DATA);
194 }
195
196
197
198 void g_usebss (void)
199 /* Switch to the bss segment */
200 {
201     UseDataSeg (SEG_BSS);
202 }
203
204
205
206 /*****************************************************************************/
207 /*                                   Code                                    */
208 /*****************************************************************************/
209
210
211
212 unsigned sizeofarg (unsigned flags)
213 /* Return the size of a function argument type that is encoded in flags */
214 {
215     switch (flags & CF_TYPE) {
216
217         case CF_CHAR:
218             return (flags & CF_FORCECHAR)? 1 : 2;
219
220         case CF_INT:
221             return 2;
222
223         case CF_LONG:
224             return 4;
225
226         default:
227             typeerror (flags);
228             /* NOTREACHED */
229             return 2;
230     }
231 }
232
233
234
235 int pop (unsigned flags)
236 /* Pop an argument of the given size */
237 {
238     return oursp += sizeofarg (flags);
239 }
240
241
242
243 int push (unsigned flags)
244 /* Push an argument of the given size */
245 {
246     return oursp -= sizeofarg (flags);
247 }
248
249
250
251 static unsigned MakeByteOffs (unsigned Flags, unsigned Offs)
252 /* The value in Offs is an offset to an address in a/x. Make sure, an object
253  * of the type given in Flags can be loaded or stored into this address by
254  * adding part of the offset to the address in ax, so that the remaining
255  * offset fits into an index register. Return the remaining offset.
256  */
257 {
258     /* If the offset is too large for a byte register, add the high byte
259      * of the offset to the primary. Beware: We need a special correction
260      * if the offset in the low byte will overflow in the operation.
261      */
262     unsigned O = Offs & ~0xFFU;
263     if ((Offs & 0xFF) > 256 - sizeofarg (Flags)) {
264         /* We need to add the low byte also */
265         O += Offs & 0xFF;
266     }
267
268     /* Do the correction if we need one */
269     if (O != 0) {
270         g_inc (CF_INT | CF_CONST, O);
271         Offs -= O;
272     }
273
274     /* Return the new offset */
275     return Offs;
276 }
277
278
279
280 /*****************************************************************************/
281 /*                      Functions handling local labels                      */
282 /*****************************************************************************/
283
284
285
286 void g_defcodelabel (unsigned label)
287 /* Define a local code label */
288 {
289     AddCodeLabel (CS->Code, LocalLabelName (label));
290 }
291
292
293
294 void g_defdatalabel (unsigned label)
295 /* Define a local data label */
296 {
297     AddDataLine ("%s:", LocalLabelName (label));
298 }
299
300
301
302 /*****************************************************************************/
303 /*                     Functions handling global labels                      */
304 /*****************************************************************************/
305
306
307
308 void g_defgloblabel (const char* Name)
309 /* Define a global label with the given name */
310 {
311     /* Global labels are always data labels */
312     AddDataLine ("_%s:", Name);
313 }
314
315
316
317 void g_defexport (const char* Name, int ZP)
318 /* Export the given label */
319 {
320     if (ZP) {
321         AddTextLine ("\t.exportzp\t_%s", Name);
322     } else {
323         AddTextLine ("\t.export\t\t_%s", Name);
324     }
325 }
326
327
328
329 void g_defimport (const char* Name, int ZP)
330 /* Import the given label */
331 {
332     if (ZP) {
333         AddTextLine ("\t.importzp\t_%s", Name);
334     } else {
335         AddTextLine ("\t.import\t\t_%s", Name);
336     }
337 }
338
339
340
341 /*****************************************************************************/
342 /*                   Load functions for various registers                    */
343 /*****************************************************************************/
344
345
346
347 static void ldaconst (unsigned val)
348 /* Load a with a constant */
349 {
350     AddCodeLine ("lda #$%02X", val & 0xFF);
351 }
352
353
354
355 static void ldxconst (unsigned val)
356 /* Load x with a constant */
357 {
358     AddCodeLine ("ldx #$%02X", val & 0xFF);
359 }
360
361
362
363 static void ldyconst (unsigned val)
364 /* Load y with a constant */
365 {
366     AddCodeLine ("ldy #$%02X", val & 0xFF);
367 }
368
369
370
371 /*****************************************************************************/
372 /*                          Function entry and exit                          */
373 /*****************************************************************************/
374
375
376
377 /* Remember the argument size of a function. The variable is set by g_enter
378  * and used by g_leave. If the functions gets its argument size by the caller
379  * (variable param list or function without prototype), g_enter will set the
380  * value to -1.
381  */
382 static int funcargs;
383
384
385 void g_enter (unsigned flags, unsigned argsize)
386 /* Function prologue */
387 {
388     if ((flags & CF_FIXARGC) != 0) {
389         /* Just remember the argument size for the leave */
390         funcargs = argsize;
391     } else {
392         funcargs = -1;
393         AddCodeLine ("jsr enter");
394     }
395 }
396
397
398
399 void g_leave (void)
400 /* Function epilogue */
401 {
402     /* How many bytes of locals do we have to drop? */
403     int k = -oursp;
404
405     /* If we didn't have a variable argument list, don't call leave */
406     if (funcargs >= 0) {
407
408         /* Drop stackframe if needed */
409         k += funcargs;
410         if (k > 0) {
411             if (k <= 8) {
412             AddCodeLine ("jsr incsp%d", k);
413             } else {
414                 CheckLocalOffs (k);
415                 ldyconst (k);
416                 AddCodeLine ("jsr addysp");
417             }
418         }
419
420     } else {
421
422         if (k == 0) {
423             /* Nothing to drop */
424             AddCodeLine ("jsr leave");
425         } else {
426             /* We've a stack frame to drop */
427             ldyconst (k);
428             AddCodeLine ("jsr leavey");
429         }
430     }
431
432     /* Add the final rts */
433     AddCodeLine ("rts");
434 }
435
436
437
438 /*****************************************************************************/
439 /*                            Register variables                             */
440 /*****************************************************************************/
441
442
443
444 void g_save_regvars (int RegOffs, unsigned Bytes)
445 /* Save register variables */
446 {
447     /* Don't loop for up to two bytes */
448     if (Bytes == 1) {
449
450         AddCodeLine ("lda regbank%+d", RegOffs);
451         AddCodeLine ("jsr pusha");
452
453     } else if (Bytes == 2) {
454
455         AddCodeLine ("lda regbank%+d", RegOffs);
456         AddCodeLine ("ldx regbank%+d", RegOffs+1);
457         AddCodeLine ("jsr pushax");
458
459     } else {
460
461         /* More than two bytes - loop */
462         unsigned Label = GetLocalLabel ();
463         g_space (Bytes);
464         ldyconst (Bytes - 1);
465         ldxconst (Bytes);
466         g_defcodelabel (Label);
467         AddCodeLine ("lda regbank%+d,x", RegOffs-1);
468         AddCodeLine ("sta (sp),y");
469         AddCodeLine ("dey");
470         AddCodeLine ("dex");
471         AddCodeLine ("bne %s", LocalLabelName (Label));
472
473     }
474
475     /* We pushed stuff, correct the stack pointer */
476     oursp -= Bytes;
477 }
478
479
480
481 void g_restore_regvars (int StackOffs, int RegOffs, unsigned Bytes)
482 /* Restore register variables */
483 {
484     /* Calculate the actual stack offset and check it */
485     StackOffs -= oursp;
486     CheckLocalOffs (StackOffs);
487
488     /* Don't loop for up to two bytes */
489     if (Bytes == 1) {
490
491         ldyconst (StackOffs);
492         AddCodeLine ("lda (sp),y");
493         AddCodeLine ("sta regbank%+d", RegOffs);
494
495     } else if (Bytes == 2) {
496
497         ldyconst (StackOffs);
498         AddCodeLine ("lda (sp),y");
499         AddCodeLine ("sta regbank%+d", RegOffs);
500         AddCodeLine ("iny");
501         AddCodeLine ("lda (sp),y");
502         AddCodeLine ("sta regbank%+d", RegOffs+1);
503
504     } else {
505
506         /* More than two bytes - loop */
507         unsigned Label = GetLocalLabel ();
508         ldyconst (StackOffs+Bytes-1);
509         ldxconst (Bytes);
510         g_defcodelabel (Label);
511         AddCodeLine ("lda (sp),y");
512         AddCodeLine ("sta regbank%+d,x", RegOffs-1);
513         AddCodeLine ("dey");
514         AddCodeLine ("dex");
515         AddCodeLine ("bne %s", LocalLabelName (Label));
516
517     }
518 }
519
520
521
522 /*****************************************************************************/
523 /*                           Fetching memory cells                           */
524 /*****************************************************************************/
525
526
527
528 void g_getimmed (unsigned flags, unsigned long val, unsigned offs)
529 /* Load a constant into the primary register */
530 {
531     if ((flags & CF_CONST) != 0) {
532
533         /* Numeric constant */
534         switch (flags & CF_TYPE) {
535
536             case CF_CHAR:
537                 if ((flags & CF_FORCECHAR) != 0) {
538                     ldaconst (val);
539                     break;
540                 }
541                 /* FALL THROUGH */
542             case CF_INT:
543                 ldxconst ((val >> 8) & 0xFF);
544                 ldaconst (val & 0xFF);
545                 break;
546
547             case CF_LONG:
548                 if (val < 0x100) {
549                     AddCodeLine ("ldx #$00");
550                     AddCodeLine ("stx sreg+1");
551                     AddCodeLine ("stx sreg");
552                     AddCodeLine ("lda #$%02X", (unsigned char) val);
553                 } else if ((val & 0xFFFF00FF) == 0) {
554                     AddCodeLine ("lda #$00");
555                     AddCodeLine ("sta sreg+1");
556                     AddCodeLine ("sta sreg");
557                     AddCodeLine ("ldx #$%02X", (unsigned char) (val >> 8));
558                 } else if ((val & 0xFFFF0000) == 0 && CodeSizeFactor > 140) {
559                     AddCodeLine ("lda #$00");
560                     AddCodeLine ("sta sreg+1");
561                     AddCodeLine ("sta sreg");
562                     AddCodeLine ("lda #$%02X", (unsigned char) val);
563                     AddCodeLine ("ldx #$%02X", (unsigned char) (val >> 8));
564                 } else if ((val & 0xFFFFFF00) == 0xFFFFFF00) {
565                     AddCodeLine ("ldx #$FF");
566                     AddCodeLine ("stx sreg+1");
567                     AddCodeLine ("stx sreg");
568                     if ((val & 0xFF) == 0xFF) {
569                         AddCodeLine ("txa");
570                     } else {
571                         AddCodeLine ("lda #$%02X", (unsigned char) val);
572                     }
573                 } else if ((val & 0xFFFF00FF) == 0xFFFF00FF) {
574                     AddCodeLine ("lda #$FF");
575                     AddCodeLine ("sta sreg+1");
576                     AddCodeLine ("sta sreg");
577                     AddCodeLine ("ldx #$%02X", (unsigned char) (val >> 8));
578                 } else {
579                     /* Call a subroutine that will load following value */
580                     AddCodeLine ("jsr ldeax");
581                     AddCodeLine (".dword $%08lX", val & 0xFFFFFFFF);
582                 }
583                 break;
584
585             default:
586                 typeerror (flags);
587                 break;
588
589         }
590
591     } else {
592
593         /* Some sort of label */
594         const char* Label = GetLabelName (flags, val, offs);
595
596         /* Load the address into the primary */
597         AddCodeLine ("lda #<(%s)", Label);
598         AddCodeLine ("ldx #>(%s)", Label);
599
600     }
601 }
602
603
604
605 void g_getstatic (unsigned flags, unsigned long label, unsigned offs)
606 /* Fetch an static memory cell into the primary register */
607 {
608     /* Create the correct label name */
609     char* lbuf = GetLabelName (flags, label, offs);
610
611     /* Check the size and generate the correct load operation */
612     switch (flags & CF_TYPE) {
613
614         case CF_CHAR:
615             if ((flags & CF_FORCECHAR) || (flags & CF_TEST)) {
616                 AddCodeLine ("lda %s", lbuf);   /* load A from the label */
617             } else {
618                 ldxconst (0);
619                 AddCodeLine ("lda %s", lbuf);   /* load A from the label */
620                 if (!(flags & CF_UNSIGNED)) {
621                     /* Must sign extend */
622                     AddCodeLine ("bpl *+3");
623                     AddCodeLine ("dex");
624                     AddCodeHint ("x:!");                /* X is invalid now */
625                 }
626             }
627             break;
628
629         case CF_INT:
630             AddCodeLine ("lda %s", lbuf);
631             if (flags & CF_TEST) {
632                 AddCodeLine ("ora %s+1", lbuf);
633             } else {
634                 AddCodeLine ("ldx %s+1", lbuf);
635             }
636             break;
637
638         case CF_LONG:
639             if (flags & CF_TEST) {
640                 AddCodeLine ("lda %s+3", lbuf);
641                 AddCodeLine ("ora %s+2", lbuf);
642                 AddCodeLine ("ora %s+1", lbuf);
643                 AddCodeLine ("ora %s+0", lbuf);
644             } else {
645                 AddCodeLine ("lda %s+3", lbuf);
646                 AddCodeLine ("sta sreg+1");
647                 AddCodeLine ("lda %s+2", lbuf);
648                 AddCodeLine ("sta sreg");
649                 AddCodeLine ("ldx %s+1", lbuf);
650                 AddCodeLine ("lda %s", lbuf);
651             }
652             break;
653
654         default:
655             typeerror (flags);
656
657     }
658 }
659
660
661
662 void g_getlocal (unsigned flags, int offs)
663 /* Fetch specified local object (local var). */
664 {
665     offs -= oursp;
666     CheckLocalOffs (offs);
667     switch (flags & CF_TYPE) {
668
669         case CF_CHAR:
670             if ((flags & CF_FORCECHAR) || (flags & CF_TEST)) {
671                 if (CPU == CPU_65C02 && offs == 0) {
672                     AddCodeLine ("lda (sp)");
673                 } else {
674                     ldyconst (offs);
675                     AddCodeLine ("lda (sp),y");
676                 }
677             } else {
678                 if (offs == 0) {
679                     AddCodeLine ("ldx #$00");
680                     AddCodeLine ("lda (sp,x)");
681                 } else {
682                     ldyconst (offs);
683                     AddCodeLine ("ldx #$00");
684                     AddCodeLine ("lda (sp),y");
685                 }
686                 if ((flags & CF_UNSIGNED) == 0) {
687                     AddCodeLine ("bpl *+3");
688                     AddCodeLine ("dex");
689                     AddCodeHint ("x:!");        /* X is invalid now */
690                 }
691             }
692             break;
693
694         case CF_INT:
695             CheckLocalOffs (offs + 1);
696             if (flags & CF_TEST) {
697                 ldyconst (offs + 1);
698                 AddCodeLine ("lda (sp),y");
699                 AddCodeLine ("dey");
700                 AddCodeLine ("ora (sp),y");
701             } else {
702                 if (CodeSizeFactor > 180) {
703                     ldyconst (offs + 1);
704                     AddCodeLine ("lda (sp),y");
705                     AddCodeLine ("tax");
706                     AddCodeLine ("dey");
707                     AddCodeLine ("lda (sp),y");
708                 } else {
709                     if (offs) {
710                         ldyconst (offs+1);
711                         AddCodeLine ("jsr ldaxysp");
712                     } else {
713                         AddCodeLine ("jsr ldax0sp");
714                     }
715                 }
716             }
717             break;
718
719         case CF_LONG:
720             if (offs) {
721                 ldyconst (offs+3);
722                 AddCodeLine ("jsr ldeaxysp");
723             } else {
724                 AddCodeLine ("jsr ldeax0sp");
725             }
726             break;
727
728         default:
729             typeerror (flags);
730     }
731 }
732
733
734
735 void g_getind (unsigned flags, unsigned offs)
736 /* Fetch the specified object type indirect through the primary register
737  * into the primary register
738  */
739 {
740     /* If the offset is greater than 255, add the part that is > 255 to
741      * the primary. This way we get an easy addition and use the low byte
742      * as the offset
743      */
744     offs = MakeByteOffs (flags, offs);
745
746     /* Handle the indirect fetch */
747     switch (flags & CF_TYPE) {
748
749         case CF_CHAR:
750             /* Character sized */
751             if (offs) {
752                 ldyconst (offs);
753                 if (flags & CF_UNSIGNED) {
754                     AddCodeLine ("jsr ldauidx");
755                 } else {
756                     AddCodeLine ("jsr ldaidx");
757                 }
758             } else {
759                 if (flags & CF_UNSIGNED) {
760                     if (CodeSizeFactor > 250) {
761                         AddCodeLine ("sta ptr1");
762                         AddCodeLine ("stx ptr1+1");
763                         AddCodeLine ("ldx #$00");
764                         AddCodeLine ("lda (ptr1,x)");
765                     } else {
766                         AddCodeLine ("jsr ldaui");
767                     }
768                 } else {
769                     AddCodeLine ("jsr ldai");
770                 }
771             }
772             break;
773
774         case CF_INT:
775             if (flags & CF_TEST) {
776                 ldyconst (offs);
777                 AddCodeLine ("sta ptr1");
778                 AddCodeLine ("stx ptr1+1");
779                 AddCodeLine ("lda (ptr1),y");
780                 AddCodeLine ("iny");
781                 AddCodeLine ("ora (ptr1),y");
782             } else {
783                 if (offs == 0) {
784                     AddCodeLine ("jsr ldaxi");
785                 } else {
786                     ldyconst (offs+1);
787                     AddCodeLine ("jsr ldaxidx");
788                 }
789             }
790             break;
791
792         case CF_LONG:
793             if (offs == 0) {
794                 AddCodeLine ("jsr ldeaxi");
795             } else {
796                 ldyconst (offs+3);
797                 AddCodeLine ("jsr ldeaxidx");
798             }
799             if (flags & CF_TEST) {
800                 AddCodeLine ("jsr tsteax");
801             }
802             break;
803
804         default:
805             typeerror (flags);
806
807     }
808 }
809
810
811
812 void g_leasp (int offs)
813 /* Fetch the address of the specified symbol into the primary register */
814 {
815     /* Calculate the offset relative to sp */
816     offs -= oursp;
817
818     /* For value 0 we do direct code */
819     if (offs == 0) {
820         AddCodeLine ("lda sp");
821         AddCodeLine ("ldx sp+1");
822     } else {
823         if (CodeSizeFactor < 300) {
824             ldaconst (offs);                    /* Load A with offset value */
825             AddCodeLine ("jsr leaasp"); /* Load effective address */
826         } else {
827             if (CPU == CPU_65C02 && offs == 1) {
828                 AddCodeLine ("lda sp");
829                 AddCodeLine ("ldx sp+1");
830                 AddCodeLine ("ina");
831                 AddCodeLine ("bne *+3");
832                 AddCodeLine ("inx");
833                 AddCodeHint ("x:!");            /* Invalidate X */
834             } else {
835                 ldaconst (offs);
836                 AddCodeLine ("clc");
837                 AddCodeLine ("ldx sp+1");
838                 AddCodeLine ("adc sp");
839                 AddCodeLine ("bcc *+3");
840                 AddCodeLine ("inx");
841                 AddCodeHint ("x:!");            /* Invalidate X */
842             }
843         }
844     }
845 }
846
847
848
849 void g_leavariadic (int Offs)
850 /* Fetch the address of a parameter in a variadic function into the primary
851  * register
852  */
853 {
854     unsigned ArgSizeOffs;
855
856     /* Calculate the offset relative to sp */
857     Offs -= oursp;
858
859     /* Get the offset of the parameter which is stored at sp+0 on function
860      * entry and check if this offset is reachable with a byte offset.
861      */
862     CHECK (oursp <= 0);
863     ArgSizeOffs = -oursp;
864     CheckLocalOffs (ArgSizeOffs);
865
866     /* Get the size of all parameters. */
867     if (ArgSizeOffs == 0 && CPU == CPU_65C02) {
868         AddCodeLine ("lda (sp)");
869     } else {
870         ldyconst (ArgSizeOffs);
871         AddCodeLine ("lda (sp),y");
872     }
873
874     /* Add the value of the stackpointer */
875     if (CodeSizeFactor > 250) {
876         AddCodeLine ("ldx sp+1");
877         AddCodeLine ("clc");
878         AddCodeLine ("adc sp");
879         AddCodeLine ("bcc *+3");
880         AddCodeLine ("inx");
881         AddCodeHint ("x:!");            /* Invalidate X */
882     } else {
883         AddCodeLine ("jsr leaasp");
884     }
885
886     /* Add the offset to the primary */
887     if (Offs > 0) {
888         g_inc (CF_INT | CF_CONST, Offs);
889     } else if (Offs < 0) {
890         g_dec (CF_INT | CF_CONST, -Offs);
891     }
892 }
893
894
895
896 /*****************************************************************************/
897 /*                             Store into memory                             */
898 /*****************************************************************************/
899
900
901
902 void g_putstatic (unsigned flags, unsigned long label, unsigned offs)
903 /* Store the primary register into the specified static memory cell */
904 {
905     /* Create the correct label name */
906     char* lbuf = GetLabelName (flags, label, offs);
907
908     /* Check the size and generate the correct store operation */
909     switch (flags & CF_TYPE) {
910
911         case CF_CHAR:
912             AddCodeLine ("sta %s", lbuf);
913             break;
914
915         case CF_INT:
916             AddCodeLine ("sta %s", lbuf);
917             AddCodeLine ("stx %s+1", lbuf);
918             break;
919
920         case CF_LONG:
921             AddCodeLine ("sta %s", lbuf);
922             AddCodeLine ("stx %s+1", lbuf);
923             AddCodeLine ("ldy sreg");
924             AddCodeLine ("sty %s+2", lbuf);
925             AddCodeLine ("ldy sreg+1");
926             AddCodeLine ("sty %s+3", lbuf);
927             break;
928
929         default:
930             typeerror (flags);
931
932     }
933 }
934
935
936
937 void g_putlocal (unsigned Flags, int Offs, long Val)
938 /* Put data into local object. */
939 {
940     Offs -= oursp;
941     CheckLocalOffs (Offs);
942     switch (Flags & CF_TYPE) {
943
944         case CF_CHAR:
945             if (Flags & CF_CONST) {
946                 AddCodeLine ("lda #$%02X", (unsigned char) Val);
947             }
948             if (CPU == CPU_65C02 && Offs == 0) {
949                 AddCodeLine ("sta (sp)");
950             } else {
951                 ldyconst (Offs);
952                 AddCodeLine ("sta (sp),y");
953             }
954             break;
955
956         case CF_INT:
957             if (Flags & CF_CONST) {
958                 ldyconst (Offs+1);
959                 AddCodeLine ("lda #$%02X", (unsigned char) (Val >> 8));
960                 AddCodeLine ("sta (sp),y");
961                 if ((Flags & CF_NOKEEP) == 0) {
962                     /* Place high byte into X */
963                     AddCodeLine ("tax");
964                 }
965                 if (CPU == CPU_65C02 && Offs == 0) {
966                     AddCodeLine ("lda #$%02X", (unsigned char) Val);
967                     AddCodeLine ("sta (sp)");
968                 } else {
969                     if ((Val & 0xFF) == Offs+1) {
970                         /* The value we need is already in Y */
971                         AddCodeLine ("tya");
972                         AddCodeLine ("dey");
973                     } else {
974                         AddCodeLine ("dey");
975                         AddCodeLine ("lda #$%02X", (unsigned char) Val);
976                     }
977                     AddCodeLine ("sta (sp),y");
978                 }
979             } else {
980                 if ((Flags & CF_NOKEEP) == 0 || CodeSizeFactor < 160) {
981                     if (Offs) {
982                         ldyconst (Offs);
983                         AddCodeLine ("jsr staxysp");
984                     } else {
985                         AddCodeLine ("jsr stax0sp");
986                     }
987                 } else {
988                     if (CPU == CPU_65C02 && Offs == 0) {
989                         AddCodeLine ("sta (sp)");
990                         ldyconst (1);
991                         AddCodeLine ("txa");
992                         AddCodeLine ("sta (sp),y");
993                     } else {
994                         ldyconst (Offs);
995                         AddCodeLine ("sta (sp),y");
996                         AddCodeLine ("iny");
997                         AddCodeLine ("txa");
998                         AddCodeLine ("sta (sp),y");
999                     }
1000                 }
1001             }
1002             break;
1003
1004         case CF_LONG:
1005             if (Flags & CF_CONST) {
1006                 g_getimmed (Flags, Val, 0);
1007             }
1008             if (Offs) {
1009                 ldyconst (Offs);
1010                 AddCodeLine ("jsr steaxysp");
1011             } else {
1012                 AddCodeLine ("jsr steax0sp");
1013             }
1014             break;
1015
1016         default:
1017             typeerror (Flags);
1018
1019     }
1020 }
1021
1022
1023
1024 void g_putind (unsigned Flags, unsigned Offs)
1025 /* Store the specified object type in the primary register at the address
1026  * on the top of the stack
1027  */
1028 {
1029     /* We can handle offsets below $100 directly, larger offsets must be added
1030      * to the address. Since a/x is in use, best code is achieved by adding
1031      * just the high byte. Be sure to check if the low byte will overflow while
1032      * while storing.
1033      */
1034     if ((Offs & 0xFF) > 256 - sizeofarg (Flags | CF_FORCECHAR)) {
1035
1036         /* Overflow - we need to add the low byte also */
1037         AddCodeLine ("ldy #$00");
1038         AddCodeLine ("clc");
1039         AddCodeLine ("pha");
1040         AddCodeLine ("lda #$%02X", Offs & 0xFF);
1041         AddCodeLine ("adc (sp),y");
1042         AddCodeLine ("sta (sp),y");
1043         AddCodeLine ("iny");
1044         AddCodeLine ("lda #$%02X", (Offs >> 8) & 0xFF);
1045         AddCodeLine ("adc (sp),y");
1046         AddCodeLine ("sta (sp),y");
1047         AddCodeLine ("pla");
1048
1049         /* Complete address is on stack, new offset is zero */
1050         Offs = 0;
1051
1052     } else if ((Offs & 0xFF00) != 0) {
1053
1054         /* We can just add the high byte */
1055         AddCodeLine ("ldy #$01");
1056         AddCodeLine ("clc");
1057         AddCodeLine ("pha");
1058         AddCodeLine ("lda #$%02X", (Offs >> 8) & 0xFF);
1059         AddCodeLine ("adc (sp),y");
1060         AddCodeLine ("sta (sp),y");
1061         AddCodeLine ("pla");
1062
1063         /* Offset is now just the low byte */
1064         Offs &= 0x00FF;
1065     }
1066
1067     /* Check the size and determine operation */
1068     switch (Flags & CF_TYPE) {
1069
1070         case CF_CHAR:
1071             if (Offs) {
1072                 ldyconst (Offs);
1073                 AddCodeLine ("jsr staspidx");
1074             } else {
1075                 AddCodeLine ("jsr staspp");
1076             }
1077             break;
1078
1079         case CF_INT:
1080             if (Offs) {
1081                 ldyconst (Offs);
1082                 AddCodeLine ("jsr staxspidx");
1083             } else {
1084                 AddCodeLine ("jsr staxspp");
1085             }
1086             break;
1087
1088         case CF_LONG:
1089             if (Offs) {
1090                 ldyconst (Offs);
1091                 AddCodeLine ("jsr steaxspidx");
1092             } else {
1093                 AddCodeLine ("jsr steaxspp");
1094             }
1095             break;
1096
1097         default:
1098             typeerror (Flags);
1099
1100     }
1101
1102     /* Pop the argument which is always a pointer */
1103     pop (CF_PTR);
1104 }
1105
1106
1107
1108 /*****************************************************************************/
1109 /*                    type conversion and similiar stuff                     */
1110 /*****************************************************************************/
1111
1112
1113
1114 void g_toslong (unsigned flags)
1115 /* Make sure, the value on TOS is a long. Convert if necessary */
1116 {
1117     switch (flags & CF_TYPE) {
1118
1119         case CF_CHAR:
1120         case CF_INT:
1121             if (flags & CF_UNSIGNED) {
1122                 AddCodeLine ("jsr tosulong");
1123             } else {
1124                 AddCodeLine ("jsr toslong");
1125             }
1126             push (CF_INT);
1127             break;
1128
1129         case CF_LONG:
1130             break;
1131
1132         default:
1133             typeerror (flags);
1134     }
1135 }
1136
1137
1138
1139 void g_tosint (unsigned flags)
1140 /* Make sure, the value on TOS is an int. Convert if necessary */
1141 {
1142     switch (flags & CF_TYPE) {
1143
1144         case CF_CHAR:
1145         case CF_INT:
1146             break;
1147
1148         case CF_LONG:
1149             AddCodeLine ("jsr tosint");
1150             pop (CF_INT);
1151             break;
1152
1153         default:
1154             typeerror (flags);
1155     }
1156 }
1157
1158
1159
1160 void g_reglong (unsigned flags)
1161 /* Make sure, the value in the primary register a long. Convert if necessary */
1162 {
1163     switch (flags & CF_TYPE) {
1164
1165         case CF_CHAR:
1166         case CF_INT:
1167             if (flags & CF_UNSIGNED) {
1168                 if (CodeSizeFactor >= 200) {
1169                     ldyconst (0);
1170                     AddCodeLine ("sty sreg");
1171                     AddCodeLine ("sty sreg+1");
1172                 } else {
1173                     AddCodeLine ("jsr axulong");
1174                 }
1175             } else {
1176                 AddCodeLine ("jsr axlong");
1177             }
1178             break;
1179
1180         case CF_LONG:
1181             break;
1182
1183         default:
1184             typeerror (flags);
1185     }
1186 }
1187
1188
1189
1190 unsigned g_typeadjust (unsigned lhs, unsigned rhs)
1191 /* Adjust the integer operands before doing a binary operation. lhs is a flags
1192  * value, that corresponds to the value on TOS, rhs corresponds to the value
1193  * in (e)ax. The return value is the the flags value for the resulting type.
1194  */
1195 {
1196     unsigned ltype, rtype;
1197     unsigned result;
1198
1199     /* Get the type spec from the flags */
1200     ltype = lhs & CF_TYPE;
1201     rtype = rhs & CF_TYPE;
1202
1203     /* Check if a conversion is needed */
1204     if (ltype == CF_LONG && rtype != CF_LONG && (rhs & CF_CONST) == 0) {
1205         /* We must promote the primary register to long */
1206         g_reglong (rhs);
1207         /* Get the new rhs type */
1208         rhs = (rhs & ~CF_TYPE) | CF_LONG;
1209         rtype = CF_LONG;
1210     } else if (ltype != CF_LONG && (lhs & CF_CONST) == 0 && rtype == CF_LONG) {
1211         /* We must promote the lhs to long */
1212         if (lhs & CF_REG) {
1213             g_reglong (lhs);
1214         } else {
1215             g_toslong (lhs);
1216         }
1217         /* Get the new rhs type */
1218         lhs = (lhs & ~CF_TYPE) | CF_LONG;
1219         ltype = CF_LONG;
1220     }
1221
1222     /* Determine the result type for the operation:
1223      *  - The result is const if both operands are const.
1224      *  - The result is unsigned if one of the operands is unsigned.
1225      *  - The result is long if one of the operands is long.
1226      *  - Otherwise the result is int sized.
1227      */
1228     result = (lhs & CF_CONST) & (rhs & CF_CONST);
1229     result |= (lhs & CF_UNSIGNED) | (rhs & CF_UNSIGNED);
1230     if (rtype == CF_LONG || ltype == CF_LONG) {
1231         result |= CF_LONG;
1232     } else {
1233         result |= CF_INT;
1234     }
1235     return result;
1236 }
1237
1238
1239
1240 unsigned g_typecast (unsigned lhs, unsigned rhs)
1241 /* Cast the value in the primary register to the operand size that is flagged
1242  * by the lhs value. Return the result value.
1243  */
1244 {
1245     unsigned ltype, rtype;
1246
1247     /* Get the type spec from the flags */
1248     ltype = lhs & CF_TYPE;
1249     rtype = rhs & CF_TYPE;
1250
1251     /* Check if a conversion is needed */
1252     if (ltype == CF_LONG && rtype != CF_LONG && (rhs & CF_CONST) == 0) {
1253         /* We must promote the primary register to long */
1254         g_reglong (rhs);
1255     }
1256
1257     /* Do not need any other action. If the left type is int, and the primary
1258      * register is long, it will be automagically truncated. If the right hand
1259      * side is const, it is not located in the primary register and handled by
1260      * the expression parser code.
1261      */
1262
1263     /* Result is const if the right hand side was const */
1264     lhs |= (rhs & CF_CONST);
1265
1266     /* The resulting type is that of the left hand side (that's why you called
1267      * this function :-)
1268      */
1269     return lhs;
1270 }
1271
1272
1273
1274 void g_scale (unsigned flags, long val)
1275 /* Scale the value in the primary register by the given value. If val is positive,
1276  * scale up, is val is negative, scale down. This function is used to scale
1277  * the operands or results of pointer arithmetic by the size of the type, the
1278  * pointer points to.
1279  */
1280 {
1281     int p2;
1282
1283     /* Value may not be zero */
1284     if (val == 0) {
1285         Internal ("Data type has no size");
1286     } else if (val > 0) {
1287
1288         /* Scale up */
1289         if ((p2 = powerof2 (val)) > 0 && p2 <= 3) {
1290
1291             /* Factor is 2, 4 or 8, use special function */
1292             switch (flags & CF_TYPE) {
1293
1294                 case CF_CHAR:
1295                     if (flags & CF_FORCECHAR) {
1296                         while (p2--) {
1297                             AddCodeLine ("asl a");
1298                         }
1299                         break;
1300                     }
1301                     /* FALLTHROUGH */
1302
1303                 case CF_INT:
1304                     if (CodeSizeFactor >= (p2+1)*130U) {
1305                         AddCodeLine ("stx tmp1");
1306                         while (p2--) {
1307                             AddCodeLine ("asl a");
1308                             AddCodeLine ("rol tmp1");
1309                         }
1310                         AddCodeLine ("ldx tmp1");
1311                     } else {
1312                         if (flags & CF_UNSIGNED) {
1313                             AddCodeLine ("jsr shlax%d", p2);
1314                         } else {
1315                             AddCodeLine ("jsr aslax%d", p2);
1316                         }
1317                     }
1318                     break;
1319
1320                 case CF_LONG:
1321                     if (flags & CF_UNSIGNED) {
1322                         AddCodeLine ("jsr shleax%d", p2);
1323                     } else {
1324                         AddCodeLine ("jsr asleax%d", p2);
1325                     }
1326                     break;
1327
1328                 default:
1329                     typeerror (flags);
1330
1331             }
1332
1333         } else if (val != 1) {
1334
1335             /* Use a multiplication instead */
1336             g_mul (flags | CF_CONST, val);
1337
1338         }
1339
1340     } else {
1341
1342         /* Scale down */
1343         val = -val;
1344         if ((p2 = powerof2 (val)) > 0 && p2 <= 3) {
1345
1346             /* Factor is 2, 4 or 8, use special function */
1347             switch (flags & CF_TYPE) {
1348
1349                 case CF_CHAR:
1350                     if (flags & CF_FORCECHAR) {
1351                         if (flags & CF_UNSIGNED) {
1352                             while (p2--) {
1353                                 AddCodeLine ("lsr a");
1354                             }
1355                             break;
1356                         } else if (p2 <= 2) {
1357                             AddCodeLine ("cmp #$80");
1358                             AddCodeLine ("ror a");
1359                             break;
1360                         }
1361                     }
1362                     /* FALLTHROUGH */
1363
1364                 case CF_INT:
1365                     if (flags & CF_UNSIGNED) {
1366                         if (CodeSizeFactor >= (p2+1)*130U) {
1367                             AddCodeLine ("stx tmp1");
1368                             while (p2--) {
1369                                 AddCodeLine ("lsr tmp1");
1370                                 AddCodeLine ("ror a");
1371                             }
1372                             AddCodeLine ("ldx tmp1");
1373                         } else {
1374                             AddCodeLine ("jsr lsrax%d", p2);
1375                         }
1376                     } else {
1377                         if (CodeSizeFactor >= (p2+1)*150U) {
1378                             AddCodeLine ("stx tmp1");
1379                             while (p2--) {
1380                                 AddCodeLine ("cpx #$80");
1381                                 AddCodeLine ("ror tmp1");
1382                                 AddCodeLine ("ror a");
1383                             }
1384                             AddCodeLine ("ldx tmp1");
1385                         } else {
1386                             AddCodeLine ("jsr asrax%d", p2);
1387                         }
1388                     }
1389                     break;
1390
1391                 case CF_LONG:
1392                     if (flags & CF_UNSIGNED) {
1393                         AddCodeLine ("jsr lsreax%d", p2);
1394                     } else {
1395                         AddCodeLine ("jsr asreax%d", p2);
1396                     }
1397                     break;
1398
1399                 default:
1400                     typeerror (flags);
1401
1402             }
1403
1404         } else if (val != 1) {
1405
1406             /* Use a division instead */
1407             g_div (flags | CF_CONST, val);
1408
1409         }
1410     }
1411 }
1412
1413
1414
1415 /*****************************************************************************/
1416 /*              Adds and subs of variables fix a fixed address               */
1417 /*****************************************************************************/
1418
1419
1420
1421 void g_addlocal (unsigned flags, int offs)
1422 /* Add a local variable to ax */
1423 {
1424     /* Correct the offset and check it */
1425     offs -= oursp;
1426     CheckLocalOffs (offs);
1427
1428     switch (flags & CF_TYPE) {
1429
1430         case CF_CHAR:
1431             AddCodeLine ("ldy #$%02X", offs & 0xFF);
1432             AddCodeLine ("clc");
1433             AddCodeLine ("adc (sp),y");
1434             AddCodeLine ("bcc *+3");
1435             AddCodeLine ("inx");
1436             AddCodeHint ("x:!");
1437             break;
1438
1439         case CF_INT:
1440             AddCodeLine ("ldy #$%02X", offs & 0xFF);
1441             AddCodeLine ("clc");
1442             AddCodeLine ("adc (sp),y");
1443             AddCodeLine ("pha");
1444             AddCodeLine ("txa");
1445             AddCodeLine ("iny");
1446             AddCodeLine ("adc (sp),y");
1447             AddCodeLine ("tax");
1448             AddCodeLine ("pla");
1449             break;
1450
1451         case CF_LONG:
1452             /* Do it the old way */
1453             g_push (flags, 0);
1454             g_getlocal (flags, offs);
1455             g_add (flags, 0);
1456             break;
1457
1458         default:
1459             typeerror (flags);
1460
1461     }
1462 }
1463
1464
1465
1466 void g_addstatic (unsigned flags, unsigned long label, unsigned offs)
1467 /* Add a static variable to ax */
1468 {
1469     /* Create the correct label name */
1470     char* lbuf = GetLabelName (flags, label, offs);
1471
1472     switch (flags & CF_TYPE) {
1473
1474         case CF_CHAR:
1475             AddCodeLine ("clc");
1476             AddCodeLine ("adc %s", lbuf);
1477             AddCodeLine ("bcc *+3");
1478             AddCodeLine ("inx");
1479             AddCodeHint ("x:!");
1480             break;
1481
1482         case CF_INT:
1483             AddCodeLine ("clc");
1484             AddCodeLine ("adc %s", lbuf);
1485             AddCodeLine ("tay");
1486             AddCodeLine ("txa");
1487             AddCodeLine ("adc %s+1", lbuf);
1488             AddCodeLine ("tax");
1489             AddCodeLine ("tya");
1490             break;
1491
1492         case CF_LONG:
1493             /* Do it the old way */
1494             g_push (flags, 0);
1495             g_getstatic (flags, label, offs);
1496             g_add (flags, 0);
1497             break;
1498
1499         default:
1500             typeerror (flags);
1501
1502     }
1503 }
1504
1505
1506
1507 /*****************************************************************************/
1508 /*             Compares of ax with a variable with fixed address             */
1509 /*****************************************************************************/
1510
1511
1512
1513 void g_cmplocal (unsigned flags, int offs)
1514 /* Compare a local variable to ax */
1515 {
1516     Internal ("g_cmplocal not implemented");
1517 }
1518
1519
1520
1521 void g_cmpstatic (unsigned flags, unsigned label, unsigned offs)
1522 /* Compare a static variable to ax */
1523 {
1524     Internal ("g_cmpstatic not implemented");
1525 }
1526
1527
1528
1529 /*****************************************************************************/
1530 /*                           Special op= functions                           */
1531 /*****************************************************************************/
1532
1533
1534
1535 void g_addeqstatic (unsigned flags, unsigned long label, unsigned offs,
1536                     unsigned long val)
1537 /* Emit += for a static variable */
1538 {
1539     /* Create the correct label name */
1540     char* lbuf = GetLabelName (flags, label, offs);
1541
1542     /* Check the size and determine operation */
1543     switch (flags & CF_TYPE) {
1544
1545         case CF_CHAR:
1546             if (flags & CF_FORCECHAR) {
1547                 AddCodeLine ("ldx #$00");
1548                 if (flags & CF_CONST) {
1549                     if (val == 1) {
1550                         AddCodeLine ("inc %s", lbuf);
1551                         AddCodeLine ("lda %s", lbuf);
1552                     } else {
1553                         AddCodeLine ("lda #$%02X", (int)(val & 0xFF));
1554                         AddCodeLine ("clc");
1555                         AddCodeLine ("adc %s", lbuf);
1556                         AddCodeLine ("sta %s", lbuf);
1557                     }
1558                 } else {
1559                     AddCodeLine ("clc");
1560                     AddCodeLine ("adc %s", lbuf);
1561                     AddCodeLine ("sta %s", lbuf);
1562                 }
1563                 if ((flags & CF_UNSIGNED) == 0) {
1564                     AddCodeLine ("bpl *+3");
1565                     AddCodeLine ("dex");
1566                     AddCodeHint ("x:!");                /* Invalidate X */
1567                 }
1568                 break;
1569             }
1570             /* FALLTHROUGH */
1571
1572         case CF_INT:
1573             if (flags & CF_CONST) {
1574                 if (val == 1) {
1575                     unsigned L = GetLocalLabel ();
1576                     AddCodeLine ("inc %s", lbuf);
1577                     AddCodeLine ("bne %s", LocalLabelName (L));
1578                     AddCodeLine ("inc %s+1", lbuf);
1579                     g_defcodelabel (L);
1580                     AddCodeLine ("lda %s", lbuf);               /* Hmmm... */
1581                     AddCodeLine ("ldx %s+1", lbuf);
1582                 } else {
1583                     AddCodeLine ("lda #$%02X", (int)(val & 0xFF));
1584                     AddCodeLine ("clc");
1585                     AddCodeLine ("adc %s", lbuf);
1586                     AddCodeLine ("sta %s", lbuf);
1587                     if (val < 0x100) {
1588                         unsigned L = GetLocalLabel ();
1589                         AddCodeLine ("bcc %s", LocalLabelName (L));
1590                         AddCodeLine ("inc %s+1", lbuf);
1591                         g_defcodelabel (L);
1592                         AddCodeLine ("ldx %s+1", lbuf);
1593                     } else {
1594                         AddCodeLine ("lda #$%02X", (unsigned char)(val >> 8));
1595                         AddCodeLine ("adc %s+1", lbuf);
1596                         AddCodeLine ("sta %s+1", lbuf);
1597                         AddCodeLine ("tax");
1598                         AddCodeLine ("lda %s", lbuf);
1599                     }
1600                 }
1601             } else {
1602                 AddCodeLine ("clc");
1603                 AddCodeLine ("adc %s", lbuf);
1604                 AddCodeLine ("sta %s", lbuf);
1605                 AddCodeLine ("txa");
1606                 AddCodeLine ("adc %s+1", lbuf);
1607                 AddCodeLine ("sta %s+1", lbuf);
1608                 AddCodeLine ("tax");
1609                 AddCodeLine ("lda %s", lbuf);
1610             }
1611             break;
1612
1613         case CF_LONG:
1614             if (flags & CF_CONST) {
1615                 if (val < 0x100) {
1616                     AddCodeLine ("ldy #<(%s)", lbuf);
1617                     AddCodeLine ("sty ptr1");
1618                     AddCodeLine ("ldy #>(%s+1)", lbuf);
1619                     if (val == 1) {
1620                         AddCodeLine ("jsr laddeq1");
1621                     } else {
1622                         AddCodeLine ("lda #$%02X", (int)(val & 0xFF));
1623                         AddCodeLine ("jsr laddeqa");
1624                     }
1625                 } else {
1626                     g_getstatic (flags, label, offs);
1627                     g_inc (flags, val);
1628                     g_putstatic (flags, label, offs);
1629                 }
1630             } else {
1631                 AddCodeLine ("ldy #<(%s)", lbuf);
1632                 AddCodeLine ("sty ptr1");
1633                 AddCodeLine ("ldy #>(%s+1)", lbuf);
1634                 AddCodeLine ("jsr laddeq");
1635             }
1636             break;
1637
1638         default:
1639             typeerror (flags);
1640     }
1641 }
1642
1643
1644
1645 void g_addeqlocal (unsigned flags, int offs, unsigned long val)
1646 /* Emit += for a local variable */
1647 {
1648     /* Calculate the true offset, check it, load it into Y */
1649     offs -= oursp;
1650     CheckLocalOffs (offs);
1651
1652     /* Check the size and determine operation */
1653     switch (flags & CF_TYPE) {
1654
1655         case CF_CHAR:
1656             if (flags & CF_FORCECHAR) {
1657                 if (offs == 0) {
1658                     AddCodeLine ("ldx #$00");
1659                     if (flags & CF_CONST) {
1660                         AddCodeLine ("clc");
1661                         AddCodeLine ("lda #$%02X", (int)(val & 0xFF));
1662                         AddCodeLine ("adc (sp,x)");
1663                         AddCodeLine ("sta (sp,x)");
1664                     } else {
1665                         AddCodeLine ("clc");
1666                         AddCodeLine ("adc (sp,x)");
1667                         AddCodeLine ("sta (sp,x)");
1668                     }
1669                 } else {
1670                     ldyconst (offs);
1671                     AddCodeLine ("ldx #$00");
1672                     if (flags & CF_CONST) {
1673                         AddCodeLine ("clc");
1674                         AddCodeLine ("lda #$%02X", (int)(val & 0xFF));
1675                         AddCodeLine ("adc (sp),y");
1676                         AddCodeLine ("sta (sp),y");
1677                     } else {
1678                         AddCodeLine ("clc");
1679                         AddCodeLine ("adc (sp),y");
1680                         AddCodeLine ("sta (sp),y");
1681                     }
1682                 }
1683                 if ((flags & CF_UNSIGNED) == 0) {
1684                     AddCodeLine ("bpl *+3");
1685                     AddCodeLine ("dex");
1686                     AddCodeHint ("x:!");        /* Invalidate X */
1687                 }
1688                 break;
1689             }
1690             /* FALLTHROUGH */
1691
1692         case CF_INT:
1693             if (flags & CF_CONST) {
1694                 g_getimmed (flags, val, 0);
1695             }
1696             if (offs == 0) {
1697                 AddCodeLine ("jsr addeq0sp");
1698             } else {
1699                 ldyconst (offs);
1700                 AddCodeLine ("jsr addeqysp");
1701             }
1702             break;
1703
1704         case CF_LONG:
1705             if (flags & CF_CONST) {
1706                 g_getimmed (flags, val, 0);
1707             }
1708             if (offs == 0) {
1709                 AddCodeLine ("jsr laddeq0sp");
1710             } else {
1711                 ldyconst (offs);
1712                 AddCodeLine ("jsr laddeqysp");
1713             }
1714             break;
1715
1716         default:
1717             typeerror (flags);
1718     }
1719 }
1720
1721
1722
1723 void g_addeqind (unsigned flags, unsigned offs, unsigned long val)
1724 /* Emit += for the location with address in ax */
1725 {
1726     /* If the offset is too large for a byte register, add the high byte
1727      * of the offset to the primary. Beware: We need a special correction
1728      * if the offset in the low byte will overflow in the operation.
1729      */
1730     offs = MakeByteOffs (flags, offs);
1731
1732     /* Check the size and determine operation */
1733     switch (flags & CF_TYPE) {
1734
1735         case CF_CHAR:
1736             AddCodeLine ("sta ptr1");
1737             AddCodeLine ("stx ptr1+1");
1738             if (offs == 0) {
1739                 AddCodeLine ("ldx #$00");
1740                 AddCodeLine ("lda #$%02X", (int)(val & 0xFF));
1741                 AddCodeLine ("clc");
1742                 AddCodeLine ("adc (ptr1,x)");
1743                 AddCodeLine ("sta (ptr1,x)");
1744             } else {
1745                 AddCodeLine ("ldy #$%02X", offs);
1746                 AddCodeLine ("ldx #$00");
1747                 AddCodeLine ("lda #$%02X", (int)(val & 0xFF));
1748                 AddCodeLine ("clc");
1749                 AddCodeLine ("adc (ptr1),y");
1750                 AddCodeLine ("sta (ptr1),y");
1751             }
1752             break;
1753
1754         case CF_INT:
1755             if (CodeSizeFactor >= 200) {
1756                 /* Lots of code, use only if size is not important */
1757                 AddCodeLine ("sta ptr1");
1758                 AddCodeLine ("stx ptr1+1");
1759                 AddCodeLine ("ldy #$%02X", offs);
1760                 AddCodeLine ("lda #$%02X", (int)(val & 0xFF));
1761                 AddCodeLine ("clc");
1762                 AddCodeLine ("adc (ptr1),y");
1763                 AddCodeLine ("sta (ptr1),y");
1764                 AddCodeLine ("pha");
1765                 AddCodeLine ("iny");
1766                 AddCodeLine ("lda #$%02X", (unsigned char)(val >> 8));
1767                 AddCodeLine ("adc (ptr1),y");
1768                 AddCodeLine ("sta (ptr1),y");
1769                 AddCodeLine ("tax");
1770                 AddCodeLine ("pla");
1771                 break;
1772             }
1773             /* FALL THROUGH */
1774
1775         case CF_LONG:
1776             AddCodeLine ("jsr pushax");         /* Push the address */
1777             push (flags);                       /* Correct the internal sp */
1778             g_getind (flags, offs);             /* Fetch the value */
1779             g_inc (flags, val);                 /* Increment value in primary */
1780             g_putind (flags, offs);             /* Store the value back */
1781             break;
1782
1783         default:
1784             typeerror (flags);
1785     }
1786 }
1787
1788
1789
1790 void g_subeqstatic (unsigned flags, unsigned long label, unsigned offs,
1791                     unsigned long val)
1792 /* Emit -= for a static variable */
1793 {
1794     /* Create the correct label name */
1795     char* lbuf = GetLabelName (flags, label, offs);
1796
1797     /* Check the size and determine operation */
1798     switch (flags & CF_TYPE) {
1799
1800         case CF_CHAR:
1801             if (flags & CF_FORCECHAR) {
1802                 AddCodeLine ("ldx #$00");
1803                 if (flags & CF_CONST) {
1804                     if (val == 1) {
1805                         AddCodeLine ("dec %s", lbuf);
1806                         AddCodeLine ("lda %s", lbuf);
1807                     } else {
1808                         AddCodeLine ("sec");
1809                         AddCodeLine ("lda %s", lbuf);
1810                         AddCodeLine ("sbc #$%02X", (int)(val & 0xFF));
1811                         AddCodeLine ("sta %s", lbuf);
1812                     }
1813                 } else {
1814                     AddCodeLine ("sec");
1815                     AddCodeLine ("sta tmp1");
1816                     AddCodeLine ("lda %s", lbuf);
1817                     AddCodeLine ("sbc tmp1");
1818                     AddCodeLine ("sta %s", lbuf);
1819                 }
1820                 if ((flags & CF_UNSIGNED) == 0) {
1821                     AddCodeLine ("bpl *+3");
1822                     AddCodeLine ("dex");
1823                     AddCodeHint ("x:!");                /* Invalidate X */
1824                 }
1825                 break;
1826             }
1827             /* FALLTHROUGH */
1828
1829         case CF_INT:
1830             AddCodeLine ("sec");
1831             if (flags & CF_CONST) {
1832                 AddCodeLine ("lda %s", lbuf);
1833                 AddCodeLine ("sbc #$%02X", (unsigned char)val);
1834                 AddCodeLine ("sta %s", lbuf);
1835                 if (val < 0x100) {
1836                     unsigned L = GetLocalLabel ();
1837                     AddCodeLine ("bcs %s", LocalLabelName (L));
1838                     AddCodeLine ("dec %s+1", lbuf);
1839                     g_defcodelabel (L);
1840                     AddCodeLine ("ldx %s+1", lbuf);
1841                 } else {
1842                     AddCodeLine ("lda %s+1", lbuf);
1843                     AddCodeLine ("sbc #$%02X", (unsigned char)(val >> 8));
1844                     AddCodeLine ("sta %s+1", lbuf);
1845                     AddCodeLine ("tax");
1846                     AddCodeLine ("lda %s", lbuf);
1847                 }
1848             } else {
1849                 AddCodeLine ("sta tmp1");
1850                 AddCodeLine ("lda %s", lbuf);
1851                 AddCodeLine ("sbc tmp1");
1852                 AddCodeLine ("sta %s", lbuf);
1853                 AddCodeLine ("stx tmp1");
1854                 AddCodeLine ("lda %s+1", lbuf);
1855                 AddCodeLine ("sbc tmp1");
1856                 AddCodeLine ("sta %s+1", lbuf);
1857                 AddCodeLine ("tax");
1858                 AddCodeLine ("lda %s", lbuf);
1859             }
1860             break;
1861
1862         case CF_LONG:
1863             if (flags & CF_CONST) {
1864                 if (val < 0x100) {
1865                     AddCodeLine ("ldy #<(%s)", lbuf);
1866                     AddCodeLine ("sty ptr1");
1867                     AddCodeLine ("ldy #>(%s+1)", lbuf);
1868                     if (val == 1) {
1869                         AddCodeLine ("jsr lsubeq1");
1870                     } else {
1871                         AddCodeLine ("lda #$%02X", (unsigned char)val);
1872                         AddCodeLine ("jsr lsubeqa");
1873                     }
1874                 } else {
1875                     g_getstatic (flags, label, offs);
1876                     g_dec (flags, val);
1877                     g_putstatic (flags, label, offs);
1878                 }
1879             } else {
1880                 AddCodeLine ("ldy #<(%s)", lbuf);
1881                 AddCodeLine ("sty ptr1");
1882                 AddCodeLine ("ldy #>(%s+1)", lbuf);
1883                 AddCodeLine ("jsr lsubeq");
1884             }
1885             break;
1886
1887         default:
1888             typeerror (flags);
1889     }
1890 }
1891
1892
1893
1894 void g_subeqlocal (unsigned flags, int offs, unsigned long val)
1895 /* Emit -= for a local variable */
1896 {
1897     /* Calculate the true offset, check it, load it into Y */
1898     offs -= oursp;
1899     CheckLocalOffs (offs);
1900
1901     /* Check the size and determine operation */
1902     switch (flags & CF_TYPE) {
1903
1904         case CF_CHAR:
1905             if (flags & CF_FORCECHAR) {
1906                 ldyconst (offs);
1907                 AddCodeLine ("ldx #$00");
1908                 AddCodeLine ("sec");
1909                 if (flags & CF_CONST) {
1910                     AddCodeLine ("lda (sp),y");
1911                     AddCodeLine ("sbc #$%02X", (unsigned char)val);
1912                 } else {
1913                     AddCodeLine ("sta tmp1");
1914                     AddCodeLine ("lda (sp),y");
1915                     AddCodeLine ("sbc tmp1");
1916                 }
1917                 AddCodeLine ("sta (sp),y");
1918                 if ((flags & CF_UNSIGNED) == 0) {
1919                     AddCodeLine ("bpl *+3");
1920                     AddCodeLine ("dex");
1921                     AddCodeHint ("x:!");                /* Invalidate X */
1922                 }
1923                 break;
1924             }
1925             /* FALLTHROUGH */
1926
1927         case CF_INT:
1928             if (flags & CF_CONST) {
1929                 g_getimmed (flags, val, 0);
1930             }
1931             if (offs == 0) {
1932                 AddCodeLine ("jsr subeq0sp");
1933             } else {
1934                 ldyconst (offs);
1935                 AddCodeLine ("jsr subeqysp");
1936             }
1937             break;
1938
1939         case CF_LONG:
1940             if (flags & CF_CONST) {
1941                 g_getimmed (flags, val, 0);
1942             }
1943             if (offs == 0) {
1944                 AddCodeLine ("jsr lsubeq0sp");
1945             } else {
1946                 ldyconst (offs);
1947                 AddCodeLine ("jsr lsubeqysp");
1948             }
1949             break;
1950
1951         default:
1952             typeerror (flags);
1953     }
1954 }
1955
1956
1957
1958 void g_subeqind (unsigned flags, unsigned offs, unsigned long val)
1959 /* Emit -= for the location with address in ax */
1960 {
1961     /* If the offset is too large for a byte register, add the high byte
1962      * of the offset to the primary. Beware: We need a special correction
1963      * if the offset in the low byte will overflow in the operation.
1964      */
1965     offs = MakeByteOffs (flags, offs);
1966
1967     /* Check the size and determine operation */
1968     switch (flags & CF_TYPE) {
1969
1970         case CF_CHAR:
1971             AddCodeLine ("sta ptr1");
1972             AddCodeLine ("stx ptr1+1");
1973             if (offs == 0) {
1974                 AddCodeLine ("ldx #$00");
1975                 AddCodeLine ("lda (ptr1,x)");
1976                 AddCodeLine ("sec");
1977                 AddCodeLine ("sbc #$%02X", (unsigned char)val);
1978                 AddCodeLine ("sta (ptr1,x)");
1979             } else {
1980                 AddCodeLine ("ldy #$%02X", offs);
1981                 AddCodeLine ("ldx #$00");
1982                 AddCodeLine ("lda (ptr1),y");
1983                 AddCodeLine ("sec");
1984                 AddCodeLine ("sbc #$%02X", (unsigned char)val);
1985                 AddCodeLine ("sta (ptr1),y");
1986             }
1987             break;
1988
1989         case CF_INT:
1990             if (CodeSizeFactor >= 200) {
1991                 /* Lots of code, use only if size is not important */
1992                 AddCodeLine ("sta ptr1");
1993                 AddCodeLine ("stx ptr1+1");
1994                 AddCodeLine ("ldy #$%02X", offs);
1995                 AddCodeLine ("lda (ptr1),y");
1996                 AddCodeLine ("sec");
1997                 AddCodeLine ("sbc #$%02X", (unsigned char)val);
1998                 AddCodeLine ("sta (ptr1),y");
1999                 AddCodeLine ("pha");
2000                 AddCodeLine ("iny");
2001                 AddCodeLine ("lda (ptr1),y");
2002                 AddCodeLine ("sbc #$%02X", (unsigned char)(val >> 8));
2003                 AddCodeLine ("sta (ptr1),y");
2004                 AddCodeLine ("tax");
2005                 AddCodeLine ("pla");
2006                 break;
2007             }
2008             /* FALL THROUGH */
2009
2010         case CF_LONG:
2011             AddCodeLine ("jsr pushax");         /* Push the address */
2012             push (flags);                       /* Correct the internal sp */
2013             g_getind (flags, offs);             /* Fetch the value */
2014             g_dec (flags, val);                 /* Increment value in primary */
2015             g_putind (flags, offs);             /* Store the value back */
2016             break;
2017
2018         default:
2019             typeerror (flags);
2020     }
2021 }
2022
2023
2024
2025 /*****************************************************************************/
2026 /*                 Add a variable address to the value in ax                 */
2027 /*****************************************************************************/
2028
2029
2030
2031 void g_addaddr_local (unsigned flags, int offs)
2032 /* Add the address of a local variable to ax */
2033 {
2034     /* Add the offset */
2035     offs -= oursp;
2036     if (offs != 0) {
2037         /* We cannot address more then 256 bytes of locals anyway */
2038         CheckLocalOffs (offs);
2039         AddCodeLine ("clc");
2040         AddCodeLine ("adc #$%02X", offs & 0xFF);
2041         AddCodeLine ("bcc *+4");        /* Do also skip the CLC insn below */
2042         AddCodeLine ("inx");
2043         AddCodeHint ("x:!");                    /* Invalidate X */
2044     }
2045
2046     /* Add the current stackpointer value */
2047     AddCodeLine ("clc");
2048     AddCodeLine ("adc sp");
2049     AddCodeLine ("tay");
2050     AddCodeLine ("txa");
2051     AddCodeLine ("adc sp+1");
2052     AddCodeLine ("tax");
2053     AddCodeLine ("tya");
2054 }
2055
2056
2057
2058 void g_addaddr_static (unsigned flags, unsigned long label, unsigned offs)
2059 /* Add the address of a static variable to ax */
2060 {
2061     /* Create the correct label name */
2062     char* lbuf = GetLabelName (flags, label, offs);
2063
2064     /* Add the address to the current ax value */
2065     AddCodeLine ("clc");
2066     AddCodeLine ("adc #<(%s)", lbuf);
2067     AddCodeLine ("tay");
2068     AddCodeLine ("txa");
2069     AddCodeLine ("adc #>(%s)", lbuf);
2070     AddCodeLine ("tax");
2071     AddCodeLine ("tya");
2072 }
2073
2074
2075
2076 /*****************************************************************************/
2077 /*                                                                           */
2078 /*****************************************************************************/
2079
2080
2081
2082 void g_save (unsigned flags)
2083 /* Copy primary register to hold register. */
2084 {
2085     /* Check the size and determine operation */
2086     switch (flags & CF_TYPE) {
2087
2088         case CF_CHAR:
2089             if (flags & CF_FORCECHAR) {
2090                 AddCodeLine ("pha");
2091                 break;
2092             }
2093             /* FALLTHROUGH */
2094
2095         case CF_INT:
2096             AddCodeLine ("sta regsave");
2097             AddCodeLine ("stx regsave+1");
2098             break;
2099
2100         case CF_LONG:
2101             AddCodeLine ("jsr saveeax");
2102             break;
2103
2104         default:
2105             typeerror (flags);
2106     }
2107 }
2108
2109
2110
2111 void g_restore (unsigned flags)
2112 /* Copy hold register to primary. */
2113 {
2114     /* Check the size and determine operation */
2115     switch (flags & CF_TYPE) {
2116
2117         case CF_CHAR:
2118             if (flags & CF_FORCECHAR) {
2119                 AddCodeLine ("pla");
2120                 break;
2121             }
2122             /* FALLTHROUGH */
2123
2124         case CF_INT:
2125             AddCodeLine ("lda regsave");
2126             AddCodeLine ("ldx regsave+1");
2127             break;
2128
2129         case CF_LONG:
2130             AddCodeLine ("jsr resteax");
2131             break;
2132
2133         default:
2134             typeerror (flags);
2135     }
2136 }
2137
2138
2139
2140 void g_cmp (unsigned flags, unsigned long val)
2141 /* Immidiate compare. The primary register will not be changed, Z flag
2142  * will be set.
2143  */
2144 {
2145     /* Check the size and determine operation */
2146     switch (flags & CF_TYPE) {
2147
2148         case CF_CHAR:
2149             if (flags & CF_FORCECHAR) {
2150                 AddCodeLine ("cmp #$%02X", (unsigned char)val);
2151                 break;
2152             }
2153             /* FALLTHROUGH */
2154
2155         case CF_INT:
2156             AddCodeLine ("cmp #$%02X", (unsigned char)val);
2157             AddCodeLine ("bne *+4");
2158             AddCodeLine ("cpx #$%02X", (unsigned char)(val >> 8));
2159             break;
2160
2161         case CF_LONG:
2162             Internal ("g_cmp: Long compares not implemented");
2163             break;
2164
2165         default:
2166             typeerror (flags);
2167     }
2168 }
2169
2170
2171
2172 static void oper (unsigned flags, unsigned long val, char** subs)
2173 /* Encode a binary operation. subs is a pointer to four groups of three
2174  * strings:
2175  *      0-2     --> Operate on ints
2176  *      3-5     --> Operate on unsigneds
2177  *      6-8     --> Operate on longs
2178  *      9-11    --> Operate on unsigned longs
2179  *
2180  * The first subroutine names in each string group is used to encode an
2181  * operation with a zero constant, the second to encode an operation with
2182  * a 8 bit constant, and the third is used in all other cases.
2183  */
2184 {
2185     unsigned offs;
2186
2187     /* Determine the offset into the array */
2188     offs = (flags & CF_UNSIGNED)? 3 : 0;
2189     switch (flags & CF_TYPE) {
2190         case CF_CHAR:
2191         case CF_INT:
2192             break;
2193
2194         case CF_LONG:
2195             offs += 6;
2196             break;
2197
2198         default:
2199             typeerror (flags);
2200     }
2201
2202     /* Encode the operation */
2203     if (flags & CF_CONST) {
2204         /* Constant value given */
2205         if (val == 0 && subs [offs+0]) {
2206             /* Special case: constant with value zero */
2207             AddCodeLine ("jsr %s", subs [offs+0]);
2208         } else if (val < 0x100 && subs [offs+1]) {
2209             /* Special case: constant with high byte zero */
2210             ldaconst (val);             /* Load low byte */
2211             AddCodeLine ("jsr %s", subs [offs+1]);
2212         } else {
2213             /* Others: arbitrary constant value */
2214             g_getimmed (flags, val, 0);                 /* Load value */
2215             AddCodeLine ("jsr %s", subs [offs+2]);
2216         }
2217     } else {
2218         /* Value not constant (is already in (e)ax) */
2219         AddCodeLine ("jsr %s", subs [offs+2]);
2220     }
2221
2222     /* The operation will pop it's argument */
2223     pop (flags);
2224 }
2225
2226
2227
2228 void g_test (unsigned flags)
2229 /* Test the value in the primary and set the condition codes */
2230 {
2231     switch (flags & CF_TYPE) {
2232
2233         case CF_CHAR:
2234             if (flags & CF_FORCECHAR) {
2235                 AddCodeLine ("tax");
2236                 break;
2237             }
2238             /* FALLTHROUGH */
2239
2240         case CF_INT:
2241             AddCodeLine ("stx tmp1");
2242             AddCodeLine ("ora tmp1");
2243             break;
2244
2245         case CF_LONG:
2246             if (flags & CF_UNSIGNED) {
2247                 AddCodeLine ("jsr utsteax");
2248             } else {
2249                 AddCodeLine ("jsr tsteax");
2250             }
2251             break;
2252
2253         default:
2254             typeerror (flags);
2255
2256     }
2257 }
2258
2259
2260
2261 void g_push (unsigned flags, unsigned long val)
2262 /* Push the primary register or a constant value onto the stack */
2263 {
2264     unsigned char hi;
2265
2266     if (flags & CF_CONST && (flags & CF_TYPE) != CF_LONG) {
2267
2268         /* We have a constant 8 or 16 bit value */
2269         if ((flags & CF_TYPE) == CF_CHAR && (flags & CF_FORCECHAR)) {
2270
2271             /* Handle as 8 bit value */
2272             if (CodeSizeFactor >= 165 || val > 2) {
2273                 ldaconst (val);
2274                 AddCodeLine ("jsr pusha");
2275             } else {
2276                 AddCodeLine ("jsr pushc%d", (int) val);
2277             }
2278
2279         } else {
2280
2281             /* Handle as 16 bit value */
2282             hi = (unsigned char) (val >> 8);
2283             if (val <= 7) {
2284                 AddCodeLine ("jsr push%u", (unsigned) val);
2285             } else if (hi == 0 || hi == 0xFF) {
2286                 /* Use special function */
2287                 ldaconst (val);
2288                 AddCodeLine ("jsr %s", (hi == 0)? "pusha0" : "pushaFF");
2289             } else {
2290                 /* Long way ... */
2291                 g_getimmed (flags, val, 0);
2292                 AddCodeLine ("jsr pushax");
2293             }
2294         }
2295
2296     } else {
2297
2298         /* Value is not 16 bit or not constant */
2299         if (flags & CF_CONST) {
2300             /* Constant 32 bit value, load into eax */
2301             g_getimmed (flags, val, 0);
2302         }
2303
2304         /* Push the primary register */
2305         switch (flags & CF_TYPE) {
2306
2307             case CF_CHAR:
2308                 if (flags & CF_FORCECHAR) {
2309                     /* Handle as char */
2310                     AddCodeLine ("jsr pusha");
2311                     break;
2312                 }
2313                 /* FALL THROUGH */
2314             case CF_INT:
2315                 AddCodeLine ("jsr pushax");
2316                 break;
2317
2318             case CF_LONG:
2319                 AddCodeLine ("jsr pusheax");
2320                 break;
2321
2322             default:
2323                 typeerror (flags);
2324
2325         }
2326
2327     }
2328
2329     /* Adjust the stack offset */
2330     push (flags);
2331 }
2332
2333
2334
2335 void g_swap (unsigned flags)
2336 /* Swap the primary register and the top of the stack. flags give the type
2337  * of *both* values (must have same size).
2338  */
2339 {
2340     switch (flags & CF_TYPE) {
2341
2342         case CF_CHAR:
2343         case CF_INT:
2344             AddCodeLine ("jsr swapstk");
2345             break;
2346
2347         case CF_LONG:
2348             AddCodeLine ("jsr swapestk");
2349             break;
2350
2351         default:
2352             typeerror (flags);
2353
2354     }
2355 }
2356
2357
2358
2359 void g_call (unsigned Flags, const char* Label, unsigned ArgSize)
2360 /* Call the specified subroutine name */
2361 {
2362     if ((Flags & CF_FIXARGC) == 0) {
2363         /* Pass the argument count */
2364         ldyconst (ArgSize);
2365     }
2366     AddCodeLine ("jsr _%s", Label);
2367     oursp += ArgSize;           /* callee pops args */
2368 }
2369
2370
2371
2372 void g_callind (unsigned Flags, unsigned ArgSize)
2373 /* Call subroutine with address in AX */
2374 {
2375     if ((Flags & CF_FIXARGC) == 0) {
2376         /* Pass arg count */
2377         ldyconst (ArgSize);
2378     }
2379     AddCodeLine ("jsr callax"); /* do the call */
2380     oursp += ArgSize;                   /* callee pops args */
2381 }
2382
2383
2384
2385 void g_jump (unsigned Label)
2386 /* Jump to specified internal label number */
2387 {
2388     AddCodeLine ("jmp %s", LocalLabelName (Label));
2389 }
2390
2391
2392
2393 void g_switch (unsigned Flags)
2394 /* Output switch statement preamble */
2395 {
2396     switch (Flags & CF_TYPE) {
2397
2398         case CF_CHAR:
2399         case CF_INT:
2400             AddCodeLine ("jsr switch");
2401             break;
2402
2403         case CF_LONG:
2404             AddCodeLine ("jsr lswitch");
2405             break;
2406
2407         default:
2408             typeerror (Flags);
2409
2410     }
2411 }
2412
2413
2414
2415 void g_case (unsigned flags, unsigned label, unsigned long val)
2416 /* Create table code for one case selector */
2417 {
2418     switch (flags & CF_TYPE) {
2419
2420         case CF_CHAR:
2421         case CF_INT:
2422             AddCodeLine (".word $%04X, %s",
2423                          (unsigned)(val & 0xFFFF),
2424                          LocalLabelName (label));
2425             break;
2426
2427         case CF_LONG:
2428             AddCodeLine (".dword $%08lX", val);
2429             AddCodeLine (".word %s", LocalLabelName (label));
2430             break;
2431
2432         default:
2433             typeerror (flags);
2434
2435     }
2436 }
2437
2438
2439
2440 void g_truejump (unsigned flags, unsigned label)
2441 /* Jump to label if zero flag clear */
2442 {
2443     AddCodeLine ("jne %s", LocalLabelName (label));
2444 }
2445
2446
2447
2448 void g_falsejump (unsigned flags, unsigned label)
2449 /* Jump to label if zero flag set */
2450 {
2451     AddCodeLine ("jeq %s", LocalLabelName (label));
2452 }
2453
2454
2455
2456 static void mod_internal (int k, char* verb1, char* verb2)
2457 {
2458     if (k <= 8) {
2459         AddCodeLine ("jsr %ssp%c", verb1, k + '0');
2460     } else {
2461         CheckLocalOffs (k);
2462         ldyconst (k);
2463         AddCodeLine ("jsr %ssp", verb2);
2464     }
2465 }
2466
2467
2468
2469 void g_space (int space)
2470 /* Create or drop space on the stack */
2471 {
2472     if (space < 0) {
2473         mod_internal (-space, "inc", "addy");
2474     } else if (space > 0) {
2475         mod_internal (space, "dec", "suby");
2476     }
2477 }
2478
2479
2480
2481 void g_cstackcheck (void)
2482 /* Check for a C stack overflow */
2483 {
2484     AddCodeLine ("jsr cstkchk");
2485 }
2486
2487
2488
2489 void g_stackcheck (void)
2490 /* Check for a stack overflow */
2491 {
2492     AddCodeLine ("jsr stkchk");
2493 }
2494
2495
2496
2497 void g_add (unsigned flags, unsigned long val)
2498 /* Primary = TOS + Primary */
2499 {
2500     static char* ops [12] = {
2501         0,              "tosadda0",     "tosaddax",
2502         0,              "tosadda0",     "tosaddax",
2503         0,              0,              "tosaddeax",
2504         0,              0,              "tosaddeax",
2505     };
2506
2507     if (flags & CF_CONST) {
2508         flags &= ~CF_FORCECHAR; /* Handle chars as ints */
2509         g_push (flags & ~CF_CONST, 0);
2510     }
2511     oper (flags, val, ops);
2512 }
2513
2514
2515
2516 void g_sub (unsigned flags, unsigned long val)
2517 /* Primary = TOS - Primary */
2518 {
2519     static char* ops [12] = {
2520         0,              "tossuba0",     "tossubax",
2521         0,              "tossuba0",     "tossubax",
2522         0,              0,              "tossubeax",
2523         0,              0,              "tossubeax",
2524     };
2525
2526     if (flags & CF_CONST) {
2527         flags &= ~CF_FORCECHAR; /* Handle chars as ints */
2528         g_push (flags & ~CF_CONST, 0);
2529     }
2530     oper (flags, val, ops);
2531 }
2532
2533
2534
2535 void g_rsub (unsigned flags, unsigned long val)
2536 /* Primary = Primary - TOS */
2537 {
2538     static char* ops [12] = {
2539         0,              "tosrsuba0",    "tosrsubax",
2540         0,              "tosrsuba0",    "tosrsubax",
2541         0,              0,              "tosrsubeax",
2542         0,              0,              "tosrsubeax",
2543     };
2544     oper (flags, val, ops);
2545 }
2546
2547
2548
2549 void g_mul (unsigned flags, unsigned long val)
2550 /* Primary = TOS * Primary */
2551 {
2552     static char* ops [12] = {
2553         0,              "tosmula0",     "tosmulax",
2554         0,              "tosumula0",    "tosumulax",
2555         0,              0,              "tosmuleax",
2556         0,              0,              "tosumuleax",
2557     };
2558
2559     int p2;
2560
2561     /* Do strength reduction if the value is constant and a power of two */
2562     if (flags & CF_CONST && (p2 = powerof2 (val)) >= 0) {
2563         /* Generate a shift instead */
2564         g_asl (flags, p2);
2565         return;
2566     }
2567
2568     /* If the right hand side is const, the lhs is not on stack but still
2569      * in the primary register.
2570      */
2571     if (flags & CF_CONST) {
2572
2573         switch (flags & CF_TYPE) {
2574
2575             case CF_CHAR:
2576                 if (flags & CF_FORCECHAR) {
2577                     /* Handle some special cases */
2578                     switch (val) {
2579
2580                         case 3:
2581                             AddCodeLine ("sta tmp1");
2582                             AddCodeLine ("asl a");
2583                             AddCodeLine ("clc");
2584                             AddCodeLine ("adc tmp1");
2585                             return;
2586
2587                         case 5:
2588                             AddCodeLine ("sta tmp1");
2589                             AddCodeLine ("asl a");
2590                             AddCodeLine ("asl a");
2591                             AddCodeLine ("clc");
2592                             AddCodeLine ("adc tmp1");
2593                             return;
2594
2595                         case 10:
2596                             AddCodeLine ("sta tmp1");
2597                             AddCodeLine ("asl a");
2598                             AddCodeLine ("asl a");
2599                             AddCodeLine ("clc");
2600                             AddCodeLine ("adc tmp1");
2601                             AddCodeLine ("asl a");
2602                             return;
2603                     }
2604                 }
2605                 /* FALLTHROUGH */
2606
2607             case CF_INT:
2608                 break;
2609
2610             case CF_LONG:
2611                 break;
2612
2613             default:
2614                 typeerror (flags);
2615         }
2616
2617         /* If we go here, we didn't emit code. Push the lhs on stack and fall
2618          * into the normal, non-optimized stuff.
2619          */
2620         flags &= ~CF_FORCECHAR; /* Handle chars as ints */
2621         g_push (flags & ~CF_CONST, 0);
2622
2623     }
2624
2625     /* Use long way over the stack */
2626     oper (flags, val, ops);
2627 }
2628
2629
2630
2631 void g_div (unsigned flags, unsigned long val)
2632 /* Primary = TOS / Primary */
2633 {
2634     static char* ops [12] = {
2635         0,              "tosdiva0",     "tosdivax",
2636         0,              "tosudiva0",    "tosudivax",
2637         0,              0,              "tosdiveax",
2638         0,              0,              "tosudiveax",
2639     };
2640
2641     /* Do strength reduction if the value is constant and a power of two */
2642     int p2;
2643     if ((flags & CF_CONST) && (p2 = powerof2 (val)) >= 0) {
2644         /* Generate a shift instead */
2645         g_asr (flags, p2);
2646     } else {
2647         /* Generate a division */
2648         if (flags & CF_CONST) {
2649             /* lhs is not on stack */
2650             flags &= ~CF_FORCECHAR;     /* Handle chars as ints */
2651             g_push (flags & ~CF_CONST, 0);
2652         }
2653         oper (flags, val, ops);
2654     }
2655 }
2656
2657
2658
2659 void g_mod (unsigned flags, unsigned long val)
2660 /* Primary = TOS % Primary */
2661 {
2662     static char* ops [12] = {
2663         0,              "tosmoda0",     "tosmodax",
2664         0,              "tosumoda0",    "tosumodax",
2665         0,              0,              "tosmodeax",
2666         0,              0,              "tosumodeax",
2667     };
2668     int p2;
2669
2670     /* Check if we can do some cost reduction */
2671     if ((flags & CF_CONST) && (flags & CF_UNSIGNED) && val != 0xFFFFFFFF && (p2 = powerof2 (val)) >= 0) {
2672         /* We can do that with an AND operation */
2673         g_and (flags, val - 1);
2674     } else {
2675         /* Do it the hard way... */
2676         if (flags & CF_CONST) {
2677             /* lhs is not on stack */
2678             flags &= ~CF_FORCECHAR;     /* Handle chars as ints */
2679             g_push (flags & ~CF_CONST, 0);
2680         }
2681         oper (flags, val, ops);
2682     }
2683 }
2684
2685
2686
2687 void g_or (unsigned flags, unsigned long val)
2688 /* Primary = TOS | Primary */
2689 {
2690     static char* ops [12] = {
2691         0,              "tosora0",      "tosorax",
2692         0,              "tosora0",      "tosorax",
2693         0,              0,              "tosoreax",
2694         0,              0,              "tosoreax",
2695     };
2696
2697     /* If the right hand side is const, the lhs is not on stack but still
2698      * in the primary register.
2699      */
2700     if (flags & CF_CONST) {
2701
2702         switch (flags & CF_TYPE) {
2703
2704             case CF_CHAR:
2705                 if (flags & CF_FORCECHAR) {
2706                     if ((val & 0xFF) != 0xFF) {
2707                         AddCodeLine ("ora #$%02X", (unsigned char)val);
2708                     }
2709                     return;
2710                 }
2711                 /* FALLTHROUGH */
2712
2713             case CF_INT:
2714                 if (val <= 0xFF) {
2715                     AddCodeLine ("ora #$%02X", (unsigned char)val);
2716                     return;
2717                 }
2718                 break;
2719
2720             case CF_LONG:
2721                 if (val <= 0xFF) {
2722                     AddCodeLine ("ora #$%02X", (unsigned char)val);
2723                     return;
2724                 }
2725                 break;
2726
2727             default:
2728                 typeerror (flags);
2729         }
2730
2731         /* If we go here, we didn't emit code. Push the lhs on stack and fall
2732          * into the normal, non-optimized stuff.
2733          */
2734         g_push (flags & ~CF_CONST, 0);
2735
2736     }
2737
2738     /* Use long way over the stack */
2739     oper (flags, val, ops);
2740 }
2741
2742
2743
2744 void g_xor (unsigned flags, unsigned long val)
2745 /* Primary = TOS ^ Primary */
2746 {
2747     static char* ops [12] = {
2748         0,              "tosxora0",     "tosxorax",
2749         0,              "tosxora0",     "tosxorax",
2750         0,              0,              "tosxoreax",
2751         0,              0,              "tosxoreax",
2752     };
2753
2754
2755     /* If the right hand side is const, the lhs is not on stack but still
2756      * in the primary register.
2757      */
2758     if (flags & CF_CONST) {
2759
2760         switch (flags & CF_TYPE) {
2761
2762             case CF_CHAR:
2763                 if (flags & CF_FORCECHAR) {
2764                     if ((val & 0xFF) != 0) {
2765                         AddCodeLine ("eor #$%02X", (unsigned char)val);
2766                     }
2767                     return;
2768                 }
2769                 /* FALLTHROUGH */
2770
2771             case CF_INT:
2772                 if (val <= 0xFF) {
2773                     if (val != 0) {
2774                         AddCodeLine ("eor #$%02X", (unsigned char)val);
2775                     }
2776                     return;
2777                 } else if ((val & 0xFF) == 0) {
2778                     AddCodeLine ("pha");
2779                     AddCodeLine ("txa");
2780                     AddCodeLine ("eor #$%02X", (unsigned char)(val >> 8));
2781                     AddCodeLine ("tax");
2782                     AddCodeLine ("pla");
2783                     return;
2784                 }
2785                 break;
2786
2787             case CF_LONG:
2788                 if (val <= 0xFF) {
2789                     if (val != 0) {
2790                         AddCodeLine ("eor #$%02X", (unsigned char)val);
2791                     }
2792                     return;
2793                 }
2794                 break;
2795
2796             default:
2797                 typeerror (flags);
2798         }
2799
2800         /* If we go here, we didn't emit code. Push the lhs on stack and fall
2801          * into the normal, non-optimized stuff.
2802          */
2803         g_push (flags & ~CF_CONST, 0);
2804
2805     }
2806
2807     /* Use long way over the stack */
2808     oper (flags, val, ops);
2809 }
2810
2811
2812
2813 void g_and (unsigned flags, unsigned long val)
2814 /* Primary = TOS & Primary */
2815 {
2816     static char* ops [12] = {
2817         0,              "tosanda0",     "tosandax",
2818         0,              "tosanda0",     "tosandax",
2819         0,              0,              "tosandeax",
2820         0,              0,              "tosandeax",
2821     };
2822
2823     /* If the right hand side is const, the lhs is not on stack but still
2824      * in the primary register.
2825      */
2826     if (flags & CF_CONST) {
2827
2828         switch (flags & CF_TYPE) {
2829
2830             case CF_CHAR:
2831                 if (flags & CF_FORCECHAR) {
2832                     AddCodeLine ("and #$%02X", (unsigned char)val);
2833                     return;
2834                 }
2835                 /* FALLTHROUGH */
2836             case CF_INT:
2837                 if ((val & 0xFFFF) != 0xFFFF) {
2838                     if (val <= 0xFF) {
2839                         ldxconst (0);
2840                         if (val == 0) {
2841                             ldaconst (0);
2842                         } else if (val != 0xFF) {
2843                             AddCodeLine ("and #$%02X", (unsigned char)val);
2844                         }
2845                     } else if ((val & 0xFF00) == 0xFF00) {
2846                         AddCodeLine ("and #$%02X", (unsigned char)val);
2847                     } else if ((val & 0x00FF) == 0x0000) {
2848                         AddCodeLine ("txa");
2849                         AddCodeLine ("and #$%02X", (unsigned char)(val >> 8));
2850                         AddCodeLine ("tax");
2851                         ldaconst (0);
2852                     } else {
2853                         AddCodeLine ("tay");
2854                         AddCodeLine ("txa");
2855                         AddCodeLine ("and #$%02X", (unsigned char)(val >> 8));
2856                         AddCodeLine ("tax");
2857                         AddCodeLine ("tya");
2858                         if ((val & 0x00FF) != 0x00FF) {
2859                             AddCodeLine ("and #$%02X", (unsigned char)val);
2860                         }
2861                     }
2862                 }
2863                 return;
2864
2865             case CF_LONG:
2866                 if (val <= 0xFF) {
2867                     ldxconst (0);
2868                     AddCodeLine ("stx sreg+1");
2869                     AddCodeLine ("stx sreg");
2870                     if ((val & 0xFF) != 0xFF) {
2871                          AddCodeLine ("and #$%02X", (unsigned char)val);
2872                     }
2873                     return;
2874                 } else if (val == 0xFF00) {
2875                     ldaconst (0);
2876                     AddCodeLine ("sta sreg+1");
2877                     AddCodeLine ("sta sreg");
2878                     return;
2879                 }
2880                 break;
2881
2882             default:
2883                 typeerror (flags);
2884         }
2885
2886         /* If we go here, we didn't emit code. Push the lhs on stack and fall
2887          * into the normal, non-optimized stuff.
2888          */
2889         g_push (flags & ~CF_CONST, 0);
2890
2891     }
2892
2893     /* Use long way over the stack */
2894     oper (flags, val, ops);
2895 }
2896
2897
2898
2899 void g_asr (unsigned flags, unsigned long val)
2900 /* Primary = TOS >> Primary */
2901 {
2902     static char* ops [12] = {
2903         0,              "tosasra0",     "tosasrax",
2904         0,              "tosshra0",     "tosshrax",
2905         0,              0,              "tosasreax",
2906         0,              0,              "tosshreax",
2907     };
2908
2909     /* If the right hand side is const, the lhs is not on stack but still
2910      * in the primary register.
2911      */
2912     if (flags & CF_CONST) {
2913
2914         switch (flags & CF_TYPE) {
2915
2916             case CF_CHAR:
2917             case CF_INT:
2918                 if (val >= 1 && val <= 3) {
2919                     if (flags & CF_UNSIGNED) {
2920                         AddCodeLine ("jsr shrax%ld", val);
2921                     } else {
2922                         AddCodeLine ("jsr asrax%ld", val);
2923                     }
2924                     return;
2925                 } else if (val == 8 && (flags & CF_UNSIGNED)) {
2926                     AddCodeLine ("txa");
2927                     ldxconst (0);
2928                     return;
2929                 }
2930                 break;
2931
2932             case CF_LONG:
2933                 if (val >= 1 && val <= 3) {
2934                     if (flags & CF_UNSIGNED) {
2935                         AddCodeLine ("jsr shreax%ld", val);
2936                     } else {
2937                         AddCodeLine ("jsr asreax%ld", val);
2938                     }
2939                     return;
2940                 } else if (val == 8 && (flags & CF_UNSIGNED)) {
2941                     AddCodeLine ("txa");
2942                     AddCodeLine ("ldx sreg");
2943                     AddCodeLine ("ldy sreg+1");
2944                     AddCodeLine ("sty sreg");
2945                     AddCodeLine ("ldy #$00");
2946                     AddCodeLine ("sty sreg+1");
2947                     return;
2948                 } else if (val == 16) {
2949                     AddCodeLine ("ldy #$00");
2950                     AddCodeLine ("ldx sreg+1");
2951                     if ((flags & CF_UNSIGNED) == 0) {
2952                         AddCodeLine ("bpl *+3");
2953                         AddCodeLine ("dey");
2954                         AddCodeHint ("y:!");
2955                     }
2956                     AddCodeLine ("lda sreg");
2957                     AddCodeLine ("sty sreg+1");
2958                     AddCodeLine ("sty sreg");
2959                     return;
2960                 }
2961                 break;
2962
2963             default:
2964                 typeerror (flags);
2965         }
2966
2967         /* If we go here, we didn't emit code. Push the lhs on stack and fall
2968          * into the normal, non-optimized stuff.
2969          */
2970         g_push (flags & ~CF_CONST, 0);
2971
2972     }
2973
2974     /* Use long way over the stack */
2975     oper (flags, val, ops);
2976 }
2977
2978
2979
2980 void g_asl (unsigned flags, unsigned long val)
2981 /* Primary = TOS << Primary */
2982 {
2983     static char* ops [12] = {
2984         0,              "tosasla0",     "tosaslax",
2985         0,              "tosshla0",     "tosshlax",
2986         0,              0,              "tosasleax",
2987         0,              0,              "tosshleax",
2988     };
2989
2990
2991     /* If the right hand side is const, the lhs is not on stack but still
2992      * in the primary register.
2993      */
2994     if (flags & CF_CONST) {
2995
2996         switch (flags & CF_TYPE) {
2997
2998             case CF_CHAR:
2999             case CF_INT:
3000                 if (val >= 1 && val <= 3) {
3001                     if (flags & CF_UNSIGNED) {
3002                         AddCodeLine ("jsr shlax%ld", val);
3003                     } else {
3004                         AddCodeLine ("jsr aslax%ld", val);
3005                     }
3006                     return;
3007                 } else if (val == 8) {
3008                     AddCodeLine ("tax");
3009                     AddCodeLine ("lda #$00");
3010                     return;
3011                 }
3012                 break;
3013
3014             case CF_LONG:
3015                 if (val >= 1 && val <= 3) {
3016                     if (flags & CF_UNSIGNED) {
3017                         AddCodeLine ("jsr shleax%ld", val);
3018                     } else {
3019                         AddCodeLine ("jsr asleax%ld", val);
3020                     }
3021                     return;
3022                 } else if (val == 8) {
3023                     AddCodeLine ("ldy sreg");
3024                     AddCodeLine ("sty sreg+1");
3025                     AddCodeLine ("stx sreg");
3026                     AddCodeLine ("tax");
3027                     AddCodeLine ("lda #$00");
3028                     return;
3029                 } else if (val == 16) {
3030                     AddCodeLine ("stx sreg+1");
3031                     AddCodeLine ("sta sreg");
3032                     AddCodeLine ("lda #$00");
3033                     AddCodeLine ("tax");
3034                     return;
3035                 }
3036                 break;
3037
3038             default:
3039                 typeerror (flags);
3040         }
3041
3042         /* If we go here, we didn't emit code. Push the lhs on stack and fall
3043          * into the normal, non-optimized stuff.
3044          */
3045         g_push (flags & ~CF_CONST, 0);
3046
3047     }
3048
3049     /* Use long way over the stack */
3050     oper (flags, val, ops);
3051 }
3052
3053
3054
3055 void g_neg (unsigned flags)
3056 /* Primary = -Primary */
3057 {
3058     switch (flags & CF_TYPE) {
3059
3060         case CF_CHAR:
3061         case CF_INT:
3062             AddCodeLine ("jsr negax");
3063             break;
3064
3065         case CF_LONG:
3066             AddCodeLine ("jsr negeax");
3067             break;
3068
3069         default:
3070             typeerror (flags);
3071     }
3072 }
3073
3074
3075
3076 void g_bneg (unsigned flags)
3077 /* Primary = !Primary */
3078 {
3079     switch (flags & CF_TYPE) {
3080
3081         case CF_CHAR:
3082             AddCodeLine ("jsr bnega");
3083             break;
3084
3085         case CF_INT:
3086             AddCodeLine ("jsr bnegax");
3087             break;
3088
3089         case CF_LONG:
3090             AddCodeLine ("jsr bnegeax");
3091             break;
3092
3093         default:
3094             typeerror (flags);
3095     }
3096 }
3097
3098
3099
3100 void g_com (unsigned flags)
3101 /* Primary = ~Primary */
3102 {
3103     switch (flags & CF_TYPE) {
3104
3105         case CF_CHAR:
3106         case CF_INT:
3107             AddCodeLine ("jsr complax");
3108             break;
3109
3110         case CF_LONG:
3111             AddCodeLine ("jsr compleax");
3112             break;
3113
3114         default:
3115             typeerror (flags);
3116     }
3117 }
3118
3119
3120
3121 void g_inc (unsigned flags, unsigned long val)
3122 /* Increment the primary register by a given number */
3123 {
3124     /* Don't inc by zero */
3125     if (val == 0) {
3126         return;
3127     }
3128
3129     /* Generate code for the supported types */
3130     flags &= ~CF_CONST;
3131     switch (flags & CF_TYPE) {
3132
3133         case CF_CHAR:
3134             if (flags & CF_FORCECHAR) {
3135                 if (CPU == CPU_65C02 && val <= 2) {
3136                     while (val--) {
3137                         AddCodeLine ("ina");
3138                     }
3139                 } else {
3140                     AddCodeLine ("clc");
3141                     AddCodeLine ("adc #$%02X", (unsigned char)val);
3142                 }
3143                 break;
3144             }
3145             /* FALLTHROUGH */
3146
3147         case CF_INT:
3148             if (CPU == CPU_65C02 && val == 1) {
3149                 AddCodeLine ("ina");
3150                 AddCodeLine ("bne *+3");
3151                 AddCodeLine ("inx");
3152                 /* Tell the optimizer that the X register may be invalid */
3153                 AddCodeHint ("x:!");
3154             } else if (CodeSizeFactor < 200) {
3155                 /* Use jsr calls */
3156                 if (val <= 8) {
3157                     AddCodeLine ("jsr incax%lu", val);
3158                 } else if (val <= 255) {
3159                     ldyconst (val);
3160                     AddCodeLine ("jsr incaxy");
3161                 } else {
3162                     g_add (flags | CF_CONST, val);
3163                 }
3164             } else {
3165                 /* Inline the code */
3166                 if (val < 0x300) {
3167                     if ((val & 0xFF) != 0) {
3168                         AddCodeLine ("clc");
3169                         AddCodeLine ("adc #$%02X", (unsigned char) val);
3170                         AddCodeLine ("bcc *+3");
3171                         AddCodeLine ("inx");
3172                         /* Tell the optimizer that the X register may be invalid */
3173                         AddCodeHint ("x:!");
3174                     }
3175                     if (val >= 0x100) {
3176                         AddCodeLine ("inx");
3177                     }
3178                     if (val >= 0x200) {
3179                         AddCodeLine ("inx");
3180                     }
3181                 } else {
3182                     AddCodeLine ("clc");
3183                     if ((val & 0xFF) != 0) {
3184                         AddCodeLine ("adc #$%02X", (unsigned char) val);
3185                         /* Tell the optimizer that the X register may be invalid */
3186                         AddCodeHint ("x:!");
3187                     }
3188                     AddCodeLine ("pha");
3189                     AddCodeLine ("txa");
3190                     AddCodeLine ("adc #$%02X", (unsigned char) (val >> 8));
3191                     AddCodeLine ("tax");
3192                     AddCodeLine ("pla");
3193                 }
3194             }
3195             break;
3196
3197         case CF_LONG:
3198             if (val <= 255) {
3199                 ldyconst (val);
3200                 AddCodeLine ("jsr inceaxy");
3201             } else {
3202                 g_add (flags | CF_CONST, val);
3203             }
3204             break;
3205
3206         default:
3207             typeerror (flags);
3208
3209     }
3210 }
3211
3212
3213
3214 void g_dec (unsigned flags, unsigned long val)
3215 /* Decrement the primary register by a given number */
3216 {
3217     /* Don't dec by zero */
3218     if (val == 0) {
3219         return;
3220     }
3221
3222     /* Generate code for the supported types */
3223     flags &= ~CF_CONST;
3224     switch (flags & CF_TYPE) {
3225
3226         case CF_CHAR:
3227             if (flags & CF_FORCECHAR) {
3228                 if (CPU == CPU_65C02 && val <= 2) {
3229                     while (val--) {
3230                         AddCodeLine ("dea");
3231                     }
3232                 } else {
3233                     AddCodeLine ("sec");
3234                     AddCodeLine ("sbc #$%02X", (unsigned char)val);
3235                 }
3236                 break;
3237             }
3238             /* FALLTHROUGH */
3239
3240         case CF_INT:
3241             if (CodeSizeFactor < 200) {
3242                 /* Use subroutines */
3243                 if (val <= 8) {
3244                     AddCodeLine ("jsr decax%d", (int) val);
3245                 } else if (val <= 255) {
3246                     ldyconst (val);
3247                     AddCodeLine ("jsr decaxy");
3248                 } else {
3249                     g_sub (flags | CF_CONST, val);
3250                 }
3251             } else {
3252                 /* Inline the code */
3253                 if (val < 0x300) {
3254                     if ((val & 0xFF) != 0) {
3255                         AddCodeLine ("sec");
3256                         AddCodeLine ("sbc #$%02X", (unsigned char) val);
3257                         AddCodeLine ("bcs *+3");
3258                         AddCodeLine ("dex");
3259                         /* Tell the optimizer that the X register may be invalid */
3260                         AddCodeHint ("x:!");
3261                     }
3262                     if (val >= 0x100) {
3263                         AddCodeLine ("dex");
3264                     }
3265                     if (val >= 0x200) {
3266                         AddCodeLine ("dex");
3267                     }
3268                 } else {
3269                     AddCodeLine ("sec");
3270                     if ((val & 0xFF) != 0) {
3271                         AddCodeLine ("sbc #$%02X", (unsigned char) val);
3272                         /* Tell the optimizer that the X register may be invalid */
3273                         AddCodeHint ("x:!");
3274                     }
3275                     AddCodeLine ("pha");
3276                     AddCodeLine ("txa");
3277                     AddCodeLine ("sbc #$%02X", (unsigned char) (val >> 8));
3278                     AddCodeLine ("tax");
3279                     AddCodeLine ("pla");
3280                 }
3281             }
3282             break;
3283
3284         case CF_LONG:
3285             if (val <= 255) {
3286                 ldyconst (val);
3287                 AddCodeLine ("jsr deceaxy");
3288             } else {
3289                 g_sub (flags | CF_CONST, val);
3290             }
3291             break;
3292
3293         default:
3294             typeerror (flags);
3295
3296     }
3297 }
3298
3299
3300
3301 /*
3302  * Following are the conditional operators. They compare the TOS against
3303  * the primary and put a literal 1 in the primary if the condition is
3304  * true, otherwise they clear the primary register
3305  */
3306
3307
3308
3309 void g_eq (unsigned flags, unsigned long val)
3310 /* Test for equal */
3311 {
3312     static char* ops [12] = {
3313         "toseq00",      "toseqa0",      "toseqax",
3314         "toseq00",      "toseqa0",      "toseqax",
3315         0,              0,              "toseqeax",
3316         0,              0,              "toseqeax",
3317     };
3318
3319     /* If the right hand side is const, the lhs is not on stack but still
3320      * in the primary register.
3321      */
3322     if (flags & CF_CONST) {
3323
3324         switch (flags & CF_TYPE) {
3325
3326             case CF_CHAR:
3327                 if (flags & CF_FORCECHAR) {
3328                     AddCodeLine ("cmp #$%02X", (unsigned char)val);
3329                     AddCodeLine ("jsr booleq");
3330                     return;
3331                 }
3332                 /* FALLTHROUGH */
3333
3334             case CF_INT:
3335                 AddCodeLine ("cpx #$%02X", (unsigned char)(val >> 8));
3336                 AddCodeLine ("bne *+4");
3337                 AddCodeLine ("cmp #$%02X", (unsigned char)val);
3338                 AddCodeLine ("jsr booleq");
3339                 return;
3340
3341             case CF_LONG:
3342                 break;
3343
3344             default:
3345                 typeerror (flags);
3346         }
3347
3348         /* If we go here, we didn't emit code. Push the lhs on stack and fall
3349          * into the normal, non-optimized stuff.
3350          */
3351         g_push (flags & ~CF_CONST, 0);
3352
3353     }
3354
3355     /* Use long way over the stack */
3356     oper (flags, val, ops);
3357 }
3358
3359
3360
3361 void g_ne (unsigned flags, unsigned long val)
3362 /* Test for not equal */
3363 {
3364     static char* ops [12] = {
3365         "tosne00",      "tosnea0",      "tosneax",
3366         "tosne00",      "tosnea0",      "tosneax",
3367         0,              0,              "tosneeax",
3368         0,              0,              "tosneeax",
3369     };
3370
3371
3372     /* If the right hand side is const, the lhs is not on stack but still
3373      * in the primary register.
3374      */
3375     if (flags & CF_CONST) {
3376
3377         switch (flags & CF_TYPE) {
3378
3379             case CF_CHAR:
3380                 if (flags & CF_FORCECHAR) {
3381                     AddCodeLine ("cmp #$%02X", (unsigned char)val);
3382                     AddCodeLine ("jsr boolne");
3383                     return;
3384                 }
3385                 /* FALLTHROUGH */
3386
3387             case CF_INT:
3388                 AddCodeLine ("cpx #$%02X", (unsigned char)(val >> 8));
3389                 AddCodeLine ("bne *+4");
3390                 AddCodeLine ("cmp #$%02X", (unsigned char)val);
3391                 AddCodeLine ("jsr boolne");
3392                 return;
3393
3394             case CF_LONG:
3395                 break;
3396
3397             default:
3398                 typeerror (flags);
3399         }
3400
3401         /* If we go here, we didn't emit code. Push the lhs on stack and fall
3402          * into the normal, non-optimized stuff.
3403          */
3404         g_push (flags & ~CF_CONST, 0);
3405
3406     }
3407
3408     /* Use long way over the stack */
3409     oper (flags, val, ops);
3410 }
3411
3412
3413
3414 void g_lt (unsigned flags, unsigned long val)
3415 /* Test for less than */
3416 {
3417     static char* ops [12] = {
3418         "toslt00",      "toslta0",      "tosltax",
3419         "tosult00",     "tosulta0",     "tosultax",
3420         0,              0,              "toslteax",
3421         0,              0,              "tosulteax",
3422     };
3423
3424     /* If the right hand side is const, the lhs is not on stack but still
3425      * in the primary register.
3426      */
3427     if (flags & CF_CONST) {
3428
3429         /* Give a warning in some special cases */
3430         if ((flags & CF_UNSIGNED) && val == 0) {
3431             Warning ("Condition is never true");
3432         }
3433
3434         /* Look at the type */
3435         switch (flags & CF_TYPE) {
3436
3437             case CF_CHAR:
3438                 if (flags & CF_FORCECHAR) {
3439                     AddCodeLine ("cmp #$%02X", (unsigned char)val);
3440                     if (flags & CF_UNSIGNED) {
3441                         AddCodeLine ("jsr boolult");
3442                     } else {
3443                         AddCodeLine ("jsr boollt");
3444                     }
3445                     return;
3446                 }
3447                 /* FALLTHROUGH */
3448
3449             case CF_INT:
3450                 if ((flags & CF_UNSIGNED) == 0 && val == 0) {
3451                     /* If we have a signed compare against zero, we only need to
3452                      * test the high byte.
3453                      */
3454                     AddCodeLine ("txa");
3455                     AddCodeLine ("jsr boollt");
3456                     return;
3457                 }
3458                 /* Direct code only for unsigned data types */
3459                 if (flags & CF_UNSIGNED) {
3460                     AddCodeLine ("cpx #$%02X", (unsigned char)(val >> 8));
3461                     AddCodeLine ("bne *+4");
3462                     AddCodeLine ("cmp #$%02X", (unsigned char)val);
3463                     AddCodeLine ("jsr boolult");
3464                     return;
3465                 }
3466                 break;
3467
3468             case CF_LONG:
3469                 if ((flags & CF_UNSIGNED) == 0 && val == 0) {
3470                     /* If we have a signed compare against zero, we only need to
3471                      * test the high byte.
3472                      */
3473                     AddCodeLine ("lda sreg+1");
3474                     AddCodeLine ("jsr boollt");
3475                     return;
3476                 }
3477                 break;
3478
3479             default:
3480                 typeerror (flags);
3481         }
3482
3483         /* If we go here, we didn't emit code. Push the lhs on stack and fall
3484          * into the normal, non-optimized stuff.
3485          */
3486         g_push (flags & ~CF_CONST, 0);      
3487
3488     }
3489
3490     /* Use long way over the stack */
3491     oper (flags, val, ops);
3492 }
3493
3494
3495
3496 void g_le (unsigned flags, unsigned long val)
3497 /* Test for less than or equal to */
3498 {
3499     static char* ops [12] = {
3500         "tosle00",      "toslea0",      "tosleax",
3501         "tosule00",     "tosulea0",     "tosuleax",
3502         0,              0,              "tosleeax",
3503         0,              0,              "tosuleeax",
3504     };
3505
3506
3507     /* If the right hand side is const, the lhs is not on stack but still
3508      * in the primary register.
3509      */
3510     if (flags & CF_CONST) {
3511
3512         /* Look at the type */
3513         switch (flags & CF_TYPE) {
3514
3515             case CF_CHAR:
3516                 if (flags & CF_FORCECHAR) {
3517                     AddCodeLine ("cmp #$%02X", (unsigned char)val);
3518                     if (flags & CF_UNSIGNED) {
3519                         AddCodeLine ("jsr boolule");
3520                     } else {
3521                         AddCodeLine ("jsr boolle");
3522                     }
3523                     return;
3524                 }
3525                 /* FALLTHROUGH */
3526
3527             case CF_INT:
3528                 if (flags & CF_UNSIGNED) {
3529                     AddCodeLine ("cpx #$%02X", (unsigned char)(val >> 8));
3530                     AddCodeLine ("bne *+4");
3531                     AddCodeLine ("cmp #$%02X", (unsigned char)val);
3532                     AddCodeLine ("jsr boolule");
3533                     return;
3534                 }
3535                 break;
3536
3537             case CF_LONG:
3538                 break;
3539
3540             default:
3541                 typeerror (flags);
3542         }
3543
3544         /* If we go here, we didn't emit code. Push the lhs on stack and fall
3545          * into the normal, non-optimized stuff.
3546          */
3547         g_push (flags & ~CF_CONST, 0);
3548
3549     }
3550
3551     /* Use long way over the stack */
3552     oper (flags, val, ops);
3553 }
3554
3555
3556
3557 void g_gt (unsigned flags, unsigned long val)
3558 /* Test for greater than */
3559 {
3560     static char* ops [12] = {
3561         "tosgt00",      "tosgta0",      "tosgtax",
3562         "tosugt00",     "tosugta0",     "tosugtax",
3563         0,              0,              "tosgteax",
3564         0,              0,              "tosugteax",
3565     };
3566
3567
3568     /* If the right hand side is const, the lhs is not on stack but still
3569      * in the primary register.
3570      */
3571     if (flags & CF_CONST) {
3572
3573         /* Look at the type */
3574         switch (flags & CF_TYPE) {
3575
3576             case CF_CHAR:
3577                 if (flags & CF_FORCECHAR) {
3578                     AddCodeLine ("cmp #$%02X", (unsigned char)val);
3579                     if (flags & CF_UNSIGNED) {
3580                         /* If we have a compare > 0, we will replace it by
3581                          * != 0 here, since both are identical but the latter
3582                          * is easier to optimize.
3583                          */
3584                         if (val & 0xFF) {
3585                             AddCodeLine ("jsr boolugt");
3586                         } else {
3587                             AddCodeLine ("jsr boolne");
3588                         }
3589                     } else {
3590                         AddCodeLine ("jsr boolgt");
3591                     }
3592                     return;
3593                 }
3594                 /* FALLTHROUGH */
3595
3596             case CF_INT:
3597                 if (flags & CF_UNSIGNED) {
3598                     /* If we have a compare > 0, we will replace it by
3599                      * != 0 here, since both are identical but the latter
3600                      * is easier to optimize.
3601                      */
3602                     if ((val & 0xFFFF) == 0) {
3603                         AddCodeLine ("stx tmp1");
3604                         AddCodeLine ("ora tmp1");
3605                         AddCodeLine ("jsr boolne");
3606                     } else {
3607                         AddCodeLine ("cpx #$%02X", (unsigned char)(val >> 8));
3608                         AddCodeLine ("bne *+4");
3609                         AddCodeLine ("cmp #$%02X", (unsigned char)val);
3610                         AddCodeLine ("jsr boolugt");
3611                     }
3612                     return;
3613                 }
3614                 break;
3615
3616             case CF_LONG:
3617                 break;
3618
3619             default:
3620                 typeerror (flags);
3621         }
3622
3623         /* If we go here, we didn't emit code. Push the lhs on stack and fall
3624          * into the normal, non-optimized stuff.
3625          */
3626         g_push (flags & ~CF_CONST, 0);
3627
3628     }
3629
3630     /* Use long way over the stack */
3631     oper (flags, val, ops);
3632 }
3633
3634
3635
3636 void g_ge (unsigned flags, unsigned long val)
3637 /* Test for greater than or equal to */
3638 {
3639     static char* ops [12] = {
3640         "tosge00",      "tosgea0",      "tosgeax",
3641         "tosuge00",     "tosugea0",     "tosugeax",
3642         0,              0,              "tosgeeax",
3643         0,              0,              "tosugeeax",
3644     };
3645
3646
3647     /* If the right hand side is const, the lhs is not on stack but still
3648      * in the primary register.
3649      */
3650     if (flags & CF_CONST) {
3651
3652         /* Give a warning in some special cases */
3653         if ((flags & CF_UNSIGNED) && val == 0) {
3654             Warning ("Condition is always true");
3655         }
3656
3657         /* Look at the type */
3658         switch (flags & CF_TYPE) {
3659
3660             case CF_CHAR:
3661                 if (flags & CF_FORCECHAR) {
3662                     AddCodeLine ("cmp #$%02X", (unsigned char)val);
3663                     if (flags & CF_UNSIGNED) {
3664                         AddCodeLine ("jsr booluge");
3665                     } else {
3666                         AddCodeLine ("jsr boolge");
3667                     }
3668                     return;
3669                 }
3670                 /* FALLTHROUGH */
3671
3672             case CF_INT:
3673                 if ((flags & CF_UNSIGNED) == 0 && val == 0) {
3674                     /* If we have a signed compare against zero, we only need to
3675                      * test the high byte.
3676                      */
3677                     AddCodeLine ("txa");
3678                     AddCodeLine ("jsr boolge");
3679                     return;
3680                 }
3681                 /* Direct code only for unsigned data types */
3682                 if (flags & CF_UNSIGNED) {
3683                     AddCodeLine ("cpx #$%02X", (unsigned char)(val >> 8));
3684                     AddCodeLine ("bne *+4");
3685                     AddCodeLine ("cmp #$%02X", (unsigned char)val);
3686                     AddCodeLine ("jsr booluge");
3687                     return;
3688                 }
3689                 break;
3690
3691             case CF_LONG:
3692                 if ((flags & CF_UNSIGNED) == 0 && val == 0) {
3693                     /* If we have a signed compare against zero, we only need to
3694                      * test the high byte.
3695                      */
3696                     AddCodeLine ("lda sreg+1");
3697                     AddCodeLine ("jsr boolge");
3698                     return;
3699                 }
3700                 break;
3701
3702             default:
3703                 typeerror (flags);
3704         }
3705
3706         /* If we go here, we didn't emit code. Push the lhs on stack and fall
3707          * into the normal, non-optimized stuff.
3708          */
3709         g_push (flags & ~CF_CONST, 0);
3710
3711     }
3712
3713     /* Use long way over the stack */
3714     oper (flags, val, ops);
3715 }
3716
3717
3718
3719 /*****************************************************************************/
3720 /*                         Allocating static storage                         */
3721 /*****************************************************************************/
3722
3723
3724
3725 void g_res (unsigned n)
3726 /* Reserve static storage, n bytes */
3727 {
3728     AddDataLine ("\t.res\t%u,$00", n);
3729 }
3730
3731
3732
3733 void g_defdata (unsigned flags, unsigned long val, unsigned offs)
3734 /* Define data with the size given in flags */
3735 {
3736     if (flags & CF_CONST) {
3737
3738         /* Numeric constant */
3739         switch (flags & CF_TYPE) {
3740
3741             case CF_CHAR:
3742                 AddDataLine ("\t.byte\t$%02lX", val & 0xFF);
3743                 break;
3744
3745             case CF_INT:
3746                 AddDataLine ("\t.word\t$%04lX", val & 0xFFFF);
3747                 break;
3748
3749             case CF_LONG:
3750                 AddDataLine ("\t.dword\t$%08lX", val & 0xFFFFFFFF);
3751                 break;
3752
3753             default:
3754                 typeerror (flags);
3755                 break;
3756
3757         }
3758
3759     } else {
3760
3761         /* Create the correct label name */
3762         const char* Label = GetLabelName (flags, val, offs);
3763
3764         /* Labels are always 16 bit */
3765         AddDataLine ("\t.word\t%s", Label);
3766
3767     }
3768 }
3769
3770
3771
3772 void g_defbytes (const void* Bytes, unsigned Count)
3773 /* Output a row of bytes as a constant */
3774 {
3775     unsigned Chunk;
3776     char Buf [128];
3777     char* B;
3778
3779     /* Cast the buffer pointer */
3780     const unsigned char* Data = (const unsigned char*) Bytes;
3781
3782     /* Output the stuff */
3783     while (Count) {
3784
3785         /* How many go into this line? */
3786         if ((Chunk = Count) > 16) {
3787             Chunk = 16;
3788         }
3789         Count -= Chunk;
3790
3791         /* Output one line */
3792         strcpy (Buf, "\t.byte\t");
3793         B = Buf + 7;
3794         do {
3795             B += sprintf (B, "$%02X", *Data++);
3796             if (--Chunk) {
3797                 *B++ = ',';
3798             }
3799         } while (Chunk);
3800
3801         /* Output the line */
3802         AddDataLine (Buf);
3803     }
3804 }
3805
3806
3807
3808 void g_zerobytes (unsigned n)
3809 /* Output n bytes of data initialized with zero */
3810 {
3811     AddDataLine ("\t.res\t%u,$00", n);
3812 }
3813
3814
3815
3816 /*****************************************************************************/
3817 /*                       User supplied assembler code                        */
3818 /*****************************************************************************/
3819
3820
3821
3822 void g_asmcode (const char* Line, int Len)
3823 /* Output one line of assembler code. If Len is greater than zero, it is used
3824  * as the maximum number of characters to use from Line.
3825  */
3826 {
3827     if (Len >= 0) {
3828         AddCodeLine ("%.*s", Len, Line);
3829     } else {
3830         AddCodeLine ("%s", Line);
3831     }
3832 }
3833
3834
3835
3836 /*****************************************************************************/
3837 /*                          Inlined known functions                          */
3838 /*****************************************************************************/
3839
3840
3841
3842 void g_strlen (unsigned flags, unsigned long val, unsigned offs)
3843 /* Inline the strlen() function */
3844 {
3845     /* We need a label in both cases */
3846     unsigned label = GetLocalLabel ();
3847
3848     /* Two different encodings */
3849     if (flags & CF_CONST) {
3850
3851         /* The address of the string is constant. Create the correct label name */
3852         char* lbuf = GetLabelName (flags, val, offs);
3853
3854         /* Generate the strlen code */
3855         AddCodeLine ("ldy #$FF");
3856         g_defcodelabel (label);
3857         AddCodeLine ("iny");
3858         AddCodeLine ("lda %s,y", lbuf);
3859         AddCodeLine ("bne %s", LocalLabelName (label));
3860         AddCodeLine ("tax");
3861         AddCodeLine ("tya");
3862
3863     } else {
3864
3865         /* Address not constant but in primary */
3866         if (CodeSizeFactor < 400) {
3867             /* This is too much code, so call strlen instead of inlining */
3868             AddCodeLine ("jsr _strlen");
3869         } else {
3870             /* Inline the function */
3871             AddCodeLine ("sta ptr1");
3872             AddCodeLine ("stx ptr1+1");
3873             AddCodeLine ("ldy #$FF");
3874             g_defcodelabel (label);
3875             AddCodeLine ("iny");
3876             AddCodeLine ("lda (ptr1),y");
3877             AddCodeLine ("bne %s", LocalLabelName (label));
3878             AddCodeLine ("tax");
3879             AddCodeLine ("tya");
3880         }
3881     }
3882 }
3883
3884
3885