This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Ellipsis use with C51 for 8051


I have some legacy code that was originally compiled with Franklin and it also compiles with my Keil compiler. The problem is that the source code does some things that make no sense and I cannot find any documentation in my Keil manuals or on the website about this topic. I want to ensure that 1) the code still operates the same using Keil vs Franklin compiler 2) I understand how it works. The manuals reference va_start,va_end macros. Can the code below be written to use those macros? How? If so, would this be a better option? Looking at the ouput src file, it appears that keil makes a undocumented library call.

I look forward to your inputs.

Thanks,

Paul Calvert
256-704-4134

Here is the code snipet:

int sscanf(char *inBfrPtr, char *fmtPtr, ...)
{
Byte scanResult = 0;
void *argPtr = &ellipsis_40; //THIS IS THE STATEMENT THAT I CAN FIND NO REFERNCE TO: ELLIPSIS_40. THERE ARE NO MACROS OR OTHER DECLARATIONS ANYWHERE IN MY CODE REFERENCING "ELLIPSIS_"
register char tmpDigit;

while(1){
if(*fmtPtr == '\0')
break;
else if(*fmtPtr == '%'){
fmtPtr++;
if(*fmtPtr == 'c'){
**(char **)argPtr = *inBfrPtr;
argPtr += 3;
fmtPtr++;
inBfrPtr++;
scanResult += 1;
continue;
}
else if(*fmtPtr == 'd'){
if(inBfrPtr[0] == '+' || inBfrPtr[0] == '-')
tmpDigit = inBfrPtr[1];
else
tmpDigit = inBfrPtr[0];
if(tmpDigit >= '0' && tmpDigit <= '9'){
**(int **)argPtr = atoi(inBfrPtr);
while(1){
inBfrPtr++;
if(inBfrPtr[0] < '0' || inBfrPtr[0] > '9')
break;
}
fmtPtr++;
argPtr += 3;
scanResult += 1;
continue;
}
else
break;
}
else if(*fmtPtr == '%'){
/* fall through to continue string comparison */
}
else{ /* unknown format specifier */
break;
}
}
if(*fmtPtr++ != *inBfrPtr++)
break;
}
return(scanResult);
}

  • Could you not just use the standard sscanf from the Keil library?

    You first need to understand how the legacy code was supposed to work; for that you'll need to look in the Franklin manuals - you can't expect Keil to document Franklin's implementation-specific details! ;-)

    My guess is that ellipsis_40 is specific to Franklin's implementation of variable-length argument lists.

    Keil implements the standard va_arg, va_end and va_start functions - see the Keil C51 User's Guide, and K&R.

    BTW: your code would look better if you enclosed it within &ltpre&gt and &lt/pre&gt tags - see "Tips for Posting," http://www.keil.com/forum/tips.asp

  • Cannot use the std library because cannot make library calls during program code reload.

    I am puzzled HOW Keil compiles this without warning or error --given no ellipsis documentation AT ALL. Keil should not document Franklin specific macros or library calls, but if it compiles under Keil, it should have documentation, no?

    I am still searching.... Thanx.

  • ellipsis_40 must be a global variable in the c51s.lib (or whatever model you use). It means you can place up to 40 byte on the stack for a variadatic function. Obviously, this is quite non-standard and you should use varargs if you can, they are macros and should not invoke any libary function calls. Also, when posting code, bracket it completey with
    < pre > and < /pre > but without the spaces between the < and >.

    This is a quick example for you to try:

    #include <stdarg.h>
    
    typedef unsigned char U8;
    
    void variable(U8 *inBfrPtr, U8 *pFormat, ...)
    {
        va_list    vargList;
        U8         argList[MAX_ARG_SIZE];
        U8         idx      = 0;
        U8         numArgs  = 0;
    
        // Extract the arguments, I'm assuming all char's. Also, you'll want to loop
        // on the va_arg() extraction based upon your pFormat.
        va_start(vargList, pFormat);
        argList[idx] = va_arg(vargList, U8);
        va_end(vargList);
    }

    - Mark

  • you might want to set up an XDATA reentrant stack and then define all your variadatic functions (only them) as reentrant, I did it like this:

    void variable(U8 *inBfrPtr, U8 *pFormat, ...) large reentrant
    {
       
    }
    - Mark

  • I *really* appreciate your feedback. Please excuse my ignorance, but I am not following you too well.

    I modified the original posted code to the following and it does compile. Does it look right? I am unsure about where to put va_end/start.

    <pre >
    int sscanf(char *inBfrPtr, char *fmtPtr, ...)
    {
    Byte scanResult = 0;
    // void *argPtr = &ellipsis_40;
    void *argPtr;
    register char tmpDigit;

    va_list vargList;
    Byte argList[40];

    va_start(vargList, fmtPtr);
    argList[0] = va_arg(vargList, Byte);
    argPtr = &argList[0];

    while(1){
    if(*fmtPtr == '\0')
    break;
    else if(*fmtPtr == '%'){
    fmtPtr++;
    if(*fmtPtr == 'c'){
    **(char **)argPtr = *inBfrPtr;
    argPtr += 3;
    fmtPtr++;
    inBfrPtr++;
    scanResult += 1;
    continue;
    }
    else if(*fmtPtr == 'd'){
    if(inBfrPtr[0] == '+' || inBfrPtr[0] == '-')
    tmpDigit = inBfrPtr[1];
    else
    tmpDigit = inBfrPtr[0];
    if(tmpDigit >= '0' && tmpDigit <= '9'){
    **(int **)argPtr = atoi(inBfrPtr);
    while(1){
    inBfrPtr++;
    if(inBfrPtr[0] < '0' || inBfrPtr[0] > '9')
    break;
    }
    fmtPtr++;
    argPtr += 3;
    scanResult += 1;
    continue;
    }
    else
    break;
    }
    else if(*fmtPtr == '%'){
    /* fall through to continue string comparison */
    }
    else{ /* unknown format specifier */
    break;
    }
    }
    if(*fmtPtr++ != *inBfrPtr++)
    break;
    }
    return(scanResult);
    va_end(vargList);
    }

    </pre >

  • Sorry, I hadn't realised that this was actually compiling under Keil!

    "but if it compiles under Keil, it should have documentation, no?"

    Unfortunately, I'm afraid that's a little naive.
    I've seen too many projects where "clever" programmers have somehow discovered - and relied upon - undocumented features of their specific compiler release. Such "cleverness" is highly likely to trip you up when the compiler is upgraded, and almost certainly if you need to change compilers!

  • Why not use the PREPRINT directive with the Keil compiler. This directive generates a preprocessor output file that you can look at to determine what ELLIPSIS_40 really is.

    Jon

  • Okay, we will get this pre tag to work. It's < pre > but *no* spaces after the < or before the >. I can't show it literally here of course. Hit the preview tab as you reply to see that you are getting blue highlighted, fixed pitch code blocks. Of course < /pre > is the < pre > terminator. Again, *no spaces* are allowed. It's really hard to read proportionally spaced code without indentation.

    Now on to the problem. You missed my point that I made poorly. You pop things off the variadatic stack which is made known to the compiler via the va_start() macro. You pop things off using the type you expect by using the va_arg() macro. Then you terminate your variadatic stack usage with the va_end() macro. Here's a printf() example (watch my < pre >):

    int printf(char *pFmt, ...) reentrant large
    {
        long fourByte;
        short twoByte;
        char byte;
        int count;
        va_list vargList;
    
        va_start(vargList, pFmt);
        while (*pFmt)
        {
            if (!strcmp(pFmt, "%c"))
            {
                 byte = va_arg(vargList, char);
                 ++count;
            }
            else if ((!strcmp(pFmt, "%d")))
            {
                 twoByte = va_arg(vargList, short);
                 count += sizeof (short)
            }
            else if ((!strcmp(pFmt, "%ld")))
            {
                 fourByte = va_arg(vargList, long);
                 count += sizeof (long);
            }
            
            // Important, move along through the variadatic stack.
            ++pFmt;
        }
    
        // All done
        va_end(vargList);
    
        return count;
    }
    Of course this is just a cheezey printf-like stub that does nothing useful and it is untested so beware.

    - Mark

  • "I can't show it literally here of course."

    If you type "&amplt" and "&ampgt" in the 'post' window, they will display as '<' and '>'
    Thus you can show the &ltpre&gt and &lt/pre&gt literally in your post!

    The ampersand, '&amp', is the standard HTML escape; of course, to show it here, I had to use the escape sequence for ampersand, which is "&ampamp".
    You should be able to see how it all works with your Browser's 'View Source' button, or equivalent.

    To check how your post will look, use the 'Preview' button below the 'Post' window (and then your Browser's 'Back' button if you need further editing).

  • Hmm... &ltpre&gt hey neat. Now why couldn't they just use the \ for escape? That seems so much better to me, of course I'm a C programmer too.

    Thanks Andrew.

    - Mark

  • Thanks for all your suggestions. Sorry about the <pre > problems with my posts.

    I tried the PREPRINT that was suggested. The resultant preprocessor file had no reference to ellipsis_40 at all. Worth a shot, but....

    Thank you Mark Odell for the last printf example you posted. I think I can figure it out from here. Did you see anything wrong with the way I implemented va_ macros? I realize I was half-*ssed doing it--in an effort to retain as much original code as possible, but it 'seemed' to work (I have yet to try on target hardware). Before this week I had never heard of or seen an ellipsis. Thanks for all your insights.....

    Paul

  • Paul,

    I did see something wrong with your use of va_* macros:

    argList[0] = va_arg(vargList, Byte);
    argPtr = &argList[0];
    
    if(*fmtPtr == 'c'){
    **(char **)argPtr = *inBfrPtr;
    
    // How you can add 3 to a void pointer is beyond me,
    // C doesn't know how to scale with void pointers.
    argPtr += 3;
    
    argPtr must be removed or I will become ill. All your funky lvalue casting not only looks weird but is just reproducing the magic of va_arg().

    You *must* pop things off the variadatic stack using va_arg() and no other means. It's really quite simple, if your format string (which you can define anyway you want) indicates that the caller has passed in two chars, a short, and a long (in that order) then you must pull them off this way in that order like this:
    char one, two;
    short three;
    long four;
    
    one   = va_arg(vargList, char);
    two   = va_arg(vargList, char);
    three = va_arg(vargList, short);
    four  = va_arg(vargList, long);
    
    va_arg() increments the variadatic stack for you and ensures that you get what you expect since you tell it the type.

    I hope this clarifies things. Your sscanf() needs a re-write, perhaps I'll give it shot in my spare time.

    - Mark

  • Paul,

    Have you ever used printf in your programs? If so, you have used a function with a variable length argument list. That's what the ...'s are.

    Jon