text: /*****************************************************************************/
  /*                                                                           */
  /*                                 asmstmt.c                                 */
  /*                                                                           */
  /*            Inline assembler statements for the cc65 C compiler            */
  /*                                                                           */
  /*                                                                           */
  /*                                                                           */
  /* (C) 2001-2008 Ullrich von Bassewitz                                       */
  /*               Roemerstrasse 52                                            */
  /*               D-70794 Filderstadt                                         */
  /* EMail:        uz@musoftware.de                                            */
  /*                                                                           */
  /*                                                                           */
  /* This software is provided 'as-is', without any expressed or implied       */
  /* warranty.  In no event will the authors be held liable for any damages    */
  /* arising from the use of this software.                                    */
  /*                                                                           */
  /* Permission is granted to anyone to use this software for any purpose,     */
  /* including commercial applications, and to alter it and redistribute it    */
  /* freely, subject to the following restrictions:                            */
  /*                                                                           */
  /* 1. The origin of this software must not be misrepresented; you must not   */
  /*    claim that you wrote the original software. If you use this software   */
  /*    in a product, an acknowledgment in the product documentation would be  */
  /*    appreciated but is not required.                                       */
  /* 2. Altered source versions must be plainly marked as such, and must not   */
  /*    be misrepresented as being the original software.                      */
  /* 3. This notice may not be removed or altered from any source              */
  /*    distribution.                                                          */
  /*                                                                           */
  /*****************************************************************************/
  
  
  
  #include <string.h>
  
  /* common */
  #include "xsprintf.h"
  
  /* cc65 */
  #include "asmlabel.h"
  #include "codegen.h"
  #include "codeseg.h"
  #include "datatype.h"
  #include "error.h"
  #include "expr.h"
  #include "function.h"
  #include "litpool.h"
  #include "scanner.h"
  #include "segments.h"
  #include "stackptr.h"
  #include "symtab.h"
  #include "asmstmt.h"
  
  
  
  /*****************************************************************************/
  /*                                   Code                                    */
  /*****************************************************************************/
  
  
  
  static void AsmRangeError (unsigned Arg)
  /* Print a diagnostic about a range error in the argument with the given number */
  {
      Error ("Range error in argument %u", Arg);
  }
  
  
  
  static void AsmErrorSkip (void)
  /* Called in case of an error, skips tokens until the closing paren or a
  ** semicolon is reached.
  */
  {
      static const token_t TokenList[] = { TOK_RPAREN, TOK_SEMI };
      SkipTokens (TokenList, sizeof(TokenList) / sizeof(TokenList[0]));
  }
  
  
  
  static SymEntry* AsmGetSym (unsigned Arg, unsigned Type)
  /* Find the symbol with the name currently in NextTok. The symbol must be of
  ** the given type. On errors, NULL is returned.
  */
  {
      SymEntry* Sym;
  
      /* We expect an argument separated by a comma */
      ConsumeComma ();
  
      /* Argument must be an identifier */
      if (CurTok.Tok != TOK_IDENT) {
          Error ("Identifier expected for argument %u", Arg);
          AsmErrorSkip ();
          return 0;
      }
  
      /* Get a pointer to the symbol table entry */
      Sym = FindSym (CurTok.Ident);
  
      /* Did we find a symbol with this name? */
      if (Sym == 0) {
          Error ("Undefined symbol '%s' for argument %u", CurTok.Ident, Arg);
          AsmErrorSkip ();
          return 0;
      }
  
      /* We found the symbol - skip the name token */
      NextToken ();
  
      /* Check if we have a global symbol */
      if ((Sym->Flags & Type) != Type) {
          Error ("Type of argument %u differs from format specifier", Arg);
          AsmErrorSkip ();
          return 0;
      }
  
      /* Mark the symbol as referenced */
      Sym->Flags |= SC_REF;
  
      /* Return it */
      return Sym;
  }
  
  
  
  static void ParseByteArg (StrBuf* T, unsigned Arg)
  /* Parse the %b format specifier */
  {
      char     Buf [16];
  
      /* We expect an argument separated by a comma */
      ConsumeComma ();
  
      /* Evaluate the expression */
      ExprDesc Expr = NoCodeConstAbsIntExpr (hie1);
  
      /* Check the range but allow negative values if the type is signed */
      if (IsSignUnsigned (Expr.Type)) {
          if (Expr.IVal < 0 || Expr.IVal > 0xFF) {
              AsmRangeError (Arg);
              Expr.IVal = 0;
          }
      } else {
          if (Expr.IVal < -128 || Expr.IVal > 127) {
              AsmRangeError (Arg);
              Expr.IVal = 0;
          }
      }
  
      /* Convert into a hex number */
      xsprintf (Buf, sizeof (Buf), "$%02lX", Expr.IVal & 0xFF);
  
      /* Add the number to the target buffer */
      SB_AppendStr (T, Buf);
  }
  
  
  
  static void ParseWordArg (StrBuf* T, unsigned Arg)
  /* Parse the %w format specifier */
  {
      char     Buf [16];
  
      /* We expect an argument separated by a comma */
      ConsumeComma ();
  
      /* Evaluate the expression */
      ExprDesc Expr = NoCodeConstAbsIntExpr (hie1);
  
      /* Check the range but allow negative values if the type is signed */
      if (IsSignUnsigned (Expr.Type)) {
          if (Expr.IVal < 0 || Expr.IVal > 0xFFFF) {
              AsmRangeError (Arg);
              Expr.IVal = 0;
          }
      } else {
          if (Expr.IVal < -32768 || Expr.IVal > 32767) {
              AsmRangeError (Arg);
              Expr.IVal = 0;
          }
      }
  
      /* Convert into a hex number */
      xsprintf (Buf, sizeof (Buf), "$%04lX", Expr.IVal & 0xFFFF);
  
      /* Add the number to the target buffer */
      SB_AppendStr (T, Buf);
  }
  
  
  
  static void ParseLongArg (StrBuf* T, unsigned Arg attribute ((unused)))
  /* Parse the %l format specifier */
  {
      char     Buf [16];
  
      /* We expect an argument separated by a comma */
      ConsumeComma ();
  
      /* Evaluate the expression */
      ExprDesc Expr = NoCodeConstAbsIntExpr (hie1);
  
      /* Convert into a hex number */
      xsprintf (Buf, sizeof (Buf), "$%08lX", Expr.IVal & 0xFFFFFFFF);
  
      /* Add the number to the target buffer */
      SB_AppendStr (T, Buf);
  }
  
  
  
  static void ParseGVarArg (StrBuf* T, unsigned Arg)
  /* Parse the %v format specifier.
  ** ### FIXME: Asm names should be generated in the same place.
  */
  {
      /* Parse the symbol name parameter and check the type */
      SymEntry* Sym = AsmGetSym (Arg, SC_STATIC);
      if (Sym == 0) {
          /* Some sort of error */
          return;
      }
  
      /* Check for external linkage */
      if (Sym->Flags & (SC_EXTERN | SC_STORAGE | SC_FUNC)) {
          /* External linkage or a function */
          SB_AppendChar (T, '_');
          SB_AppendStr (T, Sym->Name);
      } else if (Sym->Flags & SC_REGISTER) {
          char Buf[32];
          xsprintf (Buf, sizeof (Buf), "regbank+%d", Sym->V.R.RegOffs);
          SB_AppendStr (T, Buf);
      } else {
          /* Static variable */
          SB_AppendStr (T, LocalDataLabelName (Sym->V.L.Label));
      }
  }
  
  
  
  static void ParseLVarArg (StrBuf* T, unsigned Arg)
  /* Parse the %o format specifier */
  {
      unsigned Offs;
      char Buf [16];
  
      /* Parse the symbol name parameter and check the type */
      SymEntry* Sym = AsmGetSym (Arg, SC_AUTO);
      if (Sym == 0) {
          /* Some sort of error */
          return;
      }
  
      /* The symbol may be a parameter to a variadic function. In this case, we
      ** don't have a fixed stack offset, so check it and bail out with an error
      ** if this is the case.
      */
      if ((Sym->Flags & SC_PARAM) == SC_PARAM && F_IsVariadic (CurrentFunc)) {
          Error ("Argument %u has no fixed stack offset", Arg);
          AsmErrorSkip ();
          return;
      }
  
      /* Calculate the current offset from SP */
      Offs = Sym->V.Offs - StackPtr;
  
      /* Output the offset */
      xsprintf (Buf, sizeof (Buf), (Offs > 0xFF)? "$%04X" : "$%02X", Offs);
      SB_AppendStr (T, Buf);
  }
  
  
  
  static void ParseLabelArg (StrBuf* T, unsigned Arg attribute ((unused)))
  /* Parse the %g format specifier */
  {
      /* We expect an identifier separated by a comma */
      ConsumeComma ();
      if (CurTok.Tok != TOK_IDENT) {
  
          Error ("Label name expected");
  
      } else {
  
          /* Add a new C label symbol if we don't have one until now */
          SymEntry* Entry = AddLabelSym (CurTok.Ident, SC_REF);
  
          /* Append the label name to the buffer */
          SB_AppendStr (T, LocalLabelName (Entry->V.L.Label));
  
          /* Eat the label name */
          NextToken ();
  
      }
  }
  
  
  
  static void ParseStrArg (StrBuf* T, unsigned Arg attribute ((unused)))
  /* Parse the %s format specifier */
  {
      ExprDesc Expr;
      char Buf [64];
  
      /* We expect an argument separated by a comma */
      ConsumeComma ();
  
      /* Check what comes */
      switch (CurTok.Tok) {
  
          case TOK_IDENT:
              /* Identifier */
              SB_AppendStr (T, CurTok.Ident);
              NextToken ();
              break;
  
          case TOK_SCONST:
              /* String constant */
              SB_Append (T, GetLiteralStrBuf (CurTok.SVal));
              NextToken ();
              break;
  
          default:
              Expr = NoCodeConstAbsIntExpr (hie1);
              xsprintf (Buf, sizeof (Buf), "%ld", Expr.IVal);
              SB_AppendStr (T, Buf);
              break;
      }
  }
  
  
  
  static void ParseAsm (void)
  /* Parse the contents of the ASM statement */
  {
      unsigned Arg;
      char     C;
  
      /* Create a target string buffer */
      StrBuf T = AUTO_STRBUF_INITIALIZER;
  
      /* Create a string buffer from the string literal */
      StrBuf S = AUTO_STRBUF_INITIALIZER;
      SB_Append (&S, GetLiteralStrBuf (CurTok.SVal));
  
      /* Skip the string token */
      NextToken ();
  
      /* Parse the statement. It may contain several lines and one or more
      ** of the following place holders:
      **   %b     - Numerical 8 bit value
      **   %w     - Numerical 16 bit value
      **   %l     - Numerical 32 bit value
      **   %v     - Assembler name of a (global) variable
      **   %o     - Stack offset of a (local) variable
      **   %g     - Assembler name of a C label
      **   %s     - Any argument converted to a string (almost)
      **   %%     - The % sign
      */
      Arg = 0;
      while ((C = SB_Get (&S)) != '\0') {
  
          /* If it is a newline, the current line is ready to go */
          if (C == '\n') {
  
              /* Pass it to the backend and start over */
              g_asmcode (&T);
              SB_Clear (&T);
  
          } else if (C == '%') {
  
              /* Format specifier */
              ++Arg;
              C = SB_Get (&S);
              switch (C) {
                  case '%':   SB_AppendChar (&T, '%');    break;
                  case 'b':   ParseByteArg (&T, Arg);     break;
                  case 'g':   ParseLabelArg (&T, Arg);    break;
                  case 'l':   ParseLongArg (&T, Arg);     break;
                  case 'o':   ParseLVarArg (&T, Arg);     break;
                  case 's':   ParseStrArg (&T, Arg);      break;
                  case 'v':   ParseGVarArg (&T, Arg);     break;
                  case 'w':   ParseWordArg (&T, Arg);     break;
                  default:
                      Error ("Error in __asm__ format specifier %u", Arg);
                      AsmErrorSkip ();
                      goto Done;
              }
  
          } else {
  
              /* A normal character, just copy it */
              SB_AppendChar (&T, C);
  
          }
      }
  
      /* If the target buffer is not empty, we have a last line in there */
      if (!SB_IsEmpty (&T)) {
          g_asmcode (&T);
      }
  
  Done:
      /* Call the string buf destructors */
      SB_Done (&S);
      SB_Done (&T);
  }
  
  
  
  void AsmStatement (void)
  /* This function parses ASM statements. The syntax of the ASM directive
  ** looks like the one defined for C++ (C has no ASM directive), that is,
  ** a string literal in parenthesis.
  */
  {
      /* Skip the ASM */
      NextToken ();
  
      /* An optional volatile qualifier disables optimization for
      ** the entire function [same as #pragma optimize(push, off)].
      */
      if (CurTok.Tok == TOK_VOLATILE) {
          /* Don't optimize the Current code Segment */
          CS->Code->Optimize = 0;
          NextToken ();
      }
  
      /* Need left parenthesis */
      if (!ConsumeLParen ()) {
          return;
      }
  
      /* String literal */
      if (CurTok.Tok != TOK_SCONST) {
  
          /* Print a diagnostic */
          Error ("String literal expected");
  
          /* Try some smart error recovery: Skip tokens until we reach the
          ** enclosing paren, or a semicolon.
          */
          AsmErrorSkip ();
  
      } else {
  
          /* Parse the ASM statement */
          ParseAsm ();
      }
  
      /* Closing paren needed */
      ConsumeRParen ();
  }
  