PDA

توجه ! این یک نسخه آرشیو شده میباشد و در این حالت شما عکسی را مشاهده نمیکنید برای مشاهده کامل متن و عکسها بر روی لینک مقابل کلیک کنید : اسمبلی یاد بگیریم



آبجی
14th February 2010, 11:39 PM
برای یاد گرفتن اسمبلی باید با مبناهای عدد نویسی ، ساختمان داخلی کامپیوتر
و برنامه نویسی آشنا باشیم .
ما برنامه هایمان را مستقیما با اسمبلر Macro Assembler خواهیم نوشت و گاها از Debug
استفاده خواهیم کرد . بعلاوه چون برنامه های حجیم نخواهیم نوشت قالب اکثر
رنامه های ما COM. خواهد بود .
برای شروع ابتدا نگاهی به حافظه میکنیم :
حافظه و آدرس دهی
هر کامپیوتر مبتنی بر ۸۰۸۶ دارای حداقل ۶۴۰ کیلوبایت حافظه است . این ۶۴۰
کیلوبایت به قطعات ۶۴ کیلوبایتی تقسیم شده و ما این قطعات را “قطعه ” یا Segment
مینامیم . هر سگمنت هم به خانه های تک بایتی دیگری تقسیم شده است .
برای بدست آوردن مقدار یک بایت مشخص از حافظه ما باید عد مربوط به سگمنت و
همچنین شماره آن بایت در سگمنت ( که آفست Offset نامیده میشود ) را بدانیم .
مثلا اگر مقدار مورد نظر در قطعه ۰۰۳۰h(h( یعنی عدد در مبنای ۱۶ است ) و آفست ۱۳C4h
باشد ما باید قطعه ای که شماره آن ۰۰۳۰h است را بیابیم و بعد در همان قطعه
مقدار باین شماره ۱۳C4 را بخوانیم .
برای نمایش این حالت بین عدد سگمنت و آفست علامت (:) قرار میدهیم . یعنی
ابتدا عدد مربوط به قطعه را نوشته و سپس عدد آفست را می آوریم :
Segment:Offset
مثال : ۴D2F:َ۹۰۰۰ **
همیشه در آدرس دهی ها از اعداد مبنای ۱۶ استفاده میکنیم .
| | |
| CConvertional | 1 Segment=64K | | | | | Memory
| | | | | |
| | | |
| | | |
ثباتها Registers
رجیسترها مکان هائی از CPU هستند که برای نگهداری داده ها (DATA) و کنترل اجرای
برنامه بکار میروند . ما میتوانیم آنها را مقدار دهی کرده و یا بخوانیم و یا
باتغییر محتوای آنها CPU را مجبور به انجام یک پروسه (رویه یا Procedure) کنیم
دسته ای از رجیسترها که ما انها را “ثباتهای همه کاره یا همه منظوره ” میخوانیم
و شامل AX/BX/CX/DX هستند ، برای انتقال مقادیر بین رجیستر ها و CPU بکار میروند.
این ثباتها را میتوانیم به هر نحوی تغییر دهیم و مقادیری را به آنهاارسال کنیم .
ثباتهای دیگری هم که نام میبریم کاربردهای خاص خودشان را دارند و برای مقدار دهی
آنها باید قواعد خاصی (که توضیح خواهیم داد) را بکار بریم .
میکند عدد که در این ثبات وجود دارد شماره یک قطعه است و CPU برای یافتن DS : مخفف Data Segment . محل نگهداری متغییرها و ثابتهای برنامه را مشخص
مقادیر لازم به آن قطعه مراجعه میکند . CS
: مخفف Code Segment است و آدرس قطعه ای که برنامه در آن قرار گرفته را
نشان میدهد . ES
: این یک ثبات کمکی است و معمولا در آدرس دهی ها شماره قطعه را نگهداری
میکند . DI
DataIndex:Dبا DS/ESا مرتبط است و عدد آفست را نگهداری میکند . IP
: این رجیستر معلوم میکند که برنامه در حال اجرائی که در CS قرار دارد از
کدام بایت قطقه (یعنی کدام آفست ) شروع میشود . به همین دلیل همیشه این دو
ثبات را با هم و بصورت CS:IP نشان میدهند.
و …
تمام رجیسترهای فوق ۱۶ بیتی (دوبایتی ) هستند و اعداد دوبایتی را نگهداری میکنند.
ثباتهای همه منظوره به دو نیم ثبات تک بایتی تقسیم میشوند . بایت بالائی ب
نماد H و بایت پائینی با نماد L نشان داده میشود . مثلا ثبات AX دارای دو نیم -
ثبات AH/AL است :
| AH - 8 Bit | AL -8 Bit |
تمرین :
برای دیدن رجیسترها در DOS، DEBUG، را اجرا کنید و فرمان R را صادر کنید :

D:\MASM>DEBUG
-R
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=17AA ES=17AA SS=17AA CS=17AA IP=0100 NV UP EI PL NZ NA PO NC
17AA:0100 0F
VN:F [1.6.7_924]

آبجی
14th February 2010, 11:40 PM
خوب ، رجیسترها را دیدیم و آشنائی کلی با آنها پیدا کردیم .
حالا میخواهیم به رجیتسرها مقدار بدهیم و آنها را بخوانیم و … . ما معمولا در
ےزبانهای دیگر از علامت =(یا =ا:) برای مقدار دهی استفاده میکنیم ولی در زبان
ےاسمبلی این کار ممکن نیست . در عوض از دستورالعمل MOV کمک میگیریم . با MOV
میتوانیم داده ها را بین رجیسترها یا خانه های حافظه انتقال بدهیم . به این صورت
MOV in_it/Value
در اینجا In_it به معنای یک رجیستر، نام یک متغیر، یا آدرس یک مکان از حافظه
است و Value هم یک مقدار عددی یا حرفی ، نام یک رجیستر و … میباشد .
ےمانند MOV AX/200 که عدد ۲۰۰ دسیمال را به رجیستر AX منتقل میکند . (همیشه از
سمت راست به چپ ) .
در زبان اسمبلی ما میتوانیم با مبناهای ۲وَ۱۰وَ۱۶ کار کنیم . اعداد به طور پیش
فرض مبنای ۱۰ هستند . برای نشان دادن عدد هگزا (مبنای ۱۶) در انتهای عدد یک
حرف H ( یا h ) و در انتهای اعداد باینری علامت (b) قرار میدهیم . مثلا برای نشان
دادن عدد AC1 مبنای ۱۶ باید حتما آن را بصورت AC1h بنویسیم . به همین ترتیب عدد۱۱۰b
همان عدد ۶ خودمان است .
با این تفاسیر برای دادن مقدار ۴Ch به رجیستر AX از دستور زیر استفاده میکنیم :
mov ax/4Ch
به همین شکل میتوانیم به نیم ثباتها هم مقدار بدهیم . مثلا میتوانیم برای مقدار
دهی AH بنویسیم : mov ah/20h . در این حالت مقدار نیم ثبات AL ثابت بوده و
محتوای AH برابر ۲۰h میشود . چون نیم ثباتها تک بایتی هستند ما نمیتوانیم عدد
خارج از محدوده ۰ تا ۲۵۵ یا ۱۲۸- تا ۱۲۷ به آنها ارسال کنیم . در مورد اعداد منفی
هم باید از طریق تبدیل به مکمل دو عمل کنیم که به زودی آن روش را توضیح خواهیم
اد .
مثلا ما نمیتوانیم mov ah/100h را انجام دهیم چون ۱۰۰h برابر ۲۵۶ بوده و از محدوده
تعریف شده خارج است .
به همین شکل میتوانیم محتوای ثباتها را هم منتقل کنیم . مثلا برای کپی کردن محتوای
ثبات CXبه DX میتوانیم بنویسیم : mov dx/cx ، یعنی مقدار داخل Cx را به Dx کپی
کن .
ےباز هم باید به یک یا دوبایتی بودن ثباتها توجه کنیم . به عبارت دیگر ما
ےنمیتوانیم مقدار یک ثبات تک بایتی را به یک ثبات کامل دوبایتی منتقل کنیم .
مثلا عبارت mov DX/AL قابل قبول نیست چون AL یک بایتی بوده و DX دوبایتی است .
به عبارت ساده و کامل تر دو طرف عملوند MOV باید از نظر اندازه برابر باشند.
بنابر این :
MOV DL/AL
و MOV CL/BHوM درست ولی MOV DS/AH نادرست است .
به علاوه ما فقط میتوانیم ثباتهای همه منظوره AXتا DX را به این صورت مقدار دهی
ےکنیم . در صورتی که بخواهیم ثباتهائی مثل ..DS/ES/ را مقدار دهی کنیم باید از
رجیستر AX به عنوان واسطه استفاده کرده و مقدار را از طریق آن انتقال دهیم .
مثلا:
نمیتوانیم بنویسیم mov ds/20h
ولی میتوانیم داشته باشیم :
mov ax/20h
mov ds/ax
ےبه این ترتیب مقدار ۲۰hبه DS انتقال پیدا میکند ( گرچه تغییر دادن DS ایده خوبی
نیست !)
ےحالا مطالب گفته شده را تمرین میکنیم . ما میتوانیم با DEBUG اسمبلی بنویسیم و
حتی برنامه های COM. درست کنیم . بنا براین در DOS، DEBUG، را اجرا کنید .
D:\LNG\ASM> DEBUG
ےیک خط تیره به صورت - ظاهر میشود . این خط تیره اعلان DEUBG برای وارد کردن
دستورات است .
حرف A (به معنی شروع وارد کردن دستورات اسمبلی ) را وارد کرده و Enter را بزنید .
ےعددی بصورت xxxx:0100 ظاهر میشود . این عدد برای شما (فعلا) مهم نیست ، پس به
آن توجه نکنید .
حالا میتوانید دستورات زیر را وارد کنید :

MOV AX/100
MOV BX/AX
MOV ES/AX

بعد از وارد کردن خط آخر یکبار دیگر کلید Enter را بزنید تا اعلان (-) دوباره ظاهر
شود .
در سطر اول ما عدد ۱۰۰h ( پیش فرض اعداد در Debug هگزا است ) را به AX منتقل
کردیم . بعد مقدار AXبه BX و سپس مقدار AXبه ES منتقل شده . به این ترتیب همه
ثباتهای AX/BX/ES باید در نهایت برابر ۱۰۰h باشند .
برای دیدن صحت این مطلب دستور T ( به معنای Trace) را وارد کنید .
با هر بار اجرای این دستور یکی از سطرهای برنامه اجرا میشود . بعلاوه شما میتوانید
محتوای رجیسترها را هم ببینید .
با اولین فرمان T ، سطر اول اجرا میشود . بازهم فرمان T را وارد کنید . الان مقدار۱۰۰h
به BX داده شد و اگر به محتوای رجیستر AX توجه کنید خواهید دید که مقدار آن
(همانطور که انتظار داشتیم ) برابر ۱۰۰h است . دوبار دیگر هم فرمان T را صادر
کنید و در نهایت مقدار ثباتهای AX/BX/ES را ببینید . هر سه ثبات (حالا) برابر ۱۰۰h
هستند .
برای خارج شدن از Debug هم فرمان Q به معنی Quit را وارد کنید .

آبجی
14th February 2010, 11:40 PM
با همین دستور میتوانیم مقادیر را از محلهای حافظه خوانده یا در آنجا بنویسیم .
برای کار با حافظه دوحالت ممکن است وجود داشته باشد : ۱
- آدرس مورد نظر در سگمنت جاری باشد . در برنامه های COM. کل برنامه (غالبا)
از یک سگمنت تشکیل میشود . ۲
- آدرس مورد نظر خارج از سگمنت جاری باشد . ثبات DS همیشه به قطعه ای اشاره میکند که داده های مورد نیاز برنامه در آن
هستند . این قطعه در برنامه های EXE. یک قطعه مستقل است ولی در برنامه های COM
. ، قطعه داده های و قطعه کد برنامه در یک سگمنت هستند . بنا براین مقدار
ثبات DS در یک برنامه COM. ثابت است .
در حالت کلی آدرس یک محل از حافظه بصورت DS:address مشخص میشود. DS حاوی
آدرس سگمنت داده ها بوده و address آفست را مشخص میکند .
چون همانطور که گفتیم DS در برنامه های COM. ثابت است ، پس در صورتی که آدرس
مورد نظر در همین قطعه باشد از نوشتن DS صرفنظر میکنیم .
به عنوان مثال اگر قطعه داده های برنامه ما ۹۰۰۰h باشد و ما بخواهیم آفست ۲۴h
ام در همین قطعه را بدست بیاوریم ، میتوانیم از یکی از دو شکل زیر استفاده
کنیم :
DS:24h
or
24h
البته چون اسمبلر منظور ما از نوشتن عدد ۲۴h را نخواهد فهمید شکل دوم یک خطای
هنگام ترجمه تولید خواهد کرد ولی ما روش صحیح را هم خواهیم گفت .
ما آدرس ها (یا اشاره گرها) را برای این میخواهیم که بتوانیم به یک خانه از
حافظه دسترسی پیدا کنیم . برای اینکه نشان بدهیم منظور ما از عدد مشخص شده ،
آدرس است نه خود عدد (مثل ۲۴h در مثال قبلی ) آن عدد را داخل [] قرار میدهیم .
بنا براین :
mov ah/24h عدد ۲۴h را به AX منتقل میکند ولی ….
mov ah/[24h] محتوای آفست ۲۴h را به AX منتقل میکند .
در شکل دوم هر مقداری که در آفست ۲۴h ام سگمنت جاری موجود باشد به ثبات Ah
منتقل میگردد.
به همین صورت میتوانیم یک مقدار را به یک خانه از حافظه منتقل کنیم : mov [24h]/ah
: محتوای ثبات AH را به آفست ۲۴h ام منتقل میکند .
ے اگر آدرس مورد نظر خارج از محدوده سگمنت جاری بوده و در قطعه ای جدا قرار داشته
باشد ، میتوانیم از DSیا ESا (ترجیحا) برای دستیابی به حافظه استفاده کرد:
مثال : mov ax/9000h
mov ds/ax
mov ah/ds:[89h]
به این ترتیب ما به آفست ۸۹h از سگمنت ۹۰۰۰h دسترسی پیدا میکنیم .
البته دستورات فوق مارا به مقصودمان میرسانند ولی ما نمیتوانیم به دلخواه خودمان DS
را تغییر دهیم چون همانطور که گفتیم DS به قطعه داده های برنامه اشاره میکند و
برنامه ، داده ها و مقادیر متغیر ها را از سگمنتی که با DS مشخص شده میخواند .
بنا براین ما نباید مقدار DS را تغییر بدهیم مگر اینکه آن را دوباره به حالت اول
برگردانیم . برای ذخیره و بازیابی محتوای رجیسترها، یک روش ساده و عمومی وجود
دارد که به زودی خواهیم گفت ولی در این مثال ما میتوانستیم مقدار قبلی DS را در
یک رجیستر دیگر مثل CX نگهداریم :
انتقال محتوای dsبه AX mov ax/ds
انتقال محتوای AXبه CX mov cx/ax
دادن مقدار۹۰۰۰hبه AX mov ax/9000h
انتقال محتوای AXبه DS mov ds/ax
خواندن آدرس mov ah/ds:[89h]
بازیابی مقدار DS mov ax/cx mov ds/ax
اگر بخواهیم آفست آدرس را با یک رجیستر مشخص کنیم باید به نکات زیر توجه
کنیم : ۱
- اگر آدرس سگمنت با DS مشخص شده ، یا آدرس در سگمنت جاری باشد ، باید
مقدار آفست را در ثبات BX قرار دهیم . مثلا mov cx/[BX]یا mov cx/ds:[bx]ا .
۲
- اگر از ES به عنوان مقدار سگمنت استفاده میشود باید از DI به عنوان آفست
استفاده کنیم مثل mov cx/es:[di] .
چون ما با برنامه های COM. سرو کار داریم ، پس از شکل اول و BX استفاده خواهیم
کرد .

آبجی
14th February 2010, 11:41 PM
وقتی که ما به روش گفته شده مقداری را از حافظه میخوانیم ، یک داده تک بایتی
از حافظه گرفته میشود . اما ممکن است بخواهیم که یک کلمه یا کلمه
مضاعف ( ۴بایتی ) را بخوانیم یا بنویسیم . در این صورت میتوانیم از
پیشوند های زیر استفاده کنیم :
Byte Ptr
: برای دست یابی به یک بایت Word Ptr
: برای دستیابی به یک کلمه (۲بایت ) Dword Ptr
: برای دست یابی به یک مقدار ۴ بایتی این پیشوند ها را باید قبل از آدرس مورد نظر قرار دهیم . به عنوان مثال برای
خواندن یک بایت از آفست ۱۰h میتوانیم بنویسیم : mov al/byte ptr ds:[10h]
و برای خواندن دو بایت بصورت : mov ax/byte ptr ds:[10h] .
میتوانیم از همین روش استفاده کرده و مقداری را به حافظه انتقال دهیم . مثلا
میخواهیم یک کلمه دوبایتی را به آفست ۳۴h (در سگمنت برنامه ) منتقل کنیم . کافی
است بنویسیم :

mov word ptr [34h]/1FCAh .
مثال :
mov bx/34h
mov ax/ds
mov cx/ax
mov ax/00h
mov ds/ax
mov ax/word ptr ds:[bx]
mov ax/cx
mov ds/ax

جمع و تفریق
بحث ما در مورد روشهای دستیابی و انتقال داده ها (فعلا) به پایان میرسد . حالا
میخواهیم ببینیم که چطور عمل جمع و تفریق ، و بعدا ضرب و … ، را روی مقادیر
انجام دهیم .
دستورالعمل ADD به میزان خواسته شده به محتوای یک رجیستر یا متغیر اضافه میکند .
ےمثلا ADD AH/20 عدد ۲۰ را به AH اضافه کرده و مجددا در AH قرار میدهد . اگر مقدار
فعلی AH برابر ۳۰ باشد بعد از اجرای آن دستور برابر ۵۰ میشود .
باید توجه کنیم که حاصل بدست آمده از محدوده مجاز تجاوز نکند . در این مثال اگر
حاصل جمع عدد ۲۰ با محتوای AH بزرگتر از ۲۵۵ باشد ، خطای سرریز (Over Flow) رخ
میدهد .
مثال : این دستورات را در دیباگ وارد کنید : mov ax/5
add ax/4
int 20
(به معنی سطر آخر توجه نکنید) . حالا یکبار دیگر Enter را بزنید تا خط اعلان Debug
ظاهر شود . حرف G را بزنید تا برنامه شما اجرا شود . حالا فرمان آشنای R را برای
دیدن محتوای رجیسترها وارد کنید و مقدار AX را ببینید .
دستورالعمل SUB برعکس ADD بوده و به مقدار خواسته شده از محتوای یک ثبات یا
متغیر کم میکند . مثلا SUB AX/100h به اندازه ۲۵۶ (۱۰۰h) از AX کم کرده و نتیجه را
دوباره در AX قرار میدهد .
مثال : mov bbx/100h SUB bx/50
در این مثال حاصل bx را از ۱۰۰ به ۵۰ کاهش داده ایم .
فرمان INC یک حالت خاص از ADD بوده و تنها یکواحد به محتوای ثبات اضافه میکند
مثلا inc cx یعنی یک واحد به cx اضافه کن .
و برعکس این ، دستور dec یکواحد از محتوای ثبات کم میکند . مانند : dec cx .
ے باید توجه کنیم که این دستورات تنها روی ثباتهای همه منظوره DX.AX.D قابل
استفاده هستند .
پس امروز مطالب مربوط به اینها رو یاد گرفتیم :
byte ptr / word ptr / dword ptr
add / sub / inc / dec
VN:F [1.6.7_924]

آبجی
14th February 2010, 11:41 PM
وقفه ها (Interrupts) CPU
برای اینکه بتواند کارهای مختلفی را انجام دهد،از وقفه ها استفاده میکند . یک
ےوقفه درخواستی از CPU است که در طی آن زیر برنامه ای اجرا میشود. وقتی که وقفه
فراخوانی میشود، CPU اعمال دیگر را متوقف کرده و آن اینتراپت را پردازش میکند
به طور کلی وقفه ها به دودسته تقسیم میشوند:
ےَ۱- وقفه های سخت افزاری (Hardware Interrupts) . وقفه هائی هستند که از سوی
ے ادوات سخت افزاری کامپیوتر مانند کیبورد و … اجرا میشوند. مثلا با فشرده یارها
شدن هر کلید ، یکبار وقفه شماره ۹ فراخوانی میشود. ۲
- وقفه های سخت افزاری (SoftWare Interrupts). این وقفه ها در بایوس (BIOS)
کامپیوتر قرار دارند. بایوس کامپیوتر یک تراشه (IC) قابل برنامه ریزی است که
بنا بر نوع پردازنده بر روی برد اصلی کامپیوتر قرار میگیرد . بعلاوه خود DOS
نیز وقفه ای (وقفه ۲۱h) را اداره میکند که به وقفه DOS معروف است . این توابع
توسط MSDOS.SYS تعریف میشوند ولی در نهایت به بایوس مراجعه میکنند.
هر وقفه دارای یک شماره خاص خود است و از صفر شروع میشود . وقفه ۲۱h (سرویس DOS
) نیز دارای ۲۵۵ سرویس دیگر است .
برای اینکه بتوانیم یک برنامه خوب و مفید بنویسیم باید بتوانیم از اینتراپتها
به نحو صحیح استفاده کنیم . پس هر برنامه نویس اسمبلی باید یک مرجع کامل
اینتراپت در اختیار داشته باشد.
وقتی میخواهیم یک وقفه را فراخوانی کنیم ، ابتدا (درصورت لزوم ) ثباتهای خاصی را
مقدار دهی میکنیم . معمولا نیم ثبات AH ، از این جهت که اکثر اینتراپتها
دارای چند سرویس مختلف هستند ، شماره تابع را مشخص میکند . بهمین صورت ، و
اگر لازم باشد ، ثباتهای دیگر را هم مقدار دهی میکنیم . مثلا فرض کنید میخواهیم
کلیدی را از صفحه کلید بخوانیم . تابع شماره ۰ از وقفه ۱۶h میتواند این کار را
انجام دهد . وقتی میگوئیم تابع شماره ۰ ، یعنی باید به AH مقدار ۰ بدهیم و بعد
اینتراپت ۱۶h را فراخوانی کنیم .
فراخوانی اینتراپت به سادگی و با دستورالعمل INT انجام میشود. به صورت :
INT int_no که int_no شماره اینتراپت میباشد . در مورد این مثال باید دستورات زیر را انجام
دهیم : mov ah/0
int 16h
وقتی یک وقفه فراخوانی میشود ، ممکن است روی ثباتها تاثیر گذاشته و مقدار آنها
را عوض کند. به این وسیله ما میتوانیم وضعیت اجرای وقفه را بدست بیاوریم . در
مورد این مثال ، پس از خوانده شدن کلید ، کد اسکی (ASCII) کلید در ثبات AL قرار
میگیرد . مثلا اگر حرف A تایپ شود ، مقدار AL برابر ۶۵ خواهد بود.
حالا اگر عدد AH را قبل از فراخوانی وقفه بجای ۱ برابر Eh قرار دهیم و وقفه ۱۰hرا
اجرا کنیم ، بجای خواندن کلید، یک کاراکتر را چاپ میکند . به این صورت که کد
اسکی کاراکتر در ثبات AL و عدد Eh در ثبات AH قرار گرفته و وقفه ۱۰h فراخوانی
میشود . mov AX/0E07h
in 10h
به سطر اول توجه کنید !. وقتی ما یک عدد دوبایتی (Hex) را به AX ارسال میکنیم ،
دوبایت بالا در AH و دوبایت پائین در AL قرار میگیرد . پس در این مثال کاراکتر
شماره ۷ باید چاپ شود و چون این کد مربوط به کاراکتر Bell است ، صدای بیپ
شنیده خواهد شد.
خاتمه دادن به برنامه :
وقتی که یک برنامه به انتها رسید یا اگر خواستیم اجرای برنامه را متوقف
کنیم ، میتوانیم از اینتراپت ۲۰h استفاده کنیم . DOS همیشه و بمحض اجرای این
وقفه ، اجرای برنامه را متوقه میکند.
اینراپت ۲۰h فقط با برنامه های COM. درست کار میکند و در مورد برنامه های EXE.
درست جواب نمیدهد . در عوض سرویس ۴Ch از اینتراپت ۲۱h در هر دونوع برنامه
بخوبی کار میکند .
خوب ، حالا با مطالبی که یاد گرفتیم یک برنامه اسمبلی نوشته و فایل COM. آن را
میسازیم .
بنابر این در محیط DOS، DEBUG، را اجرا کنید .
D:MASM>DEBUG
سپس دستورد A را به معنی شروع دستورات اسمبلی وارد کنید : - A
xxxx:0100
به عدد آدرسی که دیده میشود توجه نکرده و دستورات زیر را تایپ کنید . mov ah/2
mov al/7
int 16
int 20
بعد از تایپ آخرین سطر، یکبار دیگر هم کلید Enter را بزنید تا اعلان debug مجددا
ظاهر شود. حالا دستور N را برای نامگذاری برنامه بکار ببرید: - N BELL.COM
بعد از آن باید طول برنامه را ، برحسب بایت ، مشخص کنیم . طول برنامه در ثبات CX
نگهداری میشود پس از فرمان RCX برای مقدار دهی استفاده میکنیم . (طول برنامه ۸
بایت است ) . - RCX
8
و در نهایت فرمان w برای نوشتن روی دیسک و Q برای خروج . حالا ما یک فایل COM.
داریم که به محض اجرا یک صدای Beep تولید میکند .
ما امروز اولین برنامه اسمبلی خودمان را نوشتیم ، در قسمت بعد یاد میگیریم که
چطور از اسمبلر استفاده کنیم و امکانات آن را بکار ببریم .

آبجی
14th February 2010, 11:41 PM
در این قسمت طرز استفاده از ماکرواسمبلر را یاد میگیریم و برنامه هایمان را بدون
استفاده از Debug مینویسیم .
برای استفاده از اسمبلر باید یک ادیتور اسکی مثل EDITیا PE2ا داشته باشید تا
بتوانید برنامه هایتان را توسط آن تایپ کنید . هر برنامه اسمبلی دارای یک فایل
منبع (Source) حاوی دستورالعملهای اسمبلی است . ما این فایل را با یک ویرایشگر
تایپ کرده و به ماکرواسمبلر MASM.EXE میدهیم تا فایل مفعولی (OBJ.) آن را بسازد
. این فایل هم باید با برنامه Link.exe به فرم EXE. تبدیل شود . چون ما میخواهیم
برنامه های COM. بتویسیم باید فایل exe. تولید شده را با EXE2BIN.COMیا EXE2COMا
به فرم com. تبدیل کنیم .
فرض کنید در محیط ویرایشگر(مثلا EDIT ) هستیم و میخواهیم یک برنامه اسمبلی
بنویسیم .
هر برنامه از ۳ قطعه (سگمنت ) تشکیل میشود : ۱
-قطعه داده ها یا DATA SEGMENT . متغیرهای برنامه و سایر داده های مورد نیاز در
این سگمن قرار میگیرند . ۲
- قطعه کد یا Code Segment . کدها و دستورات اسمبلی در این قسمت هستند . ۳
- بخش انباره یا Stack Segment . این قطعه زیر برنامه ها و مقادیر موقتی را
نگهداری میکند . ما حتی میتوانیم محتوای ثباتها را به پشته (Stack) منتقل کرده و
بعد دوباره از آن خارج کنیم .
در یک برنامه COM. قطعه داده ها و قطعه مد در یک سگمنت قرار دارند بنا براین
ما قطعه داده ها را تعریف نمیکنیم . بعلاوه قطعه سگمنت هم برای یک فایل COM.
وجود ندارد بلکه خود DOS این محیط را فراهم میکند . به همین دلایل است که نوشتن
برنامه های COM. آسانتر است . با این حال ما با محدودیتی مواجه هستیم و آن
اینست که سایز یک برنامه COM. نمیتواند بیش از ۶۴ کیلو بایت باشد .
فرض کنید میخواهیم همان برنامه ای که صدای Beepتولید میکرد را با اسمبلر بنویسیم
پس یک فایل (مثلا bell.asm) میسازیم : EDIT BELL.ASM
حالا ما در محیط ویرایشگر هستیم . برنامه ما به این شکل خواهد بود :
. MODEL SMALL
. CODE
MOV AH/0EH
MOV AL/7
INT 10H
INT 20H
END

در سطر اول ، جمله model small. یک رهنمود مترجم است . رهنمودهای مترجم
کداجرائی نیستند ولی اسمبلر را در ترجمه برنامه راهنمائی میکنند . MODEL SMALL.
به اسمبلر میگوید که ما میخواهیم برنامه com. بنویسیم و قطعه داده ها و کدها
مشترک است . این جمله باید همیشه وجود داشته باشد. CODE
. میگوید که قسمت کدهای اجرائی شروع میشود . ما باید همیشه دستوراتمان را
بعد از یک CODE. شروع کنیم و در انتها نیز جمله END را به معنی اتمام برنامه
بنویسیم .
بعد از اتمام این مراحل از ویرایشگر خارج شده و با MASM.EXE فایل برنامه را ترجمه
میکنیم : MASM BELL.ASM
در پرسشهای masm کلید enter را بزنید . اگر برنامه را صحیح تایپ کرده باشید باید
این پیغامها را دریافت کنید :

Microsoft( R )Macro Assembler Version 5.10
Copyright( C )Microsoft Corp 1981/ 1988 .All rights reserved.
50084 + 396073 Bytes symbol space free
0 Warning Errors
0 Severe Errors

حالا فایل BELL.OBJ ساخته شده و باید آن را لینک کنیم : LINK BELL.OBJ
و نتیجه این خواهد بود:

Microsoft( R )Overlay Linker Version 3.69
Copyright( C )Microsoft Corp 1983-1988 .All rights reserved.
:Run File [ASM6.EXE]
فقط Enter بزنید | :List File [NUL.MAP]
:Libraries [.LIB] LINK : warning L4021 :no stack segment

سطر آخر یک پیغام خطا است ولی دقیقا همان چیزی است که انتظار داریم . یعنی
وجود نداشتن قطعه پشته (Stack) . به همین دلیل برنامه EXE. تولید شده توسط Link
قابل اجرا نیست . پس با EXE2COM آن را به یک فایل COM. تبدیل میکنیم . EXE2COM BELL.EXE
و داریم :

EXE2COM Version 1.0( - c )Computer Magazine
ASM6.EXE converted to ASM6.COM( 8 Bytes )
Warning :Program begins at Offset 0( Entry point .)
ASM6.COM cannot be called directly!

الان فایل COM. هم تولید شد ولی EXE2COM میگوید که ما نمیتوانیم برنامه را
فراخوانی و اجرا کنیم . چرا!?
اگر بیاد داشته باشید وقتی میخواستیم در DEBUG اسمبلی بنویسیم ، دستوراتمان همیشه
از آدرس xxxx:0100h شروع میشد. دلیل آن اینست که DOS همیشه یک فضای ۲۵۶ بایتی
بنام PSP در ابتدای برنامه ایجاد کرده و اطلاعات فوق العاده مهمی را در آن
نگهداری میکند . بنا براین برنامه ما باید حتما از آدرس ۱۰۰h شروع شود . این
قانون اسمبلر برای نوشتن برنامه های COM. است . پس کد برنامه را به شکل زیر
اصلاح کنید :

. MODEL SMALL
. CODE
دستورالعمل جدید ORG 100H MOV AH/0EH
MOV AL/7
INT 10H
INT 20H
END

راهنمای Org 100hبه DOS میگوید که برنامه باید از آدرس ۱۰۰h شروع شود . ما این
کد را اجبارا در همه برنامه ها قرار خواهیم داد . حالا برنامه را با تغییرات اعمل
شده ذخیره کرده و با انجام مراحل قبلی دوباره ترجمه کنید . پس از ترجمه فایل BELL.COM
را اجرا کرده و نتیجه را مشاهده کنید %
امروز برنامه ای با اسمبلر نوشیتم . از این پس نیز تمام برنامه های را با
اسمبلر مینویسیم و از توانائیهای آن استفاده میکنیم .

آبجی
14th February 2010, 11:41 PM
پرشهای غیر شرطی
ے اگر با زبانهائی مثل Basicیا Pascalا برنامه نویسی کرده باشید حتما از دستور Goto
ے هم استفاده کرده اید . بوسیله این فرمان ، ما میتوانستیم روال اجرای برنامه را به
یک نقطه مشخص انتقال بدهیم بدون اینکه نیاز به برقراری شرط خاصی باشد .
در زبان اسمبلی هم چنین دستوری داریم : دستورالعمل JMP (مخفف JUMP) .
دستور JMP به این شکل استفاده میشود:
برچسب JMP
ے منظور از برچسب مکانی از برنامه است . در اسمبلی برای اینکه یک نقطه از برنامه
ے را علامت بزنیم ، نام برچسب مورد نظر را مینویسیم و برای اینکه اسمبلر آن را با
ے یک دستورالعمل اجرائی اشتباه نکند، کاراکتر (:) را در مقابل آن قرار میدهیم
مانند: :Start
سپس میتوانیم با دستور JMP به آنحا پرش کنیم : Start :
:
:
Jmp Start
دقت کنید که بعد از Start ی که در مقابل JMP نوشتیم علامت : قرار نداده ایم .
ے این JMP ها از نوع JUMP NERA هستند و نوعی دیگر بنام JUMP FAR هم داریم که بزودی
آن را هم یاد میگیریم .
ے مثال : برنامه ای که در مثالهای قبل نوشتیم را در نظر بگیرید . اگر ما از روی
دستورالعملهای برنامه با JMP پرشی انجام دهیم هیچکدام از آن کدها اجرا نخواهندشد:

۱] JMP Quit _
2] mov ax/0E07h
3] int 10h
4] Quit :_
5] int 20h

برنامه از روی سطرهای ۲وَ۳ پرش خواهد کرد .
ثبات پرچم (Flags)
ے ثبات پرچم یک ثبات ۱۶ بیتی است که ۱یا ۰ا بودن بیتهای آن نشانه درست یا
ے نادرست بودن یک شرط است . مثلا اگر با دستورالعمل خاصی (میخوانیم ) تست کنیم
که آیا ثبات BX مقدار ۰ را دارد ، در این صورت بیت ۶ برابر ۰ میشود و … .
از این ۱۶ بیت فقط ۹ بیت استفاده میشود که به شرح زیر هستند :
۱۶ ۱۵ ۱۴ ۱۳ ۱۲ ۱۱ ۱۰ ۹ ۸ ۷ ۶ ۵ ۴ ۳ ۲ ۱ ۰
* * * * * O D I T S Z * A * P * C
علامت * به معنای بی استفاده بودن است .
ے ۱- پرچم نقلی یا (CF) . بیت ۰ در نتیجه اجرای وقفه ها یا بعضی اعمال حسابی تغییر
میکند .
ے ۲-پرچم توازن (ZF) . بر اساس یک عمل مقایسه ای یا حسابی تغییر میکند . اگر
نتیجه یک عبارت ۰ باشد مقدار ۱ و اگر نتیجه ۱ باشد مقدار ۰ میگیرد.
ے ۳-پرچم وقفه (IF) . اگر ۰ باشد هیچ وقفه ای نمیتواند اجرا شود و اگر ۱ باشد
میتوان وقفه ها را فراخوانی کرد .
ے و … . ۶ پرچم دیگر را فعلا لازم نداریم بنا براین توضیحی برای آنها ارائه
نمیکنیم .
دستور مقایسه ای CMP
ے برای مقایسه مقادیراز دستور CMP (مخفف CoMPare) استفاده میکینم . این دستور
ے مقدار داخل یک ثبات یا متغیر را با مقداری دیگر مقایسه کره و روی ثبات های
ے CFو ZFو تاثیر میگذارد . بعد از مقایسه میتوانیم بر حسب وضعیت پرچمها پرش
لازم را انجام دهیم .
ے مثلا CMP BX/0 تست میکند که آیا مقدار BX برابر ۰ است یا نه . در صورتی که برابر
۰
باشد ،پرچم ZF برابر ۱ میشود .
با همین دستور CMP میتوانیم کوچکتر،بزرگتر و …. را هم تست کنیم .
پرشهای شرطی
برای پرشهای شرطی از دستورهای زیر درست مثل JMP استفاده میکنیم .
ے JE/JZ : اگر محتوای ZF صفر باشد جهش میکند . اگر دو مقداری که مقایسه کرده ایم
برابر باشیند پرش انجام میشود.
ے JNE/JNZ : برعکس JZو JEو هستند و اگر ZF یک باشد (بعبارتی دو مقداری که مقایسه
کردیم برابر نباشند) جهش انجام میشود.
ے JA/JNBE . اگر محتوای ثبات یا متغیری که مقایسه کرده ایم بزرگتر از عدد مورد نظر
باشد پرش انجام میدهد . مثلا :

mov bh/1
cmp bh/10
ja Dest

ے مقدار BH برابر ۱ است و در سطر دوم تست آن را با ۱۰ مقایسه میکنیم . در سطر سوم
چون BH بزرگتر از ۱ نیست ، پس پرش JA Dest انجام نمیشود .
JAE/JNB . اگر بزرگتر یا مساوی باشد ، پرش انجام میشود.
JB/JNAE: در صورتی که کوچکتر باشد پرش انجام میشود.
JBE : در صورتی که کوچکتر یا مساوی باشد پرش انجام میشود .
مثال :
ے میخواهیم برنامه ای بنویسیم که تمام کاراکترهای بین ۱۲۸ تا ۲۵۵ را
چاپ کند.

. MODEL SMALL
. CODE
ORG 100H
START :
کاراکتر ۱۲۸ برای شروع ; MOV CH/128 CHARS :
کداسکی را درAL قرار میدهیم تا چاپ شود ; MOV AL/CH
سرویس ۰Eh برای چاپ کاراکتر ; MOV AH/0EH
اینتراپت ۱۰h ; INT 10H
یکواحد به CH اضافه کن ; INC CH
مقایسه CH با ۲۵۵ ; CMP CH/255
اگر مساوی نباشد به CHARS پرش میکند ; JNZ CHARS
پایان ; INT 20H END START

ے تمام برنامه ساده و روشن است ولی در سطر آخر نکته جدیدی وجود دارد . بعد از
ے END نام برچسب Start را آورده ایم . نکته ای که در نوشتن برنامه های اسمبلی باید
ے مراعات کنیم اینست که : اگر از برچسبی در برنامه استفاده میکنیم ، اسمبلر باید
ے یک برچسب را به عنوان نقطه آغاز کدبرنامه ببیند . به همین خاطر علاوه بر برچسب
ے CHARS یک برچسب بنام Start هم در ابتدای برنامه تعریف کرده و برای اینکه
ے اسمبلر بداند مما کدام برچسب را برای انیکار اینخاب کرده ایم ، نام آن را در
مقابل END می آوریم ، یعنی END START .
ے نکته دیگر اینکه ، در اسمبلر هر چیزی که بعد از کاراکتر (;) باشد ، توضیح
ے (Comment) فرض شده و اصلا ترجمه نمیشود . (مثل REM در بیسیک و .. ) . هر comment
ے باید در یک سطر جای داده شود و اگر از این مقدار بیشتر بود میتوانیم در سطر بعد
هم یک کاراکتر (;) درج کرده و ادامه توضیحات را بعد از آن بیاوریم .

آبجی
14th February 2010, 11:42 PM
اگر دارای کارت ویدئوی VGA و طبعا VGA BIOS باشید ، میتوانید از تابع ۱۰H مربوط
به اینتراپت ۱۰h (که مربوط به سرویسهای تصویری است ) برای تعریف پالت های جدید
استفاده کنید . تعداد رنگهائی که میتوان آنها را تغییر داد به نوع کارت گرافیک
و VGA BIOS مربوط است و در حالت عادی رنگهای شماره ۰تا ۷ا قابل تعریف هستند .
پالت رنگ را به این صورت باید تعریف کنیم : AH=10H
AL=10H شماره رنگ از ۰تا BX= 7ا
عدد رنگ سبز CH=
عدد رنگ آبی CL=
عددرنگ قرمز DH=
رنگهائی که دیده میشود ، ترکیبی از سه رنگ اصلی قرمز،سبز و آبی (RGB) هستند .
برای تعریف یک رنگ جدید نیز باید مقدار هر رنگ اصلی در پالت مورد نظر را در
نیم ثباتهای CH/CLو DHوC قرار دهیم . این مقادیر ۶ بیتی و در محدوده ۱ تا ۶۳
هستند .
پس از مقداردهی ثباتها اینتراپت ۱۰h را فراخوانی میکنیم . در مثال زیر ما رنگ
شماره ۱ که آبی میباشد را تغییر داده ایم .
تمرین : برنامه را برای رنگهای شماره ۲تا ۷ا نیز با مقادیر دلخواه تکمیل کنید

. MODEL SMALL
. CODE
ORG 100H
START :
MOV AH/010H
MOV AL/010H
MOV BX/1 ; COLOR NUMBER
MOV CH/12 ; GREEN VALUE
MOV CL/24 ; BLUE VALUE - THE 16-BIT NUMBER
MOV DH/14 ; RED VALUE
INT 10H ; VIDEO BIOS INT .
INT 20H ; TERMINATE PROGRAM
END START

ما در اینجا وجود VGA BIOS را تست نکرده ایم و اگر این برنامه روی کامپیوتری
با کارت گرافیک EGA و … اجراشود نتایج غیرقابل پیش بینی بدست خواهد آمد.
یک راه ساده برای تست وجود کارت VGA وجود دارد . به اینصورت که مقدار ثباتهای AH
و ALو را به ترتیب برابر ۱Ah و ۰۰h قرار داده و اینتراپت ۱۰h را اجرا میکنیم
اگر بعد از فراخوانی وقفه ، AL برابر ۱Ah بود یعنی کارت VGA فعال است .
پس در برنامه ای که نوشتیم میتوانیم با یک دستور CMP ساده از بوجود آمدن خطای
نبود VGA BIOS جلوگیری کنیم .
بنا براین برنامه را به این صورت تکمیل میکنیم :

. MODEL SMALL
. CODE
ORG 100H ; BEGINING OFFSET : 100H
START :


MOV AH/1AH
این قسمت را | MOV AL/00
اضافه کرده ایم | INT 10H CMP AL/1AH ; VGA BIOS EXIST? |
; NO UJUMP TO THE END JNZ NOVGA
MOV AH/010H
MOV AL/010H
MOV BX/1 ; COLOR NUMBER
MOV CH/12 ; GREEN VALUE
MOV CL/24 ; BLUE VALUE - THE 16-BIT NUMBER
MOV DH/14 ; RED VALUE
INT 10H ; VIDEO BIOS INT.
NOVGA:
INT 20H ; TERMINATE PROGRAM
END START

در برنامه بالا اگر بعد از اجرای وقفه ۱۰h مقدار AL برابر ۱Ah نباشد، نمیتوانیم
از سرویس تعریف رنگ استفاده کرده و مجبوریم برنامه را با پرش به NOVGA خاتمه
دهیم .
در این قسمت با نوشتن یک برنامه ، دو تابع مفید از وقفه ۱۰h را یاد گرفتیم و
دیدیم که نوشتن یک برنامه اسمبلی برخلاف آنچه تا بحال تصور میکردیم چقدر ساده
و جالب است .

آبجی
14th February 2010, 11:42 PM
دستورالعمل LOOP تا اینجا هروقت که میخواستیم یک حلقه ایجاد کنیم از دستورالعمل CMP و پرشهای
شرطی استفاده میکردیم . راه ساده تری برای اجرای مکرر دستورالملها وجود دارد و آن
استفاده از LOOP است . دستور LOOP به تعداد دفعاتی که با ثبات CX مشخص میکنیم
حلقه ای را ایجاد میکند .
برای ایجاد چنین حالتی ابتدا مقدار لازم را در ثبات CX قرار میدهیم . دستور Loop
همیشه از مقدار CX یک واحد،یک واحد کم میکند تا به ۰ برسد . وقتی که مقدار CX
برابر ۰ شد ، از حلقه خارج میشود . بنا براین برای ایجاد حلقه ای که ۱۰۰ بار
تکرار شود، CX را برابر ۱۰۰ قرار میدهیم . MOV CX/100
حلقه مورد نظر بین دستور loop و یک برچسب انجام میشود . برچسب ، در ابتدای حلقه
و دستور loop در انتهای آن قرار میگیرد.

MOV CX/100
LPCNT :
:
:
:
LOOP LPCNT

در بالا با mov cx/100 میخواهیم حلقه ای داشته باشیم که ۱۰۰ بار انجام بشود. وقتی
به loop lpcnt میرسیم ، یکواحد از مقدار CX کاسته شده و مجددا به lpcnt پرش
میکنیم .
به همین سادگی ! .
تمرین : برنامه ای بنویسید که کاراکتر های با کد اسکی ۳۲ تا ۲۵۵ را نمایش دهد.
راهنمائی :
چون همیشه CX در حال کاهش است ، برای اینکه بتوانیم از ۳۲ تا ۲۵۵ برویم ، باید
عدد داخل CX را برابر ۳۱=۲۲۴-َ۲۵۵ قرار بدهیم تا تعداد ۲۵۴ حرف چاپ بشود. سپس
مقدار CX را از ۲۵۵ کم کرده و داخل AL قرار میدهیم تا با تابع ۰Eh ار وقفه ۱۰h
چاپ شود . این وقفه را در برنامه ALLCHR.ASM توضیح دادیم .
پشته (Stack) ، ذخیره و بازیابی ثباتها
ما تعدادی ثبات برای نگهداری و انتقال اعداد و مقادیر داریم ولی کافی نیستند .
بخصوص در DEBUG که نمیتوانیم متغیرتعریف کنیم . یا در برنامه پیش می آید
بخواهیم برای یک کار خاص و بطور موقت مقدار ثبات را تغییر دهیم ،در این مواقع
مقدار ثبات را در پشته ذخیره کرده و بعدا مجددا بازیابی میکنیم .
در برنامه های EXE. پشته یا Stack یک سگمنت مستقل است و آدرس آن در ثبات SS
(Stack Segment) قرار دارد . در برنامه های COM. پشته به آنصورت وجود ندارد و
خود DOS فضای لازم را برای برنامه فراهم میکند . در هر صورت ما به اینکه پشته در
کجاست کاری نداریم و به یک شکل مقادیر را به پشته فرستاده (PUSH) یا از آن
خارج میکنیم (POP) .
خاصیت مهمی که در PUSHو POPو کردن مقادیر به پشته وجود دارد اینست که همیشه
اولین مقداری که به پشته فرستاده میشود، آخرین مقداری است که از پشته خوانده
میشود . مثلا فرض کنید که ابتدا AX و بعد CX را به پشته میفرستیم . حال برای خارج
کردن درست این مقادیر، ابتدا CX و بعد AX را خارج میکنیم .
این قانون اسمبلی است و به (FILO=First In Last Out) معروف است .
برای فرستادن مقدار یک ثبات به پشته از دستور PUSH استفاده میکنیم .
مثلا برای قرار دادن AX مینویسیم : PUSH AX . PUSH
نمیتواند مقدار یک نیم ثبات را در پشته قرار دهد و حتما باید یک ثبات
کامل دوبایتی باشد .
وقتی ثباتی را PUSH کردیم ، مقدار آن در Stack نگهداری میشود و میتوانیم مقدار آن
را تغییر دهیم .
پس از آن ، از دستور POP برای خارج کردن ثبات از پشته استفاده میکنیم مانند . POP AX
مثال :

۱] MOV AX/0AH ; ax = 0Ah
2] MOV BX/0BH ; bx = 0Bh
3] PUSH AX ; push ax to stack
4] PUSH BX ; hold bx in stack
5] MOV AX/5 ; now ax=5
6] MOV BX/2AH ; and bx=2Ah
7] : ; other commands and
8] : ; statements …
9] POP BX ; POP bx from stack
10] POP AX ; read ax from stack

در سطر ۳وَ۴ مقادیر axو bxو را به پشته میفرستیم . در سطر ۵و ۶و مقادیر جدید به ax
bx/ میدهیم و بعد از آن یکسری دستورات دیگر هستند … . در سطر ۱۰ مقدار BX
از داخل Stack بیرون کشیده میشود . توجه داشته باشید که bx را بعد از ax در پشته
قرار داده ایم ولی در هنگام خارج کردن به ترتیب عکس عمل میکنیم .
بعلاوه دستور PUSHF به معنی PUSH FLAGS ، ثبات پرچم را در پشته قرار میدهد . نحوه
کار با آن هم مثل PUSH معمولی است ولی آرگومان ندارد .
مثال : PUSHF
در قسمت بعد، این مطالب را تمرین میکنیم و چند برنامه نمونه میبینیم ، حتی
یک برنامه گرافیکی با ۲۵۶ رنگ مینویسیم و در آن از دستورات LOOPو PUSHو
استفاده میکنیم .

آبجی
14th February 2010, 11:43 PM
در این قسمت یک تمرین دیگر با هم انجام میدهیم و برنامه ای مینویسیم که تعداد ۲۰۰
رنگ از ۲۵۶ رنگ موجود در حالت ۳۲۰×۲۰۰ گرافیکی را نمایش دهد .
تابع شماره ۰۰h از وقفه ۱۰h مربوط به تعیین حالت نمایش است . کد مربوط به
حالت صفحه نمایش در ثبات AL قرار گرفته و وقفه فراخوانی میشود: شماره تابع برابر AH=00h
حالت صفحه نمایش با استفاده از جدول AL= INT 10h
حالت صفحه نمایش در AL از جدول مخصوص موجود در کتابهای اسمبلی بدست می آید.
کد مربوط به حالت ۲۵۶C َ۳۲۰×۲۰۰ برابر ۱۳h است بنا براین AL را برابر ۱۳h قرار
میدهیم .
برای نمایش و روشن کردن یک نقطه (Pixel) در حالت گرافیکی از تابع ۰Ch همین
وقفه استفاده میکنیم . یعنی شماره ستون را در CX ، سماره سطر را در DX و شماره
رنگ را در AL قرار داده و وقفه را اجرا میکنیم . برای اینکه از ستون ۱۹۹ تا ستون
شماره صفر نقطه روشن کنیم ، CX را برابر ۳۱۹ قرار داده و با دستور LOOP نقاط را
در یک سطر روشن میکنیم .

MOV CX/319 ; COLUMN 319 = START COLUMN
COL:
INT 10H ; CALL INTERRUPT 10H
LOOP COL
سپس DX یا همان شماره سطر را یکواحد افزایش میدهیم و مقدار آن را با ۱۹۹ مقایسه
میکنیم (چون از ۰ تا ۱۹۹ سطر داریم ) و اگر برابر نبود دوباره عملیات بالا را
انجام میدهیم .
بعد از اینکه این عملیات انجام شد، تابع ۰۰h از INT 16h را فراخوانی میکنیم تا
منتظر دریافت یک کلید از صفحه کلید شود . به این ترتیب میتوانیم نتیجه برنامه
را مشاهده کنیم و کلیدی را برای اتمام برنامه بزنیم .
در نهایت باید حالت صفحه نمایش را به مود متنی برگردانیم .
برای اینکار از همان تابع تعیین مود نمایشی استفاده میکنیم و حالت صفحه نمایش
که با AL مشخص میشود را برابر ۳ قرار میدهیم .
برنامه را با روش گفته شده تایپ و کامپایل کنید . اگر از توربو اسمبلر استفاده
میکنید با فرمان ASM/T.َTASM VGA256 آن را به فرم COM. ترجمه کنید.


. MODEL SMALL
. CODE
ORG 100H
START :
MOV AH/00H
MOV AL/13H
MOV BX/00H ; PAGE NUMBER
INT 10H ; SET TO 320×200 256 COLORS MOV AH/0CH ; PUTPIXEL FUNCTION
MOV AL/25 ; COLOR #25
MOV DX/0 ; ROW 0
ROW :
MOV CX/319 ; COLUMN 319 = START COLUMN
COL :
INT 10H ; CALL INTERRUPT 10H
LOOP COL ; DOWN TO CX=0
INC DX ; DX=DX+1
INC AL ; AL=AL+1( COLOR NUMBER )
CMP DX/199 ; IF DX=199
JNZ ROW ; ELSE JUMP TO ROW
MOV AH/00H
INT 16H
MOV AH/00H ; VIDEO MODE SET
MOV AL/03H ; 80×25 16 COLORS
INT 10H ; CALL INT .10H
INT 20H ; TERMINATE PROGRAM
END START

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

آبجی
14th February 2010, 11:44 PM
حتما با ثابتها در زبانهائی مثل پاسکال آشنائی دارید . بعنوان مثال با جمله
ے Const MaxLen=1024; ، ثابتی بنام MaxLen تعریف شده و مقدار آن برابر ۱۰۲۴ قرار
ے قرار میگیرد . پس از آن کامپایلر در هرجا که MaxLen را مشاهده کند عدد ۱۰۲۴ را
بجای آن قرار میدهد .
در زبان اسمبلی برای تعریف یک ثابت از معرفه EQU به شکل زیر استفاده میکنیم مقدار EQU نام ثابت
مثلا : MaxLen EQU 1024
ے به این ترتیب اسمبلر همیشه بجای MaxLen عدد ۱۰۲۴ را قرار میدهد . بهمین دلیل
ثابتهای برنامه را باید قبل از جمله CODE. بنویسیم . مثال :

. MODEL SMALL
SECTORS EQU 18
SIDES EQU 2
. CODE
:
:

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

. MODEL SMALL
BELL EQU 7
. CODE
ORG 100H
MOV AH/0EH
MOV AL/BELL
INT 10H
INT 20H
END

متغیرها
ے از متغیرها برای نگهداری موقتی داده ها استفاده میکنیم . مثلا در زبان پاسکال
ے میتوانیم با عبارت Var یک متغیر تعریف کنیم مثل Var Buffer:Byte; و در زبان
سی مثل unsigned char Buffer; .
ے متغیرها در زبان اسمبلی باید حتما در داخل قطعه داده (DS) تعریف بشوند و در
ے برنامه های COM. هم از آنجائی که قطعه داده ها و کد یکی است میتوانیم در قطعه کد
نیز تعریف کنیم .
ے برای تعریف یک متغیر باید بعد از نام آن یکی از عبارات ..DB/DW/DD/ را
ے بیاوریم . DB مشخصه نوع بایت ،DW مشخصه نوع Word (دوبایتی ) و DD مشخصه نوع
(Double Word) 4 بایتی است .
مثلا :

. CODE
SIZE DW 1024
BELL DB 7

ے در این مثال Size یک متغیر دو بایتی بوده و مقدار اولیه ان ۱۰۲۴ است و BELL نیز
یک متغیر تک بایتی با مقدار ۷ میباشد .
ے اگر نمیخواهیم به متغیر مقدار اولیه بدهیم ، میتوانیم از علامت (?) بجای مقدار
استفاده کنیم مانند : MaxLen DW ?
ے برای تعریف یک رشته کاراکتری از معرفه DB استفاده کرده و محتوای رشته را داخل
(”) یا (”") قرار میدهیم . مثلا :

MSG DB “ASSEMBLY / A QUICK LOOK ! “

ے در این مثال MSG یک متغیر کاراکتری است . در اسمبلی میتوانیم از کد اسکی
ے کاراکتر ها نیز استفاده کنیم . مثلا اگر در تعریف DB بخواهیم کدهای اسکی ۱۳و ۱۰
را به MSG اضافه کنیم میتوانیم با کاما این کار را انجام دهیم :

MSG DB “ASSEMBLY / A QUICK LOOK ! “/13/10
یا : MSG DB “ASSEMBLY / A QUICK LOOK ! “/0Ah/0Dh
یا حتی : MSG DB “ASSEMBLY / A QUICK LOOK ! “/0Ah/0Dh/’$’

این ترکیبها همه یک رشته کاراکتری معرفی میکنند .
برای تعریف آرایه ها نیز از روشی مشابه و به شکل زیر استفاده میکنیم :
(مقدار اولیه )DUP تعداد عناصر DB/DW/DD نام متغیر
مانند: BUFFER DB 1024 DUP(0 )
که ارایه ای یک کیلوبایتی تعریف کرده و همه عناصر آن را با ۰ پر میکند.
اگر نخواهیم مقدار اولیه ای در نظر گرفته شود از ? استفاده میکنیم .
مانند: BUFFER DB 1024 DUP(? )
و برای تعریف یک آرایه حرفی باید با یک حرف یا عبارت آن را پر کنیم : BUFFER DB 1024 DUP(”A” )
و حتی : BUFFER DB 1024 DUP(”STACK” )
ے گفتیم که متغیرها همیشه (در برنامه های COM.) در قطعه کد و بعد از CODE. نوشته
ے میشوند ، بنا براین اسمبلر همیشه سعی خواهد کرد که آنها را بصورت یک کدماشین
ے قابل اجرا تفسیر کند. به همین دلیل همیشه بایک دستور JMP از روی آنها پرش
میکنیم . مثال :

. MODEL SMALL
RDISK EQU 2
. CODE
ORG1 100H
START :
JMP MAIN
BUFFER DB 512 DUP(0 )
MSG DB “DISK DUP.”/13/10/’$’
MAIN :

مجموعه کدهای اجرایی برنامه :
: END START
همانطور که میبینید با دستور JMP MAIN از قسمت تعریف داده ها پرش کرده ایم .
در قسمت بعد نحوه استفاده از متغیرها و بافرها (Buffers) را مطالعه
میکنیم . با همین معلومات برنامه ای برای نمایش محتوای سکتوهای دیسک
مینویسیم و پیغامهای لازم را به آن اضافه میکنیم .

آبجی
14th February 2010, 11:45 PM
در این قسمت نحوه دسترسی به مقادیر متغیر ها را یاد میگیریم .
ے وقتی که میخواهیم مقدار یک متغیر را به یک متغیر یا ثبات دیگر منتقل کنیم باید
ے به اندازه آن توجه داشته باشیم . مثلا اگر متغیری بصورت LOCATE DB 10 تعریف کرده
ے باشیم ، به دلیل تک بایتی بودن ، نمیتوانیم آن را به یک ثبات کامل مثل AX یا
متغیر دوبایتی که با DW تعریف شده است ارسال کنیم .
ے اما انتقال آن به یک نیم ثبات مثل ALیا AHا و … مجاز است مانند . MOV BH/LOCATE ے از متغیرها بیشتر برای نگهداری موقت داده ها استفاده میشود . مثلا وقتی که
ے برنامه ای برای کار با قطاعهای دیسک مینویسیم ، باید یک محل موقتی برای ذخیره
ے محتوای قطاع های خوانده شده ایجاد کنیم . در این موقع یک متغیر به شکل (ترجیحا)
آرایه تعریف میکنیم .
ے وقتی به این شکل با متغیرها برخورد میشود، به دانستن آدرس آن نیاز پیدا میکنیم
فرض کنید میخواهیم جمله A QUICK START TO ASSEMBLY PROGRAMMING را چاپ کنیم .
در قدم اول باید متغیری تعریف کرده و این جمله را داخل آن قرار دهیم .
پس : MSG DB ‘A QUICK START TO ASSEMBLY PROGRAMMING’/13/10/’$’
ے اعداد ۱۳وَ۱۰ انتهای رشته برای انتقال مکان نما به سطر بعد هستند و کاراکتر ‘$’
ے از این جهت وجود دارد که تابع چاپ رشته انتهای رشته کاراکتری را با بودن $
تشخیص میدهد.
ے برای چاپ رشته کاراکتری راه هائی وجود دارد که یکی از آنها استفاده از تابع ۹h
مربوط به INT 21h میباشد .
برای فراخوانی آن باید به این صورت رجیستر ها را پر کنیم : AH=09H
آدرس رشته کاراکتری DS:DX = INT 21H
ے عبارت DS:DX نشان میدهد که مقدار قطعه (Segment) رشته کاراکتری ، یعنی آن قطعه
ے ای که متغیر تعریف شده در آن قرار گرفته است ، را باید در DS قرار بدهیم . به
همین صورت نیز مقدار آفست (Offset) آن را به DX انتقال میدهیم .
برای بدست آوردن شماره قطعه یک متغیر از عملگر SEG استفاده میکنیم .
ے مثلا برای بدست آوردن شماره قطعه MSGاز MOV AX/Seg MSGز استفاده میکنیم . این
دستور شماره سگمنت MSG را پیدا کرده و در AX قرار میدهد .
برای بدست آوردن شماره آفست هم از OFFSET استفاده میکنیم مثلا MOV DX/OFFSET MSG
پس برای چاپ رشته MSG باید به این صورت عمل کنیم :

MOV AH/09H
MOV DX/OFFSET MSG
INT 21H

ے این قطعه کاری که ما میخواهیم را انجام میدهد و اگر دقت کنید متوجه میشوید که
ے اصلا شماره قطعه (Segment) را محاسبه نکرده ایم . علت اینست که متغیر ما به دلیل
ے COM. بودن برنامه در Code Segment ( که با CODE. مشخص میشود) تعریف شده پس خود
ے بخود DS حاوی مقدار سگمنت آن هست . ( باز هم یاد آوری میکنیم که CS حاوی شماره
ثبات کد و DS حاوی ثبات داده ها است و در برنامه های COM. مقدار برابر دارند)
ے یک دستور خلاصه برای بدست آوردن عدد آفست وجود دارد بنام LEA .کل کاری که این
ے دستورالعمل انجام میدهد اینست که دیگر احتیاج به نوشتن OFFSET نخواهد بود . به
عنوان مثال MOV DX/OFFSET MSGبا LEA DX/MSGا برابر است .
با این تفاسیر کل برنامه به این شکل خواهد بود .

. MODEL SMALL
. CODE
ORG 100H
START :
JMP MAIN ; skip to main codes
MSG DB ‘A QUICK START TO ASSEMBLY PROGRAMMING’/13/10/’$’
MAIN :
LEA DX/MSG ; get MSG offset
MOV AH/09 ; write string function
INT 21H ; call interrupt 21h
INT 20H ; terminate program
END START

تمرین :
ے برای اینکه تمرین بهتری داشته باشیم ، میخواهیم خودمان و فقط با استفاده از وقفه
ے مربوط به چاپ کاراکتر همین جمله را چاپ کنیم . قبلا گفتیم که تابع ۰Eh از وقفه
ے ۱۰h یک کاراکتر را در محل مکان نما چاپ کرده و مکان نما را یک خانه به راست
انتقال میدهد. میخواهیم رشته کاراکتری بالا را تا رسیدن به علامت $ چاپ کنیم .
ے بهترین کار اینست که عدد آفست را در BX قرار بدهیم . در اینموقع آفست اولین
ے کاراکتر در BX است . مقدار داخل این آفست را بصورت MOV al/[bx] به ثبات AL
ے منتقل کرده و بعد چاپ میکنیم . برای کاراکتر بعدی یکواحد به BX اضافه میکنیم و
ے دوباره همان کارهای قبلی … . این عملیات را باید تا رسیدن به کاراکتر ‘$’ ادامه
بدهیم .
ے ** این برنامه را خودتان و بدون توجه به راه حل ارائه شده بنویسید و فایل COM.
آن را بسازید.
. MODEL SMALL
. CODE
ORG 100H
START :
JMP MAIN ; jump to MAIN
MSG DB ‘A QUICK START TO ASSEMBLY PROGRAMMING’/13/10/’$’
MAIN :
LEA BX/MSG ; get MSG offset
MOV AH/0EH ; write char function
LOOP :_
MOV AL/[BX] ; move [BX] to AL: charactre code
CMP AL/’$’ ; if al is equal with ‘$’
JE END _; then jump to END _
INT 10H ; otherwise call interrupt 10h
INC BX ; BX=BX+1
JMP LOOP _; jump to next caharcter
END :_
INT 20H ; terminae program
END START

آبجی
14th February 2010, 11:45 PM
در این قسمت یک تمرین جدید انجام داده و بوسیله آن تجربیات خودمان را افزایش
میدهیم . این تمرین اولین برنامه ما هست یک عملیات سطح پائین سیستم ، یعنی
دسترسی به دیسک ، را انجام میدهد .
تابع شماره ۲(AH=2() از وقفه ۱۳h که وقفه دیسک میباشد ، برای خواندن سکتورهای
دیسک بکار میرود . وقتی شماره هد، شیار،سکتور و … را مشخص کرده و وقفه را
فراخوانی نمودیم ، محتوای قطاع خوانده شده در محلی که با جفت ثبات ES:BX مشخص
میشود قرار میگیرد. به همین دلیل ما یک آرایه تعریف میکنیم وآدرس آن را در ES:BX
قرار میدهیم تا اطلاعات خوانده شده در آن قرار بگیرد. BUF DB 512 DUP(0)
در این مثال ما میخواهیم برنامه ای بنویسیم که محتوای قطاع بوت کننده دیسکی را
خوانده و نمایش دهد . چون میخواهیم یک سکتور را بخوانیم و اندزه هر قطاع ۵۱۲
بایت است ، پس آرایه ای با ۵۱۲ عنصر تک بایتی تعریف کرده ایم .
در قدم بعدی شماره شیار (۰-۷۹) را در CH، شماره قطاع (۱-۱۸) را در CL، شماره
درایو را در DL(…/.:0=A ) ، رویه دیسک را در DH(0=1st Side() و تعداد قطاعهائی
که باید خوانده شوند را در AL قرار میدهیم .

MOV AX/0201H
MOV CH/0 ;TRACK NUMBER
MOV CL/1 ;SECTOR NUMBER
MOV DH/0 ; SIDE #1
MOV DL/0 ; 0=A / 1=B /( …DRIVE NUMBER )

حالا باید آدرس بافر تعریف شده (BUF) را به ES:BX منتقل کنیم . گفتیم که برای
بدست آوردن شماره سگمنت هر متغیر از Seg و برای بدست آوردن مقدار آفست از Offset
استفاده میکنیم . چون برنامه ما COM. است ، پس عدد سگمنت بطور خودکار در
ثبات DS هست و احتیاجی به یافتن آن نداریم . بلکه فقط باید آن را به ES منتقل
کنیم .
گفتیم که نمیتوانیم ثبات ESو DSو را مستقیما و با MOV به هم منتقل کنیم ، بلکه
باید از یک ثبات همه منظوره مثل AX کمک بگیریم . بنا براین و برای اینکه مقدار
فعلی ثبات AX از بین نرود، ابتدا AX را در Stack قرار داده و مقدار DS را در آن
قرار میدهیم : PUSH AX ; SAVE AX
MOV AX/DS
و پس از آن محتوای AX را به ES داده و دوباره مقدار AX را از پشته POP میکنیم : MOV ES/AX
POP AX
پس از آن با دستور LEA مقدار آفست BUF را بدست می آوریم : LEA BX/BUF
و در نهایت فراخوانی اینتراپت ۱۳h. INT 13H
بقیه برنامه عبارتست از چاپ کارارکترهای داخل آرایه BUF که قبلا آن را یاد
گرفتیم . فقط به این نکته توجه میکنیم که کدهای اسکی کوچکتر از ۳۲ کدهای کنترلی
بوده و به جای آنها باید کاراکتر (.) چاپ کنیم به همین دلیل هم قطعه کدی برای آن
نوشته ایم :

MOV AL/BUF[BX]
CMP AL/32
JB SKIP
:
:
SKIP :
MOV AL/’.’
INT 10H
:
:

لیست کامل : .MODEL SMALL
.CODE
ORG 100H
START:
JMP MAIN
BUF DB 512 DUP(0)
MAIN:
MOV AX/0201H
MOV CH/0 ;TRACK NUMBER
MOV CL/1 ;SECTOR NUMBER
MOV DH/0 ; SIDE #1
MOV DL/0 ; 0=A / 1=B /( …DRIVE NUMBER)
PUSH AX ; SAVE AX
MOV AX/DS
MOV ES/AX
POP AX
LEA BX/BUF
INT 13H
MOV BX/1
MOV AH/0EH ; WRITE CHAR.
PRINT:
MOV AL/BUF[BX]
CMP AL/32
JB SKIP
INT 10H
JMP CONT
SKIP:
MOV AL/’.’
INT 10H
CONT:
INC BX
CMP BX/513
JNZ PRINT
INT 20H
END START
VN:F [1.6.7_924]

آبجی
14th February 2010, 11:46 PM
عملگرهای بیتی
عملگرهای بیتی مانند عملوندهای حسابی هستند با این تفاوت که روی بیت ها کار
میکنند. این عملگرها عبارتند از : … AND/OR/XOR/SHR/SHL/RCL/RCR/ .
عملگر AND : این اپراتور بیتهای دو عدد(متغیر) را با هم AND کرده و حاصل را در
متغیر (یا ثبات ) سمت چپ قرار میدهد . اگر فرض کنیم که همیشه ۱ بودن بیت به
معنای Trueو ۰و بودن آن به معنای False است ، AND همیشه در صورتیکه هر دوبیت
مقایسه شونده ۱ باشند، حاصل ۱یا Trueا را بر میگرداند .
جدول ارزشی AND :
X | Y | X and Y |
1 | 1 | 1 |
1 | 0 | 0 |
0 | 1 | 0 |
0 | 0 | 0 |
پس وقتی ما عملوند AND را با دو رجیستر بکار میبریم ، بصورتی که گفته شد بیتها
با هم مقایسه شده و حاصل مقایسه در محل متناظر بیتها در ثبات سمت چپ قرار
میگیرند . مثلا اگر دستور AND Ah/Dh را اجرا کنیم ، حالتی نظیر شکل زیر را داریم :
AH : 01101010
DH : 01111101
AH :AH AND DH : 01101000
به نتیجه بدست آمده توجه کنید .
هر وقت که بخواهیم بیت های خاصی از یک رجیستر را ۰ کنیم ، یک عدد باینری که
همه بیتهای آن ، بجز بیتهای مورد نظر ۱ هستند را در نظر گرفته با رجیستر مورد نظرAND
میکنیم .مثلا اگر بخواهیم بیتهای دوم و سوم ثبات AX را صفر کنیم : AND AX/11111001b
عملگر OR :
این عملوند بیتهای دو عدد را با هم مقایسه کرده و اگر یکی از آن دو ۱ بود ، بیت
متناظر در ثبات سمت چپ را ۱ میکند . مثلا با دستور OR AH/DH بیتهای AHبا DHا
مقایسه شده و هر دو بیت متناظر که با هم ۰ بودند ، بیت تناظر در AHهم ۰ میشود
AH : 01101010
DH : 01111100
AH : AH OR DH : 01111110
هرگاه که بخواهیم بیت های خاصی از یک متغیر یا رجیستر را ۱ کنیم ، یک عدد
باینری که همه بیتهای آن غیر از بیتهای مورد نظر ۰ هستند در نظر گرفته و با ثبات
مورد نظر OR میکنیم . مثلا اگر بخواهیم دو بیت پائین AHرا ۱ا کنیم منویسیم : OR AH/00000011b
عملگر : XOR
عملوند XOR تنها در صورتی نتیجه ۱ میدهد که دو بیت مقایسه شونده غیرهم ارزش
باشند . یعنی یکی ۱ و دیگری ۰ باشد .
بعنوان مثال با اجرای XOR AH/DH این عملیات روی بیتها انجام میشود
AH : 01101010
DH : 01111100
AH : AH XOR DH : 11101001
وقتی بخواهیم یک مقدار ثبات را برابر صفر قرار بدهیم ، معمولا از آن را با خودش XOR
میکنیم . مثلا XOR CX/CX محتوای ثبات CX را برابر ۰ قرار میدهد .
عملگرهای SHRو SHLو :
این عملگرها، بیتها را به راست و چپ شیفت ( انتقال ) میدهند .
SHR Reg.nnum و SHL Reg.nnum
.Reg اسم یک ثبات است مثلا AXو numو معلوم میکند که چند بیت باید به طرف
راست یا چپ انتقال پیدا کند . مثلا SHR AX/6 بیتهای AXرا ۶ا واحد به راست
انتقال داده و بیتهای چپ را با ۰ پر میکند . AX :10100010
AX :SHR AX/4 : 00001010
SHL
هم عکس این عمل را انجام میدهد . یعنی بیتها را به چپ شیفت داده و از
طرف راست با ۰ پر میکند . AX :10100010
AX :SHR AX/4 : 00100000
مثال : اگر بخواهیم که محتوای نیم ثبات CL را به نیم ثبات CH منتقل کنیم ،
کافیت که CXرا ۸ا بیت به سمت چپ شیفت بدهیم . یعنی SHL CX/8
CL CH | 10110100 | 00101101 |
محتوای اولیه CX CX
CL CH | 00101101 | 00000000 |
محتوای CX بعد از SHL CX/8
انتقال

آبجی
14th February 2010, 11:48 PM
عملگر Not : عملوند Not ارزش همه بیتهای یک بایت یا کلمه را برعکس میکند .
یعنی تمام بیتهای ۱را ۰ا و تمام بیتهای ۰را ۱ا میکند . بعنوان مثال اگر AH حاوی
مقدار ۱۰۱۰۱۱۰۱ باشد، بعد از اجرای Not Al ، محتوای AL بصورت ۰۱۰۱۰۰۱۰ خواهد
بود. جدول ارزشی Not Not X X N
F | T
T | F
عملگر Neg . این اپراتور معمولا با Not اشتباه میشود در صورتی که کمتر شباهتی بین
آنها وجود دارد . Neg ارزش عددی یک عدد علامتدار را برعکس میکند . یعنی یک عدد
منفی را مثبت میکند و برعکس . در اعداد علامتدار ( همانطور که بعدا هم خواهیم
دید )، اولین بیت سمت چپ ( بیت هشتم ) بیت علامت است . ۱ بودن آن نشاندهنده
منفی بودن و ۰ بودن آن نشان دهنده مثبت بودن است .
عملگر Neg با عکس کردن بیت علامت ، ارزش عدد را عکس میکند .
این عملوند را در مبحث اعداد علامتدار مفصلا میخوانیم .
مثال : میخواهیم برنامه ای بنویسیم که تمام حروف کوچک یک عبارت را به حروف
بزرگ تبدیل کند . از نظر مقدار عددی ، تفاوت حروف کوچک و بزرگ در اینست که
بیت پنجم در حروف بزرگ برابر ۰ و در حروف کوچک ۱ است . مثلا کداسکی حرف a
به باینری برابر ۰۱۱۰۰۰۰۱ و کد A برابر ۰۱۰۰۰۰۰۱ است . پس برنامه ای مینویسیم
که تمام کاراکترهای رشته کاراکتری مورد نظر را خوانده و بیت پنچم آنها را ۰ کند
. در قسمت قبلی دیدیم که برای ۰ کردن یک بیت ، یک عدد باینری که تمام بیتهای
آن ۱ ، و بیت مورد نظر (برای ۰ کردن ) ۰ باشد را با عدد مورد نظر AND میکنیم .

. MODEL SMALL
. CODE
ORG 100H
START :
JMP MAIN
MSG DB ‘ this is an example/ ..$’
MAIN :
بدست آوردن آدرس رشته ; LEA BX/MSG LOOP :_
قرار دادن کارکتر در AL ; MOV AL/[BX]
آیا کاراکتر $ است ? ; CMP AL/’$’
بله ، به MAIN برو ; _JZ END
بیت پنجم را صفر کن ; AND [BX]/11011111B
یکواحد به BX اضافه کن - کاراکتر بعدی ; INC BX JMP LOOP _;
END :_
قرار دادن آفست رشته در DX ; MOV DX/BX
تابع ۹ برای نمایش رشته ; MOV AH/9
قفه ۲۱h ; INT 21H
پایان برنامه ; INT 20H END START
معمولا برنامه هائی که پیغامهای خود را کد میکنند ( مثل ویروسها ) از این روش یا
روشی مشابه برای Decode کردن پیغامها استفاده میکنند .
مثال :
بایت وضعیت صفحه کلید که مربوط به وضعیت کلید های کنترلی CapsLock/NumLock
در بایوس های AT/PS2 در آدرس ۰۰۱۷h:0040h قرار دارد.
بیتهای این بایت نشان میدهد که کدام کلید فعال است . ۱ بودن به معنی روشن بودن
و ۰ به معنی خاموش بودن آن است . در مثال زیر بیت ششم برای کلید CapsLockرا ۱ا
میکنیم تا Capslock روشن شود .
.MODEL SMALL
.CODE
ORG 100h
START:
PUSH ES
MOV AX/0040h
MOV ES/AX
MOV AL/ES:[17h]
OR AL/32
MOV BYTE PTR ES:[17h]/AL
POP ES
MOV AH/1
INT
VN:F [1.6.7_924]

استفاده از تمامی مطالب سایت تنها با ذکر منبع آن به نام سایت علمی نخبگان جوان و ذکر آدرس سایت مجاز است

استفاده از نام و برند نخبگان جوان به هر نحو توسط سایر سایت ها ممنوع بوده و پیگرد قانونی دارد