]> git.sur5r.net Git - cc65/blobdiff - src/ca65/main.c
Added line infos
[cc65] / src / ca65 / main.c
index ec5dac13d91d05781a6c09f1c9c08acc663ac400..b029e2f0cc64b67255ec5102819d2143cf6c9f64 100644 (file)
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <ctype.h>
 #include <time.h>
 
-#include "../common/version.h"
+/* common */
+#include "chartype.h"
+#include "cmdline.h"
+#include "print.h"
+#include "target.h"
+#include "tgttrans.h"
+#include "version.h"
 
+/* ca65 */
+#include "abend.h"
 #include "error.h"
 #include "expr.h"
+#include "feature.h"
+#include "filetab.h"
 #include "global.h"
 #include "incpath.h"
 #include "instr.h"
+#include "istack.h"
+#include "lineinfo.h"
 #include "listing.h"
 #include "macro.h"
-#include "mem.h"
+#include "nexttok.h"
 #include "objcode.h"
 #include "objfile.h"
 #include "options.h"
@@ -72,10 +83,12 @@ static void Usage (void)
             "Usage: %s [options] file\n"
             "Short options:\n"
                     "  -g\t\t\tAdd debug info to object file\n"
+                    "  -h\t\t\tHelp (this text)\n"
                     "  -i\t\t\tIgnore case of symbols\n"
                     "  -l\t\t\tCreate a listing if assembly was ok\n"
                     "  -o name\t\tName the output file\n"
                     "  -s\t\t\tEnable smart mode\n"
+                    "  -t sys\t\tSet the target system\n"
                     "  -v\t\t\tIncrease verbosity\n"
                     "  -D name[=value]\tDefine a symbol\n"
                     "  -I dir\t\tSet an include directory search path\n"
@@ -87,63 +100,17 @@ static void Usage (void)
                     "  --auto-import\t\tMark unresolved symbols as import\n"
                     "  --cpu type\t\tSet cpu type\n"
                     "  --debug-info\t\tAdd debug info to object file\n"
+            "  --feature name\tSet an emulation feature\n"
+            "  --help\t\tHelp (this text)\n"
             "  --ignore-case\t\tIgnore case of symbols\n"
                     "  --include-dir dir\tSet an include directory search path\n"
                     "  --listing\t\tCreate a listing if assembly was ok\n"
                     "  --pagelength n\tSet the page length for the listing\n"
                     "  --smart\t\tEnable smart mode\n"
+                    "  --target sys\t\tSet the target system\n"
                     "  --verbose\t\tIncrease verbosity\n"
                     "  --version\t\tPrint the assembler version\n",
             ProgName);
-    exit (EXIT_FAILURE);
-}
-
-
-
-static void UnknownOption (const char* Arg)
-/* Print an error about an unknown option. Print usage information and exit */
-{
-    fprintf (stderr, "Unknown option: %s\n", Arg);
-    Usage ();
-}
-
-
-
-static void NeedArg (const char* Arg)
-/* Print an error about a missing option argument and exit. */
-{
-    fprintf (stderr, "Option requires an argument: %s\n", Arg);
-    exit (EXIT_FAILURE);
-}
-
-
-
-static void InvSym (const char* Def)
-/* Print an error about an invalid symbol definition and die */
-{
-    fprintf (stderr, "Invalid symbol definition: `%s'\n", Def);
-    exit (EXIT_FAILURE);
-}
-
-
-
-static const char* GetArg (int* ArgNum, char* argv [], unsigned Len)
-/* Get an option argument */
-{
-    const char* Arg = argv [*ArgNum];
-    if (Arg [Len] != '\0') {
-       /* Argument appended */
-       return Arg + Len;
-    } else {
-       /* Separate argument */
-       Arg = argv [*ArgNum + 1];
-       if (Arg == 0) {
-           /* End of arguments */
-           NeedArg (argv [*ArgNum]);
-       }
-       ++(*ArgNum);
-       return Arg;
-    }
 }
 
 
@@ -172,14 +139,14 @@ static void DefineSymbol (const char* Def)
     char SymName [MAX_STR_LEN+1];
 
     /* The symbol must start with a character or underline */
-    if (Def [0] != '_' && !isalpha (Def [0])) {
-       InvSym (Def);
+    if (Def [0] != '_' && !IsAlpha (Def [0])) {
+       InvDef (Def);
     }
     P = Def;
 
     /* Copy the symbol, checking the rest */
     I = 0;
-    while (isalnum (*P) || *P == '_') {
+    while (IsAlNum (*P) || *P == '_') {
        if (I <= MAX_STR_LEN) {
            SymName [I++] = *P;
        }
@@ -190,7 +157,7 @@ static void DefineSymbol (const char* Def)
     /* Do we have a value given? */
     if (*P != '=') {
        if (*P != '\0') {
-           InvSym (Def);
+           InvDef (Def);
        }
        Val = 0;
     } else {
@@ -199,19 +166,18 @@ static void DefineSymbol (const char* Def)
        if (*P == '$') {
            ++P;
            if (sscanf (P, "%lx", &Val) != 1) {
-               InvSym (Def);
+               InvDef (Def);
            }
        } else {
            if (sscanf (P, "%li", &Val) != 1) {
-               InvSym (Def);
+               InvDef (Def);
            }
                }
     }
 
     /* Check if have already a symbol with this name */
     if (SymIsDef (SymName)) {
-       fprintf (stderr, "`%s' is already defined\n", SymName);
-       exit (EXIT_FAILURE);
+       AbEnd ("`%s' is already defined", SymName);
     }
 
     /* Define the symbol */
@@ -220,7 +186,7 @@ static void DefineSymbol (const char* Def)
 
 
 
-static void OptAutoImport (const char* Opt)
+static void OptAutoImport (const char* Opt, const char* Arg)
 /* Mark unresolved symbols as imported */
 {
     AutoImport = 1;
@@ -245,14 +211,13 @@ static void OptCPU (const char* Opt, const char* Arg)
        SetCPU (CPU_SUNPLUS);
 #endif
     } else {
-       fprintf (stderr, "Invalid CPU: `%s'\n", Arg);
-       exit (EXIT_FAILURE);
+       AbEnd ("Invalid CPU: `%s'", Arg);
     }
 }
 
 
 
-static void OptDebugInfo (const char* Opt)
+static void OptDebugInfo (const char* Opt, const char* Arg)
 /* Add debug info to the object file */
 {
     DbgSyms = 1;
@@ -260,7 +225,27 @@ static void OptDebugInfo (const char* Opt)
 
 
 
-static void OptIgnoreCase (const char* Opt)
+static void OptFeature (const char* Opt, const char* Arg)
+/* Set an emulation feature */
+{
+    /* Set the feature, check for errors */
+    if (SetFeature (Arg) == FEAT_UNKNOWN) {
+       AbEnd ("Illegal emulation feature: `%s'", Arg);
+    }
+}
+
+
+
+static void OptHelp (const char* Opt, const char* Arg)
+/* Print usage information and exit */
+{
+    Usage ();
+    exit (EXIT_SUCCESS);
+}
+
+
+
+static void OptIgnoreCase (const char* Opt, const char* Arg)
 /* Ignore case on symbols */
 {
     IgnoreCase = 1;
@@ -279,7 +264,7 @@ static void OptIncludeDir (const char* Opt, const char* Arg)
 
 
 
-static void OptListing (const char* Opt)
+static void OptListing (const char* Opt, const char* Arg)
 /* Create a listing file */
 {
     Listing = 1;
@@ -296,15 +281,14 @@ static void OptPageLength (const char* Opt, const char* Arg)
     }
     Len = atoi (Arg);
     if (Len != -1 && (Len < MIN_PAGE_LEN || Len > MAX_PAGE_LEN)) {
-       fprintf (stderr, "Invalid page length: %d\n", Len);
-       exit (EXIT_FAILURE);
+       AbEnd ("Invalid page length: %d", Len);
     }
     PageLength = Len;
 }
 
 
 
-static void OptSmart (const char* Opt)
+static void OptSmart (const char* Opt, const char* Arg)
 /* Handle the -s/--smart options */
 {
     SmartMode = 1;
@@ -312,15 +296,31 @@ static void OptSmart (const char* Opt)
 
 
 
-static void OptVerbose (const char* Opt)
+static void OptTarget (const char* Opt, const char* Arg)
+/* Set the target system */
+{
+    if (Arg == 0) {
+       NeedArg (Opt);
+    }
+
+    /* Map the target name to a target id */
+    Target = FindTarget (Arg);
+    if (Target == TGT_UNKNOWN) {
+       AbEnd ("Invalid target name: `%s'", Arg);
+    }
+}
+
+
+
+static void OptVerbose (const char* Opt, const char* Arg)
 /* Increase verbosity */
 {
-    ++Verbose;
+    ++Verbosity;
 }
 
 
 
-static void OptVersion (const char* Opt)
+static void OptVersion (const char* Opt, const char* Arg)
 /* Print the assembler version */
 {
     fprintf (stderr,
@@ -330,37 +330,14 @@ static void OptVersion (const char* Opt)
 
 
 
-static void LongOption (int* ArgNum, char* argv [])
-/* Handle a long command line option */
+static void DoPCAssign (void)
+/* Start absolute code */
 {
-    const char* Opt = argv [*ArgNum];
-    const char* Arg = argv [*ArgNum+1];
-
-    if (strcmp (Opt, "--auto-import") == 0) {
-               OptAutoImport (Opt);
-    } else if (strcmp (Opt, "--cpu") == 0) {
-       OptCPU (Opt, Arg);
-       ++(*ArgNum);
-    } else if (strcmp (Opt, "--debug-info") == 0) {
-       OptIgnoreCase (Opt);
-    } else if (strcmp (Opt, "--ignore-case") == 0) {
-       OptIgnoreCase (Opt);
-    } else if (strcmp (Opt, "--include-dir") == 0) {
-       OptIncludeDir (Opt, Arg);
-       ++(*ArgNum);
-    } else if (strcmp (Opt, "--listing") == 0) {
-       OptListing (Opt);
-    } else if (strcmp (Opt, "--pagelength") == 0) {
-       OptPageLength (Opt, Arg);
-       ++(*ArgNum);
-    } else if (strcmp (Opt, "--smart") == 0) {
-               OptSmart (Opt);
-    } else if (strcmp (Opt, "--verbose") == 0) {
-               OptVerbose (Opt);
-    } else if (strcmp (Opt, "--version") == 0) {
-               OptVersion (Opt);
+    long PC = ConstExpression ();
+    if (PC < 0 || PC > 0xFFFFFF) {
+       Error (ERR_RANGE);
     } else {
-        UnknownOption (Opt);
+       SetAbsPC (PC);
     }
 }
 
@@ -372,86 +349,94 @@ static void OneLine (void)
     char Ident [MAX_STR_LEN+1];
     int Done = 0;
 
-    /* Initialize the listing line */
-    InitListingLine ();
+    /* Initialize the new listing line if we are actually reading from file
+     * and not from internally pushed input.
+     */
+    if (!HavePushedInput ()) {
+       InitListingLine ();
+    }
 
     if (Tok == TOK_COLON) {
-       /* An unnamed label */
-       ULabDef ();
-       NextTok ();
+       /* An unnamed label */
+       ULabDef ();
+       NextTok ();
     }
 
     /* Assemble the line */
     if (Tok == TOK_IDENT) {
 
-       /* Is it a macro? */
-       if (IsMacro (SVal)) {
+       /* Is it a macro? */
+       if (IsMacro (SVal)) {
 
-           /* Yes, start a macro expansion */
-           MacExpandStart ();
-           Done = 1;
+           /* Yes, start a macro expansion */
+           MacExpandStart ();
+           Done = 1;
 
-       } else {
+       } else {
 
-           /* No, label. Remember the identifier, then skip it */
-           int HadWS = WS;     /* Did we have whitespace before the ident? */
-           strcpy (Ident, SVal);
-           NextTok ();
+           /* No, label. Remember the identifier, then skip it */
+           int HadWS = WS;     /* Did we have whitespace before the ident? */
+           strcpy (Ident, SVal);
+           NextTok ();
 
-           /* If a colon follows, this is a label definition. If there
-            * is no colon, it's an assignment.
-            */
+           /* If a colon follows, this is a label definition. If there
+            * is no colon, it's an assignment.
+            */
                    if (Tok == TOK_EQ) {
-               /* Skip the '=' */
-               NextTok ();
-               /* Define the symbol with the expression following the
-                * '='
-                */
-               SymDef (Ident, Expression (), 0);
-               /* Don't allow anything after a symbol definition */
-               Done = 1;
-           } else {
-               /* Define a label */
-               SymDef (Ident, CurrentPC (), IsZPSeg ());
-               /* Skip the colon. If NoColonLabels is enabled, allow labels
-                * without a colon if there is no whitespace before the
-                * identifier.
-                */
-               if (Tok != TOK_COLON) {
-                   if (HadWS || !NoColonLabels) {
-                       Error (ERR_COLON_EXPECTED);
-                   }
-                   if (Tok == TOK_NAMESPACE) {
-                       /* Smart :: handling */
-                       NextTok ();
-                   }
-               } else {
-                   /* Skip the colon */
-                   NextTok ();
-               }
-           }
-       }
+               /* Skip the '=' */
+               NextTok ();
+               /* Define the symbol with the expression following the '=' */
+               SymDef (Ident, Expression (), 0);
+               /* Don't allow anything after a symbol definition */
+               Done = 1;
+           } else {
+               /* Define a label */
+               SymDef (Ident, CurrentPC (), IsZPSeg ());
+               /* Skip the colon. If NoColonLabels is enabled, allow labels
+                * without a colon if there is no whitespace before the
+                * identifier.
+                */
+               if (Tok != TOK_COLON) {
+                   if (HadWS || !NoColonLabels) {
+                       Error (ERR_COLON_EXPECTED);
+                   }
+                   if (Tok == TOK_NAMESPACE) {
+                       /* Smart :: handling */
+                       NextTok ();
+                   }
+               } else {
+                   /* Skip the colon */
+                   NextTok ();
+               }
+           }
+       }
     }
 
     if (!Done) {
 
-       if (TokIsPseudo (Tok)) {
-           /* A control command, IVal is index into table */
-           HandlePseudo ();
-       } else if (Tok == TOK_MNEMO) {
-           /* A mnemonic - assemble one instruction */
-           HandleInstruction (IVal);
-       } else if (Tok == TOK_IDENT && IsMacro (SVal)) {
-           /* A macro expansion */
-           MacExpandStart ();
+       if (TokIsPseudo (Tok)) {
+           /* A control command, IVal is index into table */
+           HandlePseudo ();
+       } else if (Tok == TOK_MNEMO) {
+           /* A mnemonic - assemble one instruction */
+           HandleInstruction (IVal);
+       } else if (Tok == TOK_IDENT && IsMacro (SVal)) {
+           /* A macro expansion */
+           MacExpandStart ();
+       } else if (PCAssignment && (Tok == TOK_STAR || Tok == TOK_PC)) {
+           NextTok ();
+           if (Tok != TOK_EQ) {
+               Error (ERR_EQ_EXPECTED);
+               SkipUntilSep ();
+           } else {
+               /* Skip the equal sign */
+               NextTok ();
+               /* Enter absolute mode */
+               DoPCAssign ();
+           }
        }
     }
 
-    /* Calling InitListingLine again here is part of a hack that introduces
-     * enough magic to make the PC output in the listing work.
-     */
-    InitListingLine ();
-
     /* Line separator must come here */
     ConsumeSep ();
 }
@@ -496,6 +481,9 @@ static void CreateObjFile (void)
     /* Write debug symbols if requested */
     WriteDbgSyms ();
 
+    /* Write line infos if requested */
+    WriteLineInfo ();
+
     /* Write an updated header and close the file */
     ObjClose ();
 }
@@ -505,15 +493,27 @@ static void CreateObjFile (void)
 int main (int argc, char* argv [])
 /* Assembler main program */
 {
-    int I;
+    /* Program long options */
+    static const LongOpt OptTab[] = {
+        { "--auto-import",             0,      OptAutoImport           },
+        { "--cpu",                     1,      OptCPU                  },
+       { "--debug-info",       0,      OptDebugInfo            },
+       { "--feature",          1,      OptFeature              },
+       { "--help",             0,      OptHelp                 },
+       { "--ignore-case",      0,      OptIgnoreCase           },
+       { "--include-dir",      1,      OptIncludeDir           },
+       { "--listing",          0,      OptListing              },
+       { "--pagelength",       1,      OptPageLength           },
+       { "--smart",            0,      OptSmart                },
+       { "--target",           1,      OptTarget               },
+       { "--verbose",          0,      OptVerbose              },
+       { "--version",          0,      OptVersion              },
+    };
 
-    /* Set the program name */
-    ProgName = argv [0];
+    int I;
 
-    /* We must have a file name */
-    if (argc < 2) {
-       Usage ();
-    }
+    /* Initialize the cmdline module */
+    InitCmdLine (&argc, &argv, "ca65");
 
     /* Enter the base lexical level. We must do that here, since we may
      * define symbols using -D.
@@ -522,61 +522,69 @@ int main (int argc, char* argv [])
 
     /* Check the parameters */
     I = 1;
-    while (I < argc) {
+    while (I < ArgCount) {
 
                /* Get the argument */
-               const char* Arg = argv [I];
+               const char* Arg = ArgVec [I];
 
                /* Check for an option */
                if (Arg [0] == '-') {
                    switch (Arg [1]) {
 
                case '-':
-                   LongOption (&I, argv);
+                   LongOption (&I, OptTab, sizeof(OptTab)/sizeof(OptTab[0]));
                    break;
 
-                       case 'g':
-                           OptDebugInfo (Arg);
+                       case 'g':
+                           OptDebugInfo (Arg, 0);
                            break;
 
+               case 'h':
+                   OptHelp (Arg, 0);
+                   break;
+
                        case 'i':
-                           OptIgnoreCase (Arg);
+                           OptIgnoreCase (Arg, 0);
                            break;
 
-                       case 'l':     
-                   OptListing (Arg);
+                       case 'l':
+                   OptListing (Arg, 0);
                            break;
 
                        case 'o':
-                           OutFile = GetArg (&I, argv, 2);
+                           OutFile = GetArg (&I, 2);
                            break;
 
                        case 's':
-                           OptSmart (Arg);
+                           OptSmart (Arg, 0);
                            break;
 
+               case 't':
+                   OptTarget (Arg, GetArg (&I, 2));
+                   break;
+
                        case 'v':
-                   OptVerbose (Arg);
+                   OptVerbose (Arg, 0);
                            break;
 
                case 'D':
-                   DefineSymbol (GetArg (&I, argv, 2));
+                   DefineSymbol (GetArg (&I, 2));
                    break;
 
                case 'I':
-                   OptIncludeDir (Arg, GetArg (&I, argv, 2));
+                   OptIncludeDir (Arg, GetArg (&I, 2));
                    break;
 
                        case 'U':
-                   OptAutoImport (Arg);
+                   OptAutoImport (Arg, 0);
                            break;
 
                        case 'V':
-                   OptVersion (Arg);
+                   OptVersion (Arg, 0);
                            break;
 
                        case 'W':
-                           WarnLevel = atoi (GetArg (&I, argv, 2));
+                           WarnLevel = atoi (GetArg (&I, 2));
                            break;
 
                        default:
@@ -587,10 +595,11 @@ int main (int argc, char* argv [])
                } else {
            /* Filename. Check if we already had one */
            if (InFile) {
-               fprintf (stderr, "Don't know what to do with `%s'\n", Arg);
-               Usage ();
+               fprintf (stderr, "%s: Don't know what to do with `%s'\n",
+                        ProgName, Arg);
+               exit (EXIT_FAILURE);
            } else {
-               InFile = Arg;
+               InFile = Arg;
            }
        }
 
@@ -600,10 +609,13 @@ int main (int argc, char* argv [])
 
     /* Do we have an input file? */
     if (InFile == 0) {
-       fprintf (stderr, "No input file\n");
+       fprintf (stderr, "%s: No input files\n", ProgName);
        exit (EXIT_FAILURE);
     }
 
+    /* Intialize the target translation tables */
+    TgtTranslateInit ();
+
     /* Initialize the scanner, open the input file */
     InitScanner (InFile);
 
@@ -628,8 +640,11 @@ int main (int argc, char* argv [])
         SegCheck ();
     }
 
+    /* If we didn't have an errors, index the line infos */
+    MakeLineInfoIndex ();
+
     /* Dump the data */
-    if (Verbose >= 2) {
+    if (Verbosity >= 2) {
         SymDump (stdout);
         SegDump ();
     }