]> git.sur5r.net Git - cc65/blob - src/ca65/main.c
063f467af1ebdf9bb2ed1929fef91b1908a0606d
[cc65] / src / ca65 / main.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                  main.c                                   */
4 /*                                                                           */
5 /*                 Main program for the ca65 macroassembler                  */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 1998-2000 Ullrich von Bassewitz                                       */
10 /*               Wacholderweg 14                                             */
11 /*               D-70597 Stuttgart                                           */
12 /* EMail:        uz@musoftware.de                                            */
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 <ctype.h>
40 #include <time.h>
41
42 #include "../common/version.h"
43
44 #include "error.h"
45 #include "expr.h"
46 #include "global.h"
47 #include "incpath.h"
48 #include "instr.h"
49 #include "listing.h"
50 #include "macro.h"
51 #include "mem.h"
52 #include "nexttok.h"
53 #include "objcode.h"
54 #include "objfile.h"
55 #include "options.h"
56 #include "pseudo.h"
57 #include "scanner.h"
58 #include "symtab.h"
59 #include "ulabel.h"
60
61
62
63 /*****************************************************************************/
64 /*                                   Code                                    */
65 /*****************************************************************************/
66
67
68
69 static void Usage (void)
70 /* Print usage information and exit */
71 {
72     fprintf (stderr,
73              "Usage: %s [options] file\n"
74              "Short options:\n"
75              "  -g\t\t\tAdd debug info to object file\n"
76              "  -i\t\t\tIgnore case of symbols\n"
77              "  -l\t\t\tCreate a listing if assembly was ok\n"
78              "  -o name\t\tName the output file\n"
79              "  -s\t\t\tEnable smart mode\n"
80              "  -v\t\t\tIncrease verbosity\n"
81              "  -D name[=value]\tDefine a symbol\n"
82              "  -I dir\t\tSet an include directory search path\n"
83              "  -U\t\t\tMark unresolved symbols as import\n"
84              "  -V\t\t\tPrint the assembler version\n"
85              "  -W n\t\t\tSet warning level n\n"
86              "\n"
87              "Long options:\n"
88              "  --auto-import\t\tMark unresolved symbols as import\n"
89              "  --cpu type\t\tSet cpu type\n"
90              "  --debug-info\t\tAdd debug info to object file\n"
91              "  --ignore-case\t\tIgnore case of symbols\n"
92              "  --include-dir dir\tSet an include directory search path\n"
93              "  --listing\t\tCreate a listing if assembly was ok\n"
94              "  --pagelength n\tSet the page length for the listing\n"
95              "  --smart\t\tEnable smart mode\n"
96              "  --verbose\t\tIncrease verbosity\n"
97              "  --version\t\tPrint the assembler version\n",
98              ProgName);
99     exit (EXIT_FAILURE);
100 }
101
102
103
104 static void UnknownOption (const char* Arg)
105 /* Print an error about an unknown option. Print usage information and exit */
106 {
107     fprintf (stderr, "Unknown option: %s\n", Arg);
108     Usage ();
109 }
110
111
112
113 static void NeedArg (const char* Arg)
114 /* Print an error about a missing option argument and exit. */
115 {
116     fprintf (stderr, "Option requires an argument: %s\n", Arg);
117     exit (EXIT_FAILURE);
118 }
119
120
121
122 static void InvSym (const char* Def)
123 /* Print an error about an invalid symbol definition and die */
124 {
125     fprintf (stderr, "Invalid symbol definition: `%s'\n", Def);
126     exit (EXIT_FAILURE);
127 }
128
129
130
131 static const char* GetArg (int* ArgNum, char* argv [], unsigned Len)
132 /* Get an option argument */
133 {
134     const char* Arg = argv [*ArgNum];
135     if (Arg [Len] != '\0') {
136         /* Argument appended */
137         return Arg + Len;
138     } else {
139         /* Separate argument */
140         Arg = argv [*ArgNum + 1];
141         if (Arg == 0) {
142             /* End of arguments */
143             NeedArg (argv [*ArgNum]);
144         }
145         ++(*ArgNum);
146         return Arg;
147     }
148 }
149
150
151
152 static void SetOptions (void)
153 /* Set the option for the translator */
154 {
155     char Buf [256];
156
157     /* Set the translator */
158     sprintf (Buf, "ca65 V%u.%u.%u", VER_MAJOR, VER_MINOR, VER_PATCH);
159     OptTranslator (Buf);
160
161     /* Set date and time */
162     OptDateTime ((unsigned long) time(0));
163 }
164
165
166
167 static void DefineSymbol (const char* Def)
168 /* Define a symbol from the command line */
169 {
170     const char* P;
171     unsigned I;
172     long Val;
173     char SymName [MAX_STR_LEN+1];
174
175     /* The symbol must start with a character or underline */
176     if (Def [0] != '_' && !isalpha (Def [0])) {
177         InvSym (Def);
178     }
179     P = Def;
180
181     /* Copy the symbol, checking the rest */
182     I = 0;
183     while (isalnum (*P) || *P == '_') {
184         if (I <= MAX_STR_LEN) {
185             SymName [I++] = *P;
186         }
187         ++P;
188     }
189     SymName [I] = '\0';
190
191     /* Do we have a value given? */
192     if (*P != '=') {
193         if (*P != '\0') {
194             InvSym (Def);
195         }
196         Val = 0;
197     } else {
198         /* We have a value */
199         ++P;
200         if (*P == '$') {
201             ++P;
202             if (sscanf (P, "%lx", &Val) != 1) {
203                 InvSym (Def);
204             }
205         } else {
206             if (sscanf (P, "%li", &Val) != 1) {
207                 InvSym (Def);
208             }
209         }
210     }
211
212     /* Check if have already a symbol with this name */
213     if (SymIsDef (SymName)) {
214         fprintf (stderr, "`%s' is already defined\n", SymName);
215         exit (EXIT_FAILURE);
216     }
217
218     /* Define the symbol */
219     SymDef (SymName, LiteralExpr (Val), 0);
220 }
221
222
223
224 static void OptAutoImport (const char* Opt)
225 /* Mark unresolved symbols as imported */
226 {
227     AutoImport = 1;
228 }
229
230
231
232 static void OptCPU (const char* Opt, const char* Arg)
233 /* Handle the --cpu option */
234 {
235     if (Arg == 0) {
236         NeedArg (Opt);
237     }
238     if (strcmp (Arg, "6502") == 0) {
239         SetCPU (CPU_6502);
240     } else if (strcmp (Arg, "65C02") == 0) {
241         SetCPU (CPU_65C02);
242     } else if (strcmp (Arg, "65816") == 0) {
243         SetCPU (CPU_65816);
244 #ifdef SUNPLUS
245     } else if (strcmp (Arg, "sunplus") == 0) {
246         SetCPU (CPU_SUNPLUS);
247 #endif
248     } else {
249         fprintf (stderr, "Invalid CPU: `%s'\n", Arg);
250         exit (EXIT_FAILURE);
251     }
252 }
253
254
255
256 static void OptDebugInfo (const char* Opt)
257 /* Add debug info to the object file */
258 {
259     DbgSyms = 1;
260 }
261
262
263
264 static void OptIgnoreCase (const char* Opt)
265 /* Ignore case on symbols */
266 {
267     IgnoreCase = 1;
268 }
269
270
271
272 static void OptIncludeDir (const char* Opt, const char* Arg)
273 /* Add an include search path */
274 {
275     if (Arg == 0) {
276         NeedArg (Opt);
277     }
278     AddIncludePath (Arg);
279 }
280
281
282
283 static void OptListing (const char* Opt)
284 /* Create a listing file */
285 {
286     Listing = 1;
287 }
288
289
290
291 static void OptPageLength (const char* Opt, const char* Arg)
292 /* Handle the --pagelength option */
293 {
294     int Len;
295     if (Arg == 0) {
296         NeedArg (Opt);
297     }
298     Len = atoi (Arg);
299     if (Len != -1 && (Len < MIN_PAGE_LEN || Len > MAX_PAGE_LEN)) {
300         fprintf (stderr, "Invalid page length: %d\n", Len);
301         exit (EXIT_FAILURE);
302     }
303     PageLength = Len;
304 }
305
306
307
308 static void OptSmart (const char* Opt)
309 /* Handle the -s/--smart options */
310 {
311     SmartMode = 1;
312 }
313
314
315
316 static void OptVerbose (const char* Opt)
317 /* Increase verbosity */
318 {
319     ++Verbose;
320 }
321
322
323
324 static void OptVersion (const char* Opt)
325 /* Print the assembler version */
326 {
327     fprintf (stderr,
328              "ca65 V%u.%u.%u - (C) Copyright 1998-2000 Ullrich von Bassewitz\n",
329              VER_MAJOR, VER_MINOR, VER_PATCH);
330 }
331
332
333
334 static void LongOption (int* ArgNum, char* argv [])
335 /* Handle a long command line option */
336 {
337     const char* Opt = argv [*ArgNum];
338     const char* Arg = argv [*ArgNum+1];
339
340     if (strcmp (Opt, "--auto-import") == 0) {
341         OptAutoImport (Opt);
342     } else if (strcmp (Opt, "--cpu") == 0) {
343         OptCPU (Opt, Arg);
344         ++(*ArgNum);
345     } else if (strcmp (Opt, "--debug-info") == 0) {
346         OptIgnoreCase (Opt);
347     } else if (strcmp (Opt, "--ignore-case") == 0) {
348         OptIgnoreCase (Opt);
349     } else if (strcmp (Opt, "--include-dir") == 0) {
350         OptIncludeDir (Opt, Arg);
351         ++(*ArgNum);
352     } else if (strcmp (Opt, "--listing") == 0) {
353         OptListing (Opt);
354     } else if (strcmp (Opt, "--pagelength") == 0) {
355         OptPageLength (Opt, Arg);
356         ++(*ArgNum);
357     } else if (strcmp (Opt, "--smart") == 0) {
358         OptSmart (Opt);
359     } else if (strcmp (Opt, "--verbose") == 0) {
360         OptVerbose (Opt);
361     } else if (strcmp (Opt, "--version") == 0) {
362         OptVersion (Opt);
363     } else {
364         UnknownOption (Opt);
365     }
366 }
367
368
369
370 static void OneLine (void)
371 /* Assemble one line */
372 {
373     char Ident [MAX_STR_LEN+1];
374     int Done = 0;
375
376     /* Initialize the listing line */
377     InitListingLine ();
378
379     if (Tok == TOK_COLON) {
380         /* An unnamed label */
381         ULabDef ();
382         NextTok ();
383     }
384
385     /* Assemble the line */
386     if (Tok == TOK_IDENT) {
387
388         /* Is it a macro? */
389         if (IsMacro (SVal)) {
390
391             /* Yes, start a macro expansion */
392             MacExpandStart ();
393             Done = 1;
394
395         } else {
396
397             /* No, label. Remember the identifier, then skip it */
398             int HadWS = WS;     /* Did we have whitespace before the ident? */
399             strcpy (Ident, SVal);
400             NextTok ();
401
402             /* If a colon follows, this is a label definition. If there
403              * is no colon, it's an assignment.
404              */
405             if (Tok == TOK_EQ) {
406                 /* Skip the '=' */
407                 NextTok ();
408                 /* Define the symbol with the expression following the
409                  * '='
410                  */
411                 SymDef (Ident, Expression (), 0);
412                 /* Don't allow anything after a symbol definition */
413                 Done = 1;
414             } else {
415                 /* Define a label */
416                 SymDef (Ident, CurrentPC (), IsZPSeg ());
417                 /* Skip the colon. If NoColonLabels is enabled, allow labels
418                  * without a colon if there is no whitespace before the
419                  * identifier.
420                  */
421                 if (Tok != TOK_COLON) {
422                     if (HadWS || !NoColonLabels) {
423                         Error (ERR_COLON_EXPECTED);
424                     }
425                     if (Tok == TOK_NAMESPACE) {
426                         /* Smart :: handling */
427                         NextTok ();
428                     }
429                 } else {
430                     /* Skip the colon */
431                     NextTok ();
432                 }
433             }
434         }
435     }
436
437     if (!Done) {
438
439         if (TokIsPseudo (Tok)) {
440             /* A control command, IVal is index into table */
441             HandlePseudo ();
442         } else if (Tok == TOK_MNEMO) {
443             /* A mnemonic - assemble one instruction */
444             HandleInstruction (IVal);
445         } else if (Tok == TOK_IDENT && IsMacro (SVal)) {
446             /* A macro expansion */
447             MacExpandStart ();
448         }
449     }
450
451     /* Calling InitListingLine again here is part of a hack that introduces
452      * enough magic to make the PC output in the listing work.
453      */
454     InitListingLine ();
455
456     /* Line separator must come here */
457     ConsumeSep ();
458 }
459
460
461
462 static void Assemble (void)
463 /* Start the ball rolling ... */
464 {
465     /* Prime the pump */
466     NextTok ();
467
468     /* Assemble lines until end of file */
469     while (Tok != TOK_EOF) {
470         OneLine ();
471     }
472 }
473
474
475
476 static void CreateObjFile (void)
477 /* Create the object file */
478 {
479     /* Open the object, write the header */
480     ObjOpen ();
481
482     /* Write the object file options */
483     WriteOptions ();
484
485     /* Write the list of input files */
486     WriteFiles ();
487
488     /* Write the segment data to the file */
489     WriteSegments ();
490
491     /* Write the import list */
492     WriteImports ();
493
494     /* Write the export list */
495     WriteExports ();
496
497     /* Write debug symbols if requested */
498     WriteDbgSyms ();
499
500     /* Write an updated header and close the file */
501     ObjClose ();
502 }
503
504
505
506 int main (int argc, char* argv [])
507 /* Assembler main program */
508 {
509     int I;
510
511     /* Set the program name */
512     ProgName = argv [0];
513
514     /* We must have a file name */
515     if (argc < 2) {
516         Usage ();
517     }
518
519     /* Enter the base lexical level. We must do that here, since we may
520      * define symbols using -D.
521      */
522     SymEnterLevel ();
523
524     /* Check the parameters */
525     I = 1;
526     while (I < argc) {
527
528         /* Get the argument */
529         const char* Arg = argv [I];
530
531         /* Check for an option */
532         if (Arg [0] == '-') {
533             switch (Arg [1]) {
534
535                 case '-':
536                     LongOption (&I, argv);
537                     break;
538
539                 case 'g':
540                     OptDebugInfo (Arg);
541                     break;
542
543                 case 'i':
544                     OptIgnoreCase (Arg);
545                     break;
546
547                 case 'l':
548                     OptListing (Arg);
549                     break;
550
551                 case 'o':
552                     OutFile = GetArg (&I, argv, 2);
553                     break;
554
555                 case 's':
556                     OptSmart (Arg);
557                     break;
558
559                 case 'v':
560                     OptVerbose (Arg);
561                     break;
562
563                 case 'D':
564                     DefineSymbol (GetArg (&I, argv, 2));
565                     break;
566
567                 case 'I':
568                     OptIncludeDir (Arg, GetArg (&I, argv, 2));
569                     break;
570
571                 case 'U':
572                     OptAutoImport (Arg);
573                     break;
574
575                 case 'V':
576                     OptVersion (Arg);
577                     break;
578
579                 case 'W':
580                     WarnLevel = atoi (GetArg (&I, argv, 2));
581                     break;
582
583                 default:
584                     UnknownOption (Arg);
585                     break;
586
587             }
588         } else {
589             /* Filename. Check if we already had one */
590             if (InFile) {
591                 fprintf (stderr, "Don't know what to do with `%s'\n", Arg);
592                 Usage ();
593             } else {
594                 InFile = Arg;
595             }
596         }
597
598         /* Next argument */
599         ++I;
600     }
601
602     /* Do we have an input file? */
603     if (InFile == 0) {
604         fprintf (stderr, "No input file\n");
605         exit (EXIT_FAILURE);
606     }
607
608     /* Initialize the scanner, open the input file */
609     InitScanner (InFile);
610
611     /* Define the default options */
612     SetOptions ();
613
614     /* Assemble the input */
615     Assemble ();
616
617     /* If we didn't have any errors, check the unnamed labels */
618     if (ErrorCount == 0) {
619         ULabCheck ();
620     }
621
622     /* If we didn't have any errors, check the symbol table */
623     if (ErrorCount == 0) {
624         SymCheck ();
625     }
626
627     /* If we didn't have any errors, check and resolve the segment data */
628     if (ErrorCount == 0) {
629         SegCheck ();
630     }
631
632     /* Dump the data */
633     if (Verbose >= 2) {
634         SymDump (stdout);
635         SegDump ();
636     }
637
638     /* If we didn't have any errors, create the object and listing files */
639     if (ErrorCount == 0) {
640         CreateObjFile ();
641         if (Listing) {
642             CreateListing ();
643         }
644     }
645
646     /* Close the input file */
647     DoneScanner ();
648
649     /* Return an apropriate exit code */
650     return (ErrorCount == 0)? EXIT_SUCCESS : EXIT_FAILURE;
651 }
652
653
654