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