]> git.sur5r.net Git - cc65/blob - src/co65/main.c
74812947bca3ccce0cc678ed36b6e235d171dd73
[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     /* Output statistics */
327     Print (stdout, 1, "Size of text segment:               %5lu\n", D->Header.tlen);
328     Print (stdout, 1, "Size of data segment:               %5lu\n", D->Header.dlen);
329     Print (stdout, 1, "Size of bss segment:                %5lu\n", D->Header.blen);
330     Print (stdout, 1, "Size of zeropage segment:           %5lu\n", D->Header.zlen);
331     Print (stdout, 1, "Number of imports:                  %5u\n", CollCount (&D->Imports));
332     Print (stdout, 1, "Number of exports:                  %5u\n", CollCount (&D->Exports));
333     Print (stdout, 1, "Number of text segment relocations: %5u\n", CollCount (&D->TextReloc));
334     Print (stdout, 1, "Number of data segment relocations: %5u\n", CollCount (&D->DataReloc));
335
336     /* Open the output file */
337     F = fopen (OutFilename, "wb");
338     if (F == 0) {
339         Error ("Cannot open `%s': %s", OutFilename, strerror (errno));
340     }
341
342     /* Create a header */
343     if ((D->Header.mode & O65_CPU_MASK) == O65_CPU_65816) {
344         fprintf (F, "\t.p816\n");
345     }
346     fprintf (F, ";\n; File generated by co65 v %u.%u.%u\n;\n",
347              VER_MAJOR, VER_MINOR, VER_PATCH);
348     fprintf (F, "\t.fopt\t\tcompiler,\"co65 v %u.%u.%u\"\n",
349              VER_MAJOR, VER_MINOR, VER_PATCH);
350     fprintf (F, "\t.case\t\ton\n");
351     fprintf (F, "\t.debuginfo\t%s\n", (DebugInfo != 0)? "on" : "off");
352     fprintf (F, "\n");
353
354     /* Imported identifiers */
355     if (CollCount (&D->Imports) > 0) {
356         for (I = 0; I < CollCount (&D->Imports); ++I) {
357
358             /* Get the next import */
359             O65Import* Import = CollAtUnchecked (&D->Imports, I);
360
361             /* Import it by name */
362             fprintf (F, "\t.import\t%s\n", Import->Name);
363         }
364         fprintf (F, "\n");
365     }
366
367     /* Exported identifiers */
368     if (CollCount (&D->Exports) > 0) {
369         for (I = 0; I < CollCount (&D->Exports); ++I) {
370
371             /* Get the next import */
372             O65Export* Export = CollAtUnchecked (&D->Exports, I);
373
374             /* Import it by name */
375             fprintf (F, "\t.export\t%s\n", Export->Name);
376         }
377         fprintf (F, "\n");
378     }
379
380     /* Code segment */
381     fprintf (F, ".segment\t\"%s\"\n", CodeSeg);
382     fprintf (F, "%s:\n", CodeSeg);
383     ConvertSeg (F, D, &D->TextReloc, D->Text, D->Header.tlen);
384
385     /* Data segment */
386     fprintf (F, ".segment\t\"%s\"\n", DataSeg);
387     fprintf (F, "%s:\n", DataSeg);
388     ConvertSeg (F, D, &D->DataReloc, D->Data, D->Header.dlen);
389
390     /* BSS segment */
391     fprintf (F, ".segment\t\"%s\"\n", BssSeg);
392     fprintf (F, "%s:\n", BssSeg);
393     fprintf (F, "\t.res\t%lu\n", D->Header.blen);
394     fprintf (F, "\n");
395
396     fprintf (F, "\t.end\n");
397     fclose (F);
398 }
399
400
401
402 int main (int argc, char* argv [])
403 /* Converter main program */
404 {
405     /* Program long options */
406     static const LongOpt OptTab[] = {
407         { "--bss-name",         1,      OptBssName              },
408         { "--code-name",        1,      OptCodeName             },
409         { "--data-name",        1,      OptDataName             },
410         { "--debug-info",       0,      OptDebugInfo            },
411         { "--help",             0,      OptHelp                 },
412         { "--verbose",          0,      OptVerbose              },
413         { "--version",          0,      OptVersion              },
414         { "--zeropage-name",    1,      OptZeropageName         },
415     };
416
417     unsigned I;
418
419     /* Initialize the cmdline module */
420     InitCmdLine (&argc, &argv, "co65");
421
422     /* Check the parameters */
423     I = 1;
424     while (I < ArgCount) {
425
426         /* Get the argument */
427         const char* Arg = ArgVec [I];
428
429         /* Check for an option */
430         if (Arg [0] == '-') {
431             switch (Arg [1]) {
432
433                 case '-':
434                     LongOption (&I, OptTab, sizeof(OptTab)/sizeof(OptTab[0]));
435                     break;
436
437                 case 'g':
438                     OptDebugInfo (Arg, 0);
439                     break;
440
441                 case 'h':
442                     OptHelp (Arg, 0);
443                     break;
444
445                 case 'o':
446                     OutFilename = GetArg (&I, 2);
447                     break;
448
449                 case 'v':
450                     OptVerbose (Arg, 0);
451                     break;
452
453                 case 'V':
454                     OptVersion (Arg, 0);
455                     break;
456
457                 default:
458                     UnknownOption (Arg);
459                     break;
460
461             }
462         } else {
463             /* Filename. Check if we already had one */
464             if (InFilename) {
465                 Error ("Don't know what to do with `%s'\n", Arg);
466             } else {
467                 InFilename = Arg;
468             }
469         }
470
471         /* Next argument */
472         ++I;
473     }
474
475     /* Do we have an input file? */
476     if (InFilename == 0) {
477         Error ("No input file\n");
478     }
479
480     /* Generate the name of the output file if none was specified */
481     if (OutFilename == 0) {
482         OutFilename = MakeFilename (InFilename, AsmExt);
483     }
484
485     /* Do the conversion */
486     Convert ();
487
488     /* Return an apropriate exit code */
489     return EXIT_SUCCESS;
490 }
491
492
493