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