مسیر x86-64 در Exercism از NASM (The Netwide Assembler) به عنوان اسمبلر خود استفاده میکند.
در NASM، کامنتها با یک نقطهویرگول (;) شروع میشوند.
کامنتها میتوانند هرجای برنامه قرار بگیرند و هر چیزی که بعد از ; بیاید توسط اسمبلر نادیده گرفته میشود.
یک ثابت زمان اسمبل میتواند در NASM با استفاده از equ تعریف شود.
برای مثال، این یک ثابت به اسم UNIVERSE با مقدار 42 تعریف میکند:
UNIVERSE equ 42
ثابتها یک بار، هنگام تعریف، ارزیابی میشوند و بعداً نمیتوانند دوباره تعریف شوند.
در اسمبلی، به جای متغیرها، معمولاً از رجیسترها برای ذخیرهی مقادیر استفاده میکنیم. یک رجیستر پردازنده قطعهای از حافظهی سریع در داخل پردازندهی کامپیوتر است. بیشتر محاسبات در رجیسترهای پردازنده انجام میشوند.
برخی از رجیسترها برای انواع مختلفی از محاسبات استفاده میشوند و رجیسترهای همهمنظوره (GPR) نامیده میشوند. برخی دیگر اهداف خاص یا اختصاصی دارند.
در x86-64، ۱۶ رجیستر همهمنظورهی ۶۴ بیتی وجود دارد که میتوان به آنها به صورت ۳۲ بیتی، ۱۶ بیتی یا ۸ بیتی هم دسترسی داشت.
رجیسترهای همهمنظوره در ادامه توصیف شدهاند، که در آن n در rn از ۸ تا ۱۵ متغیر است: r8، r9، r10، r11، r12، r13، r14 و r15.
| ۶۴ بیتی | ۳۲ بیتی | ۱۶ بیتی | ۸ بیتی |
|---|---|---|---|
| rax | eax | ax | ah/al |
| rbx | ebx | bx | bh/bl |
| rcx | ecx | cx | ch/cl |
| rdx | edx | dx | dh/dl |
| rsi | esi | si | sil |
| rdi | edi | di | dil |
| rbp | ebp | bp | bpl |
| rsp | esp | sp | spl |
| rn | rnd | rnw | rnb |
وقتی از کمتر از ۶۴ بیت استفاده میکنید، بیتهای دسترسییافته معمولاً از بخش پایینی رجیستر هستند.
استثنای این قاعده ah، bh، ch و dh هستند که به ۸ بیت بالایی از بخش ۱۶ بیتی رجیستر دسترسی دارند.
تصویری از نحوهی دسترسی به بیتها برای رجیستر rax:
+--------+---------------------------------------+
| 64-bit | rax |
+--------+-------------------+-------------------+
| 32-bit | | eax |
+--------+-------------------+---------+---------+
| 16-bit | | ax |
+--------+-----------------------------+----+----+
| 8-bit | | ah | al |
+--------+-----------------------------+----+----+
برخی از این رجیسترها باید در طول فراخوانیهای تابع حفظ شوند: rbp، rsp، rbx، r12، r13، r14 و r15.
حفظ نکردن آنها ممکن است به خطا یا رفتار تعریفنشده منجر شود.
بقیه حفظ نمیشوند و میتوان آزادانه از آنها استفاده کرد: rax، rcx، rdx، rdi، rsi، r8، r9، r10 و r11.
بعداً در سرفصلها یاد میگیرید که چگونه رجیسترها را حفظ کنید.
فعلاً فقط از نوع دوم رجیسترها استفاده کنید: rax، rcx، rdx، rdi، rsi، r8، r9، r10 و r11.
دستورها تکههایی از محاسبات هستند که یک پردازنده میتواند انجام دهد. آنها معمولاً شکل زیر را دارند:
name destination, source
بنابراین، اسم دستور اول قرار میگیرد، سپس حداقل یک فاصله، بعد عملوند مقصد، یک ویرگول (,) و در نهایت یک عملوند مبدأ.
عملوند مبدأ معمولاً توسط دستور تغییر نمیکند، فقط عملوند مقصد.
هر دو عملوند باید اندازهی یکسانی داشته باشند مگر اینکه خلاف آن ذکر شود. برای مثال، اگر عملوند مبدأ ۱۶ بیت داشته باشد، عملوند مقصد هم باید ۱۶ بیت داشته باشد.
برای مثال، برای ذخیرهی یک مقدار در یک رجیستر، میتوانیم از دستور mov استفاده کنیم:
mov rax, rdx ; rax = rdx
; mov is the name, rax is the destination operand and rdx is the source operand
قطعهکد بالا محتویات تمام ۶۴ بیت rdx، یعنی عملوند مبدأ، را در تمام ۶۴ بیت rax، که عملوند مقصد این دستور است، کپی میکند.
در بیشتر دستورها میتوان از اعداد صحیح ثابت به عنوان عملوند مبدأ (اما نه به عنوان عملوند مقصد) استفاده کرد:
mov rcx, 42 ; rcx = 42
; rcx is the destination operand and 42 is the source operand
توجه کنید که نوشتن در یک رجیستر ۳۲ بیتی، بیتهای بالایی را هم پاک میکند، بنابراین mov eax, 42 معادل mov rax, 42 است.
این برای رجیسترهای ۱۶ بیتی و ۸ بیتی صادق نیست.
برای عملیات حسابی جمع، تفریق و ضرب، میتوانیم از دستورهای add، sub و imul استفاده کنیم:
add rax, rsi ; rax = rax + rsi
imul rax, rdi ; rax = rax * rdi
sub rax, r8 ; rax = rax - r8
دستورها در توابع سازماندهی میشوند. همهی توابع در section .text فایل منبع قرار میگیرند.
تعریف یک تابع شامل موارد زیر است:
: میآید.ret.این مسیر از قرارداد فراخوانی System V AMD64 ABI استفاده میکند و شش آرگومان صحیح اول در رجیسترها به تابع پاس داده میشوند.
آنها به ترتیب زیر پاس داده میشوند: rdi، rsi، rdx، rcx، r8 و r9.
یک مقدار صحیح از تابع در رجیستر rax برگردانده میشود.
برای فراخوانی یک تابع، از دستور call استفاده میکنیم.
یک تابع میتواند هرجای همان فایل منبع فراخوانی شود.
برای اینکه یک تابع برای سایر فایلهای منبع قابل مشاهده باشد، باید از دستور global استفاده کرد.
برای مثال، این یک تابع sum اعلام میکند:
section .text ; functions are placed here
global sum ; sum is visible to other source files
sum:
; first argument is passed in rdi
; second argument is passed in rsi
; return value is placed in rax
mov rax, rdi ; rax is now equal to rdi
add rax, rsi ; rax = rax + rsi
ret ; function return
و این تابع sum ما را با آرگومانهای ۳ و ۵ فراخوانی میکند:
mov rdi, 3 ; First argument in rdi
mov rsi, 5 ; Second argument in rsi
call sum
; The rax register now contains the value 8 (3 + 5), after sum returns
در این تمرین قرار است کدی بنویسید که به شما کمک کند از کتاب آشپزی محبوبتان یک لازانیای عالی بپزید.
چهار وظیفه دارید که همه به زمان صرفشده برای پخت لازانیا مربوط میشوند.
اینها دستورهایی هستند که در این مفهوم به آنها اشاره شده است:
| Instruction | Description |
|---|---|
| mov a, b | محتویات b را در a کپی میکند |
| add a, b | a = a + b |
| sub a, b | a = a - b |
| imul a, b | a = a * b |
| call a | تابع a را فراخوانی میکند |
| ret | از تابع برمیگردد |
تابع expected_minutes_in_oven را تعریف کنید که هیچ پارامتری نمیگیرد و برمیگرداند که لازانیا باید چند دقیقه در فر بماند.
طبق کتاب آشپزی، زمان مورد انتظار در فر ۴۰ دقیقه است:
expected_minutes_in_oven();
// => 40
تابع remaining_minutes_in_oven را تعریف کنید که تعداد دقیقههای واقعی ماندن لازانیا در فر را به عنوان پارامتر میگیرد و برمیگرداند که لازانیا بر اساس زمان مورد انتظار در فر از وظیفهی قبلی، چند دقیقهی دیگر باید در فر بماند.
remaining_minutes_in_oven(30);
// => 10
تابع preparation_time_in_minutes را تعریف کنید که تعداد لایههایی را که به لازانیا اضافه کردهاید به عنوان پارامتر میگیرد و برمیگرداند که برای آماده کردن لازانیا چند دقیقه صرف کردهاید، با این فرض که آماده کردن هر لایه ۲ دقیقه طول میکشد.
preparation_time_in_minutes(2);
// => 4
تابع elapsed_time_in_minutes را تعریف کنید که دو پارامتر میگیرد، به این ترتیب:
تابع باید برگرداند که چند دقیقه صرف پخت لازانیا کردهاید. این مقدار، مجموع زمان آمادهسازی بر حسب دقیقه و زمانی است که لازانیا تا این لحظه در فر گذرانده است.
elapsed_time_in_minutes(3, 20);
// => 26
در Exercism ثبتنام کنید تا x86-64 Assembly را همراه با 22 مفهوم130 تمرین و مربیگری انسانی واقعی یاد بگیرید و در آن استاد شوید، همهی اینها رایگان.