]> git.sur5r.net Git - cc65/blob - src/co65/main.c
Fix memory area start symbol
[cc65] / src / co65 / main.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                  main.c                                   */
4 /*                                                                           */
5 /*              Main program for the co65 object file converter              */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 2003      Ullrich von Bassewitz                                       */
10 /*               Römerstrasse 52                                             */
11 /*               D-70794 Filderstadt                                         */
12 /* EMail:        uz@cc65.org                                                 */
13 /*                                                                           */
14 /*                                                                           */
15 /* This software is provided 'as-is', without any expressed or implied       */
16 /* warranty.  In no event will the authors be held liable for any damages    */
17 /* arising from the use of this software.                                    */
18 /*                                                                           */
19 /* Permission is granted to anyone to use this software for any purpose,     */
20 /* including commercial applications, and to alter it and redistribute it    */
21 /* freely, subject to the following restrictions:                            */
22 /*                                                                           */
23 /* 1. The origin of this software must not be misrepresented; you must not   */
24 /*    claim that you wrote the original software. If you use this software   */
25 /*    in a product, an acknowledgment in the product documentation would be  */
26 /*    appreciated but is not required.                                       */
27 /* 2. Altered source versions must be plainly marked as such, and must not   */
28 /*    be misrepresented as being the original software.                      */
29 /* 3. This notice may not be removed or altered from any source              */
30 /*    distribution.                                                          */
31 /*                                                                           */
32 /*****************************************************************************/
33
34
35
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <errno.h>
40 #include <time.h>
41
42 /* common */
43 #include "chartype.h"
44 #include "cmdline.h"
45 #include "fname.h"
46 #include "print.h"
47 #include "segnames.h"
48 #include "version.h"
49 #include "xmalloc.h"
50 #include "xsprintf.h"
51
52 /* co65 */
53 #include "error.h"
54 #include "global.h"
55 #include "o65.h"
56
57
58
59 /*****************************************************************************/
60 /*                                   Code                                    */
61 /*****************************************************************************/
62
63
64
65 static void Usage (void)
66 /* Print usage information and exit */
67 {
68     fprintf (stderr,
69              "Usage: %s [options] file\n"
70              "Short options:\n"
71              "  -V\t\t\tPrint the version number\n"
72              "  -g\t\t\tAdd debug info to object file\n"
73              "  -h\t\t\tHelp (this text)\n"
74              "  -o name\t\tName the output file\n"
75              "  -v\t\t\tIncrease verbosity\n"
76              "\n"
77              "Long options:\n"
78              "  --bss-label name\tDefine and export a BSS segment label\n"
79              "  --bss-name seg\tSet the name of the BSS segment\n"
80              "  --code-label name\tDefine and export a CODE segment label\n"
81              "  --code-name seg\tSet the name of the CODE segment\n"
82              "  --data-label name\tDefine and export a DATA segment label\n"
83              "  --data-name seg\tSet the name of the DATA segment\n"
84              "  --debug-info\t\tAdd debug info to object file\n"
85              "  --help\t\tHelp (this text)\n"
86              "  --verbose\t\tIncrease verbosity\n"
87              "  --version\t\tPrint the version number\n"
88              "  --zeropage-label name\tDefine and export a ZEROPAGE segment label\n"
89              "  --zeropage-name seg\tSet the name of the ZEROPAGE segment\n",
90              ProgName);
91 }
92
93
94
95 static void CheckLabelName (const char* Label)
96 /* Check if the given label is a valid label name */
97 {
98     const char* L = Label;
99
100     if (strlen (L) < 256 && (IsAlpha (*L) || *L== '_')) {
101         while (*++L) {
102             if (!IsAlNum (*L) && *L != '_') {
103                 break;
104             }
105         }
106     }
107
108     if (*L) {
109         Error ("Label name `%s' is invalid", Label);
110     }
111 }
112
113
114
115 static void CheckSegName (const char* Seg)
116 /* Abort if the given name is not a valid segment name */
117 {
118     /* Print an error and abort if the name is not ok */
119     if (!ValidSegName (Seg)) {
120         Error ("Segment name `%s' is invalid", Seg);
121     }
122 }
123
124
125
126 static void OptBssLabel (const char* Opt attribute ((unused)), const char* Arg)
127 /* Handle the --bss-label option */
128 {
129     /* Check for a label name */
130     CheckLabelName (Arg);
131
132     /* Set the label */
133     BssLabel = xstrdup (Arg);
134 }
135
136
137
138 static void OptBssName (const char* Opt attribute ((unused)), const char* Arg)
139 /* Handle the --bss-name option */
140 {
141     /* Check for a valid name */
142     CheckSegName (Arg);
143
144     /* Set the name */
145     BssSeg = xstrdup (Arg);
146 }
147
148
149
150 static void OptCodeLabel (const char* Opt attribute ((unused)), const char* Arg)
151 /* Handle the --code-label option */
152 {
153     /* Check for a label name */
154     CheckLabelName (Arg);
155
156     /* Set the label */
157     CodeLabel = xstrdup (Arg);
158 }
159
160
161
162 static void OptCodeName (const char* Opt attribute ((unused)), const char* Arg)
163 /* Handle the --code-name option */
164 {
165     /* Check for a valid name */
166     CheckSegName (Arg);
167
168     /* Set the name */
169     CodeSeg = xstrdup (Arg);
170 }
171
172
173
174 static void OptDataLabel (const char* Opt attribute ((unused)), const char* Arg)
175 /* Handle the --data-label option */
176 {
177     /* Check for a label name */
178     CheckLabelName (Arg);
179
180     /* Set the label */
181     DataLabel = xstrdup (Arg);
182 }
183
184
185
186 static void OptDataName (const char* Opt attribute ((unused)), const char* Arg)
187 /* Handle the --data-name option */
188 {
189     /* Check for a valid name */
190     CheckSegName (Arg);
191
192     /* Set the name */
193     DataSeg = xstrdup (Arg);
194 }
195
196
197
198 static void OptDebugInfo (const char* Opt attribute ((unused)),
199                           const char* Arg attribute ((unused)))
200 /* Add debug info to the object file */
201 {
202     DebugInfo = 1;
203 }
204
205
206
207 static void OptHelp (const char* Opt attribute ((unused)),
208                      const char* Arg attribute ((unused)))
209 /* Print usage information and exit */
210 {
211     Usage ();
212     exit (EXIT_SUCCESS);
213 }
214
215
216
217 static void OptVerbose (const char* Opt attribute ((unused)),
218                         const char* Arg attribute ((unused)))
219 /* Increase verbosity */
220 {
221     ++Verbosity;
222 }
223
224
225
226 static void OptVersion (const char* Opt attribute ((unused)),
227                         const char* Arg attribute ((unused)))
228 /* Print the assembler version */
229 {
230     fprintf (stderr,
231              "co65 V%u.%u.%u - (C) Copyright 1998-2003 Ullrich von Bassewitz\n",
232              VER_MAJOR, VER_MINOR, VER_PATCH);
233 }
234
235
236
237 static void OptZeropageLabel (const char* Opt attribute ((unused)), const char* Arg)
238 /* Handle the --zeropage-label option */
239 {
240     /* Check for a label name */
241     CheckLabelName (Arg);
242
243     /* Set the label */
244     ZeropageLabel = xstrdup (Arg);
245 }
246
247
248
249 static void OptZeropageName (const char* Opt attribute ((unused)), const char* Arg)
250 /* Handle the --zeropage-name option */
251 {
252     /* Check for a valid name */
253     CheckSegName (Arg);
254
255     /* Set the name */
256     ZeropageSeg = xstrdup (Arg);
257 }
258
259
260
261 static const char* SegReloc (const O65Data* D, const O65Reloc* R, unsigned long Val)
262 {
263     static char Buf[256];
264     const O65Import* Import;
265
266     switch (R->SegID) {
267
268         case O65_SEGID_UNDEF:
269             if (R->SymIdx >= CollCount (&D->Imports)) {
270                 Error ("Import index out of range (input file corrupt)");
271             }
272             Import = CollConstAt (&D->Imports, R->SymIdx);
273             xsprintf (Buf, sizeof (Buf), "%s%+ld", Import->Name, (long) Val);
274             break;
275
276         case O65_SEGID_TEXT:
277             xsprintf (Buf, sizeof (Buf), "%s%+ld", CodeLabel, (long) (Val - D->Header.tbase));
278             break;
279
280         case O65_SEGID_DATA:
281             xsprintf (Buf, sizeof (Buf), "%s%+ld", DataLabel, (long) (Val - D->Header.dbase));
282             break;
283
284         case O65_SEGID_BSS:
285             xsprintf (Buf, sizeof (Buf), "%s%+ld", BssLabel, (long) (Val - D->Header.bbase));
286             break;
287
288         case O65_SEGID_ZP:
289             xsprintf (Buf, sizeof (Buf), "%s%+ld", ZeropageLabel, (long) Val - D->Header.zbase);
290             break;
291
292         case O65_SEGID_ABS:
293             Error ("Relocation entry contains O65_SEGID_ABS");
294             break;
295
296         default:
297             Internal ("Cannot handle this segment reference in reloc entry");
298     }
299
300     return Buf;
301 }
302
303
304
305 static void ConvertSeg (FILE* F, const O65Data* D, const Collection* Relocs,
306                         const unsigned char* Data, unsigned long Size)
307 /* Convert one segment */
308 {
309     const O65Reloc* R;
310     unsigned        RIdx;
311     unsigned long   Byte;
312
313     /* Get the pointer to the first relocation entry if there are any */
314     R = (CollCount (Relocs) > 0)? CollConstAt (Relocs, 0) : 0;
315
316     /* Initialize for the loop */
317     RIdx = 0;
318     Byte = 0;
319
320     /* Walk over the segment data */
321     while (Byte < Size) {
322
323         if (R && R->Offs == Byte) {
324             /* We've reached an entry that must be relocated */
325             unsigned long Val;
326             switch (R->Type) {
327
328                 case O65_RTYPE_WORD:
329                     if (Byte >= Size - 1) {
330                         Error ("Found WORD relocation, but not enough bytes left");
331                     } else {
332                         Val = (Data[Byte+1] << 8) + Data[Byte];
333                         Byte += 2;
334                         fprintf (F, "\t.word\t%s\n", SegReloc (D, R, Val));
335                     }
336                     break;
337
338                 case O65_RTYPE_HIGH:
339                     Val = (Data[Byte++] << 8) + R->Val;
340                     fprintf (F, "\t.byte\t>(%s)\n", SegReloc (D, R, Val));
341                     break;
342
343                 case O65_RTYPE_LOW:
344                     Val = Data[Byte++];
345                     fprintf (F, "\t.byte\t<(%s)\n", SegReloc (D, R, Val));
346                     break;
347
348                 case O65_RTYPE_SEGADDR:
349                     if (Byte >= Size - 2) {
350                         Error ("Found SEGADDR relocation, but not enough bytes left");
351                     } else {
352                         Val = (((unsigned long) Data[Byte+2]) << 16) +
353                               (((unsigned long) Data[Byte+1]) <<  8) +
354                               (((unsigned long) Data[Byte+0]) <<  0) +
355                               R->Val;
356                         Byte += 3;
357                         fprintf (F, "\t.faraddr\t%s\n", SegReloc (D, R, Val));
358                     }
359                     break;
360
361                 case O65_RTYPE_SEG:
362                     /* FALLTHROUGH for now */
363                 default:
364                     Internal ("Cannot handle relocation type %d at %lu",
365                               R->Type, Byte);
366             }
367
368             /* Get the next relocation entry */
369             if (++RIdx < CollCount (Relocs)) {
370                 R = CollConstAt (Relocs, RIdx);
371             } else {
372                 R = 0;
373             }
374
375         } else {
376             /* Just a constant value */
377             fprintf (F, "\t.byte\t$%02X\n", Data[Byte++]);
378         }
379     }
380
381     fprintf (F, "\n");
382 }
383
384
385
386 static void Convert (void)
387 /* Do file conversion */
388 {
389     FILE*       F;
390     unsigned    I;
391     int         cc65;
392     char*       Author = 0;
393
394     /* Read the o65 file into memory */
395     O65Data* D = ReadO65File (InputName);
396
397     /* For now, we do only accept o65 files generated by the ld65 linker which
398      * have a specific format.
399      */
400     if (D->Header.mode != O65_MODE_CC65) {
401         Error ("Cannot convert o65 files of this type");
402     }
403
404     /* Output statistics */
405     Print (stdout, 1, "Size of text segment:               %5lu\n", D->Header.tlen);
406     Print (stdout, 1, "Size of data segment:               %5lu\n", D->Header.dlen);
407     Print (stdout, 1, "Size of bss segment:                %5lu\n", D->Header.blen);
408     Print (stdout, 1, "Size of zeropage segment:           %5lu\n", D->Header.zlen);
409     Print (stdout, 1, "Number of imports:                  %5u\n", CollCount (&D->Imports));
410     Print (stdout, 1, "Number of exports:                  %5u\n", CollCount (&D->Exports));
411     Print (stdout, 1, "Number of text segment relocations: %5u\n", CollCount (&D->TextReloc));
412     Print (stdout, 1, "Number of data segment relocations: %5u\n", CollCount (&D->DataReloc));
413
414     /* Walk through the options and print them if verbose mode is enabled.
415      * Check for a os=cc65 option and bail out if we didn't find one (for
416      * now - later we switch to special handling).
417      */
418     cc65 = 0;
419     for (I = 0; I < CollCount (&D->Options); ++I) {
420
421         /* Get the next option */
422         const O65Option* O = CollConstAt (&D->Options, I);
423
424         /* Check the type */
425         switch (O->Type) {
426             case O65_OPT_FILENAME:
427                 Print (stdout, 1, "O65 filename option:         `%s'\n",
428                        GetO65OptionText (O));
429                 break;
430             case O65_OPT_OS:
431                 if (O->Len == 2) {
432                     Warning ("Operating system option without data found");
433                 } else {
434                     cc65 = (O->Data[0] == O65_OS_CC65_MODULE);
435                     Print (stdout, 1, "O65 operating system option: `%s'\n",
436                            GetO65OSName (O->Data[0]));
437                 }
438                 break;
439             case O65_OPT_ASM:
440                 Print (stdout, 1, "O65 assembler option:        `%s'\n",
441                        GetO65OptionText (O));
442                 break;
443             case O65_OPT_AUTHOR:
444                 if (Author) {
445                     xfree (Author);
446                 }
447                 Author = xstrdup (GetO65OptionText (O));
448                 Print (stdout, 1, "O65 author option:           `%s'\n", Author);
449                 break;
450             case O65_OPT_TIMESTAMP:
451                 Print (stdout, 1, "O65 timestamp option:        `%s'\n",
452                        GetO65OptionText (O));
453                 break;
454             default:
455                 Warning ("Found unknown option, type %d, length %d",
456                          O->Type, O->Len);
457                 break;
458         }
459     }
460
461     /* Open the output file */
462     F = fopen (OutputName, "wb");
463     if (F == 0) {
464         Error ("Cannot open `%s': %s", OutputName, strerror (errno));
465     }
466
467     /* Create a header */
468     fprintf (F, ";\n; File generated by co65 v %u.%u.%u\n;\n",
469              VER_MAJOR, VER_MINOR, VER_PATCH);
470
471     /* Select the CPU */
472     if ((D->Header.mode & O65_CPU_MASK) == O65_CPU_65816) {
473         fprintf (F, "\t.p816\n");
474     }
475
476     /* Object file options */
477     fprintf (F, "\t.fopt\t\tcompiler,\"co65 v %u.%u.%u\"\n",
478              VER_MAJOR, VER_MINOR, VER_PATCH);
479     if (Author) {
480         fprintf (F, "\t.fopt\t\tauthor, \"%s\"\n", Author);
481         xfree (Author);
482         Author = 0;
483     }
484
485     /* Several other assembler options */
486     fprintf (F, "\t.case\t\ton\n");
487     fprintf (F, "\t.debuginfo\t%s\n", (DebugInfo != 0)? "on" : "off");
488
489     /* Setup/export the segment labels */
490     if (BssLabel) {
491         fprintf (F, "\t.export\t\t%s\n", BssLabel);
492     } else {
493         BssLabel = xstrdup ("__BSS__");
494     }
495     if (CodeLabel) {
496         fprintf (F, "\t.export\t\t%s\n", CodeLabel);
497     } else {
498         CodeLabel = xstrdup ("__CODE__");
499     }
500     if (DataLabel) {
501         fprintf (F, "\t.export\t\t%s\n", DataLabel);
502     } else {
503         DataLabel = xstrdup ("__DATA__");
504     }
505     if (ZeropageLabel) {
506         fprintf (F, "\t.export\t\t%s\n", ZeropageLabel);
507     } else {
508         /* If this is a cc65 module, override the name for the zeropage segment */
509         if (cc65) {
510             ZeropageLabel = "__ZP_START__";
511             fprintf (F, "\t.import\t\t__ZP_START__\t; Linker generated symbol\n");
512         } else {
513             ZeropageLabel = xstrdup ("__ZEROPAGE__");
514         }
515     }
516
517     /* End of header */
518     fprintf (F, "\n");
519
520     /* Imported identifiers */
521     if (CollCount (&D->Imports) > 0) {
522         for (I = 0; I < CollCount (&D->Imports); ++I) {
523
524             /* Get the next import */
525             O65Import* Import = CollAtUnchecked (&D->Imports, I);
526
527             /* Import it by name */
528             fprintf (F, "\t.import\t%s\n", Import->Name);
529         }
530         fprintf (F, "\n");
531     }
532
533     /* Exported identifiers */
534     if (CollCount (&D->Exports) > 0) {
535         for (I = 0; I < CollCount (&D->Exports); ++I) {
536
537             /* Get the next import */
538             O65Export* Export = CollAtUnchecked (&D->Exports, I);
539
540             /* First define it */
541             fprintf (F, "%s = XXX\n", Export->Name);    /* ### */
542
543             /* The export it by name */
544             fprintf (F, "\t.export\t%s\n", Export->Name);
545         }
546         fprintf (F, "\n");
547     }
548
549     /* Code segment */
550     fprintf (F, ".segment\t\"%s\"\n", CodeSeg);
551     fprintf (F, "%s:\n", CodeLabel);
552     ConvertSeg (F, D, &D->TextReloc, D->Text, D->Header.tlen);
553
554     /* Data segment */
555     fprintf (F, ".segment\t\"%s\"\n", DataSeg);
556     fprintf (F, "%s:\n", DataLabel);
557     ConvertSeg (F, D, &D->DataReloc, D->Data, D->Header.dlen);
558
559     /* BSS segment */
560     fprintf (F, ".segment\t\"%s\"\n", BssSeg);
561     fprintf (F, "%s:\n", BssLabel);
562     fprintf (F, "\t.res\t%lu\n", D->Header.blen);
563     fprintf (F, "\n");
564
565     fprintf (F, "\t.end\n");
566     fclose (F);
567 }
568
569
570
571 int main (int argc, char* argv [])
572 /* Converter main program */
573 {
574     /* Program long options */
575     static const LongOpt OptTab[] = {
576         { "--bss-label",        1,      OptBssLabel             },
577         { "--bss-name",         1,      OptBssName              },
578         { "--code-label",       1,      OptCodeLabel            },
579         { "--code-name",        1,      OptCodeName             },
580         { "--data-label",       1,      OptDataLabel            },
581         { "--data-name",        1,      OptDataName             },
582         { "--debug-info",       0,      OptDebugInfo            },
583         { "--help",             0,      OptHelp                 },
584         { "--verbose",          0,      OptVerbose              },
585         { "--version",          0,      OptVersion              },
586         { "--zeropage-label",   1,      OptZeropageLabel        },
587         { "--zeropage-name",    1,      OptZeropageName         },
588     };
589
590     unsigned I;
591
592     /* Initialize the cmdline module */
593     InitCmdLine (&argc, &argv, "co65");
594
595     /* Check the parameters */
596     I = 1;
597     while (I < ArgCount) {
598
599         /* Get the argument */
600         const char* Arg = ArgVec [I];
601
602         /* Check for an option */
603         if (Arg [0] == '-') {
604             switch (Arg [1]) {
605
606                 case '-':
607                     LongOption (&I, OptTab, sizeof(OptTab)/sizeof(OptTab[0]));
608                     break;
609
610                 case 'g':
611                     OptDebugInfo (Arg, 0);
612                     break;
613
614                 case 'h':
615                     OptHelp (Arg, 0);
616                     break;
617
618                 case 'o':
619                     OutputName = GetArg (&I, 2);
620                     break;
621
622                 case 'v':
623                     OptVerbose (Arg, 0);
624                     break;
625
626                 case 'V':
627                     OptVersion (Arg, 0);
628                     break;
629
630                 default:
631                     UnknownOption (Arg);
632                     break;
633
634             }
635         } else {
636             /* Filename. Check if we already had one */
637             if (InputName) {
638                 Error ("Don't know what to do with `%s'\n", Arg);
639             } else {
640                 InputName = Arg;
641             }
642         }
643
644         /* Next argument */
645         ++I;
646     }
647
648     /* Do we have an input file? */
649     if (InputName == 0) {
650         Error ("No input file\n");
651     }
652
653     /* Generate the name of the output file if none was specified */
654     if (OutputName == 0) {
655         OutputName = MakeFilename (InputName, AsmExt);
656     }
657
658     /* Do the conversion */
659     Convert ();
660
661     /* Return an apropriate exit code */
662     return EXIT_SUCCESS;
663 }
664
665
666