diff -ru cc65-2.17.orig/doc/da65.sgml cc65-2.17/doc/da65.sgml
--- cc65-2.17.orig/doc/da65.sgml	2018-03-07 22:05:21.000000000 +0000
+++ cc65-2.17/doc/da65.sgml	2018-06-03 03:50:29.093272400 +0000
@@ -53,6 +53,7 @@
   -o name               Name the output file
   -v                    Increase verbosity
   -F                    Add formfeeds to the output
+  -s                    Accept line markers in the info file
   -S addr               Set the start/load address
   -V                    Print the disassembler version
 
@@ -70,6 +71,7 @@
   --mnemonic-column n   Specify mnemonic start column
   --pagelength n        Set the page length for the listing
   --start-addr addr     Set the start/load address
+  --sync-lines          Accept line markers in the info file
   --text-column n       Specify text start column
   --verbose             Increase verbosity
   --version             Print the disassembler version
@@ -205,6 +207,17 @@
   start address is specified, $10000 minus the size of the input file is used.
 
 
+  <label id="option--sync-lines">
+  <tag><tt>-s, --sync-lines</tt></tag>
+
+  Accept line markers in the info file in the following syntax:
+<tscreen><verb>
+#line <lineno> ["<filename>"]
+# <lineno> "<filename>" [<flag>] ...
+</verb></tscreen>
+  This option is intended for preprocessing info files with "cpp" or "m4".
+
+
   <label id="option--text-column">
   <tag><tt>--text-column n</tt></tag>
 
@@ -299,9 +312,10 @@
 
 <sect1>Comments<p>
 
-Comments start with a hash mark (<tt/#/); and, extend from the position of
-the mark to the end of the current line. Hash marks inside of strings will
-<em/not/ start a comment, of course.
+Comments start with a hash mark (<tt/#/) or a double slashe (<tt>//</tt>);
+and, extend from the position of the mark to the end of the current line.
+Hash marks or double slashes inside of strings will <em/not/ start a comment,
+of course.
 
 
 <sect1>Specifying global options<label id="global-options"><p>
@@ -542,6 +556,18 @@
   range, where <tt/label/ is the label name given with the <tt/NAME/
   attribute, and <tt/offs/ is the offset within the data.
 
+  <tag><tt>VOPERAND</tt></tag>
+  This optional attribute is followed by a numerical value. It tells the
+  assembler that subroutine calls to this label follow "virtual operands"
+  of the given bytes like this:
+
+<tscreen><verb>
+       	JSR	LabelWith2BytesOfVoperand
+	.byte	$00, $10    ; virtual operands
+	; return here
+	BIT	$0F
+</verb></tscreen>
+
 </descrip>
 
 
diff -ru cc65-2.17.orig/src/da65/handler.c cc65-2.17/src/da65/handler.c
--- cc65-2.17.orig/src/da65/handler.c	2018-03-07 22:05:21.000000000 +0000
+++ cc65-2.17/src/da65/handler.c	2018-06-03 03:26:25.624420500 +0000
@@ -51,6 +51,8 @@
 
 
 
+static unsigned short SubroutineVOperandSize[0x10000];
+
 /*****************************************************************************/
 /*                             Helper functions                              */
 /*****************************************************************************/
@@ -741,3 +743,39 @@
     }
     SeparatorLine ();
 }
+
+
+
+void OH_JmpAbsoluteIndirect (const OpcDesc* D)
+{
+    OH_AbsoluteIndirect (D);
+    SeparatorLine ();
+}
+
+
+
+void OH_JsrAbsolute (const OpcDesc* D)
+{
+    unsigned VOperandSize = SubroutineVOperandSize[GetCodeWord(PC+1)];
+    OH_Absolute (D);
+    if (VOperandSize > 0) {
+        unsigned RemainingBytes;
+        PC += D->Size;
+        RemainingBytes = GetRemainingBytes();
+        if (RemainingBytes < VOperandSize) {
+            VOperandSize = RemainingBytes;
+        }
+        if (VOperandSize > 0) {
+            DataByteLine (VOperandSize); /* FIXME: follow BytesPerLine */
+            PC += VOperandSize;
+        }
+        PC -= D->Size;
+    }
+}
+
+
+
+void SetSubroutineVOperand (unsigned Addr, unsigned Size)
+{
+    SubroutineVOperandSize[Addr] = Size;
+}
diff -ru cc65-2.17.orig/src/da65/handler.h cc65-2.17/src/da65/handler.h
--- cc65-2.17.orig/src/da65/handler.h	2018-03-07 22:05:21.000000000 +0000
+++ cc65-2.17/src/da65/handler.h	2018-06-03 03:26:50.176394300 +0000
@@ -104,7 +104,9 @@
 void OH_JmpAbsolute (const OpcDesc*);
 void OH_JmpAbsoluteIndirect (const OpcDesc* D);
 void OH_JmpAbsoluteXIndirect (const OpcDesc* D);
+void OH_JsrAbsolute (const OpcDesc*);
 
+void SetSubroutineVOperand (unsigned Addr, unsigned Size);
 
 
 /* End of handler.h */
diff -ru cc65-2.17.orig/src/da65/infofile.c cc65-2.17/src/da65/infofile.c
--- cc65-2.17.orig/src/da65/infofile.c	2018-03-07 22:05:21.000000000 +0000
+++ cc65-2.17/src/da65/infofile.c	2018-06-03 03:28:31.268024500 +0000
@@ -59,6 +59,7 @@
 #include "opctable.h"
 #include "scanner.h"
 #include "segment.h"
+#include "handler.h"
 
 
 
@@ -380,6 +381,7 @@
         {   "ADDR",     INFOTOK_ADDR    },
         {   "NAME",     INFOTOK_NAME    },
         {   "SIZE",     INFOTOK_SIZE    },
+       	{   "VOPERAND", INFOTOK_VOPERAND },
     };
 
     /* Locals - initialize to avoid gcc warnings */
@@ -387,6 +389,7 @@
     char* Comment = 0;
     long Value    = -1;
     long Size     = -1;
+    long VOperand = -1;
 
     /* Skip the token */
     InfoNextTok ();
@@ -448,6 +451,17 @@
                 InfoNextTok ();
                 break;
 
+            case INFOTOK_VOPERAND:
+                InfoNextTok ();
+                if (VOperand >= 0) {
+                    InfoError ("VOperand already given");
+                }
+                InfoAssureInt ();
+                InfoRangeCheck (1, 0x10000);
+                VOperand = InfoIVal;
+                InfoNextTok ();
+                break;
+
             default:
                 Internal ("Unexpected token: %u", InfoTok);
         }
@@ -484,6 +498,9 @@
     } else {
         AddExtLabelRange ((unsigned) Value, Name, Size);
     }
+    if (VOperand >= 0) {
+        SetSubroutineVOperand ((unsigned) Value, (unsigned) VOperand);
+    }
 
     /* Define the comment */
     if (Comment) {
diff -ru cc65-2.17.orig/src/da65/main.c cc65-2.17/src/da65/main.c
--- cc65-2.17.orig/src/da65/main.c	2018-03-07 22:05:21.000000000 +0000
+++ cc65-2.17/src/da65/main.c	2018-06-03 03:31:56.136174500 +0000
@@ -82,6 +82,7 @@
             "  -v\t\t\tIncrease verbosity\n"
             "  -F\t\t\tAdd formfeeds to the output\n"
             "  -S addr\t\tSet the start/load address\n"
+            "  -s\t\t\tAccept line markers in the info file\n"
             "  -V\t\t\tPrint the disassembler version\n"
             "\n"
             "Long options:\n"
@@ -98,6 +99,7 @@
             "  --mnemonic-column n\tSpecify mnemonic start column\n"
             "  --pagelength n\tSet the page length for the listing\n"
             "  --start-addr addr\tSet the start/load address\n"
+            "  --sync-lines\t\tAccept line markers in the info file\n"
             "  --text-column n\tSpecify text start column\n"
             "  --verbose\t\tIncrease verbosity\n"
             "  --version\t\tPrint the disassembler version\n",
@@ -312,6 +314,15 @@
 
 
 
+static void OptSyncLines (const char* Opt attribute ((unused)),
+                          const char* Arg attribute ((unused)))
+/* Handle the --sync-lines option */
+{
+    InfoSyncLines = 1;
+}
+
+
+
 static void OptTextColumn (const char* Opt, const char* Arg)
 /* Handle the --text-column option */
 {
@@ -539,6 +550,7 @@
         { "--mnemonic-column",  1,      OptMnemonicColumn       },
         { "--pagelength",       1,      OptPageLength           },
         { "--start-addr",       1,      OptStartAddr            },
+      	{ "--sync-lines",       0,      OptSyncLines            },
         { "--text-column",      1,      OptTextColumn           },
         { "--verbose",          0,      OptVerbose              },
         { "--version",          0,      OptVersion              },
@@ -589,6 +601,10 @@
                     OptStartAddr (Arg, GetArg (&I, 2));
                     break;
 
+                case 's':
+                    OptSyncLines (Arg, 0);
+                    break;
+
                 case 'V':
                     OptVersion (Arg, 0);
                     break;
diff -ru cc65-2.17.orig/src/da65/opc6502.c cc65-2.17/src/da65/opc6502.c
--- cc65-2.17.orig/src/da65/opc6502.c	2018-03-07 22:05:21.000000000 +0000
+++ cc65-2.17/src/da65/opc6502.c	2018-06-03 03:25:03.954532000 +0000
@@ -79,7 +79,7 @@
     {   "ora",  3,  flUseLabel|flAbsOverride, OH_AbsoluteX             }, /* $1d */
     {   "asl",  3,  flUseLabel|flAbsOverride, OH_AbsoluteX             }, /* $1e */
     {   "",     1,  flIllegal,                OH_Illegal,              }, /* $1f */
-    {   "jsr",  3,  flLabel,                  OH_Absolute              }, /* $20 */
+    {   "jsr",  3,  flLabel,                  OH_JsrAbsolute           }, /* $20 */
     {   "and",  2,  flUseLabel,               OH_DirectXIndirect       }, /* $21 */
     {   "",     1,  flIllegal,                OH_Illegal,              }, /* $22 */
     {   "",     1,  flIllegal,                OH_Illegal,              }, /* $23 */
diff -ru cc65-2.17.orig/src/da65/opc65816.c cc65-2.17/src/da65/opc65816.c
--- cc65-2.17.orig/src/da65/opc65816.c	2018-03-07 22:05:21.000000000 +0000
+++ cc65-2.17/src/da65/opc65816.c	2018-06-03 03:25:03.954532000 +0000
@@ -79,7 +79,7 @@
     {   "ora",  3,  flUseLabel|flAbsOverride, OH_AbsoluteX             }, /* $1d */
     {   "asl",  3,  flUseLabel|flAbsOverride, OH_AbsoluteX             }, /* $1e */
     {   "ora",  4,  flUseLabel,               OH_AbsoluteLongX         }, /* $1f */
-    {   "jsr",  3,  flLabel,                  OH_Absolute              }, /* $20 */
+    {   "jsr",  3,  flLabel,                  OH_JsrAbsolute           }, /* $20 */
     {   "and",  2,  flUseLabel,               OH_DirectXIndirect       }, /* $21 */
     {   "jsl",  3,  flLabel,                  OH_AbsoluteLong          }, /* $22 */
     {   "and",  2,  flNone,                   OH_StackRelative         }, /* $23 */
diff -ru cc65-2.17.orig/src/da65/opc65c02.c cc65-2.17/src/da65/opc65c02.c
--- cc65-2.17.orig/src/da65/opc65c02.c	2018-03-07 22:05:21.000000000 +0000
+++ cc65-2.17/src/da65/opc65c02.c	2018-06-03 03:25:03.970159300 +0000
@@ -79,7 +79,7 @@
     {   "ora",  3,  flUseLabel|flAbsOverride, OH_AbsoluteX             }, /* $1d */
     {   "asl",  3,  flUseLabel|flAbsOverride, OH_AbsoluteX             }, /* $1e */
     {   "bbr1", 3,  flUseLabel,               OH_BitBranch             }, /* $1f */
-    {   "jsr",  3,  flLabel,                  OH_Absolute              }, /* $20 */
+    {   "jsr",  3,  flLabel,                  OH_JsrAbsolute           }, /* $20 */
     {   "and",  2,  flUseLabel,               OH_DirectXIndirect       }, /* $21 */
     {   "",     1,  flIllegal,                OH_Illegal,              }, /* $22 */
     {   "",     1,  flIllegal,                OH_Illegal,              }, /* $23 */
diff -ru cc65-2.17.orig/src/da65/opc65sc02.c cc65-2.17/src/da65/opc65sc02.c
--- cc65-2.17.orig/src/da65/opc65sc02.c	2018-03-07 22:05:21.000000000 +0000
+++ cc65-2.17/src/da65/opc65sc02.c	2018-06-03 03:25:03.970159300 +0000
@@ -79,7 +79,7 @@
     {   "ora",  3,  flUseLabel|flAbsOverride, OH_AbsoluteX             }, /* $1d */
     {   "asl",  3,  flUseLabel|flAbsOverride, OH_AbsoluteX             }, /* $1e */
     {   "",     1,  flIllegal,                OH_Illegal,              }, /* $1f */
-    {   "jsr",  3,  flLabel,                  OH_Absolute              }, /* $20 */
+    {   "jsr",  3,  flLabel,                  OH_JsrAbsolute           }, /* $20 */
     {   "and",  2,  flUseLabel,               OH_DirectXIndirect       }, /* $21 */
     {   "",     1,  flIllegal,                OH_Illegal,              }, /* $22 */
     {   "",     1,  flIllegal,                OH_Illegal,              }, /* $23 */
diff -ru cc65-2.17.orig/src/da65/scanner.c cc65-2.17/src/da65/scanner.c
--- cc65-2.17.orig/src/da65/scanner.c	2018-03-07 22:05:21.000000000 +0000
+++ cc65-2.17/src/da65/scanner.c	2018-06-03 03:38:16.965281800 +0000
@@ -41,6 +41,8 @@
 /* common */
 #include "chartype.h"
 #include "xsprintf.h"
+#include "xmalloc.h"
+#include "strbuf.h"
 
 /* ld65 */
 #include "global.h"
@@ -72,7 +74,10 @@
 static unsigned         InputLine       = 1;
 static unsigned         InputCol        = 0;
 static FILE*            InputFile       = 0;
+static char*            InputSrcName    = 0;
 
+/* Options */
+unsigned char           InfoSyncLines   = 0;
 
 
 /*****************************************************************************/
@@ -91,7 +96,8 @@
     xvsprintf (Buf, sizeof (Buf), Format, ap);
     va_end (ap);
 
-    Warning ("%s(%u): %s", InfoFile, InfoErrorLine, Buf);
+    fprintf (stderr, "%s(%u): Warning: %s\n",
+            InputSrcName, InfoErrorLine, Buf);
 }
 
 
@@ -106,7 +112,9 @@
     xvsprintf (Buf, sizeof (Buf), Format, ap);
     va_end (ap);
 
-    Error ("%s(%u): %s", InfoFile, InfoErrorLine, Buf);
+    fprintf (stderr, "%s(%u): Error: %s\n",
+            InputSrcName, InfoErrorLine, Buf);
+    exit (EXIT_FAILURE);
 }
 
 
@@ -149,17 +157,171 @@
 
 
 
+static void SkipBlanks (int SingleLine)
+{
+    while (C != EOF && (!SingleLine || C != '\n') && IsSpace (C)) {
+        NextChar ();
+    }
+}
+
+static long GetDecimalToken ()
+{
+    long Value = 0;
+
+    while (C != EOF && IsDigit (C)) {
+        Value = Value * 10 + DigitVal (C);
+        NextChar ();
+    }
+    return Value;
+}
+
+static int GetEncodedChar (char *Buf, unsigned *IPtr, unsigned Size)
+{
+    char Decoded = 0;
+    int Count;
+
+    if (C == EOF) {
+        return -1;
+    } else if (C != '\\') {
+        Decoded = C;
+        NextChar ();
+        goto Store;
+    }
+    NextChar (); /* consume '\\' */
+    if (C == EOF) {
+        return -1;
+    } else if (IsODigit (C)) {
+        Count = 3;
+        do {
+            Decoded = Decoded * 8 + DigitVal (C);
+            NextChar ();
+            --Count;
+        } while (Count > 0 && C != EOF && IsODigit (C));
+    } else if (C == 'x') {
+        NextChar (); /* consume 'x' */
+        Count = 2;
+        while (Count > 0 && C != EOF && IsXDigit (C)) {
+            Decoded = Decoded * 16 + DigitVal (C);
+            NextChar ();
+            --Count;
+        }
+    } else {
+        switch (C) {
+            case '"': case '\'': case '\\':
+                        Decoded = C;	    break;
+            case 't':	Decoded = '\t';	    break;
+            case 'r':	Decoded = '\r';	    break;
+            case 'n':	Decoded = '\n';	    break;
+            default:	return -1;
+        }
+        NextChar ();
+    }
+Store:
+    if (*IPtr < Size - 1) {
+        Buf [(*IPtr)++] = Decoded;
+    }
+    Buf [*IPtr] = 0;
+    return 0;
+}
+
+static void LineMarkerOrComment ()
+/* Handle a line beginning with '#'. Possible interpretations are:
+ * - #line <lineno> ["<filename>"]	    (C preprocessor input)
+ * - # <lineno> "<filename>" [<flag>]...    (gcc preprocessor output)
+ * - #<comment>
+ */
+{
+    unsigned long LineNo = 0;
+    int LineDirective = 0;
+    StrBuf SrcNameBuf = AUTO_STRBUF_INITIALIZER;
+    
+    /* Skip the first "# " */
+    NextChar ();
+    SkipBlanks (1);
+
+    /* Check "line" */
+    if (C == 'l') {
+        char MaybeLine [6];
+        unsigned I;
+        for (I = 0; I < sizeof MaybeLine - 1 && C != EOF && IsAlNum (C); ++I) {
+            MaybeLine [I] = C;
+            NextChar ();
+        }
+        MaybeLine [I] = 0;
+        if (strcmp (MaybeLine, "line") != 0) {
+            goto NotMarker;
+        }
+        LineDirective = 1;
+        SkipBlanks (1);
+    }
+
+    /* Get line number */
+    if (C == EOF || !IsDigit (C)) {
+        goto NotMarker;
+    }
+    LineNo = GetDecimalToken ();
+    SkipBlanks (1);
+
+    /* Get the source file name */
+    if (C != '\"') {
+        /* The source file name is missing */
+        if (LineDirective && C == '\n') {
+            /* got #line <lineno> */
+            NextChar ();
+            InputLine = LineNo;
+            goto Last;
+        } else {
+            goto NotMarker;
+        }
+    }
+    NextChar ();
+    while (C != EOF && C != '\n' && C != '\"') {
+        char DecodeBuf [2];
+        unsigned I = 0;
+        if (GetEncodedChar (DecodeBuf, &I, sizeof DecodeBuf) < 0) {
+            goto BadMarker;
+        }
+        SB_AppendBuf (&SrcNameBuf, DecodeBuf, I);
+    }
+    if (C != '\"') {
+        goto BadMarker;
+    }
+    NextChar ();
+
+    /* Ignore until the end of line */
+    while (C != EOF && C != '\n') {
+        NextChar ();
+    }
+
+    /* Accepted a line marker */
+    SB_Terminate (&SrcNameBuf);
+    xfree (InputSrcName);
+    InputSrcName = SB_GetBuf (&SrcNameBuf);
+    SB_Init (&SrcNameBuf);
+    InputLine = (unsigned)LineNo;
+    NextChar ();
+    goto Last;
+
+BadMarker:
+    InfoWarning ("Bad line marker");
+NotMarker:
+    while (C != EOF && C != '\n') {
+        NextChar ();
+    }
+    NextChar ();
+Last:
+    SB_Done (&SrcNameBuf);
+}
+
 void InfoNextTok (void)
 /* Read the next token from the input stream */
 {
     unsigned I;
-    int      Esc;
+    char DecodeBuf [2];
 
 Again:
     /* Skip whitespace */
-    while (IsSpace (C)) {
-        NextChar ();
-    }
+    SkipBlanks (0);
 
     /* Remember the current position */
     InfoErrorLine = InputLine;
@@ -198,11 +360,7 @@
 
     /* Decimal number? */
     if (IsDigit (C)) {
-        InfoIVal = 0;
-        while (IsDigit (C)) {
-            InfoIVal = InfoIVal * 10 + DigitVal (C);
-            NextChar ();
-        }
+        InfoIVal = GetDecimalToken ();
         InfoTok = INFOTOK_INTCON;
         return;
     }
@@ -248,38 +406,31 @@
         case '\"':
             NextChar ();
             I = 0;
-            while (C != '\"') {
-                Esc = (C == '\\');
-                if (Esc) {
-                    NextChar ();
-                }
-                if (C == EOF || C == '\n') {
-                    InfoError ("Unterminated string");
-                }
-                if (Esc) {
-                    switch (C) {
-                        case '\"':      C = '\"';       break;
-                        case '\'':      C = '\'';       break;
-                        default:        InfoError ("Invalid escape char: %c", C);
+            while (C != EOF && C != '\"') {
+                if (GetEncodedChar (InfoSVal, &I, sizeof InfoSVal) < 0) {
+                    if (C == EOF) {
+                        InfoError ("Unterminated string");
+                    } else  {
+                        InfoError ("Invalid escape char: %c", C);
                     }
                 }
-                if (I < CFG_MAX_IDENT_LEN) {
-                    InfoSVal [I++] = C;
-                }
-                NextChar ();
             }
-            NextChar ();
-            InfoSVal [I] = '\0';
+            if (C != '\"') {
+                InfoError ("Unterminated string");
+            }
+	    NextChar ();
             InfoTok = INFOTOK_STRCON;
             break;
 
         case '\'':
             NextChar ();
-            if (C == EOF || IsControl (C)) {
+            if (C == EOF || IsControl (C) || C == '\'') {
                 InfoError ("Invalid character constant");
             }
-            InfoIVal = C;
-            NextChar ();
+            if (GetEncodedChar (DecodeBuf, &I, sizeof DecodeBuf) < 0 || I != 1) {
+                InfoError ("Invalid character constant");
+            }
+            InfoIVal = DecodeBuf [0];
             if (C != '\'') {
                 InfoError ("Unterminated character constant");
             }
@@ -288,8 +439,13 @@
             break;
 
         case '#':
-            /* Comment */
-            while (C != '\n' && C != EOF) {
+            /* # lineno "sourcefile" or # comment */
+            if (InfoSyncLines && InputCol == 1) {
+                LineMarkerOrComment ();
+            } else {
+                do {
+                    NextChar ();
+                } while (C != EOF && C != '\n');
                 NextChar ();
             }
             if (C != EOF) {
@@ -298,6 +454,21 @@
             InfoTok = INFOTOK_EOF;
             break;
 
+        case '/':
+            /* C++ style comment */
+            NextChar ();
+            if (C != '/') {
+                InfoError ("Invalid token `/'");
+            }
+            do {
+                NextChar ();
+            } while (C != '\n' && C != EOF);
+            if (C != EOF) {
+                goto Again;
+            }
+            InfoTok = INFOTOK_EOF;
+            break;
+
         case EOF:
             InfoTok = INFOTOK_EOF;
             break;
@@ -484,15 +655,19 @@
 /* Set a name for a config file */
 {
     InfoFile = Name;
+    xfree(InputSrcName);
+    InputSrcName = xstrdup(Name);
 }
 
 
 
+#ifdef unused
 const char* InfoGetName (void)
 /* Get the name of the config file */
 {
     return InfoFile? InfoFile : "";
 }
+#endif /* unused */
 
 
 
diff -ru cc65-2.17.orig/src/da65/scanner.h cc65-2.17/src/da65/scanner.h
--- cc65-2.17.orig/src/da65/scanner.h	2018-03-07 22:05:21.000000000 +0000
+++ cc65-2.17/src/da65/scanner.h	2018-06-03 03:38:33.335878900 +0000
@@ -105,6 +105,7 @@
     INFOTOK_COMMENT,
     INFOTOK_ADDR,
     INFOTOK_SIZE,
+    INFOTOK_VOPERAND,
 
     /* ASMINC section */
     INFOTOK_FILE,
@@ -137,6 +138,8 @@
 extern unsigned         InfoErrorLine;
 extern unsigned         InfoErrorCol;
 
+/* Options */
+extern unsigned char    InfoSyncLines;
 
 
 /*****************************************************************************/
