آموزش اسمبلی - Assembly

وضعیت
موضوع بسته شده است.

saalek110

Well-Known Member
ادامه صفحه چهار:

ماكرو ها:
Macros
سينتكس هاي زير براي توربو اسمبلر مناسب است.

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

سينتكس :
کد:
Name_of_macro macro 
;
;a sequence of instructions 
;
endm

يك ماكروي مفيد:
کد:
SaveRegs macro

push ax
push bx
push cx
push dx

endm
و عكس آن:
کد:
RestoreRegs macro

pop dx
pop cx
pop bx
pop ax

endm

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

طرز صدا كردن دو ماكروي بالا را در زير مي بينيم:
کد:
SaveRegs

; some other instructions

RestoreRegs

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

سينتكس:
کد:
LOCAL name
كه name عبارت است از نام متغير يا ليبل لوكال(منطقه اي) ما.
يك ماكرو براي مثال در زير آورده مي شود. اين ماكرو به راحتي يك پيام چاپ مي كند.
کد:
OutMsg macro SomeText
local PrintMe,SkipData

jmp SkipData

PrintMe db SomeText,'$'

SkipData:

push ax dx cs 

mov dx,OFFSET cs:PrintMe
mov ah,9
int 21h

pop cs dx ax

endm

ماكرو همچنين مي تواند پارامتر بگيرد.
مثال:
کد:
AddMacro macro num1,num2,result

push ax ; save ax from being destroyed
mov ax,num1 ; put num1 into ax
add ax,num2 ; add num2 to it
mov result,ax ; move answer into result
pop ax ; restore ax

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

saalek110

Well-Known Member
مي رسيم به صفحه 5 .
صفحه 5 كار با فايل است كه چون هم يك بحث تخصصي و هم يك بحث خسته كننده است ،اين صفحه را فعلا از قلم مي اندازيم.

صفحه 6 :
اما صفحه 6 كدهايي داره كه هم كاربرهاي خوبي داره و هم به درك اسمبلي كمك مي كند.

به اين كدها نگاه كنيد:
کد:
movsb ; move byte
movsw ; move word
movsd ; move double word

براي انتقال بايت و كلمه و ((دو كلمه)) بكار مي رود ولي از كجا به كجا؟
جواب : از DS:SI به ES:DI .
خيلي جالبه به نظر من اين بحث. چون اولا نقش مهم si و di را نشان ميدهد و دوما تصور كنيد كه ما داريم قسمتي از ram را به جاي ديگر منتقل مي كنيم.

اينجا es و ds هم كه قطعات data و اكسترا را مشخص مي كنند دارند در نشانه روي قطعه كمك مي كنند.

دستورات بعدي:
کد:
cmpsb ; compare byte
cmpsw ; compare word
cmpsd ; compare double word

براي مقايسه بايت و كلمه و ((دو كلمه)). باز از DS:SI to ES:DI استفاده ميشه.
داخل كد مي گذارم تا جابجايي احتمالي پيدا نكنه:
کد:
DS:SI to ES:DI

با دستور rep هم اين دستورات قابل استفاده است.

سرچ. كدها:
کد:
scasb ; search for AL
scasw ; search for AX
scasd ; search for EAX

دنبال محتواي al و ax و eax در ES:DI مي گردند.
با repz و repnz استفاده مي شوند.

طرز استفاده از rep :
دستور rep يك Prefix(پيشوند) است كه باعث تكرار به ميزان cx بار ميشه.

Stos :
کد:
stosb ; move AL into ES:DI
stosw ; move AX into ES:DI
stosd ; move EAX into ES:DI

باعث انتقال al و ax و eax به ES:DI ميشوند.

Lods :
کد:
lodsb ; move ES:DI into AL
lodsw ; move ES:DI into AX
lodsd ; move ES:DI into EAX

= = = = = = = = = == = = =
مثال كدهاي بالا:
کد:
.model small
.stack
.code 

mov ax,@data ; ax points to of data segment
mov ds,ax ; put it into ds
mov es,ax ; put it in es too
mov ah,9 ; function 9 - display string
mov dx,OFFSET Message1 ; ds:dx points to message
int 21h ; call dos function

cld ; clear direction flag
mov si,OFFSET String1 ; make ds:si point to String1
mov di,OFFSET String2 ; make es:di point to String2
mov cx,18 ; length of strings
rep movsb ; copy string1 into string2

mov ah,9 ; function 9 - display string
mov dx,OFFSET Message2 ; ds:dx points to message
int 21h ; call dos function

mov dx,OFFSET String1 ; display String1
int 21h ; call DOS service

mov dx,OFFSET Message3 ; ds:dx points to message
int 21h ; call dos function

mov dx,OFFSET String2 ; display String2
int 21h ; call DOS service

mov si,OFFSET Diff1 ; make ds:si point to Diff1 
mov di,OFFSET Diff2 ; make es:di point to Diff2 
mov cx,39 ; length of strings
repz cmpsb ; compare strings
jnz Not_Equal ; jump if they are not the same

mov ah,9 ; function 9 - display string
mov dx,OFFSET Message4 ; ds:dx points to message
int 21h ; call dos function

jmp Next_Operation

Not_Equal:
mov ah,9 ; function 9 - display string
mov dx,OFFSET Message5  ; ds:dx points to message
int 21h ; call dos function

Next_Operation:
mov di,OFFSET SearchString ; make es:di point to string
mov cx,36 ; length of string
mov al,'H' ; character to search for
repne scasb ; find first match
jnz Not_Found

mov ah,9 ; function 9 - display string
mov dx,OFFSET Message6 ; ds:dx points to message
int 21h ; call dos function
jmp Lodsb_Example

Not_Found:
mov ah,9 ; function 9 - display string
mov dx,OFFSET Message7 ; ds:dx points to message
int 21h ; call dos function

Lodsb_Example:
mov ah,9 ; function 9 - display string
mov dx,OFFSET NewLine ; ds:dx points to message
int 21h ; call dos function

mov cx,17 ; length of string
mov si,OFFSET Message ; DS:SI - address of string
xor bh,bh ; video page - 0
mov ah,0Eh ; function 0Eh - write character

NextChar:
lodsb ; AL = next character in string
int 10h ; call BIOS service
loop NextChar

mov ax,4C00h ; return to DOS
int 21h 

.data
CR equ 13
LF equ 10
NewLine db CR,LF,"$"

String1  db "This is a string!$"
String2  db 18 dup(0)
Diff1    db "This string is nearly the same as Diff2$"
Diff2    db "This string is nearly the same as Diff1$"
Equal1   db "The strings are equal$"
Equal2   db "The strings are not equal$"
Message  db "This is a message"
SearchString db "1293ijdkfjiu938uHello983fjkfjsi98934$"

Message1 db "Using String instructions example program.$"
Message2 db CR,LF,"String1 is now: $"
Message3 db CR,LF,"String2 is now: $"
Message4 db CR,LF,"Strings are equal!$"
Message5 db CR,LF,"Strings are not equal!$"
Message6 db CR,LF,"Character was found.$"
Message7 db CR,LF,"Character was not found.$"

end

= == == = = = = = =
روش پيدا كردن ورژن DOS:
کد:
mov ah,30h ; function 30h - get MS-DOS version
int 21h ; call DOS function

اين وقفه عدد بزرگ ورژن را در al و عدد كوچك را در ah برمي گرداند.
مثلا ورژن 4 مميز صفر يك اگر باشد ، 4 داخل al و صفر يك داخل ah قرار خواهد گرفت. در داس 5 و بالاتر ممكنه مشكل ايجاد بشه. براي اين منظور از كد زير استفاده كنيد:

کد:
mov ah,33h ; function 33h - actual DOS version
mov al,06h ; subfunction 06h
int 21h ; call interrupt 21h

كه ماژور را در Bl و مينور را در bh قرار مي دهد.
ولي اين فقط ورژن 5 و بالاتر را نشان مي دهد پس براي ورژن پايين بايد از كد قبلي استفاده كنيد.

= = = = == == = =
Multiple Pushes and Pops :

در tasm و a86 مي توانيم به شكل زير:
کد:
push ax bx cx dx ; save registers
pop dx cx bx ax ; restore registers

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

= = = = = == = = ==
The PUSHA/PUSHAD and POPA/POPAD Instructions :

دستور PUSHA مشابه دستورات زير عمل مي كند:
کد:
temp = SP
push ax
push cx
push dx
push bx
push temp
push bp
push si
push di

و اين باعث راحتي است. هم در تايپ صرفه جويي ميشه و هم سريع تر از اين دستورات اجرا ميشه.

دستور POPA هم عكسش عمل مي كند . يعني push شده ها را دوباره pop مي كند.
دو دستور PUSHAD and POPAD هم مشابه اينها هستند ولي رجيسترهاي 32 بيتي بكار مي روند. شامل: ESP, EAX, ECX, EDX, EBX, EBP, ESI and EDI.

ادامه در پست بعد.
.
 

saalek110

Well-Known Member
ادامه صفحه 6 :

Using Shifts for faster Multiplication and Division :
استفاده از دستور shift براي اجراي سريعتر ضرب و تقسيم:

دستورات ضرب و تقسيم دستورات بسيار كندي هستند و بايد موقعي استفاده شوند كه سرعت مورد نياز نيست.

براي ضرب و تقسيم سريعتر شما مي توانيد از شيفت به چپ يا راست به تعداد يكي يا بيشتر استفاده كنيد.
هر شيفت برابر توان 2 است.
در زبان سي مشابه شيفت اپراتورهاي << و >> را داريم.

دستورات:
کد:
SHL Unsigned multiple by two
SHR Unsigned divide by two
SAR Signed divide by two
SAL same as SHL
سينتكس:
کد:
SHL operand1,operand2

در اين سينتكس در 8086 ما فقط به opperand2 بايد 1 بدهيم.
و در 286/386 تا رقم 31 مي شود به operand2 مقدارداد.

= = = = = = = = = = == = = = =
LOOP :

براي ساخت حلقه بهتره از دستور loop استفاده كنيم نه jmp .
براي ساخت حلقه با loop به cx تعداد چرخش را مي دهيم . و با هر بار رسيدن كنترل به دستور loop به طور اتوماتيك يك واحد از cx كم مي شود.

نكته: دستور loop هم مثل پرشها بايد پرشش زير 127 بايت باشد.
دقيق ترش اينكه 128 به عقب و 127 به جلو.

سينتكس:
کد:
mov cx,100 ; 100 times to loop
Label:
.
.
.
Loop Label: ; decrement CX and loop to Label
كد بالا مشابه كد زير است:
کد:
mov cx,100 ; 100 times to loop

Label:
dec cx ; CX = CX-1
jnz Label ; continue until done

شرح كد دوم: دستور dec باعث كاهش يك واحد از cx مي شود. دستور dec (decrease ) هم مثل تفريق بر فلاگها اثر مي گذارد و اين تاثير را jnz حس مي كند و با آن پرش خود را تنظيم مي كند. يعني اگر cx صفر بشود ديگر پرش نمي كند و حلقه خاتمه مي يابد.
پرشهاي شرطي هر كدام به فلاگ خاصي نگاه مي كنند. و جدول تقريبا پيچيده اي ايجاد مي شود. چون بعضي از پرشهاي شرطي به چند فلاگ نگاه مي كنند. براي فرار از اين پيچيدگي يك راه اين است كه به جدول تعريف پرشهاي شرطي مراجعه كنيم . چون تعريف پرش شرطي خيلي ساده تر است تا اينكه فكر كنيم به چه فلاگي نگاه مي كند. مثلا تعريف يك پرش شرطي اين است ((اگر بزرگتر باشد)). البته اين نظر من است و ممكن است اشتباه باشد.

برنامه دومي(يعني استفاده از پرش شرطي) در 486 و بالاتر از 486 سريعتر اجرا مي شود.

پرش شرطي jnz موقعي پرش مي كند كه فلاگ صفر ست نشده باشد. و وقتي cx به صفرمي رسد شرط ديگر برقرار نيست و پرش صورت نمي گيرد.

= = = == == = = == =
How to use a debugger :

در اين سايت توربو ديباگر توصيه شده. منظورش اين بوده كه بعد ساخت فايل خود آن را با توربو ديباگر نگاه كنيم. و بفهميم كه برنامه ما به چه شكلي درمي آيد.

اول بايد يك برنامه داشته باشيم كه برنامه زير را پيشنهاد كرده:
کد:
; example program to demonstrate how to use a debugger

.model tiny
.code
org 100h
start:

push ax ; save value of ax
push bx ; save value of bx
push cx ; save value of cx

mov ax,10 ; first parameter is 10
mov bx,20 ; second parameter is 20
mov cx,3 ; third parameter is 3

Call ChangeNumbers

pop cx ; restore cx
pop bx ; restore bx
pop ax ; restore dx

mov ax,4C00h ; exit to dos
int 21h

ChangeNumbers PROC

add ax,bx ; adds number in bx to ax
mul cx ; multiply ax by cx
mov dx,ax ; return answer in dx
ret
ChangeNumbers ENDP

end start

بعد با دستور زير در توربو ديباگر باز مي كنيم:
کد:
td name of file

تصوير زير نمايي ازبرنامه وي را نشان مي دهد:

a1.jpg

دستورات اول را در ابتداي قطعه در اين عكس مي بينيد يعني اينها:
کد:
cs:0000 50 push ax
cs:0001 53 push bx
cs:0002 51 push cx
من با ديباگ ويندوز باز مي كنم:


a1.jpg

همان كد بالا را فايل com ساختم و در ديباگ با دستوري كه در تصوير هست بازش كردم. و با u 100 ليست دستورات را گرفته ام. شما هم برنامه هايي را بسازيد و با ديباگ نگاه كنيد تا متوجه بشويد كه چطور زيربرنامه ها و پرش ها و حلقه ها كامپايل مي شوند.
در تصوير بالا به مشخصاتي از فايل com اشاره شده. اول اين كه همه قطعه ها شماره قطعه اشان يكي است يعنيبر هم منطبق هستند و ديگر اين كه نشانگر پشته به انتهاي قطعه fffe اشاره مي كند. و هر چه push كنيد اين رقم كوچك مي شود. يعني از ته قطعه به سمت پايين حركت مي كند.
يك فايل exe را هم در ديباگ ويندوز باز كنيد و مقايسه كنيد با فايل com .
.
در فايل اگزه دستورات از خط 100 شروع نمي شوند
بلكه از صفر شروع مي شوند.
.
 
آخرین ویرایش:

saalek110

Well-Known Member
صفحه هفتم (صفحه آخر):

برنامه زير براي انتقال cursor است و برنامه ساده اي است. از خواندن كامنت ها براحتي طرز كارش معلوم است.
از يك وقفه براي انتقال كرزر استفاده شده و قبل صدا كردن آن سرويس و ورودي هاي وقفه را تنظيم كرده.
بعد هم خواندن رشته با سرويس 9 وقفه 21 و چاپ آن. و در آخر هم دستورات خاتمه برنامه.
برنامه هم com است. بي مشكلي اجرا شد.
كد:
کد:
.model tiny 
.code
org 100h
start:

mov dh,12 ; cursor col 
mov dl,32 ; cursor row
mov ah,02h ; move cursor to the right place
xor bh,bh ; video page 0
int 10h ; call bios service

mov dx,OFFSET Text; DS:DX points to message
mov ah,9 ; function 9 - display string
int 21h ; all dos service

mov ax,4C00h  ; exit to dos
int 21h

Text DB "This is some text$" 

end start

برنامه زير هم براي نوشتن توسط سرويس 40 وقفه 21 به كار مي رود.
برنامه exe است.

کد:
.model small
.stack
.code

mov ax,@data ; set up ds as the segment for data
mov ds,ax 

mov ah,40h ; function 40h - write file
mov bx,1 ; handle = 1 (screen)
mov cx,17 ; length of string
mov dx,OFFSET Text ; DS:DX points to string
int 21h ; call DOS service

mov ax,4C00h ; terminate program
int 21h

.data

Text DB "This is some text"

End

برنامه بعدي استفاده از سرويس 13 از وقفه 10 است. ميشه رنگي و هر جاي صفحه چاپ كرد ولي تنظيم آن كمي مشكل است. فايل exe است . بي اشكال اجرا شد. نتيجه اجرا هم چاپ در وسط صفحه و رنگي.
كد:
کد:
.model small
.stack
.code

mov ax,@data ; set up ds as the segment for data
mov es,ax ; put this in es

mov bp,OFFSET Text ; ES:BP points to message
mov ah,13h ; function 13 - write string
mov al,01h ; attrib in bl,move cursor
xor bh,bh ; video page 0
mov bl,5 ; attribute - magenta
mov cx,17 ; length of string
mov dh,5 ; row to put string
mov dl,5 ; column to put string
int 10h ; call BIOS service

mov ax,4C00h ; return to DOS
int 21h

.data

Text DB "This is some text"

end

برنامه زير نشان مي دهد كه چگونه با استفاده از rep stows ميشه در حافظه تصوير نوشت.
فايل exe است . بي نقص اجرا شد. براي اجرا در داس پرومپت اجرا كنيد.
کد:
.model small
.stack
.code

mov ax,0B800h ; segment of video buffer
mov es,ax ; put this into es
xor di,di ; clear di, ES:DI points to video memory
mov ah,4 ; attribute - red
mov al,"G" ; character to put there
mov cx,4000 ; amount of times to put it there 
cld ; direction - forwards
rep stosw ; output character at ES:[DI]

mov ax,4C00h ; return to DOS
int 21h

end

شرح برنامه بالا:

حافظه تصوير در 0B800 قرار دارد. ابتدا اين عدد را در ax و سپس در es گذاشته. چون مستقيما نميشه به es مقدار داد.
بعد di را صفر كرده. كلا ما داريم ES:DI را براي نشانه روي روي حافظه تصوير تنظيم مي كنيم.
در ah رنگ را تنظيم كرده. و در al كاراكتر را تنظيم كرده. (حرف g ).
مي دانيد كه دستور rep كارش تكرار است به تعداد cx ، پس cx را ابتدا تنظيم كرده. 4000 تا تنظيم كرده. مواظب باشيد شما بيشتر از حد لازم ندهيد. چون از حافظه تصوير رد مي شويد و مي رويد به قسمتهاي ديگر ram كه ممكن است باعث شود مجبور شويد ريست كنيد.
بعد با cld جهت را تعيين كرده كه فوروارد (رو به جلو) است.
در آخر هم كه دستور stows باعث انتقال به ram است.

پس حالا با اين برنامه شما در مي يابيد كه در حافظه تصوير براي هر كاراكتر دو بايت در نظر گرفته شده. كه ما اين دو بايت را در ah و al محيا كرده بوديم. با تغيير اعداد در ah و al ببينيد چه نوع جلوه هايي مي توانيد به وجود آوريد.

= = = = = == = = =
برنامه بعدي نشان مي دهد كه چطور مي شود يك رشته را در حافظه تصوير نوشت. فايل exe . اجرا بي اشكال.
کد:
; write a string direct to video memory 
.model small
.stack
.code

mov ax,@data
mov ds,ax

mov ax,0B800h ; segment of video buffer
mov es,ax ; put this into es
mov ah,3 ; attribute - cyan
mov cx,17 ; length of string to print
mov si,OFFSET Text ; DX:SI points to string
xor di,di

Wr_Char:

lodsb ; put next character into al 
mov es:[di],al ; output character to video memory
inc di ; move along to next column
mov es:[di],ah ; output attribute to video memory
inc di

loop Wr_Char ; loop until done

mov ax,4C00h ; return to DOS
int 21h

.data

Text DB "This is some text" 

end

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

شرح كدها:
در اين برنامه از lodsb براي انتقال از ram به al استفاده شده. و قبلش بايد براي lodsb تنظيماتش را انجام داد.
بعد كه با اين دستور al را پر كرده با دستور :
کد:
mov es:[di],al

به حافظه تصوير منتقل كرده. بعد يا همين نوع mov آمده ah كه صفت رنگ كاراكتر است و براي همه كاراكترها در اين برنامه ثابت است را به حافظه تصوير منتقل كرده.
قشنگي اين برنامه اين است كه از دو راه با ram ارتباط برقرار كرده يكي با lodsb و ديگري با mov .
البته مي بينيد كه دستور mov هم برايش بايد es و di را تنظيم كرد. براي lodsb هم همين طور. پس تنظيم قطعه و آفست براي اين دو را بايد در نظر داشت كه همه كدهاي اول برنامه مشغول همين كار بوده اند.
حلقه اي كه با loop ساختيم هم مي دانيد كه قبلش بايد برايش cx تنظيم بشه. و داخل حلقه هم di داره يكي يكي لفزايش مي يابد تا خانه هاي حافظه تصوير به نوبت با حلقه پر شوند.
 

saalek110

Well-Known Member
ادامه صفحه هفتم:

Mode 13h :
اين mode فقط در كارتهاي VGA, MCGA و بالاتر ساپورت ميشه.
با اين mode استفاده آسان گرافيك ميسر است.
براي چك اين كه اين mode را كامپيوتر شما مي تواند ساپورت كند برنامه exe زير را اجرا كنيد:
کد:
.model small
.stack
.data

NoSupport db "Mode 13h is not supported on this computer."
  db ,"You need either a MCGA or VGA video" 
  db ,"card/monitor.$"

Supported db "Mode 13h is supported on this computer.$"

.code

mov ax,@data ; set up DS to point to data segment
mov ds,ax ; use ax 

call Check_Mode_13h ; check if mode 13h is possible

jc Error ; if cf=1 there is an error

mov ah,9 ; function 9 - display string
mov dx,OFFSET Supported ; DS:DX points to message
int 21h ; call DOS service

jmp To_DOS ; exit to DOS

Error:
mov ah,9 ; function 9 - display string
mov dx,OFFSET NoSupport ; DS:DX points to message
int 21h ; call DOS service

To_DOS:
mov ax,4C00h ; exit to DOS 
int 21h

Check_Mode_13h PROC ; Returns: CF = 1 Mode 13h not possible

mov ax,1A00h ; Request video info for VGA
int 10h ; Get Display Combination Code
cmp al,1Ah ; Is VGA or MCGA present?
je Mode_13h_OK ; mode 13h is supported
stc ; mode 13h isn't supported CF=1

Mode_13h_OK:

ret 
Check_Mode_13h ENDP

end

ديگه اين روزها همه كامپيوترها پيشرفته اند و فكر نمي كنم كسي باشه كه جواب رد از اين برنامه بشنود. براي من هم گفت كه ساپورت مي كند.

حالا استفاده:
برنامه زير(نوع com ) در mode گرافيكي تعدادي پيكسل بر صفحه رسم مي كند:
کد:
; example of plotting pixels in mode 13 using bios services - 
; INT 10h

.model tiny
.code
org 100h
start:

mov ax,13 ; mode = 13h 
int 10h ; call bios service

mov ah,0Ch ; function 0Ch
mov al,4 ; color 4 - red
mov cx,160 ; x position = 160
mov dx,100 ; y position = 100
int 10h ; call BIOS service

inc dx ; plot pixel downwards
int 10h ; call BIOS service
inc cx ; plot pixel to right
int 10h ; call BIOS service
dec dx ; plot pixel up
int 10h ; call BIOS service

xor ax,ax ; function 00h - get a key
int 16h ; call BIOS service

mov ax,3 ; mode = 3
int 10h ; call BIOS service

mov ax,4C00h ; exit to DOS
int 21h

end start
 

saalek110

Well-Known Member
براي شرح برنامه نمايش پيكسل پست قبلي من سري زدم به اين سايت:
وقفه 10 را مي توانيد شرح سرويس هايش را در اينجا ببينيد:

http://www.bookcase.com/library/dos/ints/int10.html

===========================
با توجه به اين صفحه در برنامه دوم پست قبلي ، كد زير:

mov ax,13 ; mode = 13h
int 10h ; call bios service

درست و دقيقش اينه كه ما ah را صفر كرديم و al را برابر 13 قرار داديم تا mode را به حالت گرافيكي ببريم.

در قطعه بعدي برنامه ، يعني قطعه زير:

mov ah,0Ch ; function 0Ch
mov al,4 ; color 4 - red
mov cx,160 ; x position = 160
mov dx,100 ; y position = 100
int 10h ; call BIOS service

داريم set pixel مي كنيم. نقل از سايت بالا:
کد:
Set Pixel

Call 
AH = 0Ch 
AL = Pixel Value 
If bit 7 is set, then value is XOR-ed (not in 256 color mode) 
BH = Page Number 
CX = Pixel Column 
DX = Pixel Row 
Return 
N/A

در سينتكس بالامي بينيد كه چه تنظيماتي براي set pixel مورد نياز است. در برنامه ما bh مقدار دهي نشده. يعني نخواسته ايم page تعيين كنيم.

در قطعه بعدي برنامه ما ، يعني قطعه زير:

inc dx ; plot pixel downwards
int 10h ; call BIOS service
inc cx ; plot pixel to right
int 10h ; call BIOS service
dec dx ; plot pixel up
int 10h ; call BIOS service

كار خاصي انجام نشده. فقط بازي با مختصات پيكسل است و بعد تغيير مختصات احضار وقفه 10 . دستورهاي inc و dec ( اينكريز و دكريز-افزايش و كاهش) را كه آشنا هستيد.
توجه كنيد كه فقط مدام وقفه صدا شده و تنظيمات قطعه قبلي دارد اينجا استفاده مي شود.

قطعه بعدي برنامه ، يعني قطعه زير:

xor ax,ax ; function 00h - get a key
int 16h ; call BIOS service

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

mov ax,3 ; mode = 3
int 10h ; call BIOS service

اين جا باز به mode شماره 3 برمي گرديم تا براي خروج آماده شويم.

و اين هم قطعه خروج :

mov ax,4C00h ; exit to DOS
int 21h
 

saalek110

Well-Known Member
ادامه صفحه هفتم:

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

در برنامه قبلي ( نمايش پيكسل) سرعت بالا نيست و سرعت بالاتر را با نوشتن مستقيم در حافظه تصوير مي توانيم بدست آوريم.

The VGA segment is 0A000h.
قبلا ديديم كه وقتي مي خواستيم كاراكتري را روي صفحه چاپ كنيم يك راه اين بود كه مستقيما داخل حافظه تصوير بنويسيم و آدرسش را در پستهاي قبلي گفتيم و چند برنامه هم بنا بر آن حافظه نوشتيم.

و حالا آدرس حافظه گرافيكي را به شما مي دهيم. و روش نوشتن در حافظه مثل همان مثالهاي حافظه كاراكتري است و ديگه مثال نمي زنيم.
The VGA segment is 0A000h.

پس پيكسل ها را مستقيما ايجاد مي كنيم.
از فرمول زير هم براي محاسبه آفست استفاده كنيد.
کد:
Offset = X + ( Y * 320 )

شما بايد عددي را در اين حافظه ها قرار دهيد. كه رنگ پيكسل است.

روشهاي قرار دادن در حافظه:

راه اول: با stosb (بايت منتقل مي كند از al ) به آدرس: ES:DI

راه دوم: با استفاده از mov با كد زير:
کد:
mov es:[di], color

مقايسه اين دو روش:
از لحاظ سرعت در پنتيوم براي stosb رقم 3 و براي mov رقم 1 گذاشته شده. حالا من نفهميدم كه اين اعداد 1 و 3 سرعت است يا زمان طول كشيده شده. بعدا از منابع بعدي تكميل مي كنيم.

در مورد mov هم مي دانيد كه بايد بعد هر عمل بايد di را افزايش دهيد.

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

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

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

و ثانيا هدف من تمرين با برنامه هاي ساده تر و ساختن برنامه هايي در جهت درك محيط كار است.

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

saalek110

Well-Known Member
سايت زير:
http://courses.ece.uiuc.edu/ece390/books/artofasm/artofasm.html
اسمبلي 32 بيتي است و كمي سنگين است مباحثش. ولي به نظرم اساسي كار كرده.
در 13 صفحه آموزش داده. البته سورس نداده. . فقط شكل دستوراته و بحث و بيشتر 32 بيتي است نه 16 بيتي. يعني كمي انرژي بيشتري مي برد تبديلش تا اون سايت 7 صفحه اي كه حاضر آماده بود.

بگذريم.

عكسهاي خيلي عالي ئي هم داره. مخصوصا 3 صفحه اول كه عكساش را يك نگاهي بكنيد خيلي خوبه. جداولش را هم ببينيد خيلي عاليه. مثلا يك جدول داره كه گفته از ابتدايي ترين كامپيوترها تا حالا ، هر كدام چه مقدار حافظه را پشتيباني مي كند. كه پنتيوم 4 گيگا بود. البته پنتيوم هاي جديد نبود شايد. پيشنهاد مي كنم 13 صفحه را باز كنيد و در هاردتان ذخيره كنيد و حداقل يك نگاهي به صفحات بكنيد. خيلي خوب دستورات را دسته بندي كرده.
كلا براي حرفه اي ها مي تواند عالي باشد.
ولي اگر فعلا وقت نداريد مهم نيست. سايتهاي بهتري هم هستند.
 

saalek110

Well-Known Member
چاپ رجيستر:

فرض كنيد دو دستور زير را داريم:
Mov al,3
Inc al
كه دستور اول به al مقدار مي دهد و دومي آن را يك واحد افزايش مي دهد.
حالا مي خواهيم al را ببينيم كه متوجه بشويم كه آيا يك واحد افزايش پيدا كرده يا نه.

اينجاست كه چاپ رجيستر معني پيدا مي كند.
در شكل زير من دو دستور را در ديباگ نوشتم و مي بينيد كه درست كار مي كنه . ولي من مي خواهم خودمان al را چاپ كنيم.


a1.jpg
 
آخرین ویرایش:

saalek110

Well-Known Member
براي مقدمه بايد بگويم كه چه رجيسترها كه حافظه هايي داخل cpu هستند و چه حافظه هاي موجود در ram همه از تعدادي بيت تشكيل شده اند.

واحد خريدن حافظه هم بيت است. البته بايت است كه 8 بيت است و به طور روزمره به مگا بايت و گيگا بايت نام برده مي شود كه ميليون و ميليارد بايت است.

ولي منظور اين كه بيت اساس سخت افزار است.

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

حالا al را در نظر بگيريد. يك بايت است. با ah كه آن هم يك بايت است مي شود دو بايت. كه دو بايت را يك word يا كلمه مي گويند.

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

ما قبلا دستوراتي را آموزش داديم كه به راحتي يك رجيستر را به ram و بالعكس منتقل مي كرد.
و دستوراتي داريم كه به راحتي مي تواند رجيسترها را به هم ديگر انتقال دهد.

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

حالا بحثمان ميشه چاپ بايت:
بايت 8 بيت است.
در مبناي 16 هر 4 بيت را مي شود اين طوري خواند. 0123456789abcdef .
شد 16 حالت. از صفر تا f ميشه 16 حالت. پس al را ميشه 4 بيت ، 4 بيت خواند تا بشه اين طوري a2 يا cc يا 23 يا ff و .....

اما سئوال مهم اينجاست كه ما مي خواهيم al را اين طوري چاپ كنيم 22 يا اين طوري 0010 0010 . اين حالت صفر و يكي هم همان است ولي در مبناي 2 است.
حقيقتش اينه كه اين حالت صفر و يكي همان حالت حقيقي حافظه است چون بيت ها همين طور پشت سر هم به حالت ((صفر و يك)) (روشن و خاموش) داخل ram (يا جاي ديگر) نشسته اند.

ولي كلا خواندن صفر و يك ها مشكل است پس بهتره كه به همان حالت 22 بخوانيم. ولي اين 22 در مبناي 10 نيست ها. در مبناي 16 است. در مبناي 10 اگر مي خواهيد بايد تبديل كنيد.

اين عدد 00100010 را به مبناي 10 تبديل كنيد:
ميشه 2 بعلاوه 32 . چرا؟ جواب: از سمت راست اين طوري بخوانيد يك – دو – چهار – هشت – شانزده – سي و دو – شصت و چهار. و هر جا يك داشتيم اين اعداد را موجود دانسته و با هم جمع بزنيد.

حالا عدد 22h را به مبناي 10 تبديل كنيد. ميشه 2 بعلاوه 2 ضرب در 16 .(عدد سمت راست بعلاوه عدد سمت چپ ضرب در 16 )

نتيجه هر دو يكي است. ولي دومي راحت تر بود.

من دو برنامه آماده دارم كه يكي به شكل صفر و يك چاپ مي كند و يكي در مبناي 16 . ولي مي خواهم دومي را بگذارم. چون به نظر من كاربردش بيشتره.
.
 

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 بتواند راه برگشت خود را پيدا كند.


a1.jpg

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

saalek110

Well-Known Member
منبع عكس ها:
Articles and Tutorials - GameDev.net

معرفي نام رجيسترها:

a1.jpg


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

a1.jpg
 
آخرین ویرایش:

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
امروز سايتهاي فارسي اسمبلي را سرچ كردم. آموزشها و نمونه برنامه هاي خوبي پيدا كردم.
يكي از آنها تاپيكي به نام ((درخواست كمك در مورد اسمبلي)) است با لينك زي:
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

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

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

بالا