زبان اسمبلی - Assembly Language

زبان اسمبلی (Assembly Language) یک زبان برنامه‌نویسی سطح پایین (Low-Level Programming language) است.

دستورهای زبان اسمبلی به کد ماشین و در نتیجه، به سخت‌افزار بسیار نزدیک هستند. به همین دلیل، فراگیری زبان اسمبلی دشوار و کاربردهای آن، منحصر به برخی از زمینه‌های خاص است. از یادگیری زبان اسمبلی برای سازوکار کلیه قطعات کامپیوتر از واحد پردازش مرکزی (CPU) گرفته تا حافظه استفاده می‌شود.

برای درک بهتر جایگاه زبان اسمبلی در میان دیگر زبان‌ها از جهت نزدیکی نحو به سخت‌افزار باید گفت که زبان اسمبلی از زبان ماشین سطح بالاتر و از زبان‌هایی مانند پاسکال و C/C++‎ سطح پایین‌تر است. بنابراین، کد نوشته شده به زبان اسمبلی نیز برای اجرا نیاز به آن دارد که به کد قابل اجرا توسط ماشین یا همان زبان ماشین تبدیل شود.

از پایین به بالا دسترسی کمتر میشه ولی بیان کار آسون تر میشه
از پایین به بالا دسترسی کمتر میشه ولی بیان کار آسون تر میشه


زبان اسمبلی اساسا به معماری سخت‌افزار و سیستم‌عامل نصب شده روی آن بستگی دارد.

کد اسمبلی با بهره‌گیری از برنامه ای که اسمبلر (assembler) نام دارد، به کد ماشین قابل اجرا تبدیل می‌شود. زبان اسمبلی معمولا یک دستور به ازای هر دستوالعمل ماشین دارد. البته، در زبان اسمبلی از نظرات (همون کامنت - متنی که کاری انجام نمیده و برنامه نویسیا واسه خودشون مینویسن)، دایرکتیوها (directive)و ماکروها(macro) ( چیزی ساده از تابع در زبان های برنامه نویسی سطح بالا ) و نمادها (symbol) برنامه و دستورات تخصیص حافظه نیز پشتیبانی می‌شود.

هر زبان اسمبلی مختص یک معماری کامپیوتری خاص و گاهی نیز مختص یک سیستم‌عامل خاص است. اگرچه، اغلب زبان‌های اسمبلی نحو اختصاصی را برای فراخوانی‌های عملیات های سیستمی (System Calls) فراهم نمی‌کنند و اغلب زبان‌ها اسمبلی را می‌توان با سیستم‌عامل‌های گوناگون به کار برد. زبان اسمبلی دسترسی به "همه" ظرفیت‌های واقعی پردازنده را فراهم می‌کند.

مزایای زبان اسمبلی

  • امکان انجام کارهای پیچیده را برای اجرا به صورت ساده‌تر فراهم می‌کند.
  • کارایی حافظه خوبی دارد و نیاز به حافظه کمی دارد.
  • سرعت بالاتری در اجرا دارد و زمان اجرای آن کمتر است.
  • اساسا مبتنی بر سخت‌افزار است.
  • نیاز به دستورالعمل‌های کمتری برای رسیدن به نتیجه دارد.


معایب زبان اسمبلی

  • زمان و تلاش زیادی نسبت به دیگر زبان‌های برنامه‌نویسی برای نوشتن کدی مشابه، می‌طلبد.
  • بسیار پیچیده و درک آن دشوار است.
  • یادآوری نحو آن دشوار است.
  • قابل استفاده نبودن کد اسمبلی در سخت افزار های مختلف (البته در برخی از نسخه‌های اسمبلی این مشکل تا حدی رفع شده و سخت‌افزارهای بیشتری را پوشش می‌دهند.)

برخی از حوزه‌هایی که هنوز هم از زبان اسمبلی در آن‌ها استفاده می‌شود در ادامه بیان شده‌اند:

  • سیستم عامل‌ها
  • سخت‌افزارها
  • درایورهای دستگاه
  • طراحی کامپایلر
  • درک ساز و کار کامپیوترها

کاربرد زبان اسمبلی در درک ساز و کار کامپیوترها چیست؟

چنانکه پیش‌تر نیز بیان شد، یکی از دلایل یادگیری آن است که زبان اسمبلی می‌تواند به درک بهتر آنچه که در کامپیوتر به وقوع می‌پیوندد کمک شایان توجهی کند دلیل این امر آن است که زبان اسمبلی به زبان ماشین و سخت‌افزار بسیار نزدیک است و امکان کنترل کامل روی سخت‌افزار را به کاربر می‌دهد.

کاربرد زبان اسمبلی به طور متداول چیست؟

زبان اسمبلی معمولا برای کدنویسی‌های سطح پایین مورد استفاده قرار می‌گیرد. برای مثال، برای کدنویسی کرنل سیستم‌عامل مورد استفاده قرار می‌گیرد که نمی‌توان به توابع موجود در سطح زبان برنامه نویسی برای کار با سخت افزار های مختلف اکتفا کرد و گاهی لازم میشود برای هر سخت افزار کد جدا نوشت.

تفاوت زبان ماشین (0و1) و زبان اسمبلی چیست؟

تفاوت اساسی بین کد ماشین و زبان اسمبلی آن است که کد ماشین شامل کدهای دودویی است که می‌توان آن‌ها را به طور مستقیم توسط کامپیوتر اجرا کرد. در حالی که زبان اسمبلی نیاز به نرم‌افزاری به نام اسمبلر (assembler) برای تبدیل به کد ماشین دارد.

نحوه کار یک اسمبلر :

  • رایانه ها با مجموعه خاصی از دستورالعمل های اصلی ارائه می شوند که با عملکردهای اساسی که کامپیوتر می تواند انجام دهد مطابقت دارند. به عنوان مثال ، عبارت “Load" باعث می شود که پردازنده مجموعه ای از بیت ها را از مکانی در حافظه پردازنده به یک مخزن ویژه به نام Register منتقل کند.
  • برنامه نویس می تواند با استفاده از هر یک از دستورالعمل های اسمبلی برنامه ای بنویسد.
  • اسمبلر هر دستورالعمل برنامه اسمبلی را در پردازش میکند و کد ماشین مربوط به آن را ایجاد میکند.

زبان های اسمبلی از نظر معماری پردازنده متفاوت هستند ، اما آنها اغلب دارای دستورالعمل ها و عملگرهای مشترک هستند. در زیر چند نمونه از دستورالعمل های پردازنده x86 (پردازنده های 32 بیتی) ارائه شده است.

  • دستور MOV - انتقال داده ها از یک مکان به مکان دیگر.
  • دستور ADD - جمع دو مقدار
  • دستور SUB - عملیات تفریق
  • دستور PUSH - اضافه کردن داده به stack
  • دستور POP - برداشتن داده از stack
  • دستور JMP - پرش به مکان دیگری از برنامه
  • دستور INT - قطع یک روند

مفاهیم

ا Instructions : منظور از دستورالعمل های زبان اسمبلی دستورالعمل هایی مانند ADD، MOV و ... هستند که در زمان ترجمه برنامه، اسمبلر معادل کد زبان ماشین آنها را پیدا کرده و جایگزین می کند.

  • ا Opcode :بخشی از دستورالعمل زبان ماشین است که دستور اسمبلی (Instruction) را مشخص میکند.
  • ا literal: مقداری که صریحا نوشته شده باشد ( برای مثال 43 یک literal از نوع عدد و true یک literal از نوع بولین میباشد )
  • ا Symbol: عبارتی که به مقداری اشاره کند. فرض کنید مقدار 32 در متغیر a ذخیره شده باشد - این این مثال خود عبارت "a" یک نماد یا symbol است. symbol برخلاف literal است
  • ا Object code: تکه کد ماشینی که که هنوز به اجزای دیگر برنامه متصل نشده است
  • ا linker: این قسمت از کامپایلر اسمبلی ( اسمبلر ) ، object code ها را بهم وصل میکند و فایل نهایی رو آماده میسازد.
  • ا Directives : راهنماهای اسمبلر دستورات خاصی هستند که از دستورالعمل های معتبر پردازنده نیستند و در فایل ترجمه شده ظاهر نمی شوند بلکه هنگام ترجمه برنامه به اسمبلر مطالبی را می فهمانند:

ds، code, data، stack، model, end

  • ا Forward referencing:

به اشاره به مقدار یا نمادی که در چند خط بعد تعریف شده میگویند. برای مثال در تکه کد زیر در زبان c++ ، متغیر myValue در کلاس C قبل از تعریف خود در تابع های mutator و accessor مورد استفاده و فراخوانی قرار گرفته.


دو نوع اسمبلر بر اساس تعداد گذرها از روی کد منبع برای تولید برنامهٔ قابل اجرا وجود دارد:

  • یک بار گذر
  • چند بار گذر

یک بار گذر:

اسمبلرهای یکبار گذر فقط یک بار از روی کد عبور می‌کنند. هر نمادی که قبل از تعریف شدنش استفاده شده باشد به یک خطا در پایان object code نیاز دارد تا به linker اعلام کند که برگرد و قسمتی را که بر اثر استفاده از این نماد قبل از تعریف شدنش، جاگذاشته شده بود را بازنویسی کند. به این عمل Forward reference میگویند

چند بار گذر:

اسمبلرهای چندبار گذر جدولی از تمامی نمادها و مقادیر آنها در گذر اول ایجاد می‌کنند و سپس در گذرهای بعدی با استفاده از این جدول، نمادها را با مقادیر آنها جایگزین کرده و فایل اجرایی را تولید می‌کنند.

روند کار یک اسمبلر چندبار گذر
روند کار یک اسمبلر چندبار گذر


مراحل forward reference در یک بار گذر ها

  • اگر symbol هنوز تعریف نشده است، آدرس دهی را رها میکند.
  • این symbol تعریف نشده را وارد SymTab (symbol table) میکند و مشخص میکند که تعریف نشده است.
  • آدرس این آدرس symbol را به لیستی از forward references مرتبط با ردیف SymTab اضافه میکند.
  • وقتی تعریفی برای نماد پیدا شد، لیست مرجع را اسکن کرده و آدرس را وارد می کند.
  • در پایان فرایند، linker بر اساس این مقادیر، آدرس را جایگذاری میکند و اگر هنوز ردیفی از SymTab وجود دارد که این symbol تعریف نشده است، خطای عدم تعریف را گزارش میکند.

مقایسه:

  • سرعت کمتر نسبت به یک بار گذر ها
  • نیاز به حافظه بیشتر
  • در صورت نبودن خطا، سرعت پیوند دهی بالا تر می رود

مثالی از اسمبلر دوبار گذر

در گذر اول:

1- محل کد را نگهداری میکند. (Location Counter) LC شمارنده‌اي است كه هنگام پردازش اين دستور توسط اسمبلر، آدرس نخستين بايت هر دستور را در خود ذخيره مي كند.

2- نمادها (symbol) و literal ها را تعریف میکند و آنها را به ترتیب در جدول نمادها و جدول literal ذخیره میکند.

جدولي كه در آن هر Label با آدرس متناظر آن خط (Location)، در كنار هم مي‌آيند و اسمبلر هر دو عبارت در يك خط از Symbol Table، را معادل هم و يكسان ميگيرد. يعني از آن به بعد هر جا در برنامه Label خاصي را به كار برديم، اسمبلر به جاي آن، آدرس متناظر در Symbol Table را قرار مي‌دهد.

در گذر دوم:

1- با تبدیل opcode نمادین (mnemonic) به opcode عددی مربوطه، کد شی را ایجاد میکند.

2- مقداردهی به literal ها و یافتن مقادیر نمادها (symbols)


1.ا START : اجرای برنامه را از مکان 200 آغاز می‌کند و label نام برنامه را مشخص میکند. (در اینجا نام برنامه JOHN است)

2.ا MOVER : محتوای literal یعنی عبارت (="3") را به رجیستر R1 انتقال میدهد.

3.ا MOVEM : محتوای رجیستر را به حافظه عملوند (X) منتقل میکند.

4.ا MOVER : مجداد محتوای literal یعنی عبارت (="2") را با برچسب L1 به رجیستر R2 انتقال میدهد.

5.ا LTORG : آدرس literal ها را اختصاص میدهد. ( در واقع location counter فعلی )

6.ا DS (Data Space) : فضای داده‌ای (data space) 1 را به نماد (symbol) X اختصاص میدهد.

7.ا END : اجرا برنامه را پایان میدهد.

مثال- طرز کار گذر اول

دو جدول با نام‌های literal table و Symbol table را که هرکدام شامل ستونی به نام آدرس هستند را تعریف میکند.

نکته: همانطور که گفته شد  Literal address توسط دستور LTORG مشخص شده اند.

گام اول: START 200

در اینجا هیچ نماد یا literal استفاده نشده است، بنابراین هردو جدول خالی میمانند.


گام دوم: MOVER R1, =’3′ 200

یک literal است، بنابراین جدول literal تشکیل خواهد شد.

گام سوم: MOVEM R1, X 201

نماد X نمادی است که قبل از اعلام آن فراخوانی شده است ، بنابراین در جدول نمادین با فیلد آدرس خالی ذخیره می شود

گام چهارم: L1 MOVER R2, =’2′ 202 X

ا ا ( « L1» یک label است و «=’2’ » literal است) بنابراین آنها را در جداول مربوطه ذخیره میکند.

گام پنجم: LTORG 203

آدرس literal اول را که با مقدار LC ، یعنی 203 مشخص شده است، اختصاص میدهد.

گام ششم: X DS 1 204

این دستورالعمل اعلام میکند که فضای داده 1 به نماد X اختصاص داده شده است.

ولی X نمادی است که قبل در گام سوم فراخوانی شده است و در گام شش تعریف میشود. که به این روش Forward Reference Problem گویند که در ان متغیر در ابتدا فراخوانی میشود و بعدا تعریف میشود. پس در اینجا اسمبلر ادرس مشخص شده در LC را به X اختصاص خواهد داد.

گام هفتم: END 205

در این گام اجرای برنامه به پایان میرسد و literal ها باقی مانده، با مقدار LC تخصیص داده شده به دستور END مقداردهی خواهند شد. در اینجا نمایش کامل شده از جدول‌های literal ها و نمادها (symbol) ساخته شده توسط گذر اول به شکل زیر خواهند بود.

اکنون جداول تولید شده توسط گذر اول به همراه مقدار LC آنها برای پردازش بیشتر pseudo-opcodes و machine op-codes به گذر دوم اسمبلر می‌رود.
اکنون جداول تولید شده توسط گذر اول به همراه مقدار LC آنها برای پردازش بیشتر pseudo-opcodes و machine op-codes به گذر دوم اسمبلر می‌رود.


مثال- طرز کار گذر دوم

در گذر دوم، اسمبلر دستورات اسمبلی را با توجه به جدول های ساخته شده در گذر اول، به کد ماشین تبدیل می‌کند.


انواع اسمبلر:

  • ماکرو اسمبلر: ماکرو اسمبلر شامل قابلیت کلان‌دستور Macro instruction می‌شود؛ بنابراین متن زبان اسمبلی با یک نام قابل ارائه است و از آن نام می‌توان برای درج متن بسط یافته در کدهای دیگر استفاده کرد.
  • اسمبلر متقابل: Cross Assembler یک اسمبلر است که روی کامپیوتر یا سیستم‌عامل (سیستم میزبان) با انواع گوناگون اجرا می‌شود. اسمبلی متقابل توسعه برنامه‌ها را برای سیستم‌هایی تسهیل می‌کند که منابع کافی برای پشتیبانی از توسعه نرم‌افزار ندارند. از جمله این سیستم‌ها می‌توان به Embedded System یا یک Microcontroller اشاره کرد. در چنین موردی، یک شی حاصل شده باید به یک سیستم هدف با حافظه فقط خواندنی (EROM ،ROM و دیگر موارد)، به صورت یک کپی دقیقا بیت به بیت از کد شی یا ارائه مبتنی بر متن از آن کد انتقال پیدا کند.
  • اسمبلر سطح بالا:, High-Level Assembler برنامه‌ای است که انتزاعات زبان را که معمولا مرتبط با زبان‌های برنامه‌نویسی سطح بالا هستند، مانند ساختارهای کننترلی سطح (Do Case ،Else ،Then ،IF و دیگر موارد) و انواع داده انتزاعی سطح بالا شامل ساختارها/رکورها، اتحادها، کلاس‌ها و مجموعه‌ها را فراهم می‌کند.
  • میکرو اسمبلر: Micro assembler برنامه‌ای است که به آماده‌سازی یک ریزبرنامهMicroprogram که به آن «سفت‌افزار» Firmware گفته می‌شود، برای کنترل عملیات سطح پایین از کامپیوتر، کمک می‌کند.
  • متا اسمبلر: برنامه‌ای است که توصیف نحوی Syntactic یا معنایی Semantic از یک زبان اسمبلی را امکان‌پذیر می‌سازد و یک اسمبلر برای آن زبان می‌سازد.
  • اسمبلر درون‌برنامه‌ای: Inline Assembler یا Embedded Assembler یک کد اسمبلر حاوی یک زبان برنامه‌نویسی سطح بالا است. این اسمبلر درون برنامه‌ای اغلب در برنامه‌های سیستمی مورد استفاده قرار می‌گیرد که نیاز به دسترسی مستقیم به سخت‌افزار دارد.


اینتل ۸۰۸۶ و ۸۰۸۸ اولین پردازنده‌هایی بودند که یک مجموعه دستورالعمل داشتند که در حال حاضر معمولاً آن را x86 می‌نامند.

برخی از رایج ترین اسمبلرهای معماری x86 یا 32 بیت عبارتند از:

  • MASM یا Microsoft Macro Assembler
  • NASM یا Netwide Assembler
  • GAS یا Gnu Assembler
  • TASM یا Turbo Assembler

پردازنده‌های x86 دارای مجموعه ای از register ها هستند که برای ذخیره‌سازی داده‌های باینری استفاده می‌شوند. در مجموع، register های داده و register های آدرس، register های عمومی نامیده می‌شوند. هر register ای به جز آنچه که همهٔ register ها می‌توانند انجام دهند، هدف خاصی نیز دارد:

  • ا AX: ضرب/تقسیم، بارگذاری و ذخیرهٔ رشته
  • ا CX: تعداد عملیات روی رشته و تعداد شیفت‌ها
  • ا DX: پورت آدرس برای IN و OUT
  • ا BX: شاخص برای MOVE
  • ا SP: به بالای stack اشاره می‌کند
  • ا BP: به انتهای فریم stack اشاره دارد
  • ا SI: به یک منبع در عملیات جریان اشاره می‌کند
  • ا DI: به یک مقصد در عملیات جریان اشاره می‌کند

برخی از دستورالعمل خاص خود، در برخی از پردازنده‌های اینتل i386SL , i486 و بعد از این‌ها در دسترس است. در حالت طولانی (از AMD Opteron به بعد)، دستورالعمل‌های ۶۴ بیتی و register های بیشتری نیز در دسترس هستند. مجموعهٔ دستورالعمل‌ها در همهٔ حالت‌ها مشابه هم هستند اما آدرس دهی حافظه و اندازهٔ literal ها متفاوتند؛ در نتیجه برنامه‌نویسی در حالت‌های مختلف نیاز به استراتژی‌های برنامه‌نویسی متفاوت دارد.


اسمبلی x86 دارای عملیات‌های ریاضی استاندارد، مانند: add، sub، mul، idiv عملگرهای منطقی مانندand or: xor، neg ؛ عملیات شیفت ریاضی و منطقی: sal / sar، shl / shr ؛ چرخش همراه با رقم نقلی و چرخش بدون رقم نقلی مانند: rcl / rcr rol / ror، مکمل دستورالعمل‌های محاسبهٔ BCD , aaa، aad، daa و سایر دستورالعمل‌ها است.


تهیه کنندگان:

  • سعید واعظی
  • محمود پریز
  • علی غلامی
  • محمد حسین صادقی

بازنگری توسط انجمن علمی:

حمیدرضا بلوری


منابع:

introduction of assembler