We are running a survey to help us improve the experience for all of our members. If you see the survey appear, please take the time to tell us about your experience if you can.
In ASM51, the following code is every useful:
-------- file1.asm --------- public MAX MAX EQU 10 -------- file2.asm --------- extrn number(MAX) MOV A,#MAX
-------- file2.C ----------- extern code MAX; #define MAX_NUMBER ((unsigned char)&MAX)
One year has passed but nobody give me a good answer. I insist on "linking" the constant, because I am generating a LIB file, whose internal configuration constants should be defined by user. So, can any one give me better answer?
The usual way to do this in C would be to supply a header file with some definitions: config.h extern U8 const myVal; and tell users of the library that they need to create an object file that defines values for those constants. config.c: U8 const myVal = 99; You then link config.o with the rest of your library. Typically, use of myVal will generate code that loads myVal from some address (determined by the linker) -- something like (at a guess) ; small model MOV R0,#myVal MOV A,@R0 ; large model MOV DPTR,#myVal MOVX A,@DPTR This code will be larger than code that loads a value as an immediate value. That is, the value is included literally in the instruction, not by reference. MOV A,#99 If this difference is important to you, then life will be a little more painful. When the assembler runs, it actually generates code like this: 0100: MOV A,#00 along with an entry in the object file that says "label MAX is at address 101". The linker then replaces the byte at address 101 with the value defined for MAX. There is no way to write C code with a "placeholder" value such as the EXTERN NUMBER in the assembler. (Such syntax would have to look something like: U16 myMax = ???; where "???" is the value to be replaced by the linker. So, there's no way to write C to get that "to be determined" integer. There is a way to cheat, though. It's easy to write C code that generates addresses that get patched up by the linker. And you can cast an address. So, you could write C thus: extern U8 MAX; // type doesn't really matter U16 myMax = (U16)&MAX; Now, all you have to do is tell your linker the address of MAX. It will find all references to the address-of-MAX in the object files and fix them up, thus replacing the value in line, as desired. I find this technique "painful" because on the one hand, it's not really obvious what you're doing. It obscures the real type and purpose of MAX. And every linker has its own idiosyncratic way of defining symbols, so it's a really non-portable technique. Unless the few extra bytes of code are really going to cripple your application, you might want to just use the first method. Yet another method would be to distribute source code (obfuscated, perhaps, if piracy is a concern), put the definitions in a common header file, and have the user recompile the whole works.
One year has passed but nobody give me a good answer. ... That same year also passed without any feedback from you about the answers you did get. If you didn't consider those "good" answers, why wait all of a whole year to say so? Are you actually trying to make us believe it never occured to you that the reason none of the answers were to your liking might have been that the trick you're trying to pull is impossible? Even in the ligth of some of your answers telling you exactly that?
Hi, I have the following line in my assembly code and I want to link with C program. How to declare in C? EXTRN NUMBER (RS485_TASK, RECV_END) Thanks in advance.
"One year has passed but nobody give me a good answer." Well, I'm sorry - I didn't realise I was supposed to drop everything and concentrate on your problem! Anyway, think about what the Linker does: it deals with addresses - you define a symbol in 'C', and the Linker fixes its address. Therefore, if you want the Linker to give a constant back to 'C', it will be as the address of a 'C' symbol. Your 'C' code will have to take the address of the symbol, and interpret that address value as the required constant (probably via a cast).
Dear Hans-Bernhard Broeker, and all those who concern my question, Actually, I know all the ways everybody have mentioned. And I have tried even more. At last I found that the problem lies in the optimization defects of Keil. Let me expain in detail. Define in a configuration asm:
MAX equ 10 public MAX
extern unsigned char MAX[1];
uint8 Test(uint8 i) { if(i == (uint8)MAX) return 1; return 0; }
uint8 Test(uint8 i) { if(i == (uint8)MAX) return (uint8)MAX; return 0; }
extern code uint8 MAX
You don't give any real explanation why the differences in behaviour of the code for these two particular functions would constitute an optimizer defect. C51 functions aren't reentrant unless you ask for them to be. That has nothing to do with the subject of this thread.
"At last I found that the problem lies in the optimization defects of Keil." Or perhaps in your understanding of the 'C' programming language? "You may find the translated ASM code quite different." Well of course you will - the two source functions are quite different: The "ideal" function only has to choose one of the two manifest constants 0 or 1; the "real" function may have to return the actual value of the symbol MAX "Keil consider MAX as constant in the first example" That's because it is a constant: you defined MAX as an array; in 'C', using the array name on its own like this (with no index) is effectively a constant pointer: "MAX" is equivalent to "&MAX[0]" "But in the seconds example, MAX is used twice" Of course it is - because you used it twice in the source code! First, in the comparison operation; Second, in the return operation "so the 'register variables' optimization takes effect, which will save i to R7, and then load MAX to R7, so as to use MAX in the return statement." What else could it do? Look at the C51 calling convention in the Manual - that defines what registers must be used for passing parameters & return values. "As a result, the second function will use one more register and the ASM code size is doubled!" I make it 11 bytes for the 1st function, and 12 for the second - methinks thou dost protest too much!
; FUNCTION _idealTest (BEGIN) ; SOURCE LINE # 6 ;---- Variable 'i' assigned to Register 'R7' ---- ; SOURCE LINE # 7 ; SOURCE LINE # 8 0000 7400 E MOV A,#LOW MAX 0002 B50703 CJNE A,AR7,?C0001 ; SOURCE LINE # 9 0005 7F01 MOV R7,#01H 0007 22 RET 0008 ?C0001: ; SOURCE LINE # 10 0008 7F00 MOV R7,#00H ; SOURCE LINE # 11 000A ?C0002: 000A 22 RET ; FUNCTION _idealTest (END)
; FUNCTION _realTest (BEGIN) ; SOURCE LINE # 14 ;---- Variable 'i' assigned to Register 'R6' ---- 0000 AE07 MOV R6,AR7 ; SOURCE LINE # 15 ; SOURCE LINE # 16 0002 7F00 E MOV R7,#LOW MAX 0004 EF MOV A,R7 0005 B50601 CJNE A,AR6,?C0003 ; SOURCE LINE # 17 0008 22 RET 0009 ?C0003: ; SOURCE LINE # 18 0009 7F00 MOV R7,#00H ; SOURCE LINE # 19 000B ?C0004: 000B 22 RET ; FUNCTION _realTest (END)
uint8 goodTest( const uint8 testmax, uint8 i) { if(i == (uint8)testmax) return (uint8)testmax; return 0; }
; FUNCTION _goodTest (BEGIN) ; SOURCE LINE # 21 ;---- Variable 'testmax' assigned to Register 'R7' ---- ;---- Variable 'i' assigned to Register 'R5' ---- ; SOURCE LINE # 22 ; SOURCE LINE # 23 0000 ED MOV A,R5 0001 B50701 CJNE A,AR7,?C0005 ; SOURCE LINE # 24 0004 22 RET 0005 ?C0005: ; SOURCE LINE # 25 0005 7F00 MOV R7,#00H ; SOURCE LINE # 26 0007 ?C0006: 0007 22 RET ; FUNCTION _goodTest (END)
I think your explaination is quite good and everything is OK. Unfortunately, you did not resolve the problem that: ONE MORE REGISTER IS USED AND MAKE THE FUNCTION UN-REENTRANTABLE, because in my project, the function is quite complexed and one more abuse of register will make the function un-reentrantable. If you replace MAX with a constant value such as #define MAX 5 then the extra register will not be used and make the function reentrantable. Please note that my problem is to pass MAX as a constant whose value is defined in an ASM configuration file. In your example of passing it as a funtion parameter, it's meaningless to me. By they way, a function is inherently reentrantable if it only uses registers as local automatic variables. The "reentrant" keywork is using a software stack to realize reentrant feature. If you are not clear about this, you will hardly understant my question. For example, such functions as "memcpy", "isdigit" are reentrant (please refer to the manual), but they don't use software stack at all. Now. Please see the result of my "optimization defect" example: Example 1:
#define MAX 5 uint8 Test(uint8 i) { if(i == (uint8)MAX) return (uint8)MAX; return 0; }
0000 BF0503 CJNE R7,#05H,?C0140 0003 7F05 MOV R7,#05H 0005 22 RET 0006 ?C0140: 0006 7F00 MOV R7,#00H 0008 22 RET
extern uint8 MAX[1];
0000 AE07 MOV R6,AR7 0002 7F00 E MOV R7,#LOW MAX 0004 EF MOV A,R7 0005 B50601 CJNE A,AR6,?C0140 0008 22 RET 0009 ?C0140: 0009 7F00 MOV R7,#00H 000B 22 RET