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