]> git.sur5r.net Git - cc65/blobdiff - src/ld65/o65.c
Added support for arbitrary alignments.
[cc65] / src / ld65 / o65.c
index a30d5916451bbf55d24ad50958e63e89001aaa3d..1c8eb6cc1db510516198e8ef24c96c563a6ad77a 100644 (file)
@@ -6,10 +6,10 @@
 /*                                                                           */
 /*                                                                           */
 /*                                                                           */
-/* (C) 1999     Ullrich von Bassewitz                                        */
-/*              Wacholderweg 14                                              */
-/*              D-70597 Stuttgart                                            */
-/* EMail:       uz@musoftware.de                                             */
+/* (C) 1999-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 <stdio.h>
 #include <string.h>
+#include <limits.h>
 #include <errno.h>
 #include <time.h>
 
-#include "../common/version.h"
-#include "../common/xmalloc.h"
+/* common */
+#include "chartype.h"
+#include "check.h"
+#include "print.h"
+#include "version.h"
+#include "xmalloc.h"
 
+/* ld65 */
 #include "error.h"
 #include "exports.h"
 #include "expr.h"
 #include "fileio.h"
 #include "global.h"
+#include "lineinfo.h"
+#include "memarea.h"
 #include "o65.h"
+#include "spool.h"
 
 
 
 
 
 /* Header mode bits */
+#define MF_CPU_65816    0x8000         /* Executable is for 65816 */
+#define MF_CPU_6502     0x0000          /* Executable is for the 6502 */
+#define MF_CPU_MASK     0x8000          /* Mask to extract CPU type */
+
+#define MF_RELOC_PAGE   0x4000          /* Page wise relocation */
+#define MF_RELOC_BYTE   0x0000          /* Byte wise relocation */
+#define MF_RELOC_MASK   0x4000          /* Mask to extract relocation type */
+
 #define MF_SIZE_32BIT  0x2000          /* All size words are 32bit */
-#define MF_CPU_816             0x8000          /* Executable is for 65816 */
+#define MF_SIZE_16BIT   0x0000          /* All size words are 16bit */
+#define MF_SIZE_MASK    0x2000          /* Mask to extract size */
+
+#define MF_FTYPE_OBJ    0x1000          /* Object file */
+#define MF_FTYPE_EXE    0x0000          /* Executable file */
+#define MF_FTYPE_MASK   0x1000          /* Mask to extract type */
+
+#define MF_ADDR_SIMPLE  0x0800          /* Simple addressing */
+#define MF_ADDR_DEFAULT 0x0000          /* Default addressing */
+#define MF_ADDR_MASK    0x0800          /* Mask to extract addressing */
+
+#define MF_ALIGN_1      0x0000          /* Bytewise alignment */
+#define MF_ALIGN_2      0x0001          /* Align words */
+#define MF_ALIGN_4      0x0002          /* Align longwords */
+#define MF_ALIGN_256    0x0003          /* Align pages (256 bytes) */
+#define MF_ALIGN_MASK   0x0003                 /* Mask to extract alignment */
 
 /* The four o65 segment types. Note: These values are identical to the values
  * needed for the segmentID in the o65 spec.
 #define O65SEG_TEXT    0x02
 #define O65SEG_DATA    0x03
 #define O65SEG_BSS     0x04
-#define O65SEG_ZP      0x05
+#define O65SEG_ZP      0x05
 
 /* Relocation type codes for the o65 format */
 #define O65RELOC_WORD  0x80
 #define O65RELOC_HIGH  0x40
 #define O65RELOC_LOW   0x20
-#define O65RELOC_SEGADR        0xc0
-#define O65RELOC_SEG   0xa0
+#define O65RELOC_SEGADR        0xC0
+#define O65RELOC_SEG   0xA0
+#define O65RELOC_MASK   0xE0
 
 /* O65 executable file header */
-typedef struct O65Header_ O65Header;
-struct O65Header_ {
+typedef struct O65Header O65Header;
+struct O65Header {
     unsigned       Version;            /* Version number for o65 format */
     unsigned        Mode;                      /* Mode word */
     unsigned long   TextBase;          /* Base address of text segment */
@@ -94,75 +127,91 @@ struct O65Header_ {
 };
 
 /* An o65 option */
-typedef struct O65Option_ O65Option;
-struct O65Option_ {
+typedef struct O65Option O65Option;
+struct O65Option {
     O65Option*     Next;               /* Next in option list */
-    unsigned char   Type;              /* Type of option */
-    unsigned char   Len;               /* Data length */
-    unsigned char   Data [1];          /* Data, dynamically allocated */
+    unsigned char   Type;              /* Type of option */
+    unsigned char   Len;               /* Data length */
+    unsigned char   Data [1];          /* Data, dynamically allocated */
 };
 
 /* A o65 relocation table */
-#define RELOC_BLOCKSIZE        4096
-typedef struct O65RelocTab_ O65RelocTab;
-struct O65RelocTab_ {
-    unsigned       Size;               /* Size of the table */
-    unsigned       Fill;               /* Amount used */
-    unsigned char*  Buf;               /* Buffer, dynamically allocated */
+typedef struct O65RelocTab O65RelocTab;
+struct O65RelocTab {
+    unsigned       Size;               /* Size of the table */
+    unsigned       Fill;               /* Amount used */
+    unsigned char*  Buf;               /* Buffer, dynamically allocated */
 };
 
 /* Structure describing the format */
-struct O65Desc_ {
-    O65Header      Header;             /* File header */
-    O65Option*     Options;            /* List of file options */
-    ExtSymTab*     Exports;            /* Table with exported symbols */
-    ExtSymTab*     Imports;            /* Table with imported symbols */
-    unsigned       Undef;              /* Count of undefined symbols */
-    FILE*          F;                  /* The file we're writing to */
-    char*          Filename;           /* Name of the output file */
-    O65RelocTab*    TextReloc;         /* Relocation table for text segment */
-    O65RelocTab*    DataReloc;         /* Relocation table for data segment */
-
-    unsigned       TextCount;          /* Number of segments assigned to .text */
-    SegDesc**      TextSeg;            /* Array of text segments */
-    unsigned       DataCount;          /* Number of segments assigned to .data */
-    SegDesc**      DataSeg;            /* Array of data segments */
-    unsigned       BssCount;           /* Number of segments assigned to .bss */
-    SegDesc**      BssSeg;             /* Array of bss segments */
-    unsigned       ZPCount;            /* Number of segments assigned to .zp */
-    SegDesc**      ZPSeg;              /* Array of zp segments */
+struct O65Desc {
+    O65Header      Header;             /* File header */
+    O65Option*     Options;            /* List of file options */
+    ExtSymTab*     Exports;            /* Table with exported symbols */
+    ExtSymTab*     Imports;            /* Table with imported symbols */
+    unsigned               Undef;              /* Count of undefined symbols */
+    FILE*          F;                  /* The file we're writing to */
+    const char*     Filename;          /* Name of the output file */
+    O65RelocTab*    TextReloc;         /* Relocation table for text segment */
+    O65RelocTab*    DataReloc;         /* Relocation table for data segment */
+
+    unsigned       TextCount;          /* Number of segments assigned to .text */
+    SegDesc**      TextSeg;            /* Array of text segments */
+    unsigned       DataCount;          /* Number of segments assigned to .data */
+    SegDesc**      DataSeg;            /* Array of data segments */
+    unsigned       BssCount;           /* Number of segments assigned to .bss */
+    SegDesc**      BssSeg;             /* Array of bss segments */
+    unsigned       ZPCount;            /* Number of segments assigned to .zp */
+    SegDesc**      ZPSeg;              /* Array of zp segments */
 
     /* Temporary data for writing segments */
     unsigned long   SegSize;
     O65RelocTab*    CurReloc;
-    long           LastOffs;
+    long           LastOffs;
 };
 
 /* Structure for parsing expression trees */
-typedef struct ExprDesc_ ExprDesc;
-struct ExprDesc_ {
-    O65Desc*       D;                  /* File format descriptor */
-    long           Val;                /* The offset value */
+typedef struct ExprDesc ExprDesc;
+struct ExprDesc {
+    O65Desc*               D;                  /* File format descriptor */
+    long                   Val;                /* The offset value */
     int                    TooComplex;         /* Expression too complex */
-    Section*       SegRef;             /* Section referenced if any */
-    ExtSym*        ExtRef;             /* External reference if any */
+    MemoryArea*     MemRef;             /* Memory reference if any */
+    Segment*        SegRef;             /* Segment reference if any */
+    Section*               SecRef;             /* Section reference if any */
+    ExtSym*                ExtRef;             /* External reference if any */
 };
 
 
 
 /*****************************************************************************/
-/*                            Helper functions                              */
+/*                            Helper functions                              */
 /*****************************************************************************/
 
 
 
+static ExprDesc* InitExprDesc (ExprDesc* ED, O65Desc* D)
+/* Initialize an ExprDesc structure for use with O65ParseExpr */
+{
+    ED->D         = D;
+    ED->Val       = 0;
+    ED->TooComplex = 0;
+    ED->MemRef     = 0;
+    ED->SegRef     = 0;
+    ED->SecRef     = 0;
+    ED->ExtRef     = 0;
+    return ED;
+}
+
+
+
 static void WriteSize (const O65Desc* D, unsigned long Val)
 /* Write a "size" word to the file */
 {
-    if (D->Header.Mode & MF_SIZE_32BIT) {
-       Write32 (D->F, Val);
-    } else {
-       Write16 (D->F, (unsigned) Val);
+    switch (D->Header.Mode & MF_SIZE_MASK) {
+       case MF_SIZE_16BIT:     Write16 (D->F, (unsigned) Val); break;
+       case MF_SIZE_32BIT:     Write32 (D->F, Val);            break;
+       default:                Internal ("Invalid size in header: %04X", D->Header.Mode);
     }
 }
 
@@ -178,20 +227,113 @@ static unsigned O65SegType (const SegDesc* S)
      * to check SF_ZP first.
      */
     if (S->Flags & SF_RO) {
-       return O65SEG_TEXT;
+       return O65SEG_TEXT;
     } else if (S->Flags & SF_ZP) {
-       return O65SEG_ZP;
+       return O65SEG_ZP;
     } else if (S->Flags & SF_BSS) {
-       return O65SEG_BSS;
+       return O65SEG_BSS;
     } else {
-       return O65SEG_DATA;
+       return O65SEG_DATA;
     }
 }
 
 
 
+static void CvtMemoryToSegment (ExprDesc* ED)
+/* Convert a memory area into a segment by searching the list of run segments
+ * in this memory area and assigning the nearest one.
+ */
+{
+    /* Get the memory area from the expression */
+    MemoryArea* M = ED->MemRef;
+
+    /* Remember the "nearest" segment and its offset */
+    Segment* Nearest   = 0;
+    unsigned long Offs = ULONG_MAX;
+
+    /* Walk over all segments */
+    unsigned I;
+    for (I = 0; I < CollCount (&M->SegList); ++I) {
+
+        /* Get the segment and check if it's a run segment */
+       SegDesc* S = CollAtUnchecked (&M->SegList, I);
+        if (S->Run == M) {
+
+            unsigned long O;
+
+            /* Get the segment from the segment descriptor */
+            Segment* Seg = S->Seg;
+
+            /* Check the PC. */
+            if ((long) Seg->PC <= ED->Val && (O = (ED->Val - Seg->PC)) < Offs) {
+                /* This is the nearest segment for now */
+                Offs = O;
+                Nearest = Seg;
+
+                /* If we found an exact match, don't look further */
+                if (Offs == 0) {
+                    break;
+                }
+            }
+        }
+    }
+
+    /* If we found a segment, use it and adjust the offset */
+    if (Nearest) {
+        ED->SegRef = Nearest;
+        ED->MemRef = 0;
+        ED->Val    -= Nearest->PC;
+    }
+}
+
+
+
+static const SegDesc* FindSeg (SegDesc** const List, unsigned Count, const Segment* S)
+/* Search for a segment in the given list of segment descriptors and return
+ * the descriptor for a segment if we found it, and NULL if not.
+ */
+{
+    unsigned I;
+
+    for (I = 0; I < Count; ++I) {
+       if (List[I]->Seg == S) {
+           /* Found */
+           return List[I];
+       }
+    }
+
+    /* Not found */
+    return 0;
+}
+
+
+
+static const SegDesc* O65FindSeg (const O65Desc* D, const Segment* S)
+/* Search for a segment in the segment lists and return it's segment descriptor */
+{
+    const SegDesc* SD;
+
+    if ((SD = FindSeg (D->TextSeg, D->TextCount, S)) != 0) {
+       return SD;
+    }
+    if ((SD = FindSeg (D->DataSeg, D->DataCount, S)) != 0) {
+       return SD;
+    }
+    if ((SD = FindSeg (D->BssSeg, D->BssCount, S)) != 0) {
+       return SD;
+    }
+    if ((SD = FindSeg (D->ZPSeg, D->ZPCount, S)) != 0) {
+       return SD;
+    }
+
+    /* Not found */
+    return 0;
+}
+
+
+
 /*****************************************************************************/
-/*                           Expression handling                            */
+/*                           Expression handling                            */
 /*****************************************************************************/
 
 
@@ -207,11 +349,7 @@ static void O65ParseExpr (ExprNode* Expr, ExprDesc* D, int Sign)
     switch (Expr->Op) {
 
        case EXPR_LITERAL:
-           if (Sign < 0) {
-               D->Val -= Expr->V.Val;
-           } else {
-               D->Val += Expr->V.Val;
-           }
+            D->Val += (Sign * Expr->V.IVal);
            break;
 
        case EXPR_SYMBOL:
@@ -236,18 +374,46 @@ static void O65ParseExpr (ExprNode* Expr, ExprDesc* D, int Sign)
                }
            } else {
                MarkExport (E);
-               O65ParseExpr (E->Expr, D, Sign);
+               O65ParseExpr (E->Expr, D, Sign);
                UnmarkExport (E);
            }
            break;
 
-       case EXPR_SEGMENT:
+       case EXPR_SECTION:
+           if (D->SecRef) {
+               /* We cannot handle more than one segment reference in o65 */
+               D->TooComplex = 1;
+           } else {
+               /* Remember the segment reference */
+               D->SecRef = GetExprSection (Expr);
+                /* Add the offset of the section to the constant value */
+                D->Val += Sign * (D->SecRef->Offs + D->SecRef->Seg->PC);
+           }
+           break;
+
+        case EXPR_SEGMENT:
            if (D->SegRef) {
                /* We cannot handle more than one segment reference in o65 */
                D->TooComplex = 1;
            } else {
-               /* Remember the segment reference */
-               D->SegRef = GetExprSection (Expr);
+               /* Remember the segment reference */
+                       D->SegRef = Expr->V.Seg;
+                /* Add the offset of the segment to the constant value */
+                D->Val += (Sign * D->SegRef->PC);
+           }
+           break;
+
+        case EXPR_MEMAREA:
+           if (D->MemRef) {
+               /* We cannot handle more than one memory reference in o65 */
+               D->TooComplex = 1;
+           } else {
+               /* Remember the memory area reference */
+                       D->MemRef = Expr->V.Mem;
+                /* Add the start address of the memory area to the constant
+                 * value
+                 */
+                D->Val += (Sign * D->MemRef->Start);
            }
            break;
 
@@ -284,9 +450,9 @@ static O65RelocTab* NewO65RelocTab (void)
     O65RelocTab* R = xmalloc (sizeof (O65RelocTab));
 
     /* Initialize the data */
-    R->Size = RELOC_BLOCKSIZE;
+    R->Size = 0;
     R->Fill = 0;
-    R->Buf  = xmalloc (RELOC_BLOCKSIZE);
+    R->Buf  = 0;
 
     /* Return the created struct */
     return R;
@@ -309,10 +475,12 @@ static void O65RelocPutByte (O65RelocTab* R, unsigned B)
     /* Do we have enough space in the buffer? */
     if (R->Fill == R->Size) {
        /* We need to grow the buffer */
-               unsigned char* NewBuf = xmalloc (R->Size + RELOC_BLOCKSIZE);
-       memcpy (NewBuf, R->Buf, R->Size);
-       xfree (R->Buf);
-       R->Buf = NewBuf;
+        if (R->Size) {
+            R->Size *= 2;
+        } else {
+            R->Size = 1024;     /* Initial size */
+        }
+        R->Buf = xrealloc (R->Buf, R->Size);
     }
 
     /* Put the byte into the buffer */
@@ -339,7 +507,7 @@ static void O65WriteReloc (O65RelocTab* R, FILE* F)
 
 
 /*****************************************************************************/
-/*                             Option handling                              */
+/*                             Option handling                              */
 /*****************************************************************************/
 
 
@@ -376,7 +544,7 @@ static void FreeO65Option (O65Option* O)
 
 
 /*****************************************************************************/
-/*                    Subroutines to write o65 sections                     */
+/*                    Subroutines to write o65 sections                     */
 /*****************************************************************************/
 
 
@@ -390,7 +558,6 @@ static void O65WriteHeader (O65Desc* D)
 
     O65Option* O;
 
-
     /* Write the fixed header */
     WriteData (D->F, Trailer, sizeof (Trailer));
     Write8    (D->F, D->Header.Version);
@@ -423,16 +590,17 @@ static void O65WriteHeader (O65Desc* D)
 
 
 static unsigned O65WriteExpr (ExprNode* E, int Signed, unsigned Size,
-                                     unsigned long Offs, void* Data)
+                                     unsigned long Offs, void* Data)
 /* Called from SegWrite for an expression. Evaluate the expression, check the
  * range and write the expression value to the file, update the relocation
  * table.
  */
 {
-    long Diff;
-    long BinVal;
-    ExprNode* Expr;
-    ExprDesc ED;
+    long          Diff;
+    unsigned      RefCount;
+    long          BinVal;
+    ExprNode*     Expr;
+    ExprDesc      ED;
     unsigned char RelocType;
 
     /* Cast the Data pointer to its real type, an O65Desc */
@@ -448,11 +616,11 @@ static unsigned O65WriteExpr (ExprNode* E, int Signed, unsigned Size,
      * Calculate the number of bytes between this entry and the last one, and
      * setup all necessary intermediate bytes in the relocation table.
      */
-    Offs += D->SegSize;                /* Calulate full offset */
+    Offs += D->SegSize;                /* Calulate full offset */
     Diff = ((long) Offs) - D->LastOffs;
     while (Diff > 0xFE) {
-       O65RelocPutByte (D->CurReloc, 0xFF);
-       Diff -= 0xFE;
+               O65RelocPutByte (D->CurReloc, 0xFF);
+               Diff -= 0xFE;
     }
     O65RelocPutByte (D->CurReloc, (unsigned char) Diff);
 
@@ -461,86 +629,131 @@ static unsigned O65WriteExpr (ExprNode* E, int Signed, unsigned Size,
 
     /* Determine the expression to relocate */
     Expr = E;
-    if (E->Op == EXPR_LOBYTE || E->Op == EXPR_HIBYTE) {
-       /* Use the real expression */
+    if (E->Op == EXPR_BYTE0 || E->Op == EXPR_BYTE1 ||
+               E->Op == EXPR_BYTE2 || E->Op == EXPR_BYTE3 ||
+               E->Op == EXPR_WORD0 || E->Op == EXPR_WORD1) {
+               /* Use the real expression */
                Expr = E->Left;
     }
 
-    /* Initialize the descriptor for expression parsing */
-    ED.D         = D;
-    ED.Val       = 0;
-    ED.TooComplex = 0;
-    ED.SegRef     = 0;
-    ED.ExtRef     = 0;
-
     /* Recursively collect information about this expression */
-    O65ParseExpr (Expr, &ED, 1);
+    O65ParseExpr (Expr, InitExprDesc (&ED, D), 1);
 
-    /* We cannot handle both, an imported symbol and a segment ref */
-    if (ED.SegRef != 0 && ED.ExtRef != 0) {
-       ED.TooComplex = 1;
+    /* We cannot handle more than one external reference */
+    RefCount = (ED.MemRef != 0) + (ED.SegRef != 0) +
+               (ED.SecRef != 0) + (ED.ExtRef != 0);
+    if (RefCount > 1) {
+               ED.TooComplex = 1;
+    }
+
+    /* If we have a memory area reference, we need to convert it into a
+     * segment reference. If we cannot do that, we cannot handle the
+     * expression.
+     */
+    if (ED.MemRef) {
+        CvtMemoryToSegment (&ED);
+        if (ED.SegRef == 0) {
+            return SEG_EXPR_TOO_COMPLEX;
+        }
     }
 
     /* Bail out if we cannot handle the expression */
     if (ED.TooComplex) {
-       return SEG_EXPR_TOO_COMPLEX;
+               return SEG_EXPR_TOO_COMPLEX;
     }
 
-    /* Safety: Check that we are really referencing a symbol or a segment */
-    CHECK (ED.SegRef != 0 || ED.ExtRef != 0);
+    /* Safety: Check that we have exactly one reference */
+    CHECK (RefCount == 1);
 
     /* Write out the offset that goes into the segment. */
     BinVal = ED.Val;
-    if (E->Op == EXPR_LOBYTE) {
-       BinVal &= 0x00FF;
-    } else if (E->Op == EXPR_HIBYTE) {
-               BinVal = (BinVal >> 8) & 0x00FF;
+    switch (E->Op) {
+               case EXPR_BYTE0:    BinVal &= 0xFF;                     break;
+               case EXPR_BYTE1:    BinVal = (BinVal >>  8) & 0xFF;     break;
+               case EXPR_BYTE2:    BinVal = (BinVal >> 16) & 0xFF;     break;
+               case EXPR_BYTE3:    BinVal = (BinVal >> 24) & 0xFF;     break;
+               case EXPR_WORD0:    BinVal &= 0xFFFF;                   break;
+               case EXPR_WORD1:    BinVal = (BinVal >> 16) & 0xFFFF;   break;
     }
     WriteVal (D->F, BinVal, Size);
 
     /* Determine the actual type of relocation entry needed from the
      * information gathered about the expression.
      */
-    if (E->Op == EXPR_LOBYTE) {
-       RelocType = O65RELOC_LOW;
-    } else if (E->Op == EXPR_HIBYTE) {
-       RelocType = O65RELOC_HIGH;
+    if (E->Op == EXPR_BYTE0) {
+               RelocType = O65RELOC_LOW;
+    } else if (E->Op == EXPR_BYTE1) {
+               RelocType = O65RELOC_HIGH;
+    } else if (E->Op == EXPR_BYTE2) {
+               RelocType = O65RELOC_SEG;
     } else {
-       switch (Size) {
+               switch (Size) {
 
-           case 1:
-               RelocType = O65RELOC_LOW;
-               break;
+                   case 1:
+                       RelocType = O65RELOC_LOW;
+                       break;
 
-           case 2:
-               RelocType = O65RELOC_WORD;
-               break;
+                   case 2:
+                       RelocType = O65RELOC_WORD;
+                       break;
 
-           case 3:
-               RelocType = O65RELOC_SEGADR;
-               break;
+                   case 3:
+                       RelocType = O65RELOC_SEGADR;
+                       break;
 
-           case 4:
-               /* 4 byte expression not supported by o65 */
-               return SEG_EXPR_TOO_COMPLEX;
+                   case 4:
+                       /* 4 byte expression not supported by o65 */
+                       return SEG_EXPR_TOO_COMPLEX;
 
-           default:
-               Internal ("O65WriteExpr: Invalid expression size: %u", Size);
-               RelocType = 0;          /* Avoid gcc warnings */
-       }
+                   default:
+                       Internal ("O65WriteExpr: Invalid expression size: %u", Size);
+                       RelocType = 0;          /* Avoid gcc warnings */
+               }
     }
 
     /* Determine which segment we're referencing */
-    if (ED.ExtRef) {
-       /* Imported symbol */
-       RelocType |= O65SEG_UNDEF;
+    if (ED.SegRef || ED.SecRef) {
+
+        const SegDesc* Seg;
+
+        /* Segment or section reference. */
+        if (ED.SecRef) {
+            /* Get segment from section */
+            ED.SegRef = ED.SecRef->Seg;
+        }
+
+               /* Search for the segment and map it to it's o65 segmentID */
+               Seg = O65FindSeg (D, ED.SegRef);
+       if (Seg == 0) {
+           /* For some reason, we didn't find this segment in the list of
+            * segments written to the o65 file.
+            */
+           return SEG_EXPR_INVALID;
+       }
+       RelocType |= O65SegType (Seg);
+       O65RelocPutByte (D->CurReloc, RelocType);
+
+       /* Output additional data if needed */
+       switch (RelocType & O65RELOC_MASK) {
+           case O65RELOC_HIGH:
+               O65RelocPutByte (D->CurReloc, ED.Val & 0xFF);
+               break;
+           case O65RELOC_SEG:
+               O65RelocPutWord (D->CurReloc, ED.Val & 0xFFFF);
+               break;
+       }
+
+    } else if (ED.ExtRef) {
+               /* Imported symbol */
+               RelocType |= O65SEG_UNDEF;
                O65RelocPutByte (D->CurReloc, RelocType);
                /* Put the number of the imported symbol into the table */
                O65RelocPutWord (D->CurReloc, ExtSymNum (ED.ExtRef));
-    } else {
-       /* Segment reference */
 
+    } else {
 
+        /* OOPS - something bad happened */
+        Internal ("External reference not handled");
 
     }
 
@@ -567,37 +780,35 @@ static void O65WriteSeg (O65Desc* D, SegDesc** Seg, unsigned Count, int DoWrite)
                S = Seg [I];
 
        /* Keep the user happy */
-       if (Verbose) {
-           printf ("    Writing `%s'\n", S->Name);
-       }
+       Print (stdout, 1, "    Writing `%s'\n", GetString (S->Name));
 
        /* Write this segment */
-       if (DoWrite) {
-                   SegWrite (D->F, S->Seg, O65WriteExpr, D);
-       }
+               if (DoWrite) {
+                   SegWrite (D->Filename, D->F, S->Seg, O65WriteExpr, D);
+               }
 
-       /* Mark the segment as dumped */
-       S->Seg->Dumped = 1;
+               /* Mark the segment as dumped */
+               S->Seg->Dumped = 1;
 
-       /* Calculate the total size */
-       D->SegSize += S->Seg->Size;
+               /* Calculate the total size */
+               D->SegSize += S->Seg->Size;
     }
 
-    /* Terminate the relocation table for the this segment */
+    /* Terminate the relocation table for this segment */
     if (D->CurReloc) {
         O65RelocPutByte (D->CurReloc, 0);
     }
 
     /* Check the size of the segment for overflow */
-    if ((D->Header.Mode & MF_SIZE_32BIT) == 0 && D->SegSize > 0xFFFF) {
-       Error ("Segment overflow in file `%s'", D->Filename);
+    if ((D->Header.Mode & MF_SIZE_MASK) == MF_SIZE_16BIT && D->SegSize > 0xFFFF) {
+               Error ("Segment overflow in file `%s'", D->Filename);
     }
 
 }
 
 
 
-static void O65WriteTextSeg (O65Desc* D, Memory* M)
+static void O65WriteTextSeg (O65Desc* D)
 /* Write the code segment to the o65 output file */
 {
     /* Initialize variables */
@@ -612,7 +823,7 @@ static void O65WriteTextSeg (O65Desc* D, Memory* M)
 
 
 
-static void O65WriteDataSeg (O65Desc* D, Memory* M)
+static void O65WriteDataSeg (O65Desc* D)
 /* Write the data segment to the o65 output file */
 {
     /* Initialize variables */
@@ -627,7 +838,7 @@ static void O65WriteDataSeg (O65Desc* D, Memory* M)
 
 
 
-static void O65WriteBssSeg (O65Desc* D, Memory* M)
+static void O65WriteBssSeg (O65Desc* D)
 /* "Write" the bss segments to the o65 output file. This will only update
  * the relevant header fields.
  */
@@ -644,7 +855,7 @@ static void O65WriteBssSeg (O65Desc* D, Memory* M)
 
 
 
-static void O65WriteZPSeg (O65Desc* D, Memory* M)
+static void O65WriteZPSeg (O65Desc* D)
 /* "Write" the zeropage segments to the o65 output file. This will only update
  * the relevant header fields.
  */
@@ -664,20 +875,20 @@ static void O65WriteZPSeg (O65Desc* D, Memory* M)
 static void O65WriteImports (O65Desc* D)
 /* Write the list of imported symbols to the O65 file */
 {
-    const ExtSym* E;
+    const ExtSym* S;
 
-    /* Write the number of external symbols */
+    /* Write the number of imports */
     WriteSize (D, ExtSymCount (D->Imports));
 
     /* Write out the symbol names, zero terminated */
-    E = ExtSymList (D->Imports);
-    while (E) {
-       /* Get the name */
-       const char* Name = ExtSymName (E);
-       /* And write it to the output file */
-       WriteData (D->F, Name, strlen (Name) + 1);
-       /* Next symbol */
-       E = ExtSymNext (E);
+    S = ExtSymList (D->Imports);
+    while (S) {
+       /* Get the name */
+       const char* Name = GetString (ExtSymName (S));
+       /* And write it to the output file */
+       WriteData (D->F, Name, strlen (Name) + 1);
+       /* Next symbol */
+       S = ExtSymNext (S);
     }
 }
 
@@ -702,8 +913,87 @@ static void O65WriteDataReloc (O65Desc* D)
 static void O65WriteExports (O65Desc* D)
 /* Write the list of exports */
 {
-    /* For now... */
-    WriteSize (D, 0);
+    const ExtSym* S;
+
+    /* Write the number of exports */
+    WriteSize (D, ExtSymCount (D->Exports));
+
+    /* Write out the symbol information */
+    S = ExtSymList (D->Exports);
+    while (S) {
+
+       ExprNode* Expr;
+       unsigned char SegmentID;
+       ExprDesc ED;
+
+       /* Get the name */
+       unsigned NameIdx = ExtSymName (S);
+        const char* Name = GetString (NameIdx);
+
+       /* Get the export for this symbol. We've checked before that this
+        * export does really exist, so if it is unresolved, or if we don't
+        * find it, there is an error in the linker code.
+        */
+       Export* E = FindExport (NameIdx);
+       if (E == 0 || IsUnresolvedExport (E)) {
+           Internal ("Unresolved export `%s' found in O65WriteExports", Name);
+       }
+
+       /* Get the expression for the symbol */
+       Expr = E->Expr;
+
+       /* Recursively collect information about this expression */
+       O65ParseExpr (Expr, InitExprDesc (&ED, D), 1);
+
+               /* We cannot handle expressions with imported symbols, or expressions
+         * with more than one segment reference here
+         */
+       if (ED.ExtRef != 0 || (ED.SegRef != 0 && ED.SecRef != 0)) {
+           ED.TooComplex = 1;
+       }
+
+       /* Bail out if we cannot handle the expression */
+       if (ED.TooComplex) {
+           Error ("Expression for symbol `%s' is too complex", Name);
+       }
+
+       /* Determine the segment id for the expression */
+        if (ED.SegRef != 0 || ED.SecRef != 0) {
+
+            const SegDesc* Seg;
+
+            /* Segment or section reference */
+            if (ED.SecRef != 0) {
+                ED.SegRef = ED.SecRef->Seg;     /* Get segment from section */
+            }
+
+                   /* Search for the segment and map it to it's o65 segmentID */
+           Seg = O65FindSeg (D, ED.SegRef);
+           if (Seg == 0) {
+               /* For some reason, we didn't find this segment in the list of
+                * segments written to the o65 file.
+                */
+               Error ("Segment for symbol `%s' is undefined", Name);
+           }
+           SegmentID = O65SegType (Seg);
+
+        } else {
+
+           /* Absolute value */
+           SegmentID = O65SEG_ABS;
+
+       }
+
+       /* Write the name to the output file */
+       WriteData (D->F, Name, strlen (Name) + 1);
+
+       /* Output the segment id followed by the literal value */
+               Write8 (D->F, SegmentID);
+       WriteSize (D, ED.Val);
+
+       /* Next symbol */
+       S = ExtSymNext (S);
+    }
 }
 
 
@@ -787,10 +1077,26 @@ void FreeO65Desc (O65Desc* D)
 
 
 
-void O65Set816 (O65Desc* D)
+void O65Set6502 (O65Desc* D)
+/* Enable 6502 mode */
+{
+    D->Header.Mode = (D->Header.Mode & ~MF_CPU_MASK) | MF_CPU_6502;
+}
+
+
+
+void O65Set65816 (O65Desc* D)
 /* Enable 816 mode */
 {
-    D->Header.Mode |= MF_CPU_816;
+    D->Header.Mode = (D->Header.Mode & ~MF_CPU_MASK) | MF_CPU_65816;
+}
+
+
+
+void O65SetSmallModel (O65Desc* D)
+/* Enable a small memory model executable */
+{
+    D->Header.Mode = (D->Header.Mode & ~MF_SIZE_MASK) | MF_SIZE_16BIT;
 }
 
 
@@ -798,24 +1104,24 @@ void O65Set816 (O65Desc* D)
 void O65SetLargeModel (O65Desc* D)
 /* Enable a large memory model executable */
 {
-    D->Header.Mode |= MF_SIZE_32BIT;
+    D->Header.Mode = (D->Header.Mode & ~MF_SIZE_MASK) | MF_SIZE_32BIT;
 }
 
 
 
-void O65SetAlignment (O65Desc* D, unsigned Align)
+void O65SetAlignment (O65Desc* D, unsigned Alignment)
 /* Set the executable alignment */
 {
     /* Remove all alignment bits from the mode word */
-    D->Header.Mode &= ~0x0003;
+    D->Header.Mode &= ~MF_ALIGN_MASK;
 
     /* Set the alignment bits */
-    switch (Align) {
-       case 1:                           break;
-       case 2:   D->Header.Mode |= 0x01; break;
-       case 4:   D->Header.Mode |= 0x02; break;
-        case 256: D->Header.Mode |= 0x03; break;
-        default:  Error ("Invalid alignment for O65 format: %u", Align);
+    switch (Alignment) {
+       case 1:   D->Header.Mode |= MF_ALIGN_1;   break;
+       case 2:   D->Header.Mode |= MF_ALIGN_2;   break;
+       case 4:   D->Header.Mode |= MF_ALIGN_4;   break;
+        case 256: D->Header.Mode |= MF_ALIGN_256; break;
+        default:  Error ("Invalid alignment for O65 format: %u", Alignment);
     }
 }
 
@@ -834,32 +1140,35 @@ void O65SetOption (O65Desc* D, unsigned Type, const void* Data, unsigned DataLen
 
 
 
-void O65SetOS (O65Desc* D, unsigned OS)
+void O65SetOS (O65Desc* D, unsigned OS, unsigned Version, unsigned Id)
 /* Set an option describing the target operating system */
 {
-    static const unsigned char OSA65 [2] = { O65OS_OSA65, 0 };
-    static const unsigned char Lunix [2] = { O65OS_LUNIX, 0 };
+    /* Setup the option data */
+    unsigned char Opt[4];
+    Opt[0] = OS;
+    Opt[1] = Version;
 
-    /* Write the correct option */
+    /* Write the correct option length */
     switch (OS) {
 
-       case O65OS_OSA65:
-           O65SetOption (D, O65OPT_OS, OSA65, sizeof (OSA65));
-           break;
-
-       case O65OS_LUNIX:
-           O65SetOption (D, O65OPT_OS, Lunix, sizeof (Lunix));
+       case O65OS_CC65:
+            /* Set the 16 bit id */
+            Opt[2] = (unsigned char) Id;
+            Opt[3] = (unsigned char) (Id >> 8);
+           O65SetOption (D, O65OPT_OS, Opt, 4);
            break;
 
        default:
-           Internal ("Trying to set invalid O65 operating system: %u", OS);
+            /* No id for OS/A65, Lunix, and unknown OSes */
+                   O65SetOption (D, O65OPT_OS, Opt, 2);
+           break;
 
     }
 }
 
 
 
-ExtSym* O65GetImport (O65Desc* D, const char* Ident)
+ExtSym* O65GetImport (O65Desc* D, unsigned Ident)
 /* Return the imported symbol or NULL if not found */
 {
     /* Retrieve the symbol from the table */
@@ -868,7 +1177,7 @@ ExtSym* O65GetImport (O65Desc* D, const char* Ident)
 
 
 
-void O65SetImport (O65Desc* D, const char* Ident)
+void O65SetImport (O65Desc* D, unsigned Ident)
 /* Set an imported identifier */
 {
     /* Insert the entry into the table */
@@ -877,7 +1186,7 @@ void O65SetImport (O65Desc* D, const char* Ident)
 
 
 
-ExtSym* O65GetExport (O65Desc* D, const char* Ident)
+ExtSym* O65GetExport (O65Desc* D, unsigned Ident)
 /* Return the exported symbol or NULL if not found */
 {
     /* Retrieve the symbol from the table */
@@ -886,20 +1195,27 @@ ExtSym* O65GetExport (O65Desc* D, const char* Ident)
 
 
 
-void O65SetExport (O65Desc* D, const char* Ident)
+void O65SetExport (O65Desc* D, unsigned Ident)
 /* Set an exported identifier */
 {
+    /* Get the export for this symbol and check if it does exist and is
+     * a resolved symbol.
+     */
+    Export* E = FindExport (Ident);
+    if (E == 0 || IsUnresolvedExport (E)) {
+       Error ("Unresolved export: `%s'", GetString (Ident));
+    }
+
     /* Insert the entry into the table */
     NewExtSym (D->Exports, Ident);
 }
 
 
 
-static void O65SetupSegments (O65Desc* D, Memory* M)
+static void O65SetupSegments (O65Desc* D, File* F)
 /* Setup segment assignments */
 {
-    MemListNode* N;
-    SegDesc* S;
+    unsigned I;
     unsigned TextIdx, DataIdx, BssIdx, ZPIdx;
 
     /* Initialize the counters */
@@ -908,24 +1224,27 @@ static void O65SetupSegments (O65Desc* D, Memory* M)
     D->BssCount  = 0;
     D->ZPCount   = 0;
 
-    /* Walk through the memory list and count the segment types */
-    N = M->SegList;
-    while (N) {
-
-               /* Get the segment from the list node */
-               S = N->Seg;
-
-               /* Check the segment type. */
-       switch (O65SegType (S)) {
-           case O65SEG_TEXT:   D->TextCount++; break;
-           case O65SEG_DATA:   D->DataCount++; break;
-           case O65SEG_BSS:    D->BssCount++;  break;
-           case O65SEG_ZP:     D->ZPCount++;   break;
-           default:            Internal ("Invalid return from O65SegType");
-       }
-
-       /* Next segment node */
-       N = N->Next;
+    /* Walk over the memory list */
+    for (I = 0; I < CollCount (&F->MemoryAreas); ++I) {
+        /* Get this entry */
+        MemoryArea* M = CollAtUnchecked (&F->MemoryAreas, I);
+
+        /* Walk through the segment list and count the segment types */
+        unsigned J;
+        for (J = 0; J < CollCount (&M->SegList); ++J) {
+
+            /* Get the segment */
+            SegDesc* S = CollAtUnchecked (&M->SegList, J);
+
+            /* Check the segment type. */
+            switch (O65SegType (S)) {
+                case O65SEG_TEXT:   D->TextCount++; break;
+                case O65SEG_DATA:   D->DataCount++; break;
+                case O65SEG_BSS:    D->BssCount++;  break;
+                case O65SEG_ZP:            D->ZPCount++;   break;
+                default:                   Internal ("Invalid return from O65SegType");
+            }
+        }
     }
 
     /* Allocate memory according to the numbers */
@@ -936,29 +1255,32 @@ static void O65SetupSegments (O65Desc* D, Memory* M)
 
     /* Walk again through the list and setup the segment arrays */
     TextIdx = DataIdx = BssIdx = ZPIdx = 0;
-    N = M->SegList;
-    while (N) {
-
-       /* Get the segment from the list node */
-       S = N->Seg;
-
-       /* Check the segment type. */
-       switch (O65SegType (S)) {
-           case O65SEG_TEXT:   D->TextSeg [TextIdx++] = S;     break;
-           case O65SEG_DATA:   D->DataSeg [DataIdx++] = S;     break;
-           case O65SEG_BSS:    D->BssSeg [BssIdx++]   = S;     break;
-           case O65SEG_ZP:     D->ZPSeg [ZPIdx++]     = S;     break;
-           default:            Internal ("Invalid return from O65SegType");
-       }
-
-       /* Next segment node */
-       N = N->Next;
+    for (I = 0; I < CollCount (&F->MemoryAreas); ++I) {
+        /* Get this entry */
+        MemoryArea* M = CollAtUnchecked (&F->MemoryAreas, I);
+
+        /* Walk over the segment list and check the segment types */
+        unsigned J;
+        for (J = 0; J < CollCount (&M->SegList); ++J) {
+
+            /* Get the segment */
+            SegDesc* S = CollAtUnchecked (&M->SegList, J);
+
+            /* Check the segment type. */
+            switch (O65SegType (S)) {
+                case O65SEG_TEXT:   D->TextSeg [TextIdx++] = S;        break;
+                case O65SEG_DATA:   D->DataSeg [DataIdx++] = S;        break;
+                case O65SEG_BSS:    D->BssSeg [BssIdx++]   = S; break;
+                case O65SEG_ZP:            D->ZPSeg [ZPIdx++]     = S; break;
+                default:           Internal ("Invalid return from O65SegType");
+            }
+        }
     }
 }
 
 
 
-static int O65Unresolved (const char* Name, void* D)
+static int O65Unresolved (unsigned Name, void* D)
 /* Called if an unresolved symbol is encountered */
 {
     /* Check if the symbol is an imported o65 symbol */
@@ -974,67 +1296,99 @@ static int O65Unresolved (const char* Name, void* D)
 
 
 
+static void O65SetupHeader (O65Desc* D)
+/* Set additional stuff in the header */
+{
+    /* Set the base addresses of the segments */
+    if (D->TextCount > 0) {
+        SegDesc* FirstSeg  = D->TextSeg [0];
+        D->Header.TextBase = FirstSeg->Seg->PC;
+    }
+    if (D->DataCount > 0) {
+        SegDesc* FirstSeg  = D->DataSeg [0];
+        D->Header.DataBase = FirstSeg->Seg->PC;
+    }
+    if (D->BssCount > 0) {
+        SegDesc* FirstSeg  = D->BssSeg [0];
+        D->Header.BssBase = FirstSeg->Seg->PC;
+    }
+    if (D->ZPCount > 0) {
+        SegDesc* FirstSeg = D->ZPSeg [0];
+        D->Header.ZPBase  = FirstSeg->Seg->PC;
+    }
+
+    /* If we have byte wise relocation and an alignment of 1, we can set
+     * the "simple addressing" bit in the header.
+     */
+    if ((D->Header.Mode & MF_RELOC_MASK) == MF_RELOC_BYTE &&
+        (D->Header.Mode & MF_ALIGN_MASK) == MF_ALIGN_1) {
+        D->Header.Mode = (D->Header.Mode & ~MF_ADDR_MASK) | MF_ADDR_SIMPLE;
+    }
+}
+
+
+
 void O65WriteTarget (O65Desc* D, File* F)
 /* Write an o65 output file */
 {
-    Memory* M;
-    char OptBuf [256]; /* Buffer for option strings */
-    time_t T;
+    char        OptBuf [256];  /* Buffer for option strings */
+    unsigned    OptLen;
+    time_t      T;
 
     /* Place the filename in the control structure */
-    D->Filename = F->Name;
-
-    /* The o65 format uses only one memory area per file. Check that. */
-    M = F->MemList;
-    if (M->Next != 0) {
-       Warning ("Cannot handle more than one memory area for o65 format");
-    }
+    D->Filename = GetString (F->Name);
 
     /* Check for unresolved symbols. The function O65Unresolved is called
      * if we get an unresolved symbol.
      */
     D->Undef = 0;              /* Reset the counter */
-    CheckExports (O65Unresolved, D);
+    CheckUnresolvedImports (O65Unresolved, D);
     if (D->Undef > 0) {
        /* We had unresolved symbols, cannot create output file */
                Error ("%u unresolved external(s) found - cannot create output file", D->Undef);
     }
 
     /* Setup the segment arrays */
-    O65SetupSegments (D, M);
+    O65SetupSegments (D, F);
+
+    /* Setup additional stuff in the header */
+    O65SetupHeader (D);
 
     /* Open the file */
-    D->F = fopen (F->Name, "wb");
+    D->F = fopen (D->Filename, "wb");
     if (D->F == 0) {
-       Error ("Cannot open `%s': %s", F->Name, strerror (errno));
+       Error ("Cannot open `%s': %s", D->Filename, strerror (errno));
     }
 
     /* Keep the user happy */
-    if (Verbose) {
-       printf ("Opened `%s'...\n", F->Name);
-    }
+    Print (stdout, 1, "Opened `%s'...\n", D->Filename);
 
     /* Define some more options: A timestamp and the linker version */
     T = time (0);
     strcpy (OptBuf, ctime (&T));
-    O65SetOption (D, O65OPT_TIMESTAMP, OptBuf, strlen (OptBuf) + 1);
-    sprintf (OptBuf, "ld65 V%u.%u.%u", VER_MAJOR, VER_MINOR, VER_PATCH);
+    OptLen = strlen (OptBuf);
+    while (OptLen > 0 && IsControl (OptBuf[OptLen-1])) {
+        --OptLen;
+    }
+    OptBuf[OptLen] = '\0';
+    O65SetOption (D, O65OPT_TIMESTAMP, OptBuf, OptLen + 1);
+    sprintf (OptBuf, "ld65 V%s", GetVersionAsString ());
     O65SetOption (D, O65OPT_ASM, OptBuf, strlen (OptBuf) + 1);
 
     /* Write the header */
     O65WriteHeader (D);
 
     /* Write the text segment */
-    O65WriteTextSeg (D, M);
+    O65WriteTextSeg (D);
 
     /* Write the data segment */
-    O65WriteDataSeg (D, M);
+    O65WriteDataSeg (D);
 
     /* "Write" the bss segments */
-    O65WriteBssSeg (D, M);
+    O65WriteBssSeg (D);
 
     /* "Write" the zeropage segments */
-    O65WriteZPSeg (D, M);
+    O65WriteZPSeg (D);
 
     /* Write the undefined references list */
     O65WriteImports (D);
@@ -1054,7 +1408,7 @@ void O65WriteTarget (O65Desc* D, File* F)
 
     /* Close the file */
     if (fclose (D->F) != 0) {
-       Error ("Cannot write to `%s': %s", F->Name, strerror (errno));
+       Error ("Cannot write to `%s': %s", D->Filename, strerror (errno));
     }
 
     /* Reset the file and filename */