PDA

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



Spy
17-03-2005, 22:59
سلام
از عنوانم ميعلومه...
من الان نميدونم ثبات چيه رجيستر چيه؟
اگه يكسي يه كتاب اموزشي خوب ميشناسه(برا دانلود كه چه بهتر) ولي اگه يه كتاب واقعي :shock: هم بود اسمش رو بنويسيد ممنون ميشم

Nesta
18-03-2005, 01:57
سلام
بهتر هست در اين مورد کتاب بخواری چون خيلی سخت هست من اين درس رو گزرندم ولی کار باهاش سخته يعنی حوصله ميخواد ثبات يه نو حافظه هست مثله افّست ا غيره مثلاً يه مثال برات ميزنم فرمنهش 3 حرفی هست مثله mov f1,f2 يعنی از مثلاً ثبات f1 انتقال بده به ثبات f2 يا sum l1+l2 يعنی مقادير اينرو باهم جم کن اينطوری هست به صورت مبتدی [ برای مشاهده لینک ، لطفا با نام کاربری خود وارد شوید یا ثبت نام کنید ] اينجام يه سر بزن

Nesta
18-03-2005, 02:02
اسمبلي ياد بگيريم - 1

براي ياد گرفتن اسمبلي بايد با مبناهاي عدد نويسي ، ساختمان داخلي كامپيوتر
و برنامه نويسي آشنا باشيم .
ما برنامه هايمان را مستقيما با اسمبلر Macro Assembler خواهيم نوشت و گاها از Debug
استفاده خواهيم كرد . بعلاوه چون برنامه هاي حجيم نخواهيم نوشت قالب اكثر
رنامه هاي ما COM. خواهد بود .
براي شروع ابتدا نگاهي به حافظه ميكنيم :

حافظه و آدرس دهي

هر كامپيوتر مبتني بر 8086 داراي حداقل 640 كيلوبايت حافظه است . اين 640
كيلوبايت به قطعات 64 كيلوبايتي تقسيم شده و ما اين قطعات را "قطعه " يا Segment
ميناميم . هر سگمنت هم به خانه هاي تك بايتي ديگري تقسيم شده است .

براي بدست آوردن مقدار يك بايت مشخص از حافظه ما بايد عد مربوط به سگمنت و
همچنين شماره آن بايت در سگمنت ( كه آفست Offset ناميده ميشود ) را بدانيم .
مثلا اگر مقدار مورد نظر در قطعه 0030h(h( يعني عدد در مبناي 16 است ) و آفست 13C4h
باشد ما بايد قطعه اي كه شماره آن 0030h است را بيابيم و بعد در همان قطعه
مقدار باين شماره 13C4 را بخوانيم .
براي نمايش اين حالت بين عدد سگمنت و آفست علامت (:) قرار ميدهيم . يعني
ابتدا عدد مربوط به قطعه را نوشته و سپس عدد آفست را مي آوريم :
Segment:Offset

مثال : 4D2F:َ9000 **
هميشه در آدرس دهي ها از اعداد مبناي 16 استفاده ميكنيم .

| | |

| 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 نشان ميدهند.
و ...

تمام رجيسترهاي فوق 16 بيتي (دوبايتي ) هستند و اعداد دوبايتي را نگهداري ميكنند.
ثباتهاي همه منظوره به دو نيم ثبات تك بايتي تقسيم ميشوند . بايت بالائي ب
نماد 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
خوب ، رجيسترها را ديديم و آشنائي كلي با آنها پيدا كرديم .
حالا ميخواهيم به رجيتسرها مقدار بدهيم و آنها را بخوانيم و ... . ما معمولا در
ےزبانهاي ديگر از علامت =(يا =ا:) براي مقدار دهي استفاده ميكنيم ولي در زبان
ےاسمبلي اين كار ممكن نيست . در عوض از دستورالعمل MOV كمك ميگيريم . با MOV
ميتوانيم داده ها را بين رجيسترها يا خانه هاي حافظه انتقال بدهيم . به اين صورت
MOV in_it/Value


در اينجا In_it به معناي يك رجيستر، نام يك متغير، يا آدرس يك مكان از حافظه
است و Value هم يك مقدار عددي يا حرفي ، نام يك رجيستر و ... ميباشد .
ےمانند MOV AX/200 كه عدد 200 دسيمال را به رجيستر AX منتقل ميكند . (هميشه از
سمت راست به چپ ) .

در زبان اسمبلي ما ميتوانيم با مبناهاي 2وَ10وَ16 كار كنيم . اعداد به طور پيش
فرض مبناي 10 هستند . براي نشان دادن عدد هگزا (مبناي 16) در انتهاي عدد يك
حرف H ( يا h ) و در انتهاي اعداد باينري علامت (b) قرار ميدهيم . مثلا براي نشان
دادن عدد AC1 مبناي 16 بايد حتما آن را بصورت AC1h بنويسيم . به همين ترتيب عدد110b
همان عدد 6 خودمان است .

با اين تفاسير براي دادن مقدار 4Ch به رجيستر AX از دستور زير استفاده ميكنيم :
mov ax/4Ch


به همين شكل ميتوانيم به نيم ثباتها هم مقدار بدهيم . مثلا ميتوانيم براي مقدار
دهي AH بنويسيم : mov ah/20h . در اين حالت مقدار نيم ثبات AL ثابت بوده و
محتواي AH برابر 20h ميشود . چون نيم ثباتها تك بايتي هستند ما نميتوانيم عدد
خارج از محدوده 0 تا 255 يا 128- تا 127 به آنها ارسال كنيم . در مورد اعداد منفي
هم بايد از طريق تبديل به مكمل دو عمل كنيم كه به زودي آن روش را توضيح خواهيم
اد .
مثلا ما نميتوانيم mov ah/100h را انجام دهيم چون 100h برابر 256 بوده و از محدوده
تعريف شده خارج است .
به همين شكل ميتوانيم محتواي ثباتها را هم منتقل كنيم . مثلا براي كپي كردن محتواي
ثبات 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


ےبه اين ترتيب مقدار 20hبه 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 را بزنيد تا اعلان (-) دوباره ظاهر
شود .
در سطر اول ما عدد 100h ( پيش فرض اعداد در Debug هگزا است ) را به AX منتقل
كرديم . بعد مقدار AXبه BX و سپس مقدار AXبه ES منتقل شده . به اين ترتيب همه
ثباتهاي AX/BX/ES بايد در نهايت برابر 100h باشند .
براي ديدن صحت اين مطلب دستور T ( به معناي Trace) را وارد كنيد .
با هر بار اجراي اين دستور يكي از سطرهاي برنامه اجرا ميشود . بعلاوه شما ميتوانيد
محتواي رجيسترها را هم ببينيد .
با اولين فرمان T ، سطر اول اجرا ميشود . بازهم فرمان T را وارد كنيد . الان مقدار100h
به BX داده شد و اگر به محتواي رجيستر AX توجه كنيد خواهيد ديد كه مقدار آن
(همانطور كه انتظار داشتيم ) برابر 100h است . دوبار ديگر هم فرمان T را صادر
كنيد و در نهايت مقدار ثباتهاي AX/BX/ES را ببينيد . هر سه ثبات (حالا) برابر 100h
هستند .
براي خارج شدن از Debug هم فرمان Q به معني Quit را وارد كنيد .
******


پس امروز ياد گرفتيم گه چطور مقادير و داده ها را بين ثباتها منتقل كنيم .
خودتان همين تمرينات را با DEBUG انجام داده و در مورد MOV مطالعه كنيد .
در قسمت بعد چيزهاي بيشتري رو خواهيم خواند و ياد خواهيم گرفت .
تا اينجا ياد گرفتيم كه چطور مقادير را بين ثباتها منتقل كنيم : با فرمان MOV.
با همين دستور ميتوانيم مقادير را از محلهاي حافظه خوانده يا در آنجا بنويسيم .
براي كار با حافظه دوحالت ممكن است وجود داشته باشد : 1
- آدرس مورد نظر در سگمنت جاري باشد . در برنامه هاي COM. كل برنامه (غالبا)
از يك سگمنت تشكيل ميشود . 2
- آدرس مورد نظر خارج از سگمنت جاري باشد .

ثبات DS هميشه به قطعه اي اشاره ميكند كه داده هاي مورد نياز برنامه در آن
هستند . اين قطعه در برنامه هاي EXE. يك قطعه مستقل است ولي در برنامه هاي COM
. ، قطعه داده هاي و قطعه كد برنامه در يك سگمنت هستند . بنا براين مقدار
ثبات DS در يك برنامه COM. ثابت است .
در حالت كلي آدرس يك محل از حافظه بصورت DS:address مشخص ميشود. DS حاوي
آدرس سگمنت داده ها بوده و address آفست را مشخص ميكند .
چون همانطور كه گفتيم DS در برنامه هاي COM. ثابت است ، پس در صورتي كه آدرس
مورد نظر در همين قطعه باشد از نوشتن DS صرفنظر ميكنيم .
به عنوان مثال اگر قطعه داده هاي برنامه ما 9000h باشد و ما بخواهيم آفست 24h
ام در همين قطعه را بدست بياوريم ، ميتوانيم از يكي از دو شكل زير استفاده
كنيم :
DS:24h
or
24h


البته چون اسمبلر منظور ما از نوشتن عدد 24h را نخواهد فهميد شكل دوم يك خطاي
هنگام ترجمه توليد خواهد كرد ولي ما روش صحيح را هم خواهيم گفت .
ما آدرس ها (يا اشاره گرها) را براي اين ميخواهيم كه بتوانيم به يك خانه از
حافظه دسترسي پيدا كنيم . براي اينكه نشان بدهيم منظور ما از عدد مشخص شده ،
آدرس است نه خود عدد (مثل 24h در مثال قبلي ) آن عدد را داخل [] قرار ميدهيم .
بنا براين :
mov ah/24h عدد 24h را به AX منتقل ميكند ولي ....
mov ah/[24h] محتواي آفست 24h را به AX منتقل ميكند .

در شكل دوم هر مقداري كه در آفست 24h ام سگمنت جاري موجود باشد به ثبات Ah
منتقل ميگردد.
به همين صورت ميتوانيم يك مقدار را به يك خانه از حافظه منتقل كنيم : mov [24h]/ah
: محتواي ثبات AH را به آفست 24h ام منتقل ميكند .
ے اگر آدرس مورد نظر خارج از محدوده سگمنت جاري بوده و در قطعه اي جدا قرار داشته
باشد ، ميتوانيم از DSيا ESا (ترجيحا) براي دستيابي به حافظه استفاده كرد:
مثال : mov ax/9000h
mov ds/ax
mov ah/ds:[89h]


به اين ترتيب ما به آفست 89h از سگمنت 9000h دسترسي پيدا ميكنيم .
البته دستورات فوق مارا به مقصودمان ميرسانند ولي ما نميتوانيم به دلخواه خودمان DS
را تغيير دهيم چون همانطور كه گفتيم DS به قطعه داده هاي برنامه اشاره ميكند و
برنامه ، داده ها و مقادير متغير ها را از سگمنتي كه با DS مشخص شده ميخواند .
بنا براين ما نبايد مقدار DS را تغيير بدهيم مگر اينكه آن را دوباره به حالت اول
برگردانيم . براي ذخيره و بازيابي محتواي رجيسترها، يك روش ساده و عمومي وجود
دارد كه به زودي خواهيم گفت ولي در اين مثال ما ميتوانستيم مقدار قبلي DS را در
يك رجيستر ديگر مثل CX نگهداريم :

انتقال محتواي dsبه AX mov ax/ds
انتقال محتواي AXبه CX mov cx/ax
دادن مقدار9000hبه AX mov ax/9000h
انتقال محتواي AXبه DS mov ds/ax
خواندن آدرس mov ah/ds:[89h]
بازيابي مقدار DS mov ax/cx mov ds/ax


اگر بخواهيم آفست آدرس را با يك رجيستر مشخص كنيم بايد به نكات زير توجه
كنيم : 1
- اگر آدرس سگمنت با DS مشخص شده ، يا آدرس در سگمنت جاري باشد ، بايد
مقدار آفست را در ثبات BX قرار دهيم . مثلا mov cx/[BX]يا mov cx/ds:[bx]ا .
2
- اگر از ES به عنوان مقدار سگمنت استفاده ميشود بايد از DI به عنوان آفست
استفاده كنيم مثل mov cx/es:[di] .

چون ما با برنامه هاي COM. سرو كار داريم ، پس از شكل اول و BX استفاده خواهيم
كرد .

Nesta
18-03-2005, 02:04
وقتي كه ما به روش گفته شده مقداري را از حافظه ميخوانيم ، يك داده تك بايتي
از حافظه گرفته ميشود . اما ممكن است بخواهيم كه يك كلمه يا كلمه
مضاعف ( 4بايتي ) را بخوانيم يا بنويسيم . در اين صورت ميتوانيم از
پيشوند هاي زير استفاده كنيم :
Byte Ptr
: براي دست يابي به يك بايت Word Ptr
: براي دستيابي به يك كلمه (2بايت ) Dword Ptr
: براي دست يابي به يك مقدار 4 بايتي

اين پيشوند ها را بايد قبل از آدرس مورد نظر قرار دهيم . به عنوان مثال براي
خواندن يك بايت از آفست 10h ميتوانيم بنويسيم : mov al/byte ptr ds:[10h]

و براي خواندن دو بايت بصورت : mov ax/byte ptr ds:[10h] .
ميتوانيم از همين روش استفاده كرده و مقداري را به حافظه انتقال دهيم . مثلا
ميخواهيم يك كلمه دوبايتي را به آفست 34h (در سگمنت برنامه ) منتقل كنيم . كافي
است بنويسيم :
جمع و تفريق


بحث ما در مورد روشهاي دستيابي و انتقال داده ها (فعلا) به پايان ميرسد . حالا
ميخواهيم ببينيم كه چطور عمل جمع و تفريق ، و بعدا ضرب و ... ، را روي مقادير
انجام دهيم .

دستورالعمل ADD به ميزان خواسته شده به محتواي يك رجيستر يا متغير اضافه ميكند .
ےمثلا ADD AH/20 عدد 20 را به AH اضافه كرده و مجددا در AH قرار ميدهد . اگر مقدار
فعلي AH برابر 30 باشد بعد از اجراي آن دستور برابر 50 ميشود .
بايد توجه كنيم كه حاصل بدست آمده از محدوده مجاز تجاوز نكند . در اين مثال اگر
حاصل جمع عدد 20 با محتواي AH بزرگتر از 255 باشد ، خطاي سرريز (Over Flow) رخ
ميدهد .
مثال : اين دستورات را در ديباگ وارد كنيد : mov ax/5
add ax/4
int 20


(به معني سطر آخر توجه نكنيد) . حالا يكبار ديگر Enter را بزنيد تا خط اعلان Debug
ظاهر شود . حرف G را بزنيد تا برنامه شما اجرا شود . حالا فرمان آشناي R را براي
ديدن محتواي رجيسترها وارد كنيد و مقدار AX را ببينيد .

دستورالعمل SUB برعكس ADD بوده و به مقدار خواسته شده از محتواي يك ثبات يا
متغير كم ميكند . مثلا SUB AX/100h به اندازه 256 (100h) از AX كم كرده و نتيجه را
دوباره در AX قرار ميدهد .

مثال : mov bbx/100h SUB bx/50


در اين مثال حاصل bx را از 100 به 50 كاهش داده ايم .
فرمان INC يك حالت خاص از ADD بوده و تنها يكواحد به محتواي ثبات اضافه ميكند
مثلا inc cx يعني يك واحد به cx اضافه كن .
و برعكس اين ، دستور dec يكواحد از محتواي ثبات كم ميكند . مانند : dec cx .
ے بايد توجه كنيم كه اين دستورات تنها روي ثباتهاي همه منظوره DX.AX.D قابل
استفاده هستند .

پس امروز مطالب مربوط به اينها رو ياد گرفتيم :
byte ptr / word ptr / dword ptr
add / sub / inc / dec

Nesta
18-03-2005, 02:05
وقفه ها (Interrupts) CPU
براي اينكه بتواند كارهاي مختلفي را انجام دهد،از وقفه ها استفاده ميكند . يك
ےوقفه درخواستي از CPU است كه در طي آن زير برنامه اي اجرا ميشود. وقتي كه وقفه
فراخواني ميشود، CPU اعمال ديگر را متوقف كرده و آن اينتراپت را پردازش ميكند
به طور كلي وقفه ها به دودسته تقسيم ميشوند:
ےَ1- وقفه هاي سخت افزاري (Hardware Interrupts) . وقفه هائي هستند كه از سوي
ے ادوات سخت افزاري كامپيوتر مانند كيبورد و ... اجرا ميشوند. مثلا با فشرده يارها
شدن هر كليد ، يكبار وقفه شماره 9 فراخواني ميشود. 2
- وقفه هاي سخت افزاري (SoftWare Interrupts). اين وقفه ها در بايوس (BIOS)
كامپيوتر قرار دارند. بايوس كامپيوتر يك تراشه (IC) قابل برنامه ريزي است كه
بنا بر نوع پردازنده بر روي برد اصلي كامپيوتر قرار ميگيرد . بعلاوه خود DOS
نيز وقفه اي (وقفه 21h) را اداره ميكند كه به وقفه DOS معروف است . اين توابع
توسط MSDOS.SYS تعريف ميشوند ولي در نهايت به بايوس مراجعه ميكنند.
هر وقفه داراي يك شماره خاص خود است و از صفر شروع ميشود . وقفه 21h (سرويس DOS
) نيز داراي 255 سرويس ديگر است .
براي اينكه بتوانيم يك برنامه خوب و مفيد بنويسيم بايد بتوانيم از اينتراپتها
به نحو صحيح استفاده كنيم . پس هر برنامه نويس اسمبلي بايد يك مرجع كامل
اينتراپت در اختيار داشته باشد.
وقتي ميخواهيم يك وقفه را فراخواني كنيم ، ابتدا (درصورت لزوم ) ثباتهاي خاصي را
مقدار دهي ميكنيم . معمولا نيم ثبات AH ، از اين جهت كه اكثر اينتراپتها
داراي چند سرويس مختلف هستند ، شماره تابع را مشخص ميكند . بهمين صورت ، و
اگر لازم باشد ، ثباتهاي ديگر را هم مقدار دهي ميكنيم . مثلا فرض كنيد ميخواهيم
كليدي را از صفحه كليد بخوانيم . تابع شماره 0 از وقفه 16h ميتواند اين كار را
انجام دهد . وقتي ميگوئيم تابع شماره 0 ، يعني بايد به AH مقدار 0 بدهيم و بعد
اينتراپت 16h را فراخواني كنيم .
فراخواني اينتراپت به سادگي و با دستورالعمل INT انجام ميشود. به صورت :
INT int_no

كه int_no شماره اينتراپت ميباشد . در مورد اين مثال بايد دستورات زير را انجام
دهيم : mov ah/0
int 16h

وقتي يك وقفه فراخواني ميشود ، ممكن است روي ثباتها تاثير گذاشته و مقدار آنها
را عوض كند. به اين وسيله ما ميتوانيم وضعيت اجراي وقفه را بدست بياوريم . در
مورد اين مثال ، پس از خوانده شدن كليد ، كد اسكي (ASCII) كليد در ثبات AL قرار
ميگيرد . مثلا اگر حرف A تايپ شود ، مقدار AL برابر 65 خواهد بود.
حالا اگر عدد AH را قبل از فراخواني وقفه بجاي 1 برابر Eh قرار دهيم و وقفه 10hرا
اجرا كنيم ، بجاي خواندن كليد، يك كاراكتر را چاپ ميكند . به اين صورت كه كد
اسكي كاراكتر در ثبات AL و عدد Eh در ثبات AH قرار گرفته و وقفه 10h فراخواني
ميشود . mov AX/0E07h
in 10h

به سطر اول توجه كنيد !. وقتي ما يك عدد دوبايتي (Hex) را به AX ارسال ميكنيم ،
دوبايت بالا در AH و دوبايت پائين در AL قرار ميگيرد . پس در اين مثال كاراكتر
شماره 7 بايد چاپ شود و چون اين كد مربوط به كاراكتر Bell است ، صداي بيپ
شنيده خواهد شد.

خاتمه دادن به برنامه :
وقتي كه يك برنامه به انتها رسيد يا اگر خواستيم اجراي برنامه را متوقف
كنيم ، ميتوانيم از اينتراپت 20h استفاده كنيم . DOS هميشه و بمحض اجراي اين
وقفه ، اجراي برنامه را متوقه ميكند.
اينراپت 20h فقط با برنامه هاي COM. درست كار ميكند و در مورد برنامه هاي EXE.
درست جواب نميدهد . در عوض سرويس 4Ch از اينتراپت 21h در هر دونوع برنامه
بخوبي كار ميكند .
خوب ، حالا با مطالبي كه ياد گرفتيم يك برنامه اسمبلي نوشته و فايل 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 براي مقدار دهي استفاده ميكنيم . (طول برنامه 8
بايت است ) . - RCX
8

و در نهايت فرمان w براي نوشتن روي ديسك و Q براي خروج . حالا ما يك فايل COM.
داريم كه به محض اجرا يك صداي Beep توليد ميكند .

ما امروز اولين برنامه اسمبلي خودمان را نوشتيم ، در قسمت بعد ياد ميگيريم كه
چطور از اسمبلر استفاده كنيم و امكانات آن را بكار ببريم .

Nesta
18-03-2005, 02:07
در اين قسمت طرز استفاده از ماكرواسمبلر را ياد ميگيريم و برنامه هايمان را بدون
استفاده از Debug مينويسيم .
براي استفاده از اسمبلر بايد يك اديتور اسكي مثل EDITيا PE2ا داشته باشيد تا
بتوانيد برنامه هايتان را توسط آن تايپ كنيد . هر برنامه اسمبلي داراي يك فايل
منبع (Source) حاوي دستورالعملهاي اسمبلي است . ما اين فايل را با يك ويرايشگر
تايپ كرده و به ماكرواسمبلر MASM.EXE ميدهيم تا فايل مفعولي (OBJ.) آن را بسازد
. اين فايل هم بايد با برنامه Link.exe به فرم EXE. تبديل شود . چون ما ميخواهيم
برنامه هاي COM. بتويسيم بايد فايل exe. توليد شده را با EXE2BIN.COMيا EXE2COMا
به فرم com. تبديل كنيم .
فرض كنيد در محيط ويرايشگر(مثلا EDIT ) هستيم و ميخواهيم يك برنامه اسمبلي
بنويسيم .
هر برنامه از 3 قطعه (سگمنت ) تشكيل ميشود : 1
-قطعه داده ها يا DATA SEGMENT . متغيرهاي برنامه و ساير داده هاي مورد نياز در
اين سگمن قرار ميگيرند . 2
- قطعه كد يا Code Segment . كدها و دستورات اسمبلي در اين قسمت هستند . 3
- بخش انباره يا Stack Segment . اين قطعه زير برنامه ها و مقادير موقتي را
نگهداري ميكند . ما حتي ميتوانيم محتواي ثباتها را به پشته (Stack) منتقل كرده و
بعد دوباره از آن خارج كنيم .
در يك برنامه COM. قطعه داده ها و قطعه مد در يك سگمنت قرار دارند بنا براين
ما قطعه داده ها را تعريف نميكنيم . بعلاوه قطعه سگمنت هم براي يك فايل COM.
وجود ندارد بلكه خود DOS اين محيط را فراهم ميكند . به همين دلايل است كه نوشتن
برنامه هاي COM. آسانتر است . با اين حال ما با محدوديتي مواجه هستيم و آن
اينست كه سايز يك برنامه COM. نميتواند بيش از 64 كيلو بايت باشد .
فرض كنيد ميخواهيم همان برنامه اي كه صداي 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 هميشه يك فضاي 256 بايتي
بنام PSP در ابتداي برنامه ايجاد كرده و اطلاعات فوق العاده مهمي را در آن
نگهداري ميكند . بنا براين برنامه ما بايد حتما از آدرس 100h شروع شود . اين
قانون اسمبلر براي نوشتن برنامه هاي COM. است . پس كد برنامه را به شكل زير
اصلاح كنيد :

. MODEL SMALL
. CODE

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


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

امروز برنامه اي با اسمبلر نوشيتم . از اين پس نيز تمام برنامه هاي را با
اسمبلر مينويسيم و از توانائيهاي آن استفاده ميكنيم .

Nesta
18-03-2005, 02:08
پرشهاي غير شرطي

ے اگر با زبانهائي مثل Basicيا Pascalا برنامه نويسي كرده باشيد حتما از دستور Goto
ے هم استفاده كرده ايد . بوسيله اين فرمان ، ما ميتوانستيم روال اجراي برنامه را به
يك نقطه مشخص انتقال بدهيم بدون اينكه نياز به برقراري شرط خاصي باشد .
در زبان اسمبلي هم چنين دستوري داريم : دستورالعمل JMP (مخفف JUMP) .
دستور JMP به اين شكل استفاده ميشود:
برچسب JMP
ے منظور از برچسب مكاني از برنامه است . در اسمبلي براي اينكه يك نقطه از برنامه
ے را علامت بزنيم ، نام برچسب مورد نظر را مينويسيم و براي اينكه اسمبلر آن را با
ے يك دستورالعمل اجرائي اشتباه نكند، كاراكتر (:) را در مقابل آن قرار ميدهيم
مانند: :Start
سپس ميتوانيم با دستور JMP به آنحا پرش كنيم : Start :
:
:
Jmp Start


دقت كنيد كه بعد از Start ي كه در مقابل JMP نوشتيم علامت : قرار نداده ايم .
ے اين JMP ها از نوع JUMP NERA هستند و نوعي ديگر بنام JUMP FAR هم داريم كه بزودي
آن را هم ياد ميگيريم .
ے مثال : برنامه اي كه در مثالهاي قبل نوشتيم را در نظر بگيريد . اگر ما از روي
دستورالعملهاي برنامه با JMP پرشي انجام دهيم هيچكدام از آن كدها اجرا نخواهندشد:


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


برنامه از روي سطرهاي 2وَ3 پرش خواهد كرد .

ثبات پرچم (Flags)


ے ثبات پرچم يك ثبات 16 بيتي است كه 1يا 0ا بودن بيتهاي آن نشانه درست يا
ے نادرست بودن يك شرط است . مثلا اگر با دستورالعمل خاصي (ميخوانيم ) تست كنيم
كه آيا ثبات BX مقدار 0 را دارد ، در اين صورت بيت 6 برابر 0 ميشود و ... .
از اين 16 بيت فقط 9 بيت استفاده ميشود كه به شرح زير هستند :
16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
* * * * * O D I T S Z * A * P * C

علامت * به معناي بي استفاده بودن است .

ے 1- پرچم نقلي يا (CF) . بيت 0 در نتيجه اجراي وقفه ها يا بعضي اعمال حسابي تغيير
ميكند .
ے 2-پرچم توازن (ZF) . بر اساس يك عمل مقايسه اي يا حسابي تغيير ميكند . اگر
نتيجه يك عبارت 0 باشد مقدار 1 و اگر نتيجه 1 باشد مقدار 0 ميگيرد.
ے 3-پرچم وقفه (IF) . اگر 0 باشد هيچ وقفه اي نميتواند اجرا شود و اگر 1 باشد
ميتوان وقفه ها را فراخواني كرد .

ے و ... . 6 پرچم ديگر را فعلا لازم نداريم بنا براين توضيحي براي آنها ارائه
نميكنيم .

دستور مقايسه اي CMP


ے براي مقايسه مقاديراز دستور CMP (مخفف CoMPare) استفاده ميكينم . اين دستور
ے مقدار داخل يك ثبات يا متغير را با مقداري ديگر مقايسه كره و روي ثبات هاي
ے CFو ZFو تاثير ميگذارد . بعد از مقايسه ميتوانيم بر حسب وضعيت پرچمها پرش
لازم را انجام دهيم .

ے مثلا CMP BX/0 تست ميكند كه آيا مقدار BX برابر 0 است يا نه . در صورتي كه برابر
0
باشد ،پرچم ZF برابر 1 ميشود .
با همين دستور CMP ميتوانيم كوچكتر،بزرگتر و .... را هم تست كنيم .

پرشهاي شرطي


براي پرشهاي شرطي از دستورهاي زير درست مثل JMP استفاده ميكنيم .
ے JE/JZ : اگر محتواي ZF صفر باشد جهش ميكند . اگر دو مقداري كه مقايسه كرده ايم
برابر باشيند پرش انجام ميشود.
ے JNE/JNZ : برعكس JZو JEو هستند و اگر ZF يك باشد (بعبارتي دو مقداري كه مقايسه
كرديم برابر نباشند) جهش انجام ميشود.
ے JA/JNBE . اگر محتواي ثبات يا متغيري كه مقايسه كرده ايم بزرگتر از عدد مورد نظر
باشد پرش انجام ميدهد . مثلا :


mov bh/1
cmp bh/10
ja Dest



ے مقدار BH برابر 1 است و در سطر دوم تست آن را با 10 مقايسه ميكنيم . در سطر سوم
چون BH بزرگتر از 1 نيست ، پس پرش JA Dest انجام نميشود .

JAE/JNB . اگر بزرگتر يا مساوي باشد ، پرش انجام ميشود.
JB/JNAE: در صورتي كه كوچكتر باشد پرش انجام ميشود.
JBE : در صورتي كه كوچكتر يا مساوي باشد پرش انجام ميشود .

مثال :
ے ميخواهيم برنامه اي بنويسيم كه تمام كاراكترهاي بين 128 تا 255 را
چاپ كند.


. MODEL SMALL
. CODE
ORG 100H
START :

كاراكتر 128 براي شروع ; MOV CH/128 CHARS :

كداسكي را درAL قرار ميدهيم تا چاپ شود ; MOV AL/CH
سرويس 0Eh براي چاپ كاراكتر ; MOV AH/0EH
اينتراپت 10h ; INT 10H
يكواحد به CH اضافه كن ; INC CH
مقايسه CH با 255 ; CMP CH/255
اگر مساوي نباشد به CHARS پرش ميكند ; JNZ CHARS
پايان ; INT 20H END START



ے تمام برنامه ساده و روشن است ولي در سطر آخر نكته جديدي وجود دارد . بعد از
ے END نام برچسب Start را آورده ايم . نكته اي كه در نوشتن برنامه هاي اسمبلي بايد
ے مراعات كنيم اينست كه : اگر از برچسبي در برنامه استفاده ميكنيم ، اسمبلر بايد
ے يك برچسب را به عنوان نقطه آغاز كدبرنامه ببيند . به همين خاطر علاوه بر برچسب
ے CHARS يك برچسب بنام Start هم در ابتداي برنامه تعريف كرده و براي اينكه
ے اسمبلر بداند مما كدام برچسب را براي انيكار اينخاب كرده ايم ، نام آن را در
مقابل END مي آوريم ، يعني END START .

ے نكته ديگر اينكه ، در اسمبلر هر چيزي كه بعد از كاراكتر (;) باشد ، توضيح
ے (Comment) فرض شده و اصلا ترجمه نميشود . (مثل REM در بيسيك و .. ) . هر comment
ے بايد در يك سطر جاي داده شود و اگر از اين مقدار بيشتر بود ميتوانيم در سطر بعد
هم يك كاراكتر (;) درج كرده و ادامه توضيحات را بعد از آن بياوريم .

Nesta
18-03-2005, 02:09
اگر داراي كارت ويدئوي VGA و طبعا VGA BIOS باشيد ، ميتوانيد از تابع 10H مربوط
به اينتراپت 10h (كه مربوط به سرويسهاي تصويري است ) براي تعريف پالت هاي جديد
استفاده كنيد . تعداد رنگهائي كه ميتوان آنها را تغيير داد به نوع كارت گرافيك
و VGA BIOS مربوط است و در حالت عادي رنگهاي شماره 0تا 7ا قابل تعريف هستند .
پالت رنگ را به اين صورت بايد تعريف كنيم : AH=10H
AL=10H

شماره رنگ از 0تا BX= 7ا
عدد رنگ سبز CH=
عدد رنگ آبي CL=
عددرنگ قرمز DH=

رنگهائي كه ديده ميشود ، تركيبي از سه رنگ اصلي قرمز،سبز و آبي (RGB) هستند .
براي تعريف يك رنگ جديد نيز بايد مقدار هر رنگ اصلي در پالت مورد نظر را در
نيم ثباتهاي CH/CLو DHوC قرار دهيم . اين مقادير 6 بيتي و در محدوده 1 تا 63
هستند .
پس از مقداردهي ثباتها اينتراپت 10h را فراخواني ميكنيم . در مثال زير ما رنگ
شماره 1 كه آبي ميباشد را تغيير داده ايم .

تمرين : برنامه را براي رنگهاي شماره 2تا 7ا نيز با مقادير دلخواه تكميل كنيد


. 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و را به ترتيب برابر 1Ah و 00h قرار داده و اينتراپت 10h را اجرا ميكنيم
اگر بعد از فراخواني وقفه ، AL برابر 1Ah بود يعني كارت 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



در برنامه بالا اگر بعد از اجراي وقفه 10h مقدار AL برابر 1Ah نباشد، نميتوانيم
از سرويس تعريف رنگ استفاده كرده و مجبوريم برنامه را با پرش به NOVGA خاتمه
دهيم .


در اين قسمت با نوشتن يك برنامه ، دو تابع مفيد از وقفه 10h را ياد گرفتيم و
ديديم كه نوشتن يك برنامه اسمبلي برخلاف آنچه تا بحال تصور ميكرديم چقدر ساده
و جالب است .

Nesta
18-03-2005, 02:11
سلام
تا اينجا برای مبتدی هست اگ پيشت رفت کردی بدن برو دنبال پيشرفته هاش
:wink:

Spy
18-03-2005, 14:30
كارلوس جان عالي بود خيلي عزيزي ..
واقعا ممنون :P

Nesta
18-03-2005, 19:24
سلام
آقا ما که کاری نکرديم اين به ماله تو در

:lol:

Hossein Adel
25-03-2005, 14:28
mamnon as hamegi :)

Nesta
26-03-2005, 04:16
سلم
حسين جان منظورت ممنون از من ديگه
:oops: :wink: :wink:

Emprof
07-04-2005, 23:34
به نام خدا
سلام
آقا یکی بگه حلا اگه من بخوام یه برنامه به زبان اسمبلی یا هرزبان دیگربنویسم باید توچه محیطی باشه. در واقع از چه نرم افزاری باید استفاده بکنم.

ali_yousefian19
27-04-2005, 00:51
سلام
آقا كارلوس خيلي عاليه .فقط يه ايراد داره كه سر فصلها بترتيب نيست
يا شايد سر فصلهاش با كتاب ما نميخونه :mrgreen:
اگه مطالبي مربوط به سر فصلهاي كه ميگم بكذاري خيلي ممنونت ميشم(البته الانم كه چاكريم)
تعيين اهداف هر سگمنت
تعريف داده در سگمنت داده ها
انواع عملوند ها و كارشون
تعريف دستورات مختلف(pop-poush-xchg...)
راستي نمونه سوال سراغ نداري :mrgreen:
من كه فكر كنم اين ترم از اين يكي هم بيفتم(به اميد خدا تو اين ترم ; روي هم رفته يه 4 واحدي پاس ميكنيم :lol: )

Nesta
27-04-2005, 02:18
سلام
دوسته عزيز من به در خواست شما که اين آموزش رو نزشتم من برايه کسه ديگه گذشتم و از کتابی هم که نيست اگه شما چيزی مد نظرت هست بگو من سی کنم که در مورد همون مبحث بگزرم که شما هم راضی باشی

ali_yousefian19
27-04-2005, 14:22
سلام
آقا كارلوس خيلي عاليه .فقط يه ايراد داره كه سر فصلها بترتيب نيست
يا شايد سر فصلهاش با كتاب ما نميخونه :mrgreen:
اگه مطالبي مربوط به سر فصلهاي كه ميگم بكذاري خيلي ممنونت ميشم(البته الانم كه چاكريم)
تعيين اهداف هر سگمنت
تعريف داده در سگمنت داده ها
انواع عملوند ها و كارشون
تعريف دستورات مختلف(pop-poush-xchg...)
راستي نمونه سوال سراغ نداري :mrgreen:
من كه فكر كنم اين ترم از اين يكي هم بيفتم(به اميد خدا تو اين ترم ; روي هم رفته يه 4 واحدي پاس ميكنيم :lol: )
سلام آقا كارلوس
فكر كنم اين بالا بطور كامل گفتم چه موضوعاتي را ميخوام :wink:
آخه كتابي را كه دارن به ما تدريس ميكنند(چي گفتيم) كتاب :برنامه نويسي به زبان اسمبلي نوشته جعفر نژاد قمي
راستي اگه نمونه سوال گير بياري كه ديگه خيلي توپ ميشه(شرمنده كه پر رو بازي در مي آوردم هااا
با تشكر

double_n
30-04-2005, 12:55
سلام
از عنوانم ميعلومه...
من الان نميدونم ثبات چيه رجيستر چيه؟
اگه يكسي يه كتاب اموزشي خوب ميشناسه(برا دانلود كه چه بهتر) ولي اگه يه كتاب واقعي :shock: هم بود اسمش رو بنويسيد ممنون ميشم

داداش از كتاب آموزشي اسمبلي نوشته پيتر ايبل استفاده كن ..... تا حدودي كمكت مي كنه . :wink: