پیوند و اجرای اسمبلی

saalek110

Well-Known Member
در ادامه بايد گفت كه اين كه يك بيت روشن باشه يا خاموش معنايي ندارد.
و بستگي داره كه كسي كه اين بيتها را پر كرده به چه منظوري پر كرده. تا كسي كه مي خواهد آنها را بخواند بداند با چه قانوني آنها را بخواند.

مثلا فرض كنيد شما در قسمتي از ram دستورات اسمبلي بنويسيد و بعد با تنظيم cs:ip كنترل cpu را به آنجا منتقل كنيد. كه cs شماره قطعه است و ip شماره حافظه داخل اين قطعه.
كار cpu اينه كه هر بايت را يك دستور در نظر مي گيرد. اوفقط دستور اسمبلي مي شناسد و لاغير.

مثال بعدي اون وقفه اي بود كه با تنظيم قطعه داده و آدرس حافظه مي فرستاديمش براي خواندن رشته.
اون وقفه فقط كاراكتر مي شناسد. هر بايت را يك كاراكتر مي داند و تا علامت $ به خواندن ادامه مي دهد.

و cpu هر بايت را يك دستور مي شناسد. البته اگر ببينه هنوزدستور نشده بايت دوم و سوم و ... را هم بهش اضافه مي كنه تا بشه يك دستور.

مثال سوم وقتي است كه مثلا يك عكس داخل حافظه باشه. اينجا نه ديگه بيتها دستورند نه كاراكتر.
كلا هر بيت يا بايت هر چيزي مي تواند باشد.

= == == = = = = == = =
با اين مقدمه به وقفه اي مي پردازيم كه يك بايت را يك كاراكتر مي شناسد.
و قبلا با اون كار كرده ايم. سرويس 2 وقفه 21 است. كه محتواي dl را كه يك بايت است را يك كاراكتر مي شناسد.
 

saalek110

Well-Known Member
اين برنامه ساده را نگاه كنيد:
کد:
.model small

.stack
.code

chaap proc

mov ah,2h
mov dl,41h
int 21h

mov ah,4ch
int 21h

chaap endp

        end chaap

سه خط اصليش اينهاست:
mov ah,2h
mov dl,41h
int 21h
كه خط اول شماره سرويس را تنظيم مي كند و خط دوم dl را پر مي كند و خط آخر احضار وقفه 21 .

نتيجه اجرا هم چاپ حرف A .

حالا چطوري مي خواهيم از آن براي چاپ بايت استفاده كنيم. به جاي 41 در dl بگذاريد 5 . و اجرا مي كنيم.
در اجرا علامت خاج پاسور چاپ ميشه. اگر پاسور باز نيستيد ، همان شبدر 3 پر.
وقتي به جدول اسكي مراجعه كنيد كه ته هر كتاب برنامه نويسي هست.(اگر نبود سرچ كنيد پيدا كنيد.) مي بينيد كه خوب درسته يعني عدد 5 كاراكترش ميشه خاج. حالا در همان ستوني كه خاج داخلش هست در جدول اسكي برويد پايين تا به عدد 5 برسيد. آفرين. مي بينيد كه عدد 5 شماره اش در جدول 35 است.
پس مال خاج 5 بود.
مال 5 هم 35 بود.

پس اگر ما dl را 30 تا اضافه كنيم 5 چاپ ميشه.
يعني اين طوري:
mov ah,2h
mov dl,35h
int 21h

كه كاراكتر 5 را چاپ مي كنه.

هدف ما اين بود كه al را چاپ كنيم. فرض كنيد در al 5 بود.
آن را 30 تا اضافه كنيد و در dl قرار دهيد و وقفه را احضار كنيد تا 5 چاپ بشه.
پس فهميديم برنامه را چطور بسازيم.
در پست بعدي به اين مي پردازيم كه اگر al به جاي 5 اگر 55 بود چي . و اگر ef بود چي. و از اين بحثها كه ديگه راحته. ستون فقرات كار همين بود كه اينجا گفته شد.
 

saalek110

Well-Known Member
اولا بگويم در جدول اسكي من 3 ستون دارد . ستون اول شماره در مبناي 10 . ستون دوم شماره در مبناي 16 و ستون سوم كاراكتر.
و اين كه گفتم كاراكتر 5 شماره اش 35 است از ستون 2 بود يعني در مبناي 16 . كه ستون اولش 53 بود.

حالا ، برنامه:
برنامه زير ، برنامه آماده شده براي چاپ al است. البته بايد al رقم سمت چپش صفر باشد و رقم سمت راستش هم صفر تا 9 باشد.
کد:
.model small

.stack
.code

chaap proc

mov al,08h
mov ah,2h
mov dl,al
add dl,30h
int 21h

mov ah,4ch
int 21h

chaap endp

        end chaap

شرح برنامه:
اول در al رقم 08 گذاشته شده.
بعد سرويس تعيين شده(mov ah,2h )
بعد al را در dl گذاشته ايم.
بعد dl را 30h افزوده ايم.(30 در مبناي 16 كه ميشه 3 ضرب در 16 در مبناي 10 )
بعد وقفه را فراخوانده ايم.

نتيجه اجرا :
8

برنامه بعدي برتري اش اينه كه اگر در رقم سمت راست al به جاي صفر تا 9 ، حروف a تا f هم بود مي تواند پوشش دهد.
کد:
.model small

.stack
.code

chaap proc

mov al,0eh
mov ah,2h
mov dl,al
add dl,30h

cmp dl,3ah
jl lable1

add dl,07h

lable1:
int 21h

mov ah,4ch
int 21h

chaap endp

        end chaap

شرح برنامه:
اگر به جدول اسكي نگاه كنيد مي فهميد كه چرا باز 7 تا اضافه شده. چون اگر مثلا e بخواهد به شماره مناسب خود برسد به جاي 30 واحد افزايش بايد 37 واحد افزايش يابد. زيرا بين اعداد و حروف در جدول اسكي 7 كاراكتر فاصله انداخته اند. يعني حروف 7 تا رفته اند در جدول پايين.

شرطي در برنامه بالا وجود دارد كه نگاه مي كند ببيند كه dl در چه حدودي است و اگر كوچكتر از آن حد باشد 7 تا اضافه مي كند.

= = = = = = = = = = = = =
برنامه بالا رقم سمت راست al را چاپ كرد ولي رقم سمت چپ al هنوز مانده.

بگذاريد كل برنامه اي كه دو رقم را مي تواند چاپ كند را توضيح بدهم.
اين برنامه اول رقم سمت چپ al را نابود مي كند . يعني صفرش مي كند. پس مي ماند رقم سمت راست . كه برنامه بالايي مي تواند آن را چاپ كند.
بعد يك شيفت به راست(چهار تايي) مي دهد al را تا رقم سمت چپ بنشيند جاي رقم سمت راست. پس باز برنامه بالا مي تواند آن را چاپ كند.
اين شيفت به راست را كمي توضيح مي دهم. مي دانيد كه al داراي 8 بيت است. پس اگر 4 تا هلش بدهيم به راست. 4 بيت نابود مي شود و 4 بيت سمت چپ مي نشيند به جاي 4 بيت سمت راست. و جاي 4 بيت سمت چپ هم صفر مي شود. يك دستور چرخش هم داريم كه هر بيتي بيرون مي رود از آنطرف مي چرخد مي آيد اين طرف مي نشيند انتهاي اين طرف. ولي اين دستور شيفت ، مثل دستور چرخش نيست كه بيتهاي بيرون رفته از آنطرف بچرخند بيايند اين طرف. پس سمت چپ صفر مي شود و سمت چپي ها مي روند جاي سمت راستي ها مي نشينند. مثلا اگر al درونش e3 باشد با اين كار ميشه 0e . مي دانيد كه در 0e ، صفر 4 بيت است و e هم 4 بيت است.

اين از مانور شيفت ولي مانور اول كه براي نگهداشتن سمت راست al است راحت تره. كافيه كه فقط سمت چپ را نابود كنيم. كه با يك and با صفر راحت انجام ميشه.

حالا كل برنامه:
اولا بگم كه من در چاپ al مشكل پيدا كردم و به جاي آن bl قرار دادم. فرقي هم نميكنه ميشه اول al را در bl ريخت و بعد برنامه زير را استفاده كرد.
ميشه برنامه زير را به نام bl_print در برنامه هاي بعدي خودمان استفاده كنيم.
يعني هر موقع خواستيم رجيستري را ببينيم چي داخلشه ، آن را در bl بريزيم و برنامه زير را در قالب يك زيربرنامه احضار كنيم .

كار اصولي تر هم اينه كه اين نوع برنامه ها را جدا كامپايل كنيم و با استفاده از برنامه link هم برنامه در دست اجرا را لينك كنيم و هم برنامه آرشيو خود را و اين طوري از برنامه هاي قبلي خود هم استفاده كنيم. بعدا روش link دو آبجكت را با هم مي گويم.

کد:
.model small

.stack
.code

chaap proc

mov bl,0ech
;----------part 1
mov ah,2h
mov dl,bl

mov cl,04h
shr dl,cl

add dl,30h

cmp dl,3ah
jl lable1

add dl,07h

lable1:
int 21h

; -----------part 2

mov dl,bl
and dl,0fh

add dl,30h

cmp dl,3ah
jl lable2

add dl,07h

lable2:
int 21h

mov ah,4ch
int 21h

chaap endp

        end chaap

تقريبا همه قسمتها را قبلا شرح داديم و نياز به شرح ديگه نيست . فقط دو خط زيررا نگاه كنيد:
mov cl,04h
shr dl,cl

اينكه آمده اول در cl عدد 4 را گذاشته و بعد آمده dl را اين مقدار شيفت داده شايد در كامپايلر شما نياز به اين نوع احتياط نباشه و يك دفعه هم بتوانيد به جاي cl همان 4 را بگذاريد.

اول برنامه خط mov bl,0ech به bl مقدار اوليه مي دهد كه همين مقدار بايد چاپ بشه. براي چك برنامه آن را عوض كنيد تا ببينيد درست كار مي كند يا نه.

توجه كنيد كه در ابتداي هر part كه هر part براي چاپ يك نميه bl است ما با دستور mov dl,bl آمده ايم dl را پر كرده ايم.

دستور mov ah,2h كه در part 1 آمده در part 2 هم معتبره. ولي مي توانيد باز هم در part 2 براي احتياط بنويسيد.

در مقايسه part 1 و part 2 بايد بگوييم كه همه قسمتها تقريبا يكي هستند به جز اين كه در يكي and استفاده شده در ديگري shr (بخوانيد شيفت تو رايت) . در مورد اون 0f هم كه در دستور and استفاده شده هم كه مي دونيد يعني چي. يعني 4 بيت صفر و 4 بيت 1 كه باعث ميشه مثل فيلتر قسمت چپ را صفر كند. مي دانيد كه هر عددي با صفر and بشه نتيجه صفر بيرون مي آيد.
اگر يك روز خواستيد مثلا 4 بيت يك رجيستر را يك كنيد مي توانيد با يك آنرا or كنيد.
فكر مي كنم ديگه اين برنامه احتياج به توضيح ندارد.
اين برنامه به نظر من بسيار ضروري است چون باعث ميشه نتايج كارمان را بتوانيم چاپ كنيم.
فعلا كه link دو آبجكت را نگفتيم هم مي توانيم به عنوان يك زير برنامه در برنامه هاي خود استفاده كنيم و مدام ازش استفاده كنيم تا نتايج كارهاي خود را ببينيم.

ولي موقع تبديل اين برنامه به زير برنامه يك نكته مهم را به ياد داشته باشيد. و آن اين كه ((زير برنامه بايد امين باشد.)) و اين برنامه چون dl و cl و .... را دستكاري كرده به درد نمي خوره. پس بهتره اين رجيسترها(و براي احتياط همه رجيسترها را) اول زيربرنامه push كنيد و آخر برنامه pop كنيد.

در مورد چنين زيربرنامه اي مي بينيد كه ورودي خود را از bl گرفته پس با آن بايد متفاوت عمل كرد. سه نوع ورودي گرفتن زيربرنامه را قبلا گفتيم كه اين نوع يك بود كه از رجيستر ورودي مي گرفت.
 

saalek110

Well-Known Member
نقل از پست 50:

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

حالا مثال:
کد:
; this a procedure to print a block on the screen using the 
; stack to pass parameters (cursor position of where to print it
; and colour).

.model tiny 
.code
org 100h

Start: 

mov dh,4 ; row to print string on
mov dl,5 ; column to print string on
mov al,254 ; ascii value of block to display
mov bl,4 ; colour to display character
push dx ax bx ; put parameters onto the stack 

call PrintString ; print our string

pop bx ax dx ;restore registers
mov ax,4C00h ;terminate program 
int 21h

PrintString PROC NEAR

push bp ; save bp
mov bp,sp ; put sp into bp
push cx ; save registers to be destroyed

xor bh,bh ; clear bh - video page 0
mov ah,2 ; function 2 - move cursor
mov dx,[bp+8] ; restore dx
int 10h ; call bios service

mov ax,[bp+6] ; character
mov bx,[bp+4] ; attribute
xor bh,bh ; display page - 0
mov ah,9 ; function 09h write char & attrib 
mov cx,1 ; display it once
int 10h ; call bios service

pop cx ; restore registers
pop bp 

ret ; return to where it was called
PrintString ENDP

end Start

برنامه بالا را امتحان كردم. يك مربع قرمز چاپ شد. حالا نمي دانم اين نشانه سلامت است يا خطا . بگذريم.
شرح: براي بهره برداري ازپشته تنها چيزي كه بايد بدانيم اين است كه آن چيز در كجاي پشته است.
مي دانيد كه پشته به سمت پايين رشد مي كند. يعني اگر ما سه چيز را به طور متوالي بياندازيم داخل پشته ، آدرس اولي بزرگتر از دومي و آدرس دومي بزرگتر از سومي است.
دقيق تر بگوييم آدرس اولي تا سومي عبارت است از bp+6 و bp+4 و bp+2 .
بايد بدانيد كه شما مي توانيد ax را به پشته بفرستيد ولي نمي توانيد ah را به پشته بفرستيد. پس مستاجرين پشته همه 2 بايتي هستند. براي همين اين اعداد 2 و 4 و 6 توليد شده است.
اما برنامه پيچيده بالا باز هم نياز به توضيح دارد كه بايد مثل معمايي حل شود. مي سپارم به شما حلش را. خودم هم روي آن فكر مي كنم اگر معما را توانستم حل كنم در همين پست مي نويسم. از عكس زير هم كه براي فهم اين قضيه در همين صفحه آمده كمك بگيريد. مشكل من اينه كه تفاوت sp و Bp را خوب نمي دانم.


2v19feo.gif


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

نقل:================================
ببين شما وقتي stack رو تعريف ميكني
دوتا حالت وجود داره يا با گذاشتن اطلاعات جديد اشاره گر زياد ميشه و يا كم ميشه . تويdos اگر اين اشاره گر از محدوده تعيين شده براي برنامه بيشتر و يا كمتر بشه برنامه مشكل پيدا ميكنه ( توي ويندوز رو نميدونم اما فكر كنم اونجا هم همين طور باشه ) براي جلوگيري از اين اتفاق انتهاي محدوده رو توي bp و يا base memory ذخيره مي شه . اما كار اصلي اون براي دسترسي به اطلاعات در توابع است ( معمولا ). اما كار sp نگهداري محل آخرين اطلاعات قابل برداشت و يا محل جديد براي ريختن اطلاعات است .

توي اسمبلي هنگامي كه برنامه اي اجرا ميشه بايد هنگامي ورود به برنامه و خروج از برنامه ( يا هر تابع و يا ساب روتين ) مقدار اوليه sp هنگام ورود با مقدار sp هنگام خروج يكي باشه و گرنه امكان كرش برنامه وجود داره .
براي ارسال اطلاعات به توابع دو روش وجود داره
1- خود اطلاعات رو بفرستي يعني محل آدرس اطلاعات كه در اين صورت اطلاعات اوليه مكنه تغيير كنه ( بر اساس تابع )
2- كپي از اطلاعت رو بفرستي كه اطلاعات اوليه بدون دست خوردن باقي مي مونه
توي برنامه شما روش 2 بكار رفته بود
متغير ax,bx,cx پوش شدند
بعد توي تابع براي اينكه sp تغيير نكنه و بعد از خروج از تابع بشه مقادير رو از توي پشته برداشت تا sp ثابت ببمونه از روش فرستادن مقادير براي پروسس هاي 8088 استفاده شده است
چون هر تابعي از bp براي دسترسي به پشته بدون تغيير sp براي برداشتن مقدار هاي مورد نياز ورودي تابع استفاده ميكنه و همانطور كه گفتم از اين ريجيستر در برنامه اصلي براي چكينگ استفاده ميشه ابتدا اون رو ريخته توي حافظه و بعد sp رو ريخته توش .حالا bp همون sp با اين تفاوت كه براي دسترسي به اطلاعات اوليه بايد خانه هاي bp+2,4,6,8 رو خوند چون دوتا براي ذخيره bp استفاده شده و متغير هاي ديگه همشون 2 تا بايت جا ميگيرن
در اخر bp رو با پاپ برگردونده و بعد توي برنامه اصلي كپي از مقادير اصلي رو از پشته خارج كرده
===================================

و نقل از :
http://barnamenevis.org/forum/showthread.php?t=12628
-------------
seyedof :
سلام
sp = stack pointer
bp = base pointer
چون sp حياتيه و خراب شدنش بحراني هست کپيش رو توي bp ميريزند و معمولا متغيرهاي پاس شده به تابع رو با bp آدرس ميدن.
------------
MSK:
sp آدرس آخرين داده در سگمنت استاكه و در برنامه نويسي سنتي bp مثل سباتاي عمومي كاربرد خاصي نداشت.
ولي از چندي پيش به bp وظيفه آدرس دهي به متغير هاي داخل تابع رو دادن. و چون مبدا اش با اسپي يكيه ولي جهتش اكسشه بهش ميگن اشاره گر قاب پشته.
 
آخرین ویرایش:

saalek110

Well-Known Member
براي تمرين برنامه پست قبلي ، من برنامه زير را نوشتم:
کد:
.model tiny 
.code
org 100h

Start: 
mov ax,1234h
push  ax; put number into the stack 

call PrintString ; print our string

pop ax  ;restore number

mov ax,4C00h ;terminate program 
int 21h
; ======== start sub s ============
PrintString PROC NEAR
mov bp,sp
mov ax,[bp+2] ; number
;----------
mov bl,ah
call bl_print

mov bl,al
call bl_print
;----------
ret ; return to where it was called
PrintString ENDP

bl_print PROC NEAR
push ax
push bx
push cx
push dx
;----------part 1
mov ah,2h
mov dl,bl  

mov cl,04h
shr dl,cl

add dl,30h

cmp dl,3ah
jl lable1

add dl,07h

lable1:
int 21h

; -----------part 2

mov dl,bl
and dl,0fh

add dl,30h

cmp dl,3ah
jl lable2

add dl,07h

lable2:
int 21h
;------------
pop dx
pop cx
pop bx
pop ax

ret ; return to where it was called
bl_print ENDP



end Start

اين برنامه براي تمرين ارسال مقدار به زير برنامه از طريق پشته است.
همچنين در اين برنامه براي اولين بار از زير برنامه bl_print كه در 2 پست قبل ساختيم استفاده كردم. كمي آن را كامل كردم. آمدم push هاي اولش و pop هاي آخرش را اضافه كردم تا رجيستري را تغيير ندهد و فقط چاپ خودش را بكند.
نگاه كنيد به اين كه ax اول push شده ولي آخر pop شده.
از اين برنامه bl_print فقط براي نمايش تحويل گرفته هاي زير برنامه PrintString از طريق پشته از برنامه اصلي استفاده شده.

اما برويم به سراغ برنامه اصلي:
اولا من 1234h را push كرده ام.البته اول به ax ريختم . بعد ax را push كردم. البته اولش مستقيما 1234 را push كردم. چون عدد هم ميشه push كرد. ولي وقتي در ديباگ برنامه را باز كردم ديدم پيچيده شده كدش دوباره به اين حالت برگرداندم . چون مي خواهم روي كدش در ديباگ هم كار كنم.
حالا زير برنامه بايد اين 1234 را از پشته بردارد.

برويم سراغ زير برنامه PrintString :
من ابتدا اين طوري نوشتم :
mov ax,[sp+2] ; number
چون اطلاعاتم كم بود و نمي دانستم كه bp است كه مي تواند به عنوان پوينتر استفاده شود نه sp . error داد tasm در اين مورد. پس bp به جاي sp گذاشتم.
پس بايد قبلش sp را در bp مي ريختم كه ريختم(mov bp,sp ).

خوب. حالا برنامه ما با برنامه پست قبل يك فرق داره. او bp را push كرد و آخر كار pop كرد . يعني حفظش كرد. ولي من با ريختن sp روي bp آمدم bp را نابود كرده ام. عجب اشتباهي؟ دفعه بعدي درست مي نويسم.
نتيجه اجراي برنامه هم اين است:
1234
= = = = = = = =
خرده نكته:
اول:در انتهاي زيربرنامه ها ret را فراموش نكنيد.

دوم: نويسنده برنامه بالا ذكاوت به خرج داده و برنامه را com گرفته . اگر exe بود. آيا فقط با استفاده از يك پارامتر مي توانست به پشته برسد؟ يعني با اين تك خط:
mov ax,[bp+2] ; number
من كه فكر نمي كنم. البته امتحان نكردم قضيه را. ولي به نظر من بايد قطعه را هم تنظيم مي كرد اول. چون فقط در برنامه com است كه همه اعضاي برنامه در يك قطعه اند.

سوم: چرا بايد bp را بعلاوه 2 كنيم تا به 1234 (دو بايت خود) برسيم؟
در انتقال از برنامه اصلي به زيربرنامه ، چه چيزي روي پشته ريخته شده كه 1234 ما يكي رفته پايين. احتمالا آدرس برگشت. براي تست من اين خط را :
mov ax,[bp+2] ; number
به اين شكل:
mov ax,[bp+00] ; number
تغيير دادم. نتيجه اجراي برنامه اين شد:
0107
اين 0107 يعني چه؟

در شكل زير به اين سئوال پاسخ داده شده. جواب اين كه آدرس خط بعد از خط call در پشته قرار مي گيرد تا با رويت ret از پشته cpu بتواند راه برگشت خود را پيدا كند.


2a98v94.gif


همچنين در شكل بالا روش استفاده از tlink16 كه جلوي آن حرف t را به شكل پارامتر بايد گذاشت ديده مي شود.
همچنين نتيجه اجراي برنامه كه 1234 است را مي بينيد. و قطعه هاي برنامه اصلي و دو زير برنامه . برنامه اصلي را با رنگ زرد نشان داده شده. زير برنامه اول با رنگ بنفش و زير برنامه دوم فقط اولش معلومه كه رنگ كادرش قهوه اي است.
همچنين از call ها فلش هايي به شماره خط ها داده ام تا ببينيد كه موقع كامپايل چطور به طور اتواتيك اسم زيربرنامه تبديل به شماره خط ميشه. ليبل ها هم به همين شكل تبديل به شماره خط مي شوند. و هيچ اثري از راهنماها نيست. فقط كداسمبلي خالي داريم.
.
 
آخرین ویرایش:

saalek110

Well-Known Member
منبع:
http://newdata.box.sk/neworder/cr/Cracking.html

مطالبي كه در زير مي آورم براي مرور روزنامه اي براي كمي احاطه يافتن بر دستورات و محيط كار است. به همين خاطر بدون ترجمه قرار مي دهم.

کد:
AX => accumulator - this register is most commonly used for mathematical or I/O operations 

BX => base - this register is used commonly as a base or a pointer register (we'll talk more about this later) 

CX => count - used commonly for counting instructions such as loops 

 DX => displacement - much like the base register

کد:
CS => code segment - the block of memory where the code (instructions are located) 

DS => data segment - the block of memory where data can be accessed. In block move operations in which huge blocks of memory are moved, this is commonly the segment in which the CPU reads from. 

ES => extra segment - also another data segment. In block move operations in which huge blocks of memory are moved, this is commonly the segment in which the CPU writes to. 

SS => stack segment - this is the block of memory in which the CPU uses to store return addresses from subroutines. (more on this later)

کد:
SI => source index - this register is used in conjunction with block move instructions. 
This is a pointer within a segment (usually DS) that is read from by the CPU. 

DI => destination index - this register is also used in conjunction with block move instructions. This is a pointer within a segment (usually ES) that is written to by the CPU. 

BP => base pointer - a pointer used commonly with the stack segment 

SP => stack pointer - another pointer used commonly with the stack segment (this one, you don't touch)

کد:
zero => ZR/NZ (zero/not zero) - tells you whether an instruction (such as subtraction) yielded a zero as an answer 

sign => NG/PL (negative/positive) - tells you whether an instruction yielded a positive or negative number 

carry => CY/NC (carry/no carry) - tells you whether an instruction needed to carry a bit (like in addition, you carry a number over to the next digit). Various system (BIOS) functions use this flag to denote an error. 

direction => DN/UP (decrement/increment) - tells a block instruction to either move forward or backwards in reads and writes

انواع طرز استفاده از mov :
کد:
xxxx:0100 B8A77A MOV AX,7AA7 
xxxx:0103 89C2 MOV DX,AX 
xxxx:0105 B90000 MOV CX,0000 
xxxx:0108 88D1 MOV CL,DL 
xxxx:010A 890E0005 MOV [0500],CX 
xxxx:010E 8B160005 MOV DX,[0500] 
xxxx:0112 BB0200 MOV BX,0002 
xxxx:0115 26A30005 MOV ES:[0500],AX

چه مواقعي پرش هاي شرطي پرش مي كنند. با توجه به فلاگها:
کد:
JB/JNAE CF=1 Jump if below/not above or equal (unsigned) 
JAE/JNB CF=0 Jump if above or equal/not above (unsigned) 
JBE/JNA CF=1 or ZF=1 Jump if below or equal/not above (unsigned) 
JE/JZ ZF=1 Jump if equal/zero 
JNE/JNZ ZF=0 Jump if not equal/not zero 
JL/JNGE SF not equal Jump if less/not greater or to OF equal (signed) 
JGE/JNL SF=OF Jump if greater or equal/not less (signed) 
JLE/JNG ZF=1 or SF Jump is less or equal/not equal OF greater (signed) 
JG/JNLE ZF=0 or SF=OFJump if greater/not less or equal (signed) 
JS SF=1 Jump if sign 
JNS SF=0 Jump if no sign 
JC CF=1 Jump if carry 
JNC CF=0 Jump if no carry 
JO OF=1 Jump if overflow 
JNO OF=0 Jump if not overflow 
JP/JPE PF=1 Jump if parity/parity even 
JNP/JPO PF=0 Jump if no parity/parity odd
 

saalek110

Well-Known Member
يك سري نكته از:
http://www.bits-pilani.ac.in:12356/faculty/pankajv/8086 Assembly Programs.htm

نكات پروسيجر ها:

يك – هر برنامه حداقل يك پروسيجر دارد.
دو – زير برنامه ها همگي به عنوان پروسيجرهاي مستقل تعريف مي شوند.
سه – هر پروسيجر محدود ميشه بين proc و endp .
چهار – پروسيجرها بايد نامي داشته باشند و هم همراه proc و هم همراه endp بيايند.
پنج – پروسيجرها نبايد روي هم بيافتند.
Procedures may not overlap one another

= = = = == = = = == == ==

اينيتيالايز قطعه داده:

بايد با شروع برنامه es و ds به psp اشاره كنند. Program Segment Prefix
Programs begin with the DS and ES registers pointing to the Program Segment Prefix (PSP)


برنامه نويس بايد ds را تغيير دهد تا به قطعه داده ها اشاره كند اگر كه متغيرهايي تعريف شده اند. با كد زير:
MOV AX, @DATA
MOV DS, AX

The programmer must change the DS to point to the program's data segment if it is to reference variables that have been declared.

= = == == == = = = = ==
قوانين نام گذاري:


اول : تا 31 كاراكتر مجاز است.

دوم:
Period must be first character if used in label.

سوم: كاراكتر اول نمي تواند يك عدد باشد.

چهارم : ليبل بايد از اعداد(0 تا 9) يا حروف(a-z ) يا اين 6 كاراكتر باشد: ? . @ _ $ %

پنجم:نامهاي مجاز مثل:
کد:
.name @character $100 time_of_day

ششم : نامهاي غير مجاز مثل:
کد:
3time #fun for.now

= = = = = = = = == = == =
قسمت بعدي Instruction Pseudo-Ops:

اول : We are working only with a subset of the entire 80x86 instruction set

دوم: Instructions can be divided into different categories

سوم: Data Transfer Instructions:
کد:
Pseudo-Op---Description----------------Example	
IN------------	Input from I/O port--------IN AX, DX	
LEA----------Load effective address----LEA DX, Buffer	
MOV---------Move data------------------MOV AL, DL	
OUT----------Output to I/O port	---------64h, AL	
POP----------Pop a word from the stack-----POP DI	
PUSH	--------Push a word onto the stack----PUSH BX	
XCHG-------Exchange data--------------XCHG AX, SI

= = = = = = = = = = ==
Arithmetic Instructions :
محاسباتي ها:
کد:
ADD----Add------------------ADD AX, CX	
DEC----Decrement by 1-----DEC BL	
DIV----Divide-----------------DIV BX	
INC----Increment by 1-------INC DI	
MUL—Multiply--------------MUL CL	
NEG---Negate-----------------NEG AX	
SUB---Subtract----------------SUB CH, BL

دست كاري بيت ها:
Bit Manipulation Instructions :
کد:
Pseudo-Op------------Description------------------Example	
CLC-------------------Clear carry flag--------------CLC	
CLI--------------------Clear interrupt flag----------CLI	
CMC------------------Complement carry flag------CMC	
NOP-------------------No operation------------------NOP	
STC-------------------Set Carry-----------------------STC	
STI--------------------Set interrupt flag--------------STI

= = = = == = == =
انتقال كنترل برنامه:
Control Transfer Instructions:
کد:
Pseudo-Op-------------Description------------------Example	
CALL------------------Call a subroutine------------CALL SUM	
INT---------------------Software interrupt------	----INT 21h	
IRET-------------------Return from interrupt------IRET	
JA----------------------Jump if above---------------JA TOP	
JAE--------------------Jump if above or equal----JAE TOP	
JB----------------------Jump if below--------------JB TOP	
JBE--------------------Jump if below or equal----JBE TOP	
JC----------------------Jump if carry flag set-------JC Carry_Code	
JE----------------------Jump if equal----------------JE SumIt	
JG---------------------Jump if greater---------------JG DOIT	
LOOP-----------------Loop---------------------------LOOP AGAIN	
RET-------------------Return from subroutine------RET

= == = = == = = = = = =
Flag and Processor Control Instructions:
دستورات فلگ كنترل پردازشگر:
کد:
Pseudo-Op------------Description-----------------Example	
CLC--------------------Clear carry flag------------CLC	
CLI---------------------Clear interrupt flag--------CLI	
CMC-------------------Complement carry flag	---CMC	
NOP-------------------No operation-----------------NOP	
STC-------------------Set Carry----------------------STC	
STI--------------------Set interrupt flag-------------STI

== == = = = = = ==
ديتاهاي برنامه هاي ما به 3 شكل مي تواند باشد:
يك – اعداد (در مبناي 2 يا 16 يا 10 )
دو – به شكل كاراكترها . كه بايستي در كوتيشن گذاشته شود.
سه – به شكل متغير.

متغيرها: ( هر متغير ((ديتا تايپ)) دارد و محل ذخيره متغير.)
انواع متغير:
== = == = =
نوع اول : بايت .
طريقه تعريف:
name DB any_byte
محدوده علامت دار آن:
منفي 128 تا 127 .
محدوده بي علامت:
صفر تا 255 .
= = = = = == =
نوع دوم : word كلمه.
طريقه تعريف:
name DW any_word
محدوده علامت دار آن:
منفي 32768 تا 32768 .
محدوده بي علامت:
صفر تا 65535 .

نكته در مورد word : وقتي ما 1234 را در حافظه 1000 مثلا ذخيره مي كنيم در حافظه 1000 عدد 34 مي نشيند و در 1001 عدد 12 . اين چيزي طبيعي است و بايستي انتظارش را داشته باشيد.

== = = = = = = = = == =
آرايه:
طريقه تعريف:
کد:
array_name db 5,6,2,56,34
كد بالا باعث:
Defines a five element array of character/byte size values

دو نكته ديگر:
کد:
Can use addressing modes to access


MOV BX, array_name \
MOV SI,j            > same as temp = array_name[j]
MOV temp,[BX + SI]   /

Can create arrays of any data type, or structures using this method.
بعدا روي اينها تمرين مي كنيم. كد بالا ناقص است و بايد داخل برنامه به كار ببريم. ولي با همين 3 خط به سبك كار با آرايه مي شود پي برد.
 

saalek110

Well-Known Member
تا حالا هر چي لينك من دادم سايتهاي انگليسي بود.
كلا هر وقت نمي گويم فارسي و لينك مي دهم منظورم سايت انگليسي است.
ولي حالا مي خواهم يك آموزش اسمبلي فارسي معرفي كنم.
از صفر شروع كرده مثل ما و در 20 تا 30 درس گفته.
خيلي قسمتهايش با مطالب ما مشتركه. اگه ديديد چيزي را گفته كه ما كم گفتيم خودتان بگيد.

http://www.irandevelopers.com/category.asp?id=16
 

saalek110

Well-Known Member
امروز سايتهاي فارسي اسمبلي را سرچ كردم. آموزشها و نمونه برنامه هاي خوبي پيدا كردم.
يكي از آنها تاپيكي به نام ((درخواست كمك در مورد اسمبلي)) است با لينك زي:
http://forum.p30world.com/showthread.php?t=9862

كه جناب Nesta مطالبي نوشته آنجا. من از نوشته هاي Nesta خيلي خوشم مي آيد. به نظرم سوادش هم خيلي خوب باشه. يك آموزش جاوا هم از ايشان ديدم خيلي خوشم اومد.

حالا اينجا با اجازه از ايشان چند تا از نمونه برنامه هايش را تمرين كنيم.

= == == = = = = == = =
نقل از ايشان:

در اين قسمت يك تمرين ديگر با هم انجام ميدهيم و برنامه اي مينويسيم كه تعداد 200رنگ از 256 رنگ موجود در حالت 320 در 200 گرافيكي را نمايش دهد .
تابع شماره 00h از وقفه 10h مربوط به تعيين حالت نمايش است . كد مربوط به
حالت صفحه نمايش در ثبات AL قرار گرفته و وقفه فراخواني ميشود:

شماره تابع برابر AH=00h
حالت صفحه نمايش با استفاده از جدول AL= INT 10h


حالت صفحه نمايش در AL از جدول مخصوص موجود در كتابهاي اسمبلي بدست مي آيد.
كد مربوط به حالت 320 در 200 در 256 رنگ برابر 13h است بنا براين AL را برابر 13h قرار ميدهيم .
براي نمايش و روشن كردن يك نقطه (Pixel) در حالت گرافيكي از تابع 0Ch همين وقفه استفاده ميكنيم . يعني شماره ستون را در CX ، سماره سطر را در DX و شماره رنگ را در AL قرار داده و وقفه را اجرا ميكنيم . براي اينكه از ستون 199 تا ستون شماره صفر نقطه روشن كنيم ، CX را برابر 319 قرار داده و با دستور LOOP نقاط را در يك سطر روشن ميكنيم .
کد:
MOV CX,319    ; COLUMN 319 = START COLUMN
COL:
INT 10H ; CALL INTERRUPT 10H
LOOP COL
سالك: البته اين mode 13 را ما يك پست زديم(همان نمايش چند پيكسل) ولي نستا دقيق تر توضيح داده و اعداد را ذكر كرده.
سالك : كد بالا قسمتي از برنامه است كه نشون ميدهچطور با قرار دادن عدد در cx ميشه loop را به اون تعداد چرخاند. 319 هم از همان 320 در 200 بيرون آمده.

ادامه نقل مطلب:
سپس DX يا همان شماره سطر را يك واحد افزايش ميدهيم و مقدار آن را با 199 مقايسه ميكنيم (چون از 0 تا 199 سطر داريم ) و اگر برابر نبود دوباره عمليات بالا را انجام ميدهيم .

بعد از اينكه اين عمليات انجام شد، تابع 00h از INT 16h را فراخواني ميكنيم تا
منتظر دريافت يك كليد از صفحه كليد شود . به اين ترتيب ميتوانيم نتيجه برنامه را مشاهده كنيم و كليدي را براي اتمام برنامه بزنيم .
در نهايت بايد حالت صفحه نمايش را به مود متني برگردانيم .
براي اينكار از همان تابع تعيين مود نمايشي استفاده ميكنيم و حالت صفحه نمايش
كه با AL مشخص ميشود را برابر 3 قرار ميدهيم .

کد:
.model tiny 
.code
org 100h
START :
; --------------part 1 ---------------
MOV AH,00H 
MOV AL,13H 
MOV BX,00H ; PAGE NUMBER 
INT 10H ; SET TO 320x200 256 COLORS
; --------------part 2-----------------
MOV AH,0CH ; PUTPIXEL FUNCTION
MOV AL,25  ; COLOR #25 
MOV DX,0   ; ROW 0 
ROW :
; ----------------------------
MOV CX,319 ; COLUMN 319 = START COLUMN 
COL :
INT 10H    ; CALL INTERRUPT 10H 
LOOP COL   ; DOWN TO CX=0
; ----------------------------
INC DX     ; DX=DX+1 
INC AL     ; AL=AL+1( COLOR NUMBER )
CMP DX,199 ; IF DX=199 
JNZ ROW    ; ELSE JUMP TO ROW 
---------- part 4:daryaft 1 kelid----
MOV AH,00H 
INT 16H 
-----------part 5 ---------
MOV AH,00H ; VIDEO MODE SET
MOV AL,03H ; 80x25 16 COLORS 
INT 10H    ; CALL INT .10H 
---------------------------
INT 20H    ; TERMINATE PROGRAM
END START

سالك: من اجراش كردم. چه قشنگ بود. از بالا تا پايين صفحه يك طيف زيبايي از رنگها ايجاد شد. Com است برنامه.

ادامه نقل قول:
با انجام اين تمرين ساده ، ياد گرفتيم كه دانستن يكسري از وقفه ها و توابع(سالك: ما گفتيم سرويس ها) مربوط به آنها براي نوشتن برنامه هاي اسمبلي الزامي است .

سالك: شرح برنامه : در part 1 كه آماده مي شويم برويم به حالت گرافيكي.
در part 2 احتمالا داره تنظيمات مي كند كه int 10 براي نوشتن پيكس آماده بشه.
معمولا وقفه ها كارهاي مختلفي دارند. ديده مي شود كه يك وقفه اول استارت مي كنه خودش را كه در part 1 اين برنامه شده. بعد كارش را انجام ميده كه در part 2 تنظيمات اين كار انجام مي شود و احيانا close كي كنه كار را كه در part 5 انجام شده. خيلي وقفه ها اين طوري هستند.
خط INT 20H را نگاه كنيد . آخرين خطهاي برنامه. اين نوعي اختتام است كه براي برنامه com فقط كار مي كند نه براي exe .
اشتباها آن را براي exe ننويسيد.
اختتام exe كه ديديد اين طوري بود:
mov ax,4C00h ;terminate program
int 21h

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

saalek110

Well-Known Member
از همين تاپيك : پست بعدي.
= = == = == = = ==
نستا:
در اين قسمت نحوه دسترسي به مقادير متغير ها را ياد ميگيريم .
وقتي كه ميخواهيم مقدار يك متغير را به يك متغير يا ثبات ديگر منتقل كنيم بايد
به اندازه آن توجه داشته باشيم . مثلا اگر متغيري بصورت:
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,'$'

اعداد 13وَ10 انتهاي رشته براي انتقال مكان نما به سطر بعد هستند و كاراكتر '$'
از اين جهت وجود دارد كه تابع چاپ رشته انتهاي رشته كاراكتري را با بودن $
تشخيص ميدهد.
براي چاپ رشته كاراكتري راه هائي وجود دارد كه يكي از آنها استفاده از تابع 9h مربوط به INT 21h ميباشد .
براي فراخواني آن بايد به اين صورت رجيستر ها را پر كنيم:
AH=09H
آدرس رشته كاراكتري DS:DX = INT 21H

عبارت DS:DX نشان ميدهد كه مقدار قطعه (Segment) رشته كاراكتري ، يعني آن قطعه اي كه متغير تعريف شده در آن قرار گرفته است ، را بايد در DS قرار بدهيم .
به همين صورت نيز مقدار آفست (Offset) آن را به DX انتقال ميدهيم .
براي بدست آوردن شماره قطعه يك متغير از عملگر SEG استفاده ميكنيم .
مثلا براي بدست آوردن شماره قطعه MSGاز:
MOV AX,Seg MSG
استفاده ميكنيم . اين دستور شماره سگمنت MSG را پيدا كرده و در AX قرار ميدهد .
براي بدست آوردن شماره آفست هم از OFFSET استفاده ميكنيم مثلا:
MOV DX,OFFSET MSG

پس براي چاپ رشته MSG بايد به اين صورت عمل كنيم :

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


اين قطعه كاري كه ما ميخواهيم را انجام ميدهد و اگر دقت كنيد متوجه ميشويد كه اصلا شماره قطعه (Segment) را محاسبه نكرده ايم . علت اينست كه متغير ما به دليل
COM
بودن برنامه در
Code Segment
كه با
CODE.
مشخص ميشود تعريف شده پس خود بخود DS حاوي مقدار سگمنت آن هست .
( باز هم ياد آوري ميكنيم كه CS حاوي شماره ثبات كد و DS حاوي ثبات داده ها است و در برنامه هاي COM. مقدار برابر دارند)

يك دستور خلاصه براي بدست آوردن عدد آفست وجود دارد بنام
LEA .
كل كاري كه اين دستورالعمل انجام ميدهد اينست كه ديگر احتياج به نوشتن
OFFSET
نخواهد بود . به عنوان مثال
MOV DX,OFFSET MSG
با
LEA DX,MSG
برابر است .

با اين تفاسير كل برنامه به اين شكل خواهد بود .
کد:
.model tiny 
.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

تمرين :
براي اينكه تمرين بهتري داشته باشيم ، ميخواهيم خودمان و فقط با استفاده از وقفه
مربوط به چاپ كاراكتر همين جمله را چاپ كنيم . قبلا گفتيم كه تابع 0Eh از وقفه
10h يك كاراكتر را در محل مكان نما چاپ كرده و مكان نما را يك خانه به راست انتقال ميدهد. ميخواهيم رشته كاراكتري بالا را تا رسيدن به علامت $ چاپ كنيم . بهترين كار اينست كه عدد آفست را در BX قرار بدهيم . در اينموقع آفست اولين كاراكتر در BX است . مقدار داخل اين آفست را بصورت
MOV al,[bx]
به ثبات AL منتقل كرده و بعد چاپ ميكنيم . براي كاراكتر بعدي يك واحد به BX اضافه ميكنيم و دوباره همان كارهاي قبلي ... . اين عمليات را بايد تا رسيدن به كاراكتر '$' ادامه بدهيم .
اين برنامه را خودتان و بدون توجه به راه حل ارائه شده بنويسيد و فايل COM.
آن را بسازيد.


کد:
.model tiny 
.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 
halgheh :
MOV AL,[BX]         ; move [BX] to AL: charactre code 
CMP AL,'$'              ; if al is equal with '$' 
JE payan                    ; then jump to END 
INT 10H                   ; otherwise call interrupt 10h 
INC BX                    ; BX=BX+1 
JMP halgheh              ; jump to next caharcter 
payan :
INT 20H                  ; terminae program 
END START
سالك: دو برنامه اين پست را هر دو را اجرا كردم . بي نقص بود.
با توضيحات داده شده فكر نمي كنم ديگه توضيح لازم باشد.
 
آخرین ویرایش:

saalek110

Well-Known Member
پست ديگري از همين تاپيك:
مقاله اي راجع به وقفه ها از نستا:
= = = = == = == == = = = = ==
وقفه هاي (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 توليد ميكند .

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

== == ==
سالك:
برنامه :
mov ah,2
mov al,7
int 16
int 20
يك برنامه كامل است در محيط ديباگ و دستور خاتمه هم دارد كه همان int 20 است. پس براي اجراي آن مي توانيد از دستور g استفاده كنيد.
من در برنامه هايي كه در ديباگ مي نوشتم هميشه از دستور t استفاده مي كردم. چون برنامه هاي من دستور خاتمه نداشت و ثانيا اين كه من بيشتر مي خواستم تغيير رجيسترها را نشان دهم كه با g نشان نمي دهد. اين دستور g برنامه را اجرا مي كند و بلافاصله بعد خاتمه برنامه رجيسترها را به شكل حالت اول در مي آورد.
همچنين به طرز ساختن فايل با ديباگ ويندوز توجه كنيد. براي امتحان هم كه شده يك برنامه در ديباگ بنويسيد و با دستوراتي كه نستا گفته فايلش را بسازيد.
من وقتي اين جوري فايل مي ساختم .فايلها مي رفت روي دسكتاپ. تجربه جالبي بود. من هم اولين فايل خود را با ديباگ ساختم و اين پست من را به ياد آن روز انداخت. با n نام مي دهيم. بعد اندازه فايل كه برابر تعداد بايتهاي نوشته شده ماست را مي دهيم. اگر بيشتر هم بدهيد زياد مهم نيست چون برنامه شما اختتام دارد و همش اجرا نميشه و در آخر هم با w فايل ساخته ميشه. من الان همين برنامه كوچك را در ديباگ تبديل به فايل مي كنم و نتيجه را گزارش مي كنم:
با اون برنامه من چيزي گيرم نيامد. من اين جوري نوشتم:
Mov ah,2
Mov dl,7
Int 21
Int 20
كه يك دينگ دريافت كردم.
با دستورات زير رفتم براي ساخت فايل:
N ff.com[inter]
R cx[inter]
20[inter]
w[inter]
و فايل هم باز روي دسكتاپ افتاد.
 

saalek110

Well-Known Member
يك بسته 250 كيلويي براي دانلود اينجا هست :
http://www.eji.com/a86.zip

كه من دانلود كردم. يك برنامه به نام a86 داخلشه. يكي هم به نام d86 . دومي فكر كنم يك نوع ديباگر باشه. ولي اولي يك خوبي كه من ازش فعلا ديدم اينه كه مي توانيم در prompt dosاين طوري بنويسيم:
A86 myfile.asm ff.com

يعني يكباره از asm مي رود به com . و كار ما را راحت مي كند.

قبلا بحثهايي راجع به فرقهاي tasm و a86 داشتيم. البته نمي دونم كه اين a86 همان a86 است يا نه.
راستش من دنبال يك ديباگر داشتم سرچ مي كردم ولي اين ديباگر d86 را هنوز چيزي ازش نفهميدم. البته توربوديباگر را دارم. مي خواستم يك چيزي را با هم دانلود كنيم تا باهم كار كنيم.

اين طوري هم نوشتم:
A86 myfile.asm
و خودش اتوماتيك فايل com است. البته كد من براي ساخت فايل com بود. كد فايل مناسب براي exe سازي را هم دادم كه اشتباها رفت com ساخت. ولي خوب براي com ساختن كه خوبه. بيشتر برنامه هاي اخير ما هم كه شده فايل com .

= == = = == =
يك نكته جالب:
فايل exe ئي كه الان روي آن امتحان كردم داخلش اين كد بود:
mov ax,@data

كه a86 خطا گرفت و گفت نمي شناسد. كه اين جوري دادم:
mov ax,data
و قبول كرد.
و يكي از فرقهاي a86 و tasm همين بود. پس احتمال اينكه اين a86 همان a86 مورد بحث باشه بيشتره.
= = = = == =
كلا چون حجمش كوچكه فايل(250 كيلو)،
پيشنهاد مي كنم دانلود كنيد و يك نگاهي بكنيد.
 
آخرین ویرایش:

saalek110

Well-Known Member
بحث حلقه هاي تو در تو:

برنامه اي كه جناب نستا براي رسم پيكسلها نوشتند يعني برنامه زير:
کد:
.model tiny 
.code
org 100h
START :
; -----------------------------------
MOV AH,00H 
MOV AL,13H 
MOV BX,00H ; PAGE NUMBER 
INT 10H ; SET TO 320x200 256 COLORS
; -----------------------------------
MOV AH,0CH ; PUTPIXEL FUNCTION
MOV AL,25  ; COLOR #25 
MOV DX,0   ; ROW 0 
ROW :
; ----------------------------
MOV CX,319 ; COLUMN 319 = START COLUMN 
COL :
INT 10H    ; CALL INTERRUPT 10H 
LOOP COL   ; DOWN TO CX=0
; ----------------------------
INC DX     ; DX=DX+1 
INC AL     ; AL=AL+1( COLOR NUMBER )
CMP DX,199 ; IF DX=199 
JNZ ROW    ; ELSE JUMP TO ROW 

MOV AH,00H 
INT 16H 

MOV AH,00H ; VIDEO MODE SET
MOV AL,03H ; 80x25 16 COLORS 
INT 10H    ; CALL INT .10H 
INT 20H    ; TERMINATE PROGRAM
END START

از بعد ديگري قابل توجه است و آن كاربرد حلقه هاي تو در تو است.
من براي ساده شدن كد بالا ، قسمتهاي رسم پيكسل آن را حذف مي كنم و فقط حلقه تو در تو را باقي مي گذارم.
کد:
.model tiny 
.code
org 100h
START :

MOV DX,0  
; --------------- outer loop -------------- 
ROW :
; --------- inner loop -------
MOV CX,10 
COL :

LOOP COL   ; DOWN TO CX=0
; ----------------------------
INC DX     ; DX=DX+1 

CMP DX,10 ; IF DX=10 
JNZ ROW    ; ELSE JUMP TO ROW 
; -----------------------------------------

INT 20H    ; TERMINATE PROGRAM
END START

با خطوط كامنت حلقه هاي دروني و بيروني را مشخص كرده ام. برنامه بالا بي اشكال اجرا شد ولي كاري انجام نمي دهم.

شرح برنامه كوچك شده بالا:
برنامه اول dx را مقدار صفر داده. كه اين در حلقه بيروني استفاده مي شود . چون با دستور inc dx مقدار dx افزايش مي يابد و دستور شرطي jnz با دستور مقايسه cmp با هم چك مي كنند تا از 10 بالاتر نرود dx و اگر چنين شود ، پرش صورت نمي گيرد و برنامه خاتمه مي يابد.
اما حلقه دروني با cx است كه حلقه مرسوم اسمبلي است. يعني تعداد چرخش را در cx قرار مي دهيم. كه من 10 گذاشته ام و بعد loop به تعداد 10 بار مي چرخد.

= = == == = =
نقد برنامه نستا:
نستا مي خواسته هم cx و هم dx تغيير كند. چون اينها x و y پيكسل بوده. و داخل حلقه از اين دو براي رسم پيكسل استفاده كرده. چون ايشان مي خواسته تمام صفحه را با پيكسل بپوشاند ، پس بايست همزمان x و y كه در اينجا cx و dx است ، تغيير كند.
پس حلقه تو در توي برنامه ايشان ايده آل براي اين كار است.
پس با يك آفرين بر ايشان نقد ما پايان مي يابد.

= = == = = == = = = = =
حالا من مي خواهم زيربرنامه bl_print را اضافه كنم به اين برنامه و dx را چاپ كنم.
کد:
.model tiny 
.code
org 100h
START :

MOV DX,0  
; --------------- outer loop -------------- 
ROW :

; --------- inner loop -------
MOV CX,10 
COL :

LOOP COL   ; DOWN TO CX=0
; ----------------------------
INC DX     ; DX=DX+1 
mov bl,dh
call bl_print
mov bl,dl
call bl_print
push dx
mov ah,2
mov dl,2dh
int 21h
pop dx

CMP DX,10 ; IF DX=10 
JNZ ROW    ; ELSE JUMP TO ROW 
; -----------------------------------------

INT 20H    ; TERMINATE PROGRAM
; ============= sub bl_print =====
bl_print PROC NEAR
push ax
push bx
push cx
push dx
;----------part 1
mov ah,2h
mov dl,bl  

mov cl,04h
shr dl,cl

add dl,30h

cmp dl,3ah
jl lable1

add dl,07h

lable1:
int 21h

; -----------part 2

mov dl,bl
and dl,0fh

add dl,30h

cmp dl,3ah
jl lable2

add dl,07h

lable2:
int 21h
;------------
pop dx
pop cx
pop bx
pop ax

ret ; return to where it was called
bl_print ENDP



END START

كار خاصي در اين برنامه نسبت به برنامه قبلي صورت نگرفته فقط خواستم با زيربرنامه bl_print با چاپ dx برنامه ام خروجي داشته باشد.
شرح برنامه:
آمده ام بعد INC DX كد زير را افزوده ام:
mov bl,dh
call bl_print
mov bl,dl
call bl_print

كه واضح است كارش . اول مي آيد قطعه بالايي dx يعني dh را در bl مي گذارد و بعد با خط call bl_print زيربرنامه فراخوانده مي شود كه باعث چاپ dh است و بعد هم همين كار براي dl يعني قسمت پاييني dx .
و بعد كد زير را داريم:
push dx
mov ah,2
mov dl,2dh
int 21h
pop dx

كارش فقط اين است كه يك منها بگذارد بين dx هاي چاپ شده.
كه با سرويس 2 از وقفه 21 انجام شده كه اين سرويس به dl نگاه مي كند. و در dl من كد اسكي منها را قرار داده ام.
ولي نكته مهم اينه كه dx (كه شامل dl هم هست) در حال استفاده است به عنوان شمارنده حلقه بيروني. پس نميشد بي محابا تغييرش داد. ولي من لازمش داشتم تا يك منها رسم كنم. پس با Push و pop يك پل زده ام روي اين سه خط كد:
mov ah,2
mov dl,2dh
int 21h
يعني كه قبل دستكاري dl مقدارش ذخيره و بعد اتمام دستكاري از پشته بازيابي شده.
خروجي برنامه هم اينه:
0001-0002-0003-0004-0005-0006-0007-0008-0009-000a

ولي انگار در تاپيك برعكس شده. اول 3 صفر يك است و در آخر 3 صفر آ.
= == = = == = == = == = =
حالا سئوال من اينه كه آيا نمي شد با همان فقط cx حلقه تو در تو ساخت.
منظورم با همين روش پل زدن.
من فكر مي كنم بشه. مي خواهم همين كار را الان انجام دهم. در پست بعد به اين قضيه خواهيم پرداخت.
 

saalek110

Well-Known Member
بله . امكانش بود.
اين هم برنامه اش:
کد:
.model tiny 
.code
org 100h
START :

MOV cX,6  
; --------------- outer loop -------------- 
ROW :

; --------- inner loop -------
push cx
MOV CX,10 
COL :

LOOP COL   ; DOWN TO CX=0
pop cx
; ----------------------------

mov bl,ch
call bl_print
mov bl,cl
call bl_print

mov ah,2
mov dl,2dh
int 21h

LOOP ROW    ; ELSE JUMP TO ROW 
; -----------------------------------------

INT 20H    ; TERMINATE PROGRAM
; ============= sub bl_print =====
bl_print PROC NEAR
push ax
push bx
push cx
push dx
;----------part 1
mov ah,2h
mov dl,bl  

mov cl,04h
shr dl,cl

add dl,30h

cmp dl,3ah
jl lable1

add dl,07h

lable1:
int 21h

; -----------part 2

mov dl,bl
and dl,0fh

add dl,30h

cmp dl,3ah
jl lable2

add dl,07h

lable2:
int 21h
;------------
pop dx
pop cx
pop bx
pop ax

ret ; return to where it was called
bl_print ENDP



END START
خروجي:
0006-0005-0004-0003-0002-0001-
اول از همه 0006 چاپ شده و در آخر 0001 .

شرح برنامه :
نام برنامه را اولا حلقه تو در تو با فقط cx مي گذارم. در دو طرف حلقه دروني با push و pop كردن cx سدي درست كرده ام بين cx حلقه بيروني و cx حلقه دروني. يعني انگار دو عدد cx داريم. درست ترش اينه كه از cx حلقه هاي بيروني و دروني نوبتي استفاده كرده اند. ميشه تا 10 بار هم حلقه ها را داخل هم گذاشت و مشكلي فكر نمي كنم پيش بيايد.

از دو طرف كد :
mov ah,2
mov dl,2dh
int 21h
مي بينيد كه push dx و pop dx را برداشته ام چون ديگه مثل برنامه قبلي از dx به عنوان شمارنده استفاده نميشه و اگر هم dx تغيير كنه مهم نيست.
كلا اين برنامه نسبت به قبلي كه از dx هم به عنوان شمارنده استفاده ميشد خيلي خلوت تر شده. چون inc dx حذف شده. دستور cmp حذف شده. پرش شرطي نداريم و بجاش همان دستور loop ساده را گذاشته ايم.
دقت كنيد كه در برنامه قبلي من dx را چاپ كردم و اينجا cx را چاپ كردم. در برنامه قبلي dx داشت افزايش مي يافت ولي در اين برنامه cx در حال كاهش است تا به صفر برسه تا دستور loop ديگه عمل نكند. از خروجي دو برنامه هم مشخصه . در اولي خروجي در حال افزايش و در دومي خروجي در حال كاهش است.
در آخر بگويم كه اين برنامه مشقي است كه من الان از خودم نوشتم و شايد روشهاي درست تري نسبت به اين باشد. پس از سايتي نيست و سليقه اي است.
.
 

saalek110

Well-Known Member
عمل link دو فايل:

در اين برنامه مي خواهيم دو فايل را با هم link كنيم. يعني اول فايل obj هر دو را مي سازيم و هر دو obj با هم در يك فايل exe قرار مي گيرند.

براي اين كار زير برنامه كوچكي را در فايلي به نام archive قرار مي دهيم. و فايل اصلي آن زير برنامه را با كدهاي خود صدا مي كند.

به برنامه زير دقت كنيد:
کد:
.model small
.code
public one_sub
one_sub    proc
push ax
push dx
; ---------
mov ah,2
mov dl,53h
int 21h
; ---------
pop dx
pop ax

ret ; return to where it was called
one_sub ENDP

  end

اول شرح 3 خط كدي كه بين خطوط افقي محصور است. قبلا بارها اين استفاده شده ولي باز هم مي گويم كه با استفاده از سرويس 2 از وقفه 21 كاراكتر موجود در dl را چاپ مي كند. كه همان حرف ((s )) را چاپ مي كند.
چون اين 3 خط كد dl و ah را دستكاري مي كنند من آمده ام ax و dx را با ارسال به پشته حفظ كرده ام. مي دانيد كه نميشه ah را push كرد و فقط رجيسترهاي 16 بيتي قابل push هستند. پس به جاي ah بايد ax را push كنيم.

اما برسيم به راهنماها:
اول فايل model و code گفته شده. و نه چيز ديگر مثل stack چون اين برنامه كامل نيست. فقط مي خواهيم با آن يك obj بسازيم تا با obj برنامه اصلي link بشود. و در انتها هم بعد end اسم برنامه اصلي را نداريم. چون اصلا اين كدها برنامه اصلي ندارند و برنامه اصلي در فايل ديگري است.
به جاي :
public one_sub
one_sub proc
در كتاب چاپي من اين بود:
Public one_sub
كه كامپايلر من يعني tasm قبول نمي كرد كه با سرچ كلمه public در گوگل سينتكس جديد را پيدا كردم. اگر كامپايلر شما چيز ديگري است مثل masm بايد سينتكس مناسب براي كامپايلر خود را بيابيد. شايد هم يكي از دو سينتكس بالا باهاش سازگار باشه.
ولي كلا كامپايلرها در راهنماها كمي فرق دارند و در كدهاي اسمبلي آنچنان فرقي نيست. اون مورد @data و data كه بين tasm و a86 فرق داشت هم نوعي راهنما است نه كد اسمبلي.

خوب. حالا تفسير كد بالا.
اولا در نكاتي از يك سايت گفتيم كه proc و endp بايد جفت باشند. و من الان با برداشتن proc خطاي غير مچ بودن اين دو را مي گرفتم. اسم زير برنامه هم همراه اين دو بايد باشه . مي ماند كلمه public : كه ميگه اين زيربرنامه بايد در دسترس همه باشه. من يك بار هم بدون كلمه public ساختم كه موقع link دو فايل خطايي دريافت كردم ولي متنش را نگفت . و فكر كنم چون برنامه اصلي داشت زير برنامه فايل دوم را فرامي خواند و چون اون public نبود خطا مي داد.

برنامه اصلي را حالا با هم مي بينيم:
کد:
.model small
.stack 
.data 
.code 
  EXTRN one_sub : PROC
asli proc

call one_sub

mov ax,4c00h ; return to dos DOS 
int 21h 

asli endp
     end asli

فقط اين خط داخلش جديده:
EXTRN one_sub : PROC
كه واضح هم هست.
معني اش اين است كه در اين جا قراره يك زيربرنامه اي صدا بشه(call one_sub ) كه در يك فايل ديگر قرار دارد.

سينتكس تبديلات:
کد:
tasm asli.asm
tasm archive.asm

و بعد اين كه هر دو فايل obj ساخته شد. مي نويسيم:
کد:
link asli archive
= = = == = == == = =
در برنامه پست قبلي يعني ((حلقه تو در تو فقط با cx)) هم خوبه كه زيربرنامه bl_print را در فايلي به نام bb بگذاريم. اسم آرشيو بهتره ولي چون مدام بايد اسمش تايپ بشه bb گذاشتم يعني بايگاني.
در فايل آرشيو ما ، زير برنامه ها همين طور زير هم نوشته مي شوند. هر يك با همان راهنماهاي public و proc در سرش و endp در ته آنها. يعني با همين سينتكس ولي به تعداد بالا.
و اين فايل ميشه tools شما. كه به همه برنامه هاي قبلي خود راحت دسترسي داريد و مي توانيد فقط با نام بردن اسم زيربرنامه ازش استفاده كنيد.

دو نكته يادتان نرود:
يكي اينكه در هر زير برنامه اگر رجيستري مخدوش ميشه آنرا push و pop كنيد در اول و آخر زيربرنامه.
و نكته ديگه اينكه سعي كنيد يك راهنما به شكل كامنت بالاي هر زيربرنامه بنويسيد. مثل يك تابلو بسازيد آنرا.با سليقه و زيبا.
شامل ورودي زيربرنامه. يعني چه رجيستري به عنوان ورودي دارد. يا انواع ورودي ديگري ديگر مثل متغير و پشته.
خروجي زيربرنامه. باز همان رجيستر ها و غيره.
عمل زيربرنامه. مثلا زيربرنامه بالا يك حرف s چاپ مي كند.
كلا يك شرح خلاصه خيلي خوبه تا خودتان بعدا بفهميد و راحت از زير برنامه استفاده كنيد.
يك نكته هم كه يادم رفته بود اينه كه اين برنامه از چه زير برنامه هايي استفاده مي كند.
با عنوان uses اين را هم اظافه كنيد بر تابلوي بالاي زيربرنامه.
ديگه چيزي به نظرم نمي رسه. خودتان هم با سليقه خودتان چيزهاي مفيدي اضافه كنيد .
 

saalek110

Well-Known Member
من آمدم از زير برنامه موجود در فايل archive را به دو زيربرنامه تبديل كردم. البته نيازي به اين كار نبود. ولي مي خواستم اولا چگونگي نشستن زيربرنامه ها پشت سرهم را نمايش بدهم و دوما تابلوهاي كامنت بالاي زيربرنامه ها را نمايش دهم. و سوما در ديباگ برنامه حاصل از لينك دو فايل را بررسي كنيم.

اين تابلوهايي كه من ساختم نمي گويم خوب و كامل است. خودتان رفع نقص كنيد.

اما يك نكته:
من وقتي الان فايل exe را ساختم اجرا نشد . با وجودي كه كامپايل شده بود. و مي دانيد كه در اسمبلي هم به آن صورت پيغام خطاي واضحي نداريم.
راه حل: براي رفع خطا همان فايل exe كه اجرا نميشد را در ديباگ ويندوز با دستور :
Debug asli.exe
باز كردم. برنامه debug جوري است كه انگار در همه پوشه ها هست ولي براي نوشتن دستور بالا بايد در پوشه اي باشيد كه asli.exe آنجا باشد.
بعد كه در ديباگ كدهاي خود را نگاه كردم ديدم كه ret زيربرنامه دوم موجود نيست و سريع اصلاح شد.
حالا برنامه ساخته شده:
کد:
.model small
.code
; ================================
; description    : this sub with int 21 print dl 
; input register : none
; output register: none
; uses           : tanzime_dl
; action         : print 's'
; --------------------------------
public one_sub
one_sub    proc
push ax
push dx
; ---------
mov ah,2
call tanzime_dl
int 21h
; ---------
pop dx
pop ax

ret ; return to where it was called
one_sub ENDP
; ================================
; description    : this sub set dl register
; input register : none
; output register: dl
; uses           : none
; action         : set dl
; --------------------------------
public tanzime_dl
tanzime_dl    proc

mov dl,53h
ret ; return to where it was called
tanzime_dl ENDP

  end

مي بينيد كه در زيربرنامه اولي من dx را push كرده ام نه در دومي. چون دومي داره با dl خروجي به اولي برمي گرداند. وقتي در اولي dx ذخيره و بازيابي ميشه يعني هر تغيير كه دومي هم ايجاد كرده اصلاح ميشه.
براي ساخت فايل exe فايل archive را با tasm مجددا به obj تبديل مي كنيم و ديگه نياز نيست فايل obj حاصل از فايل asli.asm را بسازيم. چون اون كه تغييري نكرده. بعد با دستور
Link asli archive
فايل exe را مي سازيم.
يك برنامه به نام make داريم كه در يك فايلي مي نويسيم چه فايلي به چه فايلي قراره تبديل بشه و كلا همه مسيرها را مي نويسيم تا وقتي تايپ كنيم:
Make filename
همه آن تبديلات از لحاظ تاريخ رفرش فايلها انجام بشه . يعني وقتي برنامه make مي بيند كه archive.asm نو_جديدتره تا archive.obj اين كار را انجام مي دهد. و تمام مسرهاي بعدي را انجام مي دهد تا فايل انتهايي هم جديد بشه.

= = = == = = = =
حالا بحث جالب نگاه در ديباگ به برنامه انتهايي:


2wgfwk4.gif


در عكس بالا مي بيند كه انگار نه انگار كه ما در 2 فايل كدهايمان را نوشته ايم. فقط ساختار زيربرنامه ها در كدها منعكس شده نه چيز ديگر. اگر همه را زير هم بنويسيم و كامپايل كنيم هم همين بدست مي آيد. من الان هر دو فايل را يكي كردم. كد آن:
کد:
.model small
.stack 
.data 
.code 
 
asli proc

call one_sub

mov ax,4c00h ; return to dos DOS 
int 21h 

asli endp
; ============= sub haa =====
one_sub PROC NEAR
push ax
push dx

mov ah,2
call tanzime_dl
int 21h

pop dx
pop ax
ret ; return to where it was called
one_sub ENDP
; --------------------------
tanzime_dl PROC NEAR

mov dl,53h

ret ; return to where it was called
tanzime_dl ENDP

     end asli

و دقيقا همين كدها را در ديباگ يافتم كه وقتي دو فايل بود برنامه.
نتيجه گيري: تمام اين راهنماها و فايل سازي ها فقط براي محيط كار ماست و به جز كدهاي اسمبلي چيزي به فايل افزوده نمي شود.
البته محل قرارگيري قطعه ها با راهنماها تغيير مي كند . البته نه اين راهنماهاي ما. اين راهنماهاي ما فقط جهت وصل كردن تكه كدهاي اسمبلي به هم است و چيز بيشتري نيست.

يك نكته كوچك تايپي:
در محيط ديباگ نوشتن حرف h بعد اعداد نياز نيست
ولي وقتي داريد با كامپايلر و محيط اديتوري كار مي كنيد ، بعد اعداد مبناي 16 حتما h را بگذاريد. اعداد زير 10 تك رقمي در دو مبناي 10 و 16 يكي هستند و من اكثرا h را نمي گذارم. ولي بالاي 10 اگر h را نگذاريد مبناي 10 در نظر گرفته ميشه و موقع كامپايل به مبناي 16 تبديل مي شود.
 
آخرین ویرایش:

saalek110

Well-Known Member
من شروع كردم به ساخت خزانه زيربرنامه هاي خود:
يعني همان فايلي كه با برنامه اصلي با هم link ميشه و حاوي ابزارهاي لازم من است.
نام فايل را bb.asm گذاشتم تا تايپ اسمش راحت باشه و در آن 3 زيربرنامه فعلا قرار دادم. اين فايل را با هم مي بينيم:
کد:
.model small
.code
; ================================
; description    : this_sub cout cx
; input register : none
; output register: none
; uses           : none
; --------------------------------
public cx_count
cx_count proc
push cx

mov cx,0ffffh
lable3:
loop lable3

pop cx
ret ; return to where it was called
cx_count ENDP

; ================================
; description    : this_sub print bx
; input register : bx
; output register: none
; uses           : bl_print
; --------------------------------
public bx_print
bx_print proc
push bx
push bx
mov bl,bh
call bl_print
pop bx
call bl_print
pop bx
ret ; return to where it was called
bx_print ENDP

; ================================
; description    : this_sub print bl 
; input register : bl
; output register: none
; uses           : none
; --------------------------------
public bl_print
bl_print proc
push ax
push bx
push cx
push dx
;----------part 1
mov ah,2h
mov dl,bl  

mov cl,04h
shr dl,cl

add dl,30h

cmp dl,3ah
jl lable1

add dl,07h

lable1:
int 21h

; -----------part 2

mov dl,bl
and dl,0fh

add dl,30h

cmp dl,3ah
jl lable2

add dl,07h

lable2:
int 21h
;------------
pop dx
pop cx
pop bx
pop ax

ret ; return to where it was called
bl_print ENDP

  end

زيربرنامه اول فقط كارش اينه كه cx را مي شمارد. گفتم شايد به اين وسيله بشه يك تاخير زماني بعدا ايجاد كرد. اين زيربرنامه cx را push و pop كرده تا مخدوش نشود.

زيربرنامه بعدي bx را مي گيرد و دو بار bl_print را احضار مي كند تا bx چاپ شود. در اول اين برنامه دو بار bx را push كرده ام. يكي براي همان قانون كه هر زيربرنامه بايد در اول و آخر خود رجيسترهاي مورد عمل خود را با push و pop حفظ كند و يكي هم براي اين كه وقتي bh را مي ريزم روي bl ، باعث نابودي bl مي شوم و من بعدا bl را نياز دارم تا چاپش كنم. پس قبل ارسال bl به bl_print آن را pop كرده ام. ممكنه كارهاي من اضافه باشه ولي برنامه درست كار مي كند.

زيربرنامه سوم كه همان bl_print است را هم كه مي شناسيد و نياز به توضيح نيست.

يك نكته: من با اسامي ليبل ها در bb.asm دچار مشكل شدم. يعني نبايد در اين فايل ليبل ها هم نام باشند. ولي در asli.asm من براي امتحان ليبلي هم نام يك ليبل bb.asm انتخاب كردم كه مشكلي پيش نيامد.

شروع عمل:
با دستور
Tasm bb.asm
آن را به bb.obj تبديل مي كنيم.

بعد من 2 فايل اصلي ساختم براي 2 آزمايش.
اولي نامش هست asli.asm با كد زير:
کد:
.model small
.stack 
.data 
.code 
  EXTRN bx_print : PROC
asli proc

mov bx,4567h
call bx_print

mov ax,4c00h ; return to dos DOS 
int 21h 

asli endp
     end asli
كه فقط bx_print را احضار مي كند.
و دومي اسمش هست asli2.asm با كد زير:
کد:
.model small
.stack 
.data 
.code 
  EXTRN bx_print : PROC
  EXTRN cx_count : PROC
asli proc

mov cx,10
lable1:
call cx_count
mov bx,cx
call bx_print
loop lable1

mov ax,4c00h ; return to dos DOS 
int 21h 

asli endp
     end asli

اين برنامه هم cx_count را احضارمي كند و هم bx_print را. مي بينيد كه دو بار extrn نوشتم بالاي برنامه.
اين برنامه cx را 10 مي گيره و آن را تا صفر مي برد و اعداد را چاپ مي كند. خروجي:
000a000900080007000600050004000300020001

ساخت:
برنامه هاي asli را هم به obj تبديل و با دستورات:
Link asli bb
Link asli2 bb
به exe تبديل مي كنيم.
شكل زير بازشده هر دو در ديباگ است. نام هر فايل داخل عكس هست.

4732hde.gif


زير خط آبي bx_print است و بالاي خط زرد برنامه asli .
بين اين دو خط در برنامه asli2 ، زيربرنامه cx_count را داريم و در asli به اندازه تعداد خط كدهاي cx_count ،، كدهاي بي معني.

نتيجه گيري فعلي فرضي: تمام زيربرنامه هاي فايل بايگاني حجم اشغال مي كنند . حتي اگر استفاده نشوند. البته اين حجم خيلي كم است و فقط براي اطلاع گفته شد. بعدا بايد اين قضيه را علمي تر بررسي كنيم.
 

saalek110

Well-Known Member
ليست وقفه ها از يك سايت خوب:
بالاي اين صفحه نوشته:
کد:
 The list of all interrupts that are currently supported by the 8086 assembler emulator. 
These interrupts should be compatible will IBM PC and all generations of x86, original Intel 8086 and AMD compatible microprocessors, however Windows XP may overwrite some of the original interrupts.

لينك:
http://www.emu8086.com/assembly_lan...r_reference/8086_bios_and_dos_interrupts.html

به نقل از يكي از تاپيكهاي تالار اسمبلي فروم برنامه نويس
http://barnamenevis.org/forum/forumdisplay.php?f=47

ليست وقفه هاي اين صفحه:
کد:
INT 10h/00h
INT 10h/01h
INT 10h/02h
INT 10h/03h
INT 10h/05h
INT 10h/06h
INT 10h/07h
INT 10h/08h
INT 10h/09h
INT 10h/0Ah
INT 10h/0Ch
INT 10h/0Dh
INT 10h/0Eh
INT 10h/13h
	INT 10h/1003h
INT 11h
INT 12h
INT 13h/00h
INT 13h/02h
INT 13h/03h
INT 15h/86h
INT 16h/00h
INT 16h/01h
INT 19h
INT 1Ah/00h
INT 20h
	INT 21h
INT 21h/01h
INT 21h/02h
INT 21h/05h
INT 21h/06h
INT 21h/07h
INT 21h/09h
INT 21h/0Ah
INT 21h/0Bh
INT 21h/0Ch
INT 21h/0Eh
INT 21h/19h
INT 21h/25h
INT 21h/2Ah
INT 21h/2Ch
	INT 21h/35h
INT 21h/39h
INT 21h/3Ah
INT 21h/3Bh
INT 21h/3Ch
INT 21h/3Dh
INT 21h/3Eh
INT 21h/3Fh
INT 21h/40h
INT 21h/41h
INT 21h/42h
INT 21h/47h
INT 21h/4Ch
INT 21h/56h
	INT 33h/0000h
INT 33h/0001h
INT 33h/0002h
INT 33h/0003h

=========
=========
آدرس دانلود 6 كتاب اسمبلي حجم بالا:
يك كوچكش را من دانلود كردم. يعني اين را:
Professional Assembly Language
سالم دانلود شد. بقيه را دانلود نكردم.
http://www.ebookism.net/index.php?cid=25
 
آخرین ویرایش:

saalek110

Well-Known Member
نقل از:
http://barnamenevis.org/forum/showthread.php?t=45301

همه چیز برای یادگیری زبان اسمبلی در یک بسته!
Emu8086 ترکیبی است از یک ادیتور پیشرفته ، disassembler ، assembler ، شبیه ساز نرم افزاری (Virtual PC) با debugger و همچنین آموزش گام به گام.
این برنامه برای کسانی که قصد شروع یادگیری زبان اسمبلی را دارند بسیار مفید است. این برنامه کد اسمبلی را کامپایل و در یک شبیه ساز مرحله به مرحله اجرا می کند. شما می توانید رجیسترها ، فلگ ها ، حافظه ، ALU و پشته را در حالیکه برنامه شما در حال اجرا است ، مشاهده کنید.
این برنامه برای شبیه سازی دارای Virtual Device های متفاوتی مثل LED ، Printer (کار با پورت ها) و … می باشد. Emu8086 قالب های COM ، EXE ، BIN و BOOT را پشتیبانی کرده و کد کامپایل شده توسط این برنامه در ریز پردازنده های اینتل قابل اجرا است:

http://www.kcspot.net/upload/files/rocko/emu8086.zip

سالك:
من قبلا از اين برنامه به عنوان اموليتور استفاده مي كردم. شبيه ساز خيلي خوبي است. پشته و رجيسترها و ........... را شبيه سازي كرده و چيزهاي ديگر. و com ها را كامپايل مي كند و run در محيطي ويندوزي و ....

حجم : 2 و نيم مگا.
من الان دانلود كردم . مشكل نداشت.
 

جدیدترین ارسال ها

بالا