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