Bu makalemde 80x86
komutlarını değişik örneklerle anlatmaya devam edeceğim. Bu makalede toplama ve
çıkartma işlemlerine bir nokta koyup çarpma ve bölme işlemlerine bir giriş
yapmak istiyorum.
80x86 KOMUT SETİ (Bölüm 3)
NEG komutu
NEG negatif
kelimesinin kısaltmasıdır. Tek operandı vardır. Kullanım formatı aşağıdaki
gibidir.
neg reg
neg mem
yani operandı
herhangi bir kaydedici veya hafıza adresi olabilir. Yaptığı iş operandın
değerinin negatifini almaktır. Daha doğru bir deyişle operandını 0dan çıkartır.
Binary düzende düşünecek olursanız 1lein yerine sıfır 0ların yerine 1 getirir
ve bu sonuca 1 ekler.
mov al,0fh
neg ax
yukarıdaki işlemden
sonra AXin içeriği F1h olacaktır.
0Fh
= 0000 1111
tersi = 1111 0000
1111 0000 +1 = 1111 0001 = F1h
Bu komutun ne amaçla kullanılabileceğini makalenin sonlarına doğru
anlayacaksınız.
MUL ve IMUL
Komutları
MUL çarpma IMUL ise
işareti dikkate alarak çarpma işlemlerini yapar. Kullanım formatı aşağıdaki
gibidir.
İşaretsiz çarpma:
mul reg
mul mem
İşaretli çarpma:
imul reg
imul mem
imul reg, reg,
imm (*)
imul reg, mem,
imm (*)
imul reg, imm
(*)
imul reg, reg
(**)
imul reg, mem
(**)
*- Sadece 286 ve sonrası işlemcilerde.
**- Sadece 386 ve sonrası işlemcilerde.
IMUL komutunun 286
ve 386 dan sonraki kullanım formatlarına bakacak olursanız, bu güne kadar
gördüğümüz formatlardan biraz farklı olduğunu görürsünüz. Bu kullanım şekilleri
programcıların kafasını biraz karıştırmakla beraber tek bir komut satırı ile
çabucak çarpma işlemini yapmasını sağlar. Nede olsa Intel karmaşık komut setini
benimsemiştir.
Çarpma komutları
bayt-bayt, word-word veya Doubleword-Doubleword çarpma yapabilir. Tabi ki
Doubleword çarpım için 386 ve sonrası işlemci kullanmanız gerekiyor, çünkü 32
bitlik kaydediciler 386 ile birlikte gelmiştir.
Ayrıca çarpma işlemi
toplamadan daha büyük sonuçlar çıkarabilir. Yani 2 basamaklı bir değeri başka
bir 2 basamaklı değer ile toplarsanız sonuç en fazla 3 basamaklı çıkarken
çarpmada bu 4 basamağa kadar çıkabilir. Daha fazla basamaklı sayıların
çarpımında sonuç çarpılan veya çarpandan çok daha fazla basamaklı çıkabilir. Bu
gerçeği göz önüne alarak işlemci tasarımcıları sonucu her zaman çarpan ve
çarpılanın boyutundan daha büyük bir kaydedicide saklama yoluna gitmişlerdir.
Bunları aşağıdaki şekil ve açıklamalarla daha iyi anlayacaksınız.
Byte Çarpma
27h ile 17hı
çarpmak için;
mov al, 27h
mov dl, 17h
mul dl
komutlarını
kullanabilirsiniz. Burada "mul dl" komutu ile DL*AL işlemi yani bu
kaydedicilerdeki değerler olan 17h ve 27h sayıları çarpılır. Peki sonuç nerede?
Yukarıdaki şekle baktığınızda sonucun AX içinde olacağını görebilirsiniz. Bu
çarpma işleminden sonra AXte 0381h değeri görülür.
Pratik olarak 8
bitlik bir değerin karesini almak için;
mov al,sayi
mul al
komutlarını
kullanabilirsiniz.
Word
Çarpma
Bu tür bir çarpma
işleminde operand AX ile çarpılır ve sonuç DX-AX kaydedicilerinden okunur. DXte
daha önce ne olduğu önemli değildir çünkü çarpmadan sonra buraya sonucun yüksek
değerlikli byteı yerleşir. Sonucun düşük değerlikli byteı ise AX
kaydedicisinde saklanır.
Burada dikkat
ederseniz çarpma işlemiyle birlikte AX kaydedicisindeki "çarpılan" da
kaybedilecektir. Benim tavsiyem bu tür çarpmalarda çarpan ve çarpılanı birer
değişken olarak programınızın data segmentinde tanımlamanızdır. Aşağıdaki
örnekleri inceleyin,
100h ile 2345h
değerlerini çarpalım;
mov ax, 2345h
mov bx, 100h
mul bx
bu işlemden sonra DX=0023h
ve AX=4500h olur. Yani asıl sonuç olan 234500h değerinin yüksek değerlikli
wordu DXte düşük değerlikli kısmıda AXte görülür. Fakat çarpılan değer yani
2345h bu işlemden sonra kaybolacaktır. Şayet bu çarpılan değer sizin için
önemliyse;
carpilan db 2345h
..
..
..
mov ax, carpilan
mov bx,100h
mul bx
böylece 2345h değeri
daima "carpılan" ismi ile hafızada korunur. Aynı şeyi tabi ki çarpan için yani
100h değeri içinde yapabilirsiniz.
carpilan db 2345h
carpan db
100h
..
..
..
mov ax, carpilan
mov bx, carpan
mul bx
Double Word Çarpma
Double word
boyutundaki verilerin çarpımında da word dekine benzer bir yapı kullanılır.
Çarpılan değer EAX kaydedicisine yerleştirilip, MUL veya IMUL komutunun peşinden
gelen operand ile bu değer çarpılır. Daha sonra elde edilen sonucun yüksek
değerlikli doublewordu EDXte düşük değerlikli doublewordü ise EAXte
saklanır. Yani sonuç 64 bitliktir.
carpilan dd 12345678h
carpan dd
34522344h
..
..
.386
..
..
mov ax, carpilan
mov bx, carpan
mul bx
Yukarıdaki örnekte
sonuç olarak işlemci 03B878D610295FE0h değerini hesaplar. Bu çarpma işleminden
sonra EDX=03B878D6h ve EAX=10295FE0h olur.
Buradaki .386 32
bitlik kaydedicileri kullanmak için assemblera verilen bir direktif (talimat)
dır. EDX ve EAX gibi 32 kaydedicilerin 32 bitlik alanlarını kullanmak için nu
talimatı vermeniz gerekir. 32 bitlik programlama, 16 bitlik programlama nedir
bunlar? Şimdilik sadece 32 bitlik programların 16 bitliklere göre daha avantajlı
olduğunu görebilirsiniz. Çünkü 32 bitlik programlama ile kaydedici boyutlarımı 2
katına çıkıyor ve bir kaydedicide hesaplayabileceğimiz değerlerde aynı oranda
artıyor, bu işlemi 16 bitlik bir programlama ile de halledebilmemize rağmen 2
katı daha fazla komut yazmamız gerekir.
MUL komutu bayrak
kaydedicisinin C ve O bitlerini etkiler. Bu bayraklar beraber
değerlendirildiğinde aşağıdaki sonuçlar çıkartılır.
1- Byte boyutundaki
bir operand AL ile çarpılırsa sonuç AXte görülür. AH=0 ise C ve O sıfır olur,
aksi durumlarda bu bayraklar set (1) olur.
2- Word çarpmada C
ve O sıfır ise DXte sıfır demektir, aksi durumlarda bu bayraklar set olur.
3- Double word
çarpmada ise C ve O sıfır ise EDXte sıfır demektir, aksi durumlarda bu
bayraklar yine set olur.
Yukarıdaki üç durum
soldaki sıfırların çarmada bir değeri olmadığından, sonucu optimize etmenize
yardımcı olacaktır.
IMUL
ile Diğer Çarpma Formatları
IMUL (Integer
Multiplication) komutu ile yukarıdaki MUL komutu için verilmiş kalıpları
kullanabilirsiniz, bununla beraber IMUL komutuna özel çok operandlı kullanım
formatlarıda mevcuttur. Tüm kullanım formatları bu makalenin başında verildiği
gibidir fakat kaydedicilerin 8 16 ve 32 bitlik durumları da göz önüne almamız
gerekir. Şimdi henüz açıklamadığımız 286 ve 386 sonrası işlemcilerde
kullanılabilen komut formatlarını aşağıdaki örneklerle inceleyelim.
imul operand1,
operand2, imm ;Genel kullanım formatı
imul reg16, reg16,
imm8
imul reg16, reg16,
imm16
imul reg16, mem16,
imm8
imul reg16, mem16,
imm16
imul reg16, imm8
imul reg16, imm6
imul reg32, reg32,
imm8 (*)
imul reg32, reg32,
imm32 (*)
imul reg32, mem32,
imm8 (*)
imul reg32, mem32,
imm32 (*)
imul reg32, imm8
(*)
imul reg32, imm32
(*)
* Sadece 80386 ve sonrası işlemcilerde kullanılabilir
Yukarıdaki komut
formatlarının 3 operandlı olanları
operand1 := operand2 x imm
ve 2 operandlı
olanlarıda
operand1 := operand1 x imm
şeklinde çalışır.
Her zaman son kullanılacak olan operandın "imm" yani sayısal bir değer olduğuna
dikkatinizi çekerim.
mov bx, 4 ; BX =
0004h
imul ax, bx, 3
; AX = 4 * 3 = 000Ch
Bu komutlar ile 8x8
bit çarpım söz konusu değildir, imm8 olarak yukarıda gördüğünüz operand sadece
komutunun makine kodunun olmasını sağlar. Ayrıca bu çarpma işlemlerinde sonucun
boyutu operandta belirtilen kaydedicilerin boyutuyla aynıdır, yani makalenin
başında anlattığımız mul komutu gibi sonuç operandın 2 katı olmaz. Bu durumda
sonucun hedef kaydediciye sığmaması durumuna karşı C ve O bitleri birlikte kontrol
edilmelidir, bu durumlara Intelin komut setinden bakabilirsiniz.
Bununla beraber bu formattaki çarpma komutları Z bitini her zaman
doğru bir şekilde etkilemeyebilir, şayet sonucun sıfır olup olmadığı sizin için
önemli ise ancak sonucu sıfır ile karşılaştırdıktan sonra Z bitini kontrol
etmelisiniz. Aynı şekilde sonucun işaretini öğrenmek için işaret bayrağı yerine
C ve O bitlerinin sıfır olup olmadığı kontrol
edilmelidir.
IMUL komutu için
Intelin 80286 ve sonrası işlemcilere koyduğu bu adresleme biçimleri çok boyutlu
diziler ile yapılan işlemleri hatırı sayılır biçimde kolaylaştırmıştır. Bu
konuya çok boyutlu dizileri ve karmaşık veri yapılarını anlatırken bir daha
değinmeyi düşünüyorum.
DIV ve IDIV Komutları
DIV division yani
bölme kelimesinin kısaltmasıdır. Bölme işlemi çarpmanın tersine bölünene göre
küçük sonuç üretir, bu yüzden bu komutları kullanırken bölünenin boyutu
bölenin boyutunun iki katı olmak zorundadır, an azından Intel bu komutlar için
böyle bir form öngörmüştür. Bu yüzden bölünenin boyutu en az word türünde
olmalıdır, çünkü x86 Assembly dilinde en küçük veri tipi bytedır. Bu durumda
byte türünden bir değeri bölmek isterseniz bunu CBW komutu ile word
boyutuna dönüştürmeniz gerekir, hatırlarsanız bu tür komutları daha önceki
makalelerimizde açıklamıştık.
Div ve idiv
komutlarının genel kullanım formatları aşağıdaki gibidir.
div reg ; İşaretsiz
çarpma
div mem
idiv reg
; İşaretli çarpma
idiv mem
Wordu Bytea Bölmek
Örnek: 10h:3h
işlemini yapmak istiyoruz, bu değerlerin ikisinin de byte türünden olduğunu
varsayalım;
mov al, 10h
mov bl, 03h ; bl
yerine başka bir kaydedicide olabilir!
cbw
; 10h şimdi 0010h ve AXte
div bl
; ax, blye bölündü
bu işlemden sonra AX,
0105h olur. AHtaki 01h kalan ve ALdeki 05h ise bölümdür. Nitekim 16nın (yani
10h) 3e bölümü ile de bu sonuç üretilir. Bu tür bölme işleminde elde
edebileceğiniz en büyük bölüm 255 (FFh işaretsiz değerler için) yada 127 (7Fh
işaretli değerler için) değerleridir.
Doublewordu Worde Bölmek
Bu tür bölme
işleminde elde edebileceğiniz en büyük bölüm 32767 (FFFFh işaretsiz değerler
için) yada 16383 (7FFFFh işaretli değerler için) değerleridir.
Quadwordu Doubleworde Bölmek
Peki ya sonuç tam değilse?
Mesela 11hı (yani
17yi) 3e bölerseniz sonuç normalde 5,666... şeklinde olur, bu işlemi div
komutu ile yaptığınızda ise AXte 0205h değerini görürsünüz, yani bölüm 5 ve
kalan 2. Özetle 17nin içinde 5 tane 3 ve 1 tanede 2 vardır ve div ve idiv
komutları tamsayı bölme işlemlerini gerçekleştirebilir. Ondalıklı bölme
işlemleri için floating point kaydedicileri kullanılır ve bu kaydediciler
matematik işlemcisinin içindedir. 80486 DX işlemcisine kadar matematik işlemci
normal işlemcinin yanına opsiyonel olarak konulurdu mesela işlemci 80386 ise
matematik işlemcisi 80387 olurdu. Artık matematik işlemcisi normal işlemcinin
içine gömülü olarak geliyor. Bu arada floating point ünitelerini kullanmak için
yüksek seviyeli assembly kodları yazmak gerekir ve bu iş bizim için henüz çok
erken. Fakat kalanı 10 ile çarpıp sonrada bölünen ile karşılaştırıp şayet
bölünenden büyükse tekrar bölene bölme işlemine gidebilirsiniz, aynı kağıt
üzerinde normal bölme işlemi yapar gibi, fakat floating point ünitelerini
kullanmak inanın bu işten daha pratiktir ve daha kolay sonuç verir.
Malesef Tüm Sonuçlar Binary
Toplama, çıkartma,
çarpma ve bölme komutlarını gördük, artık bu komutları kullanarak basit bir
hesap makinesi programı yazmak isteyebilirsiniz, böyle bir programı yazmaya
başladığınızda karşınıza sonuçları ekranda desimal formatta göstermek gibi bir
problem çıkacaktır. Evet ekrana yazdırmak için daha önce programlar yazdık fakat
bunu sadece stringler ile gerçekleştirdik. DB direktifi ile deklare edilen
kelime katarları (stringler) hafızada byte-byte ve ardışık olarak saklanır aynı
zamanda bu bytelar elbette harflerin veya sayıların ASCII kod karşılıklarıdır.
8086 komut seti (bölüm 1) başlıklı makalemizde saat programı yapmıştık ve
kaydedicilerde elde ettiğimiz sonuçları bir tablo vasıtasıyla ASCII
karakterlerini ekranda göstermiştik. Bu yöntem sıkça kullanılmaz, hatta daha
önce bu iş için bu yöntemi kullanan bir program görmedim diyebilirim.
Representation yani
gösterme veya sunum işlemi çok geniş bir yelpazede incelenebilir, bu tamamen
kullanıcının hayal gücüne kalmış bir olaydır. Benim burada anlatmaya çalışacağım
olay ise ekran text modunda iken hafızadaki binary ifadelerin ASCII karakter
karşılıklarını ekranda göstermek olacaktır.
Diyelim ki bir işlem
yaptınız ve sonucunu 20h (32) olarak AL kaydedicisinde saklamayı başardınız ve
bunu ekranda göstereceksiniz, bunu direk olarak ekrana yazdırırsanız sadece
imleci 1 kez ilerletmiş olursunuz çünkü 20h ascii kod tablosunda space yani
boşluk karakterine karşılık gelir, klavyedeki en büyük tuş yani. Peki 32yi nasıl
yazdıracağız? Unutmayın ki ascii kod tablosunda sadece rakamların, harflerin ve
bir dizi kontrol karakterinin kod karşılıkları vardır sayıların kod karşılıkları
yoktur. Bu bağlamda biz 32 yi değil 3 ve 2 yi yan yana ekranda göstermeyi
düşünmeliyiz. Ama şu anda ALde ne 3 ne 2 var sadece 20h var. Diğer bir gerçek
3un ascii kod karşılığı 33h ve 2 nin ki ise 32h dır. 30h ile 39h arası ascii
kod tablosunda rakamlar için ayrılmıştır.
şimdi ekrana
sırasıyla 33hı ve 32hı gönderirsek kullanıcı 32 yi görecektir. Problemi
özetleyelim elimizde bir baytlık 20h var ve bizim bunu 2 byte lık 3332h dizisine
dönüştürmemiz gerekiyor. Keşke bir komut bu işlemi bizim yerimize yapsa!
Böyle bir komut
varmı yokmu oraya geleceğiz ama 20h ile 3332h arasında güzel bir bağ var. 20h ı
0ah yani 10a bölsek zaten hexden decimale dönüşüm işlemi yapmış oluruz;
mov ax, 20h
mov bl, 0Ah
div bl
;AX=0203 yapar.
Keşke AXteki 0203h
değerinde 0ların yerine 3 gelseydi;
or ax,3030h ;AXteki 0203h artık 3233h oldu.
birde al ile ahı
yer değiştirsek! Acaba buna gerçekten gerek var mı? Hatırlarsanız ekrana bir
string yazdırmak için DOS kesmelerinden 09h nolu fonksiyonunu kullanmıştık ve bu
fonksiyon hafızadaki stringleri yazdırıyordu, yani biz AXteki bu 3233h değerini
önce bir hafızaya atalım sonra 32h ile 33hın yerini değiştirmek gerekiyor mu
düşünürüz;
mov sonuc, ax ; sonuc=3332h olur.
x86 tabanlı
işlemciler hafızayı adreslerken little endian byte sıralamasını kullanırlar, bu
yüzden aldeki düşük değerlikli byte hafızanın düşük numaralı adresine axteki
yüksek değerlikli byteta hafızanın yüksek numaralı adresine yerleşmiş olur,
böylece ax ile alnin içeriklerini takas etmemize de gerek kalmaz. Artık sonuc
değişkenini referans göstererek ekrana yazdırma kesmesini kullanabilirsiniz.
lea dx,sonuc
mov ah,9
int 21h
AAA, AAS Komutları
Klavyeden giriş
yapıldığında, basılan tuşa ait kod hafızada ascii formatta saklanır,
mikroişlemci hesaplamaları binary yapar bu yüzden asciiden binaryye dönüşüm
yapmak gerekir. Şayet ekrana bir karakter basılacaksa bu karakter ekrana
gönderilmeden önce ascii forma dönüştürülmelidir.
Bizler günlük
yaşamımızda desimal değerleri kullanırız, bu desimal değerleri hafızada binary
rakamlar olan 1 ve 0ları kullanarak gösterebiliriz, buna BCD (Binary Coded
Decimal) kodlama diyoruz. Desimal numaralar hafızada BCD olarak gösterilmenin
yanında ASCII olarakta gösterilebilir. İşte AAA, DAA, AAS gibi komutları bunun
için kullanıyoruz.
Örneğin klavyeden
girilen 1234 hafızada 31 32 33 34 olarak ASCII formda saklanır. BCD gösterimin
ise iki farklı çeşidi vardır, bunlar packed BCD (paketlenmiş BCD) ve unpacked
(paketlenmemiş) BCDdir.
1234 hafızada 01 02
03 04 byte dizisi olarak saklanırsa bu paketlenmemiş BCDdir, şayet aynı değer
hafızada 12 34 byte dizisi olarak saklanırsa bu da paketlenmiş BCDdir. AAA, AAS,
AAD ve AAM komutlarının hepsi ascii değerlere dönüşüm için kullanılır. Bu
komutların operandı yoktur AL kaydedicisindeki değerleri dönüştürürler.
AAA (Ascii Adjust
After Addition) komutu toplama komutundan sonra sonucu ascii ye ayarlar.
Aşağıdaki örnekleri inceleyin;
34h = 0011 0100b
35h = 0011 0101b
+_______________
69h = 0110 1001b
Sonuç 09
olması gerekir AAA komutu 6 değerini siler. Sonuç AL’de 09 olarak görülür.
36 = 0011 0100
35 = 0011 0101
+_______________
6B = 0110 1011
Sonuç 11
olması gerekir AAA komutu B değerinin yerine 1 koyar ve toplama sonucu 9
değerini aştığından AH kaydedicisine de 1 koyar, yani AX = 0101 olur.
Tüm bu
işlemlerden sonra OR komutunu kullanarak sonucu ASCII gösterim için
hazırlayabiliriz.
SUB
AH, AH ; AH temizleniyor
MOV AL, 6 ;
ilk değer ALde
ADD AL, 8 ; AL
+ 08h işlemi yapıldı sonuç 0Eh
AAA
; AX = 0104h
OR AL,30h ; AL = 34h
34h artık ascii
olarak 4 demektir. Tabi burada AH kaydedicisini de kontrol edip şayet 01 ise bu
değeri de 30h ile OR işlemine tabii tutmalıyız. Bu tür işlemler için genelde
ekrana tek bir karakter basma fonksiyonu kullanılır yani ALdeki değer teker
teker ekrana bastırılır. Henüz mantıksal komutları görmedik bu yüzden aşağıdaki
programı tam olarak anlamayabilirsiniz, bu yüzden açıklamalara dikkat edin.
Şekil -
Makine dilinden insan diline dönüşüm
Pekte güzel bir
ekran çıktısı olmasa da bu program dönüşümleri anlamak için iyi bir örnektir.
Yukarıdaki şekilde program ilk çakıştırıldığında klavyeden sırayla 2 ve 3
girilmiş ve sonuç 5 olarak ekrana basılmış. Daha sonra 7 ve 8 değerleri girilmiş
ve sonuç 15 olarak ekrana basılmış. Gerçektende sonuçlar doğru :) Unutulmaması
gereken bir nokta AAA komutunun C bitini etkilediğidir, şayet AAA komutundan
önceki bayrakların durumu sizin için önemliyse bayrakların durumunu saklamanız
gerekir.
AAS (Ascii Adjust
after Subtract) komutuda AAA gibi çalışır, çıkartma komutundan sonra sonucu
ASCIIye ayarlamak için kullanılır. AAS komutu sekiz bitlik AL kaydedicisinin
yüksek değerlikli 4 bitini kontrol eder, şayet AF bayrağı 1 ise (başka bir
deyişle bu 4 bit Ah...Fh arasında ise) ALden 6, AHtan 1 çıkartılır. AHtan 1
çıkartmak normalde 00 olan AHı FF yapmak yani rakamın negatifliğini ayarlamak
demektir.
mov al,35h ; ascii 5
sub al,31h
; 5 - 1 = 4
aas
; AF=0 olduğundan bir değişiklik olmaz sonuç hala 04h
or
al,34h ; ascii 4
sonucun negatif
çıktığı bir örnek;
mov al,34h ;ascii 4
sub al,38h
;ascii 8 -- sonuç FCh (negatif)
aas
; AX = FF06 (yanlış sonuç)
yukarıdaki gibi bir
durumda SUB komutu ile A ve C bayrakları set (1) olur. Bu durumda sonucu
doğrudan 30h ile OR işlemine tabi tutmak hatalı olacaktır. FCh sonucu desimal -4
değerine eşittir bu yüzden burada programcı OR komutu ile doğru ascii değeri
ekrana yazdırmadan önce NEG komutu ile tersini alabilir. Bu durumda sonuc 4
olacaktır. Tabiki bu 4 değeri ekrana yazdırılmadan önce önüne - işareti
konulmalı.
mov al,34h
sub
al,38h
jnc
devam
neg
al
devam: aas
or
al,30h
yukarıdaki kod
parçası sonuç negatif olsa da pozitif olsa da, sonucun mutlak değerini doğru bir
şekilde ascii değere dönüştürür.
Unutmayalım ki
klavye bize ekran text moundayken daima ascii değerler verecektir, bu çarpma ve
bölme işlemlerinde de problem oluşturur. Klavyeden girilen değerlere sanki 30
eklenmiş gibi geleceğinden çarpma işleminin sonucu girilen değerde bir
modifikasyon yapmadığımız sürece yanlış hesaplarız. Bu tür modifikasyonları
yapmak için başka mantıksal komutlara ihtiyacımız olacak, bu yüzden bu komutları
anlatmadan
DAA, DAS, AAD ve AAM gibi komutları açıklamak istemiyorum.
Gelecek makalemde Intelin komut setini anlatmaya devam edeceğim, bir sonraki
makaleye kadar hoşçakalın.
Makale:
80x86 KOMUT SETİ (Bölüm 3) Assembly ve X86 Programlama Eren Erener
|