Commodore 64 ile Grafik (4)
Şu ana kadar gördüklerimiz bize, bilgisayarımızın grafik işlemleriyle uğraşan yapı taşlarının her birinin ne işe yaradığını gösterdi. Onları byte byte, bit bit tanıdık. Artık sıra geldi onları amaçlarımız doğrultusunda kullanmaya.
Bu bölümde, grafik kullanımlarımızın temel taşlarından biri olan bir konuyu göreceğiz: Grafik bilgilerimi hafızanın neresine nasıl yerleştireceğiz? Gelecek bölümden itibaren de, noktasal grafiğin programlamasına başlayacağız, gözünüz aydın. Geçen bölümde gördüğünüz gibi, bilgisayarımızın hafızası değişik bölümlere ayrılmıştır ve bunların bir kısmı üst üste, yani aynı adreslerde bulunmaktadır. 1 numaralı yazmacın ilk iki bit'i ile bu konfigürasyonları seçebiliyorduk. Ancak buralara bir şeyler yazabilmenin daha kolay bir yolu olmalı, değil mi? İşte bu nedenle, C64'ün dizaynını yapanlar şöyle bir yol bulmuşlar: Hafızanın herhangi bir yerine bir sayı yazıldığında (örneğin BASIC'de POKE veya makina dilinde STA komutları ile), bu sayı, doğrudan doğruya verilen adresteki RAM'a yazılır. Bu arada 1 numaralı yazmacın değeri önem taşımaz. Zaten ROM'a (yalnız okunabilir bellek ünitesi) bir değer yazmanın anlamsız olduğu göz önüne alınırsa, yukarıdaki yöntem oldukça mantıklı geliyor. Bir örnek olarak aşağıdaki programa göz atalım:
10 FOR 1-40960 TO 49151
20 POKE IPEEK(1)
30 NEXT I
40 POKE 1,PEEK(1) AND 254
Normalde, 40960-49151 ($A000-$BFFF) adresleri arasında, BASIC yorumlayıcısı ROM'u durmaktadır. Ancak aynı adreslerde bir de boş RAM vardır. İşte biz yukarıdaki RAM ile aynı adreslere sahip ROM'un değerini RAM'a kopyalıyoruz. En son satırda da, geçen bölümde gördüğümüz gibi, 1 numaralı yazmacın alt bit'ini değiştirerek kopyaladığımız adreslerdeki ROM'u kapatıp, RAM'ı aktif hale getiriyoruz. Bu işlem, her BASIC ilerletme programında kullanılan bir numaradır. Örneğin yukarıdaki programı çalıştırdıktan sonra şöyle bir komut verin:
POKE
Artık, PRINT komutu çalışmayacak, ama aynı işlemi TRINT görecektir. Çünkü, 41220 numaralı hafıza bölgesinde PRINT komutunu oluşturan harflerin tanımı başlamaktadır ve biz 1 eskiden P olan harfi T haline getirdik. Neyse, bunun grafikle pek bir alakası yok ama, bir bilgi olması açısından çok önemli. Özetle, 1 numaralı yazmaç, POKE işlemleri için önemsiz ama, PEEK işlemlerinde nerenin içeriğinin okunacağını belirlemesi açısından önemli. Ancak tabii ki, bu kuralın da bir istisnası var. 0-1 arasında bir ROM, iki RAM bulunmaktadır. Buraya bir sayı yazıldığında, Giriş/Çıkış bölgesi olarak adlandırılan RAM'a yazılır. Okuma sırasında da, gene bu RAM okunur. Diğer RAM veya ROM bölgesine ulaşmak için 1 numaralı yazmacın 0-2 numaralı bit'leri ile oynamak gerekir. Bunları daha sonra göreceğiz. Okuma yazma işlemlerine böylece açıklık getirdikten sonra, şimdi de hafıza bölgelerine grafik açısından verdiğimiz isimleri, bunların hafızanın nerelerinde bulunduğunu ve nasıl yerlerinin değiştirilebileceğini görelim. Grafik işlemlerinde toplam beş değişik hafıza bölümüne gerek vardır:
1) Sprite tanımlamaları 2) Renk RAM'ı 3) Video RAM'ı 4) Grafik hafızası 5) Karakter üreteci
Bunları sırasıyla tanıyalım: 1) Sprite tanımlamaları Her sprite'ın görüntüsünü oluşturan noktaların tanımladığı 63 byte'lık bölgedir. 2) Renk RAM'ı Bu bölge yaklaşık 1 Kbyte'lık (tam olarak 1000 byte'lık) bir alan kaplar ve daima 55296-56319 ($D800-$DBFF) adresleri arasındadır. Ancak buradaki her byte'ın sadece alt 4 bit'i
(0-3 numaralı bit'ler) kullanılır. Böylece 0-15 arası renk kodları saklanabilir. Üst dört bit, daima 1 olur. Normal yazı ekranı aktif iken, her karakterin rengi bu bölgede saklanır. Nokta grafiğinde ise, eğer çok renkli mod kullanılıyorsa, renk RAM'ının her byte'ı grafik ekranında karşılık geldiği 4*8'lik alanın 3 numaralı rengini belirler. 3) Video RAM'ı Bu bölüm de 1000 byte'dan oluşur ve değişik görevleri vardır. Normal yazı ekranı kullanılırken, ekranda gördüğünüz harflerin kodlarını saklar. Grafik ekranında ise, eğer çok renklilik yoksa, 8*8'lik alanın çizgi ve arkaplan rengini verir. Çok renkli mod kullanılıyorsa, 4*8'lik alanın 1 ve 2 numaralı renklerini belirler. Normalde 1024-2023 ($0400-$07E7) arasında bulunur, ama biraz sonra göreceğimiz yöntemlerle kaydırılabilir. Ayrıca 2024-2047 ($07E8-$07FF) arası da, sprite vektörlerini içermesi açısından önemlidir ve video RAM'ı ile birlikte kayar.
4) Grafik hafızası 320*200'lük grafik ekranının gerektirdiği bilgiyi saklayan 8 Kbyte'lık bir bölümdür. Normalde $2000-$3FFF (8192-16383) arasında bulunur ve kaydırılabilir.
5) Karakter Üreteci Yazı yazarken gördüğünüz karakterlerin her birinin görüntü tanımlamasının yer aldığı bölüm, işte burasıdır. Büyük ve küçük harf olmak üzere 256'şar karakterlik, toplam 512 karakter saklayan bu bölüm, her bir karakter 8 byte gerektirdiği için 4 Kbyte'lık bir alan kaplar. Normalde $D000-$DFFF (53248-57343) arasında bulunan ROM'da saklanan bu tanımlar, gerektiğinde başka bir bölgeye taşınıp değiştirilebilirler. Deminden beri, taşınma ve kaydırma sözlerini çok kullandık. Peki bunlar nasıl oluyor? İşte buna cevap verebilmek için, C64'ün kalbi 6510'un adreslemesinin dışında, grafik sorumlusu VIC'ın de bir adreslemesi olduğunu bilmemiz gerekir. Şimdi de bunun ayrıntılarına inelim... VIC'ın bize sunduğu en önemli avantaj, tüm hafızayı bir RAM olarak görmesi. Örneğin bizim ROM'un altındaki RAM'ı okumamız için 1 numaralı yazmacı değiştirmemiz gerekirken, VIC oradaki bilgiyi doğrudan doğruya alabiliyor. Dolayısıyla, grafik bilgilerimizi hiçbir sorunla karşılaşmadan ROM'un altına yerleştirebileceğiz. VIC, tek başına sadece 16K'lık bir alanı gözetleyebilir. Yani, VIC'ın kullanacağı tüm bilgilerin, renk RAM'ı hariç, o sırada okuduğu 16K'nın içinde olması gerekir. Demek ki, 64K'lık bilgisayarımızın içinde VIC'ın ulaşabileceği 4 ayrı bölüm (BANK 0... BANK 3 olarak adlandırılan) var. Bunların nasıl kaydırılacağını birazdan göreceğiz. Ancak en başta bir sorundan söz etmek gerekiyor. Bilgisayar ilk açıldığında, BANK 0 kullanılmaktadır. Yani 0-16383 ($0000-$3FFF) arası. Ancak, karakter üreteci ROM'u $D000-$DFFF arasında olduğuna göre, VIC, karakter tanımlamalarını “göremeyecektir”. İşte bunu önlemek için, C64 dizayncıları bir numara daha yapmışlar ve bir iki tel yardımı ile, ROM'un bulunduğu adreslerdeki bilgileri $1000-$1FFF ve $9000-$9FFF arasına da taşımışlardır. Bunlara, bilgisayar çevrelerinde, “hayalet bölgeler” denir, çünkü orada olmayan bilgi, VIC için sanki oradaymış gibi görünmektedir. Bu nedenle, örneğin $1000-$1FFF arasına sprite tanımlamaları koymak anlamsızdır. Çünkü VIC bunları değil, $D000-$DFFF arasındaki karakter tanımlarını okuyacaktır. Bir diğer önemli nokta da, $D800-$DBFF (55296-56319) arasındaki renk RAM'ının, aynı bölgedeki boş RAM'la karıştırılmaması gerektiğidir. Yukarıda gördüğümüz gibi VIC, 16K'lık 4 değişik bölgeyi adresleyebilmekte. Bu hangi bölüm olacağını, daha önce de gördüğümüz, CIA 2 olarak adlandırdığımız hafıza biriminin ilk iki bit'i yapacaktır. Hatırlarsanız, bu bit'ler için LOW-aktif demiştik. Dolayısıyla, biz bu iki bit'i örneğin 01 yaparsak, bunlar sanki 10 değerini almış gibi davranacaklardır. Bu iki bit'in durumu ile hangi 16K'lık bölgenin seçildiğini aşağıdaki tablodan görebilirsiniz:
BİT Adresler 11 $0000-$3FFF (0-16383) 10 $4000-$7FFF (16384-32767) 01 $8000-$BFFF (32768-49151) 00 $C000-$FFFF (49152-65535)
Bunu daha mantıklı bir şekilde bulmanın bir yolu var. Diyelim ki,
BANK'ın $8000'den başlamasını istiyorsunuz. Eğer 8'i ikilik sistemde yazarsak %01000 elde ederiz. En soldaki iki bit 10 durumunda. Kontrol bit'lerimiz LOW-aktif olduklarına göre, bunu ters çevirmemiz gerekiyor. Yani sonuç, 01 olacak. Tabloda sonucu elde ettik. Daha da basiti, BANK 0...BANK 3 isimlendirmesini kullanırsak, $8000'den BANK 2 başlar; ikilik sistemde iki için %0010 yazarız; tersi de bize ihtiyacını duyduğumuz verir. Şimdi sıra geldi, bu iki bit'i CIA 2'ye yerleştirmeye. Bunun için aşağıdaki formülü kullanabilirsiniz:
POKE 56576,PEEK(56576)
AND 252 OR (3-X)
*X” yerine, kullanacağınız BANK numarasını (0..3) koyacaksınız. Bir örnek verelim. Diyelim ki, $0400'de başlayan video RAM'ını $8400'e taşımak istediniz. Hemen 'X” yerine 2 koyarak yukarıdaki komutu yazın. Bir hal alacak, çünkü ekran editörüne falan burayı kullandığımızı söylemedik. Bu sonraki iş. Şimdi de geri dönmek için, aynı komutu, X yerine 0 koyarak yazın. Yazdığınızı ekranda görmeyeceksiniz ama, eğer yazarken bir hata yapmamışsanız, RETURN'a basar basmaz, ekran eski haline gelecektir.
VIC'ın adresleme bölgelerini böylece kaydırdıktan sonra, sıra geldi video RAM, grafik hafızası ve karakter üretecinin bu seçtiğimiz 16K içindeki yerini belirlemeye. Bu işlem için, daha önce de gördüğümüz, VIC'ın 24 numaralı yazmacını kullanacağız. Video RAM'ının 1K gerektirdiğini daha önce görmüştük. Yani 16K'nın içinde, 16 değişik yerden başlayabilir. Bunlara sırasıyla 0-15 arası numaralar verirsek, aşağıdaki komut ile, 16K'nın içindeki istediğimiz bir bölgeyi video RAM'ı olarak kullanabiliriz. Bu kez, 0-15 arası ekran numarasını, 24. yazmacın 4-7 numaralı bit'leri saklamakta.
POKE 53248 4 24,PEEK(53248 * 24)
AND 15 OR X*16
Komuttaki yerine 0-15 arası sayılar koyacaksınız. Normalde 0 numaralı ekran kullanılır. Denemeler yaparsanız, ekranın gene karmakarışık olduğunu göreceksiniz. Bunun da sebebi, ekran editörünün yaptığımızdan haberi olmaması. Grafik hafızasını da, gene 24 numaralı VIC yazmacını kullanarak kaydırıyoruz. Grafik hafızası 8K yer kapladığına göre, 16K'nın içine iki kere sığabilmekte. Bunlara 0 ve 1 numaralarını verirsek sadece bir bit'e gerek duyduğumuzu görürüz. İşte o bit, 24. yazmacın üçüncü bit'i. Bu iş için de bir formülümüz var.
POKE 53248 4 24,PEEK(53248 4 24)
AND 247 OR X*8
*X” yerine ya 0 ya da 1 gelecek. Diyelim ki, VIC'ın başlangıç adresini, CIA 2 ile oynayıp $8000 yaptınız. Yukarıda da X yerine 1 koydunuz. Bu durumda grafik hafızası, $8000-$BFFF arasının ikinci yarısında, yani $A000-$BFFF arasında olacaktır.
Tüm hafızayı göz önüne aldığınızda, C64'ün içine 8 grafik sayfası sığacağını görürsünüz. Ancak bunlardan ilki, C64'ün yaşam fonksiyonlarını kontrol eden bilgilerin bulunduğu $0000-$0800 arasını da kapladığı için kullanılamaz. Geriye kalan 7 tanesi bize yeter...
Bugünlük son olarak da, karakter üretecinin kaydırılmasına bir bakalım. Karakter üretici 2K'lık iki bloktan oluşur. Daha önce de belirttiğimiz gibi, bunlar büyük ve küçük harf setlerini içerir. Toplam 4K'lık bu set, 16K'nın içinde 2K'lık adımlarla kaydırılabilir. Dolayısıyla, 16K'nın içinde, karakter setinin başlayabileceği toplam 8 değişik adres vardır. Bunlara 0-7 arası rakamlar verirsek, aşağıdaki formülü kullanarak 24. yazmacın 1-3 numaralı bit'lerini etkileyebilir ve karakter setini kaydırabiliriz.
POKE 53248 4 24,PEEK(53248 * 24)
AND 241 OR X*2
Burada da, *x” yerine 0-7 arası sayılar koyacaksınız. Dikkat ettiyseniz, 24 numaralı yazmacın üçüncü bit'i hem grafik ekranını hem de karakter setini kaydırırken kullanılıyor. Yani bu iki bölgenin kayması birbirine bağımlı. Dikkat etmeniz gereken bir nokta daha var. Tüm bu kaydırma işlemleri sırasında, eski bölgedeki bilgiler yeni bölgeye taşınmıyor. Eski bölge içeriğini olduğu gibi saklarken, yeni bölge yeni bilgiler almayı bekliyor. Bu aylık da bu kadar. Gelecek ay, daha zevkli konulara başlıyoruz. Bunca teknik bilgiden sonra, grafik ekranının yapısını, noktaların nasıl oluştuğunu, nasıl renklendirildiğini, temel geometrik şekil çizildiğini görmeye başlayacağız. Hepinizin güzel bir ay geçirmesini dilerim... O