]> git.sur5r.net Git - cc65/blob - src/ca65/objcode.c
a7e98472457b0e45afc24f2fd3097543190cc69c
[cc65] / src / ca65 / objcode.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                 objcode.c                                 */
4 /*                                                                           */
5 /*             Objectcode management for the ca65 macroassembler             */
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 <string.h>
37 #include <errno.h>
38
39 /* common */
40 #include "chartype.h"
41 #include "check.h"
42 #include "segdefs.h"
43 #include "xmalloc.h"
44
45 /* cc65 */
46 #include "error.h"
47 #include "fragment.h"
48 #include "global.h"
49 #include "lineinfo.h"
50 #include "listing.h"
51 #include "objfile.h"
52 #include "scanner.h"
53 #include "symtab.h"
54 #include "objcode.h"
55
56
57
58 /*****************************************************************************/
59 /*                                   Data                                    */
60 /*****************************************************************************/
61
62
63
64 /* Are we in absolute mode or in relocatable mode? */
65 int             RelocMode = 1;
66 unsigned long   AbsPC     = 0;          /* PC if in absolute mode */
67
68
69
70 typedef struct Segment_ Segment;
71 struct Segment_ {
72     Segment*        List;               /* List of all segments */
73     Fragment*       Root;               /* Root of fragment list */
74     Fragment*       Last;               /* Pointer to last fragment */
75     unsigned char   Align;              /* Segment alignment */
76     unsigned char   SegType;            /* True if zero page segment */
77     unsigned long   PC;
78     unsigned        Num;                /* Segment number */
79     char*           Name;               /* Segment name */
80 };
81
82
83
84 /* Predefined segments */
85 static Segment NullSeg = {
86     0, 0, 0, 0, SEGTYPE_ABS, 0, 5, "NULL"
87 };
88 static Segment ZeropageSeg = {
89     &NullSeg, 0, 0, 0, SEGTYPE_ZP, 0, 4, "ZEROPAGE"
90 };
91 static Segment DataSeg = {
92     &ZeropageSeg, 0, 0, 0, SEGTYPE_ABS, 0, 3, "DATA"
93 };
94 static Segment BssSeg = {
95     &DataSeg, 0, 0, 0, SEGTYPE_ABS, 0, 2, "BSS"
96 };
97 static Segment RODataSeg = {
98     &BssSeg, 0, 0, 0, SEGTYPE_ABS, 0, 1, "RODATA"
99 };
100 static Segment CodeSeg = {
101     &RODataSeg, 0, 0, 0, SEGTYPE_ABS, 0, 0, "CODE"
102 };
103
104 /* Number of segments */
105 static unsigned SegmentCount = 6;
106
107 /* List of all segments */
108 static Segment* SegmentList = &CodeSeg;
109 static Segment* SegmentLast = &NullSeg;
110
111 /* Currently active segment */
112 static Segment* ActiveSeg = &CodeSeg;
113
114
115
116 /*****************************************************************************/
117 /*                            Segment management                             */
118 /*****************************************************************************/
119
120
121
122 static Segment* NewSegment (const char* Name, unsigned SegType)
123 /* Create a new segment, insert it into the global list and return it */
124 {
125     Segment* S;
126     const char* N;
127
128     /* Check for too many segments */
129     if (SegmentCount >= 256) {
130         Fatal (FAT_TOO_MANY_SEGMENTS);
131     }
132
133     /* Check the segment name for invalid names */
134     N = Name;
135     if ((*N != '_' && !IsAlpha (*N)) || strlen (Name) > 80) {
136         Error (ERR_ILLEGAL_SEGMENT, Name);
137     }
138     do {
139         if (*N != '_' && !IsAlNum (*N)) {
140             Error (ERR_ILLEGAL_SEGMENT, Name);
141             break;
142         }
143         ++N;
144     } while (*N);
145
146     /* Create a new segment */
147     S = xmalloc (sizeof (*S));
148
149     /* Initialize it */
150     S->List    = 0;
151     S->Root    = 0;
152     S->Last    = 0;
153     S->Align   = 0;
154     S->SegType = SegType;
155     S->PC      = 0;
156     S->Num     = SegmentCount++;
157     S->Name    = xstrdup (Name);
158
159     /* Insert it into the segment list */
160     SegmentLast->List = S;
161     SegmentLast = S;
162
163     /* And return it... */
164     return S;
165 }
166
167
168
169 void UseCodeSeg (void)
170 /* Use the code segment */
171 {
172     ActiveSeg = &CodeSeg;
173 }
174
175
176
177 void UseRODataSeg (void)
178 /* Use the r/o data segment */
179 {
180     ActiveSeg = &RODataSeg;
181 }
182
183
184
185 void UseDataSeg (void)
186 /* Use the data segment */
187 {
188     ActiveSeg = &DataSeg;
189 }
190
191
192
193 void UseBssSeg (void)
194 /* Use the BSS segment */
195 {
196     ActiveSeg = &BssSeg;
197 }
198
199
200
201 void UseZeropageSeg (void)
202 /* Use the zero page segment */
203 {
204     ActiveSeg = &ZeropageSeg;
205 }
206
207
208
209 void UseNullSeg (void)
210 /* Use the null segment */
211 {
212     ActiveSeg = &NullSeg;
213 }
214
215
216
217 void UseSeg (const char* Name, unsigned SegType)
218 /* Use the segment with the given name */
219 {
220     Segment* Seg = SegmentList;
221     while (Seg) {
222         if (strcmp (Seg->Name, Name) == 0) {
223             /* We found this segment. Check if the type is identical */
224             if (SegType != SEGTYPE_DEFAULT && Seg->SegType != SegType) {
225                 Error (ERR_SEG_ATTR_MISMATCH);
226                 /* Use the new attribute to avoid errors */
227                 Seg->SegType = SegType;
228             }
229             ActiveSeg = Seg;
230             return;
231         }
232         /* Check next segment */
233         Seg = Seg->List;
234     }
235
236     /* Segment is not in list, create a new one */
237     if (SegType == SEGTYPE_DEFAULT) {
238         SegType = SEGTYPE_ABS;
239     }
240     Seg = NewSegment (Name, SegType);
241     ActiveSeg = Seg;
242 }
243
244
245
246 unsigned long GetPC (void)
247 /* Get the program counter of the current segment */
248 {
249     return RelocMode? ActiveSeg->PC : AbsPC;
250 }
251
252
253
254 void SetAbsPC (unsigned long PC)
255 /* Set the program counter in absolute mode */
256 {
257     RelocMode = 0;
258     AbsPC = PC;
259 }
260
261
262
263 unsigned GetSegNum (void)
264 /* Get the number of the current segment */
265 {
266     return ActiveSeg->Num;
267 }
268
269
270
271 void SegAlign (unsigned Power, int Val)
272 /* Align the PC segment to 2^Power. If Val is -1, emit fill fragments (the
273  * actual fill value will be determined by the linker), otherwise use the
274  * given value.
275  */
276 {
277     unsigned char Data [4];
278     unsigned long Align = (1UL << Power) - 1;
279     unsigned long NewPC = (ActiveSeg->PC + Align) & ~Align;
280     unsigned long Count = NewPC - ActiveSeg->PC;
281
282     if (Val != -1) {
283         /* User defined fill value */
284         memset (Data, Val, sizeof (Data));
285         while (Count) {
286             if (Count > sizeof (Data)) {
287                 EmitData (Data, sizeof (Data));
288                 Count -= sizeof (Data);
289             } else {
290                 EmitData (Data, Count);
291                 Count = 0;
292             }
293         }
294     } else {
295         /* Linker defined fill value */
296         EmitFill (Count);
297     }
298
299     /* Remember the alignment in the header */
300     if (ActiveSeg->Align < Power) {
301         ActiveSeg->Align = Power;
302     }
303 }
304
305
306
307 int IsZPSeg (void)
308 /* Return true if the current segment is a zeropage segment */
309 {
310     return (ActiveSeg->SegType == SEGTYPE_ZP);
311 }
312
313
314
315 int IsFarSeg (void)
316 /* Return true if the current segment is a far segment */
317 {
318     return (ActiveSeg->SegType == SEGTYPE_FAR);
319 }
320
321
322
323 unsigned GetSegType (unsigned SegNum)
324 /* Return the type of the segment with the given number */
325 {
326     /* Search for the segment */
327     Segment* S = SegmentList;
328     while (S && SegNum) {
329         --SegNum;
330         S = S->List;
331     }
332
333     /* Did we find it? */
334     if (S == 0) {
335         FAIL ("Invalid segment number");
336     }
337
338     /* Return the segment type */
339     return S->SegType;
340 }
341
342
343
344 void SegCheck (void)
345 /* Check the segments for range and other errors */
346 {
347     Segment* S = SegmentList;
348     while (S) {
349         Fragment* F = S->Root;
350         while (F) {
351             if (F->Type == FRAG_EXPR || F->Type == FRAG_SEXPR) {
352                 F->V.Expr = FinalizeExpr (F->V.Expr);
353                 if (IsConstExpr (F->V.Expr)) {
354                     /* We are able to evaluate the expression. Get the value
355                      * and check for range errors.
356                      */
357                     unsigned I;
358                     long Val = GetExprVal (F->V.Expr);
359                     int Abs = (F->Type != FRAG_SEXPR);
360
361                     if (F->Len == 1) {
362                         if (Abs) {
363                             /* Absolute value */
364                             if (Val > 255) {
365                                 PError (&F->Pos, ERR_RANGE);
366                             }
367                         } else {
368                             /* PC relative value */
369                             if (Val < -128 || Val > 127) {
370                                 PError (&F->Pos, ERR_RANGE);
371                             }
372                         }
373                     } else if (F->Len == 2) {
374                         if (Abs) {
375                             /* Absolute value */
376                             if (Val > 65535) {
377                                 PError (&F->Pos, ERR_RANGE);
378                             }
379                         } else {
380                             /* PC relative value */
381                             if (Val < -32768 || Val > 32767) {
382                                 PError (&F->Pos, ERR_RANGE);
383                             }
384                         }
385                     }
386
387                     /* Convert the fragment into a literal fragment */
388                     for (I = 0; I < F->Len; ++I) {
389                         F->V.Data [I] = Val & 0xFF;
390                         Val >>= 8;
391                     }
392                     F->Type = FRAG_LITERAL;
393                 } else {
394                     /* We cannot evaluate the expression now, leave the job for
395                      * the linker. However, we are able to check for explicit
396                      * byte expressions and we will do so.
397                      */
398                     if (F->Type == FRAG_EXPR && F->Len == 1 && !IsByteExpr (F->V.Expr)) {
399                         PError (&F->Pos, ERR_RANGE);
400                     }
401                 }
402             }
403             F = F->Next;
404         }
405         S = S->List;
406     }
407 }
408
409
410
411 void SegDump (void)
412 /* Dump the contents of all segments */
413 {
414     unsigned X = 0;
415     Segment* S = SegmentList;
416     printf ("\n");
417     while (S) {
418         unsigned I;
419         Fragment* F;
420         int State = -1;
421         printf ("New segment: %s", S->Name);
422         F = S->Root;
423         while (F) {
424             if (F->Type == FRAG_LITERAL) {
425                 if (State != 0) {
426                     printf ("\n  Literal:");
427                     X = 15;
428                     State = 0;
429                 }
430                 for (I = 0; I < F->Len; ++I) {
431                     printf (" %02X", F->V.Data [I]);
432                     X += 3;
433                 }
434             } else if (F->Type == FRAG_EXPR || F->Type == FRAG_SEXPR) {
435                 State = 1;
436                 printf ("\n  Expression (%u): ", F->Len);
437                 DumpExpr (F->V.Expr);
438             } else if (F->Type == FRAG_FILL) {
439                 State = 1;
440                 printf ("\n  Fill bytes (%u)", F->Len);
441             } else {
442                 Internal ("Unknown fragment type: %u", F->Type);
443             }
444             if (X > 65) {
445                 State = -1;
446             }
447             F = F->Next;
448         }
449         printf ("\n  End PC = $%04X\n", (unsigned)(S->PC & 0xFFFF));
450         S = S->List;
451     }
452     printf ("\n");
453 }
454
455
456
457 static void WriteOneSeg (Segment* Seg)
458 /* Write one segment to the object file */
459 {
460     Fragment* Frag;
461     unsigned LineInfoIndex;
462
463     /* Write the segment name followed by the byte count in this segment */
464     ObjWriteStr (Seg->Name);
465     ObjWrite32 (Seg->PC);
466     ObjWrite8 (Seg->Align);
467     ObjWrite8 (Seg->SegType);
468
469     /* Now walk through the fragment list for this segment and write the
470      * fragments.
471      */
472     Frag = Seg->Root;
473     while (Frag) {
474
475         /* Write data depending on the type */
476         switch (Frag->Type) {
477
478             case FRAG_LITERAL:
479                 ObjWrite8 (FRAG_LITERAL);
480                 ObjWriteVar (Frag->Len);
481                 ObjWriteData (Frag->V.Data, Frag->Len);
482                 break;
483
484             case FRAG_EXPR:
485                 switch (Frag->Len) {
486                     case 1:   ObjWrite8 (FRAG_EXPR8);   break;
487                     case 2:   ObjWrite8 (FRAG_EXPR16);  break;
488                     case 3:   ObjWrite8 (FRAG_EXPR24);  break;
489                     case 4:   ObjWrite8 (FRAG_EXPR32);  break;
490                     default:  Internal ("Invalid fragment size: %u", Frag->Len);
491                 }
492                 WriteExpr (Frag->V.Expr);
493                 break;
494
495             case FRAG_SEXPR:
496                 switch (Frag->Len) {
497                     case 1:   ObjWrite8 (FRAG_SEXPR8);  break;
498                     case 2:   ObjWrite8 (FRAG_SEXPR16); break;
499                     case 3:   ObjWrite8 (FRAG_SEXPR24); break;
500                     case 4:   ObjWrite8 (FRAG_SEXPR32); break;
501                     default:  Internal ("Invalid fragment size: %u", Frag->Len);
502                 }
503                 WriteExpr (Frag->V.Expr);
504                 break;
505
506             case FRAG_FILL:
507                 ObjWrite8 (FRAG_FILL);
508                 ObjWriteVar (Frag->Len);
509                 break;
510
511             default:
512                 Internal ("Invalid fragment type: %u", Frag->Type);
513
514         }
515
516         /* Write the file position of this fragment */
517         ObjWritePos (&Frag->Pos);
518
519         /* Write extra line info for this fragment. Zero is considered
520          * "no line info", so add one to the value.
521          */
522         LineInfoIndex = Frag->LI? Frag->LI->Index + 1 : 0;
523         ObjWriteVar (LineInfoIndex);
524
525         /* Next fragment */
526         Frag = Frag->Next;
527     }
528 }
529
530
531
532 void WriteSegments (void)
533 /* Write the segment data to the object file */
534 {
535     Segment* Seg;
536
537     /* Tell the object file module that we're about to start the seg list */
538     ObjStartSegments ();
539
540     /* First thing is segment count */
541     ObjWriteVar (SegmentCount);
542
543     /* Now walk through all segments and write them to the object file */
544     Seg = SegmentList;
545     while (Seg) {
546         /* Write one segment */
547         WriteOneSeg (Seg);
548         /* Next segment */
549         Seg = Seg->List;
550     }
551
552     /* Done writing segments */
553     ObjEndSegments ();
554 }
555
556
557
558 /*****************************************************************************/
559 /*                                   Code                                    */
560 /*****************************************************************************/
561
562
563
564 static void IncPC (unsigned Value)
565 /* Increment the PC counter */
566 {
567     ActiveSeg->PC += Value;
568     if (!RelocMode) {
569         AbsPC += Value;
570     }
571 }
572
573
574
575 static Fragment* NewFragment (unsigned char Type, unsigned short Len)
576 /* Create, initialize and return a new fragment. The fragment will be inserted
577  * into the current segment.
578  */
579 {
580     Fragment* F;
581
582     /* Create a new fragment */
583     F = xmalloc (sizeof (*F));
584
585     /* Initialize it */
586     F->List     = 0;
587     F->Next     = 0;
588     F->LineList = 0;
589     F->Pos      = CurPos;
590     F->LI       = UseLineInfo (CurLineInfo);
591     F->Len      = Len;
592     F->Type     = Type;
593
594     /* Insert it into the list of all segments */
595     if (FragList == 0) {
596         FragList = F;
597     } else {
598         FragLast->List = F;
599     }
600     FragLast = F;
601
602     /* Insert it into the current segment */
603     if (ActiveSeg->Root) {
604         ActiveSeg->Last->Next = F;
605         ActiveSeg->Last = F;
606     } else {
607         ActiveSeg->Root = ActiveSeg->Last = F;
608     }
609
610     /* Add this fragment to the current listing line */
611     if (LineCur) {
612         if (LineCur->FragList == 0) {
613             LineCur->FragList = F;
614         } else {
615             LineCur->FragLast->LineList = F;
616         }
617         LineCur->FragLast = F;
618     }
619
620     /* Increment the program counter */
621     IncPC (Len);
622
623     /* And return it */
624     return F;
625 }
626
627
628
629 void Emit0 (unsigned char OPC)
630 /* Emit an instruction with a zero sized operand */
631 {
632     /* First fragment, wrong type or out of space, create new one */
633     Fragment* F = NewFragment (FRAG_LITERAL, 1);
634     F->V.Data [0] = OPC;
635 }
636
637
638
639 void Emit1 (unsigned char OPC, ExprNode* Value)
640 /* Emit an instruction with an one byte argument */
641 {
642     Emit0 (OPC);
643     EmitByte (Value);
644 }
645
646
647
648 void Emit2 (unsigned char OPC, ExprNode* Value)
649 /* Emit an instruction with a two byte argument */
650 {
651     Emit0 (OPC);
652     EmitWord (Value);
653 }
654
655
656
657 void Emit3 (unsigned char OPC, ExprNode* Expr)
658 /* Emit an instruction with a three byte argument */
659 {
660     Emit0 (OPC);
661     EmitFarAddr (Expr);
662 }
663
664
665
666 void Emit3b (unsigned char OPC, ExprNode* Expr, ExprNode* Bank)
667 /* Emit an instruction with a three byte argument and separate bank */
668 {
669     Emit0 (OPC);
670     EmitWord (Expr);
671     EmitByte (Bank);
672 }
673
674
675
676 void EmitPCRel (unsigned char OPC, ExprNode* Expr, unsigned Size)
677 /* Emit an opcode with a PC relative argument of one or two bytes */
678 {
679     Fragment* F;
680     Emit0 (OPC);
681     F = NewFragment (FRAG_SEXPR, Size);
682     F->V.Expr = Expr;
683 }
684
685
686
687 void EmitData (const unsigned char* Data, unsigned Size)
688 /* Emit data into the current segment */
689 {
690     /* Create lots of fragments for the data */
691     while (Size) {
692         Fragment* F;
693
694         /* Determine the length of the next fragment */
695         unsigned Len = Size;
696         if (Len > sizeof (F->V.Data)) {
697             Len = sizeof (F->V.Data);
698         }
699
700         /* Create a new fragment */
701         F = NewFragment (FRAG_LITERAL, Len);
702
703         /* Copy the data */
704         memcpy (F->V.Data, Data, Len);
705
706         /* Next chunk */
707         Data += Len;
708         Size -= Len;
709
710     }
711 }
712
713
714
715 void EmitByte (ExprNode* Expr)
716 /* Emit one byte */
717 {
718     if (IsConstExpr (Expr)) {
719         /* Constant expression, emit literal byte */
720         long Val = GetExprVal (Expr);
721         FreeExpr (Expr);
722         if ((Val & ~0xFF) != 0) {
723             Error (ERR_RANGE);
724         }
725         Emit0 (Val & 0xFF);
726     } else {
727         /* Create a new fragment */
728         Fragment* F = NewFragment (FRAG_EXPR, 1);
729
730         /* Set the data */
731         F->V.Expr = Expr;
732     }
733 }
734
735
736
737 void EmitWord (ExprNode* Expr)
738 /* Emit one word */
739 {
740     if (IsConstExpr (Expr)) {
741         /* Constant expression, emit literal byte */
742         long Val = GetExprVal (Expr);
743         FreeExpr (Expr);
744         if ((Val & ~0xFFFF) != 0) {
745             Error (ERR_RANGE);
746         }
747         Emit0 (Val & 0xFF);
748         Emit0 ((Val >> 8) & 0xFF);
749     } else {
750         /* Create a new fragment */
751         Fragment* F = NewFragment (FRAG_EXPR, 2);
752
753         /* Set the data */
754         F->V.Expr = Expr;
755     }
756 }
757
758
759
760 void EmitFarAddr (ExprNode* Expr)
761 /* Emit a 24 bit expression */
762 {
763     if (IsConstExpr (Expr)) {
764         /* Constant expression, emit literal byte */
765         long Val = GetExprVal (Expr);
766         FreeExpr (Expr);
767         if ((Val & ~0xFFFFFF) != 0) {
768             Error (ERR_RANGE);
769         }
770         Emit0 (Val & 0xFF);
771         Emit0 ((Val >> 8) & 0xFF);
772         Emit0 ((Val >> 16) & 0xFF);
773     } else {
774         /* Create a new fragment */
775         Fragment* F = NewFragment (FRAG_EXPR, 3);
776
777         /* Set the data */
778         F->V.Expr = Expr;
779     }
780 }
781
782
783
784 void EmitDWord (ExprNode* Expr)
785 /* Emit one dword */
786 {
787     if (IsConstExpr (Expr)) {
788         /* Constant expression, emit literal byte */
789         long Val = GetExprVal (Expr);
790         FreeExpr (Expr);
791         Emit0 (Val & 0xFF);
792         Emit0 ((Val >> 8) & 0xFF);
793         Emit0 ((Val >> 16) & 0xFF);
794         Emit0 ((Val >> 24) & 0xFF);
795     } else {
796         /* Create a new fragment */
797         Fragment* F = NewFragment (FRAG_EXPR, 4);
798
799         /* Set the data */
800         F->V.Expr = Expr;
801     }
802 }
803
804
805
806 void EmitFill (unsigned long Count)
807 /* Emit Count fill bytes */
808 {
809     while (Count) {
810         /* Calculate the size of the next chunk */
811         unsigned Chunk = (Count > 0xFFFF)? 0xFFFF : (unsigned) Count;
812         Count -= Chunk;
813
814         /* Emit one chunk */
815         NewFragment (FRAG_FILL, Chunk);
816     }
817 }
818
819
820