Mikroişlemci içinde yapılan işlemlerin durumlarını programcıya bildiren bir kaydedici mevcuttur. Tüm karar alma mekanizmaları bu kaydedicide gösterilen sonuçlar baz alınarak yapılır. İŞLEMCİ DURUM KAYDEDİCİSİ (PROCESSOR STATUS REGISTER) Diyelim ki klavyeden girilen iki adet değişken var bunlar eşit ise ekrana "eşit" eşit değilse "eşit değil" yazdırmak istiyoruz. Bunu üst seviye programlama dillerinde basit bir şekilde if-else mantığıyla yapabilirsiniz. Peki, mikroişlemci bu değerlerin eşit olup olmadığını nasıl gösterecek bize. Cevabı basit bu değerlerin farkını alarak, sonuç sıfırsa girilen değerler eşittir aksi takdirde eşit değildir. İyi ama assembly programcısı bu sonuç sıfır değerini nereden okuyacak? Bir işlemin sonucunun sıfır, negatif, pozitif veya elde ürettiği veya işlemin gerçekleştirilmesi için borç alındığı gibi durumları gösteren kaydediciye işlemci durum kaydedicisi diyoruz. Turbo debugger ile işlemci durum kaydedicisinin içeriğini gözlemleyebilirsiniz. Şekil 1-8086 da işlemci durum kaydedicisi. Şekilde kırmızı çerçeve içine alınmış kısım işlemci durum kaydedicisini bitsel olarak gösteriyor. Diğer kaydedicilerin aksine işlemci durum kaydedicisi programcı için bitsel olarak anlamlıdır. Ve bu bitlerin her birinin bir adı vardır. Örneğin C biti Carry yani taşıma biti veya Z biti Zero yani sıfır biti dir. Terminolojide genellikle bu bitler "flag" yani bayrak olarak adlandırılır. Aslında assembly programcıları işlemci durum kaydedicisi yerine bayrak kaydedicisi terimini daha çok kullanırlar. Örneğin Z için Zero Flag yani sıfır bayrağı denir. 8086, 80286, 80386, 80486 ve Pentium işlemcilerin bayrak kaydedicileri biraz farklıdır, daha doğrusu işlemciler geliştikçe ek bayraklar eklenmiştir, mesela 80286 dan itibaren işletim sistemlerinin yazımı için gerekli olan ayrıcalıklı mod bayrağı eklenmiştir. Yukarıdaki şekilde 8086 nın 8 bitlik bayrak kaydedicisini görmektesiniz, aşağıdaki şekilde ise 8086 dan 80486 ya kadar olan işlemcilerin sahip olduğu bayrakları görebilirsiniz.
Şekil 2 - x86 işlemci durum (bayrak) kaydedicisi. Bayrak kaydedicisindeki her bir bitin 1 olma durumuna SET, 0 olma durumuna da RESET denir. İşlemcinin ürettiği sonuçlar hakkında programcıyı uyaran bayrak kaydedicisindeki durumlar görecelidir. Örneğin carry (taşıma) bayrağı 1 olduğunda bazen işlemin elde ürettiğini bazen de borç alındığını ifade edebilir. Bazen de iki bayrağın durumu birlikte değerlendirilerek bazı sonuçlara varılır. Örneğin; MOV AX,0FFFEh ;FFFE = 65534 işaretsiz sayıyı temsil ederse ADD AX, 3 ; C = 1, O = 0 olur ve bu bir boyut taşmasını gösterir. Bu örneği daha iyi anlayabilmemiz için bazı matematik konularını hatırlatmak gerekir. Hani bir fonksiyonun çözümü için sorularda "her x gerçel sayısı için" gibi ifadeler geçer ya, işte bu tür kabullenmeler assembly dilinde çok önemlidir. Çünkü 1 ve 0 ları ancak bu şekilde yorumlayabilirsiniz, yani baştan evrensel kümenizi kafanızda tanımlamanız gerekir. Bu örnekte de FFFE=65534 değerini gösterirse diye başlıyoruz işe, nitekim FFFE işaretli sayı olarak kabul edilseydi -2 değerini gösterecekti. Ama burada işaretli değil sadece pozitif tamsayılarla işlem yapıldığı varsayılıyor. Bu işlemin sonucu AX kaydedicisinde 0001 olarak görülür. İşlemci sayıların işaretli veya işaretsiz olduğuna bakmadan FFFE ye 3 ekler ve FFFE değeri önce FFFF sonra 0000 ve daha sonrada 0001 olur. Tabi ki 65534 + 3, işleminin sonucu 1 e eşit değildir. İşlemin doğru sonucu olan 65537 değeri 2 byte lık AX kaydedicisinin içine sığmadığından bu sonuç üretilmiştir. Bu toplama işlemi sürecinde sayı FFFF den 0000 a dönerken C=1 olur, bunu elde olarak düşünebilirsiniz (tabi ki pozitif tamsayılar kümesi baz alınarak işlem yapılıyorsa) Aslında 0001 sonucunun başına bu elde getirildiğinde yani sonuç C biti ile birlikte okunursa 10001 olur ve bu gerçektende 65537 değerine eşittir. Fakat programcı doğal olarak AX kaydedicisini içindeki değeri yani 0001 i sonuç olarak alacaktır. Burada sonuç anlamlı aralık olan 0...65535 arasında olduğundan O=0 olmuştur. Özetle sonuç anlamlı aralıkta yanlış bir değerdir, bu durumu programcı iki bayrağa birden bakarak anlayabilir. Yukarıdaki gibi bir durumda programcı işlem yapıldıktan sonra bazı özel komutlarla C ve O (overflowtaşma) bitlerinide kontrol eder ve bu bitler sırasıyla 1 ve 0 ise bir boyut taşması olmuş demektir. Bu durumda programcı kullanıcı ekranına "sonuç çok büyük" gibi bir mesaj gönderebilir. Hesap makinelerinin belli bir değere kadar işlem yapabildiğini hatırlayın. Bu örnek için en başta FFFE işaretli bir saydır deseydik herhangi bir taşma söz konusu olmayacaktı. İşlemci yine aynı sonucu üretecek fakat bu sefer programcı bunu -2+3 olarak değerlendireceğinden sonuç doğal olarak 1 ve AX te 0001 değeri doğru sonuç olacaktı. Yine C=1 ve O=0 olsa da programcı için bu durum bir
taşmayı göstermeyecektir. İşte assembly programlama dili matematiğin bu basit gibi görünen temellerine bağlıdır. Hafızadaki 1 ve 0 ları nasıl yorumlarsanız öyle şekil alırlar. Bazen bu 1 ve 0 lar bir resim, bazen bir mp3 bazen de bir kelime oluveririler. Hani matrix filminde herkes kodlara bakıyor ama sadece Neo olayı idrak edebiliyorduya işte o misal 8086 için bayrak kaydedicisi 16 bitliktir ve bu 16 bit te sadece 9 adet bayrak mevcuttur. 386 ve sonrası işlemcilerde ise bayrak kaydedicisi 32 bitliktir ve bu 32 bit in her biri bir bayrağı temsil etmez, aralarda hiçbir şeyi ifade etmeyen boş bit ler mevcuttur. Şu anda gerçek mod programları yani 8086 için programlar yazdığımızdan bu 9 adet bayrağın ne anlama geldiklerini incelememiz yeterlidir. Overflow Flag (OF) (Taşma Bayrağı): İşaret taşması olduğunda olduğunda set (1) olur, mesela 1 byte lık işaretli sayılarda 100+50=150 dir, fakat sonuç -128...127 aralığında değildir. Direction Flag (DF) (Yön bayrağı): String işlemlerini yapan komutlar için kullanılır. 1 olduğunda index kaydedicisi otomatik olarak azalır. 0 ise index kaydedicisi otomatik olarak artar. (mesela bir string hafızanın bir bölgesinden başka bir bölgeye kopyalanırken işlemin stringin sonundanmı yoksa başındanmı yapılacağını D flag ile belirleyebilirsiniz) Interrupt-enable Flag (IF) (Kesme Yetki bayrağı): 1 olduğunda maskelenebilir kesmeler kullanılabilir. (kesmeler hakkında ayrıntı sonraki makalelerimizde!) Single-step Flag or Trace (TF) (Tek Adım veya İzleme Bayrağı): Genelde debugger programları için kullanılır, 1 olduğunda bir anda sadece 1 komut işlenir. Sign Flag (SF) (İşaret Bayrağı): MSB biti (en soldaki bit) 1 olduğunda bu bayrakta 1 olur. Zero Flag (ZF) (Sıfır Bayrağı): Sonuç sıfır olduğunda 1 olur. Auxiliary carry Flag (AF) (Ara Elde Bayrağı): AL kaydedicisinin 0-3 arasındaki bitlerinde yapılan işlemde elde veya borç üretilirse 1 olur. Parity Flag (PF) (Eşlik Bayrağı): 1 olduğunda sonucun düşük değerlikli baytındaki (mesela 127Fh gibi word boyutunda bir sonuç üretilmişse bunun 7F kısmı için) 1 lerin sayısının çift olduğunu gösterir. Carry Flag (CF) (Taşıma Bayrağı): 1 olduğunda elde üretildiğini veya borç alındığını belirtir. Bayrak Kaydedicisini özetleyecek olursak, 1- İşlemcinin yaptığı işlemlerin özel durumlarını gösterirler 2- Bazı işlemler yapılmadan önce bazı bayrakların 1 ya da 0 yapılması gerekebilir 3- Programcılar bazı işlemleri anlayabilmeleri için bazen birden fazla bayrağın birlikte ürettikleri durumları göz önüne almak zorundadırlar. Uzun bir süre bu bayraklardan OF, SF, ZF, CF olanları bizim için önemli olacak. Şimdi bayrak kaydedicisi ve bayraklarla ilgili kafanızda daha belirgin bir şeklin oluşabilmesi için ilk örneğe bakalım. Şimdi yapacağımız programda kullanıcı klavyeden 2 adet değer girecek ve bu değerler eşitse ekrana "Girilen Degerler Esittir" yazacak değilse "Girilen Degerler Esit Degildir" yazacak. İlk bakışta kullanıcı sayıları bilmiyor mu kardeşim ne gerek var böyle bir programa diyebilirsiniz :) Fakat değerlerin klavyeden değil de paralel porttan alındığını ve bu port un bir klimaya bağlı olduğunu ve hava sıcaklığının 24C 0 ye eşit olup olmadığını test eden ve eşitse klimayı durduran değilse çalıştıran bir programında bundan pek farklı
olmayacağını tahmin edebilirsiniz. Bu örnekte henüz işlemediğimiz konulardan olan "ekran ve klavye işlemleri" ve "şartlı dallanmalar" konularını kapsayan komutlar kullanılacak. MOV AH,01 INT 21h JMP HedefAdres JE HedefAdres CMP BL, AL Bu komutlardan sonra program kullanıcı giriş yapana kadar durur ve kullanıcı klavyeden değer girdiğinde o değerin ASCII kod karşılığı AL kaydedicisine yerleştirilir. Bu komut ile hedef adrese dallanma gerçekleştirilir. Bu komut ile hedef adrese Z bayrağı 1 ise dallanma gerçekleştirilir. Bu komut BL ile AL kaydedicisini karşılaştırır. Kaynak ve Hedef operand değerleri birbirine eşit ise Z bayrağı set (1) edilir. TITLE Eşitmi (esitmi.asm) ;######################################## ;# Klavyeden Girilen Değerler Eşitmi # ;# Son Güncelleme: 08/06/05 # ;# Yazan --> Eren ERENER # ;########################################.MODEL SMALL.STACK 32.DATA Esittir DB 0Ah,0Dh,"Girilen Degerler Esittir$" ;Esittir adında her bir elemanı bellekte 1 byte yer kaplayan başlangıç değeri Girilen Degerler Esittir olan değişken tanımlanıyor. EsitDegil DB 0Ah,0Dh,"Girilen Degerler Esit Degildir$" AltSatir DB 0Ah,0Dh,24h.CODE ANA PROC MOV AX, @DATA MOV DS, AX ;Data segment ;ayarlanıyor. MOV AH, 01 ;Klavyeden 1. karakter, INT 21h ;giriliyor. MOV BL,AL ;Bu karakter BL'de saklanıyor. MOV DX, offset AltSatir ;Bir alt, MOV AH,9 ;satıra, INT 21h ;geç. MOV AH, 01 ;Klavyeden 2. karakter, INT 21h ;giriliyor.
CMP BL,AL ;1. ve 2. karakterler karşılaştırılıyor. JE ESIT ;Bu karakterler eşitse ESIT yazan yere dallanılacak. MOV DX, offset EsitDegil ;Aksi halde, MOV AH,9 ;bu, INT 21h ;komutlar, JMP BITIR ;işlenecek. ESIT: MOV DX, offset Esittir ;1. ve 2. karakterler, MOV AH,9 ;eşitse, INT 21h ;işlenecek olan komutlar. BITIR: MOV AH,4Ch ;DOS'a INT 21h ;dönüş ANA ENDP END ANA
Şekil 4 - Klavyeden girilen değerleri test eden program. Yukarıdaki programı TASM ile derleyin. Bu programı çalıştırdığınızda sizden iki adet değer girmenizi isteyecek, değerleri girerken enter tuşuna basmanıza gerek yok. Şayet girilen değerler eşitse ekranda "Girilen Degerler Esittir" değilse "Girilen Degerler Esit Degildir" mesajını göreceksiniz.
Şekil 5 - Programın ekran çıktısı. Değerlerin eşit olup olmama durumunu belirleyen işlemci durum kaydedicisinin Z biti (Z bayrağı) dir. Bu bayrağın durumu karşılaştırma komutundan sonra girilen değerlere göre 1 yada 0 olur. Şayet girilen değerler eşit ise, eşit olan bu değerler karşılaştırılır ve sonuç sıfır elde edilir, bu durumda Z=1 olur. JE komutu ise ancak Z=1 olduğunda, operandında belirtilen adrese dallanma işlemini geçekleştirir. Aksi durumda işlemci klasik yolunu izler ve bir sonraki komutu işler.
Şekil 6 - Z=1 olduğunda 0024h offset adresine dallanılacak. Klavyeden farklı değerler girildiğinde ise, karşılaştırma işleminin sonucunda z=0 olur. Bu durumda dallanma gerçekleşmez ve işlemci normal seyrini sürdürerek bir sonraki komutu işler.
Şekil 7 - Z=0 olduğunda dallanma gerçekleşmeyecek, program sonraki komutu takip edecektir. Bayrakların durumları bayrakları etkileyen bir komut işlendiğinde değişir, her komut bayrakları etkilemez. Burada CMP komutu 6 adet bayrağı etkiliyor. Hangi komutun hangi bayrakları etkilediğine Intel in komut setinden bakabilirsiniz. Komut setinden CMP komutunu bulursanız bu 6 bayrağın neler olduğunu görebilirsiniz.
Şekil 8 - CMP ile AF, CF, OF, PF, SF, ZF bayrakları etkilenir. İsterseniz Şekil7 deki etkilenen tüm bayrakları ve neden dolayı 1 ya da 0 olduklarını inceleyelim. Burada yapılan işlem "CMP BL, AL" dir ve BL=33, AL=34 dür. BL-AL yani 33h-34h işlemini yapan CMP komutu aşağıdaki nedenlerden dolayı bu bayrakları etkilemiştir. 1-33-34 işlemi için borç gerektiğinden (Küçük sayı büyük sayıdan çıkartılıyor) CF=1, 2- Bu işlemin sonucu sıfır olmadığından ZF=0, 3- İşlemin sonucu negatif olduğundan SF=1, 4- İşlem sonucunda bir işaret taşması olmadığından OF=0, 5- Sonuçta elde edilen -1 yani FFh, başka bir deyişle 11111111b değerindeki 1 lerin sayısı sekiz adet yani çift olduğundan PF=1, 6- Binary olarak çıkartma işleminde daha 2. bitte 0 dan 1 çıkarılırken borç alındığından AF=1 olmuştur. Bizim programımızda sadece Z bayrağının durumu önemli olduğundan bu bayrağın durumuna göre programın akışını kontrol eden JE komutunu kullandık. Aslında burada JE komutu ile ilerleyen makalelerde ele alacağımız if-else mantığını oluşturduk. Gördüğünüz gibi program akış kontrolünde bayraklar hayati önem taşır. İşlemci durum kaydedicisinde bulunan bu bayraklar şu anda aklınızın alamayacağı bir çok işlemde bizlere yardımcı olacak. Bu konuyu daha iyi anlamak için Turbo Debugger da programın her komutunu adım-adım çalıştırın ve komut setinden faydalanarak, çalıştırdığınız komutun etkilediği bayrak varsa bunun nedenini araştırın. Kaynak Csharpnedir.com