C kaynak kodu içinde, assembler ile yazılan 'Fasm' adlı bir işlevin çağrılması için aşağıdakilerin

gerçekleştirilmesi gerekir. Bu işlevi C ile, aynı modül içinde yazmış olsaydınız prototipinin

int Fasm(unsigned x, int i, int  * val);  olacağını düşününüz.

Read More

1. Fasm (assembler) işlevinin prototipini C kaynak kodu içinde veriniz:

extern "C" {  void Fasm(unsigned x, int i, int  * val); }

 

2. İşlev çağrılmadan önce, soldakinden başlanarak parametreler yığıt üzerine eklenir  (C programındaki PUSH komutlarıyla. Bu işlemler C derleyicisinin ürettiği kod tarafından yapılır):

YIĞIT:

öncekiler

x

i

val

◄ EBP           (Yığıt üzerindeki birim alanlar 4’er byte uzunluğundadır.  

Offset değerlerini hesaplarken, yığıtın aşağıya doğru

uzadığını ve  üstteki konumların büyük adreslerde

◄ ESP            olduğunu hatırlayınız.)

 

 

3. Çağrı yapıldıktan hemen sonra, dönüş adresi stack üzerine eklenir (Bu PUSH işlemi, derlenmiş C kodundaki CALL komutunun işletilmesiyle yerine getirilir):

öncekiler

x

i

val

Dönüş adresi

◄ EBP

öncekiler     

   

◄ ESP

Yukarıdaki durum, assembler kodunun ilk satırının yığıt üzerinde bulacağı düzenlemedir. (Not: buradaki parametre değerleri birer değişkendir. işlev dönünceye kadar canlı durumdadırlar: POP ile kullanılamazlar, erişmek istediğinizde uygun adresleri kullanmak durumundasınız.)

4. Assembler kodu içinde:  Çağrılan işlev EBP'yi  (çağırma noktasındaki yapıyı ileride yeniden oluşturmak için) yığıt üzerine ekler (PUSH EBP). Aynı zamanda, MOV EBP,ESP komutu koda eklenir. Yazacağınız assembler yordamının ilk satırı bu iki işlemden sonra yer alır.  Bu iki işlemden sonra, işlev içinde görülecek düzenleme:

 

öncekiler

x

i

Val

Dönüş adr.

EBP (eski)    

 

 

 

 

 

◄ EBP, ESP

 

5. Elbette bundan sonra yığıt üzerine diğer değerleri PUSH edebilirsiniz (işlevden dönmeden önce, düzgün biçimde POP etmek kaydıyla.)  Örneğin, yazmaçları saklamak isteyebilirsiniz:

(yazdığınız kod)                                (Üretilen makine kodu)

Fasm     PROC                ---->       Fasm:    PUSH ebp

PUSHAD                                           MOV  ebp, esp

PUSHAD

 

PUSHAD işleminden hemen önce yığıtın durumu yukarıdaki gibiyse, işlemden sonra yandaki gibi olacaktır:

val adresi:    EBP + 8 ,   i : EBP + 12 ,      x : EBP + 16

öncekiler

x

i

Val

Dönüş adr.

EBP (eski)

PUSHAD ile eklenen 8 yazmaç(32 byte)

 

Anlaşılacağı gibi, EBP işlev parametreleri için taban değeri görevi görmektedir ve ESP'deki  değişikliklerden etkilenmez. Bu nedenle parametrelere erişimde EBP'den yararlanılmalıdır (parametrelere ESP ile de erişilebilir, ancak bu durumda ESP'de olması muhtemel değişiklikleri gözönüne almak gerekir; bu durumda, aynı parametreye işlevin farklı yerlerinde farklı offset değerleriyle erişmek gerekecektir).

 

 

 

 

 

 

6. Parametrelerin doğru biçimde yorumlanmaları gerekir. Örneğin adresi EBP+8 olan  'val' değeri aslında bir göstergedir. Bu değer bir adres olarak kullanılmalıdır.  Yordamın, C gösterimindeki (i + *val) toplama işini yapacağını varsayalım.  Aşağıda soldaki kodu yazarsanız bir mantık hatasına neden olursunuz  (tamsayı + adres).  Doğrusu sağda verilmiştir.

mov eax, [ebp+12]                                    mov eax, [ebp+12]

add eax, [ebp+8]                                       mov esi, [ebp+8]

       add eax, [esi]

 

Öncekiler

x

i

val

Dönüş adresi

EBP (eski)

PUSHAD ile eklenen 8 yazmaç (32  byte)

0 (a)

0 (b)

7. Assembler kodu içinde yeni bir değişken alanı gerektiğinde bu alanı yığıt üzerine PUSH işlemiyle gerçekleştirebilirsiniz. Bu alanın EBP'ye göre offseti değişmeyecektir. Örneğin, a ve b sembolik adlarıyla iki tamsayı gerekiyorsa, iki 'PUSH 0' komutuyla bunu sağlayabilirsiniz

Bu değişkenlerin hangisinin a, hangisinin b olduğu EBP'ye göre offsetleriyle ayırdedilir.

a :   EBP-36,  b: EBP-40

 

 

 

 

8. Dönüş işlemleri: (STDCALL)  

Aşağıdaki iki komut, programdaki 'ret' yerine, paragraf  4'deki iki işleme benzer biçimde, MASM tarafından otomatik olarak türetilir:

(yazdığınız kod)                     (Üretilen makine kodu)

ret                          ----->         POP ebp

ret n

 

(STDCALL yöntemi belirlenmişse, n değeri dönüş adresi kullanıldıktan sonra yığıt üzerinde kalan değerlerin silinmesi için ESP'den çıkartılacak byte sayısıdır. Yukarıdaki örnekte: 12.)

 

Ancak,  ’POP ebp’ işleminin gerçekten de yığıttaki (eski) EBP değerine denk düşmesi gerekir.

Kodunuzda PUSH ile yığıta eklenen diğer veriler varsa (yukarıdaki örnekte var), bunlar düzgün biçimde

POP edilmiş olmalıdır; bunu sağlamak programcıya düşer. Yığıtta bu biçimde toplam kaç ek byte

olduğu genel olarak çevirici tarafından hesaplanamaz (örneğin, PUSH işleminin bir döngü içinde

yapıldığını düşününüz.)

 

9. İşlev bir değer döndürüyorsa, bu değer eax içinde bırakılır. Doğru değerin döndürülmesi çağırılan

işlevin, dönen değerin yorumlanması (adres veya değer) çağırma noktasının sorumluluğundadır.  Geriye

dönen adres değerleri, işlev tarafından oluşturulan bir yerel değer olamaz (Bu bir mantık hatasıdır. İşlev

parametreleri ve yığıta eklenen yerel değişkenler işlevden dönüldüğünde silinirler.)

 

10. Bu dökümanda eksik/yanlış olduğunu düşünüyorsanız, bana kısa bir açıklayıcı not atınız.

Kaynaklar :

NASM : http://www.akifakkus.comhttp://www.dosyalar.akifakkus.com/Bil236-Experiment5/Belgeler/Nasm.ppt

Bütün Kaynaklar : http://www.dosyalar.akifakkus.com/Bil236-Experiment5/Belgeler/

Ödev Kaynak Dosya : Bil236_0809_5_20624305.zip