پاسخ : اسمبلی یاد بگیریم حتما با ثابتها در زبانهائی مثل پاسکال آشنائی دارید . بعنوان مثال با جمله ے 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) را مطالعه میکنیم . با همین معلومات برنامه ای برای نمایش محتوای سکتوهای دیسک مینویسیم و پیغامهای لازم را به آن اضافه میکنیم
پاسخ : اسمبلی یاد بگیریم در این قسمت نحوه دسترسی به مقادیر متغیر ها را یاد میگیریم . ے وقتی که میخواهیم مقدار یک متغیر را به یک متغیر یا ثبات دیگر منتقل کنیم باید ے به اندازه آن توجه داشته باشیم . مثلا اگر متغیری بصورت 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 آدرس رشته کاراکتری DSX = INT 21H ے عبارت DSX نشان میدهد که مقدار قطعه (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
پاسخ : اسمبلی یاد بگیریم در این قسمت یک تمرین جدید انجام داده و بوسیله آن تجربیات خودمان را افزایش میدهیم . این تمرین اولین برنامه ما هست یک عملیات سطح پائین سیستم ، یعنی دسترسی به دیسک ، را انجام میدهد . تابع شماره ۲(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
پاسخ : اسمبلی یاد بگیریم عملگرهای بیتی عملگرهای بیتی مانند عملوندهای حسابی هستند با این تفاوت که روی بیت ها کار میکنند. این عملگرها عبارتند از : … 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 انتقال ادامه این بحث را در قسمت بعد انجام میدهیم .
پاسخ : اسمبلی یاد بگیریم در قسمت قبلی چند عملگر بیتی را دیدیم . در این قسمت هم این مبحص را دنبال میکنیم . عملگر 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