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