7eace8ddf19fc633afd77821906a3cd6e6a3a802
[fw/sdcc] / as / xa51 / xa_link.c
1 /* WORK IN PROGRESS: do not watch this if you don't have the legal
2    age in your country to watch this.
3 */
4
5 /* This program is distributed in the hope that it will be useful,
6  * but WITHOUT ANY WARRANTY; without even the implied warranty of
7  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
8  * GNU General Public License for more details.
9
10  * You should have received a copy of the GNU General Public License
11  * along with this program; if not, write to the Free Software
12  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
13  */
14
15 /* This is a cheap hack. The xa51 has a couple of ways to scramble
16    relocation info into it's opcode that the standard linker can't
17    handle. No hash or qsort yet.
18
19    The relocatable format looks like the known one, BUT ISN'T.
20
21    The only things that are handled now are:
22
23    "SDCCXA rel, version %f" must be the first line, sort of MAGIC word
24    "H %d areas %d global symbols" defines the # of areas and globals
25    "S <symbol> [Ref0000 | DefXXXX | AbsXXXX]" Def's are supposed to be
26      defined in their own area/segment
27    "A <seg> size %d flags %d" switch to another segment. this can happen
28      multiple times and should be equal. flags is ignored for now
29    "T xxxx <how> <symbol> 0"
30    "R xxxx <how> <symbol> <pc+>" the relocation info. xxxx is the address
31      within relative code space. How is something like REL_FF, REL_FFFF, 
32      ABS_F0FF. Symbol is the referenced symbol and pc+ is the program 
33      counter that will be used to calculate the relative address (that is
34      the address of the following instruction).
35
36    So, this is not a standalone linker. It will only link files generated
37    by xa_rasm, which will only process files generated by the xa51 sdcc
38    port.
39 */
40
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <limits.h>
44 #include <string.h>
45
46 #include "xa_version.h"
47
48 enum {
49   // these are all concatenated into the code image
50   GSINIT=1,
51   CSEG,
52   XINIT,
53   GSFINAL, // here goes the final output
54
55   // these are only for storage
56   BSEG,
57   DSEG,
58   XSEG,
59   XISEG,
60   MAX_SEGMENTS
61 };
62
63 enum {
64   REL_FF=1,
65   REL_FFFF,
66   ABS_0F,
67   ABS_FF,
68   ABS_03FF,
69   ABS_07FF,
70   ABS_F0FF,
71   ABS_FFFF,
72   ABS_0F00FF,
73   MAX_REFS
74 };
75
76 char *refModes[]={
77   "???",
78   "REL_FF",
79   "REL_FFFF",
80   "ABS_0F",
81   "ABS_FF",
82   "ABS_03FF",
83   "ABS_07FF",
84   "ABS_F0FF",
85   "ABS_FFFF",
86   "ABS_0F00FF",
87 };
88
89 #define CODESIZE 0x10000
90
91 int fatalErrors=0;
92
93 unsigned char gsinitImage[CODESIZE];
94 unsigned char csegImage[CODESIZE];
95 unsigned char xinitImage[CODESIZE];
96 unsigned char gsfinalImage[CODESIZE];
97
98 struct SEGMENT {
99   short id;
100   char *name;
101   int hasSymbols;
102   int _size;
103   int start;
104   int current;
105   unsigned char *image;
106 } segments[MAX_SEGMENTS]={
107   {0,       "???",    0, 0, 0, 0, NULL},
108
109   {GSINIT,  "GSINIT",  0, 0, 0, 0, gsinitImage},
110   {CSEG,    "CSEG",    0, 0, 0, 0, csegImage},
111   {XINIT,   "XINIT",   0, 0, 0, 0, xinitImage},
112   {GSFINAL, "GSFINAL", 0, 0, 0, 0, gsfinalImage},
113
114   {BSEG,    "BSEG",    0, 0, 0, 0, NULL},
115   {DSEG,    "DSEG",    0, 0, 0, 0, NULL},
116   {XSEG,    "XSEG",    0, 0, 0, 0, NULL},
117   {XISEG,   "XISEG",   0, 0, 0, 0, NULL},
118 };
119
120 struct MODULE {
121   char *name;
122   int offset[MAX_SEGMENTS];
123   int size[MAX_SEGMENTS];
124   int isLib;
125   struct MODULE *next;
126   struct MODULE *last;
127 } *modules=NULL;
128
129
130 struct SYMBOL {
131   char *name;
132   struct MODULE *module;
133   int lineno;
134   struct SEGMENT *segment;
135   char absolute;
136   int address;
137   struct SYMBOL *next;
138   struct SYMBOL *last;
139 } *symbols=NULL;
140
141 struct REFERENCE {
142   char *name;
143   struct MODULE *module;
144   int lineno;
145   unsigned address, pc;
146   short how;
147   short resolved;
148   struct REFERENCE *next;
149   struct REFERENCE *last;
150 } *references=NULL;
151
152 char *libraryPaths[128];
153 int nlibPaths=0;
154 char *libraryFiles[128];
155 int nlibFiles=0;
156
157 static char outFileName[PATH_MAX]={'\0'};
158 static char mapFileName[PATH_MAX]={'\0'};
159 FILE *mapOut;
160
161 struct SEGMENT *currentSegment;
162 struct MODULE *currentModule;
163 int currentLine;
164
165 int howToReference(char *how) {
166   int r;
167   for (r=1; r<MAX_REFS; r++) {
168     if (strcmp(refModes[r], how)==0) {
169       return r;
170     }
171   }
172   return 0;
173 }
174
175 struct SEGMENT *findSegmentByName(char *segment) {
176   int s;
177   for (s=0; s<MAX_SEGMENTS; s++) {
178     if (strcmp(segments[s].name, segment)==0) {
179       return &segments[s];
180     }
181   }
182   return 0;
183 }
184
185 struct SYMBOL *findSymbolByName(char *symName) {
186   struct SYMBOL *symbol;
187   for (symbol=symbols; symbol; symbol=symbol->next) {
188     if (strcmp(symbol->name, symName)==0) {
189       return symbol;
190     }
191   }
192   return 0;
193 }
194
195 struct MODULE *findModuleByName(char *modName) {
196   struct MODULE *module;
197   for (module=modules; module; module=module->next) {
198     if (strcmp(module->name, modName)==0) {
199       return module;
200     }
201   }
202   return NULL;
203 }
204
205 void addToModules (char *name, int isLib) {
206   struct MODULE *module;
207   int s;
208
209   module=calloc(1, sizeof(struct MODULE));
210   module->name=strdup(name);
211   for (s=0; s<MAX_SEGMENTS; s++) {
212     module->offset[s]=segments[s]._size;
213   }
214   module->isLib=isLib;
215   if (!modules) {
216     modules=module;
217    } else {
218     modules->last->next=module;
219   }
220   currentModule=modules->last=module;
221 }
222
223 void addToRefs(char *ref, int address, char *how, int pc) {
224   struct REFERENCE *reference;
225
226   reference=calloc(1, sizeof(struct REFERENCE));
227   reference->name=strdup(ref);
228   reference->module=currentModule;
229   reference->lineno=currentLine;
230   reference->address=address;
231   reference->how=howToReference(how);
232   reference->pc=pc;
233   if (!references) {
234     references=reference;
235   } else {
236     references->last->next=reference;
237   }
238   references->last=reference;
239 }
240
241 void resolve() {
242   struct REFERENCE *reference;
243   for (reference=references; reference; reference=reference->next) {
244     if (findSymbolByName(reference->name)) {
245       reference->resolved=1;
246     }
247   }
248 }
249
250 int isUnresolved(char *ref, int resolved) {
251   struct REFERENCE *reference;
252
253   for (reference=references; reference; reference=reference->next) {
254     if (strcmp(reference->name, ref)==0) {
255       // found 
256       if (reference->resolved) {
257         // already resolved
258         return 0;
259       }
260       if (resolved) {
261         reference->resolved=1;
262         return 1;
263       }
264     }
265   }
266   return 0;
267 }
268
269 void addToDefs(char *def, int address, char absolute) {
270   struct SYMBOL *symbol;
271
272   // no duplicates allowed
273   if ((symbol=findSymbolByName(def))) {
274     fprintf (stderr, "*** %s:%d duplicate symbol %s first defined in "
275              "module %s:%d\n",
276              currentModule->name, currentLine, def, 
277              symbol->module->name, symbol->lineno);
278     fatalErrors++;
279   }
280
281   symbol=calloc(1, sizeof(struct SYMBOL));
282   symbol->name=strdup(def);
283   symbol->module=currentModule;
284   symbol->lineno=currentLine;
285   symbol->segment=currentSegment;
286   symbol->absolute=absolute;
287   symbol->address=currentModule->offset[currentSegment->id]+address;
288   if (!symbols) {
289     symbols=symbol;
290   } else {
291     symbols->last->next=symbol;
292   }
293   symbols->last=symbol;
294   currentSegment->hasSymbols++;
295 }
296
297 void syntaxError (char *err) {
298   fprintf (stderr, "*** %s:%d error while parsing '%s'\n", 
299            currentModule->name, currentLine, err);
300   fatalErrors++;
301 }
302
303 void readModule(char *module, int isLib) {
304   double hisVersion;
305   char line[132];
306   FILE *relModule;
307   char moduleName[PATH_MAX];
308   int segments, globals;
309
310   currentLine=1;
311
312   if ((relModule=fopen(module, "r"))==NULL) {
313     perror (module);
314     exit (1);
315   }
316
317   // first we need to check if this is a valid file
318   if (sscanf(fgets(line, 132, relModule), 
319              "SDCCXA rel, version %lf", &hisVersion)!=1) {
320     fprintf (stderr, "*** %s is not a valid input file\n", module);
321     exit (1);
322   }
323   if (hisVersion!=version) {
324     fprintf (stderr, "*** WARNING: version conflict; "
325              "we(%1.1f) != %s(%1.1f)\n", 
326              version, module, hisVersion);
327   }
328   currentLine++;
329   
330   // H 7 areas 168 global symbols
331   if (sscanf(fgets(line, 132, relModule),
332              "H %d areas %d global symbols",
333              &segments, &globals)!=2) {
334     syntaxError(line);
335   }
336   currentLine++;
337
338   // M module
339   if (sscanf(fgets(line, 132, relModule),
340              "M %s", moduleName)!=1) {
341     syntaxError(line);
342   }
343
344   // add this to the known modules with current offsets
345   addToModules(module, isLib);
346
347   currentLine++;
348
349   // now for the ASTR tags
350   while (fgets(line, 132, relModule)) {
351     switch (line[0]) 
352       {
353       case 'A': {
354         char segment[32];
355         int size, flags;
356         if (sscanf(line, "A %[^ ] size %d flags %d",
357                    segment, &size, &flags)!=3) {
358           syntaxError(line);
359         }
360         // do we know this segment?
361         if (!(currentSegment=findSegmentByName(segment))) {
362           fprintf (stderr, "*** %s:%d unknown area: %s\n", module,
363                    currentLine, segment);
364           exit (1);
365         }
366         // double check repeated 'A' records
367         if (currentModule->size[currentSegment->id]) {
368           // pleased to meet you again, I hope ...
369           if (currentModule->size[currentSegment->id] != size) {
370             fprintf (stderr, "*** %s:%d error %s size %d != %d\n",
371                      module, currentLine,
372                      currentSegment->name,
373                      currentModule->size[currentSegment->id], 
374                      size);
375             fatalErrors++;
376           }
377         } else {
378           currentSegment->_size += size;
379           currentModule->size[currentSegment->id] = size;
380         }
381         // never mind about the flags for now
382         break;
383       }
384       case 'S': {
385         char symbol[132];
386         char refdef[132];
387         unsigned int address;
388         if (sscanf(line, "S %[^ ] %s", symbol, refdef)!=2) {
389           fprintf (stderr, "*** %s:%d syntax error near \"%s\"\n",
390                    module, currentLine, line);
391           exit (1);
392         }
393         if (strncmp(refdef, "Ref", 3)==0) {
394           // we don't need them
395         } else if (strncmp(refdef, "Def", 3)==0) {
396           sscanf (refdef, "Def%04x", &address);
397           addToDefs(symbol, address, 0);
398         } else if (strncmp(refdef, "Abs", 3)==0) {
399           sscanf (refdef, "Abs%04x", &address);
400           addToDefs(symbol, address, 1);
401         } else {
402           fprintf (stderr, "%s:%d found invalid symbol definition \"%s\"\n", 
403                    module, currentLine, line);
404           exit (1);
405         }
406         break;
407       }
408       case 'T': {
409         unsigned int address;
410         unsigned int byte;
411         char *tline=NULL;
412         if (currentSegment->id!=CSEG && 
413             currentSegment->id!=GSINIT &&
414             currentSegment->id!=XINIT) {
415           fprintf (stderr, "%s:%d cannot emit bytes in %s\n",
416                    module, currentLine, currentSegment->name);
417           exit (1);
418         }
419         if (sscanf(strtok(&line[2], " "), "%04x", &address)!=1) {
420           fprintf (stderr, "%s:%d error in T record\n", module, currentLine);
421           fatalErrors++;
422         }
423
424         address+=currentModule->offset[currentSegment->id];
425         //address+=currentSegment->current;
426         for ( ;
427               (tline=strtok(NULL, " \t\n")) && 
428                 (sscanf(tline, "%02x", &byte)==1);
429               ) {
430           currentSegment->image[address++]=byte;
431           currentSegment->current++;
432         }
433         break;
434       }
435       case 'R': {
436         unsigned address, pc;
437         char symbol[132];
438         char how[32];
439         sscanf (line, "R %x %[^ ] %[^ ] %x", &address, how, symbol, &pc);
440         addToRefs (symbol, address, how, pc);
441         break;
442       }
443       default:
444         fprintf (stderr, "%s:%d unknown record \"%s\"\n",
445                  module, currentLine, line);
446         fatalErrors++;
447         break;
448       }
449     currentLine++;
450   }
451   // that's all for now, thanks for watching */
452   fclose (relModule);
453 }
454
455 void writeModule(char *outFileName) {
456   FILE *fOut;
457   unsigned int address=segments[GSFINAL].start;
458   unsigned int size=segments[GSFINAL]._size;
459   unsigned int len;
460   unsigned int checksum;
461
462   fprintf (stderr, "writeModule: %s from 0x%04x to 0x%04x\n",
463            outFileName, 
464            address,
465            address+size);
466
467   if ((fOut=fopen(outFileName, "w"))==NULL) {
468     perror (outFileName);
469   }
470   fprintf (fOut, "Just for now, make it a little bit more readable\n");
471
472   while (size) {
473     len = size>16 ? 16 : size;
474     size-=len;
475     fprintf (fOut, ":%02X.%04X.%02X >", len, address, 0);
476     checksum = len + (address>>8) + (address&0xff);
477     while (len--) {
478       checksum += gsfinalImage[address];
479       fprintf (fOut, " %02X", gsfinalImage[address++]);
480     }
481     checksum &= 0xff;
482     if (checksum) {
483       checksum = 0x100 - checksum;
484     }
485     fprintf (fOut, " < %02X\n", checksum);
486   }
487   fprintf (fOut, ":00000001FF\n");
488
489   fclose (fOut);
490 }
491
492 int relocate() {
493   struct SYMBOL *symbol;
494   struct REFERENCE *reference;
495   char *from, *to;
496   int length=segments[GSINIT]._size +
497     segments[CSEG]._size +
498     segments[XINIT]._size;
499   int unresolved=0;
500
501   // first check if it will fit
502   if (length > 0xffff) {
503     fprintf (stderr, "error: code segment exceeds 0xffff\n");
504     fatalErrors++;
505   }
506
507   // resolve reverences
508   for (reference=references; reference; reference=reference->next) {
509     if (!reference->resolved && !findSymbolByName(reference->name)) {
510       unresolved++;
511     }
512   }
513   if (unresolved) {
514     // first scan the libraries
515     return unresolved;
516   }
517
518   // GSFINAL starts at --code-loc ( -b CSEG = 0x1234 )
519   segments[GSFINAL].start=segments[CSEG].start;
520   memset(gsfinalImage, 0xff, CODESIZE);
521
522   // copy gsinit to gsfinal
523   from = gsinitImage;
524   to = gsfinalImage + segments[GSFINAL].start + segments[GSFINAL]._size;
525   memcpy(to, from, segments[GSINIT]._size);
526   segments[GSINIT].start=segments[GSFINAL].start;
527   segments[GSFINAL]._size += segments[GSINIT]._size;
528     
529   // append cseg to gsfinal
530   from=csegImage;
531   to = gsfinalImage + segments[GSFINAL].start + segments[GSFINAL]._size;
532   memcpy(to, from, segments[CSEG]._size);
533   segments[CSEG].start=segments[GSFINAL].start+segments[GSFINAL]._size;
534   segments[GSFINAL]._size += segments[CSEG]._size;
535
536   // append xinit to gsfinal
537   from=xinitImage;
538   to = gsfinalImage + segments[GSFINAL].start + segments[GSFINAL]._size;
539   memcpy(to, from, segments[XINIT]._size);
540   segments[XINIT].start=segments[GSFINAL].start+segments[GSFINAL]._size;
541   segments[GSFINAL]._size += segments[XINIT]._size;
542
543   // XISEG is located after XSEG
544   segments[XISEG].start=segments[XSEG].start + 
545     segments[XSEG]._size;  
546
547   // now relocate the defined symbols
548   for (symbol=symbols; symbol; symbol=symbol->next) {
549     if (!symbol->absolute) {
550       symbol->address += symbol->segment->start;
551     }
552   }
553   // and the references
554   for (reference=references; reference; reference=reference->next) {
555     if (!(symbol=findSymbolByName(reference->name))) {
556       // this reference isn't defined after all
557       fprintf (stderr, "*** %s:%d undefined symbol %s\n",
558                reference->module->name, reference->lineno,
559                reference->name);
560       fatalErrors++;
561     } else {
562       reference->address += symbol->segment->start;
563       reference->pc += symbol->segment->start;
564       switch (reference->how) 
565         {
566         case REL_FFFF: {
567           int rel16 = symbol->address-reference->pc;
568           if (rel16<-65536 || rel16>65534) {
569             fprintf (stderr, 
570                      "rel16 target for %s is out of range in module %s\n",
571                      reference->name, reference->module->name);
572             fatalErrors++;
573           }
574           gsinitImage[reference->address+1]=(rel16/2)>>8;
575           gsinitImage[reference->address]=rel16/2;
576           break;
577         }
578         case REL_FF: {
579           int rel8 = symbol->address-reference->pc;
580           if (rel8<-256 || rel8>256) {
581             fprintf (stderr,
582                      "rel8 target for %s is out of range in module %s\n",
583                      reference->name, reference->module->name);
584             fatalErrors++;
585           }
586           gsinitImage[reference->address]=rel8/2;
587           break;
588         }
589         case ABS_FFFF:
590           gsinitImage[reference->address+1] = symbol->address>>8;
591           // fall through
592         case ABS_FF:
593           gsinitImage[reference->address] = symbol->address;
594           break;
595         default:
596           fprintf (stderr, "unsupported reference mode %d.\n",
597                    reference->how);
598           fatalErrors++;
599         }
600     }
601   }
602   return 0;
603 }
604
605 void usage (char * progName, int errNo) {
606   fprintf (stderr, "usage: %s lnkCmdFile\n", progName);
607   if (errNo) {
608     exit (errNo);
609   }
610 }
611
612 int scanLibraries(int unresolved) {
613   int resolved=0;
614   int nlp, nlf;
615   char libFiles[PATH_MAX];
616   char libFile[PATH_MAX];
617   char line[132];
618   char symName[132];
619   FILE *lf, *lfs;
620   
621   for (nlp=0; nlp<nlibPaths; nlp++) {
622     for (nlf=0; nlf<nlibFiles; nlf++) {
623       sprintf (libFiles, "%s/%s.lib", libraryPaths[nlp], libraryFiles[nlf]);
624       //fprintf (stderr, "  %s\n", libFiles);
625       if ((lfs=fopen(libFiles,"r"))==NULL) {
626         continue;
627       }
628       while (fgets(line, 132, lfs)) {
629         // remove trailing \n
630         line[strlen(line)-1]='\0';
631         sprintf (libFile, "%s/%s", libraryPaths[nlp], line);
632         //fprintf (stderr, "    %s\n", libFile);
633         if ((lf=fopen(libFile,"r"))==0) {
634           continue;
635         }
636         while (fgets(line, 132, lf)) {
637           int dummy; // we need this to get the right count of the next sscanf
638           if (sscanf(line, "S %[^ ] Def%04x", symName, &dummy)==2) {
639             if (isUnresolved(symName, 1)) {
640               //fprintf (stderr, "%s:%s\n", libFile, symName);
641               readModule(libFile,1);
642               if (resolved++ == unresolved) {
643                 // we are done
644                 return resolved;
645               }
646               // skip to next lib module
647               break;
648             }
649           }
650         }
651       }
652     }
653   }
654   return resolved;
655 }
656
657 int main(int argc, char **argv) {
658   FILE *linkCommandsFile;
659   char linkCommandsPath[PATH_MAX];
660   char linkCommand[PATH_MAX];
661   struct MODULE *module;
662   struct SYMBOL *symbol;
663   int s;
664   int unresolved;
665
666   if (argc!=2) {
667     usage(argv[0], 1);
668   }
669
670   // read in the commands
671   sprintf (linkCommandsPath, "%s.lnk", argv[1]);
672   if (!(linkCommandsFile=fopen(linkCommandsPath, "r"))) {
673     perror(linkCommandsPath);
674     exit(1);
675   }
676   while (fgets(linkCommand, PATH_MAX, linkCommandsFile)) {
677     linkCommand[strlen(linkCommand)-1]='\0';
678
679     // skip empty lines
680     if (!*linkCommand) {
681       continue;
682     }
683
684     //puts (linkCommand);
685     if (*linkCommand=='-') {
686       switch (linkCommand[1]) 
687         {
688         case 'm':
689           // probably -muxi, ignore for now
690           break;
691         case 'e':
692           // -e, always in the last line, ignore for now
693           break;
694         case 'b': 
695           {
696             // a segment start address like: "-b XSEG = 0x4000"
697             int s;
698             char *seg=strtok(&linkCommand[3], " \t");
699             for (s=0; s<MAX_SEGMENTS; s++) {
700               if (strcmp(segments[s].name, seg)==0) {
701                 strtok(NULL, " \t"); // skip the '='
702                 if (sscanf(strtok(NULL, " \t"), "%x", 
703                            &segments[s].start)!=1) {
704                   syntaxError(linkCommand);
705                 }
706                 break;
707               }
708             }
709             if (s==MAX_SEGMENTS) {
710               syntaxError(linkCommand);
711             }
712           }
713           break;
714         case 'k':
715           // a lib path like: "-k /usr/local/share/sdcc/lib/xa51"; one/line
716           libraryPaths[nlibPaths++]=strdup(&linkCommand[3]);
717           break;
718         case 'l':
719           // a lib file like: "-l libsdcc"; one/line
720           libraryFiles[nlibFiles++]=strdup(&linkCommand[3]);
721           break;
722         default:
723           syntaxError(linkCommand);
724         }
725     } else {
726       // not a switch, must be an inputfile; one/line
727       readModule(linkCommand, 0);
728       // the first one defines the output name
729       if (!outFileName[0]) {
730         strncpy(outFileName, linkCommand,
731                 strlen(linkCommand)-4);
732         sprintf(mapFileName, "%s.map", outFileName);
733         strcat(outFileName, ".hex");
734         if ((mapOut=fopen(mapFileName, "w"))==NULL) {
735           perror(mapFileName);
736         }
737       }
738     }
739   }
740
741   // add the segment symbols
742   currentSegment=findSegmentByName("XINIT");
743   addToDefs("s_XINIT", 0, 0);
744   addToDefs("l_XINIT", segments[XINIT]._size, 1);
745   currentSegment=findSegmentByName("XISEG");
746   addToDefs("s_XISEG", 0, 0);
747   addToDefs("l_XISEG", segments[XISEG]._size, 1);
748
749 #if 1
750   // mark the resolved references
751   resolve();
752
753   // now do something EXTREMELY SLOW AND INEFFICIENT :)
754   while ((unresolved=relocate())) {
755     if (!scanLibraries(unresolved)) {
756       struct REFERENCE *reference;
757       resolve();
758       for (reference=references; reference; reference=reference->next) {
759         if (!reference->resolved) {
760           fprintf (stderr, "*** unresolved symbol %s in %s:%d\n",
761                    reference->name, reference->module->name,
762                    reference->lineno);
763           fatalErrors++;
764         }
765       }
766       break;
767     }
768   }
769
770   if (unresolved==0) {
771     writeModule(outFileName);
772   }
773 #endif
774
775   // the modules
776   fprintf (mapOut, "Modules:\n");
777   for (module=modules; module; module=module->next) {
778     fprintf (mapOut, "\t%s\n", module->name);
779     for (s=0; s<MAX_SEGMENTS; s++) {
780       if (module->size[s]) {
781         fprintf (mapOut, "\t\t%s:0x%04x-0x%04x\n", segments[s].name,
782                  module->offset[s]+segments[s].start,
783                  module->offset[s]+segments[s].start+module->size[s]);
784       }
785     }
786   }
787
788   // the segments
789   fprintf (mapOut, "\nSegments:\n");
790   for (s=1; s<MAX_SEGMENTS; s++) {
791     if (segments[s]._size) {
792       fprintf (mapOut, "\t%s start 0x%04x size 0x%04x %d symbols\n",
793                segments[s].name, segments[s].start,
794                segments[s]._size, 
795                segments[s].hasSymbols);
796     }
797   }
798
799   // the symbols
800   fprintf (mapOut, "\nSymbols:\n");
801   for (symbol=symbols; symbol; symbol=symbol->next) {
802     fprintf (mapOut, "%s\t%s %s0x%04x %s\n", symbol->name, 
803              symbol->segment->name,
804              symbol->absolute ? "= " : "",
805              symbol->address, symbol->module->name);
806   }
807
808   fclose(mapOut);
809   return fatalErrors? 1 : 0;
810 }
811