برای یاد گرفتن اسمبلی باید با مبناهای عدد نویسی ، ساختمان داخلی کامپیوتر
و برنامه نویسی آشنا باشیم .
ما برنامه هایمان را مستقیما با اسمبلر 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
بیایید یک برنامه بنویسیم
در این قسمت میخواهیم با استفاده از مطالبی که در بخشهای قبلی یاد گرفتیم
برنامه ای بنویسیم که کامل و قابل استفاده باشد .
با این برنامه میتوانیم
فلاپی دیسکهای خودمان را با سرعت کپی کنیم !
امروز برنامه را به شکلی مینویسیم که
بتواند دیسکهای 1.44 را بوسیله درایو A کپی کند .
بیشتر نیاز ما در کپی (تکثیر)
دیسکها هم به همین شکل هست .
با اینحال در قسمت بعدی نگارش (Version) جدیدتری از
برنامه را مینویسیم و قابلیت تشخیص نوع دیسک و قابلیت مشخص کردن درایو را به آن
اضافه میکنیم .
بهترین کاری که میتوانیم بکنیم اینست که بتوانیم داده های خوانده شده از
دیسک را در حافظه EMS بنویسیم (در این نسخه روی هارددیسک مینویسیم ) .
وقتی که
نحوه کار را حافظه گسترش یافته (Extended Memory) را هم یاد گرفتیم ، برنامه
خود را کامل کرده و از آن بعنوان اولین دستختمان در برنامه نویسی اسمبلی لذت
میبریم .
لیست برنامه در زیر قرار دارد و توضیحات برنامه را روی آن میبینیم
قبل از آن یاد آوری میکنم که هر دیسک HD َ1.44 دارای دو طرف و در هر طرف 80 شیار
(Track) بوده و هر شیار هم به 18 بخش بنام قطاع (Sector) تقسیم میشود .
برنامه
ما باید محتوای تمام این قطاعها را خوانده و در فایلی روی دیسک سخت ذخیره کند.
سپس همین داده ها را از فایل خوانده و مجددا روی دیسک جدید بنویسد.
در این قسمت میخواهیم با استفاده از مطالبی که در بخشهای قبلی یاد گرفتیم برنامه ای بنویسیم که کامل و قابل استفاده باشد .
با این برنامه میتوانیم فلاپی دیسکهای خودمان را با سرعت کپی کنیم !
امروز برنامه را به شکلی مینویسیم که بتواند دیسکهای 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 تمام (: خوب ، رجیسترها را دیدیم و آشنائی کلی با آنها پیدا کردیم .
حالا میخواهیم به رجیتسرها مقدار بدهیم و آنها را بخوانیم و ...
.
ما معمولا در ےزبانهای دیگر از علامت =(یا =ا:) برای مقدار دهی استفاده میکنیم ولی در زبان ےاسمبلی این کار ممکن نیست .
در عوض از دستورالعمل MOV کمک میگیریم .
با MOV میتوانیم داده ها را بین رجیسترها یا خانه های حافظه انتقال بدهیم .
به این صورت MOV in_it/Value در اینجا In_it به معنای یک رجیستر، نام یک متغیر، یا آدرس یک مکان از حافظه است و Value هم یک مقدار عددی یا حرفی ، نام یک رجیستر و ...
میباشد .
ےمانند MOV AX/200 که عدد 200 دسیمال را به رجیستر AX منتقل میکند .
(همیشه از سمت راست به چپ ) .
در زبان اسمبلی ما میتوانیم با مبناهای 2وَ10وَ16 کار کنیم .
اعداد به طور پیش فرض مبنای 10 هستند .
برای نشان دادن عدد هگزا (مبنای 16) در انتهای عدد یک حرف H ( یا h ) و در انتهای اعداد باینری علامت (b) قرار میدهیم .
مثلا برای نشان دادن عدد AC1 مبنای 16 باید حتما آن