1 | /*****************************************************************************/ |
2 | /* */ |
3 | /* segment.c */ |
4 | /* */ |
5 | /* Segments for the ca65 macroassembler */ |
6 | /* */ |
7 | /* */ |
8 | /* */ |
9 | /* (C) 1998-2011, Ullrich von Bassewitz */ |
10 | /* Roemerstrasse 52 */ |
11 | /* D-70794 Filderstadt */ |
12 | /* EMail: uz@cc65.org */ |
13 | /* */ |
14 | /* */ |
15 | /* This software is provided 'as-is', without any expressed or implied */ |
16 | /* warranty. In no event will the authors be held liable for any damages */ |
17 | /* arising from the use of this software. */ |
18 | /* */ |
19 | /* Permission is granted to anyone to use this software for any purpose, */ |
20 | /* including commercial applications, and to alter it and redistribute it */ |
21 | /* freely, subject to the following restrictions: */ |
22 | /* */ |
23 | /* 1. The origin of this software must not be misrepresented; you must not */ |
24 | /* claim that you wrote the original software. If you use this software */ |
25 | /* in a product, an acknowledgment in the product documentation would be */ |
26 | /* appreciated but is not required. */ |
27 | /* 2. Altered source versions must be plainly marked as such, and must not */ |
28 | /* be misrepresented as being the original software. */ |
29 | /* 3. This notice may not be removed or altered from any source */ |
30 | /* distribution. */ |
31 | /* */ |
32 | /*****************************************************************************/ |
33 | |
34 | |
35 | |
36 | #include <string.h> |
37 | #include <errno.h> |
38 | |
39 | /* common */ |
40 | #include "addrsize.h" |
41 | #include "alignment.h" |
42 | #include "coll.h" |
43 | #include "mmodel.h" |
44 | #include "segdefs.h" |
45 | #include "segnames.h" |
46 | #include "xmalloc.h" |
47 | |
48 | /* cc65 */ |
49 | #include "error.h" |
50 | #include "fragment.h" |
51 | #include "global.h" |
52 | #include "lineinfo.h" |
53 | #include "listing.h" |
54 | #include "objcode.h" |
55 | #include "objfile.h" |
56 | #include "segment.h" |
57 | #include "span.h" |
58 | #include "spool.h" |
59 | #include "studyexpr.h" |
60 | #include "symtab.h" |
61 | |
62 | |
63 | |
64 | /*****************************************************************************/ |
65 | /* Data */ |
66 | /*****************************************************************************/ |
67 | |
68 | |
69 | |
70 | /* If OrgPerSeg is false, all segments share the RelocMode flag and a PC |
71 | ** used when in absolute mode. OrgPerSeg may be set by .feature org_per_seg |
72 | */ |
73 | static int RelocMode = 1; |
74 | static unsigned long AbsPC = 0; /* PC if in absolute mode */ |
75 | |
76 | /* Definitions for predefined segments */ |
77 | SegDef NullSegDef = STATIC_SEGDEF_INITIALIZER (SEGNAME_NULL, ADDR_SIZE_ABS); |
78 | SegDef ZeropageSegDef = STATIC_SEGDEF_INITIALIZER (SEGNAME_ZEROPAGE, ADDR_SIZE_ZP); |
79 | SegDef DataSegDef = STATIC_SEGDEF_INITIALIZER (SEGNAME_DATA, ADDR_SIZE_ABS); |
80 | SegDef BssSegDef = STATIC_SEGDEF_INITIALIZER (SEGNAME_BSS, ADDR_SIZE_ABS); |
81 | SegDef RODataSegDef = STATIC_SEGDEF_INITIALIZER (SEGNAME_RODATA, ADDR_SIZE_ABS); |
82 | SegDef CodeSegDef = STATIC_SEGDEF_INITIALIZER (SEGNAME_CODE, ADDR_SIZE_ABS); |
83 | |
84 | /* Collection containing all segments */ |
85 | Collection SegmentList = STATIC_COLLECTION_INITIALIZER; |
86 | |
87 | /* Currently active segment */ |
88 | Segment* ActiveSeg; |
89 | |
90 | |
91 | |
92 | /*****************************************************************************/ |
93 | /* Code */ |
94 | /*****************************************************************************/ |
95 | |
96 | |
97 | |
98 | static Segment* NewSegFromDef (SegDef* Def) |
99 | /* Create a new segment from a segment definition. Used only internally, no |
100 | ** checks. |
101 | */ |
102 | { |
103 | /* Create a new segment */ |
104 | Segment* S = xmalloc (sizeof (*S)); |
105 | |
106 | /* Initialize it */ |
107 | S->Root = 0; |
108 | S->Last = 0; |
109 | S->FragCount = 0; |
110 | S->Num = CollCount (&SegmentList); |
111 | S->Flags = SEG_FLAG_NONE; |
112 | S->Align = 1; |
113 | S->RelocMode = 1; |
114 | S->PC = 0; |
115 | S->AbsPC = 0; |
116 | S->Def = Def; |
117 | |
118 | /* Insert it into the segment list */ |
119 | CollAppend (&SegmentList, S); |
120 | |
121 | /* And return it... */ |
122 | return S; |
123 | } |
124 | |
125 | |
126 | |
127 | static Segment* NewSegment (const char* Name, unsigned char AddrSize) |
128 | /* Create a new segment, insert it into the global list and return it */ |
129 | { |
130 | /* Check for too many segments */ |
131 | if (CollCount (&SegmentList) >= 256) { |
132 | Fatal ("Too many segments" ); |
133 | } |
134 | |
135 | /* Check the segment name for invalid names */ |
136 | if (!ValidSegName (Name)) { |
137 | Error ("Illegal segment name: '%s'" , Name); |
138 | } |
139 | |
140 | /* Create a new segment and return it */ |
141 | return NewSegFromDef (NewSegDef (Name, AddrSize)); |
142 | } |
143 | |
144 | |
145 | |
146 | Fragment* GenFragment (unsigned char Type, unsigned short Len) |
147 | /* Generate a new fragment, add it to the current segment and return it. */ |
148 | { |
149 | /* Create the new fragment */ |
150 | Fragment* F = NewFragment (Type, Len); |
151 | |
152 | /* Insert the fragment into the current segment */ |
153 | if (ActiveSeg->Root) { |
154 | ActiveSeg->Last->Next = F; |
155 | ActiveSeg->Last = F; |
156 | } else { |
157 | ActiveSeg->Root = ActiveSeg->Last = F; |
158 | } |
159 | ++ActiveSeg->FragCount; |
160 | |
161 | /* Add this fragment to the current listing line */ |
162 | if (LineCur) { |
163 | if (LineCur->FragList == 0) { |
164 | LineCur->FragList = F; |
165 | } else { |
166 | LineCur->FragLast->LineList = F; |
167 | } |
168 | LineCur->FragLast = F; |
169 | } |
170 | |
171 | /* Increment the program counter */ |
172 | ActiveSeg->PC += F->Len; |
173 | if (OrgPerSeg) { |
174 | /* Relocatable mode is switched per segment */ |
175 | if (!ActiveSeg->RelocMode) { |
176 | ActiveSeg->AbsPC += F->Len; |
177 | } |
178 | } else { |
179 | /* Relocatable mode is switched globally */ |
180 | if (!RelocMode) { |
181 | AbsPC += F->Len; |
182 | } |
183 | } |
184 | |
185 | /* Return the fragment */ |
186 | return F; |
187 | } |
188 | |
189 | |
190 | |
191 | void UseSeg (const SegDef* D) |
192 | /* Use the segment with the given name */ |
193 | { |
194 | unsigned I; |
195 | for (I = 0; I < CollCount (&SegmentList); ++I) { |
196 | Segment* Seg = CollAtUnchecked (&SegmentList, I); |
197 | if (strcmp (Seg->Def->Name, D->Name) == 0) { |
198 | /* We found this segment. Check if the type is identical */ |
199 | if (D->AddrSize != ADDR_SIZE_DEFAULT && |
200 | Seg->Def->AddrSize != D->AddrSize) { |
201 | Error ("Segment attribute mismatch" ); |
202 | /* Use the new attribute to avoid errors */ |
203 | Seg->Def->AddrSize = D->AddrSize; |
204 | } |
205 | ActiveSeg = Seg; |
206 | return; |
207 | } |
208 | } |
209 | |
210 | /* Segment is not in list, create a new one */ |
211 | if (D->AddrSize == ADDR_SIZE_DEFAULT) { |
212 | ActiveSeg = NewSegment (D->Name, ADDR_SIZE_ABS); |
213 | } else { |
214 | ActiveSeg = NewSegment (D->Name, D->AddrSize); |
215 | } |
216 | } |
217 | |
218 | |
219 | |
220 | unsigned long GetPC (void) |
221 | /* Get the program counter of the current segment */ |
222 | { |
223 | if (OrgPerSeg) { |
224 | /* Relocatable mode is switched per segment */ |
225 | return ActiveSeg->RelocMode? ActiveSeg->PC : ActiveSeg->AbsPC; |
226 | } else { |
227 | /* Relocatable mode is switched globally */ |
228 | return RelocMode? ActiveSeg->PC : AbsPC; |
229 | } |
230 | } |
231 | |
232 | |
233 | |
234 | void EnterAbsoluteMode (unsigned long PC) |
235 | /* Enter absolute (non relocatable mode). Depending on the OrgPerSeg flag, |
236 | ** this will either switch the mode globally or for the current segment. |
237 | */ |
238 | { |
239 | if (OrgPerSeg) { |
240 | /* Relocatable mode is switched per segment */ |
241 | ActiveSeg->RelocMode = 0; |
242 | ActiveSeg->AbsPC = PC; |
243 | } else { |
244 | /* Relocatable mode is switched globally */ |
245 | RelocMode = 0; |
246 | AbsPC = PC; |
247 | } |
248 | } |
249 | |
250 | |
251 | |
252 | int GetRelocMode (void) |
253 | /* Return true if we're currently in relocatable mode */ |
254 | { |
255 | if (OrgPerSeg) { |
256 | /* Relocatable mode is switched per segment */ |
257 | return ActiveSeg->RelocMode; |
258 | } else { |
259 | /* Relocatable mode is switched globally */ |
260 | return RelocMode; |
261 | } |
262 | } |
263 | |
264 | |
265 | |
266 | void EnterRelocMode (void) |
267 | /* Enter relocatable mode. Depending on the OrgPerSeg flag, this will either |
268 | ** switch the mode globally or for the current segment. |
269 | */ |
270 | { |
271 | if (OrgPerSeg) { |
272 | /* Relocatable mode is switched per segment */ |
273 | ActiveSeg->RelocMode = 1; |
274 | } else { |
275 | /* Relocatable mode is switched globally */ |
276 | RelocMode = 1; |
277 | } |
278 | } |
279 | |
280 | |
281 | |
282 | void SegAlign (unsigned long Alignment, int FillVal) |
283 | /* Align the PC segment to Alignment. If FillVal is -1, emit fill fragments |
284 | ** (the actual fill value will be determined by the linker), otherwise use |
285 | ** the given value. |
286 | */ |
287 | { |
288 | unsigned char Data [4]; |
289 | unsigned long CombinedAlignment; |
290 | unsigned long Count; |
291 | |
292 | /* The segment must have the combined alignment of all separate alignments |
293 | ** in the source. Calculate this alignment and check it for sanity. |
294 | */ |
295 | CombinedAlignment = LeastCommonMultiple (ActiveSeg->Align, Alignment); |
296 | if (CombinedAlignment > MAX_ALIGNMENT) { |
297 | Error ("Combined alignment for active segment is %lu which exceeds %lu" , |
298 | CombinedAlignment, MAX_ALIGNMENT); |
299 | |
300 | /* Avoid creating large fills for an object file that is thrown away |
301 | ** later. |
302 | */ |
303 | Count = 1; |
304 | |
305 | } else { |
306 | ActiveSeg->Align = CombinedAlignment; |
307 | |
308 | /* Output a warning for larger alignments if not suppressed */ |
309 | if (CombinedAlignment > LARGE_ALIGNMENT && !LargeAlignment) { |
310 | Warning (0, "Combined alignment is suspiciously large (%lu)" , |
311 | CombinedAlignment); |
312 | } |
313 | |
314 | /* Calculate the number of fill bytes */ |
315 | Count = AlignCount (ActiveSeg->PC, Alignment); |
316 | |
317 | } |
318 | |
319 | |
320 | /* Emit the data or a fill fragment */ |
321 | if (FillVal != -1) { |
322 | /* User defined fill value */ |
323 | memset (Data, FillVal, sizeof (Data)); |
324 | while (Count) { |
325 | if (Count > sizeof (Data)) { |
326 | EmitData (Data, sizeof (Data)); |
327 | Count -= sizeof (Data); |
328 | } else { |
329 | EmitData (Data, Count); |
330 | Count = 0; |
331 | } |
332 | } |
333 | } else { |
334 | /* Linker defined fill value */ |
335 | EmitFill (Count); |
336 | } |
337 | } |
338 | |
339 | |
340 | |
341 | unsigned char GetSegAddrSize (unsigned SegNum) |
342 | /* Return the address size of the segment with the given number */ |
343 | { |
344 | /* Is there such a segment? */ |
345 | if (SegNum >= CollCount (&SegmentList)) { |
346 | FAIL ("Invalid segment number" ); |
347 | } |
348 | |
349 | /* Return the address size */ |
350 | return ((Segment*) CollAtUnchecked (&SegmentList, SegNum))->Def->AddrSize; |
351 | } |
352 | |
353 | |
354 | |
355 | void SegDone (void) |
356 | /* Check the segments for range and other errors. Do cleanup. */ |
357 | { |
358 | static const unsigned long U_Hi[4] = { |
359 | 0x000000FFUL, 0x0000FFFFUL, 0x00FFFFFFUL, 0xFFFFFFFFUL |
360 | }; |
361 | static const long S_Hi[4] = { |
362 | 0x0000007FL, 0x00007FFFL, 0x007FFFFFL, 0x7FFFFFFFL |
363 | }; |
364 | |
365 | unsigned I; |
366 | for (I = 0; I < CollCount (&SegmentList); ++I) { |
367 | Segment* S = CollAtUnchecked (&SegmentList, I); |
368 | Fragment* F = S->Root; |
369 | while (F) { |
370 | if (F->Type == FRAG_EXPR || F->Type == FRAG_SEXPR) { |
371 | |
372 | /* We have an expression, study it */ |
373 | ExprDesc ED; |
374 | ED_Init (&ED); |
375 | StudyExpr (F->V.Expr, &ED); |
376 | |
377 | /* Check if the expression is constant */ |
378 | if (ED_IsConst (&ED)) { |
379 | |
380 | unsigned J; |
381 | |
382 | /* The expression is constant. Check for range errors. */ |
383 | CHECK (F->Len <= 4); |
384 | if (F->Type == FRAG_SEXPR) { |
385 | long Hi = S_Hi[F->Len-1]; |
386 | long Lo = ~Hi; |
387 | if (ED.Val > Hi || ED.Val < Lo) { |
388 | LIError (&F->LI, |
389 | "Range error (%ld not in [%ld..%ld])" , |
390 | ED.Val, Lo, Hi); |
391 | } |
392 | } else { |
393 | if (((unsigned long)ED.Val) > U_Hi[F->Len-1]) { |
394 | LIError (&F->LI, |
395 | "Range error (%lu not in [0..%lu])" , |
396 | (unsigned long)ED.Val, U_Hi[F->Len-1]); |
397 | } |
398 | } |
399 | |
400 | /* We don't need the expression tree any longer */ |
401 | FreeExpr (F->V.Expr); |
402 | |
403 | /* Convert the fragment into a literal fragment */ |
404 | for (J = 0; J < F->Len; ++J) { |
405 | F->V.Data[J] = ED.Val & 0xFF; |
406 | ED.Val >>= 8; |
407 | } |
408 | F->Type = FRAG_LITERAL; |
409 | |
410 | } else if (RelaxChecks == 0) { |
411 | |
412 | /* We cannot evaluate the expression now, leave the job for |
413 | ** the linker. However, we can check if the address size |
414 | ** matches the fragment size. Mismatches are errors in |
415 | ** most situations. |
416 | */ |
417 | if ((F->Len == 1 && ED.AddrSize > ADDR_SIZE_ZP) || |
418 | (F->Len == 2 && ED.AddrSize > ADDR_SIZE_ABS) || |
419 | (F->Len == 3 && ED.AddrSize > ADDR_SIZE_FAR)) { |
420 | LIError (&F->LI, "Range error" ); |
421 | } |
422 | } |
423 | |
424 | /* Release memory allocated for the expression decriptor */ |
425 | ED_Done (&ED); |
426 | } |
427 | F = F->Next; |
428 | } |
429 | } |
430 | } |
431 | |
432 | |
433 | |
434 | void SegDump (void) |
435 | /* Dump the contents of all segments */ |
436 | { |
437 | unsigned I; |
438 | unsigned X = 0; |
439 | |
440 | printf ("\n" ); |
441 | for (I = 0; I < CollCount (&SegmentList); ++I) { |
442 | Segment* S = CollAtUnchecked (&SegmentList, I); |
443 | unsigned I; |
444 | Fragment* F; |
445 | int State = -1; |
446 | printf ("New segment: %s" , S->Def->Name); |
447 | F = S->Root; |
448 | while (F) { |
449 | if (F->Type == FRAG_LITERAL) { |
450 | if (State != 0) { |
451 | printf ("\n Literal:" ); |
452 | X = 15; |
453 | State = 0; |
454 | } |
455 | for (I = 0; I < F->Len; ++I) { |
456 | printf (" %02X" , F->V.Data [I]); |
457 | X += 3; |
458 | } |
459 | } else if (F->Type == FRAG_EXPR || F->Type == FRAG_SEXPR) { |
460 | State = 1; |
461 | printf ("\n Expression (%u): " , F->Len); |
462 | DumpExpr (F->V.Expr, SymResolve); |
463 | } else if (F->Type == FRAG_FILL) { |
464 | State = 1; |
465 | printf ("\n Fill bytes (%u)" , F->Len); |
466 | } else { |
467 | Internal ("Unknown fragment type: %u" , F->Type); |
468 | } |
469 | if (X > 65) { |
470 | State = -1; |
471 | } |
472 | F = F->Next; |
473 | } |
474 | printf ("\n End PC = $%04X\n" , (unsigned)(S->PC & 0xFFFF)); |
475 | } |
476 | printf ("\n" ); |
477 | } |
478 | |
479 | |
480 | |
481 | void SegInit (void) |
482 | /* Initialize segments */ |
483 | { |
484 | /* Create the predefined segments. Code segment is active */ |
485 | ActiveSeg = NewSegFromDef (&CodeSegDef); |
486 | NewSegFromDef (&RODataSegDef); |
487 | NewSegFromDef (&BssSegDef); |
488 | NewSegFromDef (&DataSegDef); |
489 | NewSegFromDef (&ZeropageSegDef); |
490 | NewSegFromDef (&NullSegDef); |
491 | } |
492 | |
493 | |
494 | |
495 | void SetSegmentSizes (void) |
496 | /* Set the default segment sizes according to the memory model */ |
497 | { |
498 | /* Initialize segment sizes. The segment definitions do already contain |
499 | ** the correct values for the default case (near), so we must only change |
500 | ** things that should be different. |
501 | */ |
502 | switch (MemoryModel) { |
503 | |
504 | case MMODEL_NEAR: |
505 | break; |
506 | |
507 | case MMODEL_FAR: |
508 | CodeSegDef.AddrSize = ADDR_SIZE_FAR; |
509 | break; |
510 | |
511 | case MMODEL_HUGE: |
512 | CodeSegDef.AddrSize = ADDR_SIZE_FAR; |
513 | DataSegDef.AddrSize = ADDR_SIZE_FAR; |
514 | BssSegDef.AddrSize = ADDR_SIZE_FAR; |
515 | RODataSegDef.AddrSize = ADDR_SIZE_FAR; |
516 | break; |
517 | |
518 | default: |
519 | Internal ("Invalid memory model: %d" , MemoryModel); |
520 | } |
521 | } |
522 | |
523 | |
524 | |
525 | static void WriteOneSeg (Segment* Seg) |
526 | /* Write one segment to the object file */ |
527 | { |
528 | Fragment* Frag; |
529 | unsigned long DataSize; |
530 | unsigned long EndPos; |
531 | |
532 | /* Remember the file position, then write a dummy for the size of the |
533 | ** following data |
534 | */ |
535 | unsigned long SizePos = ObjGetFilePos (); |
536 | ObjWrite32 (0); |
537 | |
538 | /* Write the segment data */ |
539 | ObjWriteVar (GetStringId (Seg->Def->Name)); /* Name of the segment */ |
540 | ObjWriteVar (Seg->Flags); /* Segment flags */ |
541 | ObjWriteVar (Seg->PC); /* Size */ |
542 | ObjWriteVar (Seg->Align); /* Segment alignment */ |
543 | ObjWrite8 (Seg->Def->AddrSize); /* Address size of the segment */ |
544 | ObjWriteVar (Seg->FragCount); /* Number of fragments */ |
545 | |
546 | /* Now walk through the fragment list for this segment and write the |
547 | ** fragments. |
548 | */ |
549 | Frag = Seg->Root; |
550 | while (Frag) { |
551 | |
552 | /* Write data depending on the type */ |
553 | switch (Frag->Type) { |
554 | |
555 | case FRAG_LITERAL: |
556 | ObjWrite8 (FRAG_LITERAL); |
557 | ObjWriteVar (Frag->Len); |
558 | ObjWriteData (Frag->V.Data, Frag->Len); |
559 | break; |
560 | |
561 | case FRAG_EXPR: |
562 | switch (Frag->Len) { |
563 | case 1: ObjWrite8 (FRAG_EXPR8); break; |
564 | case 2: ObjWrite8 (FRAG_EXPR16); break; |
565 | case 3: ObjWrite8 (FRAG_EXPR24); break; |
566 | case 4: ObjWrite8 (FRAG_EXPR32); break; |
567 | default: Internal ("Invalid fragment size: %u" , Frag->Len); |
568 | } |
569 | WriteExpr (Frag->V.Expr); |
570 | break; |
571 | |
572 | case FRAG_SEXPR: |
573 | switch (Frag->Len) { |
574 | case 1: ObjWrite8 (FRAG_SEXPR8); break; |
575 | case 2: ObjWrite8 (FRAG_SEXPR16); break; |
576 | case 3: ObjWrite8 (FRAG_SEXPR24); break; |
577 | case 4: ObjWrite8 (FRAG_SEXPR32); break; |
578 | default: Internal ("Invalid fragment size: %u" , Frag->Len); |
579 | } |
580 | WriteExpr (Frag->V.Expr); |
581 | break; |
582 | |
583 | case FRAG_FILL: |
584 | ObjWrite8 (FRAG_FILL); |
585 | ObjWriteVar (Frag->Len); |
586 | break; |
587 | |
588 | default: |
589 | Internal ("Invalid fragment type: %u" , Frag->Type); |
590 | |
591 | } |
592 | |
593 | /* Write the line infos for this fragment */ |
594 | WriteLineInfo (&Frag->LI); |
595 | |
596 | /* Next fragment */ |
597 | Frag = Frag->Next; |
598 | } |
599 | |
600 | /* Calculate the size of the data, seek back and write it */ |
601 | EndPos = ObjGetFilePos (); /* Remember where we are */ |
602 | DataSize = EndPos - SizePos - 4; /* Don't count size itself */ |
603 | ObjSetFilePos (SizePos); /* Seek back to the size */ |
604 | ObjWrite32 (DataSize); /* Write the size */ |
605 | ObjSetFilePos (EndPos); /* Seek back to the end */ |
606 | } |
607 | |
608 | |
609 | |
610 | void WriteSegments (void) |
611 | /* Write the segment data to the object file */ |
612 | { |
613 | unsigned I; |
614 | |
615 | /* Tell the object file module that we're about to start the seg list */ |
616 | ObjStartSegments (); |
617 | |
618 | /* First thing is segment count */ |
619 | ObjWriteVar (CollCount (&SegmentList)); |
620 | |
621 | /* Now walk through all segments and write them to the object file */ |
622 | for (I = 0; I < CollCount (&SegmentList); ++I) { |
623 | /* Write one segment */ |
624 | WriteOneSeg (CollAtUnchecked (&SegmentList, I)); |
625 | } |
626 | |
627 | /* Done writing segments */ |
628 | ObjEndSegments (); |
629 | } |
630 | |