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