کد ماشین

کد ماشین یا زبان ماشین یک مجموعه از دستورالعمل‌هاست که مستقیماً توسط واحد پردازش مرکزی یک کامپیوتر (CPU) اجرا می‌شوند. هر دستورالعمل یک کار خاص را انجام می‌دهد، به عنوان مثال: یک بارگذاری، یک پرش یا یک عملیات ریاضیاتی روی یک واحد از داده‌ها در یک رجیستر CPU یا حافظه. هر برنامه‌ای که توسط CPU اجرا می‌شود، از مجموعه‌ای از دستورالعمل‌ها تشکیل شده‌است.
کد ماشین عددی (منظور کد اسمبلی نیست) ممکن است به عنوان پایین‌ترین سطح نمایش یک برنامهٔ کامپیوتری کامپایل‌شده، (یا سرهم‌شده) یا به عنوان یک زبان برنامه‌نویسی وابسته به سخت‌افزار اولیه در نظر گرفته شود. علی‌رغم اینکه مستقیماً امکان نوشتن برنامه‌ها به صورت کد ماشین عددی وجود دارد، مدیریت‌کردن بیت‌های تک و محاسبهٔ آدرس‌های عددی و ثابت‌ها به صورت دستی، خسته‌کننده و خطاپذیر است. به این دلیل، کد ماشین تقریباً هیچگاه برای نوشتن برنامه‌ها مورد استفاده قرار نمی‌گیرد.
امروزه تقریباً تمام برنامه‌های عملی با زبان‌های سطح بالاتر یا زبان اسمبلی نوشته می‌شوند. سپس کد منبع با ابزاری مانند کامپایلرها، اسمبلرها و ارتباط‌دهنده‎ها با جنبهٔ مهم برنامه‌های ترجمه‌شده، که به کد ماشین ترجمه نشده‌اند، به کد ماشین قابل اجرا ترجمه می‌شود. با این حال، خود ترجمه‌کننده (که ممکن است به عنوان یک اجراکننده یا پردازش‌کننده دیده شود و دستورالعمل‌های کد منبع را انجام می‌دهد)، معمولاً شامل کد ماشین که مستقیماً قابل اجرا باشد (که منشأ گرفته از کد منبع اسمبلی یا زبان سطح بالاست)، است.

دستورالعمل‌های کد ماشین

هر پردازنده یا خانوادهٔ پردازنده، مجموعهٔ دستورالعمل کد ماشین مربوط به خود را دارد. دستورالعمل‌ها، مولد بیت‌هایی می‌باشند که توسط طراحی فیزیکی، متناظر با دستورات مختلفی برای ماشین هستند؛ بنابراین، مجموعهٔ دستورالعمل، مخصوص به دسته‌ای از پردازنده‌هاست که (تقریباً) از معماری مشابهی بهره می‌برند. طراحی‌های جانشین یا پردازندهٔ فرعی اغلب شامل تمام دستورالعمل‌های یک دستور پیشین هستند و ممکن است دستورالعمل‌های اضافی را بیفزایند. گه گاه، یک طراحی جایگزین - که مفهوم و معنی برخی کدهای دستورالعمل را تغییر خواهد داد یا قطع خواهد کرد (چون معمولاً برای اهداف جدید مورد نیاز است) - تا حدی روی سازگاری کد تأثیر می‌گذارد؛ حتی تقریباً پردازنده‌های کاملاً سازگار ممکن است برای برخی دستورالعمل‌ها رفتار کمی متفاوت نشان دهند ولی این به ندرت یک مشکل محسوب می‌شود. همچنین ممکن است سیستم‌ها در جزئیات دیگری مانند چیدمان حافظه، سیستم‌های عامل یا دستگاه‌های جانبی متفاوت باشند. از آن‌جایی که یک برنامه به شکل معمولی به چنین عواملی بستگی دارد، سیستم‌های متفاوت معمولاً یک کد ماشین اجرا نخواهند کرد، حتی وقتی نوع پردازندهٔ یکسانی استفاده شود.
یک مجموعهٔ دستورالعمل کد ماشین ممکن است تمام دستورالعمل‌ها با طول یکسان را داشته باشد یا ممکن است دارای دستوالعمل‌هایی با طول متغیر باشد. شکلی که الگوها سازماندهی می‌شوند، شدیداً با معماری خاص و همچنین اغلب با نوع دستورالعمل تغییر می‌کند. بیشتر دستورالعمل‌ها یک یا بیشتر حوزهٔ آپ‌کد دارند که نوع اصلی دستورالعمل (مثلاً ریاضیاتی، منطقی، پرش و غیره) و عملیات واقعی (مانند جمع یا مقایسه) را مشخص می‌کند و سایر حوزه‌ها ممکن است نوع عملوند(ها)، مد(ها) آدرس‌دهی، آفست(ها) آدرس‌دهی، شاخص یا خود مقدار واقعی را بدهند (چنین عملوندهای ثابت گنجانده‌شده در یک دستورالعمل صریح نامیده می‌شوند).
تمام ماشین‌ها یا دستورالعمل‌های فردی، عملوندهای صریح ندارند. یک ماشین انباشتگر دارای یک عملوند چپ ترکیبیست که منتج به انباشتگر ضمنی برای بیشتر دستورالعمل‌های ریاضیاتی می‌شود. سایر معماری‌ها (مانند ۸۰۸۶ و خانوادهٔ x86) نسخه‌های انباشتگری از دستورالعمل‌های رایج دارند که در آن‌ها، انباشتگر توسط دستورالعمل‌های طولانی‌تر به عنوان یکی از رجیسترهای اصلی در نظر گرفته می‌شود. بیشترعملوندهای یک ماشین استک، روی یک استک، به صورت ضمنی است. دستورالعمل‌های با هدف خاص نیز اغلب دارای کمبود عملوندهای صریح هستند (به عنوان مثال CPUID در معماری x86، مقادیر را در چهار رجیستر مقصد ضمنی می‌نویسد). این تمایز بین عملوندهای ضمنی و صریح، در مولدهای کد ماشین، مخصوصاً در تخصیص رجیستر و بخش‌های ردیابی محدودهٔ زنده، مهم است. یک بهینه‌ساز کد خوب می‌تواند عملوندهای ضمنی را به خوبی عملوندهای صریح - که ممکن است اجازهٔ انتشار پرتکرار، به‌هم‌آمیختن ثابت رجیسترها (یک رجیستر که حاصل یک عبارت ثابت به آن اختصاص یافته‌است با جایگزین‌کردنش با آن ثابت، آزاد می‌شود) و سایر بهبوددهی‌های کد را بدهد - را ردیابی کند.

برنامه‌ها

یک برنامهٔ کامپیوتری یک دنباله از دستورالعمل‌هاست که توسط یک CPU اجرا می‌شوند. علی‌رغم اینکه پردازنده‌های ساده، دستورالعمل‌ها را یکی بعد از دیگری اجرا می‌کنند، پردازنده‌های ابرمقیاس‌پذیر قادر به اجرای چندین دستورالعمل به‌طور همزمان هستند.
جریان برنامه ممکن است تحت تأثیر دستورالعمل‌های «پرش» قرار گیرد که اجرا را به جای اجرای به ترتیب، به یک دستورالعمل دیگر منتقل می‌کنند. پرش‌های شرطی بسته به شرایط در نظر گرفته می‌شوند (اجرا از یک آدرس دیگر ادامه پیدا می‌کند) یا در نظر گرفته نمی‌شوند (اجرا از دستور بعدی ادامه پیدا می‌کند).

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

یک ترجمهٔ قابل خوانش‌تر از زبان ماشین، که زبان اسمبلی نامیده می‌شود، از کدهای حافظه‌ای برای ارجاع به دستورالعمل‌های کد ماشین به جای استفادهٔ مستقیم از مقادیر عددی دستورالعمل‌ها، بهره می‌برد. به عنوان مثال، در پردازندهٔ Zilog Z80، کد ماشین ۰۰۰۰۰۱۰۱ که باعث می‌شود CPU رجیستر پردازندهٔ B را یک واحد کاهش دهد، در زبان اسمبلی به صورت DEC B نشان داده می‌شود.

مثال

مجموعه دستورالعمل MIPS، یک مثال خاص برای یک زبان ماشین که دستورالعمل‌های آن همواره 32 بیتی هستند، فراهم می‌کند. نوع کلی دستورالعمل توسط آپ‌فیلد که 6 بیت با ارزش است، داده می‌شد. دستورالعمل‌های نوع J (پرش) و نوع I (صریح) با op مشخص می‌شوند. دستورالعمل‌های نوع R (رجیستر) شامل یک بخش اضافی funct برای تعیین دقیق عملیات هستند. بخش‌های استفاده‌شده در این انواع به صورت زیر هستند:
 6 5 5 5 5 6 bits
[  op  | rs | rt | rd |shamt| funct] R-type
[  op  | rs | rt | address/immediate] I-type
[  op  | target address] J-type
rs، rt و rd عملوندهای رجیستر را نشان می‌دهند،shamt یک مقدار شیفت را می‌دهد و بخش‌های address یا immediate مستقیماً حاوی یک عملوند هستند.
به عنوان مثال، جمع‌کردن رجیسترهای ۱ و ۲ و قراردادن حاصل در رجیستر ۶ به این صورت کدگذاری می‌شود:
[  op  | rs | rt | rd |shamt| funct]
 0 1 2 6 0 32 decimal
 000000 00001 00010 00110 00000 100000 binary
بارگذاری یک مقدار در رجیستر ۸ که از خانهٔ حافظهٔ ۶۸ خانه بعد از خانهٔ ذکرشده در رجیستر ۳ گرفته شده‌است:
[  op  | rs | rt | address/immediate]
 35 3 8 68 decimal
 100011 00011 01000 00000 00001 000100 binary
پرش به آدرس 1024:
[  op  | target address]
 2 1024 decimal
 000010 00000 00000 00000 10000 000000 binary

رابطه برای میکروکد

در برخی معماری‌های کامپیوتر، کد ماشین توسط یک لایهٔ اصلی‌تر و پایه‌ای تر از برنامه‌ها که زیربرنامه‌ها نامیده می‌شوند، پیاده‌سازی می‌شود و یک رابط زبان ماشین رایج در امتداد یک خط یا خانواده از مدل‌های مختلف از کامپیوتر با جریان داده‌های گستردهٔ متفاوت فراهم می‌کند. این کار انجام شده‌است تا انتقال برنامه‌های زبان ماشین بین مدل‌های مختلف را تسهیل کند. یک مثال از این استفاده، خانوادهٔ System/360 کامپیوترهای IBM و جایگزین‌های آن‌هاست. با وجود مسیر جریان داده به پهنای 8 تا 64 بیت و بالاتر، آن‌ها یک معماری رایج در سطح زبان ماشین را در طول کل خط ارائه می‌کنند.
استفاده از یک لایهٔ میکروکد برای پیاده‌سازی یک کپی‌کننده، کامپیوتر را قادر می‌سازد تا معماری یک کامپیوتر کاملاً متفاوت را ارائه کند. خط System/360 از این استفاده کرده‌است تا امکان انتقال برنامه‌ها از ماشین‌های IBM قبلی به خانوادهٔ جدید کامپیوترها مثلاً یک کپی‌کنندهٔ IBM 1401/1440/1460 روی مدل 40 IBM S/360 را ایجاد کند.

6. رابطه برای بایت‌کد

کد ماشین نباید با «بایت‌کد» (یا عبارت قدیمی‌تر p-code) که توسط یک ترجمه‌کننده اجرا می‌شود یا برای اجرای سریع‌تر، خودش به زبان ماشین کامپایل می‌شود (به شکل مستقیم) اشتباه شود. کد ماشین و کد اسمبلی هنگام ارجاع به بخش‌های وابسته به پلتفرمِ ویژگی‌ها یا کتاب‌خانه‌های زبان، بعضی اوقات کد محلی نامیده می‌شود.

7. ذخیره‌سازی در حافظه

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

۸. قابلیت خوانش توسط انسان‌ها

گفته شده‌است که کد ماشین آن‌قدر غیرقابل خوانش است که دفتر کپی‌رایت ایالات متحده نمی‌تواند بفهمد که یک برنامهٔ کدگذاری‌شدهٔ خاص، کار اصلی نویسندگانش است یا خیر !(به دلیل پیچیدگی بسیار زیاد)؛ با این حال، دفتر کپی‌رایت ایالات متحده، اجازهٔ ثبت کپی‌رایت برنامه‌های کامپیوتری را می‌دهد. داگلاس هاف‌استادتر، کد ماشین را با کد ژنتیک مقایسه می‌کند: «نگاه‌کردن به یک برنامهٔ نوشته‌شده به زبان ماشین به شکل مبهمی قابل مقایسه با نگاه‌کردن اتم به اتم به یک مولکول DNA است».