]> git.sur5r.net Git - cc65/blobdiff - src/cc65/stdfunc.c
Removed (pretty inconsistently used) tab chars from source code base.
[cc65] / src / cc65 / stdfunc.c
index 219ad470676da2404e15c010d87da75a8f04371c..1c3c89483a740da75b27e4de9288f753ff3c9d72 100644 (file)
@@ -1,15 +1,15 @@
 /*****************************************************************************/
 /*                                                                           */
-/*                                stdfunc.c                                 */
+/*                                 stdfunc.c                                 */
 /*                                                                           */
-/*        Handle inlining of known functions for the cc65 compiler          */
+/*         Handle inlining of known functions for the cc65 compiler          */
 /*                                                                           */
 /*                                                                           */
 /*                                                                           */
-/* (C) 1998     Ullrich von Bassewitz                                        */
-/*              Wacholderweg 14                                              */
-/*              D-70597 Stuttgart                                            */
-/* EMail:       uz@musoftware.de                                             */
+/* (C) 1998-2010 Ullrich von Bassewitz                                       */
+/*               Roemerstrasse 52                                            */
+/*               D-70794 Filderstadt                                         */
+/* EMail:        uz@cc65.org                                                 */
 /*                                                                           */
 /*                                                                           */
 /* This software is provided 'as-is', without any expressed or implied       */
 #include "check.h"
 
 /* cc65 */
+#include "asmcode.h"
+#include "asmlabel.h"
 #include "codegen.h"
 #include "error.h"
 #include "funcdesc.h"
 #include "global.h"
 #include "litpool.h"
+#include "loadexpr.h"
 #include "scanner.h"
+#include "stackptr.h"
 #include "stdfunc.h"
+#include "stdnames.h"
+#include "typeconv.h"
 
 
 
 /*****************************************************************************/
-/*                            Function forwards                             */
+/*                             Function forwards                             */
 /*****************************************************************************/
 
 
 
+static void StdFunc_memcpy (FuncDesc*, ExprDesc*);
 static void StdFunc_memset (FuncDesc*, ExprDesc*);
+static void StdFunc_strcmp (FuncDesc*, ExprDesc*);
+static void StdFunc_strcpy (FuncDesc*, ExprDesc*);
 static void StdFunc_strlen (FuncDesc*, ExprDesc*);
 
 
 
 /*****************************************************************************/
-/*                                  Data                                    */
+/*                                   Data                                    */
 /*****************************************************************************/
 
 
@@ -72,18 +81,33 @@ static void StdFunc_strlen (FuncDesc*, ExprDesc*);
  * alphabetically!
  */
 static struct StdFuncDesc {
-    const char*                Name;
-    void               (*Handler) (FuncDesc*, ExprDesc*);
-} StdFuncs [] = {
-    {          "memset",       StdFunc_memset          },
-    {          "strlen",       StdFunc_strlen          },
+    const char*         Name;
+    void                (*Handler) (FuncDesc*, ExprDesc*);
+} StdFuncs[] = {
+    {   "memcpy",       StdFunc_memcpy          },
+    {   "memset",       StdFunc_memset          },
+    {   "strcmp",       StdFunc_strcmp          },
+    {   "strcpy",       StdFunc_strcpy          },
+    {   "strlen",       StdFunc_strlen          },
 
 };
-#define FUNC_COUNT     (sizeof (StdFuncs) / sizeof (StdFuncs [0]))
+#define FUNC_COUNT      (sizeof (StdFuncs) / sizeof (StdFuncs[0]))
+
+typedef struct ArgDesc ArgDesc;
+struct ArgDesc {
+    const Type* ArgType;        /* Required argument type */
+    ExprDesc    Expr;           /* Argument expression */
+    const Type* Type;           /* The original type before conversion */
+    CodeMark    Load;           /* Start of argument load code */
+    CodeMark    Push;           /* Start of argument push code */
+    CodeMark    End;            /* End of the code for calculation+push */
+    unsigned    Flags;          /* Code generation flags */
+};
+
 
 
 /*****************************************************************************/
-/*                            Helper functions                              */
+/*                             Helper functions                              */
 /*****************************************************************************/
 
 
@@ -96,91 +120,436 @@ static int CmpFunc (const void* Key, const void* Elem)
 
 
 
-static struct StdFuncDesc* FindFunc (const char* Name)
-/* Find a function with the given name. Return a pointer to the descriptor if
- * found, return NULL otherwise.
+static long ArrayElementCount (const ArgDesc* Arg)
+/* Check if the type of the given argument is an array. If so, and if the
+ * element count is known, return it. In all other cases, return UNSPECIFIED.
  */
 {
-    return bsearch (Name, StdFuncs, FUNC_COUNT, sizeof (StdFuncs [0]), CmpFunc);
+    long Count;
+
+    if (IsTypeArray (Arg->Type)) {
+        Count = GetElementCount (Arg->Type);
+        if (Count == FLEXIBLE) {
+            /* Treat as unknown */
+            Count = UNSPECIFIED;
+        }
+    } else {
+        Count = UNSPECIFIED;
+    }
+    return Count;
 }
 
 
 
-static unsigned ParseArg (type* Type, ExprDesc* pval)
-/* Parse one argument but do not push it onto the stack. Return the code
- * generator flags needed to do the actual push.
+static void ParseArg (ArgDesc* Arg, Type* Type)
+/* Parse one argument but do not push it onto the stack. Make all fields in
+ * Arg valid.
  */
 {
-    unsigned CFlags;
-    unsigned Flags;
+    /* We have a prototype, so chars may be pushed as chars */
+    Arg->Flags = CF_FORCECHAR;
 
-    /* Do some optimization: If we have a constant value to push,
-     * use a special function that may optimize.
+    /* Remember the required argument type */
+    Arg->ArgType = Type;
+
+    /* Read the expression we're going to pass to the function */
+    MarkedExprWithCheck (hie1, &Arg->Expr);
+
+    /* Remember the actual argument type */
+    Arg->Type = Arg->Expr.Type;
+
+    /* Convert this expression to the expected type */
+    TypeConversion (&Arg->Expr, Type);
+
+    /* Remember the following code position */
+    GetCodePos (&Arg->Load);
+
+    /* If the value is a constant, set the flag, otherwise load it into the
+     * primary register.
      */
-    CFlags = CF_NONE;
-    if (CheckedSizeOf (Type) == 1) {
-        CFlags = CF_FORCECHAR;
-    }
-    Flags = CF_NONE;
-    if (evalexpr (CFlags, hie1, pval) == 0) {
-        /* A constant value */
-        Flags |= CF_CONST;
+    if (ED_IsConstAbsInt (&Arg->Expr) && ED_CodeRangeIsEmpty (&Arg->Expr)) {
+        /* Remember that we have a constant value */
+        Arg->Flags |= CF_CONST;
+    } else {
+        /* Load into the primary */
+        LoadExpr (CF_NONE, &Arg->Expr);
     }
 
-    /* Promote the argument if needed */
-    assignadjust (Type, pval);
-
-    /* We have a prototype, so chars may be pushed as chars */
-    Flags |= CF_FORCECHAR;
+    /* Remember the following code position */
+    GetCodePos (&Arg->Push);
+    GetCodePos (&Arg->End);
 
     /* Use the type of the argument for the push */
-    return (Flags | TypeOf (pval->Type));
+    Arg->Flags |= TypeOf (Arg->Expr.Type);
 }
 
 
 
 /*****************************************************************************/
-/*                         Handle known functions                           */
+/*                                  memcpy                                   */
 /*****************************************************************************/
 
 
 
-static void StdFunc_memset (FuncDesc* F attribute ((unused)),
-                            ExprDesc* lval attribute ((unused)))
-/* Handle the memset function */
+static void StdFunc_memcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
+/* Handle the memcpy function */
 {
-    /* Argument types */
-    static type Arg1Type[] = { T_PTR, T_VOID, T_END };  /* void* */
-    static type Arg2Type[] = { T_INT, T_END };          /* int */
-    static type Arg3Type[] = { T_UINT, T_END };         /* size_t */
+    /* Argument types: (void*, const void*, size_t) */
+    static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_VOID), TYPE(T_END) };
+    static Type Arg2Type[] = { TYPE(T_PTR), TYPE(T_VOID|T_QUAL_CONST), TYPE(T_END) };
+    static Type Arg3Type[] = { TYPE(T_SIZE_T), TYPE(T_END) };
 
-    unsigned Flags;
-    ExprDesc Arg;
-    int      MemSet    = 1;             /* Use real memset if true */
+    ArgDesc  Arg1, Arg2, Arg3;
     unsigned ParamSize = 0;
+    unsigned Label;
+    int      Offs;
+
+    /* Argument #1 */
+    ParseArg (&Arg1, Arg1Type);
+    g_push (Arg1.Flags, Arg1.Expr.IVal);
+    GetCodePos (&Arg1.End);
+    ParamSize += SizeOf (Arg1Type);
+    ConsumeComma ();
+
+    /* Argument #2 */
+    ParseArg (&Arg2, Arg2Type);
+    g_push (Arg2.Flags, Arg2.Expr.IVal);
+    GetCodePos (&Arg2.End);
+    ParamSize += SizeOf (Arg2Type);
+    ConsumeComma ();
+
+    /* Argument #3. Since memcpy is a fastcall function, we must load the
+     * arg into the primary if it is not already there. This parameter is
+     * also ignored for the calculation of the parameter size, since it is
+     * not passed via the stack.
+     */
+    ParseArg (&Arg3, Arg3Type);
+    if (Arg3.Flags & CF_CONST) {
+        LoadExpr (CF_NONE, &Arg3.Expr);
+    }
+
+    /* Emit the actual function call. This will also cleanup the stack. */
+    g_call (CF_FIXARGC, Func_memcpy, ParamSize);
+
+    if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal == 0) {
+
+        /* memcpy has been called with a count argument of zero */
+        Warning ("Call to memcpy has no effect");
 
+        /* Remove all of the generated code but the load of the first
+         * argument, which is what memcpy returns.
+         */
+        RemoveCode (&Arg1.Push);
 
-    /* Check the prototype of the function against what we know about it, so
-     * we can detect errors.
+        /* Set the function result to the first argument */
+        *Expr = Arg1.Expr;
+
+        /* Bail out, no need for further improvements */
+        goto ExitPoint;
+    }
+
+    /* We've generated the complete code for the function now and know the
+     * types of all parameters. Check for situations where better code can
+     * be generated. If such a situation is detected, throw away the
+     * generated, and emit better code.
      */
-    /* ### */
+    if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
+        ((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) ||
+         (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) &&
+        ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) ||
+         (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)))) {
+
+        int Reg1 = ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr);
+        int Reg2 = ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr);
+
+        /* Drop the generated code */
+        RemoveCode (&Arg1.Expr.Start);
+
+        /* We need a label */
+        Label = GetLocalLabel ();
+
+        /* Generate memcpy code */
+        if (Arg3.Expr.IVal <= 127) {
+
+            AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
+            AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
+            g_defcodelabel (Label);
+            if (Reg2) {
+                AddCodeLine ("lda (%s),y", ED_GetLabelName (&Arg2.Expr, 0));
+            } else {
+                AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, 0));
+            }
+            if (Reg1) {
+                AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
+            } else {
+                AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
+            }
+            AddCodeLine ("dey");
+            AddCodeLine ("bpl %s", LocalLabelName (Label));
+
+        } else {
+
+            AddCodeLine ("ldy #$00");
+            AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
+            g_defcodelabel (Label);
+            if (Reg2) {
+                AddCodeLine ("lda (%s),y", ED_GetLabelName (&Arg2.Expr, 0));
+            } else {
+                AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, 0));
+            }
+            if (Reg1) {
+                AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
+            } else {
+                AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
+            }
+            AddCodeLine ("iny");
+            AddCodeLine ("cpy #$%02X", (unsigned char) Arg3.Expr.IVal);
+            AddCodeLine ("bne %s", LocalLabelName (Label));
+
+        }
+
+        /* memcpy returns the address, so the result is actually identical
+         * to the first argument.
+         */
+        *Expr = Arg1.Expr;
+
+    } else if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
+               ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr) &&
+               ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) &&
+               (Arg1.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256) {
+
+        /* It is possible to just use one index register even if the stack
+         * offset is not zero, by adjusting the offset to the constant
+         * address accordingly. But we cannot do this if the data in
+         * question is in the register space or at an absolute address less
+         * than 256. Register space is zero page, which means that the
+         * address calculation could overflow in the linker.
+         */
+        int AllowOneIndex = !ED_IsLocRegister (&Arg2.Expr) &&
+                            !(ED_IsLocAbs (&Arg2.Expr) && Arg2.Expr.IVal < 256);
+
+        /* Calculate the real stack offset */
+        Offs = ED_GetStackOffs (&Arg1.Expr, 0);
+
+        /* Drop the generated code */
+        RemoveCode (&Arg1.Expr.Start);
+
+        /* We need a label */
+        Label = GetLocalLabel ();
+
+        /* Generate memcpy code */
+        if (Arg3.Expr.IVal <= 127 && !AllowOneIndex) {
+
+            if (Offs == 0) {
+                AddCodeLine ("ldy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal - 1));
+                g_defcodelabel (Label);
+                AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, -Offs));
+                AddCodeLine ("sta (sp),y");
+                AddCodeLine ("dey");
+                AddCodeLine ("bpl %s", LocalLabelName (Label));
+            } else {
+                AddCodeLine ("ldx #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
+                AddCodeLine ("ldy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal - 1));
+                g_defcodelabel (Label);
+                AddCodeLine ("lda %s,x", ED_GetLabelName (&Arg2.Expr, 0));
+                AddCodeLine ("sta (sp),y");
+                AddCodeLine ("dey");
+                AddCodeLine ("dex");
+                AddCodeLine ("bpl %s", LocalLabelName (Label));
+            }
+
+        } else {
+
+            if (Offs == 0 || AllowOneIndex) {
+                AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
+                g_defcodelabel (Label);
+                AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, -Offs));
+                AddCodeLine ("sta (sp),y");
+                AddCodeLine ("iny");
+                AddCodeLine ("cpy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal));
+                AddCodeLine ("bne %s", LocalLabelName (Label));
+            } else {
+                AddCodeLine ("ldx #$00");
+                AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
+                g_defcodelabel (Label);
+                AddCodeLine ("lda %s,x", ED_GetLabelName (&Arg2.Expr, 0));
+                AddCodeLine ("sta (sp),y");
+                AddCodeLine ("iny");
+                AddCodeLine ("inx");
+                AddCodeLine ("cpx #$%02X", (unsigned char) Arg3.Expr.IVal);
+                AddCodeLine ("bne %s", LocalLabelName (Label));
+            }
+
+        }
+
+        /* memcpy returns the address, so the result is actually identical
+         * to the first argument.
+         */
+        *Expr = Arg1.Expr;
+
+    } else if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
+               ED_IsRVal (&Arg2.Expr) && ED_IsLocStack (&Arg2.Expr) &&
+               (Arg2.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256 &&
+               ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) {
+
+        /* It is possible to just use one index register even if the stack
+         * offset is not zero, by adjusting the offset to the constant
+         * address accordingly. But we cannot do this if the data in
+         * question is in the register space or at an absolute address less
+         * than 256. Register space is zero page, which means that the
+         * address calculation could overflow in the linker.
+         */
+        int AllowOneIndex = !ED_IsLocRegister (&Arg1.Expr) &&
+                            !(ED_IsLocAbs (&Arg1.Expr) && Arg1.Expr.IVal < 256);
+
+        /* Calculate the real stack offset */
+        Offs = ED_GetStackOffs (&Arg2.Expr, 0);
+
+        /* Drop the generated code */
+        RemoveCode (&Arg1.Expr.Start);
+
+        /* We need a label */
+        Label = GetLocalLabel ();
+
+        /* Generate memcpy code */
+        if (Arg3.Expr.IVal <= 127 && !AllowOneIndex) {
+
+            if (Offs == 0) {
+                AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal - 1));
+                g_defcodelabel (Label);
+                AddCodeLine ("lda (sp),y");
+                AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
+                AddCodeLine ("dey");
+                AddCodeLine ("bpl %s", LocalLabelName (Label));
+            } else {
+                AddCodeLine ("ldx #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
+                AddCodeLine ("ldy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal - 1));
+                g_defcodelabel (Label);
+                AddCodeLine ("lda (sp),y");
+                AddCodeLine ("sta %s,x", ED_GetLabelName (&Arg1.Expr, 0));
+                AddCodeLine ("dey");
+                AddCodeLine ("dex");
+                AddCodeLine ("bpl %s", LocalLabelName (Label));
+            }
+
+        } else {
+
+            if (Offs == 0 || AllowOneIndex) {
+                AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
+                g_defcodelabel (Label);
+                AddCodeLine ("lda (sp),y");
+                AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, -Offs));
+                AddCodeLine ("iny");
+                AddCodeLine ("cpy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal));
+                AddCodeLine ("bne %s", LocalLabelName (Label));
+            } else {
+                AddCodeLine ("ldx #$00");
+                AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
+                g_defcodelabel (Label);
+                AddCodeLine ("lda (sp),y");
+                AddCodeLine ("sta %s,x", ED_GetLabelName (&Arg1.Expr, 0));
+                AddCodeLine ("iny");
+                AddCodeLine ("inx");
+                AddCodeLine ("cpx #$%02X", (unsigned char) Arg3.Expr.IVal);
+                AddCodeLine ("bne %s", LocalLabelName (Label));
+            }
+
+        }
+
+        /* memcpy returns the address, so the result is actually identical
+         * to the first argument.
+         */
+        *Expr = Arg1.Expr;
+
+    } else if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256   &&
+               ED_IsRVal (&Arg2.Expr) && ED_IsLocStack (&Arg2.Expr)     &&
+               (Offs = ED_GetStackOffs (&Arg2.Expr, 0)) == 0) {
+
+        /* Drop the generated code but leave the load of the first argument*/
+        RemoveCode (&Arg1.Push);
+
+        /* We need a label */
+        Label = GetLocalLabel ();
+
+        /* Generate memcpy code */
+        AddCodeLine ("sta ptr1");
+        AddCodeLine ("stx ptr1+1");
+        if (Arg3.Expr.IVal <= 127) {
+            AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal - 1));
+            g_defcodelabel (Label);
+            AddCodeLine ("lda (sp),y");
+            AddCodeLine ("sta (ptr1),y");
+            AddCodeLine ("dey");
+            AddCodeLine ("bpl %s", LocalLabelName (Label));
+        } else {
+            AddCodeLine ("ldy #$00");
+            g_defcodelabel (Label);
+            AddCodeLine ("lda (sp),y");
+            AddCodeLine ("sta (ptr1),y");
+            AddCodeLine ("iny");
+            AddCodeLine ("cpy #$%02X", (unsigned char) Arg3.Expr.IVal);
+            AddCodeLine ("bne %s", LocalLabelName (Label));
+        }
+
+        /* Reload result - X hasn't changed by the code above */
+        AddCodeLine ("lda ptr1");
+
+        /* The function result is an rvalue in the primary register */
+        ED_MakeRValExpr (Expr);
+        Expr->Type = GetFuncReturn (Expr->Type);
+
+    } else {
+
+        /* The function result is an rvalue in the primary register */
+        ED_MakeRValExpr (Expr);
+        Expr->Type = GetFuncReturn (Expr->Type);
+
+    }
+
+ExitPoint:
+    /* We expect the closing brace */
+    ConsumeRParen ();
+}
+
+
+
+/*****************************************************************************/
+/*                                  memset                                   */
+/*****************************************************************************/
+
+
+
+static void StdFunc_memset (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
+/* Handle the memset function */
+{
+    /* Argument types: (void*, int, size_t) */
+    static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_VOID), TYPE(T_END) };
+    static Type Arg2Type[] = { TYPE(T_INT), TYPE(T_END) };
+    static Type Arg3Type[] = { TYPE(T_SIZE_T), TYPE(T_END) };
+
+    ArgDesc  Arg1, Arg2, Arg3;
+    int      MemSet    = 1;             /* Use real memset if true */
+    unsigned ParamSize = 0;
+    unsigned Label;
 
     /* Argument #1 */
-    Flags = ParseArg (Arg1Type, &Arg);
-    g_push (Flags, Arg.ConstVal);
+    ParseArg (&Arg1, Arg1Type);
+    g_push (Arg1.Flags, Arg1.Expr.IVal);
+    GetCodePos (&Arg1.End);
     ParamSize += SizeOf (Arg1Type);
     ConsumeComma ();
 
     /* Argument #2. This argument is special in that we will call another
      * function if it is a constant zero.
      */
-    Flags = ParseArg (Arg2Type, &Arg);
-    if ((Flags & CF_CONST) != 0 && Arg.ConstVal == 0) {
+    ParseArg (&Arg2, Arg2Type);
+    if ((Arg2.Flags & CF_CONST) != 0 && Arg2.Expr.IVal == 0) {
         /* Don't call memset, call bzero instead */
         MemSet = 0;
     } else {
         /* Push the argument */
-        g_push (Flags, Arg.ConstVal);
+        g_push (Arg2.Flags, Arg2.Expr.IVal);
+        GetCodePos (&Arg2.End);
         ParamSize += SizeOf (Arg2Type);
     }
     ConsumeComma ();
@@ -190,104 +559,730 @@ static void StdFunc_memset (FuncDesc* F attribute ((unused)),
      * also ignored for the calculation of the parameter size, since it is
      * not passed via the stack.
      */
-    Flags = ParseArg (Arg3Type, &Arg);
-    if (Flags & CF_CONST) {
-        exprhs (CF_FORCECHAR, 0, &Arg);
+    ParseArg (&Arg3, Arg3Type);
+    if (Arg3.Flags & CF_CONST) {
+        LoadExpr (CF_NONE, &Arg3.Expr);
+    }
+
+    /* Emit the actual function call. This will also cleanup the stack. */
+    g_call (CF_FIXARGC, MemSet? Func_memset : Func__bzero, ParamSize);
+
+    if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal == 0) {
+
+        /* memset has been called with a count argument of zero */
+        Warning ("Call to memset has no effect");
+
+        /* Remove all of the generated code but the load of the first
+         * argument, which is what memset returns.
+         */
+        RemoveCode (&Arg1.Push);
+
+        /* Set the function result to the first argument */
+        *Expr = Arg1.Expr;
+
+        /* Bail out, no need for further improvements */
+        goto ExitPoint;
     }
 
-    /* Emit the actual function call */
-    g_call (CF_NONE, MemSet? "memset" : "_bzero", ParamSize);
+    /* We've generated the complete code for the function now and know the
+     * types of all parameters. Check for situations where better code can
+     * be generated. If such a situation is detected, throw away the
+     * generated, and emit better code.
+     * Note: Lots of improvements would be possible here, but I will
+     * concentrate on the most common case: memset with arguments 2 and 3
+     * being constant numerical values. Some checks have shown that this
+     * covers nearly 90% of all memset calls.
+     */
+    if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
+        ED_IsConstAbsInt (&Arg2.Expr) &&
+        ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) ||
+         (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)))) {
+
+        int Reg = ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr);
+
+        /* Drop the generated code */
+        RemoveCode (&Arg1.Expr.Start);
+
+        /* We need a label */
+        Label = GetLocalLabel ();
+
+        /* Generate memset code */
+        if (Arg3.Expr.IVal <= 127) {
+
+            AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
+            AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
+            g_defcodelabel (Label);
+            if (Reg) {
+                AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
+            } else {
+                AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
+            }
+            AddCodeLine ("dey");
+            AddCodeLine ("bpl %s", LocalLabelName (Label));
+
+        } else {
+
+            AddCodeLine ("ldy #$00");
+            AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
+            g_defcodelabel (Label);
+            if (Reg) {
+                AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
+            } else {
+                AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
+            }
+            AddCodeLine ("iny");
+            AddCodeLine ("cpy #$%02X", (unsigned char) Arg3.Expr.IVal);
+            AddCodeLine ("bne %s", LocalLabelName (Label));
+
+        }
+
+        /* memset returns the address, so the result is actually identical
+         * to the first argument.
+         */
+        *Expr = Arg1.Expr;
+
+    } else if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
+               ED_IsConstAbsInt (&Arg2.Expr) &&
+               ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) &&
+               (Arg1.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256) {
+
+        /* Calculate the real stack offset */
+        int Offs = ED_GetStackOffs (&Arg1.Expr, 0);
+
+        /* Drop the generated code */
+        RemoveCode (&Arg1.Expr.Start);
+
+        /* We need a label */
+        Label = GetLocalLabel ();
+
+        /* Generate memset code */
+        AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
+        AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
+        g_defcodelabel (Label);
+        AddCodeLine ("sta (sp),y");
+        AddCodeLine ("iny");
+        AddCodeLine ("cpy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal));
+        AddCodeLine ("bne %s", LocalLabelName (Label));
+
+        /* memset returns the address, so the result is actually identical
+         * to the first argument.
+         */
+        *Expr = Arg1.Expr;
+
+    } else if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
+               ED_IsConstAbsInt (&Arg2.Expr) &&
+               (Arg2.Expr.IVal != 0 || IS_Get (&CodeSizeFactor) > 200)) {
+
+        /* Remove all of the generated code but the load of the first
+         * argument.
+         */
+        RemoveCode (&Arg1.Push);
+
+        /* We need a label */
+        Label = GetLocalLabel ();
+
+        /* Generate code */
+        AddCodeLine ("sta ptr1");
+        AddCodeLine ("stx ptr1+1");
+        if (Arg3.Expr.IVal <= 127) {
+            AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
+            AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
+            g_defcodelabel (Label);
+            AddCodeLine ("sta (ptr1),y");
+            AddCodeLine ("dey");
+            AddCodeLine ("bpl %s", LocalLabelName (Label));
+        } else {
+            AddCodeLine ("ldy #$00");
+            AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
+            g_defcodelabel (Label);
+            AddCodeLine ("sta (ptr1),y");
+            AddCodeLine ("iny");
+            AddCodeLine ("cpy #$%02X", (unsigned char) Arg3.Expr.IVal);
+            AddCodeLine ("bne %s", LocalLabelName (Label));
+        }
+
+        /* Load the function result pointer into a/x (x is still valid). This
+         * code will get removed by the optimizer if it is not used later.
+         */
+        AddCodeLine ("lda ptr1");
+
+        /* The function result is an rvalue in the primary register */
+        ED_MakeRValExpr (Expr);
+        Expr->Type = GetFuncReturn (Expr->Type);
 
+    } else {
+
+        /* The function result is an rvalue in the primary register */
+        ED_MakeRValExpr (Expr);
+        Expr->Type = GetFuncReturn (Expr->Type);
+
+    }
+
+ExitPoint:
     /* We expect the closing brace */
     ConsumeRParen ();
 }
 
 
 
-static void StdFunc_strlen (FuncDesc* F attribute ((unused)),
-                            ExprDesc* lval attribute ((unused)))
-/* Handle the strlen function */
+/*****************************************************************************/
+/*                                  strcmp                                   */
+/*****************************************************************************/
+
+
+
+static void StdFunc_strcmp (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
+/* Handle the strcmp function */
 {
-    static type ParamType[] = { T_PTR, T_SCHAR, T_END };
+    /* Argument types: (const char*, const char*) */
+    static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_CHAR|T_QUAL_CONST), TYPE(T_END) };
+    static Type Arg2Type[] = { TYPE(T_PTR), TYPE(T_CHAR|T_QUAL_CONST), TYPE(T_END) };
 
-    ExprDesc Param;
-    unsigned CodeFlags;
-    unsigned long ParamName;
+    ArgDesc  Arg1, Arg2;
+    unsigned ParamSize = 0;
+    long     ECount1;
+    long     ECount2;
+    int      IsArray;
+    int      Offs;
+
+    /* Setup the argument type string */
+    Arg1Type[1].C = GetDefaultChar () | T_QUAL_CONST;
+    Arg2Type[1].C = GetDefaultChar () | T_QUAL_CONST;
+
+    /* Argument #1 */
+    ParseArg (&Arg1, Arg1Type);
+    g_push (Arg1.Flags, Arg1.Expr.IVal);
+    ParamSize += SizeOf (Arg1Type);
+    ConsumeComma ();
 
-    /* Fetch the parameter */
-    int k = hie1 (InitExprDesc (&Param));
+    /* Argument #2. */
+    ParseArg (&Arg2, Arg2Type);
+
+    /* Since strcmp is a fastcall function, we must load the
+     * arg into the primary if it is not already there. This parameter is
+     * also ignored for the calculation of the parameter size, since it is
+     * not passed via the stack.
+     */
+    if (Arg2.Flags & CF_CONST) {
+        LoadExpr (CF_NONE, &Arg2.Expr);
+    }
+
+    /* Emit the actual function call. This will also cleanup the stack. */
+    g_call (CF_FIXARGC, Func_strcmp, ParamSize);
+
+    /* Get the element counts of the arguments. Then get the larger of the
+     * two into ECount1. This removes FLEXIBLE and UNSPECIFIED automatically
+     */
+    ECount1 = ArrayElementCount (&Arg1);
+    ECount2 = ArrayElementCount (&Arg2);
+    if (ECount2 > ECount1) {
+        ECount1 = ECount2;
+    }
+
+    /* If the second argument is the empty string literal, we can generate
+     * more efficient code.
+     */
+    if (ED_IsLocLiteral (&Arg2.Expr) &&
+        IS_Get (&WritableStrings) == 0 &&
+        GetLiteralSize (Arg2.Expr.LVal) == 1 &&
+        GetLiteralStr (Arg2.Expr.LVal)[0] == '\0') {
+
+        /* Drop the generated code so we have the first argument in the
+         * primary
+         */
+        RemoveCode (&Arg1.Push);
+
+        /* We don't need the literal any longer */
+        ReleaseLiteral (Arg2.Expr.LVal);
+
+        /* We do now have Arg1 in the primary. Load the first character from
+         * this string and cast to int. This is the function result.
+         */
+        IsArray = IsTypeArray (Arg1.Type) && ED_IsRVal (&Arg1.Expr);
+        if (IsArray && ED_IsLocStack (&Arg1.Expr) &&
+            (Offs = ED_GetStackOffs (&Arg1.Expr, 0) < 256)) {
+            /* Drop the generated code */
+            RemoveCode (&Arg1.Load);
+
+            /* Generate code */
+            AddCodeLine ("ldy #$%02X", Offs);
+            AddCodeLine ("ldx #$00");
+            AddCodeLine ("lda (sp),y");
+        } else if (IsArray && ED_IsLocConst (&Arg1.Expr)) {
+            /* Drop the generated code */
+            RemoveCode (&Arg1.Load);
+
+            /* Generate code */
+            AddCodeLine ("ldx #$00");
+            AddCodeLine ("lda %s", ED_GetLabelName (&Arg1.Expr, 0));
+        } else {
+            /* Drop part of the generated code so we have the first argument
+             * in the primary
+             */
+            RemoveCode (&Arg1.Push);
+
+            /* Fetch the first char */
+            g_getind (CF_CHAR | CF_UNSIGNED, 0);
+        }
+
+    } else if ((IS_Get (&CodeSizeFactor) >= 165) &&
+               ((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) ||
+                (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) &&
+               ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) ||
+                (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr))) &&
+               (IS_Get (&InlineStdFuncs) || (ECount1 > 0 && ECount1 < 256))) {
+
+
+        unsigned    Entry, Loop, Fin;   /* Labels */
+        const char* Load;
+        const char* Compare;
+
+        if (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)) {
+            Load = "lda (%s),y";
+        } else {
+            Load = "lda %s,y";
+        }
+        if (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr)) {
+            Compare = "cmp (%s),y";
+        } else {
+            Compare = "cmp %s,y";
+        }
+
+        /* Drop the generated code */
+        RemoveCode (&Arg1.Expr.Start);
+
+        /* We need labels */
+        Entry = GetLocalLabel ();
+        Loop  = GetLocalLabel ();
+        Fin   = GetLocalLabel ();
+
+        /* Generate strcmp code */
+        AddCodeLine ("ldy #$00");
+        AddCodeLine ("beq %s", LocalLabelName (Entry));
+        g_defcodelabel (Loop);
+        AddCodeLine ("tax");
+        AddCodeLine ("beq %s", LocalLabelName (Fin));
+        AddCodeLine ("iny");
+        g_defcodelabel (Entry);
+        AddCodeLine (Load, ED_GetLabelName (&Arg1.Expr, 0));
+        AddCodeLine (Compare, ED_GetLabelName (&Arg2.Expr, 0));
+        AddCodeLine ("beq %s", LocalLabelName (Loop));
+        AddCodeLine ("ldx #$01");
+        AddCodeLine ("bcs %s", LocalLabelName (Fin));
+        AddCodeLine ("ldx #$FF");
+        g_defcodelabel (Fin);
+
+    } else if ((IS_Get (&CodeSizeFactor) > 190) &&
+               ((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) ||
+                (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) &&
+               (IS_Get (&InlineStdFuncs) || (ECount1 > 0 && ECount1 < 256))) {
+
+
+        unsigned    Entry, Loop, Fin;   /* Labels */
+        const char* Compare;
+
+        if (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr)) {
+            Compare = "cmp (%s),y";
+        } else {
+            Compare = "cmp %s,y";
+        }
+
+        /* Drop the generated code */
+        RemoveCode (&Arg1.Push);
+
+        /* We need labels */
+        Entry = GetLocalLabel ();
+        Loop  = GetLocalLabel ();
+        Fin   = GetLocalLabel ();
+
+        /* Store Arg1 into ptr1 */
+        AddCodeLine ("sta ptr1");
+        AddCodeLine ("stx ptr1+1");
+
+        /* Generate strcmp code */
+        AddCodeLine ("ldy #$00");
+        AddCodeLine ("beq %s", LocalLabelName (Entry));
+        g_defcodelabel (Loop);
+        AddCodeLine ("tax");
+        AddCodeLine ("beq %s", LocalLabelName (Fin));
+        AddCodeLine ("iny");
+        g_defcodelabel (Entry);
+        AddCodeLine ("lda (ptr1),y");
+        AddCodeLine (Compare, ED_GetLabelName (&Arg2.Expr, 0));
+        AddCodeLine ("beq %s", LocalLabelName (Loop));
+        AddCodeLine ("ldx #$01");
+        AddCodeLine ("bcs %s", LocalLabelName (Fin));
+        AddCodeLine ("ldx #$FF");
+        g_defcodelabel (Fin);
+
+    }
+
+    /* The function result is an rvalue in the primary register */
+    ED_MakeRValExpr (Expr);
+    Expr->Type = GetFuncReturn (Expr->Type);
+
+    /* We expect the closing brace */
+    ConsumeRParen ();
+}
+
+
+
+/*****************************************************************************/
+/*                                  strcpy                                   */
+/*****************************************************************************/
+
+
+
+static void StdFunc_strcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
+/* Handle the strcpy function */
+{
+    /* Argument types: (char*, const char*) */
+    static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_CHAR), TYPE(T_END) };
+    static Type Arg2Type[] = { TYPE(T_PTR), TYPE(T_CHAR|T_QUAL_CONST), TYPE(T_END) };
+
+    ArgDesc  Arg1, Arg2;
+    unsigned ParamSize = 0;
+    long     ECount;
+    unsigned L1;
 
     /* Setup the argument type string */
-    ParamType[1] = GetDefaultChar () | T_QUAL_CONST;
+    Arg1Type[1].C = GetDefaultChar ();
+    Arg2Type[1].C = GetDefaultChar () | T_QUAL_CONST;
 
-    /* Convert the parameter type to the type needed, check for mismatches */
-    assignadjust (ParamType, &Param);
+    /* Argument #1 */
+    ParseArg (&Arg1, Arg1Type);
+    g_push (Arg1.Flags, Arg1.Expr.IVal);
+    GetCodePos (&Arg1.End);
+    ParamSize += SizeOf (Arg1Type);
+    ConsumeComma ();
 
-    /* Check if the parameter is a constant array of some type, or a numeric
-     * address cast to a pointer.
+    /* Argument #2. Since strcpy is a fastcall function, we must load the
+     * arg into the primary if it is not already there. This parameter is
+     * also ignored for the calculation of the parameter size, since it is
+     * not passed via the stack.
      */
-    CodeFlags = 0;
-    ParamName = Param.Name;
-    if ((IsTypeArray (Param.Type) && (Param.Flags & E_MCONST) != 0) ||
-        (IsTypePtr (Param.Type) && Param.Flags == (E_MCONST | E_TCONST))) {
-
-        /* Check which type of constant it is */
-        switch (Param.Flags & E_MCTYPE) {
-
-            case E_TCONST:
-                /* Numerical address */
-                CodeFlags |= CF_CONST | CF_ABSOLUTE;
-                break;
-
-            case E_TREGISTER:
-                /* Register variable */
-                CodeFlags |= CF_CONST | CF_REGVAR;
-                break;
-
-            case E_TGLAB:
-                /* Global label */
-                CodeFlags |= CF_CONST | CF_EXTERNAL;
-                break;
-
-            case E_TLLAB:
-                /* Local symbol */
-                CodeFlags |= CF_CONST | CF_STATIC;
-                break;
-
-            case E_TLIT:
-                /* A literal of some kind. If string literals are read only,
-                 * we can calculate the length of the string and remove it
-                 * from the literal pool. Otherwise we have to calculate the
-                 * length at runtime.
-                 */
-                if (!WriteableStrings) {
-                    /* String literals are const */
-                    ExprDesc Length;
-                    MakeConstIntExpr (&Length, strlen (GetLiteral (Param.ConstVal)));
-                    ResetLiteralPoolOffs (Param.ConstVal);
-                    exprhs (CF_NONE, 0, &Length);
-                    goto ExitPoint;
-                } else {
-                    CodeFlags |= CF_CONST | CF_STATIC;
-                    ParamName = LiteralPoolLabel;
-                }
-                break;
-
-            default:
-                Internal ("Unknown constant type: %04X", Param.Flags);
+    ParseArg (&Arg2, Arg2Type);
+    if (Arg2.Flags & CF_CONST) {
+        LoadExpr (CF_NONE, &Arg2.Expr);
+    }
+
+    /* Emit the actual function call. This will also cleanup the stack. */
+    g_call (CF_FIXARGC, Func_strcpy, ParamSize);
+
+    /* Get the element count of argument 1 if it is an array */
+    ECount = ArrayElementCount (&Arg1);
+
+    /* We've generated the complete code for the function now and know the
+     * types of all parameters. Check for situations where better code can
+     * be generated. If such a situation is detected, throw away the
+     * generated, and emit better code.
+     */
+    if (((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) ||
+         (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) &&
+        ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) ||
+         (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr))) &&
+        (IS_Get (&InlineStdFuncs) ||
+        (ECount != UNSPECIFIED && ECount < 256))) {
+
+        const char* Load;
+        const char* Store;
+        if (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr)) {
+            Load = "lda (%s),y";
+        } else {
+            Load = "lda %s,y";
+        }
+        if (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)) {
+            Store = "sta (%s),y";
+        } else {
+            Store = "sta %s,y";
         }
 
+        /* Drop the generated code */
+        RemoveCode (&Arg1.Expr.Start);
+
+        /* We need labels */
+        L1 = GetLocalLabel ();
+
+        /* Generate strcpy code */
+        AddCodeLine ("ldy #$FF");
+        g_defcodelabel (L1);
+        AddCodeLine ("iny");
+        AddCodeLine (Load, ED_GetLabelName (&Arg2.Expr, 0));
+        AddCodeLine (Store, ED_GetLabelName (&Arg1.Expr, 0));
+        AddCodeLine ("bne %s", LocalLabelName (L1));
+
+        /* strcpy returns argument #1 */
+        *Expr = Arg1.Expr;
+
+    } else if (ED_IsRVal (&Arg2.Expr) && ED_IsLocStack (&Arg2.Expr) &&
+               StackPtr >= -255 &&
+               ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) {
+
+        /* It is possible to just use one index register even if the stack
+         * offset is not zero, by adjusting the offset to the constant
+         * address accordingly. But we cannot do this if the data in
+         * question is in the register space or at an absolute address less
+         * than 256. Register space is zero page, which means that the
+         * address calculation could overflow in the linker.
+         */
+        int AllowOneIndex = !ED_IsLocRegister (&Arg1.Expr) &&
+                            !(ED_IsLocAbs (&Arg1.Expr) && Arg1.Expr.IVal < 256);
+
+        /* Calculate the real stack offset */
+        int Offs = ED_GetStackOffs (&Arg2.Expr, 0);
+
+        /* Drop the generated code */
+        RemoveCode (&Arg1.Expr.Start);
+
+        /* We need labels */
+        L1 = GetLocalLabel ();
+
+        /* Generate strcpy code */
+        AddCodeLine ("ldy #$%02X", (unsigned char) (Offs - 1));
+        if (Offs == 0 || AllowOneIndex) {
+            g_defcodelabel (L1);
+            AddCodeLine ("iny");
+            AddCodeLine ("lda (sp),y");
+            AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, -Offs));
+        } else {
+            AddCodeLine ("ldx #$FF");
+            g_defcodelabel (L1);
+            AddCodeLine ("iny");
+            AddCodeLine ("inx");
+            AddCodeLine ("lda (sp),y");
+            AddCodeLine ("sta %s,x", ED_GetLabelName (&Arg1.Expr, 0));
+        }
+        AddCodeLine ("bne %s", LocalLabelName (L1));
+
+        /* strcpy returns argument #1 */
+        *Expr = Arg1.Expr;
+
+    } else if (ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr) &&
+               ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) &&
+               StackPtr >= -255) {
+
+        /* It is possible to just use one index register even if the stack
+         * offset is not zero, by adjusting the offset to the constant
+         * address accordingly. But we cannot do this if the data in
+         * question is in the register space or at an absolute address less
+         * than 256. Register space is zero page, which means that the
+         * address calculation could overflow in the linker.
+         */
+        int AllowOneIndex = !ED_IsLocRegister (&Arg2.Expr) &&
+                            !(ED_IsLocAbs (&Arg2.Expr) && Arg2.Expr.IVal < 256);
+
+        /* Calculate the real stack offset */
+        int Offs = ED_GetStackOffs (&Arg1.Expr, 0);
+
+        /* Drop the generated code */
+        RemoveCode (&Arg1.Expr.Start);
+
+        /* We need labels */
+        L1 = GetLocalLabel ();
+
+        /* Generate strcpy code */
+        AddCodeLine ("ldy #$%02X", (unsigned char) (Offs - 1));
+        if (Offs == 0 || AllowOneIndex) {
+            g_defcodelabel (L1);
+            AddCodeLine ("iny");
+            AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, -Offs));
+            AddCodeLine ("sta (sp),y");
+        } else {
+            AddCodeLine ("ldx #$FF");
+            g_defcodelabel (L1);
+            AddCodeLine ("iny");
+            AddCodeLine ("inx");
+            AddCodeLine ("lda %s,x", ED_GetLabelName (&Arg2.Expr, 0));
+            AddCodeLine ("sta (sp),y");
+        }
+        AddCodeLine ("bne %s", LocalLabelName (L1));
+
+        /* strcpy returns argument #1 */
+        *Expr = Arg1.Expr;
+
     } else {
 
-       /* Not an array with a constant address. Load parameter into primary */
-       exprhs (CF_NONE, k, &Param);
+        /* The function result is an rvalue in the primary register */
+        ED_MakeRValExpr (Expr);
+        Expr->Type = GetFuncReturn (Expr->Type);
 
     }
 
-    /* Generate the strlen code */
-    g_strlen (CodeFlags, ParamName, Param.ConstVal);
+    /* We expect the closing brace */
+    ConsumeRParen ();
+}
+
+
+
+/*****************************************************************************/
+/*                                  strlen                                   */
+/*****************************************************************************/
+
+
+
+static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
+/* Handle the strlen function */
+{
+    static Type ArgType[] = { TYPE(T_PTR), TYPE(T_CHAR|T_QUAL_CONST), TYPE(T_END) };
+    ExprDesc    Arg;
+    int         IsArray;
+    int         IsPtr;
+    int         IsByteIndex;
+    long        ECount;
+    unsigned    L;
+
+
+
+    /* Setup the argument type string */
+    ArgType[1].C = GetDefaultChar () | T_QUAL_CONST;
+
+    /* Evaluate the parameter */
+    hie1 (&Arg);
+
+    /* Check if the argument is an array. If so, remember the element count.
+     * Otherwise set the element count to undefined.
+     */
+    IsArray = IsTypeArray (Arg.Type);
+    if (IsArray) {
+        ECount = GetElementCount (Arg.Type);
+        if (ECount == FLEXIBLE) {
+            /* Treat as unknown */
+            ECount = UNSPECIFIED;
+        }
+        IsPtr = 0;
+    } else {
+        ECount = UNSPECIFIED;
+        IsPtr  = IsTypePtr (Arg.Type);
+    }
+
+    /* Check if the elements of an array can be addressed by a byte sized
+     * index. This is true if the size of the array is known and less than
+     * 256.
+     */
+    IsByteIndex = (ECount != UNSPECIFIED && ECount < 256);
+
+    /* Do type conversion */
+    TypeConversion (&Arg, ArgType);
+
+    /* If the expression is a literal, and if string literals are read
+     * only, we can calculate the length of the string and remove it
+     * from the literal pool. Otherwise we have to calculate the length
+     * at runtime.
+     */
+    if (ED_IsLocLiteral (&Arg) && IS_Get (&WritableStrings) == 0) {
+
+        /* Constant string literal */
+        ED_MakeConstAbs (Expr, GetLiteralSize (Arg.LVal) - 1, type_size_t);
+
+        /* We don't need the literal any longer */
+        ReleaseLiteral (Arg.LVal);
+
+    /* We will inline strlen for arrays with constant addresses, if either the
+     * inlining was forced on the command line, or the array is smaller than
+     * 256, so the inlining is considered safe.
+     */
+    } else if (ED_IsLocConst (&Arg) && IsArray &&
+               (IS_Get (&InlineStdFuncs) || IsByteIndex)) {
+
+        /* Generate the strlen code */
+        L = GetLocalLabel ();
+        AddCodeLine ("ldy #$FF");
+        g_defcodelabel (L);
+        AddCodeLine ("iny");
+        AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg, 0));
+        AddCodeLine ("bne %s", LocalLabelName (L));
+        AddCodeLine ("tax");
+        AddCodeLine ("tya");
+
+        /* The function result is an rvalue in the primary register */
+        ED_MakeRValExpr (Expr);
+        Expr->Type = type_size_t;
+
+    /* We will inline strlen for arrays on the stack, if the array is
+     * completely within the reach of a byte sized index register.
+     */
+    } else if (ED_IsLocStack (&Arg) && IsArray && IsByteIndex &&
+               (Arg.IVal - StackPtr) + ECount < 256) {
+
+        /* Calculate the true stack offset */
+        int Offs = ED_GetStackOffs (&Arg, 0);
+
+        /* Generate the strlen code */
+        L = GetLocalLabel ();
+        AddCodeLine ("ldx #$FF");
+        AddCodeLine ("ldy #$%02X", (unsigned char) (Offs-1));
+        g_defcodelabel (L);
+        AddCodeLine ("inx");
+        AddCodeLine ("iny");
+        AddCodeLine ("lda (sp),y");
+        AddCodeLine ("bne %s", LocalLabelName (L));
+        AddCodeLine ("txa");
+        AddCodeLine ("ldx #$00");
+
+        /* The function result is an rvalue in the primary register */
+        ED_MakeRValExpr (Expr);
+        Expr->Type = type_size_t;
+
+    /* strlen for a string that is pointed to by a register variable will only
+     * get inlined if requested on the command line, since we cannot know how
+     * big the buffer actually is, so inlining is not always safe.
+     */
+    } else if (ED_IsLocRegister (&Arg) && ED_IsLVal (&Arg) && IsPtr &&
+               IS_Get (&InlineStdFuncs)) {
+
+        /* Generate the strlen code */
+        L = GetLocalLabel ();
+        AddCodeLine ("ldy #$FF");
+        g_defcodelabel (L);
+        AddCodeLine ("iny");
+        AddCodeLine ("lda (%s),y", ED_GetLabelName (&Arg, 0));
+        AddCodeLine ("bne %s", LocalLabelName (L));
+        AddCodeLine ("tax");
+        AddCodeLine ("tya");
+
+        /* The function result is an rvalue in the primary register */
+        ED_MakeRValExpr (Expr);
+        Expr->Type = type_size_t;
+
+    /* Last check: We will inline a generic strlen routine if inlining was
+     * requested on the command line, and the code size factor is more than
+     * 400 (code is 13 bytes vs. 3 for a jsr call).
+     */
+    } else if (IS_Get (&CodeSizeFactor) > 400 && IS_Get (&InlineStdFuncs)) {
+
+        /* Load the expression into the primary */
+        LoadExpr (CF_NONE, &Arg);
+
+        /* Inline the function */
+        L = GetLocalLabel ();
+        AddCodeLine ("sta ptr1");
+        AddCodeLine ("stx ptr1+1");
+        AddCodeLine ("ldy #$FF");
+        g_defcodelabel (L);
+        AddCodeLine ("iny");
+        AddCodeLine ("lda (ptr1),y");
+        AddCodeLine ("bne %s", LocalLabelName (L));
+        AddCodeLine ("tax");
+        AddCodeLine ("tya");
+
+        /* The function result is an rvalue in the primary register */
+        ED_MakeRValExpr (Expr);
+        Expr->Type = type_size_t;
+
+    } else {
+
+        /* Load the expression into the primary */
+        LoadExpr (CF_NONE, &Arg);
+
+        /* Call the strlen function */
+        AddCodeLine ("jsr _%s", Func_strlen);
+
+        /* The function result is an rvalue in the primary register */
+        ED_MakeRValExpr (Expr);
+        Expr->Type = type_size_t;
+
+    }
 
-ExitPoint:
     /* We expect the closing brace */
     ConsumeRParen ();
 }
@@ -295,28 +1290,38 @@ ExitPoint:
 
 
 /*****************************************************************************/
-/*                                  Code                                    */
+/*                                   Code                                    */
 /*****************************************************************************/
 
 
 
-int IsStdFunc (const char* Name)
+int FindStdFunc (const char* Name)
 /* Determine if the given function is a known standard function that may be
- * called in a special way.
+ * called in a special way. If so, return the index, otherwise return -1.
  */
 {
     /* Look into the table for known names */
-    return FindFunc (Name) != 0;
+    struct StdFuncDesc* D =
+        bsearch (Name, StdFuncs, FUNC_COUNT, sizeof (StdFuncs[0]), CmpFunc);
+
+    /* Return the function index or -1 */
+    if (D == 0) {
+        return -1;
+    } else {
+        return D - StdFuncs;
+    }
 }
 
 
 
-void HandleStdFunc (FuncDesc* F, ExprDesc* lval)
+void HandleStdFunc (int Index, FuncDesc* F, ExprDesc* lval)
 /* Generate code for a known standard function. */
 {
+    struct StdFuncDesc* D;
+
     /* Get a pointer to the table entry */
-    struct StdFuncDesc* D = FindFunc ((const char*) lval->Name);
-    CHECK (D != 0);
+    CHECK (Index >= 0 && Index < (int)FUNC_COUNT);
+    D = StdFuncs + Index;
 
     /* Call the handler function */
     D->Handler (F, lval);