Bununla beraber ":" karakterinide saat bilgisini yazdırırken saat ile dakika arasına yerleştirelim, tabii ki bu bilgide data segmentinde bir adreste saklı kalsın. Ayirac DB ':' Birde saat kelimesini data segmentte tanımlayalım, Saat DB 'Saat $' Şimdi kabataslak ne yapacağım belli oldu, programımın algoritmasını da yazdıktan sonra kodlarımı yazmaya başlayabilirim. 1- Ekrana "Saat" yazdır 2- Sistem saatini ilgili kaydedicilere yükle 3- Tablodan saat bilgisinin desimal karşılığını bul 4- Ekrana yazdır 5- : karakterini ekrana yazdır 6- Sistem dakikasının karşılığını bul 7- Ekrana yazdır 8- Dur Şekil 1 - Saat bilgisini ekranda gösteren program. Şekil 2 - Programın ekran çıktısı. Elbette bu program daha kısa veya pratik bir şekilde yapılabilirdi. Bunun için komut bilgimizi genişletmeliyiz. Ayrıca değişken bildirimlerini de şu ana kadar tam olarak anlatmadım. Bu konularla ilgili makalelerimiz yolda fakat şu anda sizlerin mevcut bilgilerinizle bu programa birde salise kısmını ekleyebilmeniz gerekir. Bu programa ait assembly kaynak kodlarını buradan, kodların derlenmiş halini buradan download edebilirsiniz. Bir sonraki makalede görüşmek üzere. Alıntı...

80x86 KOMUT SETİ (Bölüm 1) Kategorisi : Assembly ve X86 Programlama [FONT=Verdana&

XLAT Komutu 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 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F Bu tablonun 11. elemanını AL'ye yüklemek istersek; MOV AL, 0Ah ; İndeks değeri (0 dan 0Ah'a kadar 11 tane değer var) LEA BX, Tablo ; BX'e Tablonun ofset adresi yükleniyor XLAT ; Tablonun 11. elemanına erişilip ASCII karakterin hex karşılığı AL'ye yükleniyor (AL=41h) Şimdi yukarıda açıkladığımız komutlardan birkaçını kullanarak bir program yazalım ve daha sonra da inceleyelim. Öncelikle Push ve Pop komutlarını DS kaydedicisinin değerini belirlemek için kullanabilirim. . exe tipi programlarda DS kaydedicisinin değerini belirlemek için bu güne kadar hep aşağıdaki iki satırı kullandık, MOV AX, @data MOV DS, AX Yığın komutlarınıda veri taşımak için kullanabileceğimizden, MOV AX, @DATA PUSH AX POP DS Yukarıdaki üç satırda DATA segmentin adresini önce AX kaydedicisine yükledik, sonra bu kaydedicideki değeri yığına ittik son olarakta yığından bu değeri çekip DS kaydedicisine yükledik. Kullandığım sistemin saat bilgisini ekrana yazdırmak istiyorum. Tabii ki bu gibi işlemler için hali hazırda DOS'un kesmeleri mevcuttur. Fonksiyon 2Ch sistem zamanı ile ilgili bilgileri işlemcinin kaydedicilerine getirir. MOV AH,2Ch INT 21h Bu komutlar işlenince CX ve DX kaydedicileri saat bilgisi ile yüklenirler, CH = saat CL = dakika DH = saniye DL = salise Kaydedicilerin içindeki değerlerin binary olduğunu ve debugger programlarında bu değerlerin hexadecimal formatta göründüğünü unutmayalım. Biz bu programda kullanıcı ekranına desimal formatta değerler yazdıracağımızdan ya çevirme işlemi yapacağız yada tablo oluşturup hex bilgilerin karşılığına tablodan bakacağız. Çevirme işlemleri için çarpma, bölme gibi komutları bilmeniz gerekir ama henüz o komutlarla ilgili örnekler çözmedik. Bunun yerine biz hex-decimal tablolar oluşturup bu tablolardan desimal değerleri bulalım ve ekrana yazdıralım. Fakat tabloların sınırlarını belirlemek için elimizdeki bilgiyi iyi tanımamız gerekir. En büyük saat bilgisi 23:59 yani 0 ile 59 elemanlarını kapsayan bir tablo oluşturmam yeterli. SaatTablo DB "00$","01$","02$","03$","04$","05$","06$","07$","08$","09$" DB "10$","11$","12$","13$","14$","15$","16$","17$","18$","19$" DB "20$","21$","22$","23$","24$","25$","26$","27$","28$","29$" DB "30$","31$","32$","33$","34$","35$","36$","37$","38$","39$" DB "40$","41$","42$","43$","44$","45$","46$","47$","48$","49$" DB "50$","51$","52$","53$","54$","55$","56$","57$","58$","59$" Yukarıdaki tablo bize 60 elamanlı gibi görünebilir fakat hafızada 60 3=180 byte yer kaplar, çünkü x86 hafızası byte adreslenebilir ve her rakam veya karakter hafızada 1 byte'lık yer kaplar. Diyelim ki CH'taki saat bilgisi 0Ah, bu saat 10 demek. Tabloda "10$" olan kısım acaba tablonun hangi adresidir? Sayacak olursanız 30. elemanın 1 31. elemanın 0 yani 30 elemandan itibaren 10 değerinin mevcut olduğunu görürsünüz. Tabloda her saat değeri için 3 byte'lık değer ayrılmıştır ve bu programda CH veya CL deki değerler yardımıyla indeks adresi hesaplanırken 3 ile çarpmak gerekir. CH'ta saat 10'u temsilen 0Ah değeri bulunuyorsa 0Ah 3 = 1Eh = 30 hesabı yapılmalıdır. Tüm bu işlemleri yapıp ekrana yazdırılıcak değeride DX kaydedicisine yüklemeliyiz. Çünkü ekranda bir karakter dizisini yazdırmak için INT 21h'ın 9. fonksiyonu karakter dizisinin başladığı adresi DX kaydedicisinde bulunmasını ister. Tüm bu işlemleri yapan kod satırları aşağıdaki gibidir. MOV AL, CH ;Şimdi saat bilgisi, MUL Uc ;3 ile çarpılarak tablodaki desimal karşılığı bulundu. MOV DI, AX ;Bu adres indeks olarak düşünüldüğünden DI ya yüklendi. LEA DX, SaatTablo ;Ve BX'e saat bilgisinin desimal karşılığı olan tablo konumu yüklendi. MUL komutunu önümüzdeki makalelerde inceleyeceğiz, çok fazla kullanım formatı mevcuttur. Bizim için burada, sadece CH veya CL deki değerleri 3 ile çarpsın yeter. Fakat MUL komutu sadece AL deki değeri bir değişken yada AL kaydedicisi ile çarpabildiğinden önce CH yada CL deki bilgileri AL'ye taşımamız gerekiyor. Daha sonra 3 değeri ile çarpıp tablodan adresi buluyoruz. Tabii ki Uc bir değişken ve data segmentinde tanımlı olmalıdır. Uc DB 3 Al deki değer 3 ile çarpıldığında sonuç 16 bitlik AX kaydedicisinde saklanır. Bu noktadan sonra bu adresi DX kaydedisine yükleyip ekranda string yazdırma kesmesini çağırırsak işlem tamam olacaktı ama, SaatTablo taban adresine göre hesapladığımız indeks adresi halen AX kaydedicisinin içinde ve LEA DX, SaatTablo gibi bir komut satırı kullanamam çünkü böyle bir adresleme modu mevcut değil. Indeks olarak kullanılacak adresler 8086 assembly dilinde SI, DI veya BX kaydedicilerinin birinde olmalıdır, bu yüzden bizde hesapladığımız indeks değerini DI ya yükledik. Son olarak ekranda bir string yazdıracağımızdan DX kaydedicisinin içine yazdıracağımız stringin başlangıç adresini atarak ve daha önce de kullandığımız INT 21h fonksiyon 9h ı kullanabiliriz.

devam... MOV komutunu kullanırken yapamayacağınız iki şey vardır, bunlardan birincisi "mem, mem" tipinde bir kullanımdır. Yani hafızanın bir konumunda diğer bir konumuna doğrudan taşıma yapamazsınız. Bu işlemi yapmak için taşınacak veri önce mikroişlemci kaydedicilerinden birine getirilmelidir. MOV sayi1, sayi2 ;yanlış kullanım Yukarıdaki gibi bir komut satırı yazarsanız, derleyiciniz hata mesajı verir. Böyle bir işlemi yapmak için genel amaçlı bir kaydediciyi kullanmanız gerekir. MOV AX, sayi1 MOV sayi2, AX ;sayi1 ve sayi2 değişkenlerinin word türünden olduğunu varsayıyoruz. MOV komutu ile yapamayacağınız ikinci şey ise segment kaydedicilerine doğrudan bir veri taşımaktır. Yani acil adresleme modunu segment kaydedicilerine uygulayamazsınız. MOV DS, 1525h ; bu kullanım hatalıdır. Segment kaydedicilerine bir değer yükleyebilmek için genellikle genel amaçlı kaydedicileri kullanılır. Ayrıca segment kaydedicilerine ancak 16 bitlik boyutunda değerler yüklenebileceğinden genel amaçlı kaydedicilerin 8 bitlik kısımlar değil 16 bitlik kısımları kullanılabilir. MOV AX, 1525h MOV DS, AX Bunların dışında operandların boyutları eşit olmak zorundadır. MOV AX, toplam ; burada toplam değişkeninin boyutu kesinlikle word tipinde yani iki byte uzunluğunda olmalıdır. Şayet acil adresleme kullanarak bir veri taşıyorsanız işlemci operandın boyutunu kaydediciye uyarlar. MOV AX, 15h ; Bu komut işlenince AX'in içinde 0015h değerini görürsünüz. Dikkat edilemesi gereken diğer bir husus ise hafıza operandlarıdır. Örneğin MOV , 5 gibi bir komut ile hafızaya neyin yükleneceği belli değildir MOV , 5 gibi bir komutla acaba hafızaya byte boyutunda bir 5 değerimi (05) yoksa word boyutunda bir 5 değerimi (0005) yüklenecek? Bunu kodlarınızda belirtmeniz gerekir. Derleyici bu komut satırına hata verir. Doğru kullanım aşağıdaki gibi olmalıdır. mov byte ptr , 5 mov word ptr , 5 mov dword ptr , 5 80386 ve sonrası işlemcilerde kullanılabilir mov byte ptr , 5 satırını açıklayalım. Burada BX=0000 olduğunu varsayalım, böyle bir durumda ds:0000 adresine bir taşıma işlemi gerçekleşeceltir. Ama bu adresten itibaren 05'mi yoksa 0005'mi yoksa 00000005'mi taşınacaktır? İşte bunu ptr operatörü belirler. mov byte ptr , 5 komut satırı için ds:0000 adresine 1 byte'lık bir veri yani 05 taşınır. Şayet operatör byte ptr değilde word ptr olsaydı o zaman ds:0000 ve ds:0001 adreslerine dırasıyla 05 ve 00 değerleri taşınacaktı.

Sayın Arkadaşım Move komutu altında RAM ve ROM olabilir demişsiniz 1-RAM=Random Access MEMORY yanlızca kullanıcının çalışabileceği kısm. 2-ROM=Read only Memıry:Buradan yazılım komutlarını alırsınız. Buraya kullanıcı dokunamaz. 3=İkisi birbirinden çok ama çok farklı şeyler.

devam... XCHG komutu xchg komutu operandlarındaki değerleri yer değiştirir. 80x86 ailesi için dört değişik kullanım şekli vardır; xchg reg, mem xchg reg, reg xchg ax, reg16 xchg eax, reg32 80386 ve sonrası işlemcilerde kullanılabilir LDS, LES, LFS, LGS, ve LSS komutları Bu komutlar 32 bitlik bir hafıza bölgesindeki değeri bir segment kaydedicisine ve bir genel amaçlı kaydediciye bir defada yükler. Kullanım formatı aşağıdaki gibidir; LxS hedef, kaynak Bu komutları aşağıdaki gibi kullanabilirsiniz; lds reg16, mem32 les reg16, mem32 lfs reg16, mem32 lgs reg16, mem32 lss reg16, mem32 80386 ve sonrası işlemcilerde kullanılabilir Reg16 genel amaçlı herhangi bir kaydedici olabilir mem32 ise double word boyutunda bir veri olmalıdır, bunu "dd" direktifi ile oluşturabilirsiniz. Daha önce bu komutlardan biri olan LES komutu için "X86 Assembly Dilinde Değişken Bildirimi -1" adlı makalede çok güzel bir örnek vermiştim. Erişmek istediğimiz adresin segment ve ofset bölümlerini birleştirerek bir değişken oluşturuyor sonrada bunu program çalışırken istediğimiz gibi kullanıyorduk. LEA Komutu LEA sadece offset adreslerini hedef operandına yükleyen bir pointer gibi düşünebilirsiniz. Genel kullanım formatı lea dest, source şeklindedir. lea reg16, mem lea reg32, mem 80386 ve sonrası işlemcilerde kullanılabilir. MOV komutunu da LEA komutu yerine kullanabilirsiniz, fakat hangi komutu kullanacağınıza adresleme moduna göre seçmelisiniz. Bazen MOV komutu LEA dan daha hızlı çalışabilir, tüm bu bilgilere herhangi bir intel komut setinden faydalanarak bakabilirsiniz. Daha önceki makalelerimizde ekrana bir karakter dizisini yazdırmıştık, bunun için kaynak kodumuzda ekrana yazdılıralacak olan veriyi aşağıdaki gibi tanımlamıştık; Dizi DB "Merhaba Assembly",0Ah,0Dh,24h Daha sonra bu dizinin adresini DX kaydedicisine yüklememiz gerektiğinde aşağıdaki komutu kullanmıştık; MOV DX, OFFSET Dizi Bu komutun yaptığı işi LEA kullanarakta yapabiliriz; LEA DX, Dizi

Teşeküürler. eline ve emeğine sağlık. saygılarımla.

PUSH ve POP komutları 80x86 push ve pop komutları Stack Memory ile ilgili işlemlerde kullanılır. Yğın hafıza bölgesini sizler. exe tipi program hazırlarken. Stack direktifi ile belirliyorsunuz. İşte bu bölge genellikle programdaki dallanma veya altrutinlerin çalışması sırasında, dönüş adreslerinin ve bayrak kaydedicisinin durumlarını saklamak için kullanılır. Push komutu bu yığın olarak adlandırılan hafıza bölgesine verileri iterken, pop komutuda bu bölgeden veri almada kullanılır. push reg16 pop reg16 push reg32 pop reg32 push segreg pop segreg push memory pop memory push immediate_data pusha popa pushad popad pushf popf pushfd popfd enter imm, imm leave 80286 ve sonrası işlemcilerde kullanılabilir 80386 ve sonrası işlemcilerde kullanılabilir Push ve pop komutları kullanıldığında yığın hafıza bölgesinin işaretçisi olan SP kaydedicisi değişir. Tabii ki bu yığına itilen veya yığından çekilen değerin boyutuna bağlıdır. Bu komutlar 2 veya 4 byte�lık değerler ile işlem yaptığından yığına 2 bytelık bir değer itildiğinde SP�nin değeri 2 byte azalır. Şayet yığına 4 byte�lık değer itilirse SP�nin değeri 4 azalır. Unutulmaması gereken önemli bir hususta yığın hafıza bölgesine itilen en son değerin çekilecek olan ilk değer olmasıdır. Tabii ki yığına birden fazla word ya da doubleword itildiyse aralardaki değerler ile işlem yapmak adresleme modlarıyla mümkündür fakat bu SP�de herhangibir değişiklik yapmaz. Yığın hafıza bölgesi ile ilgili unutulmaması gereken üç önemli kural vardır. Segment kaydedicilerinden olan SS yığın hafıza bölgesinin segment adresini gösterir. Yığına bir şeyler itildikçe SP azalır eökildikçe artar. SS:SP her zaman yığının tepesi olarak tabir edilen noktayı gösterir. LAHF ve SAHF Komutları Bu komutlar bayrakları AH kaydedicisine yükler veya AH'a yüklenen bayrak kaydedicilerinin durumlarını kayar nokta kaydedicisine yükler. Bu komutlar 8086 zamanından kalma ve günümüzdeki modern assembly programlarında pek kullanılmayan komutlardır. Genişletme İşlemleri Bazen byte boyutundaki bir değeri word boyutuna veya word boyutundaki bir değeri doubleword boyutuna genişletmek gerekebilir. Bu gibi durunlarda aşağıdaki komutlar kullanılır. movzx hedef, kaynak ;Hedef kaynağın iki katı büyüklüğünde olmalıdır. movsx hedef, kaynak ;Hedef kaynağın iki katı büyüklüğünde olmalıdır. cbw cwd cwde cdq bswap reg32 xlat MOVZX, MOVSX, CBW, CWD, CWDE, ve CDQ Komutları cbw AL kaydedicisinin 1 byte'lık içeriğini AX'e genişletir. Şayet AL'deki değer pozitifse AH'ın tüm bitleri '0' değerini alır. AL'deki değer negatifse AH'ın tüm bitleri '1' olur. cbw cwd komutu AX'in değerini DX:AX'e genişletir. CBW komutundaki kurallar bu komut içinde geçerlidir. cwd Bu komut 80386 ve sonrası işlemcilere özeldir. CWD komutunda olduğu gibi word boyutundaki bir değeri double word boyutuna genişletmede kullanılır. CWD AX'i DX:AX'e genişletirken bu komut AX'i EAX'e genişletir. cwde cdq komutu EAX kaydedicisindeki 32 bit'lik değeri EDX:EAX 'e genişletir. Bu komutda 80386 ve sonrası işlemlerde kullanılır. cdq Örnekler: ; AL' deki 8 bitlik değeri 32 bitlik dx:ax'e genişletmek için cbw cwd ; AL' deki 8 bitlik değeri 32 bitlik eax'e genişletmek için cbw cwde ; AL' deki 8 bitlik değeri 64 bitlik edx:eax'e genişletmek için cbw cwde cdq movsx komutuda yukarıdaki komutlara benzer iş yapar, kullanım formatları aşağıdaki gibidir. movsx reg16, mem8 movsx reg16, reg8 movsx reg32, mem8 movsx reg32, reg8 movsx reg32, mem16 movsx reg32, reg16 Örnekler: movsx ax, al ;CBW komutunun yaptığı işi yapar. movsx eax, ax ;CWDE komutunun yaptığı işi yapar. movsx eax, al ;CBW ve CWDE komutlarının birlikte yaptığı işi yapar. movzx komutu movsx komutu gibi kullanılır fakat negatif değerleri genişletmek için kullanılmaz, çünkü movzx komutu genişletme işleminde sadece bitleri '0' yapabilir. Bu komutun sonundaki zx harfleri İngilizcede zero extend yani sıfır ile genişlet gibi bir anlam taşır. Tüm bu genişletme komutları genellikle aritmetik işlemlerde ve özellikle bölme komutlarında kullanılır. BSWAP Komutu Bildiğiniz gibi x86 hafızası little endian yapıya sahiptir, bununla beraber big endian hafızaya sahip bilgisayarlarda çok sayıda mevcuttur. Örneğin Apple'ın Machintosh bilgisayarları big andian hafıza yapısına sahiptir. BSWAP komutu bu farklı hafıza sistemlerine sahip olan bilgisayarlar arasında veri haberleşmesi yapılması için kullanılır. BSWAP operandında belirtilen 32 bitlik kaydedicinin içindeki değeri byte-byte ters çevirir. Düşük değerlikli sekiz biri en yüksek değerlikli bölgeye, 8-15 arasındak bitleri 16-23 arasına, 16-23 arasındaki bitleri 8-15 arasına ve son olarak 24-31 arasındaki bitleride 0-7 arasına yerleştirir. BU komut sadece 80486 ve sonrası işlemcilerde kullanılabilir. Kullanım formatı aşağıdaki gibidir. bswap reg32