I have an application, for an 8051 device, that accepts commands, acts on them, and provides a reply. I want to implement a command that will load code (for an new function) into a reserved section of Code memory so that the code will run when a different command jumps to it. I'm having trouble getting the standalone function, which needs to access data arrays and other functions in the main application, to link (BL51 linker) without warnings. My test function is:
void Cipher (unsigned char *in, unsigned char *out); extern unsigned char xdata Data_Bffer[]; extern unsigned char xdata PlainTxt[]; void User1(void) { PlainTxt[0] = 0x06; //ASCII "ACK" PlainTxt[1] = 0xAA; PlainTxt[2] = 0x55; PlainTxt[3] = 0xAA; PlainTxt[4] = 0x55; PlainTxt[5] = 0xAA; PlainTxt[6] = 0x55; PlainTxt[7] = 0xAA; PlainTxt[8] = 0x55; PlainTxt[9] = 0xAA; PlainTxt[10] = 0x55; PlainTxt[11] = 0xAA; PlainTxt[12] = 0x55; PlainTxt[13] = 0xAA; PlainTxt[14] = 0x55; PlainTxt[15] = 0xAA; Cipher(&PlainTxt[0], &Data_Bffer[0]); return; }
and the project also contains the following assembly code file:
$NOMOD51 PUBLIC Data_Bffer PUBLIC PlainTxt PUBLIC Cipher Data_Bffer EQU 0DE0h PlainTxt EQU 0FF0h Cipher EQU 2104h END
where the addresses for the data arrays and the function Cipher are taken from the .M51 file of the main application. I'm getting warnings like the following from the linker:
*** WARNING L1: UNRESOLVED EXTERNAL SYMBOL SYMBOL: _CIPHER MODULE: C:\Users\R\UserFunctions.obj (USERFUNCTIONS)>> *** WARNING L1: UNRESOLVED EXTERNAL SYMBOL SYMBOL: ?_CIPHER?BYTE MODULE: C:\Users\R\UserFunctions.obj (USERFUNCTIONS)
My Assembler, Compiler, and Linker command lines are unaltered from the defaults (in the Silicon Labs IDE); e.g. linker command line is:
RS(256) PL(68) PW(78)
If I change "Cipher" to "_Cipher" in the Assembly file, then the first warning goes away. But I don't know what to do about "?_CIPHER?BYTE". Anyone tried something like this before and know the fix?
OK, I will explain this to you in more details. From your C source code, you are calling the function:
void Cipher (unsigned char *in, unsigned char *out);
This function has two generic pointer (=3-byte) as parameters. You should be aware how parameters are passed in the C51 compiler. You can find an explanation and examples on this page: http://www.keil.com/support/man/docs/c51/c51_ap_parampassreg.htmSo with the default compiler options, the first generic pointer (in) will be written into R1-3. The second generic pointer (out) cannot be passed in registers and will therefore be written into memory. This is explained on this page: http://www.keil.com/support/man/docs/c51/c51_ap_parampassmem.htm
Since at least one parameter is passed in registers, the compiler will generate a call to the symbol _Cipher. This also influences the segment name for code segment ?PR?_CIPHER?FileName and the parameter passing segment ?_CIPHER?BYTE. If you do not have local variables in the function Cipher, the segment ?_CIPHER?BYTE will have to have a size of 6 bytes. The first 3 bytes are reserved for parameter 1 (which is passed in R1-3) and the second parameter is passed in byte 3,4,5 of the segment ?_CIPHER?BYTE. In your assembly function, you need to define the symbol _CIPHER, the code segment ?PR?_CIPHER?FileName and the parameter passing segment ?_CIPHER?BYTE.
In case you want to pass all parameters in memory and not in registers, you can use the C51 option NOREGPARMS (see my last email). Then the _ is not added to the symbol name and segment names. Both generic pointers will be passed in the parameter passing segment ?_CIPHER?BYTE.
In order to get all the segment names and symbols right, you could define the (empty) function 'Cipher' in C as the only function in a C source file. You could then translate this module with the option SRC (see http://www.keil.com/support/man/docs/c51/c51_src.htm). The compiler then generates an assembly source code with all necessary definitions and segments.
Hans, thanks again. I tried adding ?PR?_CIPHER? and ?_CIPHER?BYTE to my assembly function (i.e. "PUBLIC ?_CIPHER?BYTE"), but was getting errors that the leading character (?) was not recognized. I tried your other suggestion using the SRC option, then adding the resulting .SRC file to my project. This left me with the warning: *** WARNING L10: CANNOT DETERMINE ROOT SEGMENTNot sure if this is critical - I'll see if I can get it to work.
> but was getting errors that the leading character (?) was not recognized
I don't know where this comes from. The '?' is a valid character for assembly symbols and segment names. I would need an example for this.
>This left me with the warning: *** WARNING L10: CANNOT DETERMINE ROOT SEGMENT
This is a different issue. This is a linker warning which says that the linker cannot determine where your program starts. You probably don't use our standard Startup.A51 file in your project or you don't have a C 'main' function. The linker analyzes the program flow by looking at the references and segment names. The correct program flow is necessary to get the data overlaying right. If you don't need data overlaying, you can use the linker directive NOOVERLAY. This will also make this warning disappear.
I don't have any startup file or 'main' function in this 'project' - all of that is in the main project which is already compiled, linked, and loaded into the microcontroller. That main project contains an empty User1 function at a specific location in code, as well as the Cipher function. This project is meant to be a single .c file that gets compiled and linked (with a CODE directive so it goes to the correct place in memory) to generate a .hex file. The intent is that the main program can accept the .hex file, write it to code memory, and then execute it whenever the User1 function is called. Thus, I guess the ROOT SEGMENT warning is OK?
For the other issue, the code I had in the assembly file was:
$NOMOD51 PUBLIC Data_Bffer PUBLIC PlainTxt PUBLIC _Cipher PUBLIC ?_Cipher?BYTE PUBLIC ?PR?_CIPHER?AES_CIPHER Data_Bffer EQU 0DE0h PlainTxt EQU 0FF0h _Cipher EQU 2104h END
and the Assembler messages were
ASSEMBLER INVOKED BY: C:\Keil_v9.56\C51\BIN\A51.EXE Locations.A51 XR GEN DB EP NOMOD51 INCDIR(C:\SiLabs\MCU_3\Inc) LOC OBJ LINE SOURCE 10 PUBLIC ?_Cipher?BYTE*** _________________________________^*** ERROR #A45 IN 10 (Locations.A51, LINE 10): UNDEFINED SYMBOL (PASS-2) 11 PUBLIC ?PR?_CIPHER?AES_CIPHER*** _________________________________^*** ERROR #A45 IN 11 (Locations.A51, LINE 11): UNDEFINED SYMBOL (PASS-2)
where the "^" lines up under the leading "?" for both.
OK, I understand your program structure now. Since data overlaying does not make sense for this function, I would use the linker direktive NOOVERLAY (or NOOL) and the linker warning about the root segment will go away.
I think the assembler errors are obvious. You want to make the symbol ?_CIPHER ?BYTE public but you have not defined it. In the example below, I defined it, but you need to take the address of ?_Cipher?BYTE from your main program (like you do for _Cipher). The address is fixed when you link your main program. Depending on the memory model you use, this can be a data, pdata or xdata address.
The same applies for the segment name, but I see no need to define the segment and make it public. Therefore I removed it.
$NOMOD51 PUBLIC Data_Bffer PUBLIC PlainTxt PUBLIC _Cipher PUBLIC ?_Cipher?BYTE Data_Bffer EQU 0DE0h PlainTxt EQU 0FF0h _Cipher EQU 2104h ?_Cipher?BYTE EQU 0030h ; adapt this address!!! END
Hint: If the function Cipher only gets pointers to xdata, you could redefine the function Cipher to take memory specific pointers. The prototype for this function would then be:
void Cipher (unsigned char xdata *in, unsigned char xdata *out);
This would be more efficient because both pointers would be passed in registers (in = R6/7 and out = R4/5) and you would not have to deal with ?_Cipher?BYTE. This change would require a modification of the function Cipher in your main program.
Sorry, I did not understand what you meant by "define" - from your correction of my example code I now see what you meant. Unfortunately, ?_Cipher?BYTE does not appear in the .M51 file from my main program so I can not get the address - this is why I did not have it on a line with an EQU. Both "in" and "out" appear in the .M51 file, perhaps I could use them instead?
I'm going to try your suggestion of redefining the prototype for Cipher to specify xdata, that sounds like a straight forward fix, and I don't see any reason not to (this was a function I obtained from somewhere else, and had left the way it was). And I will use the NOOVERLAY directive.
Changing the Cipher prototype to specify XDATA for the arguments and then using the NOOVERLAY directive eliminated the warnings I was receiving. Thanks.