بعضی از دستورالعمل های پایه ای استفاده شده در مجموع دستورالعمل های ARM که برای برنامه نویسی هسته های ARM استفاده میشوند را یاد بگیرید.
این مقاله برای کمک به شما در یادگیری دستورالعمل های پایه ای اسمبلی برای برنامه نویسی هسته ی ARM تهیه شده است.
ما پست قبلی از فایل های ثبتی ARM را انتخاب میکنیم. لطفا قبل از ادامه ی مقاله آن را دوباره مرور کنید، چون در دستورالعمل های زیر به علائم اختصاری ثبت شده مراجعه میکنیم.
این اطلاعات برای برنامه ریزی یک Raspberry Pi که در هسته های 32 بیتی استفاده میشود، در مقاله ی بعدی هم استفاده خواهند شد. در این مقاله تمرکز ما روی دستورالعمل های ARMv7 و رجیستر های 32 بیت خواهد بود.
توجه: ورژن های جدید Raspberry Pi که رسپبیان را اجرا میکنند، از یک پردازنده ی ARMv8 64 بیتی استفاده میکنند ولی دقیقا مثل ورژن های قدیمی آن را به شکل 32 بیت نصب میکنند. احتمالا در مقاله ی بعدی درمورد ARMv8 صحبت خواهیم کرد.
کد ماشین
پردازنده از دستورالعمل ها استفاده میکند. بیایید نگاهی به کد ماشین که دستورالعمل ها نشانش میدهند بیاندازیم. بیشتر دستورالعمل هایی که با آن سروکار داریم به عملیات های دیتا مرتبط هستند، به همین دلیل من دستورالعمل پردازش دیتا را از کتاب راهنمای ARMV7 استخراج کرده ام.
دستورالعمل پردازش دیتا
تصویر بالا نشان دهنده ی 32 بیت موجود در دستورالعمل پردازش دیتا است، هر بیت هدف ویژه و مخصوصی یا به صورت جداگانه و یا به عنوان بخشی از گروه دارد.
وسعت زمینه ی وضعیت 4 بیت است، در نتیجه 15 کد شرطی وجود دارد. کد عملیاتی که 4 بیتی است کنار پرچم صریح قرار گرفته و نشان میدهد که عملگر 2 مقدار صریح و پرچم شرایط-مجموع دارد و ما از آن برای به روز کردن موقعیت رجیستر در حین عملیات استفاده میکنیم. توجه داشته باشید که این کد عملیاتی است که عملی را- مثل جمع، تفریق و یا فقط OR – که پردازنده انجام میدهد، تعیین میکند.
همانطور که در دستورالعمل های زیر میبینید، به تصویر اول مراجعه میکنیم و میبینیم دستورالعمل اسمبلی انکد میشود و به یک جفت تبدیل میشود. برای بدست آوردن اطلاعات بیشتر، در کتاب راهنمای ARM کاوش و جست و جو کنید.
نحوه ی خواندن دستورالعمل های اسمبلی:
یادمان ها و عملگر ها
همه ی دستورالعمل ها با یک یادمان شروع میشوند که نشان دهنده ی یک عمل هستند. یادمان های بعدی عملگرهایی هستند که عملی روی آنها انجام خواهد گرفت. همانطور که در زیر میبینید اینها معمولا عملگر های منبع و مقصد هستند.
یادمان DESR، SRC2، SRC2
دستورالعمل ADD ( که در بخش زیر توضیح داده شده است)، R2 را به R1 اضافه میکند و نتیجه ی بدست آمده را در R0 میگذارد (برای توضیح این علائم اختصاری مقاله ی قبلی را بخوانید). این یک روش نرمال برای خواندن یک دستورالعمل اسمبلی است. R2 را به R1 اضافه کنید و نتیجه اش را در R0 قرار دهید. کد ماشین برابر که بر روی پردازنده اجرا میشود درکنار دستورالعمل ADD نشان داده شده است.
زبان اسمبلی
بخش Cond برای اجرای همیشگی دارای “1110” است. این بیت ها هنگام استفاده از پسوند های شرطی که به عملیات ADD پیوست شده اند، اجرا میشوند. بخش بعدی استفاده نشده است و صفر شده است. بخش”I” صفر است چون Op2 یک رجیستر است نه مقدار فوری. بخش “S” صفر است چون ما S را به عملیات ADD اضافه نکرده ایم. یعنی برای به روز رسانی پرچم های وضعیت رجیستر این دستورالعمل را نمیخواهیم. (در مورد بخش های N,Z,C و V بعدا توضیح میدهیم.)
اگر دوباره نگاهی به تصویر اول بیندازید، متوجه کد عملیاتی جمع بستن میشوید. این یک 0100b است و به پردازنده میگوید برای عملیات ADD یک مسیر دیتا تنظیم کند. سه بخش آخر، R3(0001b)، R0 (0000b) و R2 (…0010b) هستند.
Cond I OpCd S Rn Rd Op2
ADD R0, R1, R2 @ 1110|00|0|0100|0|0001|0000|000000000010
عملگر های یک دستورالعمل معمولا رجیستر هستند اما میتوانند آدرس حافظه و یا مقدار فوری هم باشند. یک مقدار فوری، عدد دقیقی است که باید استفاده شود. اینها یک پیشوند با نشان # هستند. برای مثال در بالا به جای استفاده از R2 میتوانیم از مقدار فوری 42 استفاده کنیم. این دستور در زیر نشان داده شده است:
Cond I OpCd S Rn Rd Op2
ADDS R4, R6, #42 @ 1110|00|1|0100|1|0110|0100|000000101010
این دستور العمل 42 را به R6 اضافه میکند و نتیجه را در R4 قرار میدهد. این بار “I” بر روی 1 تنظیم شده است چون ما برای عملگر 2 از یک مقدار فوری استفاده میکنیم. کد عملیاتی به همان شکل باقی میماند چون هنوز در حال جمع بستن هستیم. دقت کنید که بخش “S” 1 است. به همین دلیل برای آپدیت پرچم های رجیستر وضعیت ما در حال اجرا، به عملیات ADD احتیاج داریم.
یادمان ها و عملگر ها
دستورالعمل بعدی میتواند از بخش Cond برای بررسی پرچم های وضعیت و اجرای شرطی بر اساس نتیجه استفاده کند. Rn، 0110b ، بیان کننده ی R6 و Rd، 0100b برای R4 است. مقدار فوری Op2، 12 بیت جفتی بیانگر عدد 42 است. ادامه ی این بخش زیرمجموعه ای از اساسی ترین دستورالعمل های ARM را با یک توضیح کوتاه و مثال لیست کرده است.
دستورالعمل پردازش دیتا
دستورالعمل های زیر دیتا را اداره میکنند. میتوانند عمل های حسابی باشند که تابع های ریاضی، عمل های مقایسه ای و یا حرکت های دیتا اجرا میکنند.
جمع (ADD)
جمع یعنی همان ADD، R2 را به R1 اضافه میکند و نتیجه را در R0 میگذارد. جمع با حمل، همراه با پرچم رقم نقلی R2 را با R1 جمع میکند. این دستور هنگام معامله با اعداد بزرگتر از یک کلمه ی واحد 32 بیتی استفاده میشود.
ADD R0, R1, R2
ADC R0, R1, R2
تفریق (SUB)
تفریق (SUB)، R2 را از R1 کم میکند و نتیجه را در R0 میگذارد. تفریق با رقم حمل ( SBC) R2 را از R1 کم میکند و اگر پرچم رقم حمل پاک شود، یک را از نتیجه کسر میکند. این دستور برابر با قرض گرفتن در علم حساب است و درست کار کردن تفریق های چند کلمه ای را تضمین میکند.
SUB R0, R1, R2
SBC R0, R1, R2
مقایسه (CMP) و مقایسه ی منفی(CMN)
مقایسه (CMP) و مقایسه ی منفی (CMN) دو عملگر را مقایسه میکنند. مقایسه R1 را از R0 کسر میکند و مقایسه ی منفی R2 را به 1R اضافه میکند و سپس پرچم های وضعیت بر طبق نتیجه ی جمع و یا کسر آپدیت میشوند.
CMP R0, R1
CMN R1, R2
زبان اسمبلی
حرکت (MOV)
عمل حرکت (MOV) دقیقا همان کاری که به نظر می آید را انجام میدهد. یعنی دیتا را از جایی به جای دیگر منتقل میکند. در دستور زیر، R1 به R0 کپی میشود. در خط دوم مقدار فوری 8 در R0 قرار میگیرد.
MOV R0, R1
MOV R0, #8
حرکت منفی (MNV)
حرکت منفی (MNV) همان کار حرکت (MOV) را انجام میدهد با این تفاوت که اول دیتا را کامل میکند (و یا برعکس). این کار هنگام عملیات با اعداد منفی، مخصوصا با نشان مکمل دو قابل استفاده است. دستورالعمل زیر NOT 8 که بیشتر به عنوان 9- شناخته شده است، در R0 میگذارد. اگر یک را به نتیجه اضافه کنید و این دو مکمل را انجام دهید، 8- بدست می آورید.
MVN R0, #8
و عملکرد بیتی و R2 و R1 را انجام میدهد و نتیجه را درR0 میگذارد. به جای R2 میتوان از مقدار فوری استفاده کرد.
AND R0, R1, R2
ORR و EOR
ORR و EOR عملیات بیتی OR و XOR را به ترتیب R2 و R1 انجام میدهند.
ORR R0, R1, R2
EOR R0, R1, R2
پاک کردن بیت(BIC)
پاک کردن بیت یک عملیات بیتی AND R2 و R1 را انجام میدهد اما اول بیت های R2 را کامل میکند. معمولا این کار با استفاده از مقدار های فوری انجام میشود. همانطور که در خط دوم که مقدار فوری، 0xFF، معکوس شده است و متعاقبا با R1، AND شده است. AND کردن 8 صفر به بیت اول همه ی آن بیت هارا پاک میکند یعنی آنها را برابر با صفر میکند و نتیجه را در R0 قرار میدهد.
BIC R0, R1, R2
BIC R0, R1, #0xFF
شاخه های عناصر زبان اسمبلی
بیت های آزمایشی (TST) و تعادل آزمایشی (TEQ)
کار بیت های آزمایشی و تعادل آزمایشی، آزمایش بیت هایی است که در رجیستر قرار دارند. این دستورالعمل ها از مقصد رجیستر استفاده نمیکنند بلکه، فقط رجیستر را بر اساس نتیجه آپدیت میکنند. بیت های آزمایشی ذاتا یک عملیات بیتی AND دو عملگر را انجام میدهند. ما میتوانیم با استفاده از یک ماسک برای دو عملگر، تنظیم یک بیت تکی را در R0 آزمایش کنیم.
در این صورت بیت 3 را (bitmask = 1000b = 8) بررسی میکنیم و پرچم Z را بر اساس خروجی تنظیم میکنیم. تعادل آزمایشی هم همین کار را انجام میدهد و یا برابر بودن دو رجیستر را بررسی میکند. این کار پرچم N و Z را آپدیت میکند و به همین دلیل روی اعداد علامت دار هم کار میکند؛ اگر علامت هایشان با هم متفاوت باشند N بر روی یکی تنظیم میشود.
TST R0, #8
TEQ R1, R2
عمل ضرب(MUL)
در دستورالعمل عمل ضرب، R1 در R2 ضرب میشود و نتیجه در R0 قرار میگیرد. ضرب قابل استفاده با مقدار فوری نیست.
MUL R0, R1, R2
دستورالعمل انتقال یا چرخش
انتقال چپ منطقی (LSL)
دستورالعمل انتقال چپ منطقی (LSL) بیت ها را توسط یک مقدار انتقالی در R1، انتقال میدهد. در این صورت مقدار فوری 3 و مهم ترین بیت ها آزاد میشوند. آخرین بیتی که به بیرون منتقل شده است، در پرچم رقم حمل قرار میگیرد و کوچکترین بیت های مهم با صفر پر میشوند. در دستورالعمل های زیر، R1 توسط مقدار فوری 3 به چپ منتقل میشود، یا یک مقدار بین 0 و 31 در R2، به R0 تغییر میکنند. یک انتقال چپ منطقی، یک مقدار را در دو ضرب میکند. این کار یک روش کم هزینه برای ضرب ساده است.
LSL R0, R1, #3
LSL R0, R1, R2
انتقال و چرخش در زبان اسمبلی
انتقال راست منطقی(LSR)
دستورالعمل انتقال راست منطقی به روش معکوس انتقال چپ منطقی کار میکند و به شکل موثری یک مقدار را تقسیم بر دو میکند. مهم ترین بیت ها با صفر پر میشوند و بیت کم اهمیت در پرچم رقم حمل گذاشته میشود.
LSR R0, R1, #2
انتقال راست حسابی(ASR)
دستورالعمل انتقال راست حسابی همان کار انتقال راست منطقی را انجام میدهد اما برای اعداد علامت دار طراحی شده است. این عمل بین های علامت دار را در آخرین وضعیت چپ کپی میکند.
ASR R0, R1, #4
چرخش راست(ROR)
چرخش راست تمام بیت های یک کلمه را با چند مقدار، میچرخاند. در این دستور به جای پر کردن بیت های چپ با صفر، بیت های منتقل شده به سادگی به انتهای سمت دیگر برگردانده میشوند.
ROR R0, R1, #5
دستورالعمل های عملیات منشعب
یکی از وظایف مهم یک پردازنده، توانایی انتخاب بین دو مسیر کد مبتنی بر مجموعه ای از ورودی هاست. این دقیقا کاری است که عملیات منشعب انجام میدهند. معمولا یک پردازنده یک دستورالعمل را با افزایش R15، شمارنده ی برنامه(PC)، یکی پس از دیگری توسط 4 بیت اجرا میکند(یعنی طول یک دستورالعمل تک). شعبه بندی کردن PC را به محل دیگری که توسط یک برچسب که آن بخش از کد اسمبلی را نشان میدهد، انتقال میدهد.
شعبه(B)
دستورالعمل شعبه(B) شمارنده ی برنامه را توسط یک برچسب به آدرس تعیین شده ای منتقل میکند. برچسب (loop در مثال پایین) نشان دهنده ی بخشی از کد است که شما میخواهید پردازنده در مرحله ی بعد اجرا کند. برچسب ها فقط یک متن و معمولا یک کلمه ی معنی دار هستند.
B loop
زبان اسمبلی در مجموع دستورالعمل های ARM
پیوند شعبه(BL)
دستورالعمل پیوند شعبه (BL) همان کار را انجام میدهد اما آدرس دستورالعمل بعدی را در R14، یعنی اتصال رجیستر (LR) کپی میکند. این کار عمل هنگام انجام خوانش زیروال/روند بسیار عالی کار میکند چون به محض اینکه بخش کد در برچسب تمام میشود میتوانیم از LR برای برگرداندن به جایی که منشعب کرده ایم استفاده کنیم. ما در پایین به برچسب زیروال منشعب میشویم و سپس برای برگشتن به دستورالعمل بعدی از اتصال رجیستر استفاده میکنیم.
BL زیروال
…
زیروال:
…
MOV PC, LR
ما برای برگرداندن اتصال رجیستر به شمارنده ی برنامه از یک دستورالعمل حرکت (MOV) استفاده میکنیم. این کار درست بعد از خوانش زیروال ما، برنامه را به محل برمیگرداند. به استفاده ی LR و PC در بالا توجه کنید. اسمبل های ARM آنها را به ترتیب به عنوان R14 و R15 میشناسند. این کار یک تذکر آسان و راحت در مورد عملی که انجام میشود به برنامه نویس میدهد.
دستورالعمل های بارگیری و ذخیره سازی
اطلاعات ذخیره سازی حافظه ی کامپیوتر چیزی است که پردازنده به آن نیاز دارد. این دیتا با استفاده از یک آدرس بدست می آید. در ابتدا میتوانیم با دادن یه آدرس به یک رجیستر، از طریق همان آدرس به دیتا دسترسی پیدا کنیم. به همین خاطر است که از عملیات های بارگیری و ذخیره سازی استفاده میکنیم.
دستورالعمل های پردازش دیتا
رجیستر بارگیری(LDR)
دستورالعمل رجیستر بارگیری، دیتای مستقر در یک آدرس را درون رجیستر مقصد بارگذاری میکند. پرانتز های اطراف R1 حاکی از این است که رجیستر یک آدرس دارد. ما با استفاده از پرانتز ها دیتا را به جای خود آن آدرس در آدرس R0 قرار میدهیم. همچنین میتوانیم از این علامت گذاری برای قرار دادن دیتای افست از یک آدرس معین استفاده کنیم، همانطور که در دستورالعمل خط دوم نشان داده شده است. R0 شامل دیتای دو کلمه ای دور از هر چیزی است که دارای آدرس R0 است.
LDR R0, [R1]
LDR R0, [R1, #8]
همچنین میتوانیم برای نشان دادن یک آدرس از برچسب ها استفاده کنیم و دیتای مرتبط هم میتوانند در یک رجیستر بارگذاری شوند.
اولین خط زیر آدرس برچسب “info” را در R0 بارگذاری میکنند. مقدار ذخیره شده در آدرس هم قابل دستیابی است و در خط دوم در R1 گذاشته شده است.
LDR R0, =info
LDR R1, [R0]
ذخیره سازی (STR)
ذخیره سازی یک عملیات مکمل را برای بارگیری انجام میدهد. ذخیره سازی، محتویات رجیستر را در محل حافظه قرار میدهد. کد زیر دیتای درون R1 را در آدرس R0 ذخیره میکند. پرانتز ها باز هم نشان دهنده ی این هستند که R0 یک آدرس دارد و ما میخواهیم دیتای آن آدرس را تغییر دهیم.
STR R1, [R0]
دستورالعمل ها و توضیحاتشان
انواع بارگذاری و ذخیره سازی: بیت (B)، هف ورد (H)، کلمه (حذف شده)، علامت گذاری شده (SB)، علامت گذاری نشده (B)
هم بارگذاری و هم ذخیره سازی با یک نوع پیوست به آنها قابل نوشتن است. این نوع حاکی از این است که آیا دستورالعمل یک بیت، یک هف وورد و یا یک کلمه را دستکاری میکند و اینکه آیا دیتا علامت گذاری شده است یا خیر.
این ممکن است که برای دستکاری استرینگ (رشته) به کار بیاید، چون رقم های ASCII طول یک بایت را دارند. همانطور که در دستورالعمل خط آخر مشاهده میشود، این عملیات اجازه ی استفاده از آفست ها را هنگام بارگذاری و یا ذخیره سازی را هم میدهند.
LDR R0, = متن @بارگذاری یک آدرس 32 بیتی در R0
STRB R1, [R0] @ ذخیره سازی بیت در آدرسی در حافظه
STRB R1, [R0, + R2] @ ذخیره سازی بیت در آدری+ آفست R2
دستورالعمل های شرطی
همانطور که قبل تر گفته شده است، ممکن است یادمان های استفاده شده در یک دستورالعمل، کد های شرطی انتخابی پیوست شده به آنها را داشته باشند. این کار اجازه ی اجرای شرطی را میدهد.
یادتان باشد پرچم ها (همانطور که در مقاله ی قبلی دیدید) Z (صفر) ، C(رقم حمل)، N( منفی) و V(سرریز) هستند. برای مجبور کردن دستورالعمل ها برای آپدیت کردن رجیستر وضعیت، S انتخابی میتواند به بیشتر یادمان هایی که تاکنون به آنها اشاره شده اضافه شوند. وقتی رجیستر وضعیت آپدیت شد، تعدادی از پسوند های شرطی که در تصویر زیر نشان داده شده اند، میتوانند برای اجرای دستورالعمل ها استفاده شوند. کدهای دوتایی این پسوند ها مطابق با 4 بیت اول دستورالعمل پردازش دیتا در اولین عکس مقاله نشان داده شده اند.
پسوند های شرطی
این پسوند ها در زمان نوشتن اسمبلی به یادمان اضافه میشوند.
در لیست زیر تعدادی از پسوند های شرطی استفاده شده با دستورالعمل های که در طول مقاله به آنها اشاره شد، گردآوری شده است.
به دلیل اینکه در مقاله ی بعدی با اسمبل کننده GNU سروکار خواهیم داشت، باید برای نشان دادن یک نظر از علامت @ استفاده کنیم.
.شروع_جهانی ( .global _start )
شروع:
MOV R0, #3 @مقدار 3 را در R0میگذارد
MOV R1, #0 @مقدار 0 را در R1 میگذارد
Loop:
CMP R0, R1 @R1 را با R0 مقایسه میکند (در اصل R0 را منهای R1 میکند)
BEQ انجام شد @ اگر برابر هستند شعبه با برچسب باشند، تمام شده است. (Z=1)
ADDGT R1, #1 @ اگر R0 بزرگتر از R1 است، 1 را به R1 اضافه کنید.
SUBLT R1, #1 @ اگر R0 کوچکتر از R1 است، 1 را از R1 کسر کنید.
B loop @شعبه را به عقب ببرید و loop را دوباره نصب کنید.
انجام شد:
کار های دیگر را انجام دهید.
آموزش تعمیرات برد الکترونیکی یکی از دوره های پر متقاضی درمجتمع آموزشی فنی سازان می باشد.در تعمیرات برد الکترونیکی با توجه به پیچیدگی و تنوع زیاد این نوع بردها نیاز مبرم به تخصص می باشد. لذا از این رو مجتمع آموزشی فنی سازان با استفاده از اساتید مجرب و تجهیزات فوق مدرن تلاش نموده تا تمامی علاقه مند به این رشته به صورت تخصصی تمامی مراحل آموزشی را طی نمایند تا کارآموزان در بازار کار از لحاظ تئوری و عملی کارآمد باشند.