Assembly
 

Assembly Nedir?

Assembly aslında öğrenilmesi zor bir dildir. Uzun zaman ve büyük emek ister. Fakat iyi bir şekilde anlatıldığında hiçte zor değil. Ama baştan şunu belirteyim: Diğer programlama dillerinde yaptıklarınızı assembly altında daha çok satır kod yazarak ve daha uzun bir zamanda elde edebilirsiniz. Buna karşın diğer dillere göre birçok üstünlüğü bulunmaktadır. Lütfen bu sayfayı sonuna  kadar okumaya devam edin.

Bir programlama dili öğrenecek insan öncelikle kolay bir dili öğrenmeli (Bu genelde Basic'dir.) Bu dil vasıtası ile programlama ve algoritma geliştirme (problemlere karşın geliştirilen hatasız ve tatmin edici çözümler diyebiliriz) hakkında bilgi ve deneyim sahibi olmalı. Son olarak ise diğer dilleri tanımalı ve seçtiği dili öğrenmeli, uygulamalar geliştirmelidir.

0C85:0100 B402 MOV AH,02
0C85:0102 B203 MOV DL,03
0C85:0104 CD21 INT 21
0C85:0106 CD20 INT 20

Yukarıda ayrıntılı çıktısı alınmış bir kod parçası bulunuyor. (Daha sonra ne işe yaradığını anlatacağım) Gri renkteki bölümler bellek lokasyonlarıdır. Yani ilgili kodların şu anda hangi bellek adresinde bulunduğunu gösteriyor bize. Kırmızı renkteki yazılar assembly kodlarıdır. Koyu yeşil kısımlar ise bu assembly kodlarının makina diline çevrilmiş halidir. Bizi ilgilendiren kırmızı bölümler olacak. Biliyorum o yazılardan hiçbir şey anlamadınız, bu çok normal...

Tam olarak anlamanız için uzun bir örnek olacak ama başlıyoruz...

Şu anda bilgisayarınızın başında oturuyor, windows işletim sisteminiz altında bazı programları kullanıyorsunuz. Muhtemelen Internet Explorer bunlardan biri. Kullandığınız her program (*.exe *.dll *.ocx ...) hangi programlama dili ile yazılırsa yazılsın hepside derlenmiş ve ilgili dosyalar haline dönüştürülmüştürler. Bir program Delphi ile yazılmış ve derlenmiş (exe uzantılı dosya haline getirilmiş) ise  tersi işlem yapılması, yani programın kodlarına geri döndürülmesi imkansızdır. Derlenmiş program artık işletim sistemimizin ve makinamızın işlemcisinin anlayabileceği bir haldedir. Yani bizim makina dili dediğimiz haldedir. Tamamen iç yapısı elektronik devre elemanlardan oluşan işlemcimiz bu makina dili halindeki kodları çalıştırabilir. Başka hiçbir şeyden anlamaz. Bilgisayarlara bu nedenle de aptal makina denmektedir. Çünkü biz ne emredersek onu işlerler. Makina dili 16 lık sayı sistemindeki değerlerden oluşur. Bu kodlar sadece sayı kümelerinden oluştukları için bir insanın bunları anlaması hele hele bu sayılar üzerinden programlarını yazması nerdeyse imkansızdır...

Fakat insanların karmaşık sayılar yerine kendi konuşma dillerindeki kelimelerden oluşan bir komut kütüphanesi ile çalışmaları daha kolaydır. Bunu Basic dilinde rahatlıkla görebilirsiniz. Çünkü Basic dilindeki komutlar ingilizce bilen bir insan için öğrenme ve akılda kalma açısından çok kolay bir dildir. Makina dilindeki sayılar yani kodlar yani programımız bizim için önemlidir. Çünkü bunlar işlemcimiz üretilirken geliştirilmiş bir komut setine hitap ederler. Bir sonraki işlemci teknolojisine geçildiğinde komut seti genişletilir ve yeni komutlar eklenir. Assembly dili bu komut setindeki makina dili kodlarının insanların biraz daha kolay anlayabileceği harfler üzerinden gösterimidir. Bu tamamen semboliktir ve her assembly komutu yazıldığında birebir makina diline çevrilirler.

 

Peki Assembly bize ne sağladı? :

-Bize işlemcimiz üzerindeki her komuta aracısız erişim sağladı. Yani kod yazarken muhatabınız işlemci ve üzerinde çalışmakta olduğunuz işletim sistemidir. (Bu genelde Windows'tur ama başka bir sistemde olabilir.) Bu size oluşturacağınız programın kodlanması esnasında çok yüksek bir kod hakimiyeti sağlar. Böylece diğer dillerde aynı işi yapsanız dahi sizin assembly ile işlemci üzerindeki hakimiyetiniz en üst düzeydedir ve bunu sizin kadar temiz yapabilecek bir programlama dili ve/veya derleyici yoktur.

-Yazdığınız her satır kod 1 ila 4 byte civarında olup çok az yer kaplar. Diğer dillerde yazılan kodlar aynı işi yapsa dahi sizinle aranızda derleyici bulunmaktadır ve programın işleyişini sağlama almak amacıyla komutlar arasında ek bilgiler girilmekte ve programınızın boyutu büyümektedir. Ayrıca diğer dillerdeki komutlar işlemci üzerindeki komutlarla uyuşmaz ise derleyici bunu dolambaçlı yollardan işlemciye anlatır ve yazılan bir tek komut makina dilinde yüzlerce komuta karşılık gelebilir. Herhangi bir dildeki basit komut assembly ile yazılsa 2 ila  6 kat daha az yer kaplar.

-Yazdığınız programlar (eğer bir algoritma hatası yok ise) diğer dillerdeki şekillerine göre her zaman daha az yer kaplar. Programın az yer kaplaması çalışırken daha az bellek tüketmesi; işlemler daha az kod ile anlatıldığından daha kısa sürede işlemin bitmesi, yani programımız daha hızlı çalışması anlamına gelir.

Avantajları :

- İşlemcinizin gücünü en iyi şekilde ortaya koyabilecek tek programlama dilidir.
- Cok az yer kapladığı için bilgisayar virüslerin yazımında kullanılırlar.
- Çok hızlı çalıştıkları için işletim sistemlerinde kernel ve donanım sürücülerinin programlanmasında, hız gerektiren kritik uygulamalarda kullanılmaktadır.
- Yapısı itibariyle üç boyutlu ekran kartlarında ilgili çizim ve efektlerin işlenmesi amacıyla hem oyunlar hem de programlar içinde ekran kartına hitap eden makina dili kodları kullanılmaktadır.
- Herhangi bir amaç doğrultusunda (genelde programların daha hızlı çalışması için optimize edilirken), herhangi bir programlama dili altında, o dilin kodları arasında kullanılabilir. Yani diğer dillerle beraber de kullanabilirsiniz. Sizin uzman olduğunuz dil belki Visual Basic dir. Ama işin içinden çıkamadınız, assembly bilen bir arkadaşınız sizin için birkaç kod ekledi ve tamam.
- İyi öğrenildiğinde diğer dillerde karşılaşılan büyük problemlerin assembly ile basit çözümleri olduğu görülür. Yani başka programlama dillerde çalışan insanların bu iş imkansız dediği yerde assembly devreye girer. Bu üstünlük daima var olacaktır...
- Yazdığınız programın kaynak kodlarını tüm dünya ile paylaşsanız dahi kodlar o kadar karmaşıktır ki insanlar assembly bilse dahi ne işe yaradığını çözmeleri çok uzun zamanlarını alır. (Açıklayıcı bilgiler koymadı iseniz) Assembly bilmeyen bir insanın kaynak kodu anlaması imkansızdır.
- Her program derlendiğinde makina diline dönüşür. Bunlar assembly kodlarına dönüştürülebilirler. (Buna disassembly olayı denir, bunu yapan programlar ise disassembler'lar dır.) Bu sayede assembly bilen bir insan zor da olsa diger programların çalışma şekillerini inceleyebilir ve üzerlerinde oynama yapabilir. Bu özellik de başka hiçbir dilde yoktur. Daha ileri gidip programların şifre isteyen bölümleri değişik yöntemlerle kırılabilir. Serial Generator türü programlar bu şekilde yapılabilmektedir.
- Zaman geçtikce ve insanlar üreten yerine tüketen bilgisayar kullanıcıları haline geldikçe bu dili bilmenin ayrıcalığı artmaktadır.

Dezavantajları :

- Dilin öğrenilmesi uzun zaman ve emek ister.
- Yazılan programlar diğer diller göre daha çok satırdan oluşur.
- Yazdığınız programın hangi satırlarının ne iş yaptığını bir süre sonra anlamayabilirsiniz. Çünkü anlaşılması zor ve karmaşık bir dildir. Bu nedenle kodların sonuna açıklama bölümleri eklemek iyi bir çözüm olacaktır.
- İşlemciye birebir bağımlıdır. Yani sonraki sayfalarda anlatılacak olan kodlar Intel ve uyumlu (AMD, Cyrix, Via) işlemcilerde geçerlidir. Örneğin Motorola işlemcileri kullanan Macintosh bilgisayarlarda hem komut setinin hem de işlemci mimarisindeki büyük farklılıklardan dolayı geçersizdir. Fakat onlarda da bir makina dili tabii ki bir assembly dili bulunmaktadır. Fakat komutlar daha farklıdır. Bizim ilgi alanımız intel 386 (i386) üstü ve bunlarla uyumlu işlemcilerdir.

Bilgisayarlar ve Mikroişlemciler

Biraz bilgisayar tarihine değinelim, daha sonra mikroişlemcilere geçeceğiz.

Aslında abaküs'e kadar dayanıyor. Öncelikle matematikteki daha sonra fen bilimlerinde ve teknolojideki gelişmeler ve yeni teknikler bizi şu anda bulunduğumuz noktaya getirdi. İlk hesaplayıcılardan sonra Pascal ve Babbage'in mekanik hesaplayıcıları gelmektedir. Elektronik devre elemanlarının geliştirilmesi ve tüplü tranzistörlerin sayesinde ilk elektronik hesaplayıcılar, yani modern bilgisayara giden yol açılmış oldu. 1936-1951 arasındaki bu donemde 1945 de ENIAC yapıldı. ENIAC ile işlemler soket bağlantılarının değiştirilmesi ile yaptırılıyor idi. 70bin direnç, 10bin kondansatör, 18bin tüplü tranzistör ve çalışırken harcanan 150-200 kilowatt enerjisi ile dev ama sadece dört işlem yeteneği, çok yavaş çalışması, kablolarla programlamanın zorluğu ve çok sık arıza yapmasıyla da hantal bir cihaz idi.

1948 yılında germanyum, 1954 de silisyum tranzistörler bulundu. Artık vakum tüpleri yerini yarı iletken tranzistörler aldı. Mikroelektronik geliştikçe tranzistörler küçüldü ve sonunda bunları tek bir devre parçasında birleştirme fikri gelişti. Artık entegre devreler yani çipler karşımızda. Harcanan daha az enerji, daha küçük devreler taşınabilir radyolar gibi ürünleri beraberinde getirdi.

İntelin ilk ticari amaçlı işlemcisi 1971 yılının 4004 çipi idi. Dört bitlik bir işlemci olup sadece hesap makinalarında kullanıldı. Bizi ilgilendiren 1978 yilinda üretilen 8086. Bu işlemci 8bitlik basit ve yavaş bir işlemcidir. Fakat şu andaki Pentium 4 mimarisi bile geriye uyumlu olarak bu işlemciyi desteklemektedir. Farkları hız, ek birçok komut ve yeni teknolojilerdir. Biz de bu 8086 ile başlayan x86 komut setini programlıyor olacağız. Aşagıda bir ilerleme yer almaktadır.

8086 -> 80186 -> 80286 -> 80386 -> 80486 -> Pentium -> Pentium Pro -> Pentium !! -> Pentium !!! -> Pentium 4

İntel işlemciler 80386 den itibaren bazı yenilikler getirdiği için ( 32bit ve multitasking işlemci ) artık bizim için temel teşkil etmektedir.
Pentium 4 3000C işlemcisi için biraz açıklama yapalım: Mimari:Pentium 4 İşlemci çekirdek hızı: 3000Mhz (yani bir saniyede 3 milyar komut işler) C: Veriyolunun 800Mhz olduğu anlamında. Klasik özellikler: 32bit işlemci, 64 bit veriyolu, 4Gbyte bellek adresleme, Hypertreading teknolojisi ile çift işlemci gibi çalışma ve gelişmiş mimari özellikleri ile tek döngüde iki işlem yapabilme özellikleri bulunuyor.

İşlemcimizin 32 bit olması ne demek? Öncelikle bit nedir? 1 byte bilgi tek karakterden oluşur. Yani notepad içine yazdığınız tek bir 'A' harfi 1 byte yer kaplar. 1 byte bilgisayarda 8 bit yani 8 tane '1' veya '0' dan oluşur.

binary 11111111 = decimal 255 eder.
binary 01000001 = decimal 65 eder. Bu değeri içinde saklayan bir 8bitlik saklayıcı 'A' harfini gösterir. Yani matematiksel değerlerinin yanında bu 8 bitlik değerler ASCII karakter setine göre belirli bir karaktere de denk gelmektedir.

İlk sayfada 0C85:0100 B402 MOV AH,02 şeklinde bir satır bulunuyor idi. Buradaki B402 16lık sayı sistemindeki 2 byte bilgidir. Ve bu makina dili komutu assembly olarak Mov ah,02 ye denktir. Bu komut ise ah registerine (işlemcideki 8bit saklayıcılardan biri) 02 hexadecimal değerini atar. İleride bit düzeyinde işlemler de yapacağımız için 16 lık 10 luk ve 2 lik sayı sistemlerinin birbirine dönüşümlerini bilmek zorundayız. Örnek bir binary-decimal dönüşümü aşağıdadır.

binary 01001110 için Decimal karşılığı=0*2^7+1*2^6+0*2^5+0*2^4+1*2^3+1*2^2+1*2^1+0*2^0=64+8+4+2=78 eder.

Daha önce de belirttiğimiz gibi işlemci sadece matematiksel değerlerden anlar. İster kod isterse data olsun esas olan bitlerdir. 32 bit işlemciler 1 döngüde 32 bit yani 4 byte bilgiyi işleyebilme özelliğine sahiptir. Çarpma, bölme, bellekten işlemciye veri alınması ve gönderilmesi gibi...

İşlemciler teknik özellikleri yanında iç frekansları ile de karşılaştırılmaktadır. Pentium 3 1000Mhz bir işlemci saniyede 1 milyar işlem yapar. Fakat işlemcide her komut aynı sürede tamamlanmaz. Bu komutun karmaşıklığı ile alakalıdır. İşlemci içinde bir tür sinyal üreten saat bulunur. Bu işlemcinin hızını tayin eder. İşlenecek olan komut saat darbesinin verilmesi ile işlem görmeye başlar ve bir sonraki saat darbesine kadar tamamlanmak zorundadır. İşlemcideki tüm komutlar için bu geçerlidir.

Register'lar

İşlemcimizin bize sundukları üzerinden program yazdığımız için işlemcimizi de tanımalıyız. Komut setini yani assembly komutlarını bir sonraki derste anlatmaya başlayacağız. Registerler işlemci çalışması sırasında değişik amaçlar için kullanılan değişkenlerdir. Bellekteki verilere ulaşmak belirli bir zaman gerektirir, fakat registerler işlemci çekirdeğindedir ve fazladan zaman harcanmadan istenen işleme göre içerikleri kullanılabilmektedir. Fakat sınırlı sayıda bulunurlar. Registerler genel amaçlı kullanılabilecekleri gibi bazıları sadece özel görevleri üstlenmektedir. Bunlar ileride anlatılacaktır. Şimdi i386 uyumlu bir işlemcinin registerlarına bakalım.


 

<--- 32 bit register --->

EAX
EBX
ECX
EDX
EBP
ESP
ESI
EDI


 

<--- 16 bit register --->

AH AL
BH BL
CH CL
DH DL
BP
SP
SI
DI


<--- 16 bit register --->

CS
DS
SS
ES
FS
GS
<--- 32 bit register --->
EIP
EFLAGS

 

 

 

 

 

Evet yukarıda işlemci registerlerinin çoğu görülmekte. İlk iki tablo aslında iç içe girmektedir, fakat bu şekilde daha iyi anlışılacağını düşündüm. Aşağıda ise bu registerlerin sınıflandırılmış halini görebilirsiniz.

Data Registers Pointer Registers Index Registers Segment Registers
EAX,EBX,ECX,EDX,AX,BX,CX,DX,AH,AL,BH,BL,CH,CL,DH,DL EBP,ESP,BP,SP ESI,EDI,SI,DI CS,DS,SS,ES,FS,GS

Daha önce söylediğim gibi yukarıdaki 32 bitlik işlemcimiz 8086 tabanlı olduğu için geriye uyumlu olarak 8086 nın programlarını da çalıştırabilmelidir. Fakat 8086 işlemcisi 8 bitlik bir işlemcidir. Bu nedenle gerçekte EAX şeklindeki bir register 32 bittir, fakat bu registerin ilk 16 bitlik bölümü AX şeklinde ifade edilir. Bu AX şeklindeki 16 bitlik bölüm ise kendi içinde ilk 8 bitlik bölüm AL, sonraki 8 bitlik bölüm AH olmak üzere yine alt bölümleri bulunur. Yani ben 4 byte bilgi depolamak istersem EAX,EBX gibi 32 bitlik; 2 byte bilgi depolamak istersem AX,BX gibi 16 bit; eger 1 byte bilgi depolamak istersem AH,AL,BH,BL registerlerini kullanabilirim. Sadece şunu unutmayın, AX,AH,AL registerleri sadece EAX registerinin alt bölümleridir. Başka yerde depolanmazlar. Gerekli olduğunda alt registerlere ulaşılabilir fakat gerçekte esas olan EAX dir. Pointer ve index registerlerinde de alt bölümler bulunur. Sınıflandırma tablosunu inceleyiniz. Kafa karıştırma olasılığına karşılık aşağıda registerleri boyutlarına göre de sınıflandırdım.
 

32bit registers (4 byte) 16bit registers (2 byte) 8bit registers (1 byte)
EAX,EBX,ECX,EDX,EBP,ESP,ESI,EDI,
EIP,EFLAGS
AX,BX,CX,DX,BP,SP,SI,DI,
CS,DS,SS,ES,FS,GS,IP,FLAGS
AH,AL,BH,BL,CH,CL,DH,DL

 

Şimdi yukarıdaki registerler ne işe yarar onları öğrenelim ? (Örnekleri bir sonraki Ders içeriğinde yer alacak.)

AX : Akümülatör Registeri, Dörtişlem operasyonlarında kullanılmaktadır.
BX : Base Registeri, Bellek lokasyonlarında baz adres göstericisi olarak kullanılır, yani bir tür index registeri gibi kullanılabilir.
CX : Counter Registeri, Döngü işlemlerinde sayaç olarak kullanılır, yani döngü kaç defa daha dönecek bunun sayısını tutar.
DX : Data Registeri, Donanım ile yapılan giriş çıkış işlemlerinde kullanılır.

CS : Code Segment Registeri, Segmentler bellekte bir alt bölümü işaret ederler. Önemli olan içeriklerinin ne olduğudur. Bizim en önemli segmentimiz kod segmentdir. İçeriğinde ise tahmin edebileceğiniz gibi yazdığımız programın komutları yer alır. Hem programcı hemde kullanılan işletim sistemi kod segmentteki komutlar üzerine başka dataların yazılmayacağının garantisini almalıdır. Aksi taktirde programımız garip hatalar verir ve kitlenir. Programların sağlıklı çalışmaları için Code Segment içeriği en önemli noktadır. (NT çekirdeği taşıyan Windows 2000 ve XP işletim sistemleri gelişmiş bellek yönetimleri sayesinde Windows 9x sistemlere göre daha kararlıdırlar. Windows 9x sistemlerde programlardaki hatalar sistemi çökertebilecek seviyeye çıkabilir. Mavi ekran hataları bu sebepledir. )
DS : Data Segment Registeri, programımızı yazarken kullandığımız değişkenler,karakter dizileri ayrıca çalışma anında oluşturulan değişken tiplerinin tamamı bu segment altında tutulur.
SS : Stack Segment Registeri, Stack çok özel bir data segment tipidir. Alt programlardan, dll dosyaları içinde bulunan fonksiyonlardan, windows api fonksiyonlarından kodlar yürütülmeden önce gerekli değişkenler bu segmente sadece bu amaçla kullanılan bir assembly komutu ile alınırlar. Daha sonra çağırma işlemi yapılır. Önemli olan çalışma prensibini anlamaktır. Burada diğer data segmentlerden olduğu gibi istenilen adresinden veri çekilebilmektedir. Farkı ise stack'a yollanan son verinin çağırılma esnasında ilk olarak geri dönmesidir. Yani son giren-ilk çıkar mantığı. (ileride daha ayrıntılı anlatılacak) Veriler stackin sonundan başına doğru alınırlar. Stacka son yollanan veriyi SP isimli Stack Pointeri işaret eder.
ES : Extra Data Segment Registeri,Data segment ile aynı özelliklere sahiptir. Özel olarak bu segmenti kullanan birkaç komut bulunmaktadır.
FS,GS : Bu segment registerleri ihtiyaç olduğu zaman kullanılmaktadır. Aslında ek olarak kullanılan Data Segment Registerleridir.

BP : Base Pointer, Stack segmentin başlangıç noktasını gösterir. Yani genelde içeriği sıfırdır.
SP : Stack Pointer, Stack segment içine gönderilmiş olan son değerin (byte) adresini göstermektedir. Stack içine veriler yollandıkça değeri azalır çünkü veri segmetin sonundan başına doğru alınırlar. Veriler stackdan çekildikçe değeri artar, böylece eski verileri gösterir, eski veriler silinmez ama SP değeri değiştiği için işlem hata vermeden yürümektedir.
***BP ve SP aslında SI ve DI gibi segmentler içindeki verinin adresini gösterirler. Stack Segmentin özel bir çalışma şekli olduğu için bu Pointer registerleri özel olarak sadece Stack Segmentin sağlıklı çalışması için görev yapmaktadır.

SI : Source Index, Data segment veya istenirse başına küçük bir tanımlama eklenerek diğer data segmentlerdeki verileri de göstermek için kullanılan bir index (işaretçi) registerdir.
DI : Destination Index, SI ile tamamen aynı özelliklere sahiptir. Fakat SI ve DI bazı string komutları tarafından kaynak ve hedef işaretçisi olarak da kullanılmaktadır.

IP : Bir işaretçi registerdir. Çok özel bir işaretçidir. Code Segment içinde işlenecek bir sonraki komutun yerini işaret eder. Yaptığınız işten emin olmadan üzerinde oynama yapmayın!
FLAGS : 32 bitlik yine çok özel bir işlevi olan registerdir. Bu registerin içeriğindeki bitler çok önemlidir. Bazı bitler anlamsızdır, diğerleri ise daha önce işlenen komutların sonuçları ile ilgili bilgiler verir. Örneğin CMP komutu ile iki sayı karşılaştırılır ise sayıların eşit olma veya birinin diğerine göre büyük olması bu register içindeki bazı bitleri 1 (set) veya 0 (reset) durumuna getirecektir. Daha sonra kullanılacak bir dallanma komutu ile bu flaglar kontrol edilerek sonuca göre belirli adreslere dallanmalar yapılır. İçeriğini biz direkt olarak kullanmayacağız. Bazı komutlar işleyişleri sırasında bu registeri gizli olarak kullanmaktadırlar.

Aşağıda hangi segmentlerin hangi işaretçi registerler ile kullanılacakları verilmektedir:
CS:IP DS:SI DS:DI SS:BP SS:SP

 
  Bugün 4 ziyaretçi (7 klik) buradaydı  
 
Bu web sitesi ücretsiz olarak Bedava-Sitem.com ile oluşturulmuştur. Siz de kendi web sitenizi kurmak ister misiniz?
Ücretsiz kaydol