]> git.sur5r.net Git - cc65/blob - src/ca65/segment.c
3b760136cd09eb42614c498e324e87f782bb68a6
[cc65] / src / ca65 / segment.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                 segment.c                                 */
4 /*                                                                           */
5 /*                   Segments for the ca65 macroassembler                    */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 1998-2003 Ullrich von Bassewitz                                       */
10 /*               Römerstrasse 52                                             */
11 /*               D-70794 Filderstadt                                         */
12 /* EMail:        uz@cc65.org                                                 */
13 /*                                                                           */
14 /*                                                                           */
15 /* This software is provided 'as-is', without any expressed or implied       */
16 /* warranty.  In no event will the authors be held liable for any damages    */
17 /* arising from the use of this software.                                    */
18 /*                                                                           */
19 /* Permission is granted to anyone to use this software for any purpose,     */
20 /* including commercial applications, and to alter it and redistribute it    */
21 /* freely, subject to the following restrictions:                            */
22 /*                                                                           */
23 /* 1. The origin of this software must not be misrepresented; you must not   */
24 /*    claim that you wrote the original software. If you use this software   */
25 /*    in a product, an acknowledgment in the product documentation would be  */
26 /*    appreciated but is not required.                                       */
27 /* 2. Altered source versions must be plainly marked as such, and must not   */
28 /*    be misrepresented as being the original software.                      */
29 /* 3. This notice may not be removed or altered from any source              */
30 /*    distribution.                                                          */
31 /*                                                                           */
32 /*****************************************************************************/
33
34
35
36 #include <string.h>
37 #include <errno.h>
38
39 /* common */
40 #include "segnames.h"
41 #include "xmalloc.h"
42
43 /* cc65 */
44 #include "error.h"
45 #include "fragment.h"
46 #include "global.h"
47 #include "lineinfo.h"
48 #include "listing.h"
49 #include "objcode.h"
50 #include "objfile.h"
51 #include "segment.h"
52 #include "spool.h"
53 #include "symtab.h"
54
55
56
57 /*****************************************************************************/
58 /*                                   Data                                    */
59 /*****************************************************************************/
60
61
62
63 /* Are we in absolute mode or in relocatable mode? */
64 int             RelocMode = 1;
65 unsigned long   AbsPC     = 0;          /* PC if in absolute mode */
66
67 /* Segment initializer macro */
68 #define SEG(segdef, num, prev)      \
69     { prev, 0, 0, 0, num, 0, 0, segdef }
70
71 /* Definitions for predefined segments */
72 SegDef NullSegDef     = STATIC_SEGDEF_INITIALIZER (SEGNAME_NULL,     ADDR_SIZE_ABS);
73 SegDef ZeropageSegDef = STATIC_SEGDEF_INITIALIZER (SEGNAME_ZEROPAGE, ADDR_SIZE_ZP);
74 SegDef DataSegDef     = STATIC_SEGDEF_INITIALIZER (SEGNAME_DATA,     ADDR_SIZE_ABS);
75 SegDef BssSegDef      = STATIC_SEGDEF_INITIALIZER (SEGNAME_BSS,      ADDR_SIZE_ABS);
76 SegDef RODataSegDef   = STATIC_SEGDEF_INITIALIZER (SEGNAME_RODATA,   ADDR_SIZE_ABS);
77 SegDef CodeSegDef     = STATIC_SEGDEF_INITIALIZER (SEGNAME_CODE,     ADDR_SIZE_ABS);
78
79 /* Predefined segments */
80 static Segment NullSeg     = SEG (&NullSegDef,     5, NULL);
81 static Segment ZeropageSeg = SEG (&ZeropageSegDef, 4, &NullSeg);
82 static Segment DataSeg     = SEG (&DataSegDef,     3, &ZeropageSeg);
83 static Segment BssSeg      = SEG (&BssSegDef,      2, &DataSeg);
84 static Segment RODataSeg   = SEG (&RODataSegDef,   1, &BssSeg);
85 static Segment CodeSeg     = SEG (&CodeSegDef,     0, &RODataSeg);
86
87 /* Number of segments */
88 static unsigned SegmentCount = 6;
89
90 /* List of all segments */
91 static Segment* SegmentList = &CodeSeg;
92 static Segment* SegmentLast = &NullSeg;
93
94 /* Currently active segment */
95 Segment* ActiveSeg = &CodeSeg;
96
97
98
99 /*****************************************************************************/
100 /*                                   Code                                    */
101 /*****************************************************************************/
102
103
104
105 static Segment* NewSegment (const char* Name, unsigned AddrSize)
106 /* Create a new segment, insert it into the global list and return it */
107 {
108     Segment* S;
109
110     /* Check for too many segments */
111     if (SegmentCount >= 256) {
112         Fatal (FAT_TOO_MANY_SEGMENTS);
113     }
114
115     /* Check the segment name for invalid names */
116     if (!ValidSegName (Name)) {
117         Error (ERR_ILLEGAL_SEGMENT, Name);
118     }
119
120     /* Create a new segment */
121     S = xmalloc (sizeof (*S));
122
123     /* Initialize it */
124     S->List      = 0;
125     S->Root      = 0;
126     S->Last      = 0;
127     S->FragCount = 0;
128     S->Num       = SegmentCount++;
129     S->Align     = 0;
130     S->PC        = 0;
131     S->Def       = NewSegDef (Name, AddrSize);
132
133     /* Insert it into the segment list */
134     SegmentLast->List = S;
135     SegmentLast = S;
136
137     /* And return it... */
138     return S;
139 }
140
141
142
143 Fragment* GenFragment (unsigned char Type, unsigned short Len)
144 /* Generate a new fragment, add it to the current segment and return it. */
145 {
146     /* Create the new fragment */
147     Fragment* F = NewFragment (Type, Len);
148
149     /* Insert the fragment into the current segment */
150     if (ActiveSeg->Root) {
151         ActiveSeg->Last->Next = F;
152         ActiveSeg->Last = F;
153     } else {
154         ActiveSeg->Root = ActiveSeg->Last = F;
155     }
156     ++ActiveSeg->FragCount;
157
158     /* Add this fragment to the current listing line */
159     if (LineCur) {
160         if (LineCur->FragList == 0) {
161             LineCur->FragList = F;
162         } else {
163             LineCur->FragLast->LineList = F;
164         }
165         LineCur->FragLast = F;
166     }
167
168     /* Increment the program counter */
169     ActiveSeg->PC += F->Len;
170     if (!RelocMode) {
171         AbsPC += F->Len;
172     }
173
174     /* Return the fragment */
175     return F;
176 }
177
178
179
180 void UseSeg (const SegDef* D)
181 /* Use the segment with the given name */
182 {
183     Segment* Seg = SegmentList;
184     while (Seg) {
185         if (strcmp (Seg->Def->Name, D->Name) == 0) {
186             /* We found this segment. Check if the type is identical */
187             if (D->AddrSize != ADDR_SIZE_DEFAULT &&
188                 Seg->Def->AddrSize != D->AddrSize) {
189                 Error (ERR_SEG_ATTR_MISMATCH);
190                 /* Use the new attribute to avoid errors */
191                 Seg->Def->AddrSize = D->AddrSize;
192             }
193             ActiveSeg = Seg;
194             return;
195         }
196         /* Check next segment */
197         Seg = Seg->List;
198     }
199
200     /* Segment is not in list, create a new one */
201     if (D->AddrSize == ADDR_SIZE_DEFAULT) {
202         Seg = NewSegment (D->Name, ADDR_SIZE_ABS);
203     } else {
204         Seg = NewSegment (D->Name, D->AddrSize);
205     }
206     ActiveSeg = Seg;
207 }
208
209
210
211 unsigned long GetPC (void)
212 /* Get the program counter of the current segment */
213 {
214     return RelocMode? ActiveSeg->PC : AbsPC;
215 }
216
217
218
219 void SetAbsPC (unsigned long PC)
220 /* Set the program counter in absolute mode */
221 {
222     RelocMode = 0;
223     AbsPC = PC;
224 }
225
226
227
228 void SegAlign (unsigned Power, int Val)
229 /* Align the PC segment to 2^Power. If Val is -1, emit fill fragments (the
230  * actual fill value will be determined by the linker), otherwise use the
231  * given value.
232  */
233 {
234     unsigned char Data [4];
235     unsigned long Align = (1UL << Power) - 1;
236     unsigned long NewPC = (ActiveSeg->PC + Align) & ~Align;
237     unsigned long Count = NewPC - ActiveSeg->PC;
238
239     if (Val != -1) {
240         /* User defined fill value */
241         memset (Data, Val, sizeof (Data));
242         while (Count) {
243             if (Count > sizeof (Data)) {
244                 EmitData (Data, sizeof (Data));
245                 Count -= sizeof (Data);
246             } else {
247                 EmitData (Data, Count);
248                 Count = 0;
249             }
250         }
251     } else {
252         /* Linker defined fill value */
253         EmitFill (Count);
254     }
255
256     /* Remember the alignment in the header */
257     if (ActiveSeg->Align < Power) {
258         ActiveSeg->Align = Power;
259     }
260 }
261
262
263
264 unsigned GetSegAddrSize (unsigned SegNum)
265 /* Return the address size of the segment with the given number */
266 {
267     /* Search for the segment */
268     Segment* S = SegmentList;
269     while (S && SegNum) {
270         --SegNum;
271         S = S->List;
272     }
273
274     /* Did we find it? */
275     if (S == 0) {
276         FAIL ("Invalid segment number");
277     }
278
279     /* Return the address size */
280     return S->Def->AddrSize;
281 }
282
283
284
285 void SegCheck (void)
286 /* Check the segments for range and other errors */
287 {
288     Segment* S = SegmentList;
289     while (S) {
290         Fragment* F = S->Root;
291         while (F) {
292             if (F->Type == FRAG_EXPR || F->Type == FRAG_SEXPR) {
293                 F->V.Expr = FinalizeExpr (F->V.Expr);
294                 if (IsConstExpr (F->V.Expr)) {
295                     /* We are able to evaluate the expression. Get the value
296                      * and check for range errors.
297                      */
298                     unsigned I;
299                     long Val = GetExprVal (F->V.Expr);
300                     int Abs = (F->Type != FRAG_SEXPR);
301
302                     if (F->Len == 1) {
303                         if (Abs) {
304                             /* Absolute value */
305                             if (Val > 255) {
306                                 PError (&F->Pos, ERR_RANGE);
307                             }
308                         } else {
309                             /* PC relative value */
310                             if (Val < -128 || Val > 127) {
311                                 PError (&F->Pos, ERR_RANGE);
312                             }
313                         }
314                     } else if (F->Len == 2) {
315                         if (Abs) {
316                             /* Absolute value */
317                             if (Val > 65535) {
318                                 PError (&F->Pos, ERR_RANGE);
319                             }
320                         } else {
321                             /* PC relative value */
322                             if (Val < -32768 || Val > 32767) {
323                                 PError (&F->Pos, ERR_RANGE);
324                             }
325                         }
326                     }
327
328                     /* We don't need the expression tree any longer */
329                     FreeExpr (F->V.Expr);
330
331                     /* Convert the fragment into a literal fragment */
332                     for (I = 0; I < F->Len; ++I) {
333                         F->V.Data [I] = Val & 0xFF;
334                         Val >>= 8;
335                     }
336                     F->Type = FRAG_LITERAL;
337                 } else {
338                     /* We cannot evaluate the expression now, leave the job for
339                      * the linker. However, we are able to check for explicit
340                      * byte expressions and we will do so.
341                      */
342                     if (F->Type == FRAG_EXPR && F->Len == 1 && !IsByteExpr (F->V.Expr)) {
343                         PError (&F->Pos, ERR_RANGE);
344                     }
345                 }
346             }
347             F = F->Next;
348         }
349         S = S->List;
350     }
351 }
352
353
354
355 void SegDump (void)
356 /* Dump the contents of all segments */
357 {
358     unsigned X = 0;
359     Segment* S = SegmentList;
360     printf ("\n");
361     while (S) {
362         unsigned I;
363         Fragment* F;
364         int State = -1;
365         printf ("New segment: %s", S->Def->Name);
366         F = S->Root;
367         while (F) {
368             if (F->Type == FRAG_LITERAL) {
369                 if (State != 0) {
370                     printf ("\n  Literal:");
371                     X = 15;
372                     State = 0;
373                 }
374                 for (I = 0; I < F->Len; ++I) {
375                     printf (" %02X", F->V.Data [I]);
376                     X += 3;
377                 }
378             } else if (F->Type == FRAG_EXPR || F->Type == FRAG_SEXPR) {
379                 State = 1;
380                 printf ("\n  Expression (%u): ", F->Len);
381                 DumpExpr (F->V.Expr);
382             } else if (F->Type == FRAG_FILL) {
383                 State = 1;
384                 printf ("\n  Fill bytes (%u)", F->Len);
385             } else {
386                 Internal ("Unknown fragment type: %u", F->Type);
387             }
388             if (X > 65) {
389                 State = -1;
390             }
391             F = F->Next;
392         }
393         printf ("\n  End PC = $%04X\n", (unsigned)(S->PC & 0xFFFF));
394         S = S->List;
395     }
396     printf ("\n");
397 }
398
399
400
401 static void WriteOneSeg (Segment* Seg)
402 /* Write one segment to the object file */
403 {
404     Fragment* Frag;
405     unsigned LineInfoIndex;
406     unsigned long DataSize;
407     unsigned long EndPos;
408
409     /* Remember the file position, then write a dummy for the size of the
410      * following data
411      */
412     unsigned long SizePos = ObjGetFilePos ();
413     ObjWrite32 (0);
414
415     /* Write the segment data */
416     ObjWriteVar (GetStringId (Seg->Def->Name)); /* Name of the segment */
417     ObjWrite32 (Seg->PC);                       /* Size */
418     ObjWrite8 (Seg->Align);                     /* Segment alignment */
419     ObjWrite8 (Seg->Def->AddrSize);             /* Address size of the segment */
420     ObjWriteVar (Seg->FragCount);               /* Number of fragments */
421
422     /* Now walk through the fragment list for this segment and write the
423      * fragments.
424      */
425     Frag = Seg->Root;
426     while (Frag) {
427
428         /* Write data depending on the type */
429         switch (Frag->Type) {
430
431             case FRAG_LITERAL:
432                 ObjWrite8 (FRAG_LITERAL);
433                 ObjWriteVar (Frag->Len);
434                 ObjWriteData (Frag->V.Data, Frag->Len);
435                 break;
436
437             case FRAG_EXPR:
438                 switch (Frag->Len) {
439                     case 1:   ObjWrite8 (FRAG_EXPR8);   break;
440                     case 2:   ObjWrite8 (FRAG_EXPR16);  break;
441                     case 3:   ObjWrite8 (FRAG_EXPR24);  break;
442                     case 4:   ObjWrite8 (FRAG_EXPR32);  break;
443                     default:  Internal ("Invalid fragment size: %u", Frag->Len);
444                 }
445                 WriteExpr (Frag->V.Expr);
446                 break;
447
448             case FRAG_SEXPR:
449                 switch (Frag->Len) {
450                     case 1:   ObjWrite8 (FRAG_SEXPR8);  break;
451                     case 2:   ObjWrite8 (FRAG_SEXPR16); break;
452                     case 3:   ObjWrite8 (FRAG_SEXPR24); break;
453                     case 4:   ObjWrite8 (FRAG_SEXPR32); break;
454                     default:  Internal ("Invalid fragment size: %u", Frag->Len);
455                 }
456                 WriteExpr (Frag->V.Expr);
457                 break;
458
459             case FRAG_FILL:
460                 ObjWrite8 (FRAG_FILL);
461                 ObjWriteVar (Frag->Len);
462                 break;
463
464             default:
465                 Internal ("Invalid fragment type: %u", Frag->Type);
466
467         }
468
469         /* Write the file position of this fragment */
470         ObjWritePos (&Frag->Pos);
471
472         /* Write extra line info for this fragment. Zero is considered
473          * "no line info", so add one to the value.
474          */
475         LineInfoIndex = Frag->LI? Frag->LI->Index + 1 : 0;
476         ObjWriteVar (LineInfoIndex);
477
478         /* Next fragment */
479         Frag = Frag->Next;
480     }
481
482     /* Calculate the size of the data, seek back and write it */
483     EndPos = ObjGetFilePos ();          /* Remember where we are */
484     DataSize = EndPos - SizePos - 4;    /* Don't count size itself */
485     ObjSetFilePos (SizePos);            /* Seek back to the size */
486     ObjWrite32 (DataSize);              /* Write the size */
487     ObjSetFilePos (EndPos);             /* Seek back to the end */
488 }
489
490
491
492 void WriteSegments (void)
493 /* Write the segment data to the object file */
494 {
495     Segment* Seg;
496
497     /* Tell the object file module that we're about to start the seg list */
498     ObjStartSegments ();
499
500     /* First thing is segment count */
501     ObjWriteVar (SegmentCount);
502
503     /* Now walk through all segments and write them to the object file */
504     Seg = SegmentList;
505     while (Seg) {
506         /* Write one segment */
507         WriteOneSeg (Seg);
508         /* Next segment */
509         Seg = Seg->List;
510     }
511
512     /* Done writing segments */
513     ObjEndSegments ();
514 }
515
516
517