Bu makalede x86
adresleme modlarını anlatmaya çalışacağım, ayrıca sembolik kodlarımızı, verilerimizi daha rahat görebileceğimiz ve kontrol edebileceğimiz bir program olan Turbo Debugger’ı inceleyeceleyeceğiz.
Geçen makalemizde x86 kaydedicilerini hafızanın yapısını anlatmaya çalışmıştım.
Bu makaleye kadar çoğunlukla x86 uyumlu sistemlerin yapısı üzerinde durdum.
Bundan sonra yazacağım makaleler daha çok assembly programlama dilini
kapsayacak.
ADRESLEME MODLARI
Bildiğiniz gibi programları oluşturan kodlar ve veriler hafızaya yüklendikten
sonra işlemci tarafından satır-satır icra edilirler. Ayrıca CPU tüm giriş çıkış
işlemlerini de hafızaya erişerek yapar. Bazen hafızadan doğrudan bir kod ya da
veri alır, işler. Bazen hafızaya bir veri gönderdiğinizde birde bakmışsınız bu
bir yazıcıdan belge olarak çıkmış vs. İşte bilgisayarın donanım ve yazılım
düzeyinde yaptığı bunca çeşitli iş için CPU hafızaya değişik yollardan erişme
ihtiyacı duyar. Sizlerde programlarınızı yazarken CPU’nun hafızaya nasıl
erişeceğini yazdığınız kodlarla belirtmek zorundasınız. Assembly dilinin ilk
basamağı olan adresleme modları da bu konuları kapsıyor.
Her mikroişlemci üreticisi bir mikro işlemci piyasaya sürdüğünde, komut setini
ve adresleme modlarınıda yayınlar. Programcılar da bu belgelere göre
programlarını yazarlar. Intel 8086 işlemcisini piyasaya sürdüğünde oldukça
kullanışlı bir dizi adresleme modu sağladı. Intel’in şu ana kadar ürettiği
işlemcilerde bu adresleme modlarını kullanabilirsiniz. Daha sonraları bir devrim
niteliğinde olan 80386 işlemcisi ile ek adresleme modlarıda geldi. Bu özel
adresleme modları sadece 386 ve sonrası işlemcilerde kullanılabilir. Tabi ki bu
ek adresleme modları ile assembly programlama dili daha esnek bir yapıya
bürünmüştü. Bizde bu makalemizde adresleme modlarını 386 öncesi ve sonrası
olarak ikiye ayıracağız.
Şu ana kadar yaptığımız programlarda çok fazla komut örneği görmediniz ama
makalelerimizi takip ettiyseniz MOV komutunu yakından tanımanız lazım. MOV
komutu assembly programlama dilinde en çok kullanılan komutların başında gelir.
Çünkü bir program çalışırken genelde hafızaya yazar yada okur. MOV
komutu da bu
iş için biçilmiş kaftandır ve bu kadar çok kullanıldığından dolayı bir çok
adresleme modunu destekler, yani bu komut ile hafızaya çok değişik yollardan
erişebilirsiniz. Bu yüzden bu makalede adresleme modlarını anlatırken MOV
komutunu örnek alacağım.
MOV komutunun genel kullanım şekli;
MOV hedef, kaynak
8086 ADRESLEME MODLARI:
8086 Kaydedici Adresleme:
Adından anlaşılacağı gibi kaydediciden kaydediciye yapılan işlemlerde bu
adresleme modları kullanılır. En hızlı adresleme modu’dur, çünkü işlem hafızada
değil işlemcinin içinde gerçekleşir.
Genel amaçlı ve indeks kaydedicilerde kaydedici adresleme
modları:
mov ax, bx ; BX teki değeri AX’e kopyalar mov dl, al ; AL teki değeri DL’ye kopyalar mov si, dx
; DX teki değeri SI’ya
kopyalar mov sp, bp
; SP deki değeri BP’ye kopyalar mov dh, cl
; CL deki değeri DH’a
kopyalar mov ax, ax ; Bu da doğru bir
kullanımdır!
Kaydedici adreslemede en çok dikkat etmeniz gereken husus hedef ve kaynağın
boyutlarıdır. Örneğin 16 bitlik bir kaydediciden 8 bitlik bir kaydediciye taşıma
yapılamaz!
mov al, bx ;yanlış kullanım, derleme anında assembler hata verir.
Küçük boyuttaki kaynaktan büyük boyuttaki hedefe de kopyalama yapılamaz.
mov cx, al ; Yanlış kullanım, AL’ile CX eşit boyutta değil.
Bunlara ek olarak segment kaydedicilerinin kullanımında dikkat edilmesi gereken
noktalar vardır.
1- Segment kaydedicileri arasında bir transfer işlemi ancak genel amaçlı bir
kaydedici vasıtasıyla yapılabilir.
mov ds,cs ; doğru kullanılmayan segment kaydedicisi şeklinde assembler hata
verir!
Bu işi yapmak için,
mov ax,cs
mov ds,ax
komut satırları kullanılabilir.
2- CS ve IP kaydedicilerinin değeri kaydedici adresleme ile değiştirilemez
mov cs, ax ; kaynak genel amaçlı kaydedici olmasına rağmen bu işlem mümkün
değildir!
Segment kaydedicileri programın icrası aşamasında hafızanın segment olarak
adlandırılan bölümlerinin adreslerini tuttuklarından, bu kaydedicileri
verilerinizi saklamak veya taşımak için kullanmanızı tavsiye etmem,
kullanılmamalıdırlar. Bunun yerine genel amaçlı kaydedicileri kullanmanız daha
uygun olur.
8086
Hafıza Adresleme Modları
a- Acil Adresleme ( Immediate Addressing )
Herhangi bir genel amaçlı veya indeks kaydedicisine doğrudan bir değer yükleye
bilirsiniz. Yüklenecek olan veri kod segmentten alınacağından bu tür
kullanımları şahsen ben pek tavsiye etmem. İyi bir program organizasyonu için,
veriler hafızanın ayrı bir bölümünde (mesela data segmentte) değişkenler veya
sabitler olarak belirtilmelidir.
mov al, 17 ; AL’ye 11h yüklenir.
b- Direkt Adresleme (Displacement Only Addressing )
Acil adreslemenin doğru kullanılmış şeklidir. Bu adreslemede segment:ofset
adresi kullanılarak hafızaya erişilir.
mov al, ds:12 ; ds:000C adresinden 1 byte AL’ye kopyalanır.
mov ds:12, al ; AL’nin içeriği ds:000C adresine kopyalanır.
mov ax, ds:12 ; ds:000C adresinden 2 byte AX’ye kopyalanır.
mov ds:12, ax ; AX’nin içeriği ds:000C adresinden itibaren kopyalanır (2 byte)
Aslında kaynak kod hazırlanırken genelde bu şekilde bir kod yazımı yapılmaz.
Değişkenler ve sabitler sembolik kelimelerle ifade edildiğinden buna gerek
yoktur. Ne var ki debugger programlarında da sembolik değişken isimlerini değil
adresleri görürüz. Aşağıdaki kod parçasını inceleyin;
.data sayi1 db 5
.code
mov ax, @data
mov ds, ax
mov al, sayi ; 5 değerini al’ye yükler.
mov al, ds:0000 ; aynı işi yapar yani 5’i AL’ye yükler.
; veriler tanımlanırken sayi1 db 5 şeklinde değil de
; sadece db 5 yazılsaydı, bu komutu kullanmak zorunda kalırdık.
c- Kaydedici Dolaylı Adresleme ( Register Indirect Addressing )
Adının kaydedici olduğuna aldanmayın. Burada operand olarak kullanılan kaydedici
köşeli parantez içine alınır ve bu andan itibaren bir offset adresi oluverir.
mov al, [bx] ; hafızadan AL’ye 1 byte taşınır. Alınacak verinin offset adresi
BX’in değeridir.
mov al, [bx]
mov al, [si]
mov al, [di]
mov al, [bp]
Yukarıdaki 4 örnekte AL’ye kopyalanacak verilerin offset adresleri ilgili
kaydedicinin içindeki değerdir. Segment adresleri ise ilk üçünün DS sonuncusunun
SS’dir. BP indeks kaydedicisi yalnız başına hafıza adreslemede kullanılırsa,
daima stack segmentin (SS) offset adreslerini gösterir. Fakat BP kaydedicisi de dahil
olmak üzere bu adresleme segment kaydedicileri de belirtilerek yapılırsa, o
zaman ilgili segment:offset adresine erişilmiş olunur.
BX=0000, BP=0001, SI=0002, DI=0003 olduğunu varsayalım;
mov al, cs:[bx] ; AL’ye CS:0000’dan kopyalama yapılır mov al, [bx]
; AL’ye DS:0000’dan kopyalama yapılır mov al, ds:[bp] ; AL’ye DS:0001’den kopyalama yapılır mov al, [bp] ; AL’ye SS:0001’den kopyalama yapılır mov al, ss:[si] ; AL’ye SS:0002’den kopyalama yapılır mov al, [si] ; AL’ye DS:0002’den kopyalama yapılır mov al, es:[di] ; AL’ye ES:0003’den kopyalama yapılır mov al, [di] ; AL’ye DS:0002’dan kopyalama yapılır
d- İndeksli adresleme ( Indexed Addressing )
Kaydedici dolaylı adreslemenin operandına sabit bir değer eklenmiş halidir.
Kullanım şekli;
mov al, disp[bx]
mov al, disp[bp]
mov al, disp[si]
mov al, disp[di]
Not : disp kısaltması İngilizcede displacement kelimesinin karşılığıdır.
Buradaki anlamı ise
referans alınan ofset adresidir. Komut setlerinde adresleme modları açıklanırken disp veya mem kısaltması ile çok sık karşılaşacağınızdan, komutları
yazarken İngilizce ifadeler kullanmak durumunda kaldım.
Mesela BX=2000h olsun, mov dl, 20h[bx] şeklinde bir komut kullanıldığında, DS:2020h
adresindeki 1 byte’lık değer dl’ye kopyalanacaktır. Aynı şekilde BP=3030h
olduğunu varsayalım, mov dh,1020h[bp] gibi bir komut ile, SS:4050h adresindeki 1
byte’lık değer dh’a kopyalanır.
Bu adresleme modunda da BP segment kaydedicisi daima SS’in ofsetlerini gösterir.
Fakat kaydedici dolaylı adreslemede de olduğu gibi bizzat segment
kaydedicisinide belirterek
bu adresleme modunu kullanabiliriz. Bu durumda ofset adresleri komutta
belirtilen segmentin ofseti olur.
mov al, ss:disp[bx]
; BX normalde DS’nin ofsetlerini gösterirken burada SS’in
ofseti olmuş.
mov al, es:disp[bp]
; BP normalde SS’nin ofsetlerini gösterirken burada ES’in
ofseti olmuş.
mov al, cs:disp[si] ; SI normalde DS’nin ofsetlerini gösterirken burada CS’in
ofseti olmuş.
mov al, ss:disp[di] ; DI normalde DS’nin ofsetlerini gösterirken burada SS’in
ofseti olmuş.
e- Taban İndeksli adresleme ( Based Indexed Addressing )
Bu adresleme modu da kaydedici dolaylı adreslemeye çok benzer. Kullanım formatı
aşağıdaki gibidir;
mov al, [bx][si]
mov al, [bx][di]
mov al, [bp][si]
mov al, [bp][di]
BX’in 0500h SI’nın 0360h olduğunu varsayalım,
mov al,[bx][si]
gibi bir komut işlenince AL’ye kopyalanacak veri DS:0860 adresinden alınır.
Aynı şekilde;
BP=1598h DI=1004 ve mov ax,[bp+di] gibi bir komut
işleniyorsa; AX, SS:259Ch ve SS:259Dh
adreslerindeki veri ile yüklenir.
SI ve DI kaydedicileri için Intel’in özel komutları vardır, bu yüzden bu
kaydediciler genellikle programlamada indeks değerlerini tutar, arttırır veya
azaltırlar.
f- Taban İndeksli artı direkt adresleme (Based Indexed Plus Displacement
Addressing)
Bu adresleme modu taban indeksli adreslemeye 8 yada 16 bitlik
sabit bir değerin
eklenmiş halidir.
mov al, disp[bx][si]
mov al, disp[bx+di]
mov al, [bp+si+disp]
mov al, [bp][di][disp]
BP = 1000h, BX= 2000h, SI= 0120h, DI = 5 olduğunu varsayalım.
mov al,10h[bx+si] ; AL’ye DS:2130 adresindeki veri yüklenir
mov ch,125h[bp+di] ; CH’a SS:112A adresindeki veri yüklenir
mov bx,cs:2[bx][di] ; CS:2007 adresinden itibaten 2 byte’lık veri
yüklenir
TASM ve MASM ‘ın adresleme modları için esnekliği.
TASM ve MASM assembler’ları indeksli, taban indeksli, ve taban indeksli artı
direkt adresleme için değişik yazım şekillerini desteklerler;
İndeksli adresleme için;
disp[bx] = [bx][disp] = [bx+disp] = [disp][bx] = [disp+bx]
Taban indeksli adresleme için;
[bx][si] = [bx+si] = [si][bx] = [si+bx] ;
Taban İndeksli artı direkt adresleme için;
disp[bx][si] = disp[bx+si] = [disp+bx+si] = [disp+bx][si] = disp[si][bx] = [disp+si][bx]
= [disp+si+bx] = [si+disp+bx] = [bx+disp+si]
Yukarıda yazılan bu 3 adresleme modundaki operandlar aynı işi yaparlar. MASM ve
TASM "[ ]" sembollerine "+" operatörü gibi davranır. ( disp[bx][si] = disp[bx+si]
örneğinde olduğu gibi )
8086 Adresleme Hafıza Adresleme Modlarını Hatırlamak İçin Kolay Bir Yol:
8086 işlemcisi için toplam 17 adet adresleme modu mevcuttur. Bunlar disp, [bx], [bp],
[si], [di], disp[bx], disp[bp], disp[si], disp[di], [bx][si], [bx][di], [bp][si],
[bp][di], disp[bx][si], disp [bx][di], disp[bp][si], and disp[bp][di] adresleme modlarıdır. Aşağıdaki şekil, bu 17 adresleme modunu ezberlemeniz yerine kolayca
hatırlamanıza yardımcı olacaktır.
Şekil 1 -
8086 adresleme modları için yardımcı şekil.
Bu şekil ile 17 adresleme modunu kolayca görebilirsiniz. Her sütundaki
elemanları teker-teker seçebilirsiniz veya herhangi bir sütunu geçip diğer iki
sütunun birleşimi ile doğru bir adresleme modu yakalayabilirsiniz.
Örnekler;
Bir tek eleman seçin;
disp, [bx], [bp], [si], [di]
3. sütunu yok sayın ve diğer elemanların birleşimini çıkartın
disp[bx], disp[bp]
2. sütunu yok sayın ve diğer elemanların birleşimini çıkartın
disp[si], disp[di]
1. sütunu yok sayın ve diğer elemanların birleşimini çıkartın
[bx][si], [bx][di], [bp][si], [bp][di]
Son olarak tüm sütunların birleşimi ile aşağıdaki geçerli adresleme modlarını
kolayca çıkartabilirsiniz.
disp[bx][si], disp [bx][di], disp[bp][si], disp[bp][di].
Not:Hafıza adresleme modlarında şayet hesaplanan etkin adres 0FFFFh değerinden büyük
olursa, CPU bu hesaplama sonucu oluşan taşmayı göz ardı eder ve FFFFh’e ekleneni
0 dan itibaren ekler. Örneğin BX=10h olsun ve mov al,0FFFFh[bx] komutu işlensin.
Bu durunda AL kaydedicisine ds:1000Fh adresindeki veri değil ds:000Fh
adresindeki veri yüklenir. (FFFFh+1h=0000, FFFFh+2h=0001,….. FFFFh+16h=000Fh)
Sonuç:
8086 adresleme modlarını 2 bölümde inceledik, ilk olarak kaydedici adresleme
modlarını anlatmaya çalıştım. Kaydedici adresleme de segment kaydedicilerini
kendi amaçları dışında kullanmanız tavsiye edilmez, bu iş için yeterince genel
amaçlı ve indeks kaydedicisi var zaten.
Karışık olan kısım ise hafıza adresleme modlarıdır. Hafıza adresleme modlarında
belirtilen operand daima bir offset adresini işaret eder, buna etkin adres
hesaplama da denir. Hafıza adresleme modlarını unutmamanız için sizlere kolay bir
yol göstermeye çalıştım. Adresleme modlarının isimlerinden ziyade kullanım
formatları önemlidir. Her komut her adresleme modunu desteklemez. Hangi komutun
hangi adresleme modunda kullanılacağı komut setlerinden faydalanılarak bulunur.
Bugüne kadar gerek internetten gerekse Türkçe assembly kitaplarından yaptığım
araştırmalarda, Türkçe olarak yayınlanmış ayrıntılı bir komut seti bulamadım bu
yüzden İngilizce komut setlerinden faydalanıyorum.
“8086 instruction set” veya “x86 instruction set” anahtar kelimelerini
internetten aratacak olursanız karşınıza yığınla komut seti gelecektir. Genelde
komut seti kılavuzları aşağıdaki tablo gibidirler.
MOV - Move Byte or Word
Usage: MOV dest,src
Operands |
Clocks |
Size
Bytes |
|
286 |
386 |
486 |
|
reg, reg |
2 |
2 |
1 |
2 |
mem, reg |
3 |
2 |
1 |
2-4 |
reg, mem |
5 |
4 |
1 |
2-4 |
mem, imm |
3 |
2 |
1 |
3-6 |
reg, imm |
2 |
2 |
1 |
2-3 |
segreg,
reg16 |
2 |
2 |
3 |
2 |
segreg,
mem16 |
5 |
5 |
9 |
2-4 |
... |
... |
... |
... |
... |
... |
... |
... |
... |
... |
... |
... |
... |
... |
... |
Tablo 1 - MOV komutu için örnek
tablo
Bu tabloda birinci sütun MOV komutu ile kullanılacak operandın hangi adresleme
modlarını desteklediğini gösteriyor. Örneğin mem,imm (displacement,immediate)
acil adresleme yi desteklediğini gösterir. reg,reg (register,register) kaydedici
adreslemenin yapılabileceğini, segreg,reg16 (segment register, register 16 bit )
bir segment kaydedicisine 16 bitlik bir kaydediciden kopyalama işleminin
yapılabileceğini gösterir.
“Clocks” sütunu bu komutların işlemci tarafından kaç adımda işlendiğini
(işlemcinin tipine ve hızına göre bu zaman birimi değişebilir), “size bytes”
sütunu ise hafızada makine kodlarının byte olarak kapladığı alanı gösterir. Çoğu
zaman assembly programcıları programlarının, işlenme sürecindeki nano saniyeleri
ve hafızada kapladığı alanını byte düzeyinde önemi yoksa bu kısımlarla
ilgilenmezler. Yani 1. derecede “operands” sütunu önemlidir ve ilgili komutu bu
sütunda belirtilen formatların dışında kullanamazsınız.
Son olarak x86 uyumlu hiçbir işlemci mem,mem şeklinde yani hafızadan hafızaya
doğrudan bir adresleme modunu desteklemez, aslında bu güne kadar bunu
destekleyen hiç bir işlemci görmedim, zaten böyle bir adresleme modunun
desteklenmesi durumunda o sistem için yapılan programların çökme olasılığı çok
büyüktür.
80386 ADRESLEME
MODLARI
80386 Kaydedici Adresleme (80386 Register Addressing)
80386 den itibaren, günümüzde kullandığımız Pentium işlemcilerin 32 bitlik
kaydedicileri vardır. 8086 adresleme modlarına ek olarak bu işlemcilere de
kaydedici adresleme yaparken bu 32 bitlik kaydedicileri de kullanabilirsiniz. Bu
kaydediciler geçen makalemizde bahsettiğimiz;
eax, ebx, ecx, edx, esi, edi, ebp, ve
esp kaydedicileridir.
kullanım formatı;
mov eax, ebx
Not: Şayet programınızda 32 bitlik kaydedicileri kullanacaksanız kaynak dosyası
hazırlarken .386 talimatını kullanmanız gerekir.
80386 Hafıza Adresleme (80386 Memory Addressing)
a- 80386 Kaydedici Dolaylı Adresleme ( Register Indirect Addressing )
16 bitlik Gerçek moda (Real Mode) 80386 ve üstü işlemcilerde programlama
yapmanız offset adreslerinin 32 bit olacağı anlamına gelmez. Segmentlerin her zaman
gerçek moda 64Kb. olduğunu unutmamak gerekir, bu yüzden offset adresleri gerçek modda 0...0FFFFh arasını gösterir. Yani 64Kb.’ı geçecek ofset adreslerini gerçek
moda değil ancak korumalı moda (Protected mode) kullanabilirsiniz. Fakat gerçek
moda 32 bitlik kaydedicileri hesaplamalarda kullanmanızda bir engel yoktur.
80386 kaydedici dolaylı adreslemede kullanabileceğiniz tüm geçerli adresleme
formatı aşağıdaki gibidir.
mov al, [eax]
; DS in ofsetleri için mov al, [ebx]
; DS in ofsetleri için mov al, [ecx] ; DS in ofsetleri için mov al, [edx] ; DS in ofsetleri için mov al, [esi] ; DS in ofsetleri için mov al, [edi] ; DS in ofsetleri için mov al, [ebp]
; SS in ofsetleri için mov al, [esp]
; SS in ofsetleri için
b- 80386 İndeksli, Taban/İndeksli, ve
Taban/İndeksli/Direkt Adresleme modları ( 80386 Indexed, Base/Indexed, and Base/Indexed/Disp Addressing
)
80386 İndeksli adresleme modu 32 bitlik bir kaydedici
ile sabit bir değerin birleşiminden meydana gelir. Taban indeksli adresleme
modu iki adet 32 bitlik kaydedicinin birleşiminden ve taban indeksli artı direkt
adresleme modu ise bir sabit değer ile iki tane 32 bitlik kaydedicinin
birleşiminden meydana gelir. Hiçbir zaman unutmamalısınız ki bu adresleme
modları ne kadar 32 bitlik olsa da, şayet 16 bitlik gerçek moda kullanılırlarsa
erişebilecekleri ofset adresleri 16 bit’i geçemez.
80386 taban indeksli hafıza adresleme modlarında taban adres ve indeks adresi
diye iki tane terim kullanılır. Taban adresini ilk operand iken indeks adresini
son operand gösterir. Bu kural kaydedici ismi ne olursa olsun geçerlidir. Fakat
8086 indeksli veya taban indeksli hafıza adresleme modlarında, taban adresini
sadece BX veya sabit bir değer gösterebilirken, indeks adreslerinide SI, DI, BP
ve BX gösterebiliyordu. 80386 ve sonrası işlemcilerde bulunan bu esneklik
şüphesiz ki programcıların işini kolaylaştırır.
Aşağıda 80386 indeksli hafıza adresleme modları için birkaç örnek görülüyor;
mov al, disp[eax] ;İndeksli adresleme mov al, [ebx+disp] ;modları. mov al, [ecx][disp] mov al, disp[edx] mov al, disp[esi] mov al, disp[edi] mov al, disp[ebp]
;SS in ofsetleri için mov al, disp[esp] ;için.
Aşağıdaki örneklerde ise 80386 taban indeksli adresleme modları için çeşitli
örnekler görünüyor. Bu örneklerde ilk kaydedici taban adresini gösterirken,
ikinci kaydedici ise indeks adresini gösterir. Tabi ki sonuçta bu iki
kaydedicinin içindeki değerler toplanarak etkin olan hafıza adresini işaret
edecekler. Kaydedicilerin 32 bitlik olduğunu düşünecek olursanız 4GB’lık
hafızanın istediğiniz bir konumuna erişebilineceğini de görebilirsiniz
(tabi ki korumalı modda). Burada
taban adresi olarak esp ve ebp kullanıldığında SS’deki bir ofset adresini
göstereceğini unutmayın. Bununla birlikte indeks adreslerini gösteren kaydedicilerin
türü verinin hangi segmentten alınacağına etkisi olmaz.
mov al, [eax][ebx] ;Taban indeksli adresleme mov al, [ebx+ebx] ;modları. mov al, [ecx][edx] mov al, [edx][ebp]
;DS deki ofsetleri gösterirler. mov al, [esi][edi] mov al, [edi][esi] mov al, [ebp+ebx]
;SS deki ofsetleri gösterirler. mov al, [esp][ecx]
;SS deki ofsetleri gösterirler.
Yukarıdaki adresleme modlarına sabit bir değer eklerseniz 80386 taban indeksli
artı direkt adresleme yapmış olursunuz.
mov al, disp[eax][ebx] ;Taban indeksli artı direkt adresleme
mov al, disp[ebx+ebx] ;modları. mov al, [ecx+edx+disp] mov al, disp[edx+ebp] ;DS deki ofsetleri gösterirler. mov al, [esi][edi][disp] mov al, [edi][disp][esi] mov al, disp[ebp+ebx] ;SS deki ofsetleri gösterirler. mov al, [esp+ecx][disp] ;SS deki ofsetleri gösterirler.
80386 indeksli adresleme modlarında sadece tek bir kısıtlama söz konusudur; esp
kaydedicisini indeks kaydedicisi olarak kullanamazsınız ama esp nin taban
kaydedicisi olarak kullanılmasının bir kısıtlaması yoktur.
c- 80386 Ölçekli İndeksli Adresleme Modu (80386 Scaled Indexed Addressing Modes)
Programcılıkla az çok uğraşan arkadaşlar diziler konusu bilirler. Yukarıda
anlattığım 3 adresleme modu ile dizilerin elemanlarına rahatça erişebilirsiniz.
Fakat özellikle dizi işlemler için bir adresleme modu arıyorsanız 80386 ölçekli
indeksli adresleme modunu kullanmak daha akıllıca olur. Bu adresleme modu ile
indeks kaydedicisini 1, 2, 4 veya 8 ile çarparak dizilerin elemanlarına erişmede
daha esnek bir yapı sağlar.
Kullanımı;
disp[index*n] [base][index*n]
veya
disp[base][index*n]
"base" ve "index" 80386’nın herhangi bir genel amaçlı kaydedicisi olabilir. "n"
ise 1, 2, 4 ve 8 değerlerini alabilir.
ebx = 1000h ve esi = 4, için aşağıdaki örnekleri inceleyelim;
mov al,8[ebx][esi*4] ;ds:1018h daki veriyi dan AL’ye kopyalar. mov al,1000h[ebx][ebx*2] ;ds:4000h daki veriyi dan AL’ye kopyalar. mov al,1000h[esi*8] ;ds:1020h deki veriyi dan AL’ye kopyalar.
80386 İndeksli, Taban/İndeksli, ve Taban/İndeksli/Direkt Adresleme modlarını da
80386 Ölçekli İndeksli Adresleme Modunun n değerinin 1 olduğu adresleme modları
olarak düşünebilirsiniz
mov al, 2[ebx][esi*1]
= mov al, 2[ebx][esi] mov al, [ebx][esi*1]
= mov al, [ebx][esi] mov al, 2[esi*1]
= mov al, 2[esi]
MASM ve TASM 80386’nın tüm bu hafıza adresleme modlarının yazımında değişik
varyasyonları kabul eder. Aşağıdaki tüm operandlar aynı işi yaparlar.
disp[bx][si*2], [bx+disp][si*2], [bx+si*2+disp], [si*2+bx][disp], disp[si*2][bx],
[si*2+disp][bx], [disp+bx][si*2]
Sonuç:
80386 adresleme modları 8086 adresleme modlarına nazaran programcıya daha çok
olanak sağladığından daha çok tercih edilirler. Benim şahsi görüşüm X86 uyumlu
PC’ler gerçek gücünü 80386 işlemcisi ile birlikte gelen bu olanaklardan
almışlardır. Zaten CPU tarihinde 80386 bir devrin kapanıp diğer bir devrin
başladığı nokta olarak kabul edilir. Ne var ki 8086 adresleme modları 80386 adresleme modlarına nazaran daha hızlı
çalışırlar. Bu yüzden söz konusu olan hız ise (mikro saniyelerden bahsediyoruz
ki bunlar bazen birleşip dakikalar oluyor) adresleme modu seçilirken çok dikkat
edilmesi gerekir.
80386 taban indeksli ve taban indeksli artı direkt adresleme modları, ölçeği 1
olan (n=1) 80386 Ölçekli İndeksli Adresleme Modu olarak düşünülebilir ve bu
hafıza adresleme modlarında gösterilen ilk kaydedici taban ikinci kaydedici
indeks adresini gösterir. Taban adresini gösteren kaydedici ebp veya esp ise bu
adresleme SS’deki bir ofset adresine diğer kaydediciler taban adresi olarak
seçildiğinde DS deki bir ofset adreslenmiş olunur. İlk yazılan kaydedici ölçekli
olarak gösterilirse ("*n") bu bir taban adresi olmaktan çıkar bir indeks adresi
olur. Ayrıca operanda segment kaydedicisini de göstererek adresleme modunu
zorlayabilirsiniz.
[ebx][ebp]
;DS’nin ofsetini gösterir. [ebp][ebx] ;SS’nin ofsetini gösterir. [ebp*1][ebx] ;DS’nin ofsetini gösterir. [ebx][ebp*1] ;DS’nin ofsetini gösterir. [ebp][ebx*1] ;SS’nin ofsetini gösterir. [ebx*1][ebp] ;SS’nin ofsetini gösterir. es:[ebx][ebp*1] ;Zorlanmış,ES’nin ofsetini gösterir.
Şimdi sıra
uygulamada. Bu uygulama ile hem adresleme modlarını hem de Turbo Debugger
programının kullanımını açıklamaya çalışacağım.
TITLE
Adresleme Modları (admod.asm)
;########################################
;# Bu program 8086 ve 80386 adresleme modlarının #
;# iyi kavranması için yazılmıştır.
#
;# Son Güncelleme: 17/04/05
#
;# Yazan --> Eren ERENER
#
;########################################
.MODEL SMALL
.STACK 32
.DATA
VerilerByte DB
5h, 17h, 8Dh, 0AFh
VerilerWord
DW 1234h, 7h,
0C01Dh
VerilerDoubleWord DD
3DF0178Ah, 11223344h, 12345678h
.CODE
ANA
PROC
MOV AX, @DATA
; Data segment
MOV DS, AX
; ayarlanıyor.
MOV AX, 5566h
; Acil adresleme.
MOV BX, AX
; Kaydedici adresleme.
;----------------------------------------
;8086
Hafıza Adresleme Modlarına Örnekler
;----------------------------------------
MOV DX, DS:0000h
; Direkt adresleme.
MOV BX,
0000h
MOV AL, [BX]
; Kaydedici dolaylı adresleme, DS:0000h adresindeki 5h değeri AL’ye
kopyalanır.
MOV AL, 3[BX]
; İndeksli adresleme, DS:0003h adresindeki AFh AL’ye yüklenir
MOV AL,
3[VerilerByte] ; Yukarıdaki ile aynı işi yapar,
; zaten assembler VerilerByte değişken ismini ds:0000h olarak çevirecektir.
MOV SI, 0
; SI = 0 oldu.
MOV AL, [BX][SI]
; zaten BX=0’dı, şu anda AL’ye kopyalanacak değer DS:0000h’daki 05h’tır.
INC SI
; SI’yı 1 arttırdık,
MOV AL, [BX][SI]
; şimdi AL’ye kopyalanacak değer DS:0001h’daki 17h’tır.
INC SI
; SI’yı,
INC SI
; 2 arttırdık, şimdi SI=3 oldu.
MOV AL, [BX][SI]
; şimdi AL’ye kopyalanacak değer DS:0003h’daki AFh’tır.
; Burada BX taban SI indeks kaydedicisi oldu.
MOV AL, [BP][SI]
; DS değil SS ’in offsetlerini adresler, şu anda Stack Segment’in içinde ne
olduğunu bilmiyoruz!!!
;Taban
indeksli artı direkt adresleme
MOV SI, 0
; SI=0 oldu.
MOV AX, 4[bx+si]
; BX’i değiştirmediğimizden hala sıfır ve AX’e getirilecek 2 byte’lık veri
ds:0004h adresinden alınacak,
; çünkü 4+0+0 = 4’tür. Bu adres VerilerWord değişkenin adresidir ve bu
adreste 1234h vardır.
INC
SI
; SI,
INC
SI
; 2 kere arttırılıyor ve,
MOV AX, 4[bx+si]
; VerilerWord dizisinin 2. elemanına erişiliyor.
; çünkü 4+0+2 = 6’dır. Bu adreste 0007h vardır.
;----------------------------------------
;80386
Hafıza Adresleme Modlarına Örnekler
;----------------------------------------
.386
; 80386 adresleme modlarını ve 32 bitlik kaydedicileri kullanmak için bu
direktifin yazılması gerekir!!!
MOV ESI, 0
; İndeks’i 1 olarak ayarlandı.
SUB EBX,
EBX
; ebx = 0 yapıldı. SUB (Subtract) yani çıkartma komutu,
; burada "MOV ebx, 0" ile aynı işi fakat daha hızlı yapar.
MOV AX, 4[ebx][esi*2]
; 80386 ölçekli indeksli adresleme modu ile VerilerWord dizisinin 1. elemanı
(1234h) AX’e kopyalandı.
INC ESI
MOV AX, 4[ebx][esi*2]
; 80386 ölçekli indeksli adresleme modu ile VerilerWord dizisinin 2. elemanı
(0007h) AX’e kopyalandı.
LEA EAX,
VerilerDoubleWord ; LEA komutu (Load Effective Address)
VerilerDoubleWord dizisinin başlangıç adresini EAX’e kopyalıyor,
; 80386
kaydedici adresleme
SUB
EBX, EBX
; EBX = 0 oldu.
; 80386
taban indeksli adresleme.
MOV ECX, [EBX][EAX]
; ve bu dizideki ilk eleman (3DF0178Ah) ECX’e kopyalanıyor.
MOV AH,4CH ;DOS’a
INT
21H
;dönüş
ANA
ENDP
END ANA
|
Yukarıdaki program sadece 8086 ve 80386 adresleme modlarına örnek olması için
hazırlanmıştır. Hafızadaki veriler nasıl erişilir sorusuna yanıt arayanların bu
programı dikkatle ve satır-satır incelemesini tavsiye ederim.
Sıra bu programı derledikten sonra Turbo Debugger ile açıp hafızada ve işlemcide
hangi olayların olduğunu incelemeye geldi. Turbo Debugger programının 16 ve 32
bitlik hafızayı incelemek için iki ayrı versiyonu var şu anda gerçek modda
çalışan programlar hazırladığımızdan dolayı 16 bitlik sürümünü kullanacağız.
Programı buradan indirebilirsiniz. İndireceğiniz bu dosya Turbo Debugger, Turbo Assembler’ı, Turbo Linker paketini içermekte. Ayrıca bu pakete,
içinizde belki hala DOS veya Windows 95 kullanan vardır diye bir mouse programı
ekledim. Şayet durum böyleyse önce konsola "mouse" yazdıktan sonra Turbo
Debugger’ı çalıştırın.
Turbo Debugger Microsoft’un debug’ı ile aynı işi yapar fakat daha
kullanışlı bir kullanıcı ara yüzüne sahiptir.
Turbo Debugger ile program dosyalarınızı
açtıktan sonra bir çok işlem yapabilirsiniz.
Not: Turbo Debugger’ı pencere modunda çalıştırırsanız GDI (Graphics
Device Interface) kaynaklarını sömürürcesine kullandığından bilgisayarınız
yavaşlayabilir. Bu yüzden tam ekran modunda başlatmanızı öneririm. Programı
pencere modunda başlattıktan sonra konsol penceresinin ayarlarından tam ekran
yapmanızın da bir faydası olmaz. Bu yüzden önce komut istemini açın, sonra tam
ekran yapın ve Turbo Debugger’i çalıştırın.
Turbo Debugger kullanımına ilişkin birkaç ipucu:
İlk olarak konsola "td" yazarak programı çalıştırın.
Turbo Debugger’ın ekranına
ulaşacaksınız.
Şekil 2 - Turbo Debugger ekranı.
Turbo Debugger ekranı 5 bölümden oluşuyor. Şekildeki 1, 2 ve 3 nolu kısımlar
hafızayı incelemek için kullanılırlar. 4. kısım mikroişlemci kaydedicilerini ve
5. kısımdada yine mikroişlemci kaydedicilerinden olan flag register’ı bitsel
olarak görebilirsiniz. Genellikle 1. bölge programı oluşturan kodları, 2.
bölgede verilerin incelenmesi için kullanılır. 3. bölgeyi yığın hafızayı
gözlemlemek için kullanabilirsiniz.
Turbo Debugger açıldıktan sonra "File" menüsünden derlediğiniz program dosyasını açıp
kodlarını inceleyebilirsiniz. Program dosyanızı açtıktan sonra "Program has no symbol table"
diye bir mesaj gelebilir. Bu mesaj penceresinde "OK" e tıklayıp çalışmaya
başlayabilirsiniz.
Şekil 3 - Turbo Debugger ile dosya açma.
Ayrıca menülerden sırasıyla View/Another/File seçeneklerini tıklayarak başka bir
dosyayı diğer bir pencerede açabilirsiniz. (Mesela kaynak dosyasını)
Şekil 4 - Turbo Debugger ile aynı ekranda
kaynak dosyanızı da görebilirsiniz.
Şayet bu pencerede yazı yerine hex kodları görürseniz, pencerenin üzerine sağ
tıklayıp açılan menüden "Display As" ı seçin.
Programı çalıştırma seçeneklerini "Run" menüsünden seçebilirsiniz. F8 tuşu
programı adım adım işletmek için kullanılır, F7 ise adım-adım program işlenirken
döngüleri incelemenizi sağlar. Program adım-adım işlenirken mavi bir çubuk
o anda hangi komutun işleneceğini gösterir. Aynı ekranda işlemcinin
kaydedicilerinin nasıl değiştiğini gözlemleyebilirsiniz. Değişikliğe uğrayan
kaydediciler beyaz görünür. Programın tekrar çalıştırmak için "Run" menüsünden
"Program Reset" seçeneğini seçebilirsiniz.
Şekil 2 - Program adım-adım işleniyor.
Verilerinizi görmek için 2. pencereye sağ tıklayıp "goto" yu seçin. Açılan
pencereye görmek istediğiniz adresi yazın.
Şekil 2 - Turbo Debugger ile hafıza
pencerelerini kullanma.
Yalnız .exe tipindeki program dosyalarında verilerinizi görebilmek için mov ax,@data
ve mov ds,ax satırlarının işlenmiş olması gerekir.
Programınız satır-satır işlenme sürecinde, adresleme yapılan bir komutun
işlenmesi anında kod penceresinin sağ üst köşesinde hafızanın hangi bölgesine
erişildiği görülebilir. Bu makalemizdeki programı incelerken bu özelliği sıkça
kullanmanız gerekecek.
Şekil 2 - Turbo Debugger ile adresleme
modlarının işlevleri rahatça görülebilir.
Mesela yukarıdaki şekilde mov al, [BX] komutu işleniyor ve
kaydedici dolaylı hafıza adresleme yapılıyor. Burada AL kaydedicisine
kopyalanacak olan veriyi "ds:0000 = 05" şeklinde görebilirsiniz.
32 bitlik kaydedicileri görmek içinse kaydedicileri gösteren kısıma (4. kısım)
sağ tıklayıp "Registers 32 bit" i seçebilirsiniz.
Turbo Debugger’ın daha bir çok özelliği var ama şu anda bizlere bu kadarı yeter.
Şimdi sizler Turbo Debugger ile bu makaledeki programı inceleyerek pratik yapın,
ayrıca bu güne kadar yazdığımız programları da Turbo Debugger ile inceleyerek
programı kullanmasını daha iyi kavrayın.
Bir sonraki makalemizde veri tipleri ve x86 komutlarını inceleyeceğiz. Daha
sonra prosedür ve makrolarıda gördükten sonra nihayet kendi programlarınızı
yazabilir seviyeye geleceksiniz. Genelde sizlerden makalelerin daha sık
aralıklarla yayınlanması ile ilgili mailler alıyorum. Fakat bir makale
hazırlamak en az 1 haftamı alıyor ve üniversitedeki derslerden ve işlerden
dolayı çok az vaktim oluyor. Bu yüzden makalelerimin yayınlanma aralığı birden
20 güne sıçrayabiliyor. Hal böyleyken sizlerin arayı soğutmamak için başka
kaynaklardan faydalanmasını tavsiye ediyorum. Bu şekilde çalışan bazı arkadaşlar
internetten kaynak kod bulup, derleyip takıldıkları yerlerde bana mail
atıyorlar, bunlara cevap vermesi daha az zamanımı alıyor ve sizler içinde çok
faydalı olacağından eminim. Şayet İngilizce okuduğunuzu anlıyorsanız sizlere
Art of Assembly’yi tavsiye ederim. İnternetten okuyabileceğiniz bedava bir
kitap. Mesela bu makaleyi yazarken bu kaynaktan çok yararlandım ve sizlere de
tavsiye ederim.
Evet yine geldi ayrılık vakti. Sizlere bol assembly’li günler dileyerek
huzurlarınızdan ayrılıyor ve bir sonraki makalede görüşmek dileğiyle esen kalın
diyorum. (TV programı sunucusu olabilirdim aslında :))
Makale:
x86 Adresleme Modları ve Turbo Debugger Assembly ve X86 Programlama Eren Erener
|