]> git.sur5r.net Git - cc65/blob - src/cc65/stdfunc.c
Merge remote-tracking branch 'upstream/master' into creativision
[cc65] / src / cc65 / stdfunc.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                 stdfunc.c                                 */
4 /*                                                                           */
5 /*         Handle inlining of known functions for the cc65 compiler          */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 1998-2010 Ullrich von Bassewitz                                       */
10 /*               Roemerstrasse 52                                            */
11 /*               D-70794 Filderstadt                                         */
12 /* EMail:        uz@cc65.org                                                 */
13 /*                                                                           */
14 /*                                                                           */
15 /* This software is provided 'as-is', without any expressed or implied       */
16 /* warranty.  In no event will the authors be held liable for any damages    */
17 /* arising from the use of this software.                                    */
18 /*                                                                           */
19 /* Permission is granted to anyone to use this software for any purpose,     */
20 /* including commercial applications, and to alter it and redistribute it    */
21 /* freely, subject to the following restrictions:                            */
22 /*                                                                           */
23 /* 1. The origin of this software must not be misrepresented; you must not   */
24 /*    claim that you wrote the original software. If you use this software   */
25 /*    in a product, an acknowledgment in the product documentation would be  */
26 /*    appreciated but is not required.                                       */
27 /* 2. Altered source versions must be plainly marked as such, and must not   */
28 /*    be misrepresented as being the original software.                      */
29 /* 3. This notice may not be removed or altered from any source              */
30 /*    distribution.                                                          */
31 /*                                                                           */
32 /*****************************************************************************/
33
34
35
36 #include <stdlib.h>
37 #include <string.h>
38
39 /* common */
40 #include "attrib.h"
41 #include "check.h"
42
43 /* cc65 */
44 #include "asmcode.h"
45 #include "asmlabel.h"
46 #include "codegen.h"
47 #include "error.h"
48 #include "funcdesc.h"
49 #include "global.h"
50 #include "litpool.h"
51 #include "loadexpr.h"
52 #include "scanner.h"
53 #include "stackptr.h"
54 #include "stdfunc.h"
55 #include "stdnames.h"
56 #include "typeconv.h"
57
58
59
60 /*****************************************************************************/
61 /*                             Function forwards                             */
62 /*****************************************************************************/
63
64
65
66 static void StdFunc_memcpy (FuncDesc*, ExprDesc*);
67 static void StdFunc_memset (FuncDesc*, ExprDesc*);
68 static void StdFunc_strcmp (FuncDesc*, ExprDesc*);
69 static void StdFunc_strcpy (FuncDesc*, ExprDesc*);
70 static void StdFunc_strlen (FuncDesc*, ExprDesc*);
71
72
73
74 /*****************************************************************************/
75 /*                                   Data                                    */
76 /*****************************************************************************/
77
78
79
80 /* Table with all known functions and their handlers. Must be sorted
81 ** alphabetically!
82 */
83 static struct StdFuncDesc {
84     const char*         Name;
85     void                (*Handler) (FuncDesc*, ExprDesc*);
86 } StdFuncs[] = {
87     {   "memcpy",       StdFunc_memcpy          },
88     {   "memset",       StdFunc_memset          },
89     {   "strcmp",       StdFunc_strcmp          },
90     {   "strcpy",       StdFunc_strcpy          },
91     {   "strlen",       StdFunc_strlen          },
92
93 };
94 #define FUNC_COUNT      (sizeof (StdFuncs) / sizeof (StdFuncs[0]))
95
96 typedef struct ArgDesc ArgDesc;
97 struct ArgDesc {
98     const Type* ArgType;        /* Required argument type */
99     ExprDesc    Expr;           /* Argument expression */
100     const Type* Type;           /* The original type before conversion */
101     CodeMark    Load;           /* Start of argument load code */
102     CodeMark    Push;           /* Start of argument push code */
103     CodeMark    End;            /* End of the code for calculation+push */
104     unsigned    Flags;          /* Code generation flags */
105 };
106
107
108
109 /*****************************************************************************/
110 /*                             Helper functions                              */
111 /*****************************************************************************/
112
113
114
115 static int CmpFunc (const void* Key, const void* Elem)
116 /* Compare function for bsearch */
117 {
118     return strcmp ((const char*) Key, ((const struct StdFuncDesc*) Elem)->Name);
119 }
120
121
122
123 static long ArrayElementCount (const ArgDesc* Arg)
124 /* Check if the type of the given argument is an array. If so, and if the
125 ** element count is known, return it. In all other cases, return UNSPECIFIED.
126 */
127 {
128     long Count;
129
130     if (IsTypeArray (Arg->Type)) {
131         Count = GetElementCount (Arg->Type);
132         if (Count == FLEXIBLE) {
133             /* Treat as unknown */
134             Count = UNSPECIFIED;
135         }
136     } else {
137         Count = UNSPECIFIED;
138     }
139     return Count;
140 }
141
142
143
144 static void ParseArg (ArgDesc* Arg, Type* Type)
145 /* Parse one argument but do not push it onto the stack. Make all fields in
146 ** Arg valid.
147 */
148 {
149     /* We have a prototype, so chars may be pushed as chars */
150     Arg->Flags = CF_FORCECHAR;
151
152     /* Remember the required argument type */
153     Arg->ArgType = Type;
154
155     /* Read the expression we're going to pass to the function */
156     MarkedExprWithCheck (hie1, &Arg->Expr);
157
158     /* Remember the actual argument type */
159     Arg->Type = Arg->Expr.Type;
160
161     /* Convert this expression to the expected type */
162     TypeConversion (&Arg->Expr, Type);
163
164     /* Remember the following code position */
165     GetCodePos (&Arg->Load);
166
167     /* If the value is a constant, set the flag, otherwise load it into the
168     ** primary register.
169     */
170     if (ED_IsConstAbsInt (&Arg->Expr) && ED_CodeRangeIsEmpty (&Arg->Expr)) {
171         /* Remember that we have a constant value */
172         Arg->Flags |= CF_CONST;
173     } else {
174         /* Load into the primary */
175         LoadExpr (CF_NONE, &Arg->Expr);
176     }
177
178     /* Remember the following code position */
179     GetCodePos (&Arg->Push);
180     GetCodePos (&Arg->End);
181
182     /* Use the type of the argument for the push */
183     Arg->Flags |= TypeOf (Arg->Expr.Type);
184 }
185
186
187
188 void AddCmpCodeIfSizeNot256 (const char* Code, long Size)
189 /* Add a line of Assembly code that compares an index register
190 ** only if it isn't comparing to #<256.  (If the next line
191 ** is "bne", then this will avoid a redundant line.)
192 */
193 {
194     if (Size != 256) {
195         AddCodeLine (Code, (unsigned int)Size);
196     }
197 }
198
199
200
201 /*****************************************************************************/
202 /*                                  memcpy                                   */
203 /*****************************************************************************/
204
205
206
207 static void StdFunc_memcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
208 /* Handle the memcpy function */
209 {
210     /* Argument types: (void*, const void*, size_t) */
211     static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_VOID), TYPE(T_END) };
212     static Type Arg2Type[] = { TYPE(T_PTR), TYPE(T_VOID|T_QUAL_CONST), TYPE(T_END) };
213     static Type Arg3Type[] = { TYPE(T_SIZE_T), TYPE(T_END) };
214
215     ArgDesc  Arg1, Arg2, Arg3;
216     unsigned ParamSize = 0;
217     unsigned Label;
218     int      Offs;
219
220     /* Argument #1 */
221     ParseArg (&Arg1, Arg1Type);
222     g_push (Arg1.Flags, Arg1.Expr.IVal);
223     GetCodePos (&Arg1.End);
224     ParamSize += SizeOf (Arg1Type);
225     ConsumeComma ();
226
227     /* Argument #2 */
228     ParseArg (&Arg2, Arg2Type);
229     g_push (Arg2.Flags, Arg2.Expr.IVal);
230     GetCodePos (&Arg2.End);
231     ParamSize += SizeOf (Arg2Type);
232     ConsumeComma ();
233
234     /* Argument #3. Since memcpy is a fastcall function, we must load the
235     ** arg into the primary if it is not already there. This parameter is
236     ** also ignored for the calculation of the parameter size, since it is
237     ** not passed via the stack.
238     */
239     ParseArg (&Arg3, Arg3Type);
240     if (Arg3.Flags & CF_CONST) {
241         LoadExpr (CF_NONE, &Arg3.Expr);
242     }
243
244     /* Emit the actual function call. This will also cleanup the stack. */
245     g_call (CF_FIXARGC, Func_memcpy, ParamSize);
246
247     if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal == 0) {
248
249         /* memcpy has been called with a count argument of zero */
250         Warning ("Call to memcpy has no effect");
251
252         /* Remove all of the generated code but the load of the first
253         ** argument, which is what memcpy returns.
254         */
255         RemoveCode (&Arg1.Push);
256
257         /* Set the function result to the first argument */
258         *Expr = Arg1.Expr;
259
260         /* Bail out, no need for further improvements */
261         goto ExitPoint;
262     }
263
264     /* We've generated the complete code for the function now and know the
265     ** types of all parameters. Check for situations where better code can
266     ** be generated. If such a situation is detected, throw away the
267     ** generated, and emit better code.
268     */
269     if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
270         ((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) ||
271          (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) &&
272         ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) ||
273          (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)))) {
274
275         int Reg1 = ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr);
276         int Reg2 = ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr);
277
278         /* Drop the generated code */
279         RemoveCode (&Arg1.Expr.Start);
280
281         /* We need a label */
282         Label = GetLocalLabel ();
283
284         /* Generate memcpy code */
285         if (Arg3.Expr.IVal <= 127) {
286
287             AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
288             g_defcodelabel (Label);
289             if (Reg2) {
290                 AddCodeLine ("lda (%s),y", ED_GetLabelName (&Arg2.Expr, 0));
291             } else {
292                 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, 0));
293             }
294             if (Reg1) {
295                 AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
296             } else {
297                 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
298             }
299             AddCodeLine ("dey");
300             AddCodeLine ("bpl %s", LocalLabelName (Label));
301
302         } else {
303
304             AddCodeLine ("ldy #$00");
305             g_defcodelabel (Label);
306             if (Reg2) {
307                 AddCodeLine ("lda (%s),y", ED_GetLabelName (&Arg2.Expr, 0));
308             } else {
309                 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, 0));
310             }
311             if (Reg1) {
312                 AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
313             } else {
314                 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
315             }
316             AddCodeLine ("iny");
317             AddCmpCodeIfSizeNot256 ("cpy #$%02X", Arg3.Expr.IVal);
318             AddCodeLine ("bne %s", LocalLabelName (Label));
319
320         }
321
322         /* memcpy returns the address, so the result is actually identical
323         ** to the first argument.
324         */
325         *Expr = Arg1.Expr;
326
327     } else if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
328                ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr) &&
329                ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) &&
330                (Arg1.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256) {
331
332         /* It is possible to just use one index register even if the stack
333         ** offset is not zero, by adjusting the offset to the constant
334         ** address accordingly. But we cannot do this if the data in
335         ** question is in the register space or at an absolute address less
336         ** than 256. Register space is zero page, which means that the
337         ** address calculation could overflow in the linker.
338         */
339         int AllowOneIndex = !ED_IsLocRegister (&Arg2.Expr) &&
340                             !(ED_IsLocAbs (&Arg2.Expr) && Arg2.Expr.IVal < 256);
341
342         /* Calculate the real stack offset */
343         Offs = ED_GetStackOffs (&Arg1.Expr, 0);
344
345         /* Drop the generated code */
346         RemoveCode (&Arg1.Expr.Start);
347
348         /* We need a label */
349         Label = GetLocalLabel ();
350
351         /* Generate memcpy code */
352         if (Arg3.Expr.IVal <= 127 && !AllowOneIndex) {
353
354             if (Offs == 0) {
355                 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal - 1));
356                 g_defcodelabel (Label);
357                 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, -Offs));
358                 AddCodeLine ("sta (sp),y");
359                 AddCodeLine ("dey");
360                 AddCodeLine ("bpl %s", LocalLabelName (Label));
361             } else {
362                 AddCodeLine ("ldx #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
363                 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal - 1));
364                 g_defcodelabel (Label);
365                 AddCodeLine ("lda %s,x", ED_GetLabelName (&Arg2.Expr, 0));
366                 AddCodeLine ("sta (sp),y");
367                 AddCodeLine ("dey");
368                 AddCodeLine ("dex");
369                 AddCodeLine ("bpl %s", LocalLabelName (Label));
370             }
371
372         } else {
373
374             if (Offs == 0 || AllowOneIndex) {
375                 AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
376                 g_defcodelabel (Label);
377                 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, -Offs));
378                 AddCodeLine ("sta (sp),y");
379                 AddCodeLine ("iny");
380                 AddCmpCodeIfSizeNot256 ("cpy #$%02X", Offs + Arg3.Expr.IVal);
381                 AddCodeLine ("bne %s", LocalLabelName (Label));
382             } else {
383                 AddCodeLine ("ldx #$00");
384                 AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
385                 g_defcodelabel (Label);
386                 AddCodeLine ("lda %s,x", ED_GetLabelName (&Arg2.Expr, 0));
387                 AddCodeLine ("sta (sp),y");
388                 AddCodeLine ("iny");
389                 AddCodeLine ("inx");
390                 AddCmpCodeIfSizeNot256 ("cpx #$%02X", Arg3.Expr.IVal);
391                 AddCodeLine ("bne %s", LocalLabelName (Label));
392             }
393
394         }
395
396         /* memcpy returns the address, so the result is actually identical
397         ** to the first argument.
398         */
399         *Expr = Arg1.Expr;
400
401     } else if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
402                ED_IsRVal (&Arg2.Expr) && ED_IsLocStack (&Arg2.Expr) &&
403                (Arg2.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256 &&
404                ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) {
405
406         /* It is possible to just use one index register even if the stack
407         ** offset is not zero, by adjusting the offset to the constant
408         ** address accordingly. But we cannot do this if the data in
409         ** question is in the register space or at an absolute address less
410         ** than 256. Register space is zero page, which means that the
411         ** address calculation could overflow in the linker.
412         */
413         int AllowOneIndex = !ED_IsLocRegister (&Arg1.Expr) &&
414                             !(ED_IsLocAbs (&Arg1.Expr) && Arg1.Expr.IVal < 256);
415
416         /* Calculate the real stack offset */
417         Offs = ED_GetStackOffs (&Arg2.Expr, 0);
418
419         /* Drop the generated code */
420         RemoveCode (&Arg1.Expr.Start);
421
422         /* We need a label */
423         Label = GetLocalLabel ();
424
425         /* Generate memcpy code */
426         if (Arg3.Expr.IVal <= 127 && !AllowOneIndex) {
427
428             if (Offs == 0) {
429                 AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal - 1));
430                 g_defcodelabel (Label);
431                 AddCodeLine ("lda (sp),y");
432                 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
433                 AddCodeLine ("dey");
434                 AddCodeLine ("bpl %s", LocalLabelName (Label));
435             } else {
436                 AddCodeLine ("ldx #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
437                 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal - 1));
438                 g_defcodelabel (Label);
439                 AddCodeLine ("lda (sp),y");
440                 AddCodeLine ("sta %s,x", ED_GetLabelName (&Arg1.Expr, 0));
441                 AddCodeLine ("dey");
442                 AddCodeLine ("dex");
443                 AddCodeLine ("bpl %s", LocalLabelName (Label));
444             }
445
446         } else {
447
448             if (Offs == 0 || AllowOneIndex) {
449                 AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
450                 g_defcodelabel (Label);
451                 AddCodeLine ("lda (sp),y");
452                 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, -Offs));
453                 AddCodeLine ("iny");
454                 AddCmpCodeIfSizeNot256 ("cpy #$%02X", Offs + Arg3.Expr.IVal);
455                 AddCodeLine ("bne %s", LocalLabelName (Label));
456             } else {
457                 AddCodeLine ("ldx #$00");
458                 AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
459                 g_defcodelabel (Label);
460                 AddCodeLine ("lda (sp),y");
461                 AddCodeLine ("sta %s,x", ED_GetLabelName (&Arg1.Expr, 0));
462                 AddCodeLine ("iny");
463                 AddCodeLine ("inx");
464                 AddCmpCodeIfSizeNot256 ("cpx #$%02X", Arg3.Expr.IVal);
465                 AddCodeLine ("bne %s", LocalLabelName (Label));
466             }
467
468         }
469
470         /* memcpy returns the address, so the result is actually identical
471         ** to the first argument.
472         */
473         *Expr = Arg1.Expr;
474
475     } else if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256   &&
476                ED_IsRVal (&Arg2.Expr) && ED_IsLocStack (&Arg2.Expr)     &&
477                (Offs = ED_GetStackOffs (&Arg2.Expr, 0)) == 0) {
478
479         /* Drop the generated code but leave the load of the first argument*/
480         RemoveCode (&Arg1.Push);
481
482         /* We need a label */
483         Label = GetLocalLabel ();
484
485         /* Generate memcpy code */
486         AddCodeLine ("sta ptr1");
487         AddCodeLine ("stx ptr1+1");
488         if (Arg3.Expr.IVal <= 127) {
489             AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal - 1));
490             g_defcodelabel (Label);
491             AddCodeLine ("lda (sp),y");
492             AddCodeLine ("sta (ptr1),y");
493             AddCodeLine ("dey");
494             AddCodeLine ("bpl %s", LocalLabelName (Label));
495         } else {
496             AddCodeLine ("ldy #$00");
497             g_defcodelabel (Label);
498             AddCodeLine ("lda (sp),y");
499             AddCodeLine ("sta (ptr1),y");
500             AddCodeLine ("iny");
501             AddCmpCodeIfSizeNot256 ("cpy #$%02X", Arg3.Expr.IVal);
502             AddCodeLine ("bne %s", LocalLabelName (Label));
503         }
504
505         /* Reload result - X hasn't changed by the code above */
506         AddCodeLine ("lda ptr1");
507
508         /* The function result is an rvalue in the primary register */
509         ED_MakeRValExpr (Expr);
510         Expr->Type = GetFuncReturn (Expr->Type);
511
512     } else {
513
514         /* The function result is an rvalue in the primary register */
515         ED_MakeRValExpr (Expr);
516         Expr->Type = GetFuncReturn (Expr->Type);
517
518     }
519
520 ExitPoint:
521     /* We expect the closing brace */
522     ConsumeRParen ();
523 }
524
525
526
527 /*****************************************************************************/
528 /*                                  memset                                   */
529 /*****************************************************************************/
530
531
532
533 static void StdFunc_memset (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
534 /* Handle the memset function */
535 {
536     /* Argument types: (void*, int, size_t) */
537     static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_VOID), TYPE(T_END) };
538     static Type Arg2Type[] = { TYPE(T_INT), TYPE(T_END) };
539     static Type Arg3Type[] = { TYPE(T_SIZE_T), TYPE(T_END) };
540
541     ArgDesc  Arg1, Arg2, Arg3;
542     int      MemSet    = 1;             /* Use real memset if true */
543     unsigned ParamSize = 0;
544     unsigned Label;
545
546     /* Argument #1 */
547     ParseArg (&Arg1, Arg1Type);
548     g_push (Arg1.Flags, Arg1.Expr.IVal);
549     GetCodePos (&Arg1.End);
550     ParamSize += SizeOf (Arg1Type);
551     ConsumeComma ();
552
553     /* Argument #2. This argument is special in that we will call another
554     ** function if it is a constant zero.
555     */
556     ParseArg (&Arg2, Arg2Type);
557     if ((Arg2.Flags & CF_CONST) != 0 && Arg2.Expr.IVal == 0) {
558         /* Don't call memset, call bzero instead */
559         MemSet = 0;
560     } else {
561         /* Push the argument */
562         g_push (Arg2.Flags, Arg2.Expr.IVal);
563         GetCodePos (&Arg2.End);
564         ParamSize += SizeOf (Arg2Type);
565     }
566     ConsumeComma ();
567
568     /* Argument #3. Since memset is a fastcall function, we must load the
569     ** arg into the primary if it is not already there. This parameter is
570     ** also ignored for the calculation of the parameter size, since it is
571     ** not passed via the stack.
572     */
573     ParseArg (&Arg3, Arg3Type);
574     if (Arg3.Flags & CF_CONST) {
575         LoadExpr (CF_NONE, &Arg3.Expr);
576     }
577
578     /* Emit the actual function call. This will also cleanup the stack. */
579     g_call (CF_FIXARGC, MemSet? Func_memset : Func__bzero, ParamSize);
580
581     if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal == 0) {
582
583         /* memset has been called with a count argument of zero */
584         Warning ("Call to memset has no effect");
585
586         /* Remove all of the generated code but the load of the first
587         ** argument, which is what memset returns.
588         */
589         RemoveCode (&Arg1.Push);
590
591         /* Set the function result to the first argument */
592         *Expr = Arg1.Expr;
593
594         /* Bail out, no need for further improvements */
595         goto ExitPoint;
596     }
597
598     /* We've generated the complete code for the function now and know the
599     ** types of all parameters. Check for situations where better code can
600     ** be generated. If such a situation is detected, throw away the
601     ** generated, and emit better code.
602     ** Note: Lots of improvements would be possible here, but I will
603     ** concentrate on the most common case: memset with arguments 2 and 3
604     ** being constant numerical values. Some checks have shown that this
605     ** covers nearly 90% of all memset calls.
606     */
607     if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
608         ED_IsConstAbsInt (&Arg2.Expr) &&
609         ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) ||
610          (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)))) {
611
612         int Reg = ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr);
613
614         /* Drop the generated code */
615         RemoveCode (&Arg1.Expr.Start);
616
617         /* We need a label */
618         Label = GetLocalLabel ();
619
620         /* Generate memset code */
621         if (Arg3.Expr.IVal <= 127) {
622
623             AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
624             AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
625             g_defcodelabel (Label);
626             if (Reg) {
627                 AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
628             } else {
629                 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
630             }
631             AddCodeLine ("dey");
632             AddCodeLine ("bpl %s", LocalLabelName (Label));
633
634         } else {
635
636             AddCodeLine ("ldy #$00");
637             AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
638             g_defcodelabel (Label);
639             if (Reg) {
640                 AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
641             } else {
642                 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
643             }
644             AddCodeLine ("iny");
645             AddCmpCodeIfSizeNot256 ("cpy #$%02X", Arg3.Expr.IVal);
646             AddCodeLine ("bne %s", LocalLabelName (Label));
647
648         }
649
650         /* memset returns the address, so the result is actually identical
651         ** to the first argument.
652         */
653         *Expr = Arg1.Expr;
654
655     } else if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
656                ED_IsConstAbsInt (&Arg2.Expr) &&
657                ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) &&
658                (Arg1.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256) {
659
660         /* Calculate the real stack offset */
661         int Offs = ED_GetStackOffs (&Arg1.Expr, 0);
662
663         /* Drop the generated code */
664         RemoveCode (&Arg1.Expr.Start);
665
666         /* We need a label */
667         Label = GetLocalLabel ();
668
669         /* Generate memset code */
670         AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
671         AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
672         g_defcodelabel (Label);
673         AddCodeLine ("sta (sp),y");
674         AddCodeLine ("iny");
675         AddCmpCodeIfSizeNot256 ("cpy #$%02X", Offs + Arg3.Expr.IVal);
676         AddCodeLine ("bne %s", LocalLabelName (Label));
677
678         /* memset returns the address, so the result is actually identical
679         ** to the first argument.
680         */
681         *Expr = Arg1.Expr;
682
683     } else if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
684                ED_IsConstAbsInt (&Arg2.Expr) &&
685                (Arg2.Expr.IVal != 0 || IS_Get (&CodeSizeFactor) > 200)) {
686
687         /* Remove all of the generated code but the load of the first
688         ** argument.
689         */
690         RemoveCode (&Arg1.Push);
691
692         /* We need a label */
693         Label = GetLocalLabel ();
694
695         /* Generate code */
696         AddCodeLine ("sta ptr1");
697         AddCodeLine ("stx ptr1+1");
698         if (Arg3.Expr.IVal <= 127) {
699             AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
700             AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
701             g_defcodelabel (Label);
702             AddCodeLine ("sta (ptr1),y");
703             AddCodeLine ("dey");
704             AddCodeLine ("bpl %s", LocalLabelName (Label));
705         } else {
706             AddCodeLine ("ldy #$00");
707             AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
708             g_defcodelabel (Label);
709             AddCodeLine ("sta (ptr1),y");
710             AddCodeLine ("iny");
711             AddCmpCodeIfSizeNot256 ("cpy #$%02X", Arg3.Expr.IVal);
712             AddCodeLine ("bne %s", LocalLabelName (Label));
713         }
714
715         /* Load the function result pointer into a/x (x is still valid). This
716         ** code will get removed by the optimizer if it is not used later.
717         */
718         AddCodeLine ("lda ptr1");
719
720         /* The function result is an rvalue in the primary register */
721         ED_MakeRValExpr (Expr);
722         Expr->Type = GetFuncReturn (Expr->Type);
723
724     } else {
725
726         /* The function result is an rvalue in the primary register */
727         ED_MakeRValExpr (Expr);
728         Expr->Type = GetFuncReturn (Expr->Type);
729
730     }
731
732 ExitPoint:
733     /* We expect the closing brace */
734     ConsumeRParen ();
735 }
736
737
738
739 /*****************************************************************************/
740 /*                                  strcmp                                   */
741 /*****************************************************************************/
742
743
744
745 static void StdFunc_strcmp (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
746 /* Handle the strcmp function */
747 {
748     /* Argument types: (const char*, const char*) */
749     static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_CHAR|T_QUAL_CONST), TYPE(T_END) };
750     static Type Arg2Type[] = { TYPE(T_PTR), TYPE(T_CHAR|T_QUAL_CONST), TYPE(T_END) };
751
752     ArgDesc  Arg1, Arg2;
753     unsigned ParamSize = 0;
754     long     ECount1;
755     long     ECount2;
756     int      IsArray;
757     int      Offs;
758
759     /* Setup the argument type string */
760     Arg1Type[1].C = GetDefaultChar () | T_QUAL_CONST;
761     Arg2Type[1].C = GetDefaultChar () | T_QUAL_CONST;
762
763     /* Argument #1 */
764     ParseArg (&Arg1, Arg1Type);
765     g_push (Arg1.Flags, Arg1.Expr.IVal);
766     ParamSize += SizeOf (Arg1Type);
767     ConsumeComma ();
768
769     /* Argument #2. */
770     ParseArg (&Arg2, Arg2Type);
771
772     /* Since strcmp is a fastcall function, we must load the
773     ** arg into the primary if it is not already there. This parameter is
774     ** also ignored for the calculation of the parameter size, since it is
775     ** not passed via the stack.
776     */
777     if (Arg2.Flags & CF_CONST) {
778         LoadExpr (CF_NONE, &Arg2.Expr);
779     }
780
781     /* Emit the actual function call. This will also cleanup the stack. */
782     g_call (CF_FIXARGC, Func_strcmp, ParamSize);
783
784     /* Get the element counts of the arguments. Then get the larger of the
785     ** two into ECount1. This removes FLEXIBLE and UNSPECIFIED automatically
786     */
787     ECount1 = ArrayElementCount (&Arg1);
788     ECount2 = ArrayElementCount (&Arg2);
789     if (ECount2 > ECount1) {
790         ECount1 = ECount2;
791     }
792
793     /* If the second argument is the empty string literal, we can generate
794     ** more efficient code.
795     */
796     if (ED_IsLocLiteral (&Arg2.Expr) &&
797         IS_Get (&WritableStrings) == 0 &&
798         GetLiteralSize (Arg2.Expr.LVal) == 1 &&
799         GetLiteralStr (Arg2.Expr.LVal)[0] == '\0') {
800
801         /* Drop the generated code so we have the first argument in the
802         ** primary
803         */
804         RemoveCode (&Arg1.Push);
805
806         /* We don't need the literal any longer */
807         ReleaseLiteral (Arg2.Expr.LVal);
808
809         /* We do now have Arg1 in the primary. Load the first character from
810         ** this string and cast to int. This is the function result.
811         */
812         IsArray = IsTypeArray (Arg1.Type) && ED_IsRVal (&Arg1.Expr);
813         if (IsArray && ED_IsLocStack (&Arg1.Expr) &&
814             (Offs = ED_GetStackOffs (&Arg1.Expr, 0) < 256)) {
815             /* Drop the generated code */
816             RemoveCode (&Arg1.Load);
817
818             /* Generate code */
819             AddCodeLine ("ldy #$%02X", Offs);
820             AddCodeLine ("ldx #$00");
821             AddCodeLine ("lda (sp),y");
822         } else if (IsArray && ED_IsLocConst (&Arg1.Expr)) {
823             /* Drop the generated code */
824             RemoveCode (&Arg1.Load);
825
826             /* Generate code */
827             AddCodeLine ("ldx #$00");
828             AddCodeLine ("lda %s", ED_GetLabelName (&Arg1.Expr, 0));
829         } else {
830             /* Drop part of the generated code so we have the first argument
831             ** in the primary
832             */
833             RemoveCode (&Arg1.Push);
834
835             /* Fetch the first char */
836             g_getind (CF_CHAR | CF_UNSIGNED, 0);
837         }
838
839     } else if ((IS_Get (&CodeSizeFactor) >= 165) &&
840                ((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) ||
841                 (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) &&
842                ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) ||
843                 (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr))) &&
844                (IS_Get (&InlineStdFuncs) || (ECount1 > 0 && ECount1 < 256))) {
845
846
847         unsigned    Entry, Loop, Fin;   /* Labels */
848         const char* Load;
849         const char* Compare;
850
851         if (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)) {
852             Load = "lda (%s),y";
853         } else {
854             Load = "lda %s,y";
855         }
856         if (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr)) {
857             Compare = "cmp (%s),y";
858         } else {
859             Compare = "cmp %s,y";
860         }
861
862         /* Drop the generated code */
863         RemoveCode (&Arg1.Expr.Start);
864
865         /* We need labels */
866         Entry = GetLocalLabel ();
867         Loop  = GetLocalLabel ();
868         Fin   = GetLocalLabel ();
869
870         /* Generate strcmp code */
871         AddCodeLine ("ldy #$00");
872         AddCodeLine ("beq %s", LocalLabelName (Entry));
873         g_defcodelabel (Loop);
874         AddCodeLine ("tax");
875         AddCodeLine ("beq %s", LocalLabelName (Fin));
876         AddCodeLine ("iny");
877         g_defcodelabel (Entry);
878         AddCodeLine (Load, ED_GetLabelName (&Arg1.Expr, 0));
879         AddCodeLine (Compare, ED_GetLabelName (&Arg2.Expr, 0));
880         AddCodeLine ("beq %s", LocalLabelName (Loop));
881         AddCodeLine ("ldx #$01");
882         AddCodeLine ("bcs %s", LocalLabelName (Fin));
883         AddCodeLine ("ldx #$FF");
884         g_defcodelabel (Fin);
885
886     } else if ((IS_Get (&CodeSizeFactor) > 190) &&
887                ((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) ||
888                 (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) &&
889                (IS_Get (&InlineStdFuncs) || (ECount1 > 0 && ECount1 < 256))) {
890
891
892         unsigned    Entry, Loop, Fin;   /* Labels */
893         const char* Compare;
894
895         if (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr)) {
896             Compare = "cmp (%s),y";
897         } else {
898             Compare = "cmp %s,y";
899         }
900
901         /* Drop the generated code */
902         RemoveCode (&Arg1.Push);
903
904         /* We need labels */
905         Entry = GetLocalLabel ();
906         Loop  = GetLocalLabel ();
907         Fin   = GetLocalLabel ();
908
909         /* Store Arg1 into ptr1 */
910         AddCodeLine ("sta ptr1");
911         AddCodeLine ("stx ptr1+1");
912
913         /* Generate strcmp code */
914         AddCodeLine ("ldy #$00");
915         AddCodeLine ("beq %s", LocalLabelName (Entry));
916         g_defcodelabel (Loop);
917         AddCodeLine ("tax");
918         AddCodeLine ("beq %s", LocalLabelName (Fin));
919         AddCodeLine ("iny");
920         g_defcodelabel (Entry);
921         AddCodeLine ("lda (ptr1),y");
922         AddCodeLine (Compare, ED_GetLabelName (&Arg2.Expr, 0));
923         AddCodeLine ("beq %s", LocalLabelName (Loop));
924         AddCodeLine ("ldx #$01");
925         AddCodeLine ("bcs %s", LocalLabelName (Fin));
926         AddCodeLine ("ldx #$FF");
927         g_defcodelabel (Fin);
928
929     }
930
931     /* The function result is an rvalue in the primary register */
932     ED_MakeRValExpr (Expr);
933     Expr->Type = GetFuncReturn (Expr->Type);
934
935     /* We expect the closing brace */
936     ConsumeRParen ();
937 }
938
939
940
941 /*****************************************************************************/
942 /*                                  strcpy                                   */
943 /*****************************************************************************/
944
945
946
947 static void StdFunc_strcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
948 /* Handle the strcpy function */
949 {
950     /* Argument types: (char*, const char*) */
951     static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_CHAR), TYPE(T_END) };
952     static Type Arg2Type[] = { TYPE(T_PTR), TYPE(T_CHAR|T_QUAL_CONST), TYPE(T_END) };
953
954     ArgDesc  Arg1, Arg2;
955     unsigned ParamSize = 0;
956     long     ECount;
957     unsigned L1;
958
959     /* Setup the argument type string */
960     Arg1Type[1].C = GetDefaultChar ();
961     Arg2Type[1].C = GetDefaultChar () | T_QUAL_CONST;
962
963     /* Argument #1 */
964     ParseArg (&Arg1, Arg1Type);
965     g_push (Arg1.Flags, Arg1.Expr.IVal);
966     GetCodePos (&Arg1.End);
967     ParamSize += SizeOf (Arg1Type);
968     ConsumeComma ();
969
970     /* Argument #2. Since strcpy is a fastcall function, we must load the
971     ** arg into the primary if it is not already there. This parameter is
972     ** also ignored for the calculation of the parameter size, since it is
973     ** not passed via the stack.
974     */
975     ParseArg (&Arg2, Arg2Type);
976     if (Arg2.Flags & CF_CONST) {
977         LoadExpr (CF_NONE, &Arg2.Expr);
978     }
979
980     /* Emit the actual function call. This will also cleanup the stack. */
981     g_call (CF_FIXARGC, Func_strcpy, ParamSize);
982
983     /* Get the element count of argument 1 if it is an array */
984     ECount = ArrayElementCount (&Arg1);
985
986     /* We've generated the complete code for the function now and know the
987     ** types of all parameters. Check for situations where better code can
988     ** be generated. If such a situation is detected, throw away the
989     ** generated, and emit better code.
990     */
991     if (((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) ||
992          (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) &&
993         ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) ||
994          (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr))) &&
995         (IS_Get (&InlineStdFuncs) ||
996         (ECount != UNSPECIFIED && ECount < 256))) {
997
998         const char* Load;
999         const char* Store;
1000         if (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr)) {
1001             Load = "lda (%s),y";
1002         } else {
1003             Load = "lda %s,y";
1004         }
1005         if (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)) {
1006             Store = "sta (%s),y";
1007         } else {
1008             Store = "sta %s,y";
1009         }
1010
1011         /* Drop the generated code */
1012         RemoveCode (&Arg1.Expr.Start);
1013
1014         /* We need labels */
1015         L1 = GetLocalLabel ();
1016
1017         /* Generate strcpy code */
1018         AddCodeLine ("ldy #$FF");
1019         g_defcodelabel (L1);
1020         AddCodeLine ("iny");
1021         AddCodeLine (Load, ED_GetLabelName (&Arg2.Expr, 0));
1022         AddCodeLine (Store, ED_GetLabelName (&Arg1.Expr, 0));
1023         AddCodeLine ("bne %s", LocalLabelName (L1));
1024
1025         /* strcpy returns argument #1 */
1026         *Expr = Arg1.Expr;
1027
1028     } else if (ED_IsRVal (&Arg2.Expr) && ED_IsLocStack (&Arg2.Expr) &&
1029                StackPtr >= -255 &&
1030                ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) {
1031
1032         /* It is possible to just use one index register even if the stack
1033         ** offset is not zero, by adjusting the offset to the constant
1034         ** address accordingly. But we cannot do this if the data in
1035         ** question is in the register space or at an absolute address less
1036         ** than 256. Register space is zero page, which means that the
1037         ** address calculation could overflow in the linker.
1038         */
1039         int AllowOneIndex = !ED_IsLocRegister (&Arg1.Expr) &&
1040                             !(ED_IsLocAbs (&Arg1.Expr) && Arg1.Expr.IVal < 256);
1041
1042         /* Calculate the real stack offset */
1043         int Offs = ED_GetStackOffs (&Arg2.Expr, 0);
1044
1045         /* Drop the generated code */
1046         RemoveCode (&Arg1.Expr.Start);
1047
1048         /* We need labels */
1049         L1 = GetLocalLabel ();
1050
1051         /* Generate strcpy code */
1052         AddCodeLine ("ldy #$%02X", (unsigned char) (Offs - 1));
1053         if (Offs == 0 || AllowOneIndex) {
1054             g_defcodelabel (L1);
1055             AddCodeLine ("iny");
1056             AddCodeLine ("lda (sp),y");
1057             AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, -Offs));
1058         } else {
1059             AddCodeLine ("ldx #$FF");
1060             g_defcodelabel (L1);
1061             AddCodeLine ("iny");
1062             AddCodeLine ("inx");
1063             AddCodeLine ("lda (sp),y");
1064             AddCodeLine ("sta %s,x", ED_GetLabelName (&Arg1.Expr, 0));
1065         }
1066         AddCodeLine ("bne %s", LocalLabelName (L1));
1067
1068         /* strcpy returns argument #1 */
1069         *Expr = Arg1.Expr;
1070
1071     } else if (ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr) &&
1072                ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) &&
1073                StackPtr >= -255) {
1074
1075         /* It is possible to just use one index register even if the stack
1076         ** offset is not zero, by adjusting the offset to the constant
1077         ** address accordingly. But we cannot do this if the data in
1078         ** question is in the register space or at an absolute address less
1079         ** than 256. Register space is zero page, which means that the
1080         ** address calculation could overflow in the linker.
1081         */
1082         int AllowOneIndex = !ED_IsLocRegister (&Arg2.Expr) &&
1083                             !(ED_IsLocAbs (&Arg2.Expr) && Arg2.Expr.IVal < 256);
1084
1085         /* Calculate the real stack offset */
1086         int Offs = ED_GetStackOffs (&Arg1.Expr, 0);
1087
1088         /* Drop the generated code */
1089         RemoveCode (&Arg1.Expr.Start);
1090
1091         /* We need labels */
1092         L1 = GetLocalLabel ();
1093
1094         /* Generate strcpy code */
1095         AddCodeLine ("ldy #$%02X", (unsigned char) (Offs - 1));
1096         if (Offs == 0 || AllowOneIndex) {
1097             g_defcodelabel (L1);
1098             AddCodeLine ("iny");
1099             AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, -Offs));
1100             AddCodeLine ("sta (sp),y");
1101         } else {
1102             AddCodeLine ("ldx #$FF");
1103             g_defcodelabel (L1);
1104             AddCodeLine ("iny");
1105             AddCodeLine ("inx");
1106             AddCodeLine ("lda %s,x", ED_GetLabelName (&Arg2.Expr, 0));
1107             AddCodeLine ("sta (sp),y");
1108         }
1109         AddCodeLine ("bne %s", LocalLabelName (L1));
1110
1111         /* strcpy returns argument #1 */
1112         *Expr = Arg1.Expr;
1113
1114     } else {
1115
1116         /* The function result is an rvalue in the primary register */
1117         ED_MakeRValExpr (Expr);
1118         Expr->Type = GetFuncReturn (Expr->Type);
1119
1120     }
1121
1122     /* We expect the closing brace */
1123     ConsumeRParen ();
1124 }
1125
1126
1127
1128 /*****************************************************************************/
1129 /*                                  strlen                                   */
1130 /*****************************************************************************/
1131
1132
1133
1134 static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
1135 /* Handle the strlen function */
1136 {
1137     static Type ArgType[] = { TYPE(T_PTR), TYPE(T_CHAR|T_QUAL_CONST), TYPE(T_END) };
1138     ExprDesc    Arg;
1139     int         IsArray;
1140     int         IsPtr;
1141     int         IsByteIndex;
1142     long        ECount;
1143     unsigned    L;
1144
1145
1146
1147     /* Setup the argument type string */
1148     ArgType[1].C = GetDefaultChar () | T_QUAL_CONST;
1149
1150     /* Evaluate the parameter */
1151     hie1 (&Arg);
1152
1153     /* Check if the argument is an array. If so, remember the element count.
1154     ** Otherwise set the element count to undefined.
1155     */
1156     IsArray = IsTypeArray (Arg.Type);
1157     if (IsArray) {
1158         ECount = GetElementCount (Arg.Type);
1159         if (ECount == FLEXIBLE) {
1160             /* Treat as unknown */
1161             ECount = UNSPECIFIED;
1162         }
1163         IsPtr = 0;
1164     } else {
1165         ECount = UNSPECIFIED;
1166         IsPtr  = IsTypePtr (Arg.Type);
1167     }
1168
1169     /* Check if the elements of an array can be addressed by a byte sized
1170     ** index. This is true if the size of the array is known and less than
1171     ** 256.
1172     */
1173     IsByteIndex = (ECount != UNSPECIFIED && ECount < 256);
1174
1175     /* Do type conversion */
1176     TypeConversion (&Arg, ArgType);
1177
1178     /* If the expression is a literal, and if string literals are read
1179     ** only, we can calculate the length of the string and remove it
1180     ** from the literal pool. Otherwise we have to calculate the length
1181     ** at runtime.
1182     */
1183     if (ED_IsLocLiteral (&Arg) && IS_Get (&WritableStrings) == 0) {
1184
1185         /* Constant string literal */
1186         ED_MakeConstAbs (Expr, GetLiteralSize (Arg.LVal) - 1, type_size_t);
1187
1188         /* We don't need the literal any longer */
1189         ReleaseLiteral (Arg.LVal);
1190
1191     /* We will inline strlen for arrays with constant addresses, if either the
1192     ** inlining was forced on the command line, or the array is smaller than
1193     ** 256, so the inlining is considered safe.
1194     */
1195     } else if (ED_IsLocConst (&Arg) && IsArray &&
1196                (IS_Get (&InlineStdFuncs) || IsByteIndex)) {
1197
1198         /* Generate the strlen code */
1199         L = GetLocalLabel ();
1200         AddCodeLine ("ldy #$FF");
1201         g_defcodelabel (L);
1202         AddCodeLine ("iny");
1203         AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg, 0));
1204         AddCodeLine ("bne %s", LocalLabelName (L));
1205         AddCodeLine ("tax");
1206         AddCodeLine ("tya");
1207
1208         /* The function result is an rvalue in the primary register */
1209         ED_MakeRValExpr (Expr);
1210         Expr->Type = type_size_t;
1211
1212     /* We will inline strlen for arrays on the stack, if the array is
1213     ** completely within the reach of a byte sized index register.
1214     */
1215     } else if (ED_IsLocStack (&Arg) && IsArray && IsByteIndex &&
1216                (Arg.IVal - StackPtr) + ECount < 256) {
1217
1218         /* Calculate the true stack offset */
1219         int Offs = ED_GetStackOffs (&Arg, 0);
1220
1221         /* Generate the strlen code */
1222         L = GetLocalLabel ();
1223         AddCodeLine ("ldx #$FF");
1224         AddCodeLine ("ldy #$%02X", (unsigned char) (Offs-1));
1225         g_defcodelabel (L);
1226         AddCodeLine ("inx");
1227         AddCodeLine ("iny");
1228         AddCodeLine ("lda (sp),y");
1229         AddCodeLine ("bne %s", LocalLabelName (L));
1230         AddCodeLine ("txa");
1231         AddCodeLine ("ldx #$00");
1232
1233         /* The function result is an rvalue in the primary register */
1234         ED_MakeRValExpr (Expr);
1235         Expr->Type = type_size_t;
1236
1237     /* strlen for a string that is pointed to by a register variable will only
1238     ** get inlined if requested on the command line, since we cannot know how
1239     ** big the buffer actually is, so inlining is not always safe.
1240     */
1241     } else if (ED_IsLocRegister (&Arg) && ED_IsLVal (&Arg) && IsPtr &&
1242                IS_Get (&InlineStdFuncs)) {
1243
1244         /* Generate the strlen code */
1245         L = GetLocalLabel ();
1246         AddCodeLine ("ldy #$FF");
1247         g_defcodelabel (L);
1248         AddCodeLine ("iny");
1249         AddCodeLine ("lda (%s),y", ED_GetLabelName (&Arg, 0));
1250         AddCodeLine ("bne %s", LocalLabelName (L));
1251         AddCodeLine ("tax");
1252         AddCodeLine ("tya");
1253
1254         /* The function result is an rvalue in the primary register */
1255         ED_MakeRValExpr (Expr);
1256         Expr->Type = type_size_t;
1257
1258     /* Last check: We will inline a generic strlen routine if inlining was
1259     ** requested on the command line, and the code size factor is more than
1260     ** 400 (code is 13 bytes vs. 3 for a jsr call).
1261     */
1262     } else if (IS_Get (&CodeSizeFactor) > 400 && IS_Get (&InlineStdFuncs)) {
1263
1264         /* Load the expression into the primary */
1265         LoadExpr (CF_NONE, &Arg);
1266
1267         /* Inline the function */
1268         L = GetLocalLabel ();
1269         AddCodeLine ("sta ptr1");
1270         AddCodeLine ("stx ptr1+1");
1271         AddCodeLine ("ldy #$FF");
1272         g_defcodelabel (L);
1273         AddCodeLine ("iny");
1274         AddCodeLine ("lda (ptr1),y");
1275         AddCodeLine ("bne %s", LocalLabelName (L));
1276         AddCodeLine ("tax");
1277         AddCodeLine ("tya");
1278
1279         /* The function result is an rvalue in the primary register */
1280         ED_MakeRValExpr (Expr);
1281         Expr->Type = type_size_t;
1282
1283     } else {
1284
1285         /* Load the expression into the primary */
1286         LoadExpr (CF_NONE, &Arg);
1287
1288         /* Call the strlen function */
1289         AddCodeLine ("jsr _%s", Func_strlen);
1290
1291         /* The function result is an rvalue in the primary register */
1292         ED_MakeRValExpr (Expr);
1293         Expr->Type = type_size_t;
1294
1295     }
1296
1297     /* We expect the closing brace */
1298     ConsumeRParen ();
1299 }
1300
1301
1302
1303 /*****************************************************************************/
1304 /*                                   Code                                    */
1305 /*****************************************************************************/
1306
1307
1308
1309 int FindStdFunc (const char* Name)
1310 /* Determine if the given function is a known standard function that may be
1311 ** called in a special way. If so, return the index, otherwise return -1.
1312 */
1313 {
1314     /* Look into the table for known names */
1315     struct StdFuncDesc* D =
1316         bsearch (Name, StdFuncs, FUNC_COUNT, sizeof (StdFuncs[0]), CmpFunc);
1317
1318     /* Return the function index or -1 */
1319     if (D == 0) {
1320         return -1;
1321     } else {
1322         return D - StdFuncs;
1323     }
1324 }
1325
1326
1327
1328 void HandleStdFunc (int Index, FuncDesc* F, ExprDesc* lval)
1329 /* Generate code for a known standard function. */
1330 {
1331     struct StdFuncDesc* D;
1332
1333     /* Get a pointer to the table entry */
1334     CHECK (Index >= 0 && Index < (int)FUNC_COUNT);
1335     D = StdFuncs + Index;
1336
1337     /* Call the handler function */
1338     D->Handler (F, lval);
1339 }