+static void StdFunc_memcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
+/* Handle the memcpy function */
+{
+ /* Argument types: (void*, const void*, size_t) */
+ static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_VOID), TYPE(T_END) };
+ static Type Arg2Type[] = { TYPE(T_PTR), TYPE(T_VOID|T_QUAL_CONST), TYPE(T_END) };
+ static Type Arg3Type[] = { TYPE(T_SIZE_T), TYPE(T_END) };
+
+ CodeMark Start;
+ ArgDesc Arg1, Arg2, Arg3;
+ unsigned ParamSize = 0;
+ unsigned Label;
+
+ /* Remember where we are now */
+ GetCodePos (&Start);
+
+ /* Argument #1 */
+ ParseArg (&Arg1, Arg1Type);
+ g_push (Arg1.Flags, Arg1.Expr.IVal);
+ GetCodePos (&Arg1.End);
+ ParamSize += SizeOf (Arg1Type);
+ ConsumeComma ();
+
+ /* Argument #2 */
+ ParseArg (&Arg2, Arg2Type);
+ g_push (Arg2.Flags, Arg2.Expr.IVal);
+ GetCodePos (&Arg2.End);
+ ParamSize += SizeOf (Arg2Type);
+ ConsumeComma ();
+
+ /* Argument #3. Since memcpy is a fastcall function, we must load the
+ * arg into the primary if it is not already there. This parameter is
+ * also ignored for the calculation of the parameter size, since it is
+ * not passed via the stack.
+ */
+ ParseArg (&Arg3, Arg3Type);
+ if (Arg3.Flags & CF_CONST) {
+ LoadExpr (CF_NONE, &Arg3.Expr);
+ }
+
+ /* Emit the actual function call. This will also cleanup the stack. */
+ g_call (CF_FIXARGC, Func_memcpy, ParamSize);
+
+ if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal == 0) {
+
+ /* memcpy has been called with a count argument of zero */
+ Warning ("Call to memcpy has no effect");
+
+ /* Remove all of the generated code but the load of the first
+ * argument, which is what memcpy returns.
+ */
+ RemoveCode (&Arg1.Push);
+
+ /* Set the function result to the first argument */
+ *Expr = Arg1.Expr;
+
+ /* Bail out, no need for further improvements */
+ goto ExitPoint;
+ }
+
+ /* We've generated the complete code for the function now and know the
+ * types of all parameters. Check for situations where better code can
+ * be generated. If such a situation is detected, throw away the
+ * generated, and emit better code.
+ */
+ if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
+ ((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) ||
+ (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) &&
+ ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) ||
+ (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)))) {
+
+ int Reg1 = ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr);
+ int Reg2 = ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr);
+
+ /* Drop the generated code */
+ RemoveCode (&Start);
+
+ /* We need a label */
+ Label = GetLocalLabel ();
+
+ /* Generate memcpy code */
+ if (Arg3.Expr.IVal <= 127) {
+
+ AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
+ AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
+ g_defcodelabel (Label);
+ if (Reg2) {
+ AddCodeLine ("lda (%s),y", ED_GetLabelName (&Arg2.Expr, 0));
+ } else {
+ AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, 0));
+ }
+ if (Reg1) {
+ AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
+ } else {
+ AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
+ }
+ AddCodeLine ("dey");
+ AddCodeLine ("bpl %s", LocalLabelName (Label));
+
+ } else {
+
+ AddCodeLine ("ldy #$00");
+ AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
+ g_defcodelabel (Label);
+ if (Reg2) {
+ AddCodeLine ("lda (%s),y", ED_GetLabelName (&Arg2.Expr, 0));
+ } else {
+ AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, 0));
+ }
+ if (Reg1) {
+ AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
+ } else {
+ AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
+ }
+ AddCodeLine ("iny");
+ AddCodeLine ("cpy #$%02X", (unsigned char) Arg3.Expr.IVal);
+ AddCodeLine ("bne %s", LocalLabelName (Label));
+
+ }
+
+ /* memcpy returns the address, so the result is actually identical
+ * to the first argument.
+ */
+ *Expr = Arg1.Expr;
+
+ } else if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
+ ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr) &&
+ ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) &&
+ (Arg1.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256) {
+
+ /* It is possible to just use one index register even if the stack
+ * offset is not zero, by adjusting the offset to the constant
+ * address accordingly. But we cannot do this if the data in
+ * question is in the register space or at an absolute address less
+ * than 256. Register space is zero page, which means that the
+ * address calculation could overflow in the linker.
+ */
+ int AllowOneIndex = !ED_IsLocRegister (&Arg2.Expr) &&
+ !(ED_IsLocAbs (&Arg2.Expr) && Arg2.Expr.IVal < 256);
+
+ /* Calculate the real stack offset */
+ int Offs = ED_GetStackOffs (&Arg1.Expr, 0);
+
+ /* Drop the generated code */
+ RemoveCode (&Start);
+
+ /* We need a label */
+ Label = GetLocalLabel ();
+
+ /* Generate memcpy code */
+ if (Arg3.Expr.IVal <= 127 && !AllowOneIndex) {
+
+ if (Offs == 0) {
+ AddCodeLine ("ldy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal - 1));
+ g_defcodelabel (Label);
+ AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, -Offs));
+ AddCodeLine ("sta (sp),y");
+ AddCodeLine ("dey");
+ AddCodeLine ("bpl %s", LocalLabelName (Label));
+ } else {
+ AddCodeLine ("ldx #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
+ AddCodeLine ("ldy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal - 1));
+ g_defcodelabel (Label);
+ AddCodeLine ("lda %s,x", ED_GetLabelName (&Arg2.Expr, 0));
+ AddCodeLine ("sta (sp),y");
+ AddCodeLine ("dey");
+ AddCodeLine ("dex");
+ AddCodeLine ("bpl %s", LocalLabelName (Label));
+ }
+
+ } else {
+
+ if (Offs == 0 || AllowOneIndex) {
+ AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
+ g_defcodelabel (Label);
+ AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, -Offs));
+ AddCodeLine ("sta (sp),y");
+ AddCodeLine ("iny");
+ AddCodeLine ("cpy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal));
+ AddCodeLine ("bne %s", LocalLabelName (Label));
+ } else {
+ AddCodeLine ("ldx #$00");
+ AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
+ g_defcodelabel (Label);
+ AddCodeLine ("lda %s,x", ED_GetLabelName (&Arg2.Expr, 0));
+ AddCodeLine ("sta (sp),y");
+ AddCodeLine ("iny");
+ AddCodeLine ("inx");
+ AddCodeLine ("cpx #$%02X", (unsigned char) Arg3.Expr.IVal);
+ AddCodeLine ("bne %s", LocalLabelName (Label));
+ }
+
+ }
+
+ /* memcpy returns the address, so the result is actually identical
+ * to the first argument.
+ */
+ *Expr = Arg1.Expr;
+
+ } else if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
+ ED_IsRVal (&Arg2.Expr) && ED_IsLocStack (&Arg2.Expr) &&
+ (Arg2.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256 &&
+ ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) {
+
+ /* It is possible to just use one index register even if the stack
+ * offset is not zero, by adjusting the offset to the constant
+ * address accordingly. But we cannot do this if the data in
+ * question is in the register space or at an absolute address less
+ * than 256. Register space is zero page, which means that the
+ * address calculation could overflow in the linker.
+ */
+ int AllowOneIndex = !ED_IsLocRegister (&Arg1.Expr) &&
+ !(ED_IsLocAbs (&Arg1.Expr) && Arg1.Expr.IVal < 256);
+
+ /* Calculate the real stack offset */
+ int Offs = ED_GetStackOffs (&Arg2.Expr, 0);
+
+ /* Drop the generated code */
+ RemoveCode (&Start);
+
+ /* We need a label */
+ Label = GetLocalLabel ();
+
+ /* Generate memcpy code */
+ if (Arg3.Expr.IVal <= 127 && !AllowOneIndex) {
+
+ if (Offs == 0) {
+ AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal - 1));
+ g_defcodelabel (Label);
+ AddCodeLine ("lda (sp),y");
+ AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
+ AddCodeLine ("dey");
+ AddCodeLine ("bpl %s", LocalLabelName (Label));
+ } else {
+ AddCodeLine ("ldx #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
+ AddCodeLine ("ldy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal - 1));
+ g_defcodelabel (Label);
+ AddCodeLine ("lda (sp),y");
+ AddCodeLine ("sta %s,x", ED_GetLabelName (&Arg1.Expr, 0));
+ AddCodeLine ("dey");
+ AddCodeLine ("dex");
+ AddCodeLine ("bpl %s", LocalLabelName (Label));
+ }
+
+ } else {
+
+ if (Offs == 0 || AllowOneIndex) {
+ AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
+ g_defcodelabel (Label);
+ AddCodeLine ("lda (sp),y");
+ AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, -Offs));
+ AddCodeLine ("iny");
+ AddCodeLine ("cpy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal));
+ AddCodeLine ("bne %s", LocalLabelName (Label));
+ } else {
+ AddCodeLine ("ldx #$00");
+ AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
+ g_defcodelabel (Label);
+ AddCodeLine ("lda (sp),y");
+ AddCodeLine ("sta %s,x", ED_GetLabelName (&Arg1.Expr, 0));
+ AddCodeLine ("iny");
+ AddCodeLine ("inx");
+ AddCodeLine ("cpx #$%02X", (unsigned char) Arg3.Expr.IVal);
+ AddCodeLine ("bne %s", LocalLabelName (Label));
+ }
+
+ }
+
+ /* memcpy returns the address, so the result is actually identical
+ * to the first argument.
+ */
+ *Expr = Arg1.Expr;
+
+ } else {
+
+ /* The function result is an rvalue in the primary register */
+ ED_MakeRValExpr (Expr);
+ Expr->Type = GetFuncReturn (Expr->Type);
+
+ }
+
+ExitPoint:
+ /* We expect the closing brace */
+ ConsumeRParen ();
+}
+
+
+
+/*****************************************************************************/
+/* memset */
+/*****************************************************************************/
+
+
+
+static void StdFunc_memset (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
+/* Handle the memset function */
+{
+ /* Argument types: (void*, int, size_t) */
+ static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_VOID), TYPE(T_END) };
+ static Type Arg2Type[] = { TYPE(T_INT), TYPE(T_END) };
+ static Type Arg3Type[] = { TYPE(T_SIZE_T), TYPE(T_END) };
+
+ CodeMark Start;
+ ArgDesc Arg1, Arg2, Arg3;
+ int MemSet = 1; /* Use real memset if true */
+ unsigned ParamSize = 0;
+ unsigned Label;
+
+ /* Remember where we are now */
+ GetCodePos (&Start);
+
+ /* Argument #1 */
+ ParseArg (&Arg1, Arg1Type);
+ g_push (Arg1.Flags, Arg1.Expr.IVal);
+ GetCodePos (&Arg1.End);
+ ParamSize += SizeOf (Arg1Type);
+ ConsumeComma ();
+
+ /* Argument #2. This argument is special in that we will call another
+ * function if it is a constant zero.
+ */
+ ParseArg (&Arg2, Arg2Type);
+ if ((Arg2.Flags & CF_CONST) != 0 && Arg2.Expr.IVal == 0) {
+ /* Don't call memset, call bzero instead */
+ MemSet = 0;
+ } else {
+ /* Push the argument */
+ g_push (Arg2.Flags, Arg2.Expr.IVal);
+ GetCodePos (&Arg2.End);
+ ParamSize += SizeOf (Arg2Type);
+ }
+ ConsumeComma ();
+
+ /* Argument #3. Since memset is a fastcall function, we must load the
+ * arg into the primary if it is not already there. This parameter is
+ * also ignored for the calculation of the parameter size, since it is
+ * not passed via the stack.
+ */
+ ParseArg (&Arg3, Arg3Type);
+ if (Arg3.Flags & CF_CONST) {
+ LoadExpr (CF_NONE, &Arg3.Expr);
+ }
+
+ /* Emit the actual function call. This will also cleanup the stack. */
+ g_call (CF_FIXARGC, MemSet? Func_memset : Func__bzero, ParamSize);
+
+ if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal == 0) {
+
+ /* memset has been called with a count argument of zero */
+ Warning ("Call to memset has no effect");
+
+ /* Remove all of the generated code but the load of the first
+ * argument, which is what memset returns.
+ */
+ RemoveCode (&Arg1.Push);
+
+ /* Set the function result to the first argument */
+ *Expr = Arg1.Expr;
+
+ /* Bail out, no need for further improvements */
+ goto ExitPoint;
+ }
+
+ /* We've generated the complete code for the function now and know the
+ * types of all parameters. Check for situations where better code can
+ * be generated. If such a situation is detected, throw away the
+ * generated, and emit better code.
+ * Note: Lots of improvements would be possible here, but I will
+ * concentrate on the most common case: memset with arguments 2 and 3
+ * being constant numerical values. Some checks have shown that this
+ * covers nearly 90% of all memset calls.
+ */
+ if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
+ ED_IsConstAbsInt (&Arg2.Expr) &&
+ ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) ||
+ (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)))) {
+
+ int Reg = ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr);
+
+ /* Drop the generated code */
+ RemoveCode (&Start);
+
+ /* We need a label */
+ Label = GetLocalLabel ();
+
+ /* Generate memset code */
+ if (Arg3.Expr.IVal <= 127) {
+
+ AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
+ AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
+ g_defcodelabel (Label);
+ if (Reg) {
+ AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
+ } else {
+ AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
+ }
+ AddCodeLine ("dey");
+ AddCodeLine ("bpl %s", LocalLabelName (Label));
+
+ } else {
+
+ AddCodeLine ("ldy #$00");
+ AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
+ g_defcodelabel (Label);
+ if (Reg) {
+ AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
+ } else {
+ AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
+ }
+ AddCodeLine ("iny");
+ AddCodeLine ("cpy #$%02X", (unsigned char) Arg3.Expr.IVal);
+ AddCodeLine ("bne %s", LocalLabelName (Label));
+
+ }
+
+ /* memset returns the address, so the result is actually identical
+ * to the first argument.
+ */
+ *Expr = Arg1.Expr;
+
+ } else if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
+ ED_IsConstAbsInt (&Arg2.Expr) &&
+ ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) &&
+ (Arg1.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256) {
+
+ /* Calculate the real stack offset */
+ int Offs = ED_GetStackOffs (&Arg1.Expr, 0);
+
+ /* Drop the generated code */
+ RemoveCode (&Start);
+
+ /* We need a label */
+ Label = GetLocalLabel ();
+
+ /* Generate memset code */
+ AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
+ AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
+ g_defcodelabel (Label);
+ AddCodeLine ("sta (sp),y");
+ AddCodeLine ("iny");
+ AddCodeLine ("cpy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal));
+ AddCodeLine ("bne %s", LocalLabelName (Label));
+
+ /* memset returns the address, so the result is actually identical
+ * to the first argument.
+ */
+ *Expr = Arg1.Expr;
+
+ } else if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
+ ED_IsConstAbsInt (&Arg2.Expr) &&
+ (Arg2.Expr.IVal != 0 || IS_Get (&CodeSizeFactor) > 200)) {
+
+ /* Remove all of the generated code but the load of the first
+ * argument.
+ */
+ RemoveCode (&Arg1.Push);
+
+ /* We need a label */
+ Label = GetLocalLabel ();
+
+ /* Generate code */
+ AddCodeLine ("sta ptr1");
+ AddCodeLine ("stx ptr1+1");
+ if (Arg3.Expr.IVal <= 127) {
+ AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
+ AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
+ g_defcodelabel (Label);
+ AddCodeLine ("sta (ptr1),y");
+ AddCodeLine ("dey");
+ AddCodeLine ("bpl %s", LocalLabelName (Label));
+ } else {
+ AddCodeLine ("ldy #$00");
+ AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
+ g_defcodelabel (Label);
+ AddCodeLine ("sta (ptr1),y");
+ AddCodeLine ("iny");
+ AddCodeLine ("cpy #$%02X", (unsigned char) Arg3.Expr.IVal);
+ AddCodeLine ("bne %s", LocalLabelName (Label));
+ }
+
+ /* Load the function result pointer into a/x (x is still valid). This
+ * code will get removed by the optimizer if it is not used later.
+ */
+ AddCodeLine ("lda ptr1");
+
+ /* The function result is an rvalue in the primary register */
+ ED_MakeRValExpr (Expr);
+ Expr->Type = GetFuncReturn (Expr->Type);
+
+ } else {
+
+ /* The function result is an rvalue in the primary register */
+ ED_MakeRValExpr (Expr);
+ Expr->Type = GetFuncReturn (Expr->Type);
+
+ }
+
+ExitPoint:
+ /* We expect the closing brace */
+ ConsumeRParen ();
+}
+
+
+
+/*****************************************************************************/
+/* strcpy */
+/*****************************************************************************/
+
+
+
+static void StdFunc_strcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
+/* Handle the strcpy function */