Makina Diline Merhaba (1)

Günümüzde aşağı yukarı bütün kişisel bilgisayarlar ve ev bilgisayarları BASIC'te programlanabilir özellikte pazarlanıyorlar. İlk bakışta akla gelen tüm problemler BASIC'te çözülebilir gibi görünüyor. Ayrıca öğrenmesi de kolay. Peki bunca kimse neden makina dili öğreniyor? Nedir makina dilini, o kadar zor olmasına karşın, öğrenilecek kadar cazip kılan? Siz bu yazıyı okuyanlar: büyük bir çoğunluğunuz BASIC'te alet üzerinde kesin hakimiyetiniz var. Kolayca da öğrendiniz. Ne var şimdi durup dururken rahatınızı bozup da makina dilleri öğrenmek için yorulacak?..

Yanlış!.. Hem de çok yanlış düşünüyorsunuz. Aslında makina dilini öğrenmek, BASIC dilini öğrenmekten daha zor değil. Yazımızın ilk amacı da bu zaten. Sizin gözünüzü korkutup yormadan, sizi bu yeteneğe kavuşturmak! (yazımız bitince ve makina dilini öğrenince bu tanıma siz de çok güleceksiniz). Yalnız bu söylediğimizin gerçekleşmesi için tek bir şartımız var. O da şu anda BASIC'i çok iyi bilip kullanıyor durumda olmanız. Zaten her iki dilin temel çalışma prensipleri pek farklı değil. Şimdi elde edeceğiniz avantajları gözden geçirelim. Commodore 64'ünüz BASIC'te programlanabiliyor. Bu ismin nereden geldiğini de belirtelim. İngilizce'de 'esas' anlamına gelen 'basic' kelimesiyle sadece yazılım benzerliği taşıyor. Aslında "Beginners All-purpose Symbolic Instruction Code" tanımının baş harflerinden oluşmuş bir kısaltma, anlamı ise 'yeni başlayanlar için genel amaçlı sembolik programlama'. BASIC yüksek düzeyli diller kategorisine giriyor. Bu kategorinin diğer üyeleri FORTRAN,

PASCAL, COBOL gibi tüm diller. Bu dillerin genel özelliği belli bir problemin çözülmesi için geliştirilmiş olmaları. Makina dili ise bu dillerin tam tersine aletin donanım yapısına hitap eder. BASIC'i iyi biliyorsunuz dedik (zaten başka türlü, makina dilini düşünmek hayal olurdu). Yalnız Commodore 64'ünüz esasında BASIC bilmiyor. Peki nasıl oluyor da, BASIC komutlarını böyle düzenli ve hızlı yürütebiliyor. Bu özellikleri sağlayan şey, işletim sisteminin bir parçası olan BASIC-INTERPRETER (yorumlayıcı)'dır. Yorumlayıcının görevi, yazı karakterlerini kullanarak verdiğiniz BASIC komutlarını yorumlayıp bunlara ait (hazır olan) makina dili altprogramları yürütmek. Şimdi bir örnek üzerinde bu olayı daha detaylı olarak kavramaya çalışalım:

Bu emri ekrana yazıp 'RETURN' tuşuna bastığınızda, yorumlayıcı bu satırı harf harf okur ve kendine ait komutları arar. Bu işlemi, okuduğu kelimeyi belleğinde belli bir bölgede bulunan komut tablosuyla karşılaştırıp yapar (GOTO, FOR, INPUT gibi). Şayet kendine ait bir komuta rastlarsa, bu komutun numarasına dikkat eder. Bu numarayı ise başka bir tablodan o komutun adresini bulmak için kullanır. Artık komutun uygulanmasına gelmiştir sıra, PRINT emrine ait altprogram çağırılır. Bu altprogram da karakterleri tek tek okur. Tırnağa rastladığı zaman sonra gelen karakterleri ekrana basmaya başlar. Bu işlem ikinci tırnağa rastlayana kadar sürer. Daha sonra ise satır bitmektedir. PRINT altprogramı yorumlayıcıya geri döner. Bu da yapacağı işin bittiğini anlayınca yeni komut almak için 'READY' yazdır ve CURSOR yanıp sönmeye başlar. Şimdi diyeceksiniz ki bu kadarcık iş için aletimiz böyle mi uğraşıyor?.. Bu kadar uğraşmaktan ciddi bir şey yapmaya zaman bulamaz bu alet... Halbuki gördüğünüz gibi durum o kadar da kötü değil. Aletimiz gene de bu işlemleri oldukça hızlı yapmakta. Düşünün bir de biz aradaki birçok gereksiz işlemi atlatıp bu işleri yapan ana işlem ünitesine doğrudan hitap edebilsek. O zaman görün siz! Bir örnekle bu hız farkını daha belirgin olarak gözleyelim. Diyelim ki ekranın sol üst köşesine bir “A” harfi çıkartmak istiyoruz. Bunun için hızlı bir yöntem olarak POKE komutunu kullanalım. BASIC satırı olarak şunu girmemiz gerekmektedir:

Bunu yazar yazmaz ekranın sol üst köşesinde “A” harfi belirdi değil mi? (Bu emri yazarken ekranı kaydıracak bir yerde olmamaya dikkat edin, yoksa A harfinizi bir türlü göremezsiniz, hâlâ göremiyorsanız sizin aletiniz 1984 senesinden de eski üretilmiş demektir. O zaman uğraşmaktan vazgeçin.) Yalnız hızı gözle takip edemediniz galiba?.. Tekrar tekrar denemenin faydası yok. Oldukça hızlı oluyor galiba bu iş. Bütün bu işlemin toplam süresi 2 milisaniye civarı bir şey (bir milisaniye, saniyenin binde biridir). Şimdi bir de makina dilinde aynı işi yaptıralım:

Bunu yazıp zamanı ölçmeye falan kesinlikle kalkışmayın. Çünkü işlemimiz yalnızca 8 mikrosaniye sürüyor (bir mikrosaniye, saniyenin milyonda biridir). Gördüğümüz gibi aynı işi BASIC'te ve makina dilinde yapmak arasında 200 kat kadar zaman farkı keşfettik. Aslında genel bir yargı olarak belirtebiliriz ki, aynı işi yapmak üzere yazılmış BASIC ve makina dili programlar arasında 10 ilâ 4000 kat hız farkı vardır. Bundan başka bazı işler vardır ki yalnızca makina dilinde yapılabilir. Bunlara daha sonra değineceğiz. Başka bir genel yargı da bilgisayarın tüm olanaklarını yalnızca makina dilinden kullanabileceğiniz. Buna örnek olarak Commodore 64'ün üç boyutlu ve gerçek zamana bağımlı grafik özelliklerini kullanabilmeyi verebiliriz. İlginç bir özellik de makina dili programlarının daha ufak ve derli toplu oluşu. Mesela 1 KByte'lik bir BASIC program ciddiye alınmaz bile. Oysa bu uzunlukta bir makina dili program başlı başına tüm bilgisayarı çekip çevirebilir. Veri ve bilgileri saklama konu-

sunda da aynı şeyi söyleyebiliriz.

Kendinize uygun en kompakt şekli kullanabilirsiniz. Oysa BASIC'te kullanabileceğiniz veri tipleri sınırlıdır. Gelelim makina dilinin dezavantajlarına: Öncelikle makina dilini öğrenmek gerekiyor. Fakat BASIC'teki programlama tekniklerine hakimseniz, belli bir hızınız var demektir. Ayrıca LİSTİNG ekinde makina dilinde çalışabilmeniz için gerekli araç ve gereçleri bulacaksınız. Bunlar monitör programları, assembler programları, simülatör'ler, reassembler vs. programları olacak. Makina dilinin başka bir dezavantajı bilgisayara aşırı biçimde bağımlı olmasıdır. Aynı ana işlem ünitesine sahip bilgisayarlarda bile doğrudan programları taşıyamazsınız. Bunun nedeni makina dili programlarının bilgisayarın birçok özel fonksiyonlarına yönelik olmasıdır. Gördüğünüz gibi dezavantajlar pek o kadar da fazla değil. Bunları da yalnızca kendine güven ve cesaretle atlatabilirsiniz. Gerçekten de bilgisayarınızda arayıp bulamadığınız birçok özelliği makina dili yardımıyla görebileceksiniz. Bu arada bazılarınız diyebilir ki BASIC ne olacak? Bir kenara mı bırakacağız o'nu?.. Hayır, hiç de öyle değil. Programınızın gerekli kısımlarında makina dili kullanırsınız, bunları denetim altında tutan gene BASIC olur.

İlk makina dili programınızı yazınca göreceksiniz ki bu iş öyle ilk göründüğü kadar zor değil. Bu yazı dizisi boyunca birçok programlama örneği de vererek size program geliştirme yönünden yardımcı olmaya çalışacağız. Dediğimiz gibi, sonuçta iyi bir şeyler yapabilecek duruma geleceksiniz. O kadar da zor olmayacak. Fakat siz gene de gayretinizi koruyun; azim ve sabırı elden bırakmayın. Herkes hazırsa, haydi başlıyoruz...

6510 Ana İşlem Ünitesi

Makina dilinde programlamaya geçmeden önce programlayacağımız şeyi, ana işlem birimini tanıyalım. Bu arada veri, adres, bit, byte, register gibi bazı kavramların tanımını da yapacağız. Ana işlem ünitesinin iç yapısından başlayalım. 6510 ana işlem ünitesi 6502 ailesine ait bir üye. Bu ailenin diğer üyeleri de aynı zamanda diğer Commodore ürünlerinde yoğun şekilde kullanılıyor. Ana işlem ünitesinin içinde birçok register yeralıyor. Bu registerler yardımıyla ana işlem ünitemiz birçok işlemi gerçekleştirebiliyor. İşin ne olduğuna bakmadan önce bit kavramına değinelim (çoğunuz bu konulardan sıkılabilir, fakat yeni öğrenenler olduğunu da unutmayalım). Bilgisayarımız “dijital” çalışan bir alettir. Bu demektir ki bilgisayarımız iki şeyden anlıyor. Bunlar 1 ve 0 rakamları veya “açık” ve “kapalı” durumları veya daha da başka bir yorumla 5V ve 0V (şase seviyesi) potansiyel değeri. Böyle bir şeyin kendi başına pek bir işe yaramayacağı oldukça açık. Bu yüzden bunların birkaç tanesi biraraya getiriliyor. Bizim durumumuzda sekiz tanesi biraraya getirilmiş durumda ve bir Byte adı altında bir bilgi grubu oluşturmuş durumdalar. Bir Byte'ın yapısını gözönüne getirmek istersek:

Yukarıda her bir bitin byte içindeki pozisyonunu gösteren bir numara ve hemen altında ise o bitin içeriğini görebilirsiniz. Her bir bit iki değişik durumu gösterebildiğine göre sekiz tanesiyle çok daha fazla bilgi gösterilebileceği aşikâr. Daha iyi bir görüş için bildiğimiz ondalık sayı sisteminden bir örneği inceleyip karşılaştırma yapalım:

Burada da rakamları tek tek numaraladık. Peki bu sayının değerini nasıl elde edeceğiz?.. Bildiğimiz gibi her basamağın içeriği 0 ile 9 arasında değişebiliyor ve bulunduğu basamağa göre bir faktörle çarpılıyor. Bu faktör ise her basamak için bir sağdaki basamağın on katı değer taşıyor. Buna göre sayımızın değeri:

ni belirleyen faktör her defasında on ile değil, iki ile çarpılıyor. Şimdi register'imizin içeriğini hesaplayalım

440'240*2*2441'2'2'24 4 0*2*2*2'2'2'2'2

Demek ki register'imizin içeriği 255'miş!.. Bu yorucu işlemi biraz

Aynı yolu uygulayarak bizim register'imizin içeriğini inceleyelim. Onluk sistemde on adet rakam kullanabiliyorduk. İkili sistemde ise yalnızca iki adet seçeneğimiz var. Bu yüzden basamakların değerini basitleştireceğiz. Her basamağın belli bir değeri olduğundan bu değerleri bir kere hesaplayacağız ve aklımızda tutacağız, nasıl ondalık basamakların değerlerini aklımızda tutabiliyorsak.

Bu hesaplamalara göre bir register'in en fazla hangi değeri alabileceğini hesaplayalım. Bu iş için her basamağa 1 değeri verelim. Böylece: 1+2+4+8+16+32+64+128. Demek ki sekiz bit ile en fazla 255 sayısını gösterebiliriz. Bu sayıyı belki daha evvelden PEEK fonksiyonundan tanıyorsunuz. Fakat o konuya daha sonra döneceğiz. Bu sayı sistemine aynı zamanda ikili sayı sistemi de deniyor. Fakat gördüğünüz gibi kullanımı oldukça karışık. Ortalık 0 ve 1'lerden oluşmuş bir çorbaya dönüyor. Bu yüzden benzer karakteristikleri taşıyan bir sayı sistemi geliştirilmiş. Hekzadesimal (onaltılık) sistem denilen bu sayı sistemi dört bitlik grupları tek basamakta belirtmemizi sağlıyor. Bu sayı sistemi onaltı adet rakam tipi içeriyor. Buna göre diyebiliriz ki bir Byte iki onaltılık basamakta gösterilebilir:

Demek ki bu register'ın içeriği onaltılık sistemde 69'muş. Sayı sistemlerini karıştırmamak için bir şeyler yapmamız gerektiğini fark etmişsinizdir. Bu yüzden onaltılık sayıların önüne dolar işareti, ikili sistemde yazılmış sayıların önüne ise yüzde işareti "%" koyacağız. Baştan belirtelim ki biz onaltılık sistemde çalışmayı tercih edeceğiz. O yüzden bu sayı sistemi başta size garip gelse de zamanla alışacaksınız. Sayı sistemleri üzerine yaptığımız bu incelemeden sonra ana işlem ünitesinin kendisine dönelim. Bunun için öncelikle ana işlem ünitesinin bütün register'lerini göz önüne alalım.

Akümülatör

Görüldüğü gibi her byte iki adet yarım byte'a (aynı zamanda nibble da deniyor) ayrılıyor. Bu nibble'ların değeri 0 ile 15 arasında değişebiliyor. Ondalık sistemimizde yalnızca 0'dan 9'a kadar sayıları belirtebildiğimize göre 10'dan 15'e kadar olan sayıları belirtebilmek için yeni bir şeyler bulmalıyız. Bu iş için A'dan F'ye kadar olan rakamları kullanacağız. Şimdi değer tablomuza bir göz atalım:

Program sayacı

1 0987608493210

Durum register'ı

7 6 5 4 3 2 1 0

Yığın göstergeci

Yukarıdaki şemada görüldüğü gibi ana işlem ünitemizin 6 register'i var. Bunlardan 5 tanesi sekiz bit'lik register'lerden. Aralarında bir tanesi ise onaltı bit'lik tipten. Bu register'lerin hepsini tek tek daha yakından inceleyelim. En önce akümülatör veya nam-ı diğer akü'den, yani ana işlem ünitesinin en önemli register'inden söz edelim. Bu register ana işlem ünitesinin yapması gereken tüm mantıksal veya aritmetik işlemleri üstlenen kısımdır. X-Register'i ana işlem ünitesinin ikinci register'idir. Tablo tipi veri yapılarının kullanımında akü ile birlikte çalışır. Genel olarak tabloların indeks işleminde kullanıldığından indeks register'i olarak da anılır. Y-Register'ı X-Register'ine benzer fonksiyonlara sahiptir ve benzer yerlerde kullanılır. Program sayacı onaltı bit'lik bir register'dir. İçeriği ise program yürürken bir sonraki komutun başlangıç adresini saklamaktır. Bu yüzden program yürürken kendiliğinden daimi olarak artar. Birkaç (JMP) veya alt program çağırma (JSR) komutları dışında bu register'le doğrudan bir işimiz olmayacak. Yığın göstergeci ise, yığın denilen bir bilgi yığınını kullanmak için gerekli olan göstergeç değerini içeriyor. Yığın genelde alt programların geri dönüş adreslerini saklamak için veya anlık ve bölgesel olarak saklanması gereken bilgiler için kullanılır. Daha detaylı olarak alt programlar bölümünde inceleyeceğiz. Durum register'ı o anda ana işlem ünitesinin çalışma mod'unu ve en son yapılan sonucu ile ilgili genel bilgileri içerir. Bu genel bilgiler şartlı olarak yürüyen programlar yazabilmemizi sağlar (IF...THEN... gibi). Bu register'in bir bit'i dışında tüm bit'lerinin özel anlamları vardır. Bu bit'lerin her birine flag denir. Bu flag'ların durumu doğru (=1) veya yanlış (=0) olabilir ve belli bir durumun olup olmadığını gösterir. Durum register'inin yapısı şöyledir:

Bu flag'ların isimleri yaptıkları işin baş harfinden oluşmuş kısaltmalardır ve anlamları şunlardır:

C- Carry (taşma): Bu flag yapılan son işlemde bir taşma olup olmadığını gösterir. Mesela iki sayıyı topladığımızda sonuç 255'ten büyükse sonuç 8 bit'le gösterilemez ve bu flag yakılır. Z- Zero (sıfır): En son yapılan işlem sonucu sıfır'sa bu flag yakılır. I- Disable: Bu flag belli bir program bölümü yürürken Interrupt denilen programın periyodik olarak kesilmesi olayına izin verilip verilmediğini gösterir. Bu flag şimdilik bizi pek ilgilendirmiyor. D- Decimal (ondalık hesaplama): Bu flag toplama ve çıkarma işlemlerinin ondalık mod'da yapılıp yapılmayacağını belirler. Bu flag da bizi şimdilik ilgilendirmiyor. B- Break: Bu flag, programın kesilme işleminin BRK emri ile yapılıp yapılmadığını gösterir. Bu flag'ı da şimdilik kullanmayacağız. V- Overflow: Sayıları pozitif ve negatif diye yorumlayıp işlem yaptığımızda işimize yarayacak bu flag, yapılan işlemdeki taşmaları gösterir. N- Negative: Bu flag en son yapılan işlem sırasında 7 numaralı bit ile aynı içeriğe sahiptir. Negatif denmesi 128 ve üstündeki sayıların negatif olarak da yorumlanıp işlem yapılabileceğindendir.

Başlangıç olarak yalnızca Carry, Zero ve Negative flag'larıyla ilgileneceğiz.

Ana işlem ünitesinin bir şeyler yapabilmesi için bir program yazılı olması lazım ve bu programın bulunacağı bir alan, ayrıca bu programın yürürken kullanacağı veriler için de bir bölge lazım bize. Aletin çalışma alanı veya RAM bellek denilen şey bizim register'lerin oldukça büyük miktarda bir araya gelmişinden başka bir şey değil. Bu kadar çok register'in hepsine ad vermekle başa çıkamayacağı için her birini sırayla numaralama yolu seçilmiş. Bu numaraya aynı zamanda adres de diyoruz. Ana işlem ünitemizin 16 adet adres hattı vardır. Böylece 2^16 adres'i ayırt edebilir. Buradan vardığımız sonuç şu ki,

64 KByte'lık hafızası olan COMMODORE 64'ümüzün bütün adres bölgesi bellek elemanlarıyla kaplanmış durumda. Dikkatli okurlarımız hemen fark etmişlerdir. Program sayacımız da onaltı bit genişliğindeydi. Bu onaltı bit'lik değer bir sonraki komutun (instruction) adresini aklında tutmakta (neden o anki değil de bir sonraki komutun, diye soranlar için söyleyelim; ana işlem ünitemiz PREFETCH denilen bir teknikle çalışıyor ve belli bir oranda hızı artıyor). Ana işlem ünitesi program sayacı yardımıyla hafızayı adresler ve komutu bellekten okur, yürütür. Bir komut, bir byte'a sığabildiği için en fazla 256 komut olabilir. Tabii bu komutları BASIC komutları ile karıştırmayın; bunlar ana işlem ünitesi tarafından doğrudan yürütülebilecek komutlardır, yani makina dilinin ta kendisidir.