]> git.sur5r.net Git - cc65/blob - src/cc65/main.c
da92fcd727432351eb75e2d5cf34e9150b2e2696
[cc65] / src / cc65 / main.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                  main.c                                   */
4 /*                                                                           */
5 /*                             cc65 main program                             */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 2000-2002 Ullrich von Bassewitz                                       */
10 /*               Wacholderweg 14                                             */
11 /*               D-70597 Stuttgart                                           */
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 <string.h>
38 #include <stdlib.h>
39 #include <errno.h>
40
41 /* common */
42 #include "abend.h"
43 #include "chartype.h"
44 #include "cmdline.h"
45 #include "fname.h"
46 #include "print.h"
47 #include "target.h"
48 #include "tgttrans.h"
49 #include "version.h"
50 #include "xmalloc.h"
51
52 /* cc65 */
53 #include "asmcode.h"
54 #include "compile.h"
55 #include "codeopt.h"
56 #include "cpu.h"
57 #include "error.h"
58 #include "global.h"
59 #include "incpath.h"
60 #include "input.h"
61 #include "macrotab.h"
62 #include "scanner.h"
63 #include "segments.h"
64
65
66
67 /*****************************************************************************/
68 /*                                   Code                                    */
69 /*****************************************************************************/
70
71
72
73 static void Usage (void)
74 {
75     fprintf (stderr,
76              "Usage: %s [options] file\n"
77              "Short options:\n"
78              "  -A\t\t\tStrict ANSI mode\n"
79              "  -Cl\t\t\tMake local variables static\n"
80              "  -Dsym[=defn]\t\tDefine a symbol\n"
81              "  -I dir\t\tSet an include directory search path\n"
82              "  -O\t\t\tOptimize code\n"
83              "  -Oi\t\t\tOptimize code, inline more code\n"
84              "  -Or\t\t\tEnable register variables\n"
85              "  -Os\t\t\tInline some known functions\n"
86              "  -T\t\t\tInclude source as comment\n"
87              "  -V\t\t\tPrint the compiler version number\n"
88              "  -W\t\t\tSuppress warnings\n"
89              "  -d\t\t\tDebug mode\n"
90              "  -g\t\t\tAdd debug info to object file\n"
91              "  -h\t\t\tHelp (this text)\n"
92              "  -j\t\t\tDefault characters are signed\n"
93              "  -o name\t\tName the output file\n"
94              "  -t sys\t\tSet the target system\n"
95              "  -v\t\t\tIncrease verbosity\n"
96              "\n"
97              "Long options:\n"
98              "  --add-source\t\tInclude source as comment\n"
99              "  --ansi\t\tStrict ANSI mode\n"
100              "  --bss-name seg\tSet the name of the BSS segment\n"
101              "  --check-stack\t\tGenerate stack overflow checks\n"
102              "  --code-name seg\tSet the name of the CODE segment\n"
103              "  --codesize x\t\tAccept larger code by factor x\n"
104              "  --cpu type\t\tSet cpu type\n"
105              "  --create-dep\t\tCreate a make dependency file\n"
106              "  --data-name seg\tSet the name of the DATA segment\n"
107              "  --debug\t\tDebug mode\n"
108              "  --debug-info\t\tAdd debug info to object file\n"
109              "  --debug-opt name\tDebug optimization steps\n"
110              "  --disable-opt name\tDisable an optimization step\n"
111              "  --enable-opt name\tEnable an optimization step\n"
112              "  --help\t\tHelp (this text)\n"
113              "  --include-dir dir\tSet an include directory search path\n"
114              "  --list-opt-steps\tList all optimizer steps and exit\n"
115              "  --rodata-name seg\tSet the name of the RODATA segment\n"
116              "  --signed-chars\tDefault characters are signed\n"
117              "  --static-locals\tMake local variables static\n"
118              "  --target sys\t\tSet the target system\n"
119              "  --verbose\t\tIncrease verbosity\n"
120              "  --version\t\tPrint the compiler version number\n",
121              ProgName);
122 }
123
124
125
126 static void cbmsys (const char* sys)
127 /* Define a CBM system */
128 {
129     DefineNumericMacro ("__CBM__", 1);
130     DefineNumericMacro (sys, 1);
131 }
132
133
134
135 static void SetSys (const char* Sys)
136 /* Define a target system */
137 {
138     switch (Target = FindTarget (Sys)) {
139
140         case TGT_NONE:
141             break;
142
143         case TGT_MODULE:
144             AbEnd ("Cannot use `module' as a target for the compiler");
145             break;
146
147         case TGT_ATARI:
148             DefineNumericMacro ("__ATARI__", 1);
149             break;
150
151         case TGT_C16:
152             cbmsys ("__C16__");
153             break;
154
155         case TGT_C64:
156             cbmsys ("__C64__");
157             break;
158
159         case TGT_VIC20:
160             cbmsys ("__VIC20__");
161             break;
162
163         case TGT_C128:
164             cbmsys ("__C128__");
165             break;
166
167         case TGT_ACE:
168             cbmsys ("__ACE__");
169             break;
170
171         case TGT_PLUS4:
172             cbmsys ("__PLUS4__");
173             break;
174
175         case TGT_CBM510:
176             cbmsys ("__CBM510__");
177             break;
178
179         case TGT_CBM610:
180             cbmsys ("__CBM610__");
181             break;
182
183         case TGT_PET:
184             cbmsys ("__PET__");
185             break;
186
187         case TGT_BBC:
188             DefineNumericMacro ("__BBC__", 1);
189             break;
190
191         case TGT_APPLE2:
192             DefineNumericMacro ("__APPLE2__", 1);
193             break;
194
195         case TGT_GEOS:
196             /* Do not handle as a CBM system */
197             DefineNumericMacro ("__GEOS__", 1);
198             break;
199
200         case TGT_LUNIX:
201             DefineNumericMacro ("__LUNIX__", 1);
202             break;
203
204         case TGT_ATMOS:
205             DefineNumericMacro ("__ATMOS__", 1);
206             break;
207
208         default:
209             AbEnd ("Unknown target system type");
210     }
211
212     /* Initialize the translation tables for the target system */
213     TgtTranslateInit ();
214 }
215
216
217
218 static void DoCreateDep (const char* OutputName)
219 /* Create the dependency file */
220 {
221     /* Make the dependency file name from the output file name */
222     char* DepName = MakeFilename (OutputName, ".u");
223
224     /* Open the file */
225     FILE* F = fopen (DepName, "w");
226     if (F == 0) {
227         Fatal ("Cannot open dependency file `%s': %s", DepName, strerror (errno));
228     }
229
230     /* Write the dependencies to the file */
231     WriteDependencies (F, OutputName);
232
233     /* Close the file, check for errors */
234     if (fclose (F) != 0) {
235         remove (DepName);
236         Fatal ("Cannot write to dependeny file (disk full?)");
237     }
238
239     /* Free the name */
240     xfree (DepName);
241 }
242
243
244
245 static void DefineSym (const char* Def)
246 /* Define a symbol on the command line */
247 {
248     const char* P = Def;
249
250     /* The symbol must start with a character or underline */
251     if (Def [0] != '_' && !IsAlpha (Def [0])) {
252         InvDef (Def);
253     }
254
255     /* Check the symbol name */
256     while (IsAlNum (*P) || *P == '_') {
257         ++P;
258     }
259
260     /* Do we have a value given? */
261     if (*P != '=') {
262         if (*P != '\0') {
263             InvDef (Def);
264         }
265         /* No value given. Define the macro with the value 1 */
266         DefineNumericMacro (Def, 1);
267     } else {
268         /* We have a value, P points to the '=' character. Since the argument
269          * is const, create a copy and replace the '=' in the copy by a zero
270          * terminator.
271          */
272         char* Q;
273         unsigned Len = strlen (Def)+1;
274         char* S = (char*) xmalloc (Len);
275         memcpy (S, Def, Len);
276         Q = S + (P - Def);
277         *Q++ = '\0';
278
279         /* Define this as a macro */
280         DefineTextMacro (S, Q);
281
282         /* Release the allocated memory */
283         xfree (S);
284     }
285 }
286
287
288
289 static void CheckSegName (const char* Seg)
290 /* Abort if the given name is not a valid segment name */
291 {
292     /* Print an error and abort if the name is not ok */
293     if (!ValidSegName (Seg)) {
294         AbEnd ("Segment name `%s' is invalid", Seg);
295     }
296 }
297
298
299
300 static void OptAddSource (const char* Opt attribute ((unused)),
301                           const char* Arg attribute ((unused)))
302 /* Add source lines as comments in generated assembler file */
303 {
304     AddSource = 1;
305 }
306
307
308
309 static void OptAnsi (const char* Opt attribute ((unused)),
310                      const char* Arg attribute ((unused)))
311 /* Compile in strict ANSI mode */
312 {
313     ANSI = 1;
314 }
315
316
317
318 static void OptBssName (const char* Opt attribute ((unused)), const char* Arg)
319 /* Handle the --bss-name option */
320 {
321     /* Check for a valid name */
322     CheckSegName (Arg);
323
324     /* Set the name */
325     NewSegName (SEG_BSS, Arg);
326 }
327
328
329
330 static void OptCheckStack (const char* Opt attribute ((unused)),
331                            const char* Arg attribute ((unused)))
332 /* Handle the --check-stack option */
333 {
334     CheckStack = 1;
335 }
336
337
338
339 static void OptCodeName (const char* Opt attribute ((unused)), const char* Arg)
340 /* Handle the --code-name option */
341 {
342     /* Check for a valid name */
343     CheckSegName (Arg);
344
345     /* Set the name */
346     NewSegName (SEG_CODE, Arg);
347 }
348
349
350
351 static void OptCodeSize (const char* Opt, const char* Arg)
352 /* Handle the --codesize option */
353 {
354     /* Numeric argument expected */
355     if (sscanf (Arg, "%u", &CodeSizeFactor) != 1 ||
356         CodeSizeFactor < 100 ||
357         CodeSizeFactor > 1000) {
358         AbEnd ("Argument for %s is invalid", Opt);
359     }
360 }
361
362
363
364 static void OptCreateDep (const char* Opt attribute ((unused)),
365                           const char* Arg attribute ((unused)))
366 /* Handle the --create-dep option */
367 {
368     CreateDep = 1;
369 }
370
371
372
373 static void OptCPU (const char* Opt, const char* Arg)
374 /* Handle the --cpu option */
375 {
376     if (strcmp (Arg, "6502") == 0) {
377         CPU = CPU_6502;
378     } else if (strcmp (Arg, "65C02") == 0) {
379         CPU = CPU_65C02;
380     } else {
381         AbEnd ("Invalid argument for %s: `%s'", Opt, Arg);
382     }
383 }
384
385
386
387 static void OptDataName (const char* Opt attribute ((unused)), const char* Arg)
388 /* Handle the --data-name option */
389 {
390     /* Check for a valid name */
391     CheckSegName (Arg);
392
393     /* Set the name */
394     NewSegName (SEG_DATA, Arg);
395 }
396
397
398
399 static void OptDebug (const char* Opt attribute ((unused)),
400                       const char* Arg attribute ((unused)))
401 /* Compiler debug mode */
402 {
403     Debug = 1;
404 }
405
406
407
408 static void OptDebugInfo (const char* Opt attribute ((unused)),
409                           const char* Arg attribute ((unused)))
410 /* Add debug info to the object file */
411 {
412     DebugInfo = 1;
413 }
414
415
416
417 static void OptDebugOpt (const char* Opt attribute ((unused)), const char* Arg)
418 /* Debug optimization steps */
419 {
420     char Buf [128];
421     char* Line;
422
423     /* Open the file */
424     FILE* F = fopen (Arg, "r");
425     if (F == 0) {
426         AbEnd ("Cannot open `%s': %s", Arg, strerror (errno));
427     }
428
429     /* Read line by line, ignore empty lines and switch optimization
430      * steps on/off.
431      */
432     while (fgets (Buf, sizeof (Buf), F) != 0) {
433
434         /* Remove trailing control chars. This will also remove the
435          * trailing newline.
436          */
437         unsigned Len = strlen (Buf);
438         while (Len > 0 && IsControl (Buf[Len-1])) {
439             --Len;
440         }
441         Buf[Len] = '\0';
442
443         /* Get a pointer to the buffer and remove leading white space */
444         Line = Buf;
445         while (IsBlank (*Line)) {
446             ++Line;
447         }
448
449         /* Check the first character and enable/disable the step or
450          * ignore the line
451          */
452         switch (*Line) {
453
454             case '\0':
455             case '#':
456             case ';':
457                 /* Empty or comment line */
458                 continue;
459
460             case '-':
461                 DisableOpt (Line+1);
462                 break;
463
464             case '+':
465                 ++Line;
466                 /* FALLTHROUGH */
467
468             default:
469                 EnableOpt (Line);
470                 break;
471
472         }
473
474     }
475
476     /* Close the file, no error check here since we were just reading and
477      * this is only a debug function.
478      */
479     (void) fclose (F);
480 }
481
482
483
484 static void OptDisableOpt (const char* Opt attribute ((unused)), const char* Arg)
485 /* Disable an optimization step */
486 {
487     DisableOpt (Arg);
488 }
489
490
491
492 static void OptEnableOpt (const char* Opt attribute ((unused)), const char* Arg)
493 /* Enable an optimization step */
494 {
495     EnableOpt (Arg);
496 }
497
498
499
500 static void OptHelp (const char* Opt attribute ((unused)),
501                      const char* Arg attribute ((unused)))
502 /* Print usage information and exit */
503 {
504     Usage ();
505     exit (EXIT_SUCCESS);
506 }
507
508
509
510 static void OptIncludeDir (const char* Opt attribute ((unused)), const char* Arg)
511 /* Add an include search path */
512 {
513     AddIncludePath (Arg, INC_SYS | INC_USER);
514 }
515
516
517
518 static void OptListOptSteps (const char* Opt attribute ((unused)),
519                              const char* Arg attribute ((unused)))
520 /* List all optimizer steps */
521 {
522     /* List the optimizer steps */
523     ListOptSteps (stdout);
524
525     /* Terminate */
526     exit (EXIT_SUCCESS);
527 }
528
529
530
531 static void OptRodataName (const char* Opt attribute ((unused)), const char* Arg)
532 /* Handle the --rodata-name option */
533 {
534     /* Check for a valid name */
535     CheckSegName (Arg);
536
537     /* Set the name */
538     NewSegName (SEG_RODATA, Arg);
539 }
540
541
542
543 static void OptSignedChars (const char* Opt attribute ((unused)),
544                             const char* Arg attribute ((unused)))
545 /* Make default characters signed */
546 {
547     SignedChars = 1;
548 }
549
550
551
552 static void OptStaticLocals (const char* Opt attribute ((unused)),
553                              const char* Arg attribute ((unused)))
554 /* Place local variables in static storage */
555 {
556     StaticLocals = 1;
557 }
558
559
560
561 static void OptTarget (const char* Opt attribute ((unused)), const char* Arg)
562 /* Set the target system */
563 {
564     SetSys (Arg);
565 }
566
567
568
569 static void OptVerbose (const char* Opt attribute ((unused)),
570                         const char* Arg attribute ((unused)))
571 /* Increase verbosity */
572 {
573     ++Verbosity;
574 }
575
576
577
578 static void OptVersion (const char* Opt attribute ((unused)),
579                         const char* Arg attribute ((unused)))
580 /* Print the assembler version */
581 {
582     fprintf (stderr,
583              "cc65 V%u.%u.%u\n",
584              VER_MAJOR, VER_MINOR, VER_PATCH);
585 }
586
587
588
589 int main (int argc, char* argv[])
590 {
591     /* Program long options */
592     static const LongOpt OptTab[] = {
593         { "--add-source",       0,      OptAddSource            },
594         { "--ansi",             0,      OptAnsi                 },
595         { "--bss-name",         1,      OptBssName              },
596         { "--check-stack",      0,      OptCheckStack           },
597         { "--code-name",        1,      OptCodeName             },
598         { "--codesize",         1,      OptCodeSize             },
599         { "--cpu",              1,      OptCPU                  },
600         { "--create-dep",       0,      OptCreateDep            },
601         { "--data-name",        1,      OptDataName             },
602         { "--debug",            0,      OptDebug                },
603         { "--debug-info",       0,      OptDebugInfo            },
604         { "--debug-opt",        1,      OptDebugOpt             },
605         { "--disable-opt",      1,      OptDisableOpt           },
606         { "--enable-opt",       1,      OptEnableOpt,           },
607         { "--help",             0,      OptHelp                 },
608         { "--include-dir",      1,      OptIncludeDir           },
609         { "--list-opt-steps",   0,      OptListOptSteps         },
610         { "--rodata-name",      1,      OptRodataName           },
611         { "--signed-chars",     0,      OptSignedChars          },
612         { "--static-locals",    0,      OptStaticLocals         },
613         { "--target",           1,      OptTarget               },
614         { "--verbose",          0,      OptVerbose              },
615         { "--version",          0,      OptVersion              },
616     };
617
618     unsigned I;
619
620     /* Initialize the output file name */
621     const char* OutputFile = 0;
622     const char* InputFile  = 0;
623
624     /* Initialize the cmdline module */
625     InitCmdLine (&argc, &argv, "cc65");
626
627     /* Initialize the default segment names */
628     InitSegNames ();
629
630     /* Parse the command line */
631     I = 1;
632     while (I < ArgCount) {
633
634         const char* P;
635
636         /* Get the argument */
637         const char* Arg = ArgVec[I];
638
639         /* Check for an option */
640         if (Arg [0] == '-') {
641
642             switch (Arg [1]) {
643
644                 case '-':
645                     LongOption (&I, OptTab, sizeof(OptTab)/sizeof(OptTab[0]));
646                     break;
647
648                 case 'd':
649                     OptDebug (Arg, 0);
650                     break;
651
652                 case 'h':
653                 case '?':
654                     OptHelp (Arg, 0);
655                     break;
656
657                 case 'g':
658                     OptDebugInfo (Arg, 0);
659                     break;
660
661                 case 'j':
662                     OptSignedChars (Arg, 0);
663                     break;
664
665                 case 'o':
666                     OutputFile = GetArg (&I, 2);
667                     break;
668
669                 case 't':
670                     OptTarget (Arg, GetArg (&I, 2));
671                     break;
672
673                 case 'u':
674                     OptCreateDep (Arg, 0);
675                     break;
676
677                 case 'v':
678                     OptVerbose (Arg, 0);
679                     break;
680
681                 case 'A':
682                     OptAnsi (Arg, 0);
683                     break;
684
685                 case 'C':
686                     P = Arg + 2;
687                     while (*P) {
688                         switch (*P++) {
689                             case 'l':
690                                 OptStaticLocals (Arg, 0);
691                                 break;
692                             default:
693                                 UnknownOption (Arg);
694                                 break;
695                         }
696                     }
697                     break;
698
699                 case 'D':
700                     DefineSym (GetArg (&I, 2));
701                     break;
702
703                 case 'I':
704                     OptIncludeDir (Arg, GetArg (&I, 2));
705                     break;
706
707                 case 'O':
708                     Optimize = 1;
709                     P = Arg + 2;
710                     while (*P) {
711                         switch (*P++) {
712                             case 'f':
713                                 sscanf (P, "%lx", (long*) &OptDisable);
714                                 break;
715                             case 'i':
716                                 FavourSize = 0;
717                                 CodeSizeFactor = 200;
718                                 break;
719                             case 'r':
720                                 EnableRegVars = 1;
721                                 break;
722                             case 's':
723                                 InlineStdFuncs = 1;
724                                 break;
725                         }
726                     }
727                     break;
728
729                 case 'T':
730                     OptAddSource (Arg, 0);
731                     break;
732
733                 case 'V':
734                     OptVersion (Arg, 0);
735                     break;
736
737                 case 'W':
738                     NoWarn = 1;
739                     break;
740
741                 default:
742                     UnknownOption (Arg);
743                     break;
744             }
745         } else {
746             if (InputFile) {
747                 fprintf (stderr, "additional file specs ignored\n");
748             } else {
749                 InputFile = Arg;
750             }
751         }
752
753         /* Next argument */
754         ++I;
755     }
756
757     /* Did we have a file spec on the command line? */
758     if (InputFile == 0) {
759         AbEnd ("No input files");
760     }
761
762     /* Create the output file name if it was not explicitly given */
763     if (OutputFile == 0) {
764         OutputFile = MakeFilename (InputFile, ".s");
765     }
766
767     /* Go! */
768     Compile (InputFile);
769
770     /* Create the output file if we didn't had any errors */
771     if (ErrorCount == 0 || Debug) {
772
773         FILE* F;
774
775 #if 0
776         /* Optimize the output if requested */
777         if (Optimize) {
778             OptDoOpt ();
779         }
780 #endif
781
782         /* Open the file */
783         F = fopen (OutputFile, "w");
784         if (F == 0) {
785             Fatal ("Cannot open output file `%s': %s", OutputFile, strerror (errno));
786         }
787
788         /* Write the output to the file */
789         WriteOutput (F);
790
791         /* Close the file, check for errors */
792         if (fclose (F) != 0) {
793             remove (OutputFile);
794             Fatal ("Cannot write to output file (disk full?)");
795         }
796
797         /* Create dependencies if requested */
798         if (CreateDep) {
799             DoCreateDep (OutputFile);
800         }
801
802     }
803
804     /* Return an apropriate exit code */
805     return (ErrorCount > 0)? EXIT_FAILURE : EXIT_SUCCESS;
806 }
807
808
809