Why?
1. speed
2. size
3. low-level access (registers)
.. First, a Word About C# and IL
Managed Code vs. Native Code
IL
CLR
.NET class library
Use ILDASM to inspect C# MANAGED code =>IL
- Run the the Visual Studio command prompt
- type in ILDASM
- choose a C# exe file
References:
msdn: ildasm
a nice IL C# tutor
.. HLL to Assembly Code
HLL compilers translate mathematical expressions into assembly language.
You can do it also.
For example in C++:
int Rval;
int Xval = 26;
int Yval = 30;
int Zval = 40;
...
Rval = (Yval - – Zval) - Xval; // (30-40) - 26 = -36
...and in assembly:
mov eax, Xval
neg eax ; EAX = -26
mov ebx, Yval
sub ebx, Zval ; EBX = -10
add eax, ebx
mov Rval, eax ; -36
...
Rval DWORD ?
Xval DWORD 26
Yval DWORD 30
Zval DWORD 40
... and mixing it up:
int Rval;
int Xval = 26;
int Yval = 30;
int Zval = 40;
__asm
{
mov eax, Xval
neg eax ; EAX = -26
mov ebx, Yval
sub ebx, Zval ; EBX = -10
add eax, ebx
mov Rval, eax ; -36
}
** note that you can NOT define assembly data in mixed code - why???
note that you can NOT use the OFFSET operator in mixed code -- why??
.. Disassembly Code
DISASSEMBLY: C++ to Assembly - example...
===========================
random code from a sudoku program...
case 1:
////////////////////////////////////////////////////
// generate & print simple grid WITH repetitions
//
///////////////////////////////////////////////////
timeStart = time(0);
0044E4FA push 0
0044E4FC call time (44EE00h)
0044E501 add esp,4
0044E504 mov dword ptr [ebp-68h],eax
// allocate dynamic memory on the heap
grid1=new int[SZp*SZp];
0044E507 mov eax,dword ptr [ebp-80h]
0044E50A imul eax,dword ptr [ebp-80h]
0044E50E xor ecx,ecx
0044E510 mov edx,4
0044E515 mul eax,edx
0044E517 seto cl
0044E51A neg ecx
0044E51C or ecx,eax
0044E51E push ecx
0044E51F call operator new[] (440ADCh)
0044E524 add esp,4
0044E527 mov dword ptr [ebp-248h],eax
0044E52D mov eax,dword ptr [ebp-248h]
0044E533 mov dword ptr [ebp-14h],eax
// init grids to all zeroes
initGridP(grid1, SZp);
0044E536 mov eax,dword ptr [ebp-80h]
0044E539 push eax
0044E53A mov ecx,dword ptr [ebp-14h]
0044E53D push ecx
0044E53E call initGridP (440820h)
0044E543 add esp,8
// generate grid using random numbers(1 to gridsize)
simpleGridP(grid1, SZp);
0044E546 mov eax,dword ptr [ebp-80h]
0044E549 push eax
0044E54A mov ecx,dword ptr [ebp-14h]
0044E54D push ecx
0044E54E call simpleGridP (4406DBh)
0044E553 add esp,8
printGridP(grid1, SZp);
0044E556 mov eax,dword ptr [ebp-80h]
0044E559 push eax
0044E55A mov ecx,dword ptr [ebp-14h]
0044E55D push ecx
0044E55E call printGridP (441ED7h)
0044E563 add esp,8
cout << "Simple random grid with NO CHECK for repetitions..." << endl;
0044E566 push offset std::endl (44077Bh)
0044E56B push offset string "Simple random grid with NO CHECK"... (4C5EC0h)
0044E570 push offset std::cout (4DEFE0h)
0044E575 call std::operator<< > (441248h)
0044E57A add esp,8
0044E57D mov ecx,eax
0044E57F call std::basic_ostream >::operator<< (441284h)
// release allocated memory
delete []grid1;
0044E584 mov eax,dword ptr [ebp-14h]
0044E587 mov dword ptr [ebp-23Ch],eax
0044E58D mov ecx,dword ptr [ebp-23Ch]
0044E593 push ecx
0044E594 call operator delete[] (4401EAh)
0044E599 add esp,4
// time stamp
cout << "Seconds used is " << time(0)-timeStart << endl;
0044E59C push offset std::endl (44077Bh)
0044E5A1 push 0
0044E5A3 call time (44EE00h)
0044E5A8 add esp,4
0044E5AB mov ecx,eax
0044E5AD mov esi,edx
0044E5AF mov eax,dword ptr [ebp-68h]
0044E5B2 cdq
0044E5B3 sub ecx,eax
0044E5B5 sbb esi,edx
0044E5B7 push esi
0044E5B8 push ecx
0044E5B9 push offset string "Seconds used is " (4C5EACh)
0044E5BE push offset std::cout (4DEFE0h)
0044E5C3 call std::operator<< > (441248h)
0044E5C8 add esp,8
0044E5CB mov ecx,eax
0044E5CD call std::basic_ostream >::operator<< (441D29h)
0044E5D2 mov ecx,eax
0044E5D4 call std::basic_ostream >::operator<< (441284h)
break;
0044E5D9 jmp $LN4+62h (44EAECh)
.. C++ Code
.. Mixed Code: __asm Blocks in C++
__asm
{
assembly code
}
The __asm (2 underscores!!) keyword invokes the inline assembler and
can appear wherever a C++ statement is legal and must be followed by
an assembly instruction or a group of assembly instructions in braces.
The inline assembler lets you embed assembly-language instructions in
your C and C++ source programs.
It's built into the compiler, so you don't need a separate assembler .
Inline assembly code can use any C or C++ variable or function name that
is in scope, so it's easy to integrate with your program's C and C++ code.
Because the assembly code can be mixed with C and C++ statements, it
can do tasks that are cumbersome or impossible in C or C++ alone.
Example Code:
=============
__asm
{
mov al, 2
mov dx, 0xD007
out dx, al
}
OR, you can put __asm before each assembly instruction:
__asm mov al, 2
__asm mov dx, 0xD007
__asm out dx, al
Can NOT use assembler directives including data defintions.
Can only use registers and variables declared in C++.
Example Program:
================
add 3 to 9 equals 12 (in EAX)
subtract 2 getting 10 (in EAX)
negate giving -10 (EAX)
move into C++ variable, number
print the variable (-10)
#include <iostream>
using namespace std;
int main()
{
int number=3;
__asm
{
mov EAX, 00000009h
add EAX, number ; add 3
mov EBX, 2
sub EAX, EBX ; 12 - 2 = 10
neg EAX
mov number, EAX
}
cout << number; ; -10
return 0;
}
Example Program:
================
#include <iostream>
using namespace std;
int Sum(int, int);
int main()
{
cout << "Hello \n\n";
int x = Sum( 3, 4 );
cout << x << endl; // 7
cout << Sum( 3, 2 ) << endl; // 5
cout << Sum( Sum( 1,2 ), x ) << endl; //10
return 0;
}
int Sum (int x, int y)
{
int s;
__asm
{
mov eax, x
mov ebx, y
add eax, ebx
mov s, eax
}
return s;
}