Topic : RC51 - function calls - variable argument lists

Forum : 8051

Original Post
Post Information Post
February 1, 2007 - 5:44am
Guest

The following 'printf()' causes an application crash:

int tty_printf(const char GENERIC *format, ...)
{
int ret;
va_list ap;

va_start(ap, format);
ret = sprintf(PrintBuf.buf, format, va_arg(ap, const char GENERIC *) );
Comms_WrStr(PrintBuf.buf);
va_end(ap);

return ret;
}

Here's the listing. 'Comms_WrStr() was commented out to shorten it. Something doesn't look right.

; FUNCTION _tty_printf (BEGIN)
; SOURCE LINE # 12
0000 900000 R MOV DPTR,#format
0003 EB MOV A,R3
0004 F0 MOVX @DPTR,A
0005 A3 INC DPTR
0006 EA MOV A,R2
0007 F0 MOVX @DPTR,A
0008 A3 INC DPTR
0009 E9 MOV A,R1
000A F0 MOVX @DPTR,A
; R7 is assigned to ap
; SOURCE LINE # 17
000B E581 MOV A,__SP__
000D C3 CLR C
000E 9402 SUBB A,#02H <------ backup to before the PC - OK
; SOURCE LINE # 18
0010 24FD ADD A,#0FDH <------ Subtract a genric ptr (3) - OK
0012 04 INC A <------ Add 1!! What's this!!
0013 F8 MOV R0,A <------ Copy 3 bytes from the arg list
0014 E6 MOV A,@R0 to the top of stack
0015 C0E0 PUSH ACC
0017 08 INC R0
0018 E6 MOV A,@R0
0019 C0E0 PUSH ACC
001B 08 INC R0
001C E6 MOV A,@R0
001D C0E0 PUSH ACC
001F 7B02 MOV R3,#002H
0021 7A00 R MOV R2,#HIGH PrintBuf+002H
0023 7900 R MOV R1,#LOW PrintBuf+002H
0025 900000 R MOV DPTR,#format
0028 120000 R LCALL ?C_X2TRI0
002B 120000 R LCALL ?sprintf
002E 74FD MOV A,#0FDH
0030 2581 ADD A,SP
0032 F581 MOV SP,A
0034 900000 R MOV DPTR,#ret
0037 EE MOV A,R6
0038 F0 MOVX @DPTR,A
0039 A3 INC DPTR
003A EF MOV A,R7
003B F0 MOVX @DPTR,A
; SOURCE LINE # 22
003C 900000 R MOV DPTR,#ret
003F E0 MOVX A,@DPTR
0040 FE MOV R6,A
0041 A3 INC DPTR
0042 E0 MOVX A,@DPTR
0043 FF MOV R7,A
; SOURCE LINE # 23
0044 22 RET

; FUNCTION _tty_printf (END)

The copy of the variable arg pointer looks offset by one byte. That's from

#define va_arg(ap,t) (*(t __Stack_Space__ *)((ap-=sizeof(t))+1))

in 'main_c51.h'. However removing the '+1' from the macro doesn't doesn't fix the crash' so something else isn't right.

As a crosscheck, the lines inside tty_printf() work fine when placed inline in the application.

Compiler settings are:
large memory model
xdata pointers
internal stack

thanks Steven Pruzina

Replies
Post Information Post
+1
0
-1
February 6, 2007 - 5:14am
Guest

Oops.. The above code can never work. A function with a variable arg list can't pass that list to another function; it doesn't know how long the list is and so can't fix-up the stack.

However I did notice that function with a variable arg list has to be declared 'reentrant' for the compiler to pass parameters (to it) on the stack. The keyword is there to make a function reentrant by forcing parameters AND local variables onto the stack; a function which has a variable arg list but does NOT have to be reentrant shouldn't need this keyword (which will needlessly stack it's local variables)

Steven Pruzina

+1
0
-1
September 24, 2007 - 10:05am
Guest

Hi,
I am having similar problems.
I believe that your posted code is valid in ANSI C,we have all used similar logger indirections (on many different development environments) since our earliest coding days!
I found that if I call a function or two below main() that it will work & even work if I return from those functions all the way back to main and then down again. Weird.

Would be interested in how you got on.

+1
0
-1
September 26, 2007 - 10:50am
Raisonance Support Team

Hi Dudes,

This is an easy one: You are confusing between the variable arguments list and the pointer to this list.
You must use the vsprintf function for what you intend to do.

Here is you corrected example, I tested it and it works fine:

#include
#include

char PrintBuf[32];
int tty_printf(const char *format, ...)
{
int ret;
va_list ap;

va_start(ap, format);
ret = vsprintf(PrintBuf, format, ap); /* vsprintf, *not* sprintf! */
puts(PrintBuf);
va_end(ap);

return ret;
}

void main(void)
{
tty_printf( "%d %s", 3, "Hello" );
while(1);
}

I hope this helps,
Bruno

+1
0
-1
September 26, 2007 - 1:48pm
Guest

Thanks Bruno.

I missed the obvious mistake with the original post.
My code is similar to your corrected code and my code fails, rather fails if I call from main, 'works' when called lower down.

xdata char strBuffer[200];
void bio_printf( const char *a_Fmt, ... )
{
xdata va_list args;

xdata int Size, i;

va_start( args, a_Fmt );
vsprintf( strBuffer, a_Fmt, args );
va_end( args );

// printf( "Buffer size %d\n", strlen(strBuffer) );
/* if( (Size=strlen( strBuffer )) > 100 )
{
strBuffer[99-1] = '\0';
printf( "Output buffer overflow(%d) - %s", Size, strBuffer );
}
*/

printf( "%s", strBuffer );

return ;
} //-- endfn bio_printf() --//

(The printf() just substitutes my low level I/O. I originally started investigating why I could not print floats and ended up debugging the debug code.....)

When calling bio_printf() from main() I have identified that:
bio_printf( "text" )
But no further arguments causes program to crash.

bio_printf( "text %d, i )
Called from main displays some value other than i but otherwise does not crash.

bio_printf("text %d", i)
Called from some function below main displays i

bio_printf("text %f", f)
Displays text and no value.

My original problem was that I cannot print floats but I have eneded up with debugging some stack frame problem!

+1
0
-1
September 26, 2007 - 3:42pm
Guest

For me, Bruno's function does not work on floats either. Specifically, a bcd double under FP(BCD,ALL) using %7.4lf format. I am evaluating RIDE over Keil specifically because I need BCD variables which Keil does not have, but I am having very little luck so far. Not only does the above vsprint not work, but also sprintf exhibits the same problems if the bcd double is local rather than global. Heck, even 1.00 + 1.00 = 0.00 if the variables are local. The only thing I found in the FAQ that looked relevant was to use OMF51 setting but it made no difference. LARGE mode, external stack.

PeterH