< Asembler Dili (Assembly Language) 2 - Computer & Technology & Science - Blogcu





14/4/2006

Asembler Dili (Assembly Language) 2

Assembler komutlari

Bu kadar genel ve ön bilgi sonrasi artik komutlari tanimaya baslayabiliriz. Assembler komutlari genel amaçli komutlar, mantiksal komutlar, sistem komutlari gibi bölümlere ayrilmaktadir.

Daha önce söz ettigimiz MOV, LEA komutlari registerleri yükleme, adresleme veya degerini bir yere yazdirma için kullanilir. Genel kullanilis sekli : MOV register, deger veya MOV [adres],register seklindedir.  Bu komut birden fazla registeri bir arada kullanmayada olanak verir. Örnegin : MOV register, [register+register] gibi.

mov register,[adres]
mov [adres],register
mov [adres+register],register
mov register, [adres+register]
mov register, [register+register]

gibi burada sadece registerler arasinda toplama degil çikartma ve çarpma islemide yapilabilimektedir.

LEA komutu hem registere deger yüklemek için hemde dogrusal bir hafiza adresinin alinmasi için kullanilir.

lea register, adres
lea register, [adres]
lea register, register*4
lea register, register+10h

gibi kullanilir. Ileride uygulamali örneklerde MOV ile LEA arasindaki farki daha iyi göreceksiniz.

Ayrica LES, LDS, LFS, LSS, LGS  gibi genelde gerçek modda ve yüksek düzeyli korumali mod mimarisinde kullanilan  register yükleme komutlarida vardir. Bunlar interrupt ve segment ayarlamalarinda kullanilir. Korumali modda Segment seçici görevini yerine getirir. Su andaki sistemlerde çok fazla kullanim alanlari yoktur.

Buradan sonra çok kullanilan önemli komutlari inceleyecegiz. Bazi komutlar çalistiktan sonra birden fazla komutu etkileyebilir. Bunlari mümkün oldugunca açiklayacagim. Ayrica mümkün oldugunca her komut için örnek verecegim.

Registerleri degistiren, aktaran, ekleyen baska komutlarda vardir.

XCHG KOMUTU

XCHG komutu iki registerin yada registerler bir yerin yer degisimi saglar. Bu komut 4-5 tane ayri komut yapilacak bir islemi tek basina gerçeklestirir. Kullanilisida çok basittir.

XCHG REG1, REG2

XCHG REG, [ADDR]

XCHG [ADDR],REG

gibi kullanilir. Buradaki tek sart yer degisecek register veya adresin ayni bit uzunluguna sahip olmasidir.

XLAT  KOMUTU

XLAT komutu esas olarak BX (veya EBX) registeri esas alarak çalisir. Buna göre BX registerle gösterilen bir adresteki bilgiyi almak için AL registeri BX register ile toplar ve gösterdigi noktadaki bilgiyi (8 bitlik) alip AL register yükler. Bu islemde sadece AL registerin degeri degisir.

 

 

 

 

Örnegin:

yazi   db 'ABCDEFGH',0

        mov al,3           ; AL = 3
        lea ebx, yazi    ; EBX = yazi [ABCDEFG]
        xlat                  ; EBX+AL = [- - *---]   AL= 'C' olur

XLAT komutu bazi zamanlar XLATB olarakda geçer ve bunlar farkli degil ikiside aynidir.

MOVSX ve MOVZX komutlari

Bu komutlarda diger komutlar gibi kodu kisaltmak ve daha hizli islem yapmak içindir. MOVZX komutu bir adresdeki 8 bitlik sayiyi 8 bit registere yükler ama 8 bit disindaki bitleri resetler (sifirlar) Böylece elimizde 8 bitlik tertemiz bir deger olur. MOVSX komutuda benzer olarak registeri yükler ve diger bitleri temizler ancak bu islem 16 bitten 8 bite veya 32 bitten  8 bite olmaktadir.

deger2  db 10
....
mov eax,12345678h                   ; eax = 12345678
movzx  eax, byte ptr [deger2]     ; deger2 = 10, eax =10 oldu

Yuikaridaki örnekte önceden EAX registere 12345678 gibi 32 bitlik bir deger girdik. [deger2] adresinin 8 bit olduguna dikkat edin. MOVZX komutundan sonra EAX register sadece 10 degerini alacaktir. Eger MOVZX kullanmadan normal bir AL register yüklemesi yapsaydik EAX registerin sadece AL kismi degisecek ve EAX 12345610 degerini alacakti.

Kombine Veri komutlari (LODS, MOVS, CMPS, STOS, SCAS)

Bu komutlar birden fazla registerle, birden fazla kere islem yapmayi amaçlayan güçlü komutlardir. Bunlar tekil olarak yada REPZ, REPNZ, REPNE gibi bu komutlara özel tahsis edilmis yardimci komutlar ile beraber çalisir. Hepsi yanina yapacagi bit sayisina göre ek alirlar. Bu komut kullanacagi bit degerine göre yanina simge alir. Ancak bu bizim tarafimizdan tahsis edilen bir özellik degildir. Bu standart komut setinin ayri ayri ayirdigi opkodlardir. Bu opkodlar konunun sonunda görecegimiz REP komutu ile kullanilisinda her defasinda yapilacak islem miktarinida ayarlar.

B = byte  (8 bit) bazinda islem
W= word (16 bit) bazinda islem
D= dword (32 bit) bazda islem

Öncelikle anlasilabilmesi için tekil kullanimlarini sonrada sayaçli kullanimlarini inceleyecegiz.

LODS komutu

Bu komut yanina aldigi eke göre 8 ile 32 bit arasinda islem gücü kazanir. Temel görevi SI (yada ESI) registerin gösterdigi noktadaki degeri alip bit degerine göre A (AL, AX, EAX) registere yüklemektir. Ayrica LODS komutu degeri yükledikten sonra SI registerin degerini kullanilan bit degerine göre arttirarak sonraki veriye göre ayarlamis olur. Bu byte için 1, word için 2 ve dword için 4 byte otomatik olarak toplanacak demektir. Daha önce gördügünüz XLAT komutuna benziyor olmasina karsin LODSB çok farklidir. Asagida verilen ve diger örneklerde verilen bit degerleri gerçek modda veya korumali modda aynidir. Kullanim ihtiyacina göre hepsi kullanilabilir.

LODSB  = 1 byte islem için (8 bit)
LODSW = 2 byte islem için  (16 bit)
LODSD = 4 byte islem için (32 bit)

 

Örnek:

ornekyazi   db 'ASM GENEL',0
.....
lea esi,ornekyazi   ; ornekyazi noktasinin baslangiç adresini ESI registere yükledik.
lodsb                     ; al registere ilk harf olan 'A' harfi yüklendi. ESI register byte olarak kullandigimiz için 1 artti.

Eger ilk iki harfi alip ax registere yüklemek isteseydik lodsw kullanacaktik. Yine ilk 4 byte için lodsd kullanacaktik.

MOVS komutu

Bu komutta LODS komut gibi yanina aldigi B,W,D takilari ile ayri bir opkod halini alir ve buna göre bitte islem yapar. MOVS komutu DI (veya EDI), SI (veya ESI) ve registerleri ile beraber çalisir. SI kaynaktir yani aktarilacak verini adresini gösterir. DI ise hedeftir gidecegi yeri gösterir. Komut sonrasi her iki registerde kullanilan bit degerine göre ekleme olur ve yeni yeri gösterirler.


buf1    db 'YAZI',0
buf2    db 0,0,0,0
......
lea esi,buf1    ; buf1 = 'YAZI'
lea edi,buf2   ; buf2= 000000000
movsd           ; buf1 = 'YAZI ' ve buf2 = 'YAZI


Bu islem sonunda buf1 adresindeki veri buf2 bölgesine aktarilir. buf2 bölgesinde bir degisme olmaz, ayni kalir. Burada eger MOVSB kullanilmis olsaydi sadece ilk harf aktarilacakti. Eger MOVSW kullanilmis olsa o zaman ilk iki veri aktarilacakti. Bunlar kullanilan bit degerlerine bagli.

CMPS Komutu

CMPS komutu ileride görecegimiz karsilastirma komutunun kombine bir seklidir. Bu komut yine tekil olarak digerleri gibi yanina aldigi ek ile orantili olarak bit sayisi kadar islem yapar. Yine bu komut SI (veya ESI) ve DI (veya EDI) registerler ile birlikte çalisir.Bu komut tüm karsilastirma ve test komutlari gibi bayrak registeri etkiler. Bu komutu kisaca SI register ile gösterilen bölgeyi DI ile gösterilen bölge ile karsilastirir diye açiklayabiliriz. Bu islem tamamen hafiza bölgesi ile ilgilidir. Bu komut ileride görecegimiz karsilastima komutlari gibi registerler karsilastirmasi yapmaz. Karsilastirma sonucunu  ZF (zero flag) ile aliriz. Eger iki bölgede verdigimiz bir degeri kadar karsilastirilmasi sonucu ayni ise ZF set (1) olur. Eger yanlis ise ZF reset (0) olur. Bunu nasil kullanacagimizi ileri sartli atmala komutlarinda görecegiz. Simdilik asagidaki örnek bunun nasil kullanildigi konusunda  bir fikir verebilir.

buf1  db 01,02,03,04
buf2 db 02,03,01,04
....
lea esi, buf1        ; asil bölge
lea edi, buf2       ; karsilastirilan bölge
cmpsd                 ; dword (32 bit ) olarak karsilastirma
jz dogru              ; eger ayni ise  "dogru " etiketli  yere atlar. Degilse devam eder.
....

STOS Komutu

Bu komut A register (AL, AX, EAX) ile DI register (veya EDI) ile beraber çalisir. STOS komutu digerleri gibi yanlarina aldiklari isaret ile opkod eslenir ve bu eslesmeye göre islemde bit seviyesi kullanir. STOS komutu A registerdeki degeri DI registerin gösterdigi bölgeye aktarir.

buf1   db 00,00,00,00
......
lea edi, buf1
mov al,89h
stosb

Bu islem sonunda stosb kullandigimiz için 1 byte (8 bit) bir yazma olacak ve buf1 db 89h,0,0,0 seklinde olacaktir. Eger STOSW komutu kullanmis olsaydi 2 byte yazacakti ve dolayisiyla bu AX register olacakti. Ayni seklide STOSD kullansaydik 4 byte olacakti ve bu EAX register olacakti.

SCAS Komutu

SCAS komutu aramak - taramak kelimesinin kisaltilmisidir ve yaptigi islemde taramadir. Tek basina pek bir islevi yoktur. Asil olarak asagida göreceginiz gibi REP tekrarlaticisi ile güçlü bir arama - tarama islevi kazanir.

REP komutunu LODS, MOVS, CMPS, STOS ve SCAS  ile kullanilislari

REP komutu yukarida gördügümüz 4 sinif komut ile kombine çalisarak onlarin tek basina yaptiklari isi grup halinde ve istenilen sayida yapilmasini saglar. Burada önemli olan sayidir ve sayiyi CX (veya ECX) register ile belirtiriz. Diger kullanimlar her komut için yukarida anlattigim gibi ilgili registerlere ve kullanilan alanlara baglidir. REP komutu bir döngü olusturur ve verilen komutu belirlenen CX (veya ECX) kadar yapar.

REP komutu REPZ, REPE, REPNE olarak döngüyü kontrol altina alma olanagida vardir. REPZ ile CX (yada ECX) 0 oluncaya kadar devam et anlamindadir. REPE yine CX register esit oluncaya kadar ve REPNE ise esit olmayincaya kadar anlamindadir. REPZ genelde çok kullanilir ama DF bayragi degistirilerek tersine islem ile digerleride rahatça kullanilabilir.

MOVS ve REP kullanilisi

MOVSB daha önce anlattigim gibi kombine veri aktarma görevini yerine getiriyordu. REP komut ile SI ile belirtilen hafiza bölgesinden DI ile belirtilen hazifa bölgesine CX kadar aktarma yapar. Örnegin:

lea esi,buf1
lea edi,buf2
mov ecx,100h
repz
movsb
....
buf1 bölgesinden buf2 bölgesine 100h byte seklinde aktarma yapiliyor. Eger MOVSB yerine MOVSW kullanlirsa aktarma Word (16 bitlik) olacak ve burada kullandigimiz ECX = 100h * 2  olacak. Çünkü her sayimda 1 degil 2 veri aktarmis olacagiz. Ayni sekilde MOVSD kullanirsak 32 bitlik bir aktarma komutu kullandigimiz için 100h * 4 olacaktir.

CMPS ve REP kullanilisi

CMPS komutuda MOVS komutu gibi REP komutu ile beraber kullanilinca döngü içerisinde kontrol saglar. Bu komutun en büyük avantaji çok uzun veri yada yazilarin rahatlikla karsilastirilabilmesidir. Normalde bu komut olmasa döngü içinde tek tek karsilastiran bir program yazmak gerekir. Ancak REP CMPS kombinasyonu ile güçlü bir kontrol mekanizmasi olusturulabilir.

str1        db 'mumemu.kolayweb.com',0
str2        db 'yigiter',0
.....
lea esi,str1
lea edi,str2
mov ecx,8
repz
cmpsb
jz ayni
:::::::::

Burada str1 bölgesi ile str2 bölgesi karsilastiriliyor ve karsilastirma uzunlugu 8 byte. Eger ayni ise JZ AYNI ile baska yere atlaniliyor eger degilse devam ediliyor. Buradaki karsilastirma sonucunda ZF (Zero flag = sifir bayragi) etkilendigi için istenildigi gibi düzenlenebilir.

 

STOS ve REP kullanilisi

STOS komutu doldurma komutu oldugunu daha önce anlattim. Bu komut DI (veya EDI) komutu ve A (AL, AX, EAX) ile beraber kombine çalisir.DI ile belirtilen hazifiza bölgesini A register ile doldurulur. Burada doldurma yine CX (veya ECX) sayisi kadardir. Yine MOVS komutunda anlattigim gibi kullanilan registerin biti ve komutun çalisma biti doldurma alanini büyütür. Eger ECX=100h verip STOSW kullanirsak 100h * 2 kadar olacaktir.

buf1        db 0,0,0,0,0,0,0,0,0,0,0,0,0
....
lea edi, buf1
mov ecx, 5
mov al,99h
repz
stosw

Burada buf1 bölgesini 99 sayisi ile dolduruyoruz. Normalde ECX ile 5 kere tekrarlanacak olarak verildigi halde STOSW kullandigimiz için 5 * 2 = 10 bytelik bir doldurma olacaktir.

LODS ve REP kullanilisi

LODS ile REP komutu genelde döngü içerisinde kullanilir. Tek basina kullanilmasi SI (veya ESI) register ve CX (veya ECx) register ile saglanir ve yine digerlerinde oldugu gibi kullanilan bit uzunluguna göre opkod alir. Kullanilan bit uzunlugunca A register (AL, AX, EAX) SI registerdeki veriyi alir.

buf1    db 'ASM GENEL'
....
lea esi, buf1
mov ecx,5
repz
lodsb

bu islem sonucunda ESI register ECX register kadar veriyi buf1 adresinden alip AL registere aktarir.

STOS ve REP  kullanilisi

STOS komutu daha önce anlattigim gibi arama - tarama komutudur. Bu komut DI (veya EDI) register ile belirtilen bölgede CX (veya ECX) kadar A registerdeki (AL, AX, EAX) degeri arar. 8, 16 ve 32 bitlik bir veriyi istedigimiz hafiza alaninda arama konusunda bir hayli hizlidir. Yine digerlerinde oldugu gibi yanina aldigi ek ile opkodu sekil alir ve dolayisiyla arama degerinin biti buna göre ayarlanir. STOSB byte, STOSW word, ve STOSD dword aramasi yapar. Bu komutta aranilan deger bulundugu takdirde döngü sonlanir ve DI register aranilan degerin oldugu hafiza adresini gösterir.

buf1     db 'aranilan harfler CW olsun',0
.....
lea edi buf1
mov ecx,10
mov ax,'CW'
repz
scasb

Bu islem sonunda buf bölgesi aranacak ve 'CW' harflerinin oldugu adreste duracak. Sonuçta bize DI register ile adres vermis olacak.

MATEMATIK ISLEM KOMUTLARI

Toplama islemi

Bütün registerler arasinda standart toplama, çikarma, çarpma ve bölme islemleri rahatça yapilabilir. Ayrica daha ileri islemler için sonraki bölümlerde anlatacagim FPU (matematik islemci komutlari) vardir ve bunlarla daha üst dereceli hesaplamalar yapilabilmektedir. Simdi burada standart dört islemi inceleyecegiz. Burada genelde al, ax, eax, ebx gibi registerle örnekleme yaptim. Ancak diger registerler ilede ayni islemleri rahatça yapabilirsiniz. ESP gibi registerle islem yaparken dikkatli olmaniz gerekir çünkü ESP register sistemin yigin adresini belirttigi için degistiginde program içinde kilitlemelere neden olacaktir. Ayrica islemler sonucunda kalan sayilar konusunda EDX register yetkili kilinmistir.

Toplama yapmak için ya iki farkli degiskeni birbirleriyle toplariz yada kendisiyle kendisini toplariz. Registerler arasinda toplama veya adresler arasinda toplama islemi için ADD komutunu kullaniyoruz. ADD komutu 8,16,32 bit olarak islem yapilabilecegi gibi MMX ve FPU komutlari ile 64 bitlik islemde rahatça yapilabilir. Ayrica bazi registerler için gerekli olabilecek 48 bitlik bir ara degerde vardir.

Bu komut register+register, register+deger, deger+register, register+adres gibi degisik çok yönlü kullanima sahiptir. Eger AX registerimiz 100 olsa ve BX registerimiz 50 olsa bunlarin toplami 150 olacaktir. Bunu gerçeklestirmek için sadece:

add ax,bx

dememiz yeterli. Burada önemli husus ilk verilen register daima toplanan ikinci register eklenendir. Eger burada add bx,ax olarak kullanmis olsaydik bx registerin degerine ax registerin degeri eklenecekti. Ikinci eklenen registerin degeri degismez.

Register+deger seklinde kullanimda ise kullanilacak register ile istenilen bir deger toplanir. Örnegin:

add ebx,12345678h

gibi. Burada ebx registeri ile 12345678h degeri toplaniyor. EBX registerin o andaki degeri ne ise arti 12345678 degeride ekleniyor. Burada verebilecegim tüyo matematikte bildigimiz gibi sayidan önceki 0 (sifir) degersizdir. Diger islemlerde oldugu gibi matematiksel islemlerde sayidan önce sifir veya sifirlar eklemek gereksizdir. Ayrica sifir ile yapilan islemlerde bildigimiz matematiksel kurallar geçerli olur.

Eger bir adresteki  deger ile bir register toplanacaksa :

add word ptr [adres],ax

seklinde kullanilabilir. Burada toplanilan register ile adreslenen yerin ayni bit uzunlugunda olmasina dikkat edin. Mesela 16 bitlik bir ax register ile 8 bitlik bir alan toplanmaya çalisilirsa islem 8 bit olacaktir. Burada toplama islemi [adres] olarak simgelenen yere olur ve toplanan register degismez.

Eger bir register ile adres toplanacaksa:

add eax,dword ptr [adres]

seklinde kullanilir. Bu islemde [adres] olarak belirtilen hafiza adresindeki deger degismez, o deger register ile toplanir. Yani önceki örnegin tersidir.

Birde bayrak registerle beraber özel bir toplama komutuda mevcuttur. Bu ADC komutudur ve tasiyici (CF) bayrak  set (1) ise toplama anlamina gelir. Bu bazi matematiksel özel durumlarda kullanilir ve bir registerin degerini en yüksek noktasini geçmesi sonucunda ortaya çikan tasma durumunu anlamakta yardimci olur. Örnegin:

mov ax,0ffffh    ; ax reg = fff0 alabilecegi en büyük deger 0ffff' 'dir
add ax,10h       ; 0fff0 ile 10h toplaniyor ve 16 bitlik en büyük degere ulasiyor. Bundan sonrasi basa dönüs yani 0
adc dx,0           ; burada dx register 1 olacak çünkü ax registerde  tasma oldu carry flag (CF) set (1) durumunda

Yukaridaki örnekte görüldügü gibi ax registerin degeri 16 bitlik bir registerin alabilecegi en yüksek matematiksel degere yükseltildi. Bunun sonucu CF set oldugu için dx registerle 0 toplamasina ragmen dx register 1 olacaktir. Bunu programlarin içinde kullanarak daha iyi ögrenbilirsiniz. ADC bütün registerler için geçerli ve yukarida anlattigim toplama sekillerinde de aynen kullanilabilir. Önemli olan registerin degerini en yüksek degere ulasip ulasmamasidir.

 

Çikarma Islemi

Çikarma isleminin toplama isleminden tek farki komutun SUB olmasi ve islevinin çikarma olmasidir. Ayni toplama komutu gibi register arasi, register-adres, adres-register seklinde çikarma islemi yapilir. Toplama isleminde örnekledigim ADC komutu yerine SBB komutu kullanilir ve sonuç toplamada oldugu gibi deger en yüksek degerde ise CF bayragi set olur ve kullanilabilir durum olusur.

 

Çarpma islemi

Çarpma islevide registerler arasinda , adresler arasinda, ve kendi arasinda uygulanabilir. Çarpma islemi MUL ve IMUL komutlari ile gerçeklesmektedir. Ikisinin amacida çarpim oldugu halde MUL ve IMUL arasinda farkliliklar vardir. IMUL komutu 2 veya çarpima olanak vermekte ve kullanim sekli daha genistir. IMUL komutu 32 bit islemler için idealdir. Öncelikle MUL komutunu görelim.

MUL komutunda çarpilan daima AX (kullanim bitine göre AL ve EAX registerde) asil çarpilandir. MUL komunun yanina çarpimi yapacak register yazilir.

mov bx,20
mul bx

seklinde kullanilinca geçmemesine ragmen burada çarpilan ax registerin o andaki degeri ile bx register (burada 20 degerinde) çarpilir.

MUL komutu için CPU AX ve DX registeri yetkili kilmstir. Eger bir çarpma islemi sonucu ax registerin alabilecegi en büyük degeri geçiyorsa geçme sayisi DX register aktarilir. Eger geçme yoksa DX registerde bir degisme olmaz.
Böyle bir duruma örnek :

mov ax,100h
mov bx,1000
mul bx

IMUL komutu ise toplama ve çikarma konularinda görüldügü gibi çoklu register ve büyük degerlere olanak vermektedir. Yanliz MUL komutunda oldugu gibi tasma oldugunda DX register aktarilma yapilmaz.

Örnegin:

mov ax,0ffffh
mov bx,0ffffh
imul ax,bx

gibi kullanilir.

IMUL ile üçlü çarpimda mümkündür.

mov eax, 0ffffh
mov ebx,0ffffh
imul eax,ebx,768  ; [AX*BX]*768 = 02fff0000

Burada önce EAX*EBX islemi yapilir ardindan çikan sonuç ile 768 sayisi (bu sayi istedigimiz bir sayi olabilir 768 burada sadece örnek olarak) çarpilir. Burada islem büyük oldugu için 32 bit registelerle örnek verdim. Büyük degerlerde daima 32 bit registerler kullanin. Sonucu tam alabilmek için bu gerekli.

 

 

 

Bölme islemi

Bölme komutuda DIV ve IDIV olmak üzere kapsamli olarak iki tanedir. Çarpmada oldugu gibi DIV komutu tasma durumunda DX (veya EDX) komutu CPU tarafindan yetkili kilinmistir. Bölme isleminde DX register kalan sayilari verme konusunda görevlidir. Diger islemlerde oldugu gibi bölme isleminde de matematik kurallari geçerlidir ve matematikte "sifira bölüm" anlamsiz olarak tanimlanmistir. CPU' da sifira bölüm için özel bir kesme ayirmistir.

DIV komutu ayni MUL komutu gibidir.

mov ax,104h
mov bx,10h
div bx ;ax= 10 , dx =4
yukaridaki örnekte görüldügü gibi 104h/10h olur. 104h içerisinde 16 tane 10h vardir ve 04 sayisida kalandir. Bu islem sonucunda AX register 10h ve DX registerde 04 kalan sayisini alir. Burada örnek olarak 16 bir registerler ve sayilarla verdim. 32 bit registerler ile daha yüksek islem olanagi olur.

IDIV komutu ayni IMUL komutunda oldugu gibi daha yüksek islemler için idealdir. IDIV komutu DIV komutunun yerine kullanildigi zaman ayni islemi yapar. Ancak IMUL' da oldugu üçlü bölüm yoktur. Konu bölüm olunca bunun pek fazla geregi olmadigi görülüyor zaten.

YIGIN KOMUTLARI

Yigin konusundan daha önce bahsetmistim. Hafizada saklamasi gereken verileri yigin komutlari ile sistemin bize tahsis ettigi adreste saklariz. Bu döngülerde, çoklu islemlerde, matematiksel islemlerde çok önemlidir. Ayrica Windows' un API sistemi yigin üzerinden bilgi alisverisi saglancak sekilde tasarlanmistir. Bunun için yigin ve komutlari çok önemlidir.

Yigin isleminin ana merkezinde SP (veya 32 bit hali ESP) vardir.  SP ,stack pointer olarak açilir ve yigin noktasi anlamina gelir. Yani bu register bize yigin noktasinin dogrusal adresini verir. Gerçek modda bu isleme yardimci olarak SS registerde kullanilir. SS, Stack segment anlamina gelmekte ve Korumali mod programlamasinda gerek olmadigi için kullanilmamaktadir.

Bir degeri, adresi, registeri yigin noktasina attigimiz zaman yigin noktasi registerinden (SP veya ESP) moda göre düsme olur. Eger Windows altinda bir deger sakliyorsan o zaman SP registerden 16 bitlik bir deger olarak 2 byte gerileyecektir. Örnegin:

SP register burada 0FFFEh adresini gösteriyor.

push ax   ; 0FFFE - 2 = 0FFFCh
push bx  ; 0FFFC - 2 = 0FFFAh

oldu. Bu örnegi söyle düsünebilirsiniz, bir depomuz var. Bu depomuzun sadece 1 giris kapisi var. Biz buradan kutulari numaralarina göre önden baslayip arkaya dogru siraliyoruz. Tabi olarak deponun alani metrekare olarak düsüyor. Depo SP registerimiz olsa ve kutularda degerlerimiz olsa sanirim daha iyi anlasilir.

Yukaridaki örnekte görüldügü gibi PUSH komutu degeri, registeri, yada bir adresteki degeri alip saklamaya yarar. POP komutu ise bu degeri alip istedigimiz yere yada registere aktarmamiz saglar. Assembler da  saklanan degerin illaki saklandigi yere çikmak gibi bir zorunlugu yoktur. Örnegin eax registeri PUSH EAX olarak sakladiysak bunu yine POP EAX olarak illaki EAX register çikmak zorunda degilir. POP EBX yada POP [ADDR] olarakda çikabiliriz.

Saklanan degerlerin sondan basliyarak ilk degere dogru geri dönmesi esastir. Eger sirasiyla 1,3,5 degerlerini sakladiysak, bize geri dönüsümü 5,3,1 seklinde olacaktir.

PUSH komutu yigin noktasina saklama görevini üstlenir. PUSH komutu register, sayisal deger veya bir adres olabilir. Asagida görülen  örnekler ayri ayridir:

 

 

PUSH EAX

PUSH [ESI]

PUSH 9943512

PUSH OFFSET [410002]

Burada son örnekte görülen OFFSET aslinda derleyiciler için kullanilan bir ektir. Normalde böyle bir ek assembler komutlari içinde yoktur. Sadece DOS' da segment sistemi oldugu ve segmentler registerin  çalisma alanlarini degistirdigi için CS: , DS:, ES: gibi ön ekler kullanilir.

Geri alma komutu ise POP komutudur .Bu komut PUSH komutunda olan olaylarin tersini gerçeklestirir. Yani yigin noktasi registerinden (SP veya ESP) degerin boyutu düsülür ve o noktadaki deger istenilen registere, adrese yüklenir. PUSH ve POP komutlari döngüler içinde yogun kullanim alani bulurlar. Örnegin :

         

  mov ecx,10
dongu:
            push ecx
            mov ecx,dword ptr [sayac]
            inc ecx
            mov dword ptr [sayac],ecx
            pop ecx
            loopnz dongu
            ....
Bu örnekte iki tane ECX register degeri oldugu halde islem PUSH ve POP komutlari sayesinde degerler ayri ayri saklanip alinarak tamamlanmaktadir.

POP komutuda PUSH komutu gibi çesitli sekillerde kullanilabilir: (Hepsi ayri ayri örneklenmistir.)

POP EAX

POP [ESI]

POP 9943512

POP OFFSET [410002]

 

Döngüler, Atlamalar ve Çagrilar

Bu bölümde atlama komutlarini anlatacagim. Atlama komutlari ve döngüler programlar için çok önemlidir. Atlama komutlarini kosullu atlama komutlari, çagri komutlari ve döngü komutlari olarak üçe ayirabiliriz. Kosullu atlama komutlari bir sartin olusmasi yada yerine getirilmesi sonucu istenilen koda atlar ve çalismaya devam eder. Ayrica kosullu atlama komutlari içerisinde sadece bir tanesi sartsiz ve kosulsuz atlama yapmaktadir. Kosullu atlama komutlari islem bitince eski yerlerine dönmezler.

Çagri komutlarinin en büyük özelligi tamamlayici komutu ile kullanilarak çagrildigin kodun devamina geri dönebilmesidir. Bu islem daha önce isledigimiz yigin bölgesi sayesinde olur.

Döngü komutlari genelde CX (veya ECX) register ile birlikte çalisir ve istenilen sayi - sart olusuncaya kadar istenilen kod parçasi döngü içerisine alinir. Döngülerde genel kural olarak ilk basta sayaç degeri verilir (CX veya ECX register yüklenir) ve döngünün geri dönüs noktasinda daima bu sayaç degeri verilen yerin disindadir. Eger her döngü isleminde sayaç degeri tekrar ve tekrar yüklenirse o döngü sonsuz bir hal alir ve sart gerçeklesmedigi için döngüden çikmak imkansiz hale gelir.

 

Döngü komutlari ve kullanilimi:

Döngü komutlari yukarida da belirtigim gibi istenilen sayi ve sartta bir kod bölgesinin çalistirilmasini saglar. Iç içe istenildigi kadar döngü olusturmak mümkündür ancak sayaç degerleri korumak önemlidir. Çünkü yapilacak islem sayisini bu sayaç degerleri tayin eder ve döngü komutlarida bu sayaç register degerine göre döngüyü sürdürür. Ayrica her döngü isleminin dönüsümünde sart - durum meydana gelmemisse CX (veya ECX) registerden 1  eksiltilir.

Döngü komutlari LOOP ile baslar ve yanina aldigi ek ile ayri bir opkod olusur. Aldigi ek islemi yapma ve devam etme sartidir. Bu komutlar daima CX (veya ECX) register ile birlikte çalisir. Sarti ve döngü uzunlugunu bu sayaç register olusturur. Daha önce gördügümüz bayraklar burada da heryerde oldugu gibi etkindir. Karsilastirma ve sartin yerine getirilmesi kontrolu Z (sifir bayrak = zero flag) ile kontrol edilir. Döngüyü kaplayan alanin bir siniri vardir. Bunun nedeni ise döngü noktasini belirten degerin 8 bitlik bir sayi olmasi. Bu kodun +127 byte yukari ve -128 byte asagiya kadar ulasmasi demektir. Döngü komutunun yanina aldigi sayi 80h sonrasi eksi alan sayilir ve bulundugu yerden çikarilan döngünün baslama adresi bulunabilir. Ayni sekilde 0-7fh arasi arti alandir. Bu islemi debugger otomatik olarak açmaktadir. Derleyiciler içerisinde böyle bir tasma sözkonusu olursa derleme islemi sirasinda döngü tasma ile ilgili hata mesajini verir.

Döngü komutlari sunlardir: LOOP, LOOPE, LOOPNE,  LOOPZ, LOOPNZ. Bunlar CX (yada ECX) registerin durumuna göre islem yapar.

LOOP komutu o andaki CX yada ECX sayaç registerinin degeri 0 oluncaya kadar döngüyü isletir. Sarti sayaç registerin 0 olmasidir. Örnek vermek gerekirse:

        mov ecx, 10
        mov eax,0
dis1:
        inc eax
        loop dis1
        .....

Örnekte görüldügü gibi önce sayaç registere 10 degerini yüklüyoruz. Bu dis1 ile loop komutu arasindaki islemi 10 kere yaptirmak için. Sonra örnek bir islem yaptirmak için EAX registere örnek olarak 0 degeri veriyoruz. Burada EAX registerin ve INC EAX komutunu döngünün dönüsümü ile ilgili bir özelligi yok. Sadece döngüyü test etmek için verdim. Daha sonra dis1: olarak bir nokta belirtiyoruz. Bu noktanin MOV ECX,10 sekliyle sayaç registeri yükledigimiz yerin disinda olmasina dikkat ediyoruz. LOOP DIS1 komutu ile dönüsümün dis1: bölgesi içerisinde olmasini sagliyoruz. Burada 10 kere dönüsüm olacak ve INC EAX komutu ile basta 0 degerini verdigimiz EAX registerin degeri yükseltilecek. Her LOOP DIS1 komutunda ECX registerin degeri 1 düsecek ve ECX registerin 0 degerine ulasip - ulasmadigina bakilacak. Eger ECX = 0 ise döngü bitecek ve LOOP DIS1 komutunun sonrasindan devam edilecek.

LOOPZ ve LOOPNZ döngüleri: Bu döngüler bir islemin sonucu 0 ise zero flag bitinin (sifir bayrak biti) olmasi durumuna göre çalisir. Genelde bu döngü komutlari daha çok kullanilir ve ikiside sayaçli bir döngü islemi için yeterlidir. LOOPZ komutu CX veya ECX registerin o degerine ulasip ulasmadigini kontrol eder. Bir bakima yukarida bahsettigim  LOOP komutu gibidir ve tek farki bayrak kontrolu olmasidir. LOOPNZ ise 0 degilse durumunu kontrol eder. Yani sayaç registerin degeri 0 degilse devam eder.

LOOPE ve LOOPNE komutlari esit olmasi yada olmamasi gibi bir durum kontrol eder. Burada yapilan islem digerleri gibidir ve pek bir fark yoktur.

Iç içe döngü kurmak : Genelde bir islemin katlari kadar islem yaptirmak istedigimiz zaman iç-içe döngü kullaniriz. Örnegin, bir döngü sonundaki sayinin katlarini almak istiyoruz. Matematikte (a+b)*c seklinde basitçe ifade edebildigimiz gibi hesaplamalar yada islemler. Bunlar için iç-içe döngü kullaniriz. Iç-içe döngünün en önemli kurali bir önceki döngünün sayaç degerini saklanmasidir. Bunun için en iyi yöntem yigin komutlari olan  PUSH ve POP komutlarini kullanmaktir. Iç-içe döngü hakkinda bir örnek vermek gerekirse:

       

 

 

 mov eax,100h
        mov ecx,5
dis1:
        push ecx
        mov ecx,10
dis2:
        add eax,eax
        loopnz dis2
        pop ecx
        loopnz dis1
        .....

Burada ilk olarak ana döngünün degerini veriyoruz. Içerideki islem bitince 5 kere daha yapacak oldugunu gösteriyoruz. EAX registere örnek olarak 100h degerini veriyoruz. DIS1 noktasi bizim ana döngümüzün dönecegi yer. Buradan sonra ikinci döngü baslayacak ama yeni bir sayaç degeri verirsek ana döngünün degeride degisecek ve biz yapmak istedigimiz kadar döngü olusturamayacagiz. Bunun için iç döngünün degerini vermeden önce o andaki ECX sayacinin degerini yigin noktasina PUSH ECX ile sakliyoruz. Iç döngünün islem sayisini artik rahatça belirtebiliriz. Iç döngü degeri olarak 10 belirttik. Örnek islem olarakta buraya ADD EAX,EAX seklinde bir komut yaziyoruz ve ilk döngünün 10 kere tekrar etmesini gözlemliyoruz. Iç döngü 0 olunca sonraki opkoda devam eder ve burada POP ECX ile ana döngünün sayaç sayisi (ECX) yüklenir. LOOPNZ DIS1 ile bu sayaç degeri kontrol edilir ve 0 degilse yukaridaki islemler 0 oluncaya kadar devam eder.

Eger bunu matematiksel olarak ifade etmek gerekirse : 5* [10[eax+eax]] karsiligi gelen islem yapilmis olur.
       

Döngüyü istedigimiz bir yerde kesmek: Bu algoritma daha çok kontrollu döngü islemi olarak düsünülebilir. Biz istedigimiz gibi bir döngü sayisi veririz ancak istedigimiz sart olusunca döngünün bitmesini beklemeden döngüden çikabiliriz. Bunun için döngünün içine bir kontrol mekanizmasi olusturmak yeterli. (Burada bundan sonra görecegimiz sartli karsilastirma ve atlama komutunu kullanacagiz. Eger ilk defa okuyorsaniz önce sartli atlama komutlarina bir gözatin.)  Örnegin:

        mov eax,10h
        mov ecx, 65h
dis1:
        cmp eax, 1000h
        ja bitti
        add eax,1234
        loop dis1
bitti:
        ....

Burada 65h defalik bir döngü kuruyoruz ancak döngünün basina yaptigimiz islem ile ilgili bir sinir koyup hakimiyeti ele geçiriyoruz. Bu sinir islemde kullandigimiz EAX registerin degerinin 1000h degerini geçip - geçmedigidir. Eger geçme varsa döngünün bitmesini beklemeden BITTI noktasina atlama yapilir. Bunu daha çok istatistik olarak bir degerin kaç kerelik islem sonucunda olustugunu görmek için kullaniriz. Çünkü sart olusup BITTI noktasina atlama yapildiginda ECX registerin degeri o andaki sayaç degeri olacaktir. Yavas bir durum olmasina ragmen bir sayinin karesini almaya en uygun örnek olarak verebilirim.

Önceden anlattigim REP komutlari ve grubuda döngüler içerisinde kullanilabilir ve daha yüksek bir islem kapasitesi olusturulabilir.

 

EkleBunu Sosyal Paylaşım Butonu

Yorum yaz! :: Arkadaşına Gönder!

0 yorum yazılmıştır

« Önceki :: Sonraki »

Reklamlar