/* */
/* */
/* */
-/* (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 */
+/* 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, ADDR_SIZE_ABS);
static unsigned SegmentCount = 6;
/* List of all segments */
-static Segment* SegmentList = &CodeSeg;
+Segment* SegmentList = &CodeSeg;
static Segment* SegmentLast = &NullSeg;
/* Currently active segment */
-static Segment* NewSegment (const char* Name, unsigned AddrSize)
+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->AbsPC = 0;
S->Def = NewSegDef (Name, AddrSize);
/* Insert it into the segment list */
/* 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 */
/* We found this segment. Check if the type is identical */
if (D->AddrSize != ADDR_SIZE_DEFAULT &&
Seg->Def->AddrSize != D->AddrSize) {
- Error (ERR_SEG_ATTR_MISMATCH);
+ Error ("Segment attribute mismatch");
/* Use the new attribute to avoid errors */
Seg->Def->AddrSize = D->AddrSize;
}
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 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.
+ */
+{
+ if (OrgPerSeg) {
+ /* Relocatable mode is switched per segment */
+ ActiveSeg->RelocMode = 0;
+ ActiveSeg->AbsPC = PC;
+ } else {
+ /* Relocatable mode is switched globally */
+ RelocMode = 0;
+ AbsPC = PC;
+ }
}
-void SetAbsPC (unsigned long PC)
-/* Set the program counter in absolute mode */
+int GetRelocMode (void)
+/* Return true if we're currently in relocatable mode */
{
- RelocMode = 0;
- AbsPC = PC;
+ if (OrgPerSeg) {
+ /* Relocatable mode is switched per segment */
+ return ActiveSeg->RelocMode;
+ } else {
+ /* Relocatable mode is switched globally */
+ return RelocMode;
+ }
+}
+
+
+
+void EnterRelocMode (void)
+/* Enter relocatable mode. Depending on the OrgPerSeg flag, this will either
+ * switch the mode globally or for the current segment.
+ */
+{
+ if (OrgPerSeg) {
+ /* Relocatable mode is switched per segment */
+ ActiveSeg->RelocMode = 1;
+ } else {
+ /* Relocatable mode is switched globally */
+ RelocMode = 1;
+ }
}
-unsigned GetSegAddrSize (unsigned SegNum)
+unsigned char GetSegAddrSize (unsigned SegNum)
/* Return the address size of the segment with the given number */
{
/* Search for the segment */
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);
- }
- }
- }
+ /* 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);
+ }
+ }
+ }
/* 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;
+ /* 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 {
- /* 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;
+ }
+ 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;
}
- /* 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 */
{
+