]> git.sur5r.net Git - cc65/blobdiff - src/ca65/segment.c
If a debug symbol is an import, write out the import id.
[cc65] / src / ca65 / segment.c
index 3e50d96ac0c6f4376bd7fc5a1dccdda2b4a14264..a0a7b96623b79caae09f74baa8c128fd1cab939c 100644 (file)
@@ -6,10 +6,10 @@
 /*                                                                           */
 /*                                                                           */
 /*                                                                           */
-/* (C) 1998-2003 Ullrich von Bassewitz                                       */
-/*               Römerstrasse 52                                             */
-/*               D-70794 Filderstadt                                         */
-/* EMail:        uz@cc65.org                                                 */
+/* (C) 1998-2011, Ullrich von Bassewitz                                      */
+/*                Roemerstrasse 52                                           */
+/*                D-70794 Filderstadt                                        */
+/* EMail:         uz@cc65.org                                                */
 /*                                                                           */
 /*                                                                           */
 /* This software is provided 'as-is', without any expressed or implied       */
@@ -37,6 +37,9 @@
 #include <errno.h>
 
 /* common */
+#include "addrsize.h"
+#include "coll.h"
+#include "mmodel.h"
 #include "segnames.h"
 #include "xmalloc.h"
 
@@ -50,6 +53,7 @@
 #include "objfile.h"
 #include "segment.h"
 #include "spool.h"
+#include "studyexpr.h"
 #include "symtab.h"
 
 
 
 
 
-/* Are we in absolute mode or in relocatable mode? */
-int            RelocMode = 1;
-unsigned long  AbsPC     = 0;          /* PC if in absolute mode */
-
-
-
-typedef struct Segment Segment;
-struct Segment {
-    Segment*               List;               /* List of all segments */
-    Fragment*              Root;               /* Root of fragment list */
-    Fragment*              Last;               /* Pointer to last fragment */
-    unsigned long   FragCount;          /* Number of fragments */
-    unsigned        Num;               /* Segment number */
-    unsigned        Align;             /* Segment alignment */
-    unsigned long   PC;
-    SegDef*         Def;                /* Segment definition (name and type) */
-};
-
-
-#define SEG(segdef, num, prev)      \
-    { prev, 0, 0, 0, num, 0, 0, segdef }
+/* If OrgPerSeg is false, all segments share the RelocMode flag and a PC
+ * used when in absolute mode. OrgPerSeg may be set by .feature org_per_seg
+ */
+static int              RelocMode = 1;
+static unsigned long    AbsPC    = 0;          /* PC if in absolute mode */
 
 /* Definitions for predefined segments */
-SegDef NullSegDef     = STATIC_SEGDEF_INITIALIZER (SEGNAME_NULL,     SEGTYPE_ABS);
-SegDef ZeropageSegDef = STATIC_SEGDEF_INITIALIZER (SEGNAME_ZEROPAGE, SEGTYPE_ZP);
-SegDef DataSegDef     = STATIC_SEGDEF_INITIALIZER (SEGNAME_DATA,     SEGTYPE_ABS);
-SegDef BssSegDef      = STATIC_SEGDEF_INITIALIZER (SEGNAME_BSS,      SEGTYPE_ABS);
-SegDef RODataSegDef   = STATIC_SEGDEF_INITIALIZER (SEGNAME_RODATA,   SEGTYPE_ABS);
-SegDef CodeSegDef     = STATIC_SEGDEF_INITIALIZER (SEGNAME_CODE,     SEGTYPE_ABS);
-
-/* Predefined segments */
-static Segment NullSeg     = SEG (&NullSegDef,     5, NULL);
-static Segment ZeropageSeg = SEG (&ZeropageSegDef, 4, &NullSeg);
-static Segment DataSeg     = SEG (&DataSegDef,     3, &ZeropageSeg);
-static Segment BssSeg      = SEG (&BssSegDef,      2, &DataSeg);
-static Segment RODataSeg   = SEG (&RODataSegDef,   1, &BssSeg);
-static Segment CodeSeg     = SEG (&CodeSegDef,     0, &RODataSeg);
-
-/* Number of segments */
-static unsigned SegmentCount = 6;
-
-/* List of all segments */
-static Segment* SegmentList = &CodeSeg;
-static Segment* SegmentLast = &NullSeg;
+SegDef NullSegDef     = STATIC_SEGDEF_INITIALIZER (SEGNAME_NULL,     ADDR_SIZE_ABS);
+SegDef ZeropageSegDef = STATIC_SEGDEF_INITIALIZER (SEGNAME_ZEROPAGE, ADDR_SIZE_ZP);
+SegDef DataSegDef     = STATIC_SEGDEF_INITIALIZER (SEGNAME_DATA,     ADDR_SIZE_ABS);
+SegDef BssSegDef      = STATIC_SEGDEF_INITIALIZER (SEGNAME_BSS,      ADDR_SIZE_ABS);
+SegDef RODataSegDef   = STATIC_SEGDEF_INITIALIZER (SEGNAME_RODATA,   ADDR_SIZE_ABS);
+SegDef CodeSegDef     = STATIC_SEGDEF_INITIALIZER (SEGNAME_CODE,     ADDR_SIZE_ABS);
+
+/* Collection containing all segments */
+Collection SegmentList = STATIC_COLLECTION_INITIALIZER;
 
 /* Currently active segment */
-static Segment*        ActiveSeg = &CodeSeg;
+Segment* ActiveSeg;
 
 
 
@@ -116,37 +92,27 @@ static Segment*    ActiveSeg = &CodeSeg;
 
 
 
-static Segment* NewSegment (const char* Name, unsigned SegType)
-/* Create a new segment, insert it into the global list and return it */
+static Segment* NewSegFromDef (SegDef* Def)
+/* Create a new segment from a segment definition. Used only internally, no
+ * checks.
+ */
 {
-    Segment* S;
-
-    /* Check for too many segments */
-    if (SegmentCount >= 256) {
-       Fatal (FAT_TOO_MANY_SEGMENTS);
-    }
-
-    /* Check the segment name for invalid names */
-    if (!ValidSegName (Name)) {
-       Error (ERR_ILLEGAL_SEGMENT, Name);
-    }
-
     /* Create a new segment */
-    S = xmalloc (sizeof (*S));
+    Segment* S = xmalloc (sizeof (*S));
 
     /* Initialize it */
-    S->List      = 0;
     S->Root      = 0;
     S->Last      = 0;
     S->FragCount = 0;
-    S->Num       = SegmentCount++;
+    S->Num       = CollCount (&SegmentList);
     S->Align     = 0;
+    S->RelocMode = 1;
     S->PC        = 0;
-    S->Def       = NewSegDef (Name, SegType);
+    S->AbsPC     = 0;
+    S->Def       = Def;
 
     /* Insert it into the segment list */
-    SegmentLast->List = S;
-    SegmentLast = S;
+    CollAppend (&SegmentList, S);
 
     /* And return it... */
     return S;
@@ -154,6 +120,25 @@ static Segment* NewSegment (const char* Name, unsigned SegType)
 
 
 
+static Segment* NewSegment (const char* Name, unsigned char AddrSize)
+/* Create a new segment, insert it into the global list and return it */
+{
+    /* Check for too many segments */
+    if (CollCount (&SegmentList) >= 256) {
+       Fatal ("Too many segments");
+    }
+
+    /* Check the segment name for invalid names */
+    if (!ValidSegName (Name)) {
+       Error ("Illegal segment name: `%s'", Name);
+    }
+
+    /* Create a new segment and return it */
+    return NewSegFromDef (NewSegDef (Name, AddrSize));
+}
+
+
+
 Fragment* GenFragment (unsigned char Type, unsigned short Len)
 /* Generate a new fragment, add it to the current segment and return it. */
 {
@@ -181,8 +166,16 @@ Fragment* GenFragment (unsigned char Type, unsigned short Len)
 
     /* Increment the program counter */
     ActiveSeg->PC += F->Len;
-    if (!RelocMode) {
-       AbsPC += F->Len;
+    if (OrgPerSeg) {
+        /* Relocatable mode is switched per segment */
+        if (!ActiveSeg->RelocMode) {
+            ActiveSeg->AbsPC += F->Len;
+        }
+    } else {
+        /* Relocatable mode is switched globally */
+        if (!RelocMode) {
+            AbsPC += F->Len;
+        }
     }
 
     /* Return the fragment */
@@ -194,29 +187,28 @@ Fragment* GenFragment (unsigned char Type, unsigned short Len)
 void UseSeg (const SegDef* D)
 /* Use the segment with the given name */
 {
-    Segment* Seg = SegmentList;
-    while (Seg) {
+    unsigned I;
+    for (I = 0; I < CollCount (&SegmentList); ++I) {
+        Segment* Seg = CollAtUnchecked (&SegmentList, I);
                if (strcmp (Seg->Def->Name, D->Name) == 0) {
            /* We found this segment. Check if the type is identical */
-           if (D->Type != SEGTYPE_DEFAULT && Seg->Def->Type != D->Type) {
-               Error (ERR_SEG_ATTR_MISMATCH);
-               /* Use the new attribute to avoid errors */
-               Seg->Def->Type = D->Type;
+           if (D->AddrSize != ADDR_SIZE_DEFAULT &&
+                Seg->Def->AddrSize != D->AddrSize) {
+               Error ("Segment attribute mismatch");
+               /* Use the new attribute to avoid errors */
+               Seg->Def->AddrSize = D->AddrSize;
                    }
                    ActiveSeg = Seg;
            return;
        }
-       /* Check next segment */
-       Seg = Seg->List;
     }
 
     /* Segment is not in list, create a new one */
-    if (D->Type == SEGTYPE_DEFAULT) {
-        Seg = NewSegment (D->Name, SEGTYPE_ABS);
+    if (D->AddrSize == ADDR_SIZE_DEFAULT) {
+        ActiveSeg = NewSegment (D->Name, ADDR_SIZE_ABS);
     } else {
-        Seg = NewSegment (D->Name, D->Type);
+        ActiveSeg = NewSegment (D->Name, D->AddrSize);
     }
-    ActiveSeg = Seg;
 }
 
 
@@ -224,32 +216,61 @@ void UseSeg (const SegDef* D)
 unsigned long GetPC (void)
 /* Get the program counter of the current segment */
 {
-    return RelocMode? ActiveSeg->PC : AbsPC;
+    if (OrgPerSeg) {
+        /* Relocatable mode is switched per segment */
+        return ActiveSeg->RelocMode? ActiveSeg->PC : ActiveSeg->AbsPC;
+    } else {
+        /* Relocatable mode is switched globally */
+        return RelocMode? ActiveSeg->PC : AbsPC;
+    }
 }
 
 
 
-void SetAbsPC (unsigned long PC)
-/* Set the program counter in absolute mode */
+void EnterAbsoluteMode (unsigned long PC)
+/* Enter absolute (non relocatable mode). Depending on the OrgPerSeg flag,
+ * this will either switch the mode globally or for the current segment.
+ */
 {
-    RelocMode = 0;
-    AbsPC = PC;
+    if (OrgPerSeg) {
+        /* Relocatable mode is switched per segment */
+        ActiveSeg->RelocMode = 0;
+        ActiveSeg->AbsPC = PC;
+    } else {
+        /* Relocatable mode is switched globally */
+        RelocMode = 0;
+        AbsPC = PC;
+    }
 }
 
 
 
-const SegDef* GetCurrentSeg (void)
-/* Get a pointer to the segment defininition of the current segment */
+int GetRelocMode (void)
+/* Return true if we're currently in relocatable mode */
 {
-    return ActiveSeg->Def;
+    if (OrgPerSeg) {
+        /* Relocatable mode is switched per segment */
+        return ActiveSeg->RelocMode;
+    } else {
+        /* Relocatable mode is switched globally */
+        return RelocMode;
+    }
 }
 
 
 
-unsigned GetSegNum (void)
-/* Get the number of the current segment */
+void EnterRelocMode (void)
+/* Enter relocatable mode. Depending on the OrgPerSeg flag, this will either
+ * switch the mode globally or for the current segment.
+ */
 {
-    return ActiveSeg->Num;
+    if (OrgPerSeg) {
+        /* Relocatable mode is switched per segment */
+        ActiveSeg->RelocMode = 1;
+    } else {
+        /* Relocatable mode is switched globally */
+        RelocMode = 1;
+    }
 }
 
 
@@ -290,39 +311,16 @@ void SegAlign (unsigned Power, int Val)
 
 
 
-int IsZPSeg (void)
-/* Return true if the current segment is a zeropage segment */
+unsigned char GetSegAddrSize (unsigned SegNum)
+/* Return the address size of the segment with the given number */
 {
-    return (ActiveSeg->Def->Type == SEGTYPE_ZP);
-}
-
-
-
-int IsFarSeg (void)
-/* Return true if the current segment is a far segment */
-{
-    return (ActiveSeg->Def->Type == SEGTYPE_FAR);
-}
-
-
-
-unsigned GetSegType (unsigned SegNum)
-/* Return the type of the segment with the given number */
-{
-    /* Search for the segment */
-    Segment* S = SegmentList;
-    while (S && SegNum) {
-       --SegNum;
-       S = S->List;
-    }
-
-    /* Did we find it? */
-    if (S == 0) {
+    /* Is there such a segment? */
+    if (SegNum >= CollCount (&SegmentList)) {
        FAIL ("Invalid segment number");
     }
 
-    /* Return the segment type */
-    return S->Def->Type;
+    /* Return the address size */
+    return ((Segment*) CollAtUnchecked (&SegmentList, SegNum))->Def->AddrSize;
 }
 
 
@@ -330,65 +328,79 @@ unsigned GetSegType (unsigned SegNum)
 void SegCheck (void)
 /* Check the segments for range and other errors */
 {
-    Segment* S = SegmentList;
-    while (S) {
+    static const unsigned long U_Hi[4] = {
+               0x000000FFUL, 0x0000FFFFUL, 0x00FFFFFFUL, 0xFFFFFFFFUL
+    };
+    static const long S_Hi[4] = {
+               0x0000007FL, 0x00007FFFL, 0x007FFFFFL, 0x7FFFFFFFL
+    };
+
+    unsigned I;
+    for (I = 0; I < CollCount (&SegmentList); ++I) {
+        Segment* S = CollAtUnchecked (&SegmentList, I);
        Fragment* F = S->Root;
        while (F) {
                    if (F->Type == FRAG_EXPR || F->Type == FRAG_SEXPR) {
-                       F->V.Expr = FinalizeExpr (F->V.Expr);
-                       if (IsConstExpr (F->V.Expr)) {
-                   /* We are able to evaluate the expression. Get the value
-                    * and check for range errors.
-                    */
-                   unsigned I;
-                   long Val = GetExprVal (F->V.Expr);
-                   int Abs = (F->Type != FRAG_SEXPR);
-
-                   if (F->Len == 1) {
-                       if (Abs) {
-                           /* Absolute value */
-                           if (Val > 255) {
-                               PError (&F->Pos, ERR_RANGE);
-                           }
-                       } else {
-                           /* PC relative value */
-                           if (Val < -128 || Val > 127) {
-                               PError (&F->Pos, ERR_RANGE);
-                           }
-                       }
-                   } else if (F->Len == 2) {
-                       if (Abs) {
-                           /* Absolute value */
-                           if (Val > 65535) {
-                               PError (&F->Pos, ERR_RANGE);
-                           }
-                       } else {
-                           /* PC relative value */
-                           if (Val < -32768 || Val > 32767) {
-                               PError (&F->Pos, ERR_RANGE);
-                           }
-                       }
-                   }
-
-                   /* Convert the fragment into a literal fragment */
-                   for (I = 0; I < F->Len; ++I) {
-                       F->V.Data [I] = Val & 0xFF;
-                       Val >>= 8;
+
+                /* We have an expression, study it */
+                ExprDesc ED;
+                ED_Init (&ED);
+                StudyExpr (F->V.Expr, &ED);
+
+                /* Try to simplify it before looking further */
+                F->V.Expr = SimplifyExpr (F->V.Expr, &ED);
+
+                /* Check if the expression is constant */
+                if (ED_IsConst (&ED)) {
+
+                   unsigned J;
+
+                           /* The expression is constant. Check for range errors. */
+                    CHECK (F->Len <= 4);
+                    if (F->Type == FRAG_SEXPR) {
+                        long Hi = S_Hi[F->Len-1];
+                        long Lo = ~Hi;
+                        if (ED.Val > Hi || ED.Val < Lo) {
+                            LIError (&F->LI,
+                                     "Range error (%ld not in [%ld..%ld])",
+                                     ED.Val, Lo, Hi);
+                        }
+                    } else {
+                        if (((unsigned long)ED.Val) > U_Hi[F->Len-1]) {
+                            LIError (&F->LI,
+                                     "Range error (%lu not in [0..%lu])",
+                                     (unsigned long)ED.Val, U_Hi[F->Len-1]);
+                        }
+                    }
+
+                    /* We don't need the expression tree any longer */
+                    FreeExpr (F->V.Expr);
+
+                   /* Convert the fragment into a literal fragment */
+                   for (J = 0; J < F->Len; ++J) {
+                       F->V.Data[J] = ED.Val & 0xFF;
+                       ED.Val >>= 8;
                    }
-                   F->Type = FRAG_LITERAL;
-               } else {
-                   /* We cannot evaluate the expression now, leave the job for
-                    * the linker. However, we are able to check for explicit
-                    * byte expressions and we will do so.
-                    */
-                   if (F->Type == FRAG_EXPR && F->Len == 1 && !IsByteExpr (F->V.Expr)) {
-                       PError (&F->Pos, ERR_RANGE);
-                   }
-               }
-           }
-           F = F->Next;
-       }
-       S = S->List;
+                   F->Type = FRAG_LITERAL;
+
+               } else if (ED.AddrSize != ADDR_SIZE_DEFAULT) {
+
+                   /* We cannot evaluate the expression now, leave the job for
+                    * the linker. However, we can check if the address size
+                     * matches the fragment size, and we will do so.
+                    */
+                    if ((F->Len == 1 && ED.AddrSize > ADDR_SIZE_ZP)  ||
+                        (F->Len == 2 && ED.AddrSize > ADDR_SIZE_ABS) ||
+                        (F->Len == 3 && ED.AddrSize > ADDR_SIZE_FAR)) {
+                       LIError (&F->LI, "Range error");
+                   }
+               }
+
+                /* Release memory allocated for the expression decriptor */
+                ED_Done (&ED);
+           }
+           F = F->Next;
+       }
     }
 }
 
@@ -397,10 +409,12 @@ void SegCheck (void)
 void SegDump (void)
 /* Dump the contents of all segments */
 {
+    unsigned I;
     unsigned X = 0;
-    Segment* S = SegmentList;
+
     printf ("\n");
-    while (S) {
+    for (I = 0; I < CollCount (&SegmentList); ++I) {
+        Segment* S = CollAtUnchecked (&SegmentList, I);
        unsigned I;
        Fragment* F;
        int State = -1;
@@ -420,7 +434,7 @@ void SegDump (void)
            } else if (F->Type == FRAG_EXPR || F->Type == FRAG_SEXPR) {
                        State = 1;
                printf ("\n  Expression (%u): ", F->Len);
-               DumpExpr (F->V.Expr);
+               DumpExpr (F->V.Expr, SymResolve);
            } else if (F->Type == FRAG_FILL) {
                State = 1;
                printf ("\n  Fill bytes (%u)", F->Len);
@@ -433,18 +447,60 @@ void SegDump (void)
            F = F->Next;
        }
        printf ("\n  End PC = $%04X\n", (unsigned)(S->PC & 0xFFFF));
-       S = S->List;
     }
     printf ("\n");
 }
 
 
 
+void InitSegments (void)
+/* Initialize segments */
+{
+    /* Create the predefined segments. Code segment is active */
+    ActiveSeg = NewSegFromDef (&CodeSegDef);
+    NewSegFromDef (&RODataSegDef);
+    NewSegFromDef (&BssSegDef);
+    NewSegFromDef (&DataSegDef);
+    NewSegFromDef (&ZeropageSegDef);
+    NewSegFromDef (&NullSegDef);
+}
+
+
+
+void SetSegmentSizes (void)
+/* Set the default segment sizes according to the memory model */
+{
+    /* Initialize segment sizes. The segment definitions do already contain
+     * the correct values for the default case (near), so we must only change
+     * things that should be different.
+     */
+    switch (MemoryModel) {
+
+        case MMODEL_NEAR:
+            break;
+
+        case MMODEL_FAR:
+            CodeSegDef.AddrSize = ADDR_SIZE_FAR;
+            break;
+
+        case MMODEL_HUGE:
+            CodeSegDef.AddrSize   = ADDR_SIZE_FAR;
+            DataSegDef.AddrSize   = ADDR_SIZE_FAR;
+            BssSegDef.AddrSize    = ADDR_SIZE_FAR;
+            RODataSegDef.AddrSize = ADDR_SIZE_FAR;
+            break;
+
+        default:
+            Internal ("Invalid memory model: %d", MemoryModel);
+    }
+}
+
+
+
 static void WriteOneSeg (Segment* Seg)
 /* Write one segment to the object file */
 {
     Fragment* Frag;
-    unsigned LineInfoIndex;
     unsigned long DataSize;
     unsigned long EndPos;
 
@@ -458,7 +514,7 @@ static void WriteOneSeg (Segment* Seg)
     ObjWriteVar (GetStringId (Seg->Def->Name)); /* Name of the segment */
     ObjWrite32 (Seg->PC);                       /* Size */
     ObjWrite8 (Seg->Align);                     /* Segment alignment */
-    ObjWrite8 (Seg->Def->Type);                 /* Type of the segment */
+    ObjWrite8 (Seg->Def->AddrSize);             /* Address size of the segment */
     ObjWriteVar (Seg->FragCount);               /* Number of fragments */
 
     /* Now walk through the fragment list for this segment and write the
@@ -508,14 +564,8 @@ static void WriteOneSeg (Segment* Seg)
 
        }
 
-               /* Write the file position of this fragment */
-       ObjWritePos (&Frag->Pos);
-
-       /* Write extra line info for this fragment. Zero is considered
-        * "no line info", so add one to the value.
-        */
-       LineInfoIndex = Frag->LI? Frag->LI->Index + 1 : 0;
-       ObjWriteVar (LineInfoIndex);
+               /* Write the line infos for this fragment */
+               WriteLineInfo (&Frag->LI);
 
        /* Next fragment */
        Frag = Frag->Next;
@@ -534,21 +584,18 @@ static void WriteOneSeg (Segment* Seg)
 void WriteSegments (void)
 /* Write the segment data to the object file */
 {
-    Segment* Seg;
+    unsigned I;
 
     /* Tell the object file module that we're about to start the seg list */
     ObjStartSegments ();
 
     /* First thing is segment count */
-    ObjWriteVar (SegmentCount);
+    ObjWriteVar (CollCount (&SegmentList));
 
     /* Now walk through all segments and write them to the object file */
-    Seg = SegmentList;
-    while (Seg) {
+    for (I = 0; I < CollCount (&SegmentList); ++I) {
        /* Write one segment */
-       WriteOneSeg (Seg);
-       /* Next segment */
-       Seg = Seg->List;
+       WriteOneSeg (CollAtUnchecked (&SegmentList, I));
     }
 
     /* Done writing segments */
@@ -557,3 +604,4 @@ void WriteSegments (void)
 
 
 
+