]> git.sur5r.net Git - cc65/blob - src/co65/main.c
First import
[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 "cmdline.h"
44 #include "fname.h"
45 #include "print.h"
46 #include "segnames.h"
47 #include "version.h"
48 #include "xmalloc.h"
49 #include "xsprintf.h"
50
51 /* co65 */
52 #include "error.h"
53 #include "global.h"
54 #include "o65.h"
55
56
57
58 /*****************************************************************************/
59 /*                                   Code                                    */
60 /*****************************************************************************/
61
62
63
64 static void Usage (void)
65 /* Print usage information and exit */
66 {
67     fprintf (stderr,
68              "Usage: %s [options] file\n"
69              "Short options:\n"
70              "  -V\t\t\tPrint the version number\n"
71              "  -g\t\t\tAdd debug info to object file\n"
72              "  -h\t\t\tHelp (this text)\n"
73              "  -o name\t\tName the output file\n"
74              "  -v\t\t\tIncrease verbosity\n"
75              "\n"
76              "Long options:\n"
77              "  --bss-name seg\tSet the name of the BSS segment\n"
78              "  --code-name seg\tSet the name of the CODE segment\n"
79              "  --data-name seg\tSet the name of the DATA segment\n"
80              "  --debug-info\t\tAdd debug info to object file\n"
81              "  --help\t\tHelp (this text)\n"
82              "  --verbose\t\tIncrease verbosity\n"
83              "  --version\t\tPrint the version number\n"
84              "  --zeropage-name seg\tSet the name of the ZEROPAGE segment\n",
85              ProgName);
86 }
87
88
89
90 static void CheckSegName (const char* Seg)
91 /* Abort if the given name is not a valid segment name */
92 {
93     /* Print an error and abort if the name is not ok */
94     if (!ValidSegName (Seg)) {
95         Error ("Segment name `%s' is invalid", Seg);
96     }
97 }
98
99
100
101 static void OptBssName (const char* Opt attribute ((unused)), const char* Arg)
102 /* Handle the --bss-name option */
103 {
104     /* Check for a valid name */
105     CheckSegName (Arg);
106
107     /* Set the name */
108     BssSeg = xstrdup (Arg);
109 }
110
111
112
113 static void OptCodeName (const char* Opt attribute ((unused)), const char* Arg)
114 /* Handle the --code-name option */
115 {
116     /* Check for a valid name */
117     CheckSegName (Arg);
118
119     /* Set the name */
120     CodeSeg = xstrdup (Arg);
121 }
122
123
124
125 static void OptDataName (const char* Opt attribute ((unused)), const char* Arg)
126 /* Handle the --data-name option */
127 {
128     /* Check for a valid name */
129     CheckSegName (Arg);
130
131     /* Set the name */
132     DataSeg = xstrdup (Arg);
133 }
134
135
136
137 static void OptDebugInfo (const char* Opt attribute ((unused)),
138                           const char* Arg attribute ((unused)))
139 /* Add debug info to the object file */
140 {
141     DebugInfo = 1;
142 }
143
144
145
146 static void OptHelp (const char* Opt attribute ((unused)),
147                      const char* Arg attribute ((unused)))
148 /* Print usage information and exit */
149 {
150     Usage ();
151     exit (EXIT_SUCCESS);
152 }
153
154
155
156 static void OptVerbose (const char* Opt attribute ((unused)),
157                         const char* Arg attribute ((unused)))
158 /* Increase verbosity */
159 {
160     ++Verbosity;
161 }
162
163
164
165 static void OptVersion (const char* Opt attribute ((unused)),
166                         const char* Arg attribute ((unused)))
167 /* Print the assembler version */
168 {
169     fprintf (stderr,
170              "co65 V%u.%u.%u - (C) Copyright 1998-2003 Ullrich von Bassewitz\n",
171              VER_MAJOR, VER_MINOR, VER_PATCH);
172 }
173
174
175
176 static void OptZeropageName (const char* Opt attribute ((unused)), const char* Arg)
177 /* Handle the --zeropage-name option */
178 {
179     /* Check for a valid name */
180     CheckSegName (Arg);
181
182     /* Set the name */
183     ZeropageSeg = xstrdup (Arg);
184 }
185
186
187
188 static const char* SegReloc (const O65Data* D, const O65Reloc* R, unsigned long Val)
189 {
190     static char Buf[256];
191     const O65Import* Import;
192
193     switch (R->SegID) {
194
195         case O65_SEGID_UNDEF:
196             if (R->SymIdx >= CollCount (&D->Imports)) {
197                 Error ("Import index out of range (input file corrupt)");
198             }
199             Import = CollConstAt (&D->Imports, R->SymIdx);
200             xsprintf (Buf, sizeof (Buf), "%s%+ld", Import->Name, (long) Val);
201             break;
202
203         case O65_SEGID_TEXT:
204             xsprintf (Buf, sizeof (Buf), "%s%+ld", CodeSeg, (long) (Val - D->Header.tbase));
205             break;
206
207         case O65_SEGID_DATA:
208             xsprintf (Buf, sizeof (Buf), "%s%+ld", DataSeg, (long) (Val - D->Header.dbase));
209             break;
210
211         case O65_SEGID_BSS:
212             xsprintf (Buf, sizeof (Buf), "%s%+ld", BssSeg, (long) (Val - D->Header.bbase));
213             break;
214
215         case O65_SEGID_ZP:
216             xsprintf (Buf, sizeof (Buf), "%s%+ld", ZeropageSeg, (long) Val - D->Header.zbase);
217             break;
218
219         case O65_SEGID_ABS:
220             Error ("Relocation entry contains O65_SEGID_ABS");
221             break;
222
223         default:
224             Internal ("Cannot handle this segment reference in reloc entry");
225     }
226
227     return Buf;
228 }
229
230
231
232 static void ConvertSeg (FILE* F, const O65Data* D, const Collection* Relocs,
233                         const unsigned char* Data, unsigned long Size)
234 /* Convert one segment */
235 {
236     const O65Reloc* R;
237     unsigned        RIdx;
238     unsigned long   Byte;
239
240     /* Get the pointer to the first relocation entry if there are any */
241     R = (CollCount (Relocs) > 0)? CollConstAt (Relocs, 0) : 0;
242
243     /* Initialize for the loop */
244     RIdx = 0;
245     Byte = 0;
246
247     /* Walk over the segment data */
248     while (Byte < Size) {
249
250         if (R && R->Offs == Byte) {
251             /* We've reached an entry that must be relocated */
252             unsigned long Val;
253             switch (R->Type) {
254
255                 case O65_RTYPE_WORD:
256                     if (Byte >= Size - 1) {
257                         Error ("Found WORD relocation, but not enough bytes left");
258                     } else {
259                         Val = (Data[Byte+1] << 8) + Data[Byte];
260                         Byte += 2;
261                         fprintf (F, "\t.word\t%s\n", SegReloc (D, R, Val));
262                     }
263                     break;
264
265                 case O65_RTYPE_HIGH:
266                     Val = (Data[Byte++] << 8) + R->Val;
267                     fprintf (F, "\t.byte\t>(%s)\n", SegReloc (D, R, Val));
268                     break;
269
270                 case O65_RTYPE_LOW:
271                     Val = Data[Byte++];
272                     fprintf (F, "\t.byte\t<(%s)\n", SegReloc (D, R, Val));
273                     break;
274
275                 case O65_RTYPE_SEGADDR:
276                     if (Byte >= Size - 2) {
277                         Error ("Found SEGADDR relocation, but not enough bytes left");
278                     } else {
279                         Val = (((unsigned long) Data[Byte+2]) << 16) +
280                               (((unsigned long) Data[Byte+1]) <<  8) +
281                               (((unsigned long) Data[Byte+0]) <<  0) +
282                               R->Val;
283                         Byte += 3;
284                         fprintf (F, "\t.faraddr\t%s\n", SegReloc (D, R, Val));
285                     }
286                     break;
287                 case O65_RTYPE_SEG:
288                 default:
289                     Internal ("Invalid relocation type at %lu", Byte);
290             }
291
292             /* Get the next relocation entry */
293             if (++RIdx < CollCount (Relocs)) {
294                 R = CollConstAt (Relocs, RIdx);
295             } else {
296                 R = 0;
297             }
298
299         } else {
300             /* Just a constant value */
301             fprintf (F, "\t.byte\t$%02X\n", Data[Byte++]);
302         }
303     }
304
305     fprintf (F, "\n");
306 }
307
308
309
310 static void Convert (void)
311 /* Do file conversion */
312 {
313     FILE* F;
314     unsigned  I;
315
316     /* Read the o65 file into memory */
317     O65Data* D = ReadO65File (InFilename);
318
319     /* For now, we do only accept o65 files generated by the ld65 linker which
320      * have a specific format.
321      */
322     if (D->Header.mode != O65_MODE_CC65) {
323         Error ("Cannot convert o65 files of this type");
324     }
325
326     printf ("Textsize:   %lu\n", D->Header.tlen);
327     printf ("Datasize:   %lu\n", D->Header.dlen);
328     printf ("Imports:    %u\n", CollCount (&D->Imports));
329     printf ("Exports:    %u\n", CollCount (&D->Exports));
330     printf ("Textrelocs: %u\n", CollCount (&D->TextReloc));
331     printf ("Datarelocs: %u\n", CollCount (&D->DataReloc));
332
333     /* Open the output file */
334     F = fopen (OutFilename, "wb");
335     if (F == 0) {
336         Error ("Cannot open `%s': %s", OutFilename, strerror (errno));
337     }
338
339     /* Create a header */
340     if ((D->Header.mode & O65_CPU_MASK) == O65_CPU_65816) {
341         fprintf (F, "\t.p816\n");
342     }
343     fprintf (F, ";\n; File generated by co65 v %u.%u.%u\n;\n",
344              VER_MAJOR, VER_MINOR, VER_PATCH);
345     fprintf (F, "\t.fopt\t\tcompiler,\"co65 v %u.%u.%u\"\n",
346              VER_MAJOR, VER_MINOR, VER_PATCH);
347     fprintf (F, "\t.case\t\ton\n");
348     fprintf (F, "\t.debuginfo\t%s\n", (DebugInfo != 0)? "on" : "off");
349     fprintf (F, "\n");
350
351     /* Imported identifiers */
352     if (CollCount (&D->Imports) > 0) {
353         for (I = 0; I < CollCount (&D->Imports); ++I) {
354
355             /* Get the next import */
356             O65Import* Import = CollAtUnchecked (&D->Imports, I);
357
358             /* Import it by name */
359             fprintf (F, "\t.import\t%s\n", Import->Name);
360         }
361         fprintf (F, "\n");
362     }
363
364     /* Exported identifiers */
365     if (CollCount (&D->Exports) > 0) {
366         for (I = 0; I < CollCount (&D->Exports); ++I) {
367
368             /* Get the next import */
369             O65Export* Export = CollAtUnchecked (&D->Exports, I);
370
371             /* Import it by name */
372             fprintf (F, "\t.export\t%s\n", Export->Name);
373         }
374         fprintf (F, "\n");
375     }
376
377     /* Code segment */
378     fprintf (F, ".segment\t\"%s\"\n", CodeSeg);
379     fprintf (F, "%s:\n", CodeSeg);
380     ConvertSeg (F, D, &D->TextReloc, D->Text, D->Header.tlen);
381
382     /* Data segment */
383     fprintf (F, ".segment\t\"%s\"\n", DataSeg);
384     fprintf (F, "%s:\n", DataSeg);
385     ConvertSeg (F, D, &D->DataReloc, D->Data, D->Header.dlen);
386
387     /* BSS segment */
388     fprintf (F, ".segment\t\"%s\"\n", BssSeg);
389     fprintf (F, "%s:\n", BssSeg);
390     fprintf (F, "\t.res\t%lu\n", D->Header.blen);
391     fprintf (F, "\n");
392
393     fprintf (F, "\t.end\n");
394     fclose (F);
395 }
396
397
398
399 int main (int argc, char* argv [])
400 /* Converter main program */
401 {
402     /* Program long options */
403     static const LongOpt OptTab[] = {
404         { "--bss-name",         1,      OptBssName              },
405         { "--code-name",        1,      OptCodeName             },
406         { "--data-name",        1,      OptDataName             },
407         { "--debug-info",       0,      OptDebugInfo            },
408         { "--help",             0,      OptHelp                 },
409         { "--verbose",          0,      OptVerbose              },
410         { "--version",          0,      OptVersion              },
411         { "--zeropage-name",    1,      OptZeropageName         },
412     };
413
414     unsigned I;
415
416     /* Initialize the cmdline module */
417     InitCmdLine (&argc, &argv, "co65");
418
419     /* Check the parameters */
420     I = 1;
421     while (I < ArgCount) {
422
423         /* Get the argument */
424         const char* Arg = ArgVec [I];
425
426         /* Check for an option */
427         if (Arg [0] == '-') {
428             switch (Arg [1]) {
429
430                 case '-':
431                     LongOption (&I, OptTab, sizeof(OptTab)/sizeof(OptTab[0]));
432                     break;
433
434                 case 'g':
435                     OptDebugInfo (Arg, 0);
436                     break;
437
438                 case 'h':
439                     OptHelp (Arg, 0);
440                     break;
441
442                 case 'o':
443                     OutFilename = GetArg (&I, 2);
444                     break;
445
446                 case 'v':
447                     OptVerbose (Arg, 0);
448                     break;
449
450                 case 'V':
451                     OptVersion (Arg, 0);
452                     break;
453
454                 default:
455                     UnknownOption (Arg);
456                     break;
457
458             }
459         } else {
460             /* Filename. Check if we already had one */
461             if (InFilename) {
462                 Error ("Don't know what to do with `%s'\n", Arg);
463             } else {
464                 InFilename = Arg;
465             }
466         }
467
468         /* Next argument */
469         ++I;
470     }
471
472     /* Do we have an input file? */
473     if (InFilename == 0) {
474         Error ("No input file\n");
475     }
476
477     /* Generate the name of the output file if none was specified */
478     if (OutFilename == 0) {
479         OutFilename = MakeFilename (InFilename, AsmExt);
480     }
481
482     /* Do the conversion */
483     Convert ();
484
485     /* Return an apropriate exit code */
486     return EXIT_SUCCESS;
487 }
488
489
490