C language is a very popular programming language. In addition to the advantages of easy and flexible use of advanced languages, strong data processing capabilities, and simple programming, C language can also implement most of the functions of assembly language, for example, you can directly operate on the hardware, generate a high quality target code, and execute the code quickly. Therefore, when the hardware processing speed is not very high in engineering, C can basically be used to replace the Assembly Language and write the control software of the interface circuit. However, C cannot completely replace assembly languages. For example, in some real-time control systems with high speed requirements and special control over hardware, C is sometimes not fully competent, compilation languages are also required. Because the target code of the assembly language is more refined, the direct control capability of the hardware is stronger and the execution speed is faster, but the compilation language programming is difficult and the presentation capability is poor. A better solution is the mixed programming of C and assembly language, that is, the scheduling program, user interface, and low speed control part of the software written in C, however, the compilation language provides the highest-speed processing module for the speed-sensitive part for C calls. This method provides the best software design solution, achieving both high speed, high flexibility and convenience. As my graduation project requires the C program to call the Assembly Module Method to Improve the execution speed of arm's fixed-point commands, I learned this. The learning experience is as follows:
The C and assembly language interfaces have two major problems to solve.
1. parameter transfer between the caller and the called
This type of data is passed through the stack. When the call is executed, it is automatically pushed to the stack starting from the last parameter in the call program parameter table. After all the parameters are pushed to the stack, then, the return address (breakpoint) after the execution of the called program is automatically pushed into the stack, so that the called program can return the correct position of the main program and continue execution. For example, call the main function named add assembler module: Main (){...... add (DEST, OP1, OP2, flages );......}. In this example, the main function is de-assembled, and the main function automatically organizes the stack before calling the Add function.
.
.
.
Lea 0xfffffffe8 (% EBP), % eax # The first address of the flages array into the stack
Push % eax
Pushl 0xfffffff8 (% EBP) # OP2 inbound Stack
Pushl 0 xfffffffc (% EBP) # OP1 inbound Stack
Pushl 0xfffffff0 (% EBP) # DEST address
Call 0x80483f0 <add> # Call the Add function
.
.
After the Add CALL statement is executed, data in the stack is shown in result 1.
After entering the Assembly subroutine, in order to correctly obtain the main program and store the data in the stack, the called Assembly subroutine has to do the following:
1. Save a copy of ESP
After the Assembly subroutine is entered, the operation of pressure stack and exit stack is inevitable in the subroutine, So ESP is changing at all times. In order to use ESP to access parameters in the stack, the security method is to generate a copy for ESP after entering the subroutine, and then use a copy for access to the transmitted parameters. You can use EBP to save ESP, for example:
Push % EBP
MoV % EBP, % ESP
2. retain data space
If some local data is required in the Assembly subroutine, the ESP value can be reduced to reserve a storage area in the stack space for storing local data, the region must be restored after the subroutine ends. The following statement retains a local data zone:
Push % EBP
MoV % EBP, % ESP
Subl space, % ESP; set space = 4
Movl $0x0, % EBP
Movl $0x0,-2 (% EBP)
In the preceding statement segment, space is the total number of bytes of local data. In future applications, because ESP is changing and EBP is fixed, local variables can be accessed using a negative offset. In the preceding example, the local data of two words is initialized to 0 using EBP and offset.
3. reserve the register value
If you use ESI, EDI, and other registers in the program, you should first press them into the stack to retain the original register value. For example, the following example is to apply the values of ESI and EDI registers to the stack:
Pushl % EBP
Movl % EBP, % ESP
Subl $ space, % ESP,
Pushl % ESI
Pushl % EDI
4. Get Transfer Parameters
Completed 1 ~ After the three-step operation, combined with the above C program transfer parameter example, now stack structure 2 is shown.
It can be seen that EBP retains a copy of ESP after the parameters are passed and the EBP is pressed to the stack. Using EBP, you can easily access each parameter. It is assumed that each parameter is an integer of 2 bytes, which occupies 2 bytes in the small mode. If you want to retrieve the passed OP1 and OP2 parameters and assign them to the EBX and ECx registers, you can use the following statements to complete this function:
Movl 0x8 (% EBP), % eax
Movl 0xc (% EBP), % ECx
5. subroutine Return Value
When the execution result of a subroutine needs to be returned, C receives the return value according to the length of the returned value according to the following conventions: 1 byte in the Al register; 2 byte in the eax register; 4 bytes, the high part is in edX, and the low part is in eax register. C can return values from these registers.
6. Exit the Assembly subroutine.
The procedure for ending the Assembly subroutine is as follows:
1) if ESS, EDS, ESI, or EDI has been pushed to the stack, they should be popped up in reverse order of saving them.
2) if local data space is allocated at the beginning of the process, the command mov % ESP and % EBP are used to restore % ESP.
3) Restore % EBP with the command pop % EBP. This step is required. Or you can use the leave statement to restore % EBP. It is equivalent to movl % EBP, % ESP; popl % EBP
4) End the assembler with ret.
Ii. Description and establish the connection between the caller and the called user
To establish the connection between the call and the called module, the called assembler applies global, indicating that it can be called by the external module; the calling program should predefine the name of the external module to be referenced. The following uses my example to illustrate how C calls the Assembly subroutine of add0. The program list is as follows:
/* Add. C */
# Include <stdio. h>
Extern void add (int * DEST, int OP1, int OP2, short int * flages );
/* Declare to call external assembler functions */
Int main (void ){
Int OP1, OP2, result;
Int * DEST = & result;
Short int flages [4] = {0, 0, 0 };
Printf ("Please enter two soure operater :");
Scanf ("% x", & OP1, & OP2 );
Add (DEST, OP1, OP2, flages);/* call the add0 function */
Printf ("the result of ADD is: % x/n flages N (negative) Z (zero) C (carry) V (overflow: % d, % d/N ", * DEST, flages [3], flages [2], flages [1], flages [0]);
Return 0;
}
# Add. s
. Text
. Align 2
. Global add
. Type add, Function
# Define add as an externally callable Function
Add:
Push % EBP # EBP register content pressure stack, save the stack base address of the upper-level called function of the add function
MoV % ESP, % EBP # assign the ESP value to EBP and set the stack base address of the add function.
MoV 0x8 (% EBP), % edX
MoV 0x10 (% EBP), % eax
Add 0xc (% EBP), % eax
MoV % eax, (% EDX)
MoV 0x14 (% EBP), % eax
Jo
C:
JC cf
S:
JS SF
JZ ZF
JMP out
Of:
Movw $0x1, (% eax)
JMP C
Cf:
Movw $0x1, 0x2 (% eax)
JMP s
SF:
Movw $0x1, 0x6 (% eax)
Movw $0x0 0x4 (% eax)
JMP out
ZF:
Movw $0x1, 0x4 (% eax)
Movw $0x0 0x6 (% eax)
Out:
Leave # assign the EBP value to esp. the base address of the upper-level function stack in the pop stack is given to # EBP to restore the base address of the original stack.
RET # The add function returns to the upper-level call function.
Here,. text indicates the beginning of a code segment, which is at&t's segment format; Global add;/n
Type add, function indicates that add is public and can be called by other external compilation modules.
The C source program is saved as the file name Add. C, and the assembly language source program is saved as the Add. S. The code for compiling and connecting through make is as follows:
ALL: myadd
Myadd: adds. o addc. o
Gcc-O myadd adds. o adc. o
Adds. O: Add. s
As-O adds. o add. s
ADDC. O: Add. c
Gcc-g-o ADDC. o add. c
It can be seen from the above that it is very convenient to call the assembly module in C. Therefore, in actual software development, hybrid programming technology can be used to take advantage of the advantages of each language as much as possible. It not only meets the needs of actual problems, but also simplifies the design process to get twice the result with half the effort.