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

Passing Variables between Assembly & C functions

Hi All,

I'm more of a Assembly guy than C.

I came across this forum when I am seeking for some quick guide of sharing data registers / variables of different files of C and .a51 in a simple program.

Calling each other from C to get the function written in .a51 or vice-versa is completed. Went through c2asm2c.zip, tested and find that it worked perfectly. However, I encounter some trouble when I need to pass on some variables that need to be manipulated from .a51 to .c

My case:-

Main code is in .a51. Requires to call a function that adds 20 slightly different 8bit data bytes and then take its average (i.e. by division).

Writing the function above in .a51 is a pain due to unavailable 16bit-addition in 8051 done under Keil uV2.

My solution thought:- is to write the addition function in C so addition can be done much more easily.

Problem:- how do I pass the variables/registers defined in .a51 to the C function to be called from .a51 environment? The C function failed to recognize the similar declared register under .a51 before. Do I need to declare them under a new name OR use the similar name of data registers OR can use some global declaration of data registers. But how?

Appreciate some really cool advice if available.

Cheers,
James

  • Hi James,

    My solution is: writing your function and call it in C code, then let the Keil Translate it into ASM code. (You know how to do that, right?) You will know what you need to do in your hand written ASM code to call the C function.
    The following is just an example:

    #include <REG52.H>
    
    #define BYTE 	unsigned char
    #define WORD 	unsigned short
    
    BYTE xdata RawData[20];
    BYTE Average(BYTE * pInputData, BYTE len);
    void main (void)
    {
            BYTE Result;
    
    	while (1)
    	{
    		Result = Average(&RawData[0], 20);
    	}
    }
    
    BYTE Average(BYTE * pInputData, BYTE len)
    {
    	BYTE i;
    	WORD sum = 0;
    
    	for (i=0; i<len; i++)
    		sum += *(pInputData ++);
    
    	return (sum/len);
    }
    

    Then you can find the following result in your main.src
    	RSEG  ?XD?MAIN
            RawData:   DS   20
    ; void main (void)
    
    	RSEG  ?PR?main?MAIN
    main:
    	USING	0
    			; SOURCE LINE # 26
    ; {
    			; SOURCE LINE # 27
    ?C0001:
    ; 	BYTE Result;
    ;
    ; 	while (1)
    			; SOURCE LINE # 30
    ; 	{
    			; SOURCE LINE # 31
    ; 		Result = Average(&RawData[0], 20);
    			; SOURCE LINE # 32
    	MOV  	R3,#01H
    	MOV  	R2,#HIGH (RawData)
    	MOV  	R1,#LOW (RawData)
    	MOV  	R5,#014H
    	LCALL	_Average
    	MOV  	Result?040,R7
    ; 	}
    			; SOURCE LINE # 33
    	SJMP 	?C0001
    ?C0002:
    ; }
    			; SOURCE LINE # 34
    ?C0003:
    	RET
    ; END OF main
    
    ; BYTE Average(BYTE * pInputData, BYTE len)
    
    	RSEG  ?PR?_Average?MAIN
    _Average:
    	USING	0
    			; SOURCE LINE # 38
    	MOV  	pInputData?141,R3
    	MOV  	pInputData?141+01H,R2
    	MOV  	pInputData?141+02H,R1
    	MOV  	len?142,R5
    ; {
    			; SOURCE LINE # 39
    ; 	BYTE i;
    ; 	WORD sum = 0;
    			; SOURCE LINE # 41
    	MOV  	sum?144,#00H
    	MOV  	sum?144+01H,#00H
    ;
    ; 	for (i=0; i<len; i++)
    			; SOURCE LINE # 43
    	MOV  	i?143,#00H
    ?C0004:
    	MOV  	A,i?143
    	CLR  	C
    	SUBB 	A,len?142
    	JNC  	?C0005
    ; 		sum += *(pInputData ++);
    			; SOURCE LINE # 44
    	MOV  	R3,pInputData?141
    	INC  	pInputData?141+02H
    	MOV  	A,pInputData?141+02H
    	MOV  	R2,pInputData?141+01H
    	JNZ  	?C0008
    	INC  	pInputData?141+01H
    ?C0008:
    	DEC  	A
    	MOV  	R1,A
    	LCALL	?C?CLDPTR
    	MOV  	R7,A
    	MOV  	R6,#00H
    	MOV  	A,R7
    	ADD  	A,sum?144+01H
    	MOV  	sum?144+01H,A
    	MOV  	A,R6
    	ADDC 	A,sum?144
    	MOV  	sum?144,A
    ?C0006:
    	INC  	i?143
    	SJMP 	?C0004
    ?C0005:
    ;
    ; 	return (sum/len);
    			; SOURCE LINE # 46
    	MOV  	R7,len?142
    	MOV  	A,R7
    	MOV  	R5,A
    	MOV  	R4,#00H
    	MOV  	R6,sum?144
    	MOV  	R7,sum?144+01H
    	LCALL	?C?UIDIV
    ; }
    			; SOURCE LINE # 47
    ?C0007:
    	RET
    ; END OF _Average
    

    Hope it will help.
    Frank

  • Will it help?
    Or have you meant anything else?

    1st file - asm code: (variables defined here)

    public  Char_data, Int_data, Int_dataH, Int_dataL
    
    ;---
    ?DT?DATA_SEG    segment data
    
            rseg    ?DT?DATA_SEG
    
    Char_dataA:     ds      1               ; 1st (unsigned) char variable
    Char_dataB:     ds      1               ; 2nd (unsigned) char variable
    
    Int_data:       ds      2               ; (unsigned) int variable
    Int_dataH       data    Int_data        ; MSB (Big Endian manner)
    Int_dataL       data    Int_data+1      ; LSB
    
    Long_data:      ds      4               ; (unsigned) long variable
    Long_data1      data    Long_data       ; MSB
    Long_data2      data    Long_data+1
    Long_data3      data    Long_data+2
    Long_data4      data    Long_data+3     ; LSB
    
    ;---
    ?PR?PROG_SEG    segment code
    
            rseg    ?PR?PROG_SEG
    
    ; A51 program code
    ; bla bla ...
    
            end
    

    2nd file - C code: (variables used here)

    extern unsigned char Char_dataA;        // external variables declarations
    extern unsigned char Char_dataA;
    extern unsigned int Int_data;
    extern unsigned long Long_data;
    
    unsigned char Divider;
    unsigned char Result;
    
    void main(void)
    {
      //
      Int_data = ((unsigned int)Char_data_A + (unsigned int)Char_dataB)/Divider;
    
      // ...sum of 'chars' divided by their number gives 'char' again (=average)
      Result = Int_data;    // casting, better explicitly: (unsigned char)Int_data
      // ...
    }
    

  • 2nd line in declarations must be of course: (sorry)

    extern unsigned char Char_dataB;
    
    Eric

  • Thanks Eric & Frank!
    Terrific advice no doubt.
    Will give it a shot and tell you guys the outcome soon.
    Cheers,
    James

  • Hi Eric,

    I have tried passing of the variables from A51 to C using the proper extern expressions. It worked! How glad am I. Thanks man!

    Now, on RAM array declaration, there is an error when compiling in Keil's uVision2 that says DATA-ADDRESS EXPECTED for Int_dataH, L and Long_data1,2,3 and etc.

    On the other hand, when I specified a RAM addy say 0x050 for DataGroup1 there is no error with it.

    When the compiler looks for a specific address, must I always do it as in DataGroup, i.e. give it a RAM addy?
    I never need to do this in MOT/Microchip's own compilers.

    Some kind advice please? Is there any 'org' commands for RAM declarations? pointers etc?

    BTW, It's uVision2 I'm talking about here.

    Cheers all,
    James


    ;---- RAM Declarations ---------------
    Public DataGroup1, Int_data

    DataGroup: DS 20
    DataGroup1 DATA 0x050 ;????
    DataGroup2 DATA 0x051 ;????
    ;--- trouble in specifying hard address for every datagroup!!!

    Int_data: ds 2 ; (unsigned) int variable
    Int_dataH data Int_data ; MSB (Big Endian manner)
    Int_dataL data Int_data+1 ; LSB
    ;--- This is what I want. pointers

    Long_data: ds 4 ; (unsigned) long variable
    Long_data1 data Long_data ; MSB
    Long_data2 data Long_data+1
    Long_data3 data Long_data+2
    Long_data4 data Long_data+3 ; LSB

    Flags1 DATA 0x020 ;contains Timer0IntHap, sleepingnow and etc

  • Hi James,<br>
    I've prepared a "short" cookbook - 3 files - C, A51 and H.<br>
    <br>
    Here you go:<br>
    A51 source file (~frame):<br>

    #include <test_h.H>      ; A51 accepts (with some restrictions) header files with SFR and other definitions
    
    ; tell the linker known symbol names:
    public  DataGroup1, DataGroup2
    public  Char_dataA,Char_dataB, Int_data, Int_dataH, Int_dataL
    public  Long_data, Long_data1, Long_data2, Long_data3, Long_data4
    public  Flags1, Flags2
    public  Flag0, Flag1, abc, Flag7
    public  Bit_var1, Bit_var2
    public  Char_array
    public  Char_idata, Char_xdata
    
    public  my_function_A51, _my_regpar_function_A51
    
    ; tell the linker external symbol names needed for this file
    extrn   bit     (bit_var1_C, bit_var2_C)        ; bit variables defined in a C file and used here
    extrn   data    (data_var1_C, data_var2_C)      ; data variables defined in a C file and used here
    extrn   idata   (idata_var_C)                   ; idata variables defined in a C file and used here
    extrn   xdata   (xdata_var_C)                   ; xdata variables defined in a C file, also for pdata vars
    extrn   code    (func_C, _func_regpar_C)        ; function defined in a C file and used here
                                                    ; for func_C(void)
                                                    ; and func_regpar_C(..params) - in C without underscore!
    ;extrn   number  (MAX, MIN)                     ; defined in another asm file (here it doesn't exist)
    
    ; Absolute
    
    ;---
    ;These are the "hard" addresses for (only) byte addressable variables
    DataGroup1      DATA    0x050           ; (why is this simple variable called ..Group?)
    DataGroup2      DATA    0x051
    
    ;This is the "hard" address for a bit addressable variable:
    Flags1          DATA    0x020           ;contains Timer0IntHap, sleepingnow and etc
    
    
    ; Relative
    ; define data, bdata, idata, xdata, pdata relative segments
    
    ;---                                            ; "?BA?BDATA_SEG" is YOUR name of the segment
    ?BA?BDATA_SEG   segment data    bitaddressable  ; you can specify a bitaddressable
                                                    ; relocatable data segment this way
            rseg    ?BA?BDATA_SEG           ; the specified segment starts here
    
    Flags2:         ds      1               ; linker will locate it in the bitaddressable
                                            ; internal RAM area (20H to 2FH); here probably 21H
    Flag0   bit     Flags2.0                ; you can specify names
    Flag1   bit     Flags2.1                ; for particular bit this way
    abc     bit     Flags2.3                ; the same of course for Flags1 defined above
    ;...
    Flag7   bit     Flags2.7
    
    
    ;---                                    ; "?BI?BIT_SEG" is YOUR name of the segment
    ?BI?BIT_SEG     segment bit             ; you can specify a relocatable
                                            ; bit segment this way
            rseg    ?BI?BIT_SEG             ; the specified segment starts here
    
    Bit_var1:       dbit    1               ; bit variable1, linker will locate it
                                            ; in the bitaddressable area 20h to 2FH (bit addresses 0 to 7FH)
    Bit_var2:       dbit    1               ; bit variable2
    
    
    ;---                                    ; "?DT?DATA_SEG" is YOUR name of the segment
    ?DT?DATA_SEG    segment data            ; you can specify a relocatable data segment
                                            ; this way (linker will locate it in 0H to 7FH area)
            rseg    ?DT?DATA_SEG            ; the specified data segment starts here
    
    Char_dataA:     ds      1               ; 1st (unsigned) char variable
    Char_dataB:     ds      1               ; 2nd (unsigned) char variable
    
    Int_data:       ds      2               ; (unsigned) int variable
    Int_dataH       data    Int_data        ; MSB (Big Endian manner)
    Int_dataL       data    Int_data+1      ; LSB this way you can specify components of longer types
    
    Long_data:      ds      4               ; (unsigned) long variable
    Long_data1      data    Long_data       ; MSB ...
    Long_data2      data    Long_data+1     ; particular components
    Long_data3      data    Long_data+2     ; of long variable
    Long_data4      data    Long_data+3     ; ... LSB
    
    Char_array:     ds      A_LENGTH        ; array of chars (size in the included header file)
                                            ; just for illustration, needn't be defined
    char_array0     data    Char_array      ; 1st - Char_array[0]
    ;...
    char_array4     data    Char_array+4    ; 5th - Char_array[4]
    
    ;---                                    ; "?ID?IDATA_SEG" is YOUR name of the segment
    ?ID?IDATA_SEG   segment idata           ; you can specify a relocatable idata segment
                                            ; this way (linker will locate it in 0H to FFH area, if available)
                                            ; behind the last DATA segment
            rseg    ?ID?IDATA_SEG           ; the specified xdata segment starts here
    
    Char_idata:     ds      1               ; (unsigned) char variable in IDATA
    
    ;---                                    ; "?XD?XDATA_SEG" is YOUR name of the segment
    ?XD?XDATA_SEG   segment xdata ;inpage   ; you can specify a relocatable xdata segment
                                            ; this way (linker will locate it in available XRAM area)
                                            ; optionable "inpage" (omit the semicolon) tells the linker
                                            ; to locate it in a memory page
            rseg    ?XD?XDATA_SEG           ; the specified xdata segment starts here
    
    Char_xdata:     ds      1               ; (unsigned) char variable in XDATA
    
    
    ; define program segment(s)
    ;---                                    ; "?PR?PROG_SEG" is YOUR name of the segment
    ?PR?PROG_SEG    segment code            ; you can specify a relocatable
                                            ; code (program) segment this way
            rseg    ?PR?PROG_SEG            ; the specified segment starts here
    
    my_function_A51:
    ; A51 function code (here empty)
            ret
    
    _my_regpar_function_A51:                ; name of an "A51 written" function passing parameters
    ; take the (register)                   ; in registers starts with the underline character: _
    ; passed parameters (here in R7)
            mov     Char_dataB,r7           ; my_regpar_function_A51(uchar x)
                                            ; {
                                            ;   Char_dataB = x;
                                            ; }
            ret
    ; bla bla ...
            call    func_C                  ; function without parameters written in C
    
    ; prepare actual parameters in appropriate registers
            call    _func_regpar_C          ; function with parameters written in C
    ; bla bla
    
            end
    

    ... to be continued :-(

  • continuing...

    C source file:

    #include <test_h.H>   // include SFR and other definitions/macros
    
    // tell the linker known symbol names:
    
    // data DECLARED here (no memory space assigned here)
    // external data variables
    extern unsigned char data Char_dataA;
    extern unsigned char data Char_dataB;
    extern unsigned char data DataGroup1;
    extern unsigned char data DataGroup2;
    
    extern unsigned int data Int_data;
                                                    // not necessary:
    extern unsigned char data Int_dataH;            // MSB component of Int_data if you need it
    extern unsigned char data Int_dataL;            // LSB (Big Endian manner)
    
    extern unsigned long data Long_data;
                                                    // not necessary:
    extern unsigned char data Long_data1;           // MSB...
    extern unsigned char data Long_data2;           // t   components of Long_data
    extern unsigned char data Long_data3;           // o   if you need it
    extern unsigned char data Long_data4;           // ...LSB
    
    extern unsigned char data Char_array[A_LENGTH]; // defined in asm file
    
    // external bit variables
    extern bit Flag0;
    extern bit Flag1;
    extern bit abc;
    extern bit Flag7;
    extern bit Bit_var1;
    extern bit Bit_var2;
    
    // external bdata variables
    extern unsigned char bdata Flags1;
    extern unsigned char bdata Flags2;
    
    //Note:
    // sbit xyz = Flags1^3;       // names for particular bits of EXTERNAL
                                  // bdata variables CAN'T be defined this way,
    
    unsigned char bdata Flags3;   // but if the variable is DEFINED here,
    
    sbit xyz = Flags3^5;          // then names for particular bits of such
    sbit MNO = Flags3^7;          // bdata variables CAN be defined this way
    
    // external idata variables
    extern unsigned char idata Char_idata;
    
    // external xdata variables
    extern unsigned char xdata Char_xdata;
    
    // external functions
    extern void my_function_A51(void);
    extern void my_regpar_function_A51(unsigned char x);
    
    
    // global data DEFINED here (memory space assigned here)
    unsigned char data Divider;     // data type either explicitly (data)
    unsigned int abcd;              // or given by memory model
    unsigned char xdata Result;     // Result explicitly in xdata memory
    
    bit bit_var1_C;                   // bit variables defined in a C file
    bit bit_var2_C;
    
    unsigned char data data_var1_C;   // data variables defined in a C file
    unsigned char data data_var2_C;
    unsigned char idata idata_var_C;  // idata variables defined in a C file
    unsigned char xdata xdata_var_C;  // xdata variables defined in a C file
    
    void func_C(void)
    {
      // empty here
    }
    
    #pragma REGPARMS    // it is default
    void func_regpar_C(unsigned int y)  // function with parameters
    {
      DPTR = y; // use parameter(s)
    }
    
    void main(void)       // an example, mostly MEANINGLESS !!
    {
      unsigned char a, i; // local data (data type given by memory model used)
    
      if (Flag0) {
        a = Int_dataL + Long_data2;
      }
      else {
        a = Char_array[3];
      }
    
      Int_data = 0;
      for (i = 0; i < A_LENGTH; i++) {
        Int_data += Char_array[i];
      }
      Int_data /= A_LENGTH;
    
      // ...sum of 'chars' divided by their number gives 'char' again (=average)
      Result = Int_data;    // casting, better explicitly: (unsigned char)Int_data
    
      my_function_A51();                  // call external function without parameters
      my_regpar_function_A51(Char_idata); // call external function with parameter(s)
    
      while(1); // stop here forever
    }
    

    and "Test_h.H" header file:
    // put SFR and other definitions here (examples here)
    sfr ACC = 0xE0;                 // accumulator SFR address
    sfr PSW = 0xD0;                 // Program Status Register SFR address
    
    sbit ACC3 = ACC^3;              // bit ACC.0 in assembler
    sbit CY = PSW^7;                // bit Carry Flag - 7th bit of bitaddressable register PSW
                                    // bitaddressable SFRs have address' lower nibble 0 or 8
    
    sfr TL2 = 0xCC;                 // if LSByte address of a 16-bit SFR register (here Timer2)
    sfr TH2 = 0xCD;                 // is by 1 lower than its MSByte address,
    sfr16 TIMER2 = 0xCC; // =TL2    // then sfr16 can be defined for such an SFR register
                                    // and compiler will arrange everything else
    
    sfr DPL = 0x82;
    sfr DPH = 0x83;
    sfr16 DPTR = 0x82;
                                    // miscellaneous constants or preprocessor macro definitions:
    #define A_LENGTH        5       // size of the array "Char_array"
    

    I am sorry if it is too long.
    Copy and study it. Hope it helps you.

    Best wishes!
    Eric

  • I'm sorry, the line

    
    sbit ACC3 = ACC^3;              // bit ACC.0 in assembler
    
    in "test_h.H" file should have been

    sbit ACC3 = ACC^3;              // bit ACC.3 in assembler
    

    of course.

    Eric

  • Hi Eric,

    Thanks for very detailed 'cookbook'. Appreciate it so much. Will check it out ASAP and let you know the good results I'd reckon.

    cheerio,
    James