/* */
/* */
/* */
-/* (C) 1998-2003 Ullrich von Bassewitz */
-/* Römerstrasse 52 */
+/* (C) 1998-2007 Ullrich von Bassewitz */
+/* Roemerstrasse 52 */
/* D-70794 Filderstadt */
/* EMail: uz@cc65.org */
/* */
#include <errno.h>
/* common */
+#include "addrsize.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) */
-};
-
+/* 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 */
+/* Segment initializer macro */
#define SEG(segdef, num, prev) \
- { prev, 0, 0, 0, num, 0, 0, segdef }
+ { prev, 0, 0, 0, num, 0, 1, 0, 0, segdef }
/* 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);
+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);
/* Predefined segments */
static Segment NullSeg = SEG (&NullSegDef, 5, NULL);
static unsigned SegmentCount = 6;
/* List of all segments */
-static Segment* SegmentList = &CodeSeg;
+Segment* SegmentList = &CodeSeg;
static Segment* SegmentLast = &NullSeg;
/* Currently active segment */
-static Segment* ActiveSeg = &CodeSeg;
+Segment* ActiveSeg = &CodeSeg;
-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 */
{
Segment* S;
/* Check for too many segments */
if (SegmentCount >= 256) {
- Fatal (FAT_TOO_MANY_SEGMENTS);
+ Fatal ("Too many segments");
}
/* Check the segment name for invalid names */
if (!ValidSegName (Name)) {
- Error (ERR_ILLEGAL_SEGMENT, Name);
+ Error ("Illegal segment name: `%s'", Name);
}
/* Create a new segment */
S->FragCount = 0;
S->Num = SegmentCount++;
S->Align = 0;
+ S->RelocMode = 1;
S->PC = 0;
- S->Def = NewSegDef (Name, SegType);
+ S->AbsPC = 0;
+ S->Def = NewSegDef (Name, AddrSize);
/* Insert it into the segment list */
SegmentLast->List = S;
/* 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 */
while (Seg) {
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);
+ if (D->AddrSize != ADDR_SIZE_DEFAULT &&
+ Seg->Def->AddrSize != D->AddrSize) {
+ Error ("Segment attribute mismatch");
/* Use the new attribute to avoid errors */
- Seg->Def->Type = D->Type;
+ Seg->Def->AddrSize = D->AddrSize;
}
ActiveSeg = Seg;
return;
}
/* 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) {
+ Seg = NewSegment (D->Name, ADDR_SIZE_ABS);
} else {
- Seg = NewSegment (D->Name, D->Type);
+ Seg = 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 */
-{
- 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 */
+unsigned char GetSegAddrSize (unsigned SegNum)
+/* Return the address size of the segment with the given number */
{
/* Search for the segment */
Segment* S = SegmentList;
FAIL ("Invalid segment number");
}
- /* Return the segment type */
- return S->Def->Type;
+ /* Return the address size */
+ return S->Def->AddrSize;
}
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);
+
+ /* 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)) {
+
+ /* The expression is constant. Check for range errors. */
int Abs = (F->Type != FRAG_SEXPR);
+ long Val = ED.Val;
+ unsigned I;
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);
- }
- }
+ if (Abs) {
+ /* Absolute value */
+ if (Val > 255) {
+ LIError (&F->LI, "Range error (%ld not in [0..255])", Val);
+ }
+ } else {
+ /* PC relative value */
+ if (Val < -128 || Val > 127) {
+ LIError (&F->LI, "Range error (%ld not in [-128..127])", Val);
+ }
+ }
} 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;
+ /* Absolute value */
+ if (Val > 65535) {
+ LIError (&F->LI, "Range error (%ld not in [0..65535])", Val);
+ }
+ } else {
+ /* PC relative value */
+ if (Val < -32768 || Val > 32767) {
+ LIError (&F->LI, "Range error (%ld not in [-32768..32767])", Val);
+ }
+ }
}
- 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;
+
+ /* We don't need the expression tree any longer */
+ FreeExpr (F->V.Expr);
+
+ /* Convert the fragment into a literal fragment */
+ for (I = 0; I < F->Len; ++I) {
+ F->V.Data [I] = Val & 0xFF;
+ Val >>= 8;
+ }
+ 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;
+ }
+ S = S->List;
}
}
} 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);
/* 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 InitSegments (void)
+/* Initialize segments */
+{
+ /* 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);
+ }
+}
+
+
+
void WriteSegments (void)
/* Write the segment data to the object file */
{
+