ASSEMBLY KOMUTLARI :
Evet arkadaşlar geldik komutlar bölümüne ,assembly ‘de komutları
kendi aralarında bölümlere ayırmak bence daha iyi olacaktır.Böylece
komutların ne işlem yaptıklarını hangi komutların nereler de
kullanıldıklarını anlamak daha kolay olur.Ayrıca vereceğim bazı püf
noktalar ile de komutlara bakarak ne üzerinde etkili olduklarını hemen
kavrayacaksınız.Assembly ‘ de komutları Transfer Komutları,
Matematiksel Komutlar, Mantık Komutları, ,Döngü Komutları , Atlama
Komutları ve Kaydırma Komutları olarak inceleyeceğiz .Çok gibi görünse
de siz bir sefer bu makaleyi okuduğunuz zaman bir çoğunu hemen
kavrayacaksınız.Neyse Transfer Komutları ile başlayalım.
Transfer (Veri Taşıma )Komutları :
Bu grup içerisindeki
komutlar herhangi bir bilgiyi register-register, bellek-register ve
register-bellek bölgeleri arasında transfer etmek için
kullanılır.Başlıcaları şu şekildedir.
MOV Komutu: Arkadaşlar önceki bölümlerde birkaç örnek verirken bu
komutu kullanmıştım.Şimdi bu komutu yakından inceleyeceğiz anlaşılması
kolay bir komut.Adını zannedersem “Move”(taşı) kelimesinden
almış.Kullanımı :
Mov Hedef , Kaynak
mov register, register
mov bellek, register
mov register, bellek
mov [adres+register], register
mov register, [adres+register]
vb .şekildedir.Yalnız dikkat edin
burada ters bir durum var ,ilk önce hedef sonra ise kaynak adres
yazılır mesela Windows da bir veri kopyalarken dikkat ettiyseniz orada
önce kaynak ,sonra da hedef bölge yazılır,burada ise tam
tersidir.Assembly komutlarının hepsinde önce komut sonra hedef ve araya
( , )virgül koyarak kaynak adres yazılır.Diğer gösterimler ise Mov
komutu ile bu bellek ve registerler (kaydedici) arasında ne şekilde
veri transferi yapılabileceğini gösterir.Bu komutun bayrak registerleri
üzerinde herhangi bir etkisi yoktur.Unutmadan şunu da söyleyeyim taşıma
yaptığınız veri ile register aynı değerde olmalıdır.
MOV AX, 1525h
MOV DS, AX
Yukarıda ki örnekte 2 byte lik 1525h (hexdecimal) değer AX (AX burada
16 bit yani 2 bytedir)registere aktarılıyor.Sonra ise AX deki değer DS
‘ye (Data Segment) yazdırılıyor.
LEA (Load Effective Address) Komutu :Bu komut ise Etkin Adresi
Yükle anlamındadır.Bu komut Mov komutuna benzetilebilir ancak
aralarında etkin bir fark vardır.Mov komutu ile registere bir
yerdeki(hafızadan veya registerden) değer yüklersiniz.Oysa LEA komutu
ile registere o yerdeki değeri değil,oranın adresini yüklersiniz.Bu
şekilde iki yada üç komut ile yapılacak bir işlemi tek bir komut ile
yapmış olursunuz.
Kısaca ;LEA komutunun genel kullanım amacı herhangi bir register’a bir
bellek adresini yüklemektir. Aşağıdaki kullanım şekli ve örnek
açıklayıcı olacaktır.
lea register, adres
lea register, [adres]
dizi db ‘Merhaba’,0
Bu şekilde ki örnekte lea eax, dizi şeklinde yazarsam eax registere
‘Merhaba’ değeri değil de,bulunduğu yerin adresi yüklenir. Böylece biz
bu verinin başından itibaren istediğimiz gibi çalışma yapabiliriz. Bu
komutun da bayrak registerleri üzerinde herhangi bir etkisi yoktur.
MOVS (Move String)Komutu: Arkadaşlar en güçlü dizi (String)veri
transfer komutu olan MOVS hafızanın bir alanından diğer bir alanına
veri aktarımı yapar.Yanına aldığı B,W,D takıları ile ayrı bir opkod
halini alır ve buna göre bitte işlem yapar. Diğer bir deyişle, bu komut
hafızadan- hafızaya veri transferi yapar. Bir MOVS komutu SI(Source
index) ile adreslenen hafıza değerini, DI (Destination index) ile
adreslenen hafıza alanına aktarır.Yani bir nevi Kaynak-Hedef ilişkisi.
MOVSB = 1 byte işlem için - BYTE(8 bit)
MOVSW = 2 byte işlem için - WORD(16 bit)
MOVSD = 4 byte işlem için - DOUBLEWORD(32 bit)
LODS (Load)komutu: Bu da MOVS komutu gibi yanına aldığı ekin
boyutuna göre 8 ile 32 bit arasında işlem gücü kazanır. Temel görevi SI
(yada ESI) registerin gösterdiği noktadaki değeri alıp bit değerine
göre (AL, AX, EAX) registere (Load)yüklemektir. Ayrıca LODS komutu
değeri yükledikten sonra SI registerin değerini kullanılan bit değerine
göre arttırarak sonraki veriye göre ayarlamış olur. Bu 8 bit lik veri
için 1 byte- 16 bit lik bir veri için 2 byte - ve 32 bit lik bir veri
çin 4 byte otomatik olarak toplanacak demektir.
LODSB = 1 byte işlem için (8 bit)
LODSW = 2 byte işlem için (16 bit)
LODSD = 4 byte işlem için (32 bit)
CMPS (Compare)Komutu : CMP komutunun bir değişiği olan bu komut
tekil olarak diğerleri gibi yanına aldığı ek ile orantılı olarak bit
sayısı kadar işlem(Karşılaştırma) yapar.Yine bu komut SI (veya ESI) ve
DI (veya EDI) registerler ile birlikte çalışır.Bu komut tüm
karşılaştırma ve test komutları gibi bayrak registeri etkiler. Bu
komutu kısaca SI register ile gösterilen bölgeyi DI ile gösterilen
bölge ile karşılaştırır diye açıklayabiliriz.. Bu komut ileride
göreceğimiz karşılaştırma komutları gibi registerler karşılaştırması
yapmaz. Karşılaştırma sonucunu ZF (zero flag) ile alırız. Eğer iki
bölgede verdiğimiz bir değerin karşılaştırılması sonucu aynı ise ZF set
(1) olur. Eğer yanlış ise ZF reset (0) olur.
XCHG (exchange) Komutu: Exchange komutu iki değerin karşılıklı
olarak yer değiştirilmesi için kullanılır.Karşılıklı olarak registerler
birbirlerinin değerlerini alırlar. Tabi yine değişim yapılacak
değerlerin de aynı boyda olması gerekmektedir.Bu komut da flag
register’lar üzerinde herhangi bir değişiklik yapmaz. Kullanımı çok
basittir arkadaşlar.
Xchg register,bellek
Xchg bellek,register
Mov ax,1234 ( ax =1234 örnekteki değerler 16 bittir.)
Mov bx,5678 (bx = 5678 )
Xchg ax,bx (ax =5678- bx =1234 değerler yer değişti)
XLAT Komutu :XLAT komutu esas olarak BX (veya EBX) ve AL registeri
esas alarak çalışır Genellikle tablo olarak tasarlanan dizilere erişmek
için kullanılır. AL kaydedicisine tablonun elemanlarından birini
yükler. Bu komutu aşağıdaki örneğe bakarak daha iyi anlayabilirsiniz.
Tablo DB A, B, C, D, E, F
Bu tablonun 4. elemanını AL’ye yüklemek istersek;
MOV AL, 04 ; 04 .eleman sorguluyoruz.İndeks değeri (Diziler 0 dan başlar)
LEA BX, Tablo ; BX’e (taban kaydedici) Tablonun ofset adresi yükleniyor
XLAT ; Tablonun 4. elemanına erişilip E’nin ASCII karakterin hex karşılığı AL’ye yükleniyor(AL=105)
PUSH-PUSHA-PUSHF—POP-POPA-POPF Komutları :
Arkadaşlar Yığın (Stack) konusunu önceki bölümde anlatmıştım. Bu
yığın dediğimiz bölge döngülerin döngü sayısını tutmak içinde
kullanılır. Ayrıca Bayrak registerlerin (F veya EF) durumu da yığın
içinde saklanılabilir.Matematiksel işlemlerde de yığın kullanılır. Bu
anlamda yığın çok önemlidir.İşte bu yığına veriyi atmak ve geri almak
için de PUSH ve POP komutlarını kullanırız. PUSH ve POP komutları
kullanıldığında yığın bölgesinin işaretçisi olan SP (Stack Pointer )
kaydedicisinin durumu da değişir. Tabiî ki bu yığına attığımız veya
yığından çektiğimiz değerin boyutuna bağlıdır. 2 byte’lık değer ile
işlem yaparak yığına 2 byte lık bir değer atarsak (mesela bu AX
kaydedicisinin içeriği olabilir) SP’nin değeri de 2 byte
azalır.Anlamadıysanız bir önceki konudan stack konusunu tekrar
okuyabilirsiniz…
PUSH komutu herhangi bir bilgiyi belleğin stack adı verilen
bölümüne kaydetmek için kullanılır;diye tanımlayabiliriz.PUSH komutu
ile stack üzerine atılacak bilgi en az 16-bit uzunluğunda
olmalıdır.Komut aşağıdaki gibidir.
PUSH değer
Yukarıda “değer” ile gösterilen kısım daha öncede belirttiğim gibi
16-bit uzunluğunda olmalıdır. Bunun yanı sıra “değer” ile gösterilen
kısım sabit bir değer alamaz. Yani PUSH ile stack üzerine
yazabileceğimiz değer ya bir register içerisindeki değer yada bir
bellek bölgesindeki değer olmalıdır.
mov eax,1234 AX = 1234h değeri atıyorum
push eax AX ‘teki değer şimdi STACK ‘ta ve SP-2 olarak azaldı.
POP komutu ile de stack üzerinden bilgi okuması yaparız. Yani PUSH
komutu ile yığına gönderdiğimiz bilgileri POP komutu ile de geri
okuruz. Okunacak bilgi 16-bit uzunluğunda olmalıdır.POP komutu ile
alınan bilgi stack üzerine yazılan son bilgidir. PUSH ve POP komutları
ile bilgi transferi yapılırken yazılan ve okunan bilgilerin sıralaması
önemlidir, yine bir önceki konuda açıkladım. Programlar yazılırken
stack üzerindeki işlemlerde hesaplama hatası yapmamak için buna dikkat
etmek gerekir
.
mov eax,1234 = yukarıdaki örneğimiz
push eax = satck ‘a atıldı
xor eax,eax = eax ‘i boşaltıyorum
pop eax = eax-1234 değeri geri yükledim.
PUSHA-POPA :Bazen bütün register’ların yığında saklanması
gerekebilir. Örneğin bir donanım kesmesi oluştuğunda çağrılacak bir kod
yazmak istersek kesme çıkışında bütün register’ların ilk konumuna
getirilmesi gerekir. Bunun için kesme koduna girişte bütün register’lar
stack’ta saklanmalı, ve çıkışta da hepsi geri alınmalıdır.
İşte bu işlemi kolaylaştırmak 80386 sonrası işlemcilere tek
komutla bütün registerleri yığına atan ve yine tek komutla hepsini
yığından geri alan PUSHA ve POPA komutu eklenmiştir. Kısaca; PUSHA
komutu sırasıyla 16 bit sistemde AX, CX, DX, BX, SP, BP, SI, DI
kaydedicilerinin değerlerini stack’a atar.POPA da ters sırada geri
çeker.
PUSHF : PUSHF komutu da PUSH komutu gibi stack üzerine bilgi aktarır.
Yalnız burada ki tek fark, PUSHF komutu ile aktarılacak bilginin
herhangi bir register yada bellek bölgesinden değil de flag (bayrak)
register’dan alınmasıdır.Yani bayrak registerlerin değeri Stack üzerine
atılır.Sonunda ki “F” harfi Flag ( bayrak )anlamında olduğu için
karıştırmayacağınıza eminim.Bir de atılacak bilgi doğrudan 16-bit
uzunluğundaki flag register içerisindeki değerdir.Komut herhangi bir
işlem sırasında bayrak register’ın mevcut değerini korumak için
kullanılır.Yine PUSH komutunda olduğu gibi PUSHF komutu da atılan
değerin boyutuna göre SP ’nin değerini azaltır. Aşağıdaki gibi tek
başına kullanılır…
PUSHF
POPF: POPF komutu ile de arkadaşlar stack üzerine attığımız bayrak
değerlerini ,yine 16-bit’lik bayrak register’a geri aktarırız. Ancak
geri aldığımız bu 16 bitin hepsi işlemci tarafından dikkate
alınmaz.Anlaşılacağı gibi POPF komutu bayrak register ’ın değerlerini
tamamen değiştirmektedir. Sonunda ki “F” harfi Flag ( bayrak
)anlamındadır.Komut tıpkı PUSHF komutunda olduğu gibi tek başına
kullanılır.
Matematiksel (Aritmetik) Komutlar :
Assembly de bütün register arasında standart toplama, çıkarma, çarpma
ve bölme işlemleri rahatça yapılabilir arkadaşlar.Ben de burada
standart olarak 4 bölüme ayırıp açıkladım. Aritmetiksel komutların
icrası sırasında flag registerler değişikliğe uğrar.
a - ADD ve ADC (Toplama)Komutları : ADD komutu toplama işlemini
gerçekleştirmek için kullanılır. ADD komutu ile “kaynak” içerisindeki
değer “hedef” ile toplanıp tekrar “hedef” register içerisine
kaydedilir. “Hedef” ve “kaynak” alanları da register-register,
bellek-register, register-bellek gibi çiftlerden birisi olabilir.Örnek
olarak;
MOV AX, 5 - ax içerisine 5 değerini yazdık.
ADD AX, 6 - Ax deki değer ile 6 yı toplayıp tekrar Ax ‘e kaydettik.
Yukarıdaki örnekte AX registerimizin değeri 11 olacak ve
hexdecimal karşılığı olarak 0Bh yazılacaktı.ADC komutu da tıpkı ADD
komutu gibi toplama işlemi için kullanılır arkadaşlar.Aralarındaki tek
fark ise ADC komutunda toplama işlemine bir de carry (elde biti)
flag’ın değerinin eklenmesidir.Yukarıdaki örnekte işlemin sonucun da
eğer carry flag’ın değeri 1 olsa idi, o zaman bizim işlemimizin sonucu
da 11 değil 12 olarak çıkacaktı. Gösterimi daha anlaşılır olacaktır.
register = hedef + kaynak + carry flag’ın değeri
b- SUB , SBB ve CMP ( Çıkartma ) Komutları :
SUB (Subtract )komutu çıkarma işlemi için kullanılır. Kullanımı
ADD komutunda olduğu gibidir. “Kaynak” içerisindeki değer “hedef”
içerisinden çıkartılıp sonuç “hedef” register içerisinde saklanır.Şu
şekilde örnek gösterebiliriz.
Mov ax,8
Mov bx,3
Sub ax,bx sonuç ax = 5
SBB Komutu (Subtract with Barrow) ise SUB komutunun Carry’li
versiyonudur. Ödünç ile toplama komutu olan SBB, operand ile beraber
elde (Carry) bayrağını çıkarmada kullanır.Yani SBB komutu ile SUB
arasındaki ilişki, ADD komutu ile ADC arasındaki ilişki ile aynıdır.SUB
komutu ile aynı işlem yapılır yalnız burada “hedef” alana atılan
değerden carry flag’ın değeri de çıkartılır.
register = hedef – kaynak – carry flag’ın değeri şeklinde gösterebiliriz.
CMP bu komut aslın da karşılaştırma komutu gibi görünse de
arkadaşlar CMP komutu kullanım bakımından tamamen SUB komutu gibidir.
Ancak işlemin sonucunu herhangi bir kaydediciye yüklemez ,çıkartma
işleminden sonra yalnızca bayraklar etkilenir aralarında ki fark sadece
budur. CMP ’nin anlamı "compare" yani karşılaştır demektir. Bu komut A,
C, O, P, S ve Z bayraklarını etkiler.CMP komutundan sonra sonuca göre
genellikle koşullu dallanma komutları ile programın akışı değiştirilir.
Cmp ax, bx
Yukarıdaki örnekte Ax den BX çıkarılıyor AX, BX’e eşit ise işlemin sonucu sıfır olur ve zero flag 1 değerini alır.
INC – DEC (Artırma-Eksiltme ) Komutları :INC ve DEC komutları
registerleri, adresleri, yada değerleri azaltmak - arttırmak içindir.
Her iki komutta her çalışmasında sadece 1 byte lık azaltma veya
arttırma yapmaktadır. Daha fazla adımlarda azaltma-arttırma yapmak için
ADD ve SUB komutlarını kullanmalıyız. DEC ve INC komutları
oluşturdukları yeni değerlere göre bayrak registeri etkilerler.
Kısaca INC komutu kendisine verilen register yada bellek bölgesi içerisindeki değeri bir arttırır. C
dilindeki “++” komutu ile aynı işi yapmaktadır .Yada VB deki “a = a + 1” ile aynı anlamdadır.
Add ax,1 komutu da aynı işi yapar fakat aşağıda ki örnek daha hızlı çalışır.
inc ax (ax= ++)
DEC komutu da kendisine verilen register yada bellek bölgesi
içerisindeki değeri bir azaltır.Yine C dilindeki “--“ komutuna ,yada VB dilindeki “ a = a – 1 ” komutuna karşılık gelmektedir.
Dec ax (ax = -- anlamın da ve oldukça hızlı çalışır)
MUL ve IMUL (Çarpma) Komutları :Çarpma işlevide registerler
arasında , adresler arasında, ve kendi arasında uygulanabilir. Çarpma
işlemi MUL ve IMUL komutları ile gerçekleşmektedir. İkisini amacı
çarpim olduğu halde MUL ve IMUL arasında farklılıklar vardır. MUL
işaretsiz çarpma ,IMUL ise işareti dikkate alarak çarpma işlemlerini
yapar.Arıca IMUL komutu 2 veya çarpıma olanak vermekte ve kullanım
şekli daha geniştir. IMUL komutu 32 bit işlemler için idealdir.
MUL :Aritmetiksel olarak çarpma işlemi iki değer ile
gerçekleştirilmesine karşın MUL komutu sadece bir değer alır. MUL
komutu ile kullanılan değer gizli olarak ax /al içerisindeki değer ile
çarpma işlemine tabi tutulur.Şöyle ki;
mov ax,0045 -ax = 45
mov bx,0011 -bx =11
mul bx -ax * bx = sonuç yine ax registere aktarılır .
Yukarıdaki örnekten anlaşılacağı gibi daima AX (AL ve EAX
register) asıl çarpılandır.Eğer bir çarpma işlemi sonucu ax registerin
alabileceği en büyük değeri geçiyorsa geçme sayısı DX registere
aktarılır. Eğer geçme yoksa DX registerde bir değişme olmaz.
IMUL : IMUL komutu da MUL komutu gibi çarpma işlemi için kullanılır tek
fark IMUL komutunun işaretli sayılar üzerindeki işlemler için
kullanılan bir komut olmasıdır.Ayrıca IMUL komutu çoklu register ve
büyük değerlerle işlem yapmaya olanak vermektedir. Yanlız MUL komutunda
olduğu gibi taşma olduğunda DX register aktarılma yapılmaz. IMUL ile
üçlü çarpımda mümkündür.
mov ax, 04h
mov bx,05h
imul ax,bx,8h ; [AX*BX] * 8 = 160
Yukarıda sadece küçük sayılarla örnek verdim anlaşılması için
yoksa bu komutla daha büyük sayılar çarpılabilir.32 bit işlemcilerde bu
komut kullanılır.
DIV ve IDIV (Bölme) Komutları : Bölme komutuda DIV ve IDIV olmak
üzere kapsamlı olarak iki tanedir. DIV komut da MUL komutundan olduğu
gibi sadece bir değer ile işleme girer ve gizli olarak AX register’ını
kullanır. Yani işlem sonundaki bölüm değeri AX ve kalan değeri de DX
içerisine atılır.DIV division yani bölme kelimesinin kısaltmasıdır,
dikkat edilmesi gereken bir diğer husus da sıfır ile bölme durumudur.
Diğer işlemlerde olduğu gibi bölme işleminde de matematik kuralları
geçerlidir ve matematikte "sıfıra bölme" anlamsızdır. CPU’ da sıfıra
bölüm için özel bir kesme(interrupt) ayırmıştır ve böyle bir durum da
işlemi durdurur.
Mov ax,10
Mov bx,3
DİV bx
bx deki 3 değeri ax deki 10 değerine bölünüyor ve ax de 3 değeri ,kalan 1 ise DX registerde saklanıyor.
IDIV komutu ise aynı IMUL komutunda olduğu gibi daha yüksek işlemler
için idealdir.Ayrıca IDIV komutu işaretli sayılar üzerinde işlem yapmak
için kullanılır.Bu komutu fazla kullanmayacağınızdan bu kadar
yeterlidir diye düşünüyorum.