1. Introduction
When C/C ++ and Assembly mixed programming are required, there are two processing strategies:
- If the Assembly Code is short, you can directly embed the assembly language in the C/C ++ source file to implement mixed programming.
- If the Assembly Code is long, you can write it as an assembly file separately, and add it to the project in the form of an assembly file. The program calls and accesses each other through atpcs.
2. Embedded Assembly Language commands
Embedded in Assembler using C/C ++ programs can implement functions that are not available in advanced languages and improve program execution efficiency. The Embedded Assembler of the ARMCC compiler supports the arm instruction set. The Embedded Assembler of the TCC compiler supports the thumb instruction set.
2.1 syntax format of Embedded Assembly commands
You can use the keyword _ ASM in the C language program of arm to add an assembly language program. The format is as follows:
_ ASM {Command [; command]/* comments */... command}
All the commands in {} are Assembly commands. Multiple Assembly command statements can be written in one line. The command statements must be separated by semicolons. In the Assembly instruction segment, the Comment statement adopts the C language annotation format. In the arm C ++ program, besides the keyword _ ASM can be used to identify an embedded assembly instruction program, the keyword ASM can also be used to represent an embedded assembly instruction. The format is as follows:
ASM ("command ");
Among them, the parentheses following ASM must be an assembly instruction statement and cannot contain annotation statements.
2.2 enable/disable IRQ to interrupt an instance
Void enable_irq (void) // enable the program to interrupt {int TMP; // defines the temporary variable, followed by the keyword {Mrs TMP, CPSR // load the Status Register to TMP Bic TMP, TMP, #80 // clear the IRQ control bit 0 MSR cpsr_c, TMP // Load Program Status Register} void disable_irq (void) // disable the interrupt program {int TMP; // define temporary variables, the following uses the keyword {Mrs TMP, CPSR // Of the Embedded Assembler Program to load the Status Register to TMP Orr TMP, TMP, #80 // set IRQ to 1 MSR cpsr_c, TMP // Load Program Status Register }}
2.3 Embedded Assembly considerations
The Assembly commands in the suffix. s file are compiled using the armasm assembler, while the Embedded Assembly commands in the C language program are compiled using the Embedded Assembler. There are some differences between the two assembler types, so pay attention to the following points during Embedded Assembly:
2.3.1 careful use of physical registers
Be careful when using physical registers, such as R0 ~ The N, Z, C, and V signs in R3, IP (R12), LR (R14), and CPSR. This is because these physical registers may be used when calculating C expressions in assembly code, and the N, Z, C, and V flags will be modified.
For example, calculate y = x + x/y;
_ ASM {mov r0, X // give the X value to R0 add y, R0, x/y // when calculating x/y, the R0 value will be modified}
2.3.2 allow the use of C variables in Embedded Assembler
When x/y is calculated, R0 is modified, which affects the result of R0 + x/y. C variables can be used in Embedded Assembler programs. Using C variables instead of registers R0 can solve the above problem. In this case, the Embedded Assembler allocates appropriate storage units for the variable VAR to avoid conflicts. If the Embedded Assembler cannot allocate an appropriate storage unit, it reports an error.
Int var ;__ ASM {mov var, X // give the X value to R0 add y, VAR, x/y // when calculating x/y, the R0 value will be modified}
2.3.3 registers that do not need to be saved or restored
For registers used in Embedded Assembly Language Programs, the compiler automatically saves and restores these registers during compilation. You do not need to save or recover these registers. In addition to CPSR and spsr registers, other physical registers must be assigned a value before reading. Otherwise, the compiler reports an error.
Int fun (int x) {_ ASM {pair fd sp !, {R0} // save r0, read and write first, assembly error add r0, X, #1 eor x, R0, X ldmfd SP !, {R0} // redundant} return X ;}
3. Mutual access between Assembly and C/C ++ program variables 3.1 assembly program access C/C ++ program variables
The global variables declared in the C/C ++ program can be indirectly accessed by the assembler through the address. The specific access method/steps are as follows:
1) Declare global variables in the C/C ++ program.
2) use the Import/extern pseudo command declaration in the assembler to reference this global variable.
3) read the memory address of the global variable using the LDR pseudo command.
4) use the corresponding LDR command to read the global variable based on the data type, and use the corresponding STR command to store the value of the global variable. For different types of variables, the LDR and STR commands with different options are required, as shown in the following table.
Variable types in C/C ++
|
LDR and STR commands with suffixes
|
Description
|
Unsigned char
|
Ldrb/strb
|
Unsigned character type
|
Unsigned short
|
Ldrh/strh
|
Unsigned short integer
|
Unsigned int
|
LDR/Str
|
Unsigned integer
|
Char
|
Ldrsb/strsb
|
Balanced (8-bit)
|
Short
|
Ldrsh/strsh
|
Short INTEGER (16 digits)
|
For the structure, if you know the offset of each data item, you can access it through the storage/loading command. If the structure occupies less than 8 characters, you can use LDM and STM to read and write data at one time.
Read a global variable of C, modify it, and save the new value to the global variable:
Area example4, code, readonly export asmadd import g_cval @ declare external variable g_cval, global variable asmadd LDR R1 defined in C, = g_cval @ Load Variable address LDR r0, [R1] @ read data from the address to R0 add r0, R0, #1 @ Add 1 operation STR r0, [R1] @ save variable value mov PC, LR @ program returns end
3.2 C/C ++ program access Assembler Data
Data declared in Assembler programs can be accessed by C/C ++ programs. The specific access method/steps are as follows:
1) In the assembler, use the export/Global pseudocommand to declare this symbol as a global label, which can be applied by other files.
2) define the pointer variable of the corresponding data type in the C/C ++ program.
3) assign a value to the pointer variable to the global label in the assembler and use the pointer to access data in the assembler.
Assume that a memory area is defined in the assembler and a string of characters is saved. The Assembly Code is as follows:
Export message @ declare global label message DCB "Hello $" @ defines five valid characters, $ is the end character
Extern char * message; int messagelength () {int length = 0; char * pmessage; // define the character pointer variable pmessage = message; // pointer pointing to the first address of the message memory block/* while loop, counting the length of the string */while (* pmessage! = '$') // $ Is the string Terminator {length ++; pmessage ++;} return (length); // returns the string length}
4. Functions of the compilation and C/C ++ programs call each other.
The C/C ++ program and the arm assembler program must comply with the atpcs (ARM/thumb procedure call standard) Rules. C language subprograms compiled using the C language compiler of ADS will automatically meet the user's specified atpcs type. However, for assembly languages, it is entirely necessary to rely on users to ensure that each subroutine meets the selected atpcs type. Specifically, the assembler must meet the following three conditions to implement mutual calls with the C language.
1) the corresponding atpcs rules must be observed during subroutine writing.
2) The use of stacks must comply with the corresponding atpcs rules.
3) use the-atpcs option in the assembly compiler.
4.1 atpcs basic rules
For the basic atpcs rules, see atpcs.
4.2 C program calls Assembler
The assembly program must follow the atpcs rules to ensure that the parameters are correctly transmitted during Program Calling. In this case, the C program can call the Assembly sub-function. The C program calls the assembler as follows:
1) Use the export pseudocommand In the assembler to declare that the subprogram can be used externally, so that other programs can call this subprogram.
2) use the extern keyword in the C language program to declare an external function (declare the Assembly subroutine to be called.
# Include <stdio. h> extern void strcopy (char * D, const char * s); // declare an external function, that is, the assembler subroutine int main (void) to be called) {const char * srcstr = "first ource"; // defines the String constant char dststr [] = "second string-destination"; // defines the string variable printf ("before copying: \ n "); printf (" src = % s, DST = % s \ n ", srcstr, dststr); // display strcopy (dststr, srcstr); // call the Assembly subroutine R0 = dststr, R1 = srcstr printf ("after copying: \ n"); printf ("src = % s, DST = % s \ n ", srcstr, dststr); // display the copied result return (0 );}
The strcopy implementation code is as follows:
Area example, code, readonly @ declare the code segment example export strcopy @ declare strcopy, so that the external function calls strcopy @ R0 as the address of the target string, and R1 is the address of the source string ldrb R2, [R1], #1 @ read byte data, add 1 strb R2 to the source address, [R0], #1 @ Save the read 1 byte data, and Add 1 CMP R2 to the target address, #0 @ determine whether the characters have been copied. BNE strcopy @ the characters have not been copied. Continue copying the mov PC and LR cyclically.
4.3 assembler calls C program
The assembly program settings must follow the aptcs rules to ensure that the parameters are correctly transmitted during program calls. The method by which the assembler calls the C program is as follows:
1) Use the import pseudo command in the assembler to declare the C program function to be called.
2) when calling the C program, set the entry parameters correctly and then use the BL command to call the program.
Int sum (int A, int B, int C, int D, int e) {return (A + B + C + D + E ); // returns the sum of the five variables}
Area example, code, readonly import sum @ declares the external number sum, that is, the C function Sum () Export callsumcallsum extends fd sp !, {LR} @ LR Register into Stack mov r0, #1 @ set the sum function entry parameter, R0 is the parameter a mov R1, #2 @ R1 is the parameter B mov R2, #3 @ R2: parameter C mov R3, #5 @ parameter E = 5. Save it to STR R3, {sp, #-4} in the stack }! MoV R3, #4 @ R3 is the parameter D, D = 4 BL sum @. Call the sum function in the C program and put the result in R0 to add SP, SP, #4 @ adjust the stack pointer ldmfd sp, {PC} @ the program returns the end
The above program uses five parameters, respectively using registers R0 to store 1st parameters, R1 to store 2nd parameters, R2 to store 3rd parameters, and R3 to store 4th parameters, 5th parameters are transferred using stacks. Because stack passing parameters are used, you need to adjust the stack pointer after the program call ends. The assembler calls the sum sub-function of the C program, implements 1 + 2 + 3 + 4 + 5, and finally the sum result is saved in the R0 register.