اسمبلر درونبرنامهای
در برنامهنویسی کامپیوتر، یک اسمبلر درون برنامه ای، یک ویژگی از برخی از کامپایلرهاست که اجازه میدهد کُدِ سطح پایینی که به زبان اسمبلی نوشته شده در میان یک برنامه قرار گیرد، درون کدی که بر خلاف آن، از یک زبان سطح بالا مانند C یا Ada کامپایل شدهاست.
مزایا
جاسازی کد اسمبلی عموماً برای ۳ دلیل انجام میشود:
بهینهسازی: برنامه نویسان میتوانند کد زبان اسمبلی را برای پیادهسازی بخشهایی از الگوریتم برنامه شان استفاده کنند که بیشترین حساسیت را در مورد کارایی دارند. بخشهایی که میتوانند با این روش بهینه تر از وقتی باشند که توسط کامپایلر تولید میشوند.
دسترسی به دستورالعملهای خاص پردازنده: اکثر پردازندهها دستورالعملهای ویژه ای مانند دستورالعمل مقایسه و تعویض و تست و تنظیم را ارائه میدهند که ممکن است برای ساختن سمافورها یا سایر هماهنگ سازیها و پایههای قفل سازی استفاده شود. تقریباً هر پردازنده مدرنی این دستورالعملها یا مشابههای آنها را دارد، زیرا آنها برای اجرای فرایند چند وظیفه ای ضروری هستند. نمونههایی از دستورالعملهای تخصصی در SPARC VIS، Intel MMX و SSE و مجموعه دستورالعملهای Motorola Altivec وجود دارد.
فراخوانیهای سیستمی: زبانهای سطح بالا به ندرت دارای امکان مستقیم برای برقراری فراخوانیهای سیستمی دلخواه هستند، در این صورت کد اسمبلی استفاده میشود.
نحو در استانداردهای زبان
استانداردهای ++ ISO C و استانداردهای ISO C (پیوست J) یک نحو پشتیبانی شدهٔ مشروط را برای اسمبلر درون برنامه ای مشخص میکند:
اعلان asm فرم زیر را دارد:
asm-definition:
asm ( عبارت رشتهای ) ;
اعلان asm به صورت مشروط پشتیبانی میشود؛ معنای آن حین اجرا مشخص میشود.
مثالی از یک فراخوان سیستمی
فراخوانی مستقیم سیستم عامل، عموماً تحت سیستمی که از حافظهٔ محافظت شده استفاده میکند، امکانپذیر نیست. سیستم عامل در سطح بالاتری (حالت هسته) از کاربر (حالت کاربر) اجرا میشود؛ یک وقفه (نرمافزاری) برای درخواست از سیستم عامل مورد استفاده قرار میگیرد. زبانهای سطح بالا عموماً این ویژگی را ندارند، بنابراین با استفاده از اسمبلر درون برنامه ای، توابع بستهبندی برای فراخوانیهای سیستمی نوشته میشود.
مثال زیر کد C یک بستهبندی تماس تلفنی را در syntax assembler AT & T نشان میدهد، با استفاده از اسمبلر گنو. چنین فراخوانیهایی معمولاً با کمک ماکرو نوشته میشود؛ کد کامل برای وضوح گنجانده شدهاست.
فرمت پایه اسمبلی دورن برنامه ای بسیار ساده است:
asm (<assembly code>);
مثال:
asm ("movl %ecx, %eax"); /* moves the contents of ecx to eax */
یا
__asm__ ("movb %bh, (%eax)"); /* moves the byte from bh to the memory pointed by eax */
هر دو asm
و __asm__
معتبر هستند. __asm__
میتواند مورد استفاده قرار گیرد اگر کلمه کلیدی asm
با چیز دیگری در برنامه در تعارض باشد.
extern int errno;
int funcname(int arg1, int *arg2, int arg3)
{
int res;
__asm__ volatile (
"int $0x80" /* make the request to the OS */
: "=a" (res), /* return result in eax ("a") */
"+b" (arg1), /* pass arg1 in ebx ("b") */
"+c" (arg2), /* pass arg2 in ecx ("c") */
"+d" (arg3) /* pass arg3 in edx ("d") */
: "a" (128) /* pass system call number in eax ("a") */
: "memory", "cc"); /* announce to the compiler that the memory and condition codes have been modified */
/* The operating system will return a negative value on error;
* wrappers return -1 on error and set the errno global variable */
if (-125 <= res && res < 0) {
errno = -res;
res = -1;
}
return res;
}
مثالی برای بهینهسازی و دستورالعملهای خاص پردازنده
این مثال از اسمبلی درون برنامه ای، از زبان برنامهنویسی D، کدی را نشان میدهد که تانژانت x با استفاده از دستورالعمل FPU x86 را محاسبه میکند؛ که این روش سریعتر است از استفاده از عملیات ممیز شناور که توسط کامپایلر تولید میشود، و همچنین به برنامهنویس اجازه میدهد تا از دستورالعمل fldpi
استفاده کند، که نزدیکترین تقریب احتمالی عدد پی را در معماری x86 بارگذاری میکند.
// Compute the tangent of x
real tan(real x)
{
asm
{
fld x[EBP] ; // load x
fxam ; // test for oddball values
fstsw AX ;
sahf ;
jc trigerr ; // x is NAN, infinity, or empty
// 387's can handle denormals
SC18: fptan ;
fstp ST(0) ; // dump X, which is always 1
fstsw AX ;
sahf ;
jnp Lret ; // C2 = 1 (x is out of range)
;// Do argument reduction to bring x into range
fldpi ;
fxch ;
SC17: fprem1 ;
fstsw AX ;
sahf ;
jp SC17 ;
fstp ST(1) ; // remove pi from stack
jmp SC18 ;
}
trigerr:
return real.nan;
Lret:
;
}