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. Bura
da 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 top
lanan 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 bayr
ak 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 reg
isterin 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 gen
istir. 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]*7
68 = 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 k
ullanildigi 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 öne
mlidir.
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 s
anirim 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 dev
am 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ü sonsu
z 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 g
elmemisse 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. B
unun 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 sekl
iyle 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 kom
utlari 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 sak
liyoruz. 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ç k
erelik 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.