plazma - amatör bilgisayar kültürü

Bilgisayar Mimarisine Giriş 1

Bilgem (Nightlord) Çakır

Bu yazıda her programcının bilmesi gerektiğine inandığım bazı temel kavramlardan bahsedeceğim. Bu kavramlar programcılık üzerine çalışmak isteyen ve yeni başlayan kişilerin daha hızlı öğrenmelerini sağlarken, uzun süredir programcılık yapan fakat elektronik tabanı fazla olmayan kişilerin de bilgi dağarcıklarında kalan olası boşlukları doldurmakta yardımcı olabilir.Kısa kısa değineceğim konu başlıkları şunlar:

  1. Register(yazmaç) nedir?

  2. CPU nelerden olusur?

  3. Bellek nedir?

  4. Bir CPU bellekteki programı nasıl çalıştırır?

  5. Komutlar (instruction) neye benzer?

  6. Makine kodu komutlardan Assembler'a geçiş

1. Register Nedir

Bilgisayar mimarisinden bahsederken önce registerları incelemek lazım. Dijital elektronikte en küçük bilgi saklayabilen birim flip flop adı verilen devredir. Bu devreye 1 veya 0 değerlerinden birini verirseniz, siz başka birşey yapana kadar bu değeri saklamayı becerir. Böylece bir bitlik bir bilgiyi saklamış olursunuz. Eğer 2 flip flopunuz varsa bu ikisine 1'ler ve 0'lar saklayarak toplam 4 farklı değeri taşıyabilen bir hafıza devresi elde edebilirsiniz. İşte registerlar bu şekilde 4 veya 8 veya başka sayıda flip flopların yan yana getirilmesiyle oluşturulur. Biz genelde 8 bitlik (yani 8 flip flopluk) registerlarla uğraşacagız.

8 bitlik bir registerdaki her bit'e 1 veya 0 yazarak toplam 256 değişik değer yazabiliyoruz. Bu değerler genelde ikilik sayı düzeninde 8 basamaklı bir sayı olarak ifade edilir. 10010111 veya 01101111 gibi... Bunları genelde onluk düzendeki 0 dan 255'e kadar olan sayılar olarak yorumluyoruz. Bazen de -128'den +127'ye kadar ki sayılar olarak yorumlayabiliriz. Programdaki amacımıza göre bu bit dizilerine farklı anlamlar yükleyebiliriz. Ama her koşulda toplam 256 değişik değerden fazlasını yazamıyoruz, bunu unutmayalım. Böyle 256 değişik değer alabilen bir bilgi kutucuğuna bayt (byte) adı verilir. Yani 64 kilo byte'lık bir dosya yaklaşık 64000 tane 0 ile 255 arası sayının oluşturduğu bir bilgi grubudur.

Yeniden bir toparlayalım. Register nedir? Register içine 0'dan 255'e kadar bir sayı yazılabilen ve bu değeri unutmayan kutucuklardır. Şimdi CPU yaparken registerların oynadığı rolü inceleyelim. (Not: registerların tek görevi bilgi saklamak değildir aslında ama şimdilik konuyu karıştırmayalım.)

2. CPU(işlemci)

Bu noktada bir CPU tasarımı yaptığımızı farzedelim. İşleri basit tutmak için ilk etapta 8-bitlik (bunun tam olarak ne demek olduğunu zonra anlayacaksınız) bir CPU tasarlayacağız. Bu basit CPU'muza 3 tane 8 bitlik register koyalım ve bunlara A, X, Y adlarını verelim. Bu CPU herhangi bir anda içinde 3 tane 8-bitlik deger (yani 3 bayt) saklayabiliyor. Gerçekte bir CPU'da bundan fazlası var ama şimdilik devam edelim.

Şekil 1.

Dikkat ederseniz su ana kadar registerlarla yapabildiğimiz tek işlem içlerine bilgi saklamak. Fakat bir CPU yapmaya kalkıştığınızda CPU'nuzun bir takım bilgi işleme özellikleri de olmalıdır. Mesela toplama, çıkarma gibi aritmetik işlemler ya da VE / VEYA gibi mantıksal işlemler yapabilmemiz için registerlardan fazlasına ihtiyaç var. İşte bu noktada yardımımıza ALU koşuyor. ALU birtakım aritmetik işlemleri yapabilen kombinasyonel bir devredir.

2.1. Kombinasyonel devre nedir?

Dijital elektronikte bütün devreler iki gruptan birisine mensuptur. Kombinasyonel devreler ve durumlu(state) devreler. Kombinasyonel devreler, içlerinde herhangi bir durum bilgisi taşımayan devrelerdir. Bu devrelerin girişleri ve çıkışları arasında bir bağıntı olur. Giriş değerlerinin herhangi bir andaki değerini bilirseniz çıkış değerini de bilirsiniz. Mesela mantık kapıları bu sınıfa girer.

Örneğin 2 girişli bir VE kapısını ele alalım. Girişlerden her ikisi de 1 olursa çıkış 1'dir. En az bir giriş 0 ise çıkış 0'dır. Dolayısıyla biz iki girişe 1 veya 0 yazdığımızda sonucun ne olacağını biliriz. Girişlerin değerlerinin daha önce ne olduğunun önemi yoktur. Başka bir deyişle, devrenin bir durumu ya da hafızası yoktur.

Oysa flip floplarda durum tam tersidir. Örneğin bir flip flop türü olan toggle- flip flop lar şöyle çalışır. Bu flip flopun bir girişi vardır. Bu girişe toggle adı verilir. Çıkış ise flip flopun o an sakladığı bilgidir (bir bitlik bilgi yukarıdan hatırlayın). Toggle değeri 0 iken değer değişmez. Toggle 1 yapılırsa çıkış değişir. 0 ise 1, 1 ise 0 olur. Şimdi dikkat ederseniz bu devrenin giriş değerini bilmemiz çıkışı tahmin etmemize yetmiyor. Devre aynı girişe farklı durumlarda farklı çıkış veriyor. Yani devrenin bir durumu başka bir deyişle bir hafızası var.

Şimdi bu temel kavramı toparlayalım. En basitten en karmaşığa kadar bütün dijital devreler bu iki tip alt devrelerin birleşiminden oluşur. Bu iki tür devre tipine sık sık değineceğim.

2.2. ALU(Arithmetic Logic Unit)

Şimdi ALU konusuna dönelim. ALU nun kombinasyonel (yani hafızası olmayan) bir devre olduğunu söylemiştim. ALU giriş olarak iki tane 8 bitlik hat ve yapılacak işlemi seçen bazı kontrol hatları alır. Çıkışı da 8 bitlik bir hattır. CPU içinde genelde registerlar ALU'nun girişlerine dijital anahtarlarla (ki bunlara bus transceiver da denir) bağlanır. Bu anahtarlar kapatılırsa o registerda saklanan değer ALU ya giriş parametresi olarak verilmiş olur. Ayrıca bellekten okunan bir değer de ALU girişine bağlanabilir. Bu bağlantı yapılıp kontrol girişlerine de yapılması istenen operasyon belirtildiği anda ALUnun çıkışında sonuç belirir. Bu cıkış yine dijital anahtarlarla bir registera bağlanabilir. Böylece 2 registerla veya bellekle yapılan bir işlemin sonucu tekrar bir registera saklanmış olur.

Bu bölümü sağ salim atlatabildiyseniz ciddi bir tebriği hakettiniz. Şu anda CPU mimarisi ile ilgili çok temel bazı kavramları anlamış bulunuyorsunuz. Eğer anlamadıysanız çok önemli değil ama arasıra tekrar okuyarak ya da bize yazarak bu problemi çözmeye çalışın.

Özet olarak CPUmuzda registerlar ve ALU sayesinde artık registerlara bir takım değerler yüklüyor bunlar arasında işlemler yapıp sonuclar tekrar registerlara saklayabiliyoruz. Bu CPU ile şu halde pekçok faydalı iş yapılabilir. Fakat biz bilgisayar dediğmiz alete doğru basitten karmaşığa gelişen bir yolculuğa devam edeceğiz. Önümüzdeki konu bellek

3. Bellek

Bellek her biri registerlara benzayen 8'er bitlik saklama hücrelerinin bir araya getirilmesiyle oluşturulan hafıza bloğudur. Registerlardan daha ucuz ve daha yavaştır. Bu yüzden daha bol miktarda konulabilir.

Bellekteki her bayta erişebilmimizi sağlayan şey adresleme kavramıdır. Örneğin 64 kilobaytlık bir bellekte adresleri 0'dan başlayıp yaklaşık 64000 civarlarına kadar uzanan yaklaşık 64000 kutucuk vardır. Bu kutucukların her biri bir baytlık (0-255 arası) bir değer alabilir.

Bellek çipleri üzerinde adres ve data bus adı verilen pinler olur. Bir pin ile de okuma veya yazma seceneği seçilir. Mesela okuma/yazma pinine okuma seçeneğini koyup adres bus'a 15 yazarsak, 15. adresteki baytın değeri data busda gözükür.

Bus nedir? Dijital elektronikte çipleri veya çiplerin içindeki alt bölümlerin birbirleriyle data alışverişi yapmalarını sağlayan çoğu zaman 8, 16, 32 bitlik hatlara bus denir. Mesela CPUdan bahsederken ALUnun çıkışının A registerine bağlı olduğunu söylemiştim. İşte bu bağlantı ALU devresinin çıkışındaki 8 bağlantı noktasından A registerinin girişindeki 8 giriş bağlantı noktasına giden yan yana 8 kablodan ibarettir.

İşte CPU ve belleği bir araya koyarsak daha çok iş yapabilen bir CPU elde edebiliriz. CPU genelde Bellek Adres Tamponu ve Bellek Data Tamponu denilen registerlar içerir. Bu registerlar bellek çipinin adres ve data buslarına bağlıdır. Böylece CPU Adres Tamponuna bir değer yazıp, bellekte o adreste bulunan data değerini Bellek Data Tamponuna yükleyebilir. Böylece o registerdaki değer herhangi bir diğer registera kopyalanabilir. Bu register da ALU ya bağlıdır. Böylece bellekten okunan bir değer o an registerlarda bulunan değerlerle aritmetik/mantıksal bi işleme sokulabilir. Son olarak registerlardan birindeki değer Bellek Data Tamponuna kopyalanıp ardından Bellek Adres Tamponuna istenen bir adres yazılır ve Okuma yazma pini de Yazma durumuna getirilirse, CPU bellekteki istediği bir adrese herhangi bir registerın değerini yazmış olur.

Şekil 2.

Şu an önemli bir noktaya geldik. Artık elimizde bayağı yetenekli bir cihaz var. Elimizdeki bellekte duran bilgileri okuyup registerlara yüklüyor ardından üzerlerinde bazı işlemler yapıyor ve sonuçları tekrar bellekte saklayabiliyoruz. Bir örnek inceleyelim... Diyelim ki bellekteki 10 adresindeki sayı ile 15 adresindeki sayılar toplanıp sonuç 100 adresine yazılacak. olaylar şu sırayla olur.

  1. Adres Tamponuna 10 yazılır. Okuma modu secilir. Data tamponuna 10 adresindeki data gelir.

  2. Data tamponundaki değer A registerine kopyalanır.

  3. Adres Tamponuna 15 yazılır. Okuma modu secilir. Data tamponuna 15 adresindeki data gelir.

  4. ALUda toplama işlemi seçilir. ALUnun bir inputunda A diğer inputunda Data tamponu bağlantıları açılır. Toplamanın sonucu A registerine geri yüklenir

  5. A registerindeki değer Data Tamponuna kopyalanır.

  6. Adres Tamponuna 100 yazılır. Yazma modu seçilir. Değer belleğe yazılmış olur.

4. Komutlar (Instructions)

Şu ana kadar bellekten bahsederken hep data saklamak amacı ile bahsettik. Ama bellekte saklanabilen bir diğer veri de komutlardır. Komutlar da aslında sayısal değerlerdir diye düşünebilirsiniz. Sadece onları komut gibi yorumladığımızı düşünelim. Mesela 20 sayısını topla komutu diye yorumlayabiliriz. Komutlar çoğu zaman bir komut ve arkasından gelen komutla ilgili argumanlar olarak gelirler. Mesela "(A registerini yükle)(30)" gibi bir komut düşünülebilir. Eğer bu komutun numarası diyelim ki 25 ise biz belleğe arka arkaya 25 ve 30 sayılarını saklarsak bu komutu belleğe saklamış oluruz.

4.1. Program sayacı (Program Counter)

Bellekte ardarda duran komutların işletilmesi işini çözmek üzere CPUmuza bir register daha ekliyoruz. Bu register içinde saklanan sayı bellekten okunacak olan komutun adresini gösteriyor. Bellekten her komut okuyuşumuzda, komutu işledikten sonra program sayacı bellekte bir sonraki komutun olduğu yeri gösteriyor.

4.2. Kontrol Devresi

Şimdi kafanız karışmış olabilir. Program sayacının değerini kim artırıyor? Ya da bellek erişimlerinde Adres ve Data Tamponlarına kim değerleri koyuyor. Ya da ALU ya hangi işlemin yapılacağını kim söylüyor. İşte bütün bu işleri CPU içinde yöneten bir bölüm var ki buna Kontrol Devresi deniyor(Control Logic). Bu devre Bellekte Program sayacının gösterdiği yerdeki komuta göre registerların arasındaki dijital anahtarları açıp kapatarak CPU içinde datanın nereden nereye gideceğini yönlendirmekle kalmaz, ALUnun yönetimini, belleğe giden okuma/yazma pininin durumunu vs kontrol eder. Bir komutu tamamladıktan sonra Program sayacını değiştirip bir sonraki komuta bakmasını sağlar. Bir CPUnun mimarisindeki en önemli ögelerden biridir.

4.3. Jump komutu

Bu komut ile Program sayacı bellekteki bambaşka bir noktaya bakacak şekilde set edilerek program akışının oradan devam etmesi sağlanabilir.

5. Durum Registeri

Şimdi CPUmuza bir register daha ekliyoruz. ALU ile yaptığımız işlemlerin sonucunda ortaya çıkan durumlarla ilgili bazı faydalı bilgileri kontrol devresi buraya da yansıtır. Bu iş şöyle yapılır. Bu registerdaki bitlere özel anlamlar yüklenir. Örneğin bir tanesine Zero-flag denir. Bu bit ALUdaki işlemin sonucu sıfır çıkarsa 1 olur. Bir diğer bite Carry-flag denir. Bu da ALUdaki toplama işleminde sonuç 255'i geçerse 1 olur. Bir diğer bitte Negative-flag'dir. Bu da ALUdaki işlemin sonucu negatif çıkarsa (mesela 2 - 30) 1 olur.

Bu bayraklar bizim için çok önemlidir. Çünkü bu bayrakların durumuna bakarak hareket eden komutlar vardır. Bunlara Koşullu Dallanma (Conditional Branching) komutları denir. Bu komutlar sayesinde programlara karar mekanizmaları eklenebilir. Örneğin şöyle kodlar yazılabilir:

  1. bellekte N adresindeki değeri A'ya yükle

  2. A daki değer ile 30'u karşılaştır (ALU ile A'dan 30'u çıkar)

  3. Sonuç 0 ise 6. basamağa atla (işte burası koşullu dallanma komutu. Eğer koşul sağlanmazsa program bir sonraki komuttan devam edecek)

  4. N adresindeki değer 30 olmadığında neler yapacaksan onu yap

  5. Programdan çık

  6. N adresindeki değer 30 olduğu zaman neler yapacaksan onu yap.

  7. Programdan çık

Şekil 3.

6. Komutlara özet bakış

Şu ana kadar incelediğimiz makine dili komutları dikkat ederseniz sadece aşağıdakilerden birini yapabilir.

  1. Bellekten bir registera değer kopyalama

  2. Bir registerdan belleğe değer kopyalama

  3. Jump ile bellekte herhangi bir yerdeki bir komuta atlama

  4. İki register arasında değer kopyalama

  5. Register ve bellek arasında aritmetik/mantıksal bir işlem yapıp sonucu tekrar bir registera kopyalama

  6. ALUdaki işlem sonucuna göre dallanma veya bir sonraki komuta devam etme.

Gerçek CPU'larda da komutların %75'i bu saydıklarımızdan birini veya birkaçını yapar ama bunların dışında birşey yapmaz. Yani herbiri küçük ama faydalı işler yapar. En karmaşık programlar ile bu küçük işlerin bir araya gelmesiyle oluşur. Bu yüzden Makine dili komutlarını öğrenmek kolay bir iştir. Makine dili ile program yazmak ise problemleri en küçük parçalarına kadar bölerek tasarım yapmayı öğrenene kadar size zor gelebilir.

7. Makine Dili Komutlarından Assembler'a geçiş

İnsanlar ilk yaptıkları bilgisayarları programlamaya çalışırken bellekteki her değeri rakam rakam hazırlarlarmış. Hatta rivayetlere göre en eski bilgisayarlardan olan ENIAC da bu rakamları belleğe girerken binlerce elektrik anahtar (yani bildiğimiz ampul yakan cinsten) çat çut teknisyenlerce açılır kapanırmış. Tabii fazla geçmeden insanlar program yazarken ince ince sayılarla uğraşmasın diye makine dili komutlarını genelde 3 harfli kısaltmalarla ifade eden assemblar dillerini oluşturmuşlar. Böylece mesela 24,125,34,55,98,12,90 diye program yazmaya çalışmak yerine insanlar

lda 24  (yani A registerine 24 
         adresindeki degeri yukle)
clc     (yani Carry Flag'i sifirla)
adc 98  (98 adresindeki deger ile 
         Carry Flag degerini toplayip 
         A registerine ekle)
sta 90  (sonucu 90 adresine yolla)

diye kod yazabilmeye başlamış. Daha sonra assemblerlar daha da gelişmiş ve insanlar çeşitli rakamların yerine isimler (bunlara etiketler de diyoruz) kullanabilmeye başlamış. Böylece yukarıdaki kod yerine

mevcut_param = 24
maas = 98
ev_sahibi = 90

lda mevcut_param
clc
adc maas
sta ev_sahibi

yazabilmeye başlamışlar.

İşte günümüzde Makine dili kullanarak kod yazan hemen herkes bu tip assembler araçları kullanır. Bu araçları kullanırken yazılan kod nasıl düzenli tutulur? Aradan 5 ay geçtikten sonra eski kodlarınıza döndüğünüzde onlardan birşeyler anlayabileceğinizi nasıl garanti edersiniz? Bütün bunlar cevaplanması gereken önemli sorulardır. Bunlarla ilgili yazılım mühendisliğinde kullanılan bir ton teknik vardır. Bunlar da başka bir yazının konusu.

"Eee biz şimdi hiçbir assembler komutu öğrenmedik" diyebilirsiniz. Panik yapmayın onun da zamanı gelecek. Hali hazırda bu konuda bulabileceğiniz kaynaklar var internette. Şimdi gidin ve onlara bir daha bakın. Pek çok şeyi daha kafanızda pekiştirerek anladığınızı göreceksiniz. Ayrıca ben de er geç o konuda birşeyler yazacağım.

8. Sonuç

Bu yazı aslında giriş seviyesinde bir yazı olmakla beraber içerdiği bazı elektronik detayları yüzünden orta seviye gibi gelebilir. Eğer yazının içinde anlamadığınız yerler varsa bize yazarak sorabilirsiniz. Sonuçta anlamadığınız yerler kalsa bile anladığınız yerler size bayağı fayda sağlayacaktır. Ayrıca sizinle bire bir çalışan bir hocanız olmadıkça her konuda yeni birşeyler öğrenirken, bazı yerleri anlamayabilirsiniz. Daha sonra başka bir yerde birşey okurken anlamadığınız şeyle alakalı olduğunu anlar ve gereken ilişkilendirmeyi kafanızda yaparsınız. Zaten öğrenme böyle bir deneyimdir.

Önümüzdeki sayıda Giriş/Çıkış sistemlerinin CPU ve bellek ile nasıl entegre edildigini inceleyeceğiz.

plazma - 2008