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
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). No. That is not correct. There is no memory area associated with the type cast. There is only a data type. Besides, if an object is in data memory and you cast a pointer to it into a pointer into a different memory area, then of course the pointer is pointing to the wrong thing. Anyway, when I compile you example, the compiler generates the same code no matter what memory model I use. And, the code generated works OK.
C251 COMPILER V3.53, COMPILATION OF MODULE main OBJECT MODULE PLACED IN main.OBJ COMPILER INVOKED BY: C:\Keil\C251\BIN\C251.EXE main.c LARGE INTR2 BROWSE DEBUG CODE SYMBOLS stmt level source 1 unsigned char data buf[20]; 2 3 typedef struct { 4 int i; 5 int j; 6 } TEST_T; 7 8 #define GetStruct() (*(TEST_T *)&buf[2]) 9 10 void main(void) { 11 1 GetStruct().i = 3; 12 1 } ASSEMBLY LISTING OF GENERATED OBJECT CODE ; FUNCTION main (BEGIN) ; SOURCE LINE # 10 ; SOURCE LINE # 11 000000 7E340003 MOV WR6,#03H 000004 7A3500 R MOV buf+2,WR6 ; SOURCE LINE # 12 000007 22 RET ; FUNCTION main (END)
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
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; }