زبان اسمبلی اکس۸۶
زبان اسمبلی x86 خانواده ای از زبانهای اسمبلی دارای سازگاری عقبرو است که حتی با Intel 2088 که در آوریل ۱۹۷۲ معرفی شد، تا حدودی سازگاری دارد. زبانهای اسمبلی x86 برای تولید کد هدف برای پردازندههای کلاس x86 استفاده میشوند. زبان اسمبلی x86 همانند تمام زبانهای اسمبلی، از یادیارهای کوتاه استفاده میکند تا دستورالعملهای اساسی را که CPU در یک کامپیوتر میتواند درک و دنبال کند، نشان دهد. کامپایلرها گاهی هنگام ترجمهٔ یک برنامه سطح بالا به کد ماشین، کد اسمبلی را به عنوان یک مرحله میانی تولید میکنند.
به عنوان یک زبان برنامهنویسی، اسمبلی به صورت خاص ماشین و سطح پایین است. زبانهای اسمبلی بهطور معمول برای برنامههای کاربردی دقیق و زمان بحرانی مورد استفاده قرار میگیرند مانند سیستمهای نهفتهٔ بلادرنگ یا هستههای سیستم عامل و درایورهای دستگاه.
تاریخچه
اینتل ۸۰۸۶ و ۸۰۸۸ اولین پردازندههایی بودند که یک مجموعه دستورالعمل داشتند که در حال حاضر معمولاً آن را x86 مینامند.
این پردازندههای ۱۶ بیتی که تکامل یافتهٔ نسل قبلی پردازندههای ۸ بیتی مانند ۸۰۸۰ بودند، ویژگیها و دستورالعملهای بسیاری را از آنها به ارث برده و برای نسل ۱۶ بیتی توسعه دادهاند. ۸۰۸۶ و ۸۰۸۸ هر دو از گذرگاه آدرس ۲۰ بیتی و ثباتهای داخلی ۱۶ بیتی استفاده میکردند؛ اما تفاوتهایی نیز داشتند، برای مثال، در حالی که ۸۰۸۶ دارای یک گذرگاه داده ۱۶ بیتی بود، ۸۰۸۸، به عنوان یک گزینهٔ کم هزینه تر برای برنامههای کاربردی نهفته و کامپیوترهای کوچک، از یک گذرگاه دادهٔ ۸ بیتی بهره میبرد.
زبان اسمبلی x86 انواع مختلفی از CPUها را پوشش میدهد از جمله: پردازندههای اینتلی مانند 80186، 80188، ۸۰۲۸۶، ۸۰۳۸۶، 80486، پنتیوم، پنتیوم پرو و غیره و همچنین پردازندههای غیر اینتلی AMD و Cyrix مانند پردازندههای 5x86 و K6 و NEC V20 (هرچند این پردازندهها غالباً سازگار با اینتل هستند، دستورالعملهایی را که ممکن است به سختی بخشی از زبان اسمبلی x86 در نظر گرفته شود را به مجموعه دستورالعملهای خود اضافه کردهاند؛ همانند دستورالعملهایی که Zilog به Z80 اضافه کرد) اصطلاح x86 برای هر پردازنده ای که میتواند زبان اسمبلی اصلی را اجرا کند، مورد استفاده قرار میگیرد.
مجموعهٔ جدید دستورالعملهای x86، یک ابرمجموعه از دستورالعملهای ۸۰۸۶ و مجموعه ای از افزونههای اضافه شده به این دستورالعمل هاست که از میکروپروسسور اینتل ۸۰۰۸ آغاز شد. سازگاری عقبرو باینری تقریباً کاملی میان تراشهٔ اینتل از ۸۰۸۶ تا نسل فعلی پردازندههای x86 وجود دارد، اگر چه برخی از استثنائات وجود دارند. در عمل، استفاده از دستورالعملهایی که روی هر پردازنده ای از اینتل ۸۰۳۸۶ یا پنتیوم به بعد اجرا شود، رایج است. اما در سالهای اخیر، سیستم عاملهای مختلف و برنامههای کاربردی نرمافزار به نیاز به پردازندههای مدرن تر پیدا کردهاند، پردازندههایی که دست کم از تعدادی از افزونههایی که به مجموعهٔ دستورالعملها اضافه شدهاند، پشتیبانی کنند. (به عنوان مثال MMX، 3DNow!، SSE / SSE2 / SSE3).
یادیارها و آپ کدها
هر دستورالعمل مونتاژ x86 با یک یادیار نشان داده میشود که اغلب با یک یا چند عملوند ترکیب شدهاست و به یک یا چند بایت به نام آپ کد ترجمه میشود؛ به عنوان مثال دستور NOP به 0x90 ترجمه میشود و دستور HLT به 0xF4 ترجمه میشود. آپ کدهای بالقوه ای وجود دارند که هیچ یادیار مستندی برای آنها وجود ندارد. پردازندههای مختلف ممکن است این آپ کدها را بهطور متفاوتی از یکدیگر تفسیر کنند، برنامه ای که با استفاده از آنها ایجاد شود، متناقض رفتار خواهد کرد یا حتی در برخی از پردازندهها یک استثنا ایجاد میکند. این آپ کدها اغلب در مسابقههای کدنویسی به عنوان راهی برای کوچکتر، سریع تر و ظریف تر کردن کد استفاده میشوند یا تنها نشان دهندهٔ مهارت کدنویس میباشند.
نحو
زبان اسمبلی x86 دارای دو شاخهٔ اصلی نحو است: نحو اینتل، که در اصل برای مستندسازی پلت فرم x86 استفاده میشود، و نحو AT & T. استفاده از نحو اینتل در دنیای MS-DOS و ویندوز غالب است و استفاده از نحو AT & T در دنیای یونیکس، از آنجایی که یونیکس در آزمایشگاههای AT & T Bell ایجاد شدهاست. در اینجا خلاصه ای از تفاوتهای اصلی میان نحو اینتل و نحو AT & T آمدهاست:
AT & T | اینتل | |
---|---|---|
ترتیب پارامتر | منبع قبل از مقصد mov $5, %eax
| مقصد قبل از منبع mov eax, 5
|
اندازهٔ پارامتر | یادیارها شامل یک حرف پیشوند که نشان دهندهٔ اندازهٔ عملوند است، میباشند: addl $4, %esp
| مشتق شده از نام ثباتی که مورد استفاده قرار گرفتهاست: add esp, 4
|
نمادها | مقادیر بی واسطه با پیشوند "$" میآیند، و ثباتها با پیشوند "٪". | اسمبلر بهطور خودکار نوع نمادها را تشخیص میدهد؛ یعنی تشخیص میدهد آنها ثبات، مقدار ثابت یا چیز دیگری هستند. |
آدرسهای مؤثر | نحو عمومی
(DISP(BASE, INDEX, SCALE. مثال: movl mem_location(%ebx,%ecx,4), %eax
| عبارات ریاضی در داخل براکت؛ علاوه بر این، اگر اندازه از روی عملوندها قابل تشخیص نباشد، کلمات کلیدی مشخص کنندهٔ اندازه مانند byte , word یا dword باید استفاده شوند. مثال: mov eax, [ebx + ecx*4 + mem_location]
|
بسیاری از اسمبلرهای x86 از نحو اینتل استفاده می کنند، از جمله NASM, FASM ,MASM ,TASM و YASM. اسمبلر GAS از نسخهٔ ۲٫۱۰ به بعد از طریق دایرکتیو .intel_syntax از هر دو نحو پشتیبانی میکند.
ثباتها
پردازندههای x86 دارای مجموعه ای از ثباتها هستند که برای ذخیرهسازی دادههای باینری استفاده میشوند. در مجموع، ثباتهای داده و ثباتهای آدرس، ثباتهای عمومی نامیده میشوند. هر ثباتی به جز آنچه که همهٔ ثباتها میتوانند انجام دهند، هدف خاصی نیز دارد:
- *AX ضرب/تقسیم، بارگذاری و ذخیرهٔ رشته
علاوه بر ثباتهای عمومی، موارد زیر نیز وجود دارند:
- - نشانگر دستورالعمل IP
- ثبات IP، به افست حافظهٔ دستورالعمل بعدی در بخش کد اشاره میکند. (اشاره میکند به اولین بایت از دستور). ثبات IP توسط برنامهنویس بهطور مستقیم قابل دسترسی نیست.
ثباتهای x86 میتوانند همراه دستورالعملهای MOV استفاده شوند. به عنوان مثال، در نحو اینتل:
mov ax, 1234h ; copies the value 1234hex (4660d) into register AX
mov bx, ax ; copies the value of the AX register into the BX register
آدرس دهی قطعه بندی شده
معماری x86 در وضعیت حقیقی و مجازی ۸۰۸۶ از یک فرایند به نام قطعه بندی (به جای مدل حافظهٔ مسطح که در بسیاری از محیطهای دیگر استفاده میشود) برای آدرس دهی حافظه استفاده میکند. قطعه بندی، شامل ساخت یک آدرس حافظه از دو بخش است: یک قطعه و یک افست؛ قطعه به نقطهٔ شروع یک گروه ۶۴ کیلوبایتی از آدرسها اشاره میکند و افست تعیین میکند که آدرس مورد نظر در چه فاصله ای از این نقطهٔ شروع قرار دارد. در آدرس دهی قطعه بندی شده، برای ثبت آدرس کامل حافظه، دو رجیستر نیاز است. یکی برای نگه داشتن قطعه و دیگری برای نگه داشتن افست. به منظور ترجمه به یک آدرس مسطح؛ مقدار قطعه، چهار بیت به چپ (که معادل است با ضرب در 2 یا ۱۶) شیفت داده میشود و سپس به مقدار افست اضافه میشود تا آدرس کامل را ایجاد کند. این روش اجازه میدهد محدودیت آدرس دهی ۶۴ کیلوبایتی را از طریق انتخاب هوشمندانهٔ آدرسها دور بزنیم. هرچند که برنامهنویسی را به مراتب پیچیدهتر میکند.
برای مثال، در وضعیت حقیقی/محافظت شده، اگر DS حاوی مقدار هگزادسیمال 0xDEAD بوده و DX حاوی مقدار 0xCAFE باشد، ای دو در کنار هم به آدرس حافظهٔ 0xDEAD * 0x10 + 0xCAFE = 0xEB5CE اشاره میکنند؛ بنابراین، CPU میتواند تا ۱۰۴۸۵۷۶ بایت (۱ مگابایت) را در وضعیت حقیقی آدرس دهی کند. با ترکیب مقادیر قطعه و افست، یک آدرس ۲۰ بیتی را خواهیم داشت.
کامپیوتر اصلی آی بی ام برنامهها را محدود به ۶۴۰ کیلوبایت میکرد، اما مشخصات حافظه گسترش یافته برای پیادهسازی یک طرح سوئیچینگ بانکی که سقوط از استفاده از زمانی که بعد از سیستم عامل، مانند ویندوز، استفاده از محدوده آدرس بزرگتر از پردازندههای جدید و استفاده از حافظه مجازی خود طرحها
حالت محافظت شده، که از اینتل ۸۰۲۸۶ آغاز شد، توسط OS / 2 مورد استفاده قرار گرفت. نقطه ضعفهایی مانند عدم قابلیت دسترسی به BIOS و عدم توانایی برای بازگشت به حالت واقعی بدون تنظیم مجدد پردازنده، مانع استفادهٔ گستردهٔ آن شد. ۸۰۲۸۶ همچنان محدود به آدرس دهی حافظه در بخشهای ۱۶ بیتی بود، به این معنی که فقط 2 بایت (۶۴ کیلوبایت) در هر لحظه قابل دسترسی بود. برای دسترسی به قابلیتهای گسترش یافتهٔ ۸۰۲۸۶، سیستم عامل، پردازنده را روی حالت محافظت شده تنظیم میکند، بنابراین آدرس دهی ۲۴ بیتی و نتیجتاً دسترسی به 2 بایت (۱۶ مگابایت) حافظه را امکانپذیر میکند.
در حالت محافظت شده، انتخابگر قطعه را میتوان به سه بخش تقسیم کرد: یک شاخص ۱۳ بیتی، یک نشانگر جدول یک بیتی که تعیین میکند ورودی در GDT قرار دارد یا در LDT و یک سطح مجوز درخواستی ۲ بیتی.
در هنگام اشاره به یک آدرس مشخص شده با یک قطعه و یک افست، از نشانه گذاری قطعه:افست استفاده میشود، بنابراین در مثال فوق، آدرس مسطح 0xEB5CE میتواند یا به صورت 0xDEAD:0xCAFE نوشته شود یا به صورت یک جفت ثبات که یکی برای قطعه و دیگری برای افست است؛ DS:DX.
- برخی از ترکیبات خاص ثباتهای قطعه و ثباتهای عمومی به آدرسهای مهمی اشاره دارند. مانند:
- *CS:IP به آدرسی اشاره میکند که پردازنده در بایت بعدی از کد، آن را واکشی خواهد کرد (CS کوتاه شدهٔ قطعه کد و IP کوتاه شدهٔ نشانگر دستورالعمل میباشد).
- *SS:SP به آدرس بالای پشته اشاره میکند یا به عبارت دیگر به آخرین بایتی که روی پشته قرار گرفتهاست (SS کوتاه شدهٔ قطعهٔ پشته و SP کوتاه شدهٔ نشانگر پشته میباشد).
- *DS:SI معمولاً برای اشاره به دادهای استفاده میشود که قصد داریم آن را در ES:DI کپی کنیم (DS کوتاه شدهٔ قطعهٔ داده و SI کوتاه شدهٔ شاخص مبدأ میباشد).
- *ES:DI معمولاً برای اشاره به مقصدی استفاده میشود که قصد کپی کردن رشتهای را در آن مقصد داریم، همانطور که در بالا گفته شد.
اینتل ۸۰۳۸۶ دارای سه حالت کارکرد میباشد: حالت واقعی، حالت محافظت شده و حالت مجازی. حالت محافظت شده که در ۸۰۲۸۶ عرضه شد، به ۸۰۳۸۶ این امکان را میدادکه تا حداکثر ۴ گیگابایت حافظه در اختیار داشته باشد؛ حالت مجازی جدید 8086 (VM86)، اجرای یک یا چند برنامهٔ حالت واقعی را در یک محیط محافظت شده که عمدتاً مشابه حالت واقعی بود، امکانپذیر کرد، هر چند برخی از برنامهها با این حالت سازگار نبودند.
مدل حافظه مسطح ۳۲ بیتی در حالت حفاظت شدهٔ توسعه یافتهٔ ۸۰۳۸۶، احتمالاً مهمترین ویژگی تغییر یافته در خانوادهٔ پردازنده x86 بود تا زمانی که ای ام دی، x86-64 را در سال ۲۰۰۳ منتشر کرد، ویندوز اکنون میتواند بسیاری از برنامههای کاربردی، از جمله برنامههای DOS، را با استفاده از حافظهٔ مجازی و چند اجرای توامان، به صورت همزمان اجرا کند.
حالت اجرا
پردازندههای x86 از پنج حالت عملکرد پشتیبانی میکنند: حالت حقیقی، حالت محافظت شده، حالت طولانی، حالت مجازی ۸۶ و حالت مدیریت سیستم، که در هر کدام از این حالتها برخی دستورالعملها در دسترس بوده و برخی دیگر در دسترس نخواهند بود. یک زیر مجموعهٔ ۱۶ بیتی از دستورالعملها در حالت حقیقی در تمام پردازندههای x86 قابل استفاده هستند و در حالت محافظت شدهٔ ۱۶ بیتی (از ۸۰۲۸۶ به بعد) دستورالعملهای افزدوه شده مربوط به حالت محافظت شده هم در دسترس هستند. در ۸۰۳۸۶ و پردازندههای بعد از آن، دستورالعملهای ۳۲ بیتی نیز در همهٔ حالتها، از جمله حالت واقعی قابل استفاده هستند. در این پردازندهها، حالت V86 و حالت حفاظت شدهٔ ۳۲ بیتی، همراه با دستورالعملهای اضافی ارائه شده در این حالتها برای مدیریت امکانات این پردازندهها، اضافه شدهاند. SMM، با برخی از دستورالعمل خاص خود، در برخی از پردازندههای اینتل i386SL , i486 و بعد از اینها در دسترس است. در حالت طولانی (از ای ام دی Opteron به بعد)، دستورالعملهای ۶۴ بیتی و ثباتهای بیشتری نیز در دسترس هستند. مجموعهٔ دستورالعملها در همهٔ حالتها مشابه هم هستند اما آدرس دهی حافظه و اندازهٔ کلمات متفاوتند؛ در نتیجه برنامهنویسی در حالتهای مختلف نیاز به استراتژیهای برنامهنویسی متفاوت دارد.
کد x86 میتواند در حالتهای زیر اجرا شود:
- حالت واقعی (۱۶ بیتی)
- حالت حفاظت شده (۱۶ بیت و ۳۲ بیتی)
- حالت طولانی (۶۴ بیتی)
- حالت مجازی ۸۰۸۶ (۱۶ بیتی)
- حالت مدیریت سیستم (۱۶ بیتی)
جابجایی بین حالتها
پردازنده بلافاصله پس از روشن شدن در حالت واقعی اجرا میشود، بنابراین هستهٔ سیستم عامل یا برنامههای دیگر در صورتی که بخواهند در حالت دیگری به جز حالت واقعی اجرا شوند، باید به صورت صریح به آن حالت منتقل شوند. جابجایی بین حالتها با تغییر دادن برخی بیتهای مشخص از ثباتهای کنترلکننده ی پردازنده، بعد از یک سری آمادهسازیها، انجام میشود و ممکن است تعدادی تنظیمات اضافی نیز بعد از جابجایی مورد نیاز باشد.
انواع دستورالعملها
بهطور کلی، ویژگیهای مجموعه دستورالعملهای مدرن x86 عبارتند از:
- _ رمزگذاری فشرده
طول متغیر و همسایگی مستقل (کدگذاری شده به عنوان Endian کمی، همانطور که تمام دادهها در معماری x86)
- بهطور عمده دستورالعملهای یک آدرس و دو آدرس، یعنی اولین عملوند نیز مقصد است.
- Operands حافظه به عنوان منبع و مقصد پشتیبانی میشوند (اغلب برای خواندن / نوشتن عناصر پشته با استفاده از فوریتهای فوری کوچک) استفاده میشود.
- استفاده از ثبت نام عمومی و ضمنی؛ اگرچه همه هفت (counting
ebp
) بهطور کلی در حالت ۳۲ بیت ثبت میشوند و تمام پانزده (rbp
شمارش) بهطور کلی در حالت ۶۴ بیت ثبت میشود، میتواند به صورت آزادانه به عنوان باتری یا برای آدرس استفاده شود، اکثر آنها نیز به صورت ضمنی توسط برخی بیشتر یا کمتر) دستورالعملهای ویژه؛ بنابراین باید ثبت نامهای آسیب دیده را بهطور موقت حفظ (بهطور معمول انباشته)، اگر در طول چنین توالی آموزش فعال است.
- استفاده از ثبت نام عمومی و ضمنی؛ اگرچه همه هفت (counting
- پرچم شرطی را به صورت ضمنی از طریق اکثر دستورالعملهای ALU صحیح تولید میکند.
- پشتیبانی از حالتهای مختلف آدرس بندی از جمله فوراً، افست و شاخص مقیاس، اما نه نسل PC، به جز جهش (به عنوان بهبود در معماری x86-64 معرفی شده).
- شامل نقطه شناور به پشته ثبت میشود.
- شامل پشتیبانی ویژه برای دستورالعمل read-modify-write اتمی (
xchg
،cmpxchg
/cmpxchg8b
،xadd
، و دستورالعملهای عدد صحیح که با پیشوندlock
ترکیب میشوند) - دستورالعمل SIMD (دستورالعملهایی که دستورالعملهای همزمان همزمان را در بسیاری از operands کدگذاری شده در سلولهای مجاور رکوردهای گستردهتر انجام میدهند).
دستورالعملهای پشته
معماری x86 دارای پشتیبانی سختافزاری برای اجرای مکانیسمهای مربوط به پشته است. دستورالعملهایی مانند push
، pop
، call
و ret
با کمک پشته اجرا میشوند و برای پاس دادن پارامترها، اختصاص دادن فضا به دادههای محلی و ذخیره و بازیابی نقاط بازگشت دستور call استفاده میشود. دستورالعمل ret size
وقتی به کار میرود که تابع صدا زده شده مسئولیت احیای فضای اشغال شدهٔ پشته توسط پارامترها را به عهده داشته باشد.
برای تنظیم یک کادر پشته جهت نگهداری دادههای محلی یک تابع بازگشتی چندین راه وجود دارد؛ اولین راه استفاده از دستورالعمل enter
(این دستور همراه با ۸۰۳۸۶ معرفی شد) است که میتواند سریعتر از بسیاری دستورالعملهای دیگر باشد (مانند push bp
؛ mov bp,sp
؛ sub sp,size
). هرچند سریع تر بودن یا آهستهتر بودن این روش علاوه بر نوع پیادهسازی پردازندهٔ x86، وابسته به قراردادهای فراخوانی مربوط به کامپایلر، برنامهنویس یا کد خاص یک برنامه نیز خواهد بود.
طیف گستردهٔ حالتهای آدرس دهی (از جمله بی واسطه و پایه + افست) برای دستورالعملهایی مانند push
و pop
، استفادهٔ مستقیم از پشته را برای دادههایی مانند عدد صحیح، ممیز شناور و آدرس سادهتر میکند، همچنین، نگه داشتن مشخصات و مکانیزم ABI نسبت به برخی معماریهای RISC سادهتر خواهد بود.
دستورالعملهای مربوط به اعداد صحیح در ALU
اسمبلی x86 دارای عملیاتهای ریاضی استاندارد، مانند: add
، sub
، mul
، idiv
عملگرهای منطقی مانندand
or
: xor
، neg
؛ عملیات شیفت ریاضی و منطقی: sal
/ sar
، shl
/ shr
؛ چرخش همراه با رقم نقلی و چرخش بدون رقم نقلی مانند: rcl
/ rcr
rol
/ ror
، مکمل دستورالعملهای محاسبهٔ BCD , aaa
، aad
، daa
و سایر دستورالعملها است.
دستورالعملهای ممیز شناور
زبان اسمبلی x86 شامل دستورالعملهایی برای یک واحد ممیز شناور مبتنی بر پشته (FPU) است. FPU در سری پردازندههای ۸۰۸۶ تا ۸۰۳۸۶، یک پردازندهٔ جداگانهٔ کمکی اختیاری بود، در سری پردازندههای ۸۰۴۸۶ یک ویژگی اختیاری روی تراشه بود و از سری ۸۰۴۸۶ به بعد، یک ویژگی استاندارد در هر پردازنده Intel x86 است، که از پنتیوم آغاز شد. دستورالعملهای FPU شامل جمع، تفریق، نفی، ضرب، تقسیم، باقی مانده، ریشههای مربع، کوتاه سازی عدد صحیح، کوتاه سازی کسر و مقیاس کردن با توان دو است. این دستوالعملها همچنین شامل دستورالعملهای تبدیل هستند که میتوانند یک مقدار را از حافظه در هر یک از فرمتهای: دهدهی کدگذاری شده به صورت دودویی، صحیح ۳۲ بیتی، صحیح ۶۴ بیتی، ممیز شناور ۳۲ بیتی، ممیز شناور ۶۴ بیتی یا ممیز شناور ۸۰ بیتی (در هنگام بارگیری، مقدار مورد نظر به حالت ممیز شناور در حال حاضر استفاده شده تبدیل میشود) بارگذاری یا در حافظه ذخیره کنند. x86 همچنین شامل تعدادی از توابع غیرجبری، از جمله سینوس، کسینوس، تانژانت، آرکتانژانت، به توان رساندن با پایه ۲ و لگاریتم در مبنایهای ۲، ۱۰، یا e، میشود.
دستورالعملهایی که مربوط به ثباتهای پشته هستند معمولاً فرمتی به شکلfop st,st(n)
یا fop st(n),st
دارند که st
معادل st(0)
میباشد و st(n)
یکی از ۸ رکورد پشته (st(0)
، st(1)
، …، st(7)
). مانند دستورالعملهای مربوط به اعداد صحیح، اولین عملوند هم اولین عملوند مبدأ است و هم عملوند مقصد. fsubr
و fdivr
باید قبل از انجام تفریق یا تقسیم، اولی مبادله اپراتورهای منبع را انتخاب کنند. دستورالعمل اضافه، تفریق، ضرب، تقسیم، ذخیره و مقایسه شامل دستورالعملهایی است که بعد از عملیاتشان پشته بالای پشته میشوند؛ بنابراین، به عنوان مثال، faddp st(1), st
محاسبه st(1) = st(1) + st(0)
، سپس st(0)
از بالای پشته حذف میکند، بنابراین نتیجه در st(1)
بالای پشته در st(0)
.
دستورالعمل SIMD
CPUهای مدرن x86 شامل دستورالعملهای SIMD هستند که عمدتاً عملیات مشابه را بهطور موازی در بسیاری از مقادیر کد گذاری شده در ثبت یک SIMD گسترده انجام میدهند. تکنولوژیهای مختلف آموزش از عملیاتهای مختلف در مجموعههای ثبت نام مختلف پشتیبانی میکنند اما در کل کامل (از MMX به SSE4.2) شامل محاسبات عمومی در ریاضی عدد صحیح یا شناور (اضافه کردن، تفریق، ضرب، تغییر، حداقل، حداکثر سازی، مقایسه، تقسیم یا مربع ریشه) بنابراین برای مثال، paddw mm0, mm1
انجام ۴ موازی ۱۶ بیتی (نشان داده شده w
) عدد صحیح میافزاید (نشان داده شده padd
) از mm0
ارزش به mm1
و در نتیجه در ذخیره mm0
. جریان SIMD Extensions یا SSE همچنین شامل یک حالت نقطه شناور است که در آن فقط تنها اولین مقدار از registers در واقع اصلاح شدهاست (گسترش در SSE2). برخی از دستورالعملهای غیرمعمول دیگر شامل مجموع اختلافهای مطلق (استفاده میشود برای برآورد حرکت در فشرده سازی ویدئو، مانند در MPEG انجام میشود) و دستورالعمل تجمع ۱۶ بیتی (مفید برای نرمافزار مبتنی بر ترکیب آلفا و فیلتر دیجیتال). SSE (از SSE3) و 3DNow! برنامههای افزودنی شامل دستورالعمل اضافه کردن و تفریق برای درمان مقادیر ذره شناور زوج مانند اعداد پیچیدهاست.
این مجموعه دستورالعملها همچنین شامل دستورالعملهای چند کلمه ای ثابت متعدد برای شیفت دادن، قرار دادن و استخراج مقادیر درون ثباتها هستند. علاوه بر این دستورالعملهایی برای انتقال داده بین ثباتهای عدد صحیح و ثباتهای XMM (مورد استفاده در SSE) FPU (استفاده شده در MMX)
دستورالعملهای دستکاری اطلاعات
پردازندهٔ x86 همچنین شامل حالتهای پیچیدهٔ آدرس دهی حافظه با یک افست بی واسطه، یک ثبات، یک ثبات با یک افست، یک ثبات مقیاس پذیر با افست یا بدون افست، و یک ثبات با یک افست اختیاری و یک ثبات مقیاس پذیر؛ بنابراین، برای مثال، میتوان mov eax, [Table + ebx + esi*4]
را به عنوان یک دستور واحد که ۳۲ بیت داده را از آدرس محاسبه شده با افست (Table + ebx + esi * 4)
از ds
بارگذاری میکند و در ثبات eax ذخیره میکند. بهطور کلی، پردازندههای x86 میتوانند بارگذاری کرده و استفاده از حافظه سازگار با اندازه هر یک از ثبت نام آن عمل میکند. (دستورالعمل SIMD همچنین شامل دستورالعمل نیمه بار است)
مجموعه آموزشی x86 شامل بار رشته، فروشگاه، حرکت، اسکن و مقایسه دستورالعمل (lods
، stos
، movs
، scas
و cmps
) که انجام هر عملیات به یک اندازه مشخص (b
برای بایت ۸ بیتی، w
برای کلمه ۱۶ بیتی، d
برای کلمهٔ ۳۲ بیتی دو برابر) پس از آن افزایش / کم می (بسته به DF، پرچم جهت) آدرس ثبت نام ضمنی (si
برای lods
، di
برای stos
و scas
، و هر دو برای movs
و cmps
). برای بار، فروشگاه و عملیات اسکن، ضمنی هدف / منبع / مقایسه ثبت نام در است al
، ax
یا eax
ثبت نام (بسته به اندازه). ثبات بخش ضمنی استفاده میشود ds
برای si
و es
برای di
. ثبت نام cx
یا ecx
به عنوان یک شمارنده کاهش مییابد و عملیات زمانی که شمارنده برابر صفر میشود یا (برای اسکن و مقایسهها) هنگامی که نابرابری شناسایی میشود، متوقف میشود.
پشته با اشاره گر پشته ضمنی کاهش (افزایش) و افزایش (پاپ) قرار میگیرد. در حالت ۱۶ بیتی، این نشانگر پشته ضمنی به عنوان SS: [SP] خطاب شدهاست، در حالت ۳۲ بیت SS است: [ESP]، و در حالت ۶۴ بیتی [RSP] است. اشاره گر پشته عموماً به آخرین مقدار ذخیره شده اشاره میکند، با این فرض که اندازه آن با حالت عملیاتی پردازنده (یعنی ۱۶، ۳۲ یا ۶۴ بیت) مطابقت دارد با عرض پیش فرض push
/ pop
/ call
/ ret
دستورالعمل. همچنین دستورالعملها enter
و leave
که ذخیره و حذف دادهها از بالای پشته در هنگام تنظیم یک اشاره گر قاب پشته در bp
/ ebp
/ rbp
. با این حال، تنظیم مستقیم، یا جمع و تفریق به ثبت sp
/ esp
/ rsp
نیز پشتیبانی میشود، بنابراین دستورالعمل enter
/ leave
بهطور کلی غیر ضروری است.
این کد در آغاز یک تابع نوشته میشود:
push ebp ; save calling function's stack frame (ebp)
mov ebp, esp ; make a new stack frame on top of our caller's stack
sub esp, 4 ; allocate 4 bytes of stack space for this function's local variables
… که به لحاظ عملکردی برابر است با:
enter 4, 0
دستورالعملهای دیگر برای دستکاری پشته شامل pushf
/ popf
برای ذخیره و بازیابی (E) FLAGS، دستورالعمل pusha
/ popa
برای ذخیره و بازیابی تمامی ثباتهای صحیح در پشته، میباشد.
فرض بر این است که ارزش برای یک بار یا فروشگاه SIMD در موقعیتهای مجاور برای ثبت SIMD بستهبندی میشود و آنها را در ترتیبی از ترتیب کمی محدود میکند. بعضی از دستورالعملهای بارگذاری و ذخیره SSE به درستی عمل میکنند تا هماهنگ سازی ۱۶ بایت انجام شود. دستورالعملهای SIMD همچنین شامل دستورالعمل پیشفرشی هستند که بار را انجام میدهند اما هیچ ثبتی را هدف قرار نمیدهند، که برای بارگیری کش استفاده میشود. دستورالعملهای SSE همچنین شامل دستورالعملهای فروشگاه غیر زمانی هستند که فروشگاهها را مستقیماً به حافظه بدون انجام کش کش اختصاص میدهند اگر مقصد قبلاً ذخیره نگردد (در غیر این صورت مانند یک فروشگاه منظم رفتار خواهد کرد)
بیشترین دستورالعمل عدد صحیح عمومی و شناور (اما بدون SIMD) میتواند از یک پارامتر به عنوان یک منبع پیچیده به عنوان پارامتر منبع دوم استفاده کند. دستورالعملهای عدد صحیح نیز میتوانند یک پارامتر حافظه را به عنوان یک عملگر مقصد اعمال کنند.
مأموریت x86 دارای عملیات پرش بدون قید و شرط، <code id="mwAbs">jmp</code> که میتواند یک آدرس فوری، یک ثبت یا یک آدرس غیرمستقیم به عنوان یک پارامتر (توجه داشته باشید که اکثر پردازندههای RISC فقط از یک پرونده لینک یا یک جابجایی فوری کوتاه برای پریدن حمایت میکنند).
همچنین پشتیبانی میشود چند جهش مشروط، از جمله jz
(پرش به صفر)، jnz
(پرش در غیر صفر)، jg
(پرش در بزرگتر از، امضا)، jl
(پرش در کمتر از، امضا)، ja
(پرش در بالا / بزرگتر از، بدون علامت)، jb
(پرش در زیر / کمتر از، unsigned). این عملیات مشروط بر اساس بیت مشخص در ثبت (E) FLAGS بنا شدهاست. بسیاری از عملیات ریاضی و منطقی، بسته به نتیجه آنها، این پرچمها را مشخص یا تکمیل میکنند. مقایسه مقادیر cmp
(مقایسه) و دستورالعملهای <code id="mwAcY">test</code>، پرچمها را به گونه ای تنظیم میکند که به ترتیب، آنها را بهطور جداگانه یا عملیات bitwise AND انجام دادهاند، بدون تغییر مقادیر عملان. همچنین دستورالعملهایی مانند clc
(پرچم حمل روشن) و cmc
(پرچم تکمیل حمل) وجود دارد که بهطور مستقیم بر روی پرچمها کار میکند. شناور مقایسه نقاط از طریق انجام fcom
یا ficom
دستورالعمل که در نهایت باید به پرچم عدد صحیح تبدیل میشود.
هر عمل پرش به سه شکل متفاوت است: بسته به اندازه اپنج. پرش کوتاه از عملیات امضا شده ۸ بیتی استفاده میکند که از دستورالعمل فعلی جبران میشود. پرش نزدیک شبیه یک پرش کوتاه است اما با استفاده از عملگر ۱۶ بیتی امضا شده (در حالت واقعی یا محافظت شده) یا عمل ۳۲ بیتی امضا شده (فقط در حالت محافظت شده ۳۲ بیتی). پرش به مراتب یکی است که از پایه کامل بخش استفاده میکند: مقدار افست به عنوان آدرس مطلق. اشکال غیرمستقیم و نمایه شده از هر کدام نیز وجود دارد.
علاوه بر عملیات پرش ساده، call
(call subroutine) و ret
(return from subroutine) دستورالعملها هستند. قبل از انتقال کنترل به زیر فرعی، call
را فشار دهید آدرس offset بخش دستورالعمل زیر call
به پشته؛ ret
این مقدار را از پشته باز میکند و به آن میپیوندد، بهطور مؤثر بازگشت جریان کنترل به آن قسمت از برنامه. در مورد far call
، پایه بخش تحت فشار قرار میگیرد؛ far ret
میآید افست و سپس پایه بخش به بازگشت.
همچنین دو دستورالعمل مشابه وجود دارد: <code id="mwAdk">int</code> (interrupt)، که موجب صرفه جویی در مقدار فعلی (E) FLAGS ثبت شده در پشته میشود، سپس یک far call
انجام میدهد، به جز آن که به جای آدرس، از یک بردار وقفه، یک شاخص به یک جدول استفاده میکند از آدرسهای پردازش وقفه. بهطور معمول، پردازنده وقفه موجب صرفه جویی در تمام سایر پردازندههای ثبت شده مورد استفاده میشود، مگر اینکه آنها برای بازگشت نتیجه عملیات به برنامه تماس (در نرمافزار به نام وقفهها) مورد استفاده قرار گیرد. بازگشت تطبیقی از دستورالعمل وقفه iret
، که پس از بازگشت، پرچمها را بازیابی میکند. قطعهای نرم از نوع توصیف شده در بالا بعضی از سیستم عاملها برای تماسهای سیستم استفاده میشود و همچنین میتواند در اشکال زدایی مدرسان وقفههای سخت استفاده شود. وقفههای سخت باعث وقایع سختافزاری خارجی میشوند و باید تمام مقادیر ثبت را حفظ کنند به عنوان وضعیت برنامه فعلی اجرای ناشناخته است. در حالت حفاظت شده، وقفه ممکن است توسط سیستم عامل برای راه اندازی یک سوئیچ کار، که بهطور خودکار تمام ثبت نام از کار فعال را ذخیره کنید.
مثالها
برنامهٔ "سلام دنیا!" برای DOS در اسمبلی به سبک MASM
از وقفهٔ 21h برای نمایش خروجی استفاده میشود، مثالهای دیگر از printf برای نمایش خروجی استفاده میکنند .
.model small
.stack 100h
.data
msg db 'Hello world!$'
.code
start:
mov ah, 09h ; Display the message
lea dx, msg
int 21h
mov ax, 4C00h ; Terminate the executable
int 21h
end start
برنامهٔ "سلام دنیا!" برای ویندوز در اسمبلی به سبک MASM
; requires /coff switch on 6.15 and earlier versions
.386
.model small,c
.stack 1000h
.data
msg db "Hello world!",0
.code
includelib libcmt.lib
includelib libvcruntime.lib
includelib libucrt.lib
includelib legacy_stdio_definitions.lib
extrn printf:near
extrn exit:near
public main
main proc
push offset msg
call printf
push 0
call exit
main endp
end
برنامهٔ "سلام دنیا!" برای ویندوز در اسمبلی به سبک NASM
; Image base = 0x00400000
%define RVA(x) (x-0x00400000)
section .text
push dword hello
call dword [printf]
push byte +0
call dword [exit]
ret
section .data
hello db "Hello world!"
section .idata
dd RVA(msvcrt_LookupTable)
dd -1
dd 0
dd RVA(msvcrt_string)
dd RVA(msvcrt_imports)
times 5 dd 0 ; جدول را تمام میکند
msvcrt_string dd "msvcrt.dll", 0
msvcrt_LookupTable:
dd RVA(msvcrt_printf)
dd RVA(msvcrt_exit)
dd 0
msvcrt_imports:
printf dd RVA(msvcrt_printf)
exit dd RVA(msvcrt_exit)
dd 0
msvcrt_printf:
dw 1
dw "printf", 0
msvcrt_exit:
dw 2
dw "exit", 0
dd 0
برنامهٔ "سلام دنیا!" برای لینوکس در اسمبلی به سبک NASM
;
; This program runs in 32-bit protected mode.
; build: nasm -f elf -F stabs name.asm
; link: ld -o name name.o
;
; In 64-bit long mode you can use 64-bit registers (e.g. rax instead of eax, rbx instead of ebx, etc.)
; Also change "-f elf " for "-f elf64" in build command.
;
section .data ; section for initialized data
str: db 'Hello world!', 0Ah ; message string with new-line char at the end (10 decimal)
str_len: equ $ - str ; calcs length of string (bytes) by subtracting the str's start address
; from this address ($ symbol)
section .text ; this is the code section
global _start ; _start is the entry point and needs global scope to be 'seen' by the
; linker --equivalent to main() in C/C++
_start: ; definition of _start procedure begins here
mov eax, 4 ; specify the sys_write function code (from OS vector table)
mov ebx, 1 ; specify file descriptor stdout --in gnu/linux, everything's treated as a file,
; even hardware devices
mov ecx, str ; move start _address_ of string message to ecx register
mov edx, str_len ; move length of message (in bytes)
int 80h ; interrupt kernel to perform the system call we just set up -
; in gnu/linux services are requested through the kernel
mov eax, 1 ; specify sys_exit function code (from OS vector table)
mov ebx, 0 ; specify return code for OS (zero tells OS everything went fine)
int 80h ; interrupt kernel to perform system call (to exit)
برنامهٔ "سلام دنیا!" برای لینوکس در اسمبلی به سبک NASM با استفاده از کتابخانه استاندارد C
;
; This program runs in 32-bit protected mode.
; gcc links the standard-C library by default
; build: nasm -f elf -F stabs name.asm
; link: gcc -o name name.o
;
; In 64-bit long mode you can use 64-bit registers (e.g. rax instead of eax, rbx instead of ebx, etc..)
; Also change "-f elf " for "-f elf64" in build command.
;
global main ;main must be defined as it being compiled against the C-Standard Library
extern printf ;declares use of external symbol as printf is declared in a different object-module.
;Linker resolves this symbol later.
segment .data ;section for initialized data
string db 'Hello world!', 0Ah, 0h ;message string with new-line char (10 decimal) and the NULL terminator
;string now refers to the starting address at which 'Hello, World' is stored.
segment .text
main:
push string ;push the address of first character of string onto stack. This will be argument to printf
call printf ;calls printf
add esp, 4 ;advances stack-pointer by 4 flushing out the pushed string argument
ret ;return
برنامهٔ "سلام دنیا!" برای لینوکس ۶۴ بیتی در اسمبلی به سبک NASM
; build: nasm -f elf64 -F stabs name.asm
; link: ld -o name name.o
BITS 64
SECTION .data
Hello: db "Hello world!",10
len_Hello: equ $-Hello
SECTION .text
global _start
_start:
mov rax,1 ; write syscall (x86_64)
mov rdi,1 ; fd = stdout
mov rsi,Hello ; *buf = Hello
mov rdx,len_Hello ; count = len_Hello
syscall
mov rax,60 ; exit syscall (x86_64)
mov rdi,0 ; status = 0 (exit normally)
syscall
استفاده از ثباتهای پرچم
پرچمها در معماری x86 برای عمل مقایسه بسیار مورد استفاده قرار میگیرند. هنگام مقایسهٔ بین دو داده، CPU، مقدار پرچم یا پرچم مربوطه را تنظیم میکند. با توجه به مقادیر قرار گرفته در پرچمها، دستورالعملهای پرش شرطی انجام میشوند و برنامه به کدی که باید انجام شود پرش میکند، به عنوان مثال:
cmp eax, ebx
jne do_something
; ...
do_something:
; do something here
پرچمها نیز در معماری x86 برای روشن و خاموش کردن ویژگیهای خاص یا حالتهای اجرا استفاده میشود. برای مثال، برای غیرفعال کردن همه وقفههای ماسک، میتوانید از دستور زیر استفاده کنید:
cli
ثبت نام پرچمها نیز میتواند بهطور مستقیم مشاهده شود. کم ۸ بیت از ثبت نام پرچم را میتوان با استفاده از دستور lahf
به ah
lahf
. کل ثبت نام پرچمها همچنین میتواند با استفاده از دستورالعمل pushf
، popf
، int
(از جمله into
) و iret
.
اشارهگر دستور ip
در حالت ۱۶ بیت نامیده میشود، در حالت ۳۲ بیتی eip
در حالت ۶۴ بیتی rip
شود. ثبت نام اشارهگر دستور اشاره به آدرس حافظه است که پردازنده بعدی تلاش برای اجرا؛ آن را نمیتوان به صورت مستقیم در حالت ۱۶ بیتی یا ۳۲ بیتی مشاهده کرد، اما یک توالی مانند زیر میتوان برای قرار دادن آدرس next_line
به eax
:
call next_line
next_line:
pop eax
این دنبالهٔ دستورالعملها، کد مستقل موقعیتی را ایجاد میکند، زیرا call
یک دستور promoter-relative prompt را به دست میدهد که صفر را در بایت دستورالعمل هدف از دستورالعمل بعدی (در این مورد ۰) توصیف میکند.
نوشتن به اشاره گر دستورالعمل ساده است - دستور jmp
دستورالعمل دستورالعمل را به آدرس مقصد هدایت میکند، به عنوان مثال، یک توالی مانند زیر، محتویات eax
را به eip
:
jmp eax
در حالت ۶۴ بیتی، دستورالعملها خود میتوانند دادههای مربوط به اشاره گر دستورالعمل را ارجاع دهند، بنابراین دیگر کمتر نیاز به کپی کردن مقدار IP در یک رجیستر دیگر، خواهیم داشت.
جستارهای وابسته
- زبان اسمبلی
- دستورالعمل X86 دستورالعمل
- معماری X86
- طراحی CPU
- فهرست مونتاژ کنندهها
- کد خود را تغییر دهید
- داس
منابع
- ↑ Narayam, Ram (2007-10-17). "Linux assemblers: A comparison of GAS and NASM". Archived from the original on October 3, 2013. Retrieved 2008-07-02.
- ↑ "The Creation of Unix". Archived from the original on April 2, 2014.
- ↑ Hyde, Randall. "Which Assembler is the Best?". Archived from the original on 18 October 2007. Retrieved 2008-05-18.
- ↑ "GNU Assembler News, v2.1 supports Intel syntax". 2008-04-04. Retrieved 2008-07-02.
- ↑ Mueller, Scott (March 24, 2006). "P2 (286) Second-Generation Processors". Upgrading and Repairing PCs, 17th Edition (Book) (17 ed.). Que. ISBN 0-7897-3404-4. Retrieved 2017-12-06.
خواندن بیشتر
دفترچهٔ راهنما
- اینتل ۶۴ و IA-32 دستورالعملهای توسعه دهنده نرمافزار
- دستورالعمل برنامهنویس AMD64 معماری (دوره ۱-۵)
کتابها
- Ed, Jorgensen (May 2018). x86-64 Assembly Language Programming with Ubuntu (PDF) (1.0.97 ed.). p. 367.