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.
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); }
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
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
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;
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
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 }
// 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'm sorry, the line
sbit ACC3 = ACC^3; // bit ACC.0 in assembler
sbit ACC3 = ACC^3; // bit ACC.3 in assembler
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