/* */
/* */
/* */
-/* (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 */
#include <errno.h>
/* common */
+#include "addrsize.h"
+#include "coll.h"
+#include "mmodel.h"
#include "segnames.h"
#include "xmalloc.h"
#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;
-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;
+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. */
{
/* 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 */
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;
}
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;
+ }
}
-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;
}
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;
+ }
}
}
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;
} 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);
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;
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
}
- /* 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;
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 */
+