سلام
اگه یادتون باشه، آخرین بار با مطلبی به نام «پروژه ساده با اسمبلی؛ برنامه جمع دو عدد» سعی کردیم کمی شما رو با کلیت زبان برنامهنویسی اسمبلی آشنا کنیم.
یعنی صرفا برای بدست آوردن یه دید کلی بود.
البته اونطور که متوجه شدم، برای برخی مخاطبین که با زبانهای سطح پایین و کلا اسمبلی آشنا نبودن، خیلی گنگ بود!
واقعیت اینه که مشکل خیلی از من نیست؛ چون پیشنیاز درک اسمبلی، شناخت مختصری از معماری کامپیوتر و عملکرد پردازنده هست.
این بار قراره وارد جزئیات بشیم؛ اما بازم پیشنیاز درک مفاهیم پردازنده وجود داره.
چون اصلا دلم نمیخواد مثلا توضیح بدم رجسیتر چیه، ساختار حافظه رم چطوریه و… 🙃
اما انتظار میره که با این مفاهیم آشنا باشید.
با من همراه باشید.
مفهوم اسمبلی
خب اسمبلی یه زبان (خیلی) سطح پایین هست که برخی مفاهیم جمع و جور توی سایر زبانها، توی این زبان دیگه جمع و جور نیستن.
مثلا مفاهیم توابع کمی در سطح این زبان تغییر میکنن؛ چرا که اصلا چیز مستقلی به نام تابع نداریم.
ولی راههایی برای ساخت یه تیکه کد که بعدا صدا بزنیم و به عنوان تابع ازش استفاده کنیم، داریم.
توی اسمبلی، ما مستقیما با پردازنده مرتبط میشیم و بهش دستور میدیم! و همینطور دستورات ما به شکل مستقیم روی رجسیترهای(ثباتهای) پردازنده و حافظه تاثیر میگزاره.
برنامه تحت اسمبلی
یه برنامه و در واقع کد کامل اسمبلی معمولا از دو بخش اصلی تشکیل میشه:
.data : بخش مربوط به دادهها (متغیرها،رشتهها و…)
.text : بخش کد و دستورات
و یه نقطه شروع همیشه برای برناممون داریم که کدها از اونجا شروع به اجرا شدن میکنن.
_start
(پیشنهاد میکنم همزمان مطلب قبل رو باز کنید و سعی کنید نگاهی به کد داشته باشید)
برخی دستورات:
MOV
اگه به سورسی که توی مطلب سابق هست نگاهی بندازید، میبینید که از دستور MOV زیاد استفاده شده.
همونطور که از اسمش مشخص هست، برای جابجایی هست.
مثلا در
1 |
mov eax, 3 |
عدد 3 در رجسیتر eax قرار گرفته.
همینطور اگه توی سورس نگاه کنید، ما بعد از گرفتن مقدار num1 ، اون رو در ecx ریختیم.
ADD
مشخصا برای جمع استفاده میشه.
مثل:
1 |
add eax, ebx |
مقادیر درون رجیسترهای eax و ebx با هم جمع شدن.
اما اگه دقیقتر بگیم، ebx اضافه شده به eax.
SUB
این هم مخفف subtract هست و برای تفریق استفاده میشه.
مثل:
1 2 |
sub eax, ebx |
تو این دستور، مقدار موجود در رجیستر ebx
از eax
کم میشه.
یعنی خروجی نهایی داخل eax
قرار میگیره و ebx
هم بدون تغییر میمونه.
(نکته: رجیسترها مثل متغیر نیستن که با = مقدارشون عوض بشه؛ بیشتر مثل ظرفهایی هستن که عملیات مستقیم روشون انجام میگیره.)
INT
برای ایجاد وقفه (Interupt) استفاده میشه؛ مثلا:
1 2 |
int 0x80 |
اینجا درخواست میکنیم یه «خدمت» یا همون syscall برامون انجام بده.
این خدمت میتونه خروج از برنامه باشه (exit
)، چاپ چیزی روی صفحه (write
) یا حتی خوندن از ورودی.
ولی یادتون نره که قبلش باید توی رجیستر eax
شماره syscall مربوطه رو بذارید، و رجیسترهای دیگه رو هم بر اساس نوع درخواست پر کنید.
(الان باید مطلب قبل رو بهتر متوجه بشید)
مثلاً برای خروج از برنامه:
1 2 3 |
mov eax, 1 mov ebx, 0 int 0x80 |
محاسبه طول رشته
1 |
len_resultMsg equ $ - resultMsg |
توی این خط کد داریم یه ثابت به نام len_resultMsg
تعریف میکنیم که مقدارش برابر باشه با تعداد بایتهای بین resultMsg
و $
، یعنی طول رشتهای که با resultMsg
شروع شده.
توی این مطلب سعی کردم یه مرور جزئیتر از برخی دستورات پایه اسمبلی داشته باشم.
تو قسمت بعدی احتمالاً بریم سراغ مفاهیم مهمتری مثل استک (stack)، فراخوانی تابع، و مدیریت حافظه توی اسمبلی.
اگه الان که اینجا هستید، حس کردید یه چیزی گنگه یا جا نیفتاده، کامنت بذارید اینجا یا توی مطلب قبل؛ چون این مسیریه که فقط با پرسیدن و تجربه کردن یاد گرفته میشه.