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