Let program a simple working assembly code called from the Visual Studio Cpp project. The assembly code is intentional placed in the external file.
Create a simple C++ console project. Right click on the project name in the solution explorer, choose the Build dependencies and then the Build customization submenu and check the masm box:
Now we are ready to add a new item. Give it a name: myAsmFile.asm – Now right click on the asm file and choose the properties. You have to see the Microsoft Macro Assembler to the left of property pages. If not, you will not be able to build this example. Please check out this window:
The content of the asm file:
.386
.MODEL FLAT, C
.STACK 128
.CONST
.DATA
.CODE ;Indicates the start of a code segment.
mySeg1 SEGMENT PAGE PUBLIC 'MUJ'
power2 PROC NEAR
push ebp ; Save EBP
mov ebp, esp ; Move ESP into EBP so we can refer to arguments on the stack
mov eax, [ebp + 8] ; Get first argument
mov ecx, [ebp + 12] ; Get second argument
dec ecx
shl eax, cl ; EAX = EAX * ( 2 ^ CL )
pop ebp ; Restore EBP
ret ; Return with sum in EAX
power2 ENDP
mySeg1 ENDS
END
Then insert this code to the main.cpp
#include "stdafx.h"
extern "C" int power2(int num, int power);
int main()
{
int prs = power2(2, 2);
std::cout << "prs 2^2: " << prs << std::endl;
prs = power2(2, 3);
std::cout << "prs 2^3: " << prs << std::endl;
prs = power2(2, 4);
std::cout << "prs 2^4: " << prs << std::endl;
return 0;
}
We run the program and see the results:
prs 2^2: 4 prs 2^3: 8 prs 2^4: 16
Now let disassemble the results. Here follow the disassembly for the first case 2 ^ 2:
00301680 /$ 55 push ebp
00301681 |. 8BEC mov ebp,esp
00301683 |. 83EC 18 sub esp,18
00301686 |. 53 push ebx
00301687 |. 56 push esi
00301688 |. 6A 02 push 2
0030168A |. 6A 02 push 2
0030168C |. E8 6F390000 call myAsm4.00305000
The second case 2 ^ 3:
003016C4 |. 6A 03 push 3
003016C6 |. 6A 02 push 2
003016C8 |. E8 33390000 call myAsm4.00305000
And the thirth case 2 ^ 4:
003016FF |. 6A 04 push 4
00301701 |. 6A 02 push 2
00301703 |. E8 F8380000 call myAsm4.00305000
The core procedure looks like this:
00305000 55 push ebp
00305001 8BEC mov ebp,esp
00305003 8B45 08 mov eax,dword ptr ss:[ebp+8]
00305006 8B4D 0C mov ecx,dword ptr ss:[ebp+C]
00305009 49 dec ecx
0030500A D3E0 shl eax,cl
0030500C 5D pop ebp
0030500D C3 retn
The 3 cases differ from each other only with the pushed values. The parameters a 4 bytes long and must be taken from the stack to calculate the result. The register EAX in line 00305003 is the base value, and the ECX is the power value. Decrementing of ECX is a must, otherwise the results are twice so big.
The return address is the 1st value pushed on stack, the EBP is the 2st value, the EAX gets 3th and the ECX the 4th value pushed on stack. The result of calculation is as always in the EAX register.
Here is the working code: