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