آموزش اسمبلی از پایه تا پیشرفته - تالار گفتمان آذر فروم





دعوت به همکاری با آذر فروم

 

آموزش اسمبلی از پایه تا پیشرفته
زمان کنونی: 20-09-1395،04:15 ب.ظ
کاربران در حال بازدید این موضوع: 1 مهمان
نویسنده: Friga
آخرین ارسال: Friga
پاسخ: 17
بازدید: 888

 
 
رتبه موضوع:
  • 0 رای - 0 میانگین
  • 1
  • 2
  • 3
  • 4
  • 5

موضوع: آموزش اسمبلی از پایه تا پیشرفته
ارسال: #1
آموزش اسمبلی از پایه تا پیشرفته
پست‌ها: 11,943
تاریخ عضویت: 20 اردیبهشت 1390
اعتبار: 288
حالت من: Shad
بخش اول آموزش : 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
















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


=====ஜ۩۞۩ஜ=====

18-05-1391 12:25 ق.ظ
 
ارسال: #2
RE: آموزش اسمبلی از پایه تا پیشرفته
پست‌ها: 11,943
تاریخ عضویت: 20 اردیبهشت 1390
اعتبار: 288
حالت من: Shad
قدم دوم : با اطلاعاتی که تا کنون شروع کردیم به یاد گرفتن . از این به بعد باید این اطلاعا رو جلوه بدیم

در اين قسمت ميخواهيم با استفاده از مطالبي كه در بخشهاي قبلي ياد گرفتيم
برنامه اي بنويسيم كه كامل و قابل استفاده باشد . با اين برنامه ميتوانيم
فلاپي ديسكهاي خودمان را با سرعت كپي كنيم ! امروز برنامه را به شكلي مينويسيم كه
بتواند ديسكهاي 1.44 را بوسيله درايو A كپي كند . بيشتر نياز ما در كپي (تكثير)
ديسكها هم به همين شكل هست . با اينحال در قسمت بعدي نگارش (Version) جديدتري از
برنامه را مينويسيم و قابليت تشخيص نوع ديسك و قابليت مشخص كردن درايو را به آن
اضافه ميكنيم .
بهترين كاري كه ميتوانيم بكنيم اينست كه بتوانيم داده هاي خوانده شده از
ديسك را در حافظه EMS بنويسيم (در اين نسخه روي هاردديسك مينويسيم ) . وقتي كه
نحوه كار را حافظه گسترش يافته (Extended Memory) را هم ياد گرفتيم ، برنامه
خود را كامل كرده و از آن بعنوان اولين دستختمان در برنامه نويسي اسمبلي لذت
ميبريم .
ليست برنامه در زير قرار دارد و توضيحات برنامه را روي آن ميبينيم
قبل از آن ياد آوري ميكنم كه هر ديسك HD َ1.44 داراي دو طرف و در هر طرف 80 شيار
(Track) بوده و هر شيار هم به 18 بخش بنام قطاع (Sector) تقسيم ميشود . برنامه
ما بايد محتواي تمام اين قطاعها را خوانده و در فايلي روي ديسك سخت ذخيره كند.
سپس همين داده ها را از فايل خوانده و مجددا روي ديسك جديد بنويسد.



طول هر قطاع 512 بايت است EQU 512 SECTORSIZE
تعداد شيار ها 80 شيار (79- 0-) است EQU 79 MAXTRACK
هر ديسك دو طرف دارد EQU 2 NUMSIDES
تعداد سكتور در هر شيار 18 تا است EQU 118 SECTOR_PER_TRACK E
.MODEL SMALL
.CODE
ORG 100H
START:
JMP MAIN

بافر براي ذخيره (0)BUF DB SECTORSIZE*SECTOR_PER_TRACK DUP
داده ها . اندازه آن به اندازه بايتهاي يك شيار است
معرف رويه فعلي ديسك SIDE D DB 0
معرف تراك جاري TRACK DDB 0
هندل (مشخصه ) فايل HANDLE DW 0

اسم فايل براي دخيره موقت داده ها FILENAME DB 'C:TTEMP.$$$'/0

MSG1 DB 'ENTER A DISK INTO DRIVE A :THEN PRESS A KEY'/13/10/'$'
MSG2 DB 'ENTER A NEW DISK INTO DRIVE A :THEN PRESS A KEY'/13/10/'$'



رويه ReadTrack داده هاي يك شيار را بطور كامل ميخواند . براي خواندن يك شيار
كامل از Int 13h/Ah=02h استفاده كرده ايم . داده ها بعد از خوانده شدن در محلي
كه با ES:BX مشخص ميشود ذخيره ميشوند . (به مرجع اينتراپيتها مراجعه كنيد) قبلا
كار با اين وقفه را توضيح داده ايم (برنامه Boots.asm را ببينيد)


READTRACK PROC ;READ A TRACK
PUSH ES
MOV AX/DS
MOV ES/AX
LEA BX/BUF
MOV AH/2
MOV DL/0 ;DRIVE A:
MOV DH/SIDE
MOV CH/TRACK
MOV CL/1 ;THE 1st SECTOR
MOV AL/SECTOR_PER_TRACK
INT 13H
POP ES
RET
READTRACK ENDP



اين رويه داده هاي موجود در BUF را خوانده و در يك شيار كامل كه با متغير Track
مشخص ميشود مينويسد . براي اينكار از INT 13h/AH=03h استفاده شده است . آدرس
متغير BUF را بايد در ES:BX قرار بدهيم .


WRITETRACK PROC
LEA BX/BUF
PUSH ES
MOV AX/DS
MOV ES/AX


شماره تابع براي نوشتن MOV AH/03
تعداد سكتورها براي نوشتن MOV AL/SECTOR_PER_TRACK
شماره تراك MOV CH/TRACK
شماره سكتور شروع MOV CL/1
رويه ديسك (طرف ديسك ) MOV DH/SIDE
شماره درايو كه اينجا A است MOV DL/0 INT 13H
POP ES
RET
WRITETRACK ENDP


اين پروسيجر به اندازه يك تراك كامل از فايل خوانده و در متغير BUF قرار ميدهد
READFILE PROC
MOV BX/HANDLE

اندازه يك تراك MOV CX/SECTORSIZE*SECTOR_PER_TRACK
آدرس بافر براي ذخيره كه DS:DX است LEA DX/BUF MOV AH/3FH
INT 21H
RET
READFILE ENDP


اين پروسيجر كليه داده هاي داخل BUF كه به اندازه يك تراك كامل (18*512 بايت )
است را خوانده و در فايل مينويسد تا بعدا مجددا خوانده و روي ديسك جديد بنويسد


WRITEFILE PROC
MOV BX/HANDLE
MOV CX/SECTORSIZE*SECTOR_PER_TRACK
LEA DX/BUF
MOV AH/40H
INT 21H
RET
WRITEFILE ENDP



منتظر ميماند تا كليدي فشرده شود WAIT PPROC
تابع خواندن كليد MOV AH/0 INT 16H
RET
WAIT _ENDP



اين رويه فايل با هندل مشخص شده را ميبندد CLOSEFILE PROC MOV AH/3EH
MOV BX/HANDLE
INT 21H
RET
CLOSEFILE ENDP


شروع برنامه اصلي . MAIN:

در اين قسمت اعذم ميكنيم كه ديسكي را در درايو A قرار دهده و كليدي را
برنند . MOV AH/9
LEA DX/MSG1
INT 21H

مكث براي دريافت كليد _CALL WAIT

ساختن فايل براي ذخيره داده ها MOV AH/3CH
LEA DX/FILENAME
MOV CX/0
INT 21H

MOV SIDE/0
MOV HANDLE/AX
MOV TRACK/1

موتور ديسك خوان مدت زماني لازم دارد تا به سرعت كافي برسد . بنا براين بايد
يك يا دو بار قبل از خواندن ديسك ، تابع خواندن را اجرا كنيم تا موتور ديسك در
حالت مناسب قرار بگيرد.
CALL READTRACK ; START UP THE CASSETTE-MOTOR
COPY:
MOV TRACK/0
COPYTRACK:

خواندن شيار CALL READTRACK
نوشتن داده هاي خوانده شده در ديسك CALL WRITEFILE
شيار بعدي INC TRACK
آيا شيار80 هستيم / CMP TRACK/80
نه ، شيار بعدي TRACKS َ; COPY 80 JNZ COPYTRACK

طرف بعدي ديسك INC SIDE
آيا طرف دوم ديسك هستيم ? CMP SIDE/1
نه ، پس ادامه بده JZ COPY
وگر نه فايل را ببند CALL CLOSEFILE

حالا اعلام ميكنيم كه ديسك جديد را در درايو A قرار دهد و كليدي را بزند MOV AH/09H
LEA DX/MSG2
INT 21H
CALL WAIT_

MOV SIDE/0


همان فايل را براي خواندن باز ميكنيم . وقتي كه فايلي را ميسازيم تنها ميتوانيم
در آن فايل بنويسيم . بنا براين براي خواندن از فايل ، بايد آن را بسته و مجددا
براي خواندن باز كنيم . LEA DX/FILENAME
MOV AH/3DH
MOV AL/0
INT 21H

مشخصه فايل در Handle قرار ميگيرد MOV HANDLE/AX

MOV TRACK/1
MOV SIDE/0

اجراي تابع نوشتن براي راه اندازي موتور ديسك CALL WRITETRACK
WRITE:
MOV TRACK/0
WRITE_ON_TRACK:

داده هارا از فايل بخوان CALL READFILE
داده ها را روي شيار بنويس CALL WRITETRACK
شيار بعدي INC TRACK
آيا شيار 80 هستيم ? CMP TRACK/80
نه ، پس ادامه بده JNZ WRITE_ON_TRACK
بله ، طرف بعدي ديسك INC SIDE
آيا الان طرف دوم را هم خوانده ايم ? CMP SIDE/1
نه ، پس شيار بعدي را بنويس JZ WRITE
بله ، فايل را ببند CALL CLOSEFILE

فايلي كه ساخته بوديم فضائي از ديسك سخت را اشغال كرده ، بنا براين بهتر است
آن را با استفاده از وقفه 21h و تابع 3Ah حذف كنيم . LEA DX/FILENAME
MOV AH/3AH
INT 21H ;ERASE THE TEMPORARY FILE
INT 20H
END START
















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


=====ஜ۩۞۩ஜ=====

18-05-1391 12:25 ق.ظ
 
ارسال: #3
RE: آموزش اسمبلی از پایه تا پیشرفته
پست‌ها: 11,943
تاریخ عضویت: 20 اردیبهشت 1390
اعتبار: 288
حالت من: Shad
خوب ، رجيسترها را ديديم و آشنائي كلي با آنها پيدا كرديم .
حالا ميخواهيم به رجيتسرها مقدار بدهيم و آنها را بخوانيم و ... . ما معمولا در
ےزبانهاي ديگر از علامت =(يا =ابراي مقدار دهي استفاده ميكنيم ولي در زبان
ےاسمبلي اين كار ممكن نيست . در عوض از دستورالعمل 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 مطالعه كنيد .
در قسمت بعد چيزهاي بيشتري رو خواهيم خواند و ياد خواهيم گرفت .
















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


=====ஜ۩۞۩ஜ=====

18-05-1391 12:25 ق.ظ
 
ارسال: #4
RE: آموزش اسمبلی از پایه تا پیشرفته
پست‌ها: 11,943
تاریخ عضویت: 20 اردیبهشت 1390
اعتبار: 288
حالت من: Shad
تا اينجا ياد گرفتيم كه چطور مقادير را بين ثباتها منتقل كنيم : با فرمان 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 استفاده خواهيم
كرد .
دستيابي به مكانهاي حافظه نكته هاي جالب ديگري هم دارد كه در قسمت بعدي ياد
خواهيم گرفت .
















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


=====ஜ۩۞۩ஜ=====

18-05-1391 12:26 ق.ظ
 
ارسال: #5
RE: آموزش اسمبلی از پایه تا پیشرفته
پست‌ها: 11,943
تاریخ عضویت: 20 اردیبهشت 1390
اعتبار: 288
حالت من: Shad
وقتي كه ما به روش گفته شده مقداري را از حافظه ميخوانيم ، يك داده تك بايتي
از حافظه گرفته ميشود . اما ممكن است بخواهيم كه يك كلمه يا كلمه
مضاعف ( 4بايتي ) را بخوانيم يا بنويسيم . در اين صورت ميتوانيم از
پيشوند هاي زير استفاده كنيم :
Byte Ptr
: براي دست يابي به يك بايت Word Ptr
: براي دستيابي به يك كلمه (2بايت ) Dword Ptr
: براي دست يابي به يك مقدار 4 بايتي

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

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

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 عدد 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 قابل
استفاده هستند .
















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


=====ஜ۩۞۩ஜ=====

18-05-1391 12:26 ق.ظ
 
ارسال: #6
RE: آموزش اسمبلی از پایه تا پیشرفته
پست‌ها: 11,943
تاریخ عضویت: 20 اردیبهشت 1390
اعتبار: 288
حالت من: Shad
وقفه ها (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 توليد ميكند .

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
















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


=====ஜ۩۞۩ஜ=====

18-05-1391 12:26 ق.ظ
 
ارسال: #7
RE: آموزش اسمبلی از پایه تا پیشرفته
پست‌ها: 11,943
تاریخ عضویت: 20 اردیبهشت 1390
اعتبار: 288
حالت من: Shad
در اين قسمت طرز استفاده از ماكرواسمبلر را ياد ميگيريم و برنامه هايمان را بدون
استفاده از 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
را اجرا كرده و نتيجه را مشاهده كنيد %

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
















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


=====ஜ۩۞۩ஜ=====

18-05-1391 12:27 ق.ظ
 
ارسال: #8
RE: آموزش اسمبلی از پایه تا پیشرفته
پست‌ها: 11,943
تاریخ عضویت: 20 اردیبهشت 1390
اعتبار: 288
حالت من: Shad
پرشهاي غير شرطي

ے اگر با زبانهائي مثل 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
ے بايد در يك سطر جاي داده شود و اگر از اين مقدار بيشتر بود ميتوانيم در سطر بعد
هم يك كاراكتر ( درج كرده و ادامه توضيحات را بعد از آن بياوريم .
















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


=====ஜ۩۞۩ஜ=====

18-05-1391 12:27 ق.ظ
 
ارسال: #9
RE: آموزش اسمبلی از پایه تا پیشرفته
پست‌ها: 11,943
تاریخ عضویت: 20 اردیبهشت 1390
اعتبار: 288
حالت من: Shad
برنامه اي براي تعريف رنگهاي جديد!
اگر داراي كارت ويدئوي 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 را ياد گرفتيم و
ديديم كه نوشتن يك برنامه اسمبلي برخلاف آنچه تا بحال تصور ميكرديم چقدر ساده
و جالب است .
















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


=====ஜ۩۞۩ஜ=====

18-05-1391 12:27 ق.ظ
 
ارسال: #10
RE: آموزش اسمبلی از پایه تا پیشرفته
پست‌ها: 11,943
تاریخ عضویت: 20 اردیبهشت 1390
اعتبار: 288
حالت من: Shad
دستورالعمل LOOP

تا اينجا هروقت كه ميخواستيم يك حلقه ايجاد كنيم از دستورالعمل CMP و پرشهاي
شرطي استفاده ميكرديم . راه ساده تري براي اجراي مكرر دستورالملها وجود دارد و آن
استفاده از LOOP است . دستور LOOP به تعداد دفعاتي كه با ثبات CX مشخص ميكنيم
حلقه اي را ايجاد ميكند .
براي ايجاد چنين حالتي ابتدا مقدار لازم را در ثبات CX قرار ميدهيم . دستور Loop
هميشه از مقدار CX يك واحد،يك واحد كم ميكند تا به 0 برسد . وقتي كه مقدار CX
برابر 0 شد ، از حلقه خارج ميشود . بنا براين براي ايجاد حلقه اي كه 100 بار
تكرار شود، CX را برابر 100 قرار ميدهيم . MOV CX/100


حلقه مورد نظر بين دستور loop و يك برچسب انجام ميشود . برچسب ، در ابتداي حلقه
و دستور loop در انتهاي آن قرار ميگيرد.


MOV CX/100
LPCNT :
:
:
:
LOOP LPCNT



در بالا با mov cx/100 ميخواهيم حلقه اي داشته باشيم كه 100 بار انجام بشود. وقتي
به loop lpcnt ميرسيم ، يكواحد از مقدار CX كاسته شده و مجددا به lpcnt پرش
ميكنيم .
به همين سادگي ! .
تمرين : برنامه اي بنويسيد كه كاراكتر هاي با كد اسكي 32 تا 255 را نمايش دهد.

راهنمائي :
چون هميشه CX در حال كاهش است ، براي اينكه بتوانيم از 32 تا 255 برويم ، بايد
عدد داخل CX را برابر 31=224-َ255 قرار بدهيم تا تعداد 254 حرف چاپ بشود. سپس
مقدار CX را از 255 كم كرده و داخل AL قرار ميدهيم تا با تابع 0Eh ار وقفه 10h
چاپ شود . اين وقفه را در برنامه 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


مثال :


1] 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



در سطر 3وَ4 مقادير axو bxو را به پشته ميفرستيم . در سطر 5و 6و مقادير جديد به ax
bx/ ميدهيم و بعد از آن يكسري دستورات ديگر هستند ... . در سطر 10 مقدار BX
از داخل Stack بيرون كشيده ميشود . توجه داشته باشيد كه bx را بعد از ax در پشته
قرار داده ايم ولي در هنگام خارج كردن به ترتيب عكس عمل ميكنيم .

بعلاوه دستور PUSHF به معني PUSH FLAGS ، ثبات پرچم را در پشته قرار ميدهد . نحوه
كار با آن هم مثل PUSH معمولي است ولي آرگومان ندارد .
مثال : PUSHF


در قسمت بعد، اين مطالب را تمرين ميكنيم و چند برنامه نمونه ميبينيم ، حتي
يك برنامه گرافيكي با 256 رنگ مينويسيم و در آن از دستورات LOOPو PUSHو
استفاده ميكنيم .
















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


=====ஜ۩۞۩ஜ=====

18-05-1391 12:27 ق.ظ
 


[-]
پاسخ سریع
پیام
پاسخ خود را برای این پیام در اینجا بنویسید.


کد تصویری
royalfuns
(غیر حساس به بزرگی و کوچکی حروف)
لطفاً کد نشان داده شده در تصویر را وارد نمایید. این اقدام جهت جلوگیری از ارسال‌های خودکار ضروری می‌باشد.

موضوعات مشابه ...
موضوع: نویسنده پاسخ: بازدید: آخرین ارسال
  دانلود فیلم آموزشی زبان اسمبلی Friga 0 281 18-05-1391 12:23 ق.ظ
آخرین ارسال: Friga
  اسمبلی movs Friga 0 234 18-05-1391 12:17 ق.ظ
آخرین ارسال: Friga
  آموزش زبان برنامه نويسي Assembly Friga 0 227 21-04-1391 01:03 ق.ظ
آخرین ارسال: Friga
  برنامه هایی با اسمبلی Friga 1 356 21-04-1391 12:54 ق.ظ
آخرین ارسال: Friga

پرش به انجمن:


کاربران در حال بازدید این موضوع: 1 مهمان