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