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

General Question Re: Memory Models and Abstract Pointers

Hi All.

I'm becoming more familiar with C51's various memory models, but I could use some confirmation on one issue.

Here's a code snippet:

char CODE_B = 'B';
char CODE_C = 'C';

...

void TaskShow( void )
{
    OStypeMsgP msgP;
    
    InitPORT();
    PORT = 0x00;
    
    for (;;) {
        OS_WaitMsg(MSG_UPDATE_PORT_P, &msgP, OSNO_TIMEOUT, TaskShow1);
        
        if ( *(char *)msgP == CODE_C ) {
            PORT &= ~0xFE;
            PORT |= ( counter >> 8 ) & 0xFE; 
        }
        else
            PORT ^= 0x01; 
    }
}

where OStypeMsgP is void *.

For the SMALL memory model, this code works fine. For the LARGE memory model (with variables in XDATA), I must change the line that dereferences the pointer to
if ( *(char xdata *)msgP == CODE_C ) {

Makes sense to me ...

1) Could someone just verify that this is the intended behavior of variables that do not have an explicit memory type? I.e. if I do not add an explicitly declared memory type (e.g. data|xdata|idata|pdata|...) to the variable's declaration, then will it automatically be in the selected memory model's default memory area?

2) From what I can see, this means that "abstract pointers" are always of type void *, and one must simply dereference them properly, using the explicitly declared memory type in the dereferencing cast. Correct?

Thanks,

Parents
  • Hi Mark.

    Thanks for your continued interest in this thread.

    (The #define is ugly, I know, but it is there for historical reasons to support other non-Keil compilers. Perhaps I can find an elegant way to remove it and use only the typedef ...)

    Anyway, I see your point re the use of a local var to avoid casting the lvalue, but how is that any different from

    if ( CODE_C == *(char *)msgP ) {
    ? Isn't that effectively the same thing?

    Thanks,

Reply
  • Hi Mark.

    Thanks for your continued interest in this thread.

    (The #define is ugly, I know, but it is there for historical reasons to support other non-Keil compilers. Perhaps I can find an elegant way to remove it and use only the typedef ...)

    Anyway, I see your point re the use of a local var to avoid casting the lvalue, but how is that any different from

    if ( CODE_C == *(char *)msgP ) {
    ? Isn't that effectively the same thing?

    Thanks,

Children
  • support other non-Keil compilers. Perhaps I can find an elegant way to remove it and use only the typedef ...)

    The common way is to do
    #ifdef __WIN32__
    blah
    #elif __C51__
    blix
    #endif

    Anyway, I see your point re the use of a local var to avoid casting the lvalue, but how is that any different from

    if ( CODE_C == *(char *)msgP ) {
    ? Isn't that effectively the same thing?

    Yes it is. I screwed up on the l-value. L-values are "left hand side of equates" or "linker resoved" whereas r-values are "right hand side of equates" or "run-time resolved". Since the dereference of msgP is a run-time resolved value it is an r-value. Thus it's not a problem. My apologies. I confused it with:
    *(char *) msgP = 'a';
    which shows a constant being stored into a linker resolved address as an l-value.

    Regards.

    - Mark

  • Hi Mark.

    Here's the general reason why we use a #define instead of a typedef in certain places of the Salvo code (we already test for compilers using their defined symbols, etc. -- it's not for that reason).

    Before this thread expires :-) I thought I'd pester you on your thoughts for a a more elegant solution ...

    Let's assume that, by default, we want our message pointers to be of type

    void *
    . But, we also want our users to be able to recompile the code with an alternative typedef on a per-project basis. Well, if we have
    typedef void * OStypeMsgP;
    in one of our header files, then it's not possible for the user to override this without directly editing the Salvo source code. This is bad because they might want one definition for one project, and another for another ...

    So what we've done (as a general rule) is to use a predefined symbol (e.g. OSMESSAGE_TYPE) which defaults to a value if the user doesn't define it in an earlier, well-defined header file called salvocfg.h. E.g.
    #ifndef OSMESSAGE_TYPE
    #define OSMESSAGE_TYPE void
    #endif
    typedef OSMESSAGE_TYPE * OStypeMsgP;
    This way a user can do nothing, in which case the message pointers are void pointers (3 bytes, big, inefficient but completely abstract and versatile), or they can
    #define OSMESSAGE_TYPE data
    in salvocfg.h and shrink the pointers to a single byte, with the caveat that they can only point into data space.

    This actually works quite well, though it can be difficult to see what's going on when one peruses a .pre preprocessor output file.

    The only other solution I can think of is something like this:
    #ifndef OSOVERRIDE_MESSAGE_POINTER_TYPE
    typedef void * OStypeMsgP;
    #endif
    and then the user has to
     #define OSOVERRIDE_MESSAGE_POINTER_TYPE TRUE
    typedef data * OStypeMsgP
    in salvocfg.h in order to "change" OStypeMsgP.

    This is all due to the fact that AFAIK one cannot "redefine" a typedef, nor can one detect if one is already defined.

    If you're interested, download Salvo Lite for 8051 family from our website http://www.pumpkininc.com and you'll see how this is used in salvo.h and portkc51.h.

    Regards, and thanks for all your input in this thread.

  • Andrew, I'm afraid I don't have much to add here. I'd have done the message object a bit differently to avoid the 8051 data space specifier conundrum but as I'm sure you're well aware, there's not much you can do with a typedef once you've declared it except use it.

    For the message object I'd simply forgo the pointer version and supply the base type letting user create a pointer from it. E.g.

    typedef enum MsgTypes
    {
        MSG_COMPLEX,
        MSG_A,
        MSG_B,
        MSG_C,
        NUM_MSGS
    } MsgTypes;
    
    typedef struct o_complex
    {
        int  var;
        char arr[5];
    } o_complex;
    
    typedef struct o_msg
    {
        MsgTypes            type;
        union MsgBody
        {
            o_complex       complex;
            unsigned long   apple;
            unsigned int    butter;
            unsigned char   cheese;
        } body;
    } o_msg;
    
    int main(void)
    {
        /* Pointer resides in IDATA but points only to DATA space
        */
        o_msg idata * data pMsg;
    
        pMsg->type        = MSG_A;
        pMsg->body.cheese = 'a';
    
        /* blah, blah, blah...
        */
        for (;;);
    
        return 0;
    }

    If you think I might be of any further assistance, maybe we should take it off-line, :-)

    Regards,

    - Mark

  • Ahem, off-line is tough without an email address, eh?

    mark@embeddedfw.com

  • maybe we should take it off-line

    That'd be a shame. Then all of us Salvo users couldn't see what you guys are up to ;-)

    I've always found it beneficial to follow a problem as it is worked through to its eventual solution.

  • I just don't want to turn Keil's website into a Salvo discussion forum, they have their own already.

    Regards,

    - Mark

  • I just don't want to turn Keil's website into a Salvo discussion forum, they have their own already.

    Agreed. This is Keil's web space, after all.

    I'm pretty happy with the way we do message pointers -- anything more explicit invariably leads to cross-platform portability problems and increased maintenance headaches. It does work well, once users get the hang of it. And it can be quite efficient.

    Thanks again, Mark, for your help and interest in my original query. It has helped me in my understanding of the pointer situation with Cx51.

    Regards,