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