*** empty log message ***
[fw/sdcc] / as / xa51 / xa_link.c
index 7c376f156bd35e9e3b64abaadf6e7984e88bce2c..3284925ddac7e783847d058ac2c2de9e6380bdff 100644 (file)
 
    "SDCCXA rel, version %f" must be the first line, sort of MAGIC word
    "H %d areas %d global symbols" defines the # of areas and globals
-   "S <symbol> [Ref0000 | DefXXXX]" Def's are supposed to be defined in
-     their own area/segment
+   "S <symbol> [Ref0000 | DefXXXX | AbsXXXX]" Def's are supposed to be
+     defined in their own area/segment
    "A <seg> size %d flags %d" switch to another segment. this can happen
      multiple times and should be equal. flags is ignored for now
-   "T xx xx bb bb ..." where xx xx is the address within the current segment
-     and bb are the bytes
-   "R xx <how> <symbol>" the relocation info. xx is the offset within the
-     previous "T .." line. <how> could be something like REL_FF, REL_FFFF, 
-     ABS_F0FF. symbol is the (previous) defined symbol it refers to
+   "T xxxx <how> <symbol> 0"
+   "R xxxx <how> <symbol> <pc+>" the relocation info. xxxx is the address
+     within relative code space. How is something like REL_FF, REL_FFFF, 
+     ABS_F0FF. Symbol is the referenced symbol and pc+ is the program 
+     counter that will be used to calculate the relative address (that is
+     the address of the following instruction).
 
    So, this is not a standalone linker. It will only link files generated
    by xa_asm, which will only process files generated by the xa51 sdcc
@@ -59,11 +60,40 @@ enum {
   MAX_SEGMENTS
 };
 
+enum {
+  REL_FF=1,
+  REL_FFFF,
+  ABS_0F,
+  ABS_FF,
+  ABS_03FF,
+  ABS_07FF,
+  ABS_F0FF,
+  ABS_FFFF,
+  ABS_0F00FF,
+  MAX_REFS
+};
+
+char *refModes[]={
+  "???",
+  "REL_FF",
+  "REL_FFFF",
+  "ABS_0F",
+  "ABS_FF",
+  "ABS_03FF",
+  "ABS_07FF",
+  "ABS_F0FF",
+  "ABS_FFFF",
+  "ABS_0F00FF",
+};
+
 #define CODESIZE 0x10000
 
-char codeImage[CODESIZE];
+int fatalErrors=0;
+
 char gsinitImage[CODESIZE];
+char csegImage[CODESIZE];
 char xinitImage[CODESIZE];
+//char gsfinalImage[CODESIZE];
 
 struct SEGMENT {
   short id;
@@ -74,10 +104,10 @@ struct SEGMENT {
   int current;
   unsigned char *image;
 } segments[MAX_SEGMENTS]={
-  {0,       "????",    0, 0, 0, 0, NULL},
+  {0,       "???",    0, 0, 0, 0, NULL},
 
   {GSINIT,  "GSINIT",  0, 0, 0, 0, gsinitImage},
-  {CSEG,    "CSEG",    0, 0, 0, 0, codeImage},
+  {CSEG,    "CSEG",    0, 0, 0, 0, csegImage},
   {XINIT,   "XINIT",   0, 0, 0, 0, xinitImage},
   //{GSFINAL, "GSFINAL", 0, 0, 0, 0, NULL},
 
@@ -99,27 +129,60 @@ struct MODULE {
 struct SYMBOL {
   char *name;
   struct MODULE *module;
+  int lineno;
   struct SEGMENT *segment;
+  char absolute;
   int address;
   struct SYMBOL *next;
   struct SYMBOL *last;
 } *symbols=NULL;
 
+struct REFERENCE {
+  char *name;
+  struct MODULE *module;
+  int lineno;
+  unsigned address, pc;
+  short how;
+  struct REFERENCE *next;
+  struct REFERENCE *last;
+} *references=NULL;
+
 char *libPaths[128];
 int nlibPaths=0;
 char *libFiles[128];
 int nlibFiles=0;
 
-static char outFileName[PATH_MAX];
+static char outFileName[PATH_MAX]={'\0'};
 
 struct SEGMENT *currentSegment;
 struct MODULE *currentModule;
+int currentLine;
+
+int howToReference(char *how) {
+  int r;
+  for (r=1; r<MAX_REFS; r++) {
+    if (strcmp(refModes[r], how)==0) {
+      return r;
+    }
+  }
+  return 0;
+}
 
-struct SEGMENT *findSegment(char *segment) {
-  int i;
-  for (i=1; i<MAX_SEGMENTS; i++) {
-    if (strcmp(segments[i].name, segment)==0) {
-      return &segments[i];
+struct SEGMENT *findSegmentByName(char *segment) {
+  int s;
+  for (s=0; s<MAX_SEGMENTS; s++) {
+    if (strcmp(segments[s].name, segment)==0) {
+      return &segments[s];
+    }
+  }
+  return 0;
+}
+
+struct SYMBOL *findSymbolByName(char *symName) {
+  struct SYMBOL *symbol;
+  for (symbol=symbols; symbol; symbol=symbol->next) {
+    if (strcmp(symbol->name, symName)==0) {
+      return symbol;
     }
   }
   return 0;
@@ -144,20 +207,49 @@ void addToModules (char *name) {
   currentModule=modules->last=module;
 }
 
-void addToRefs(char *ref) {
+void addToRefs(char *ref, int address, char *how, int pc) {
+  struct REFERENCE *reference;
+
   //fprintf (stderr, "addToRefs: %s\n", ref);
+
+  reference=calloc(1, sizeof(struct REFERENCE));
+  reference->name=strdup(ref);
+  reference->module=currentModule;
+  reference->lineno=currentLine;
+  reference->address=address;
+  reference->how=howToReference(how);
+  reference->pc=pc;
+  if (!references) {
+    references=reference;
+  } else {
+    references->last->next=reference;
+  }
+  references->last=reference;
 }
 
-void addToDefs(char *def, int address) {
+void addToDefs(char *def, int address, char absolute) {
   struct SYMBOL *symbol;
+
   /* fprintf (stderr, "addToDefs: %s %s 0x%04x + 0x%04x\n", 
      currentSegment->name, def, 
      currentModule->offset[currentSegment->id],
      address); */
+
+  // no duplicates allowed
+  if ((symbol=findSymbolByName(def))) {
+    fprintf (stderr, "*** %s:%d duplicate symbol %s first defined in "
+            "module %s:%d\n", 
+            currentModule->name, currentLine, def, 
+            symbol->module->name, symbol->lineno);
+    fatalErrors++;
+  }
+
   symbol=calloc(1, sizeof(struct SYMBOL));
   symbol->name=strdup(def);
   symbol->module=currentModule;
+  symbol->lineno=currentLine;
   symbol->segment=currentSegment;
+  symbol->absolute=absolute;
   symbol->address=currentModule->offset[currentSegment->id]+address;
   if (!symbols) {
     symbols=symbol;
@@ -169,7 +261,8 @@ void addToDefs(char *def, int address) {
 }
 
 void syntaxError (char *err) {
-  fprintf (stderr, "error while parsing '%s'\n", err);
+  fprintf (stderr, "*** %s:%d error while parsing '%s'\n", 
+          currentModule->name, currentLine, err);
   exit(1);
 }
 
@@ -206,7 +299,8 @@ void readModule(char *module) {
   FILE *relModule;
   char moduleName[PATH_MAX];
   int segments, globals;
-  int currentLine=1;
+
+  currentLine=1;
 
   if ((relModule=fopen(module, "r"))==NULL) {
     perror (module);
@@ -218,11 +312,11 @@ void readModule(char *module) {
   // first we need to check if this is a valid file
   if (sscanf(fgets(line, 132, relModule), 
             "SDCCXA rel, version %lf", &hisVersion)!=1) {
-    fprintf (stderr, "%s is not a valid input file\n", module);
+    fprintf (stderr, "*** %s is not a valid input file\n", module);
     exit (1);
   }
   if (hisVersion!=version) {
-    fprintf (stderr, "WARNING: version conflict; "
+    fprintf (stderr, "*** WARNING: version conflict; "
             "we(%1.1f) != %s(%1.1f)\n", 
             version, module, hisVersion);
   }
@@ -258,23 +352,23 @@ void readModule(char *module) {
        int size, flags;
        if (sscanf(line, "A %[^ ] size %d flags %d",
                   segment, &size, &flags)!=3) {
-         fprintf (stderr, "%s:%d error in A record line\n", 
-                  module, currentLine);
-         exit (1);
+         syntaxError(line);
        }
        // do we know this segment?
-       if (!(currentSegment=findSegment(segment))) {
-         fprintf (stderr, "%s:%d unknown area: %s\n", module,
+       if (!(currentSegment=findSegmentByName(segment))) {
+         fprintf (stderr, "*** %s:%d unknown area: %s\n", module,
                   currentLine, segment);
          exit (1);
        }
        if (currentModule->size[currentSegment->id]) {
          if (currentModule->size[currentSegment->id] != size) {
-           fprintf (stderr, "%s:%d error %s %d %d\n",
+           fprintf (stderr, "*** %s:%d error %s size %d != %d\n",
                     module, currentLine,
                     currentSegment->name,
                     currentModule->size[currentSegment->id], 
                     size);
+         } else {
+           // pleased to meet you again
          }
        } else {
          currentModule->size[currentSegment->id]=size;
@@ -290,15 +384,18 @@ void readModule(char *module) {
        char refdef[132];
        unsigned int address;
        if (sscanf(line, "S %[^ ] %s", symbol, refdef)!=2) {
-         fprintf (stderr, "%s:%d syntax error near \"%s\"\n",
+         fprintf (stderr, "*** %s:%d syntax error near \"%s\"\n",
                   module, currentLine, line);
          exit (1);
        }
        if (strncmp(refdef, "Ref", 3)==0) {
-         addToRefs(symbol);
+         // we don't need them
        } else if (strncmp(refdef, "Def", 3)==0) {
          sscanf (refdef, "Def%04x", &address);
-         addToDefs(symbol, address);
+         addToDefs(symbol, address, 0);
+       } else if (strncmp(refdef, "Abs", 3)==0) {
+         sscanf (refdef, "Abs%04x", &address);
+         addToDefs(symbol, address, 1);
        } else {
          fprintf (stderr, "%s:%d found invalid symbol definition \"%s\"\n", 
                   module, currentLine, line);
@@ -335,12 +432,19 @@ void readModule(char *module) {
        //fprintf (stderr, "\n");
        break;
       }
-      case 'R':
+      case 'R': {
+       unsigned address, from;
+       char symbol[132];
+       char how[32];
        //fprintf (stderr, "%s", line);
+       sscanf (line, "R %x %[^ ] %[^ ] %x", &address, how, symbol, &from);
+       addToRefs (symbol, address, how, from);
        break;
+      }
       default:
-       /* fprintf (stderr, "%s:%d unknown record \"%s\"\n",
-          module, currentLine, line); */
+       fprintf (stderr, "%s:%d unknown record \"%s\"\n",
+                module, currentLine, line);
+       fatalErrors++;
        break;
       }
     currentLine++;
@@ -349,32 +453,108 @@ void readModule(char *module) {
   fclose (relModule);
 }
 
-void writeModule() {
+void writeModule(char *outFileName) {
+  FILE *fOut;
+
   fprintf (stderr, "WriteModule: %s\n", outFileName);
+  if ((fOut=fopen(outFileName, "w"))==NULL) {
+    perror (outFileName);
+  }
   // oops, forgot something :) */
+  fclose (fOut);
 }
 
 void relocate() {
   struct SYMBOL *symbol;
+  struct REFERENCE *reference;
+  char *from, *to;
   int length=segments[GSINIT].current +
     segments[CSEG].current +
     segments[XINIT].current;
 
+  //fprintf (stderr, "relocate: total code size: 0x%04x\n", length);
+
   // first check if it will fit
   if (length > 0xffff) {
     fprintf (stderr, "error: code segment exceeds 0xffff\n");
-    exit(1);
+    fatalErrors++;
   }
-  fprintf (stderr, "relocate: total code size: 0x%04x\n", length);
 
   // GSINIT gets the --code-loc
   segments[GSINIT].start=segments[CSEG].start;
   segments[CSEG].start=segments[GSINIT].start+segments[GSINIT]._size;
+  // concat cseg to gsinit
+  from=csegImage;
+  to=&gsinitImage[segments[GSINIT].start+segments[GSINIT]._size];
+  memcpy(to, from, segments[CSEG]._size);
   segments[XINIT].start=segments[CSEG].start+segments[CSEG]._size;
+  from=xinitImage;
+  to+=segments[CSEG]._size;
+  memcpy(to, from, segments[XINIT]._size);
+#if 0
+  from=gsfinalImage;
+  to+=segments[XINIT]._size;
+  memcpy(to, from, segments[GSFINAL]._size);
+#endif
   segments[XISEG].start=segments[XSEG].start+segments[XINIT]._size;  
   // now relocate the defined symbols
   for (symbol=symbols; symbol; symbol=symbol->next) {
-    symbol->address += symbol->segment->start;
+    if (!symbol->absolute) {
+      symbol->address += symbol->segment->start;
+    }
+  }
+  // add the segment symbols
+  currentSegment=findSegmentByName("XINIT");
+  addToDefs("s_XINIT", segments[XINIT].start, 0);
+  addToDefs("l_XINIT", segments[XINIT]._size, 0);
+  currentSegment=findSegmentByName("XISEG");
+  addToDefs("s_XISEG", segments[XISEG].start, 0);
+  addToDefs("l_XISEG", segments[XISEG]._size, 0);
+  // and the references
+  for (reference=references; reference; reference=reference->next) {
+    if (!(symbol=findSymbolByName(reference->name))) {
+      fprintf (stderr, "*** %s:%d undefined symbol %s\n",
+              reference->module->name, reference->lineno,
+              reference->name);
+    } else {
+      reference->address += symbol->segment->start;
+      switch (reference->how) 
+       {
+       case REL_FFFF: {
+         int rel16 = symbol->address-reference->pc;
+         if (rel16<-65536 || rel16>65534) {
+           fprintf (stderr, 
+                    "rel16 target for %s is out of range in module %s\n",
+                    reference->name, reference->module->name);
+           fatalErrors++;
+         }
+         gsinitImage[reference->address+1]=(rel16/2)>>8;
+         gsinitImage[reference->address]=rel16/2;
+         break;
+       }
+       case REL_FF: {
+         int rel8 = symbol->address-reference->pc;
+         if (rel8<-256 || rel8>256) {
+           fprintf (stderr,
+                    "rel8 target for %s is out of range in module %s\n",
+                    reference->name, reference->module->name);
+           fatalErrors++;
+         }
+         gsinitImage[reference->address]=rel8/2;
+         break;
+       }
+       case ABS_FFFF:
+         gsinitImage[reference->address+1] = symbol->address>>8;
+         // fall through
+       case ABS_FF:
+         gsinitImage[reference->address] = symbol->address;
+         break;
+       default:
+         fprintf (stderr, "unsupported reference mode %d.\n",
+                  reference->how);
+         fatalErrors++;
+       }
+    }
   }
 }
 
@@ -397,8 +577,6 @@ int main(int argc, char **argv) {
     usage(argv[0], 1);
   }
 
-  memset(codeImage, 0xff, CODESIZE);
-
   // read in the commands
   sprintf (linkCommandsPath, "%s.lnk", argv[1]);
   if (!(linkCommandsFile=fopen(linkCommandsPath, "r"))) {
@@ -459,6 +637,12 @@ int main(int argc, char **argv) {
     } else {
       // not a switch, must be an inputfile; one/line
       readModule(linkCommand);
+      // the first one defines the output name
+      if (!outFileName[0]) {
+       strncpy(outFileName, linkCommand,
+               strlen(linkCommand)-4);
+       strcat(outFileName, ".hex");
+      }
     }
   }
 
@@ -478,7 +662,9 @@ int main(int argc, char **argv) {
 
   // the symbols
   for (symbol=symbols; symbol; symbol=symbol->next) {
-    fprintf (stderr, "%s %s 0x%04x %s\n", symbol->name, symbol->segment->name,
+    fprintf (stderr, "%s %s %s0x%04x %s\n", symbol->name, 
+            symbol->segment->name,
+            symbol->absolute ? "= " : "",
             symbol->address, symbol->module->name);
   }
 
@@ -492,7 +678,7 @@ int main(int argc, char **argv) {
     }
   }
 
-  writeModule();
-  return 0;
+  writeModule(outFileName);
+  return fatalErrors? 1 : 0;
 }