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

C251 memory pointer problem

Hi,
Is this a valid construct in you Keil C-251 compiler?

unsigned char data buf[20];

typedef struct {
int i;
int j;
} TEST_T;

#define GetStruct() (*(TEST_T *)&buf[2])

void Test(void) {
GetStruct().i = 3;
}

Notice that buf is in the data area and I'm type casting it to a TEST_T struct in the default memory area (which is in far). V3.53 of the compiler compiles buggy code when similar scheme is used. If I change the buf memory area to "near", or if I change the macro to

#define GetStruct() (*(TEST_T data *)&buf[2])

or

#define GetStruct() (*(TEST_T *)(unsigned char *)&buf[2])


then it compiles good code. Question is, was there something fundamentaly wrong with what I did in the macro?

Andy

Parents
  • The actual source is too big to include here, but here's a snipet


    #define L_SER_RBUF_LEN 50
    #define DATA_SEG data
    extern unsigned char DATA_SEG L_gucSerialBuf[L_SER_RBUF_LEN];

    #define L_SerialGetRBuf() L_gucSerialBuf

    typedef union
    {
    struct
    {
    unsigned short start;
    unsigned short word_count;
    } in;
    struct
    {
    /* The first two bytes of this struct maps to the register
    * starting address. */
    short do_not_touch;
    /* The next two bytes are the actual start of the response
    * message. In this case, they contain the controller address
    * and function code. Clients must not touch the next 3 bytes. */
    unsigned char addr;
    unsigned char fn_code;
    unsigned char byte_count;
    /* Size of the array doesn't matter here as long as it's the last
    * union in the struct. The compiler won't compile unless the
    * size is given. So 1 was used to make the compiler happy. */
    union
    {
    float ieee[1]; /* IEEE. */
    short decimal[1]; /* Virtual decimal. */
    } Data;
    } out;
    } L_MBUS_READ_T;

    /* Access to the buffers. */
    #define L_MBusReadStruct() (*(L_MBUS_READ_T*)&L_gucSerialBuf[2])

    ===========================
    and in my program, I'm using the L_MBusReadStruct() like this

    L_MBusReadStruct().out.Data.decimal[sucRegisterServIdx] = fVal;

    The compiler generated non-working code when L_gucSerialBuf was assigned to the data area, but when assign it to the "near" area, then the code starts to work. I looked at the assembler output and it seems that the compiler converted the pointer to L_gucSerialBuf to a 16-bit value, but then tried to fetch the address of out.Data.decimal from the upper byte of the 16-bit value. It did the correct thing when the buffer was assigned to the "near" area. Was what I did wrong? Sorry for the long and ugly looking code.

    Andy

Reply
  • The actual source is too big to include here, but here's a snipet


    #define L_SER_RBUF_LEN 50
    #define DATA_SEG data
    extern unsigned char DATA_SEG L_gucSerialBuf[L_SER_RBUF_LEN];

    #define L_SerialGetRBuf() L_gucSerialBuf

    typedef union
    {
    struct
    {
    unsigned short start;
    unsigned short word_count;
    } in;
    struct
    {
    /* The first two bytes of this struct maps to the register
    * starting address. */
    short do_not_touch;
    /* The next two bytes are the actual start of the response
    * message. In this case, they contain the controller address
    * and function code. Clients must not touch the next 3 bytes. */
    unsigned char addr;
    unsigned char fn_code;
    unsigned char byte_count;
    /* Size of the array doesn't matter here as long as it's the last
    * union in the struct. The compiler won't compile unless the
    * size is given. So 1 was used to make the compiler happy. */
    union
    {
    float ieee[1]; /* IEEE. */
    short decimal[1]; /* Virtual decimal. */
    } Data;
    } out;
    } L_MBUS_READ_T;

    /* Access to the buffers. */
    #define L_MBusReadStruct() (*(L_MBUS_READ_T*)&L_gucSerialBuf[2])

    ===========================
    and in my program, I'm using the L_MBusReadStruct() like this

    L_MBusReadStruct().out.Data.decimal[sucRegisterServIdx] = fVal;

    The compiler generated non-working code when L_gucSerialBuf was assigned to the data area, but when assign it to the "near" area, then the code starts to work. I looked at the assembler output and it seems that the compiler converted the pointer to L_gucSerialBuf to a 16-bit value, but then tried to fetch the address of out.Data.decimal from the upper byte of the 16-bit value. It did the correct thing when the buffer was assigned to the "near" area. Was what I did wrong? Sorry for the long and ugly looking code.

    Andy

Children
  • You should probably contact our technical support department with this problem. I suspect that you are trying to access an object in one memory space using a pointer to a different memory space. It's kinda like looking for Las Vegas by going to Ohio.

    Jon

  • Hi,
    The only pointer type I used are "data area" pointer and generic pointers. I'm converting the "data area" pointer to generic pointers in the macros (at least that's what I intended). I have created a very simple compilable program to illustrate my problem. The source is included at the end. I ran this program in the built-in simulator and found that if the project's "Memory Model" is set to "Tiny" or "XTiny", then the program would work by assigning the value of 3 to buf[3]. If the "Memory Model" is set to anything else like "Small", then the program would not work as intended. The "Memory Model" affects what a generic pointer is. And "(L_MBUS_READ_T*)" is defining a generic pointer.
    From my understanding, the statement

    (*(L_MBUS_READ_T*)&buf[2])

    is asking the compiler to convert the pointer to &buf[2] to a generic pointer and then derefernece it. So this should work regardless of what memory area buf is allocated and regardless of what the Memroy Model is set for the project, right? Funny thing is if L_MBUS_READ_T does not contain the union, then it would work..

    Andy

    Here's the program

    /* ========================== */
    unsigned char data buf[20];

    typedef struct {
    union {
    float IEEE[1]; /* float */
    short decimal[1]; /* Virtual decimal. */
    } out;
    } L_MBUS_READ_T;

    /* Access to the buffers. */
    #define L_MBusReadStruct() (*(L_MBUS_READ_T*)&buf[2])

    void main(void) {
    unsigned char i = 0;

    L_MBusReadStruct().out.decimal[i] = 3;

    buf[0] = 0;
    }