Atölye
Vedat Hallaç bu yavaşlıktan ne gibi yollarla kurtulabileceğimiz konusu gibi... Bu ayın konusuna girmeden önce geçen ayki bilgilerimizi kısaca bir tazeleyelim isterseniz. Rutinimiz beş ayrı komut ekliyordu BASIC'e. Bu komutlar sırasıyla şu adreslerde bulunuyordu: SYS 49152: Grafik ekranını açma. SYS 49155: Normal ekrana geri dönme. SYS 49158: Ekranı temizleme. SYS 49161,R1,R2,R3: Grafik ekranının renklerini belirleme. R1, R2, R3 sayıları ile 0-15 arası renkleri seçebilirsiniz. SYS 49161,X,Y,RN: X,Y koordinatlarına R1,R2,R3 renklerinden biri ile nokta koyma veya 0 ile buradaki noktayı silme. İlk dört rutini geçen sayıdaki yazıda detaylı olarak ele almıştık. Sadece grafik ekranıyla ilgili bilgilerinizi tazelemeye yetecek kadar yer olmadığını düşünerek nokta koyma ve silme işini bu aya ertelemiştik. Şimdi grafik ekranını multi-color modda kullanmanız için gereken tüm bilgileri ve formülleri vereceğim: Her şeyden önce grafik ekranının bellekte nasıl durduğunu göreceğiz. Bunun için de “card” veya “kart” kavramını size tekrar hatırlatmam gerekecek. 'Kart', ekrandaki 8x8'lik alanların her birine verilen addır. Bu alanlar hep 8'in tam katı olan koordinatlardan başlar. Grafik alanını bu şekilde kartlara ayırmamızın sebebi, her kartın, içindeki noktaların hepsinin rengini belli kurallar çerçevesinde kısıtlamasıdır. Yani ekrandaki her 8x8'lik alana birbirinden bağımsız renkler verebilirsiniz. Şekil 1'e bakarsanız ekranın sol üst köşesinde yer alan kartlardan birkaçını görebilirsiniz. Grafik ekranı 320 x 200 boyutunda olduğunu da göz önüne alarak tam 40*25 = 1000 adet kartımız olduğunu görebiliriz. Dikkatli arkadaşlarınız bu kartların sayısının karakter ekranına basılabilecek karakter sayısıyla aynı olduğunu fark etmişlerdir. Evet, bu bir tesadüf değil. Çok renkli grafik ekranı kullandığınızda hizmetinize sunulan üç renkten biri renk RAM'i olan $D800'daki 1000 baytta, diğer ikisi de o an aktif olan video matrisinde yer alan 1000 baytta saklanır. AMICA PAINT gibi programlar on altı rengin bu koşulları yok etmeden kullanılmasını sağlarken, PAINT MAGIC gibi bazı programlar kullanıcıyı seçilmiş üç renkle sınırlarlar. İsterseniz şimdi de bu kartların hangi baytlardan oluştuğunu ve çok renkli modda renklerin neye göre verildiğini görelim. Grafik ekranları, sadece $0000, $2000, $4000 gibi 8K'lık alanların en başında yer alabilirler. Bu alanın ilk baytı, grafik ekranın sol üst köşesine ait sekiz noktanın değerini içerir. Bir bayt sekiz bitten oluştuğuna göre bu bayta vereceğimiz değere göre bu noktaların ayrı ayrı veya hep birlikte seçilmelerini ayarlayabiliriz. Bir bayttaki değerin noktaların açık veya kapalı olmasını nasıl sağladığını şekil 2'de görebilirsiniz. Şekilde de belirtildiği gibi her noktanın bir “ağırlığı” vardır. Baytta tutulan sayı ise görünen bitlerin ağırlıklarının toplamından başka bir şey değildir. Tabii ki bu ağırlıklar kafadan atılmış değerler değildirler. En sağdaki noktaya 0. nokta, en soldaki noktaya da 7. nokta dersek, ağırlık = 2^nokta no formülünden nokta ağırlıklarını hesaplayabiliriz. Grafik ekranının başlangıcından bir sonraki adreste tutulan bayt, kartın ikinci piksel satırının değerini tutar. Böylece grafik adresinin ilk sekiz baytının değerleriyle oynayarak sol üst köşedeki kartın noktalarını açıp kapayabiliriz. Dokuzuncu bayt ise, çoğunuzun tahmin edeceğinin aksine, bu sol üstün bir altındaki kartın değil, bir sağındaki kartın en üst sekiz noktasının değerlerini ayarlamaya yarar. Sol üst kartın bir altındaki karta ait noktalarla oynamak içinse grafik adresinin başlangıcından 320 sonraki baytla oynanması gerekir. Grafik başlangıcını $2000 (8192) kabul edersek, şekil 3'e bakarak hangi baytın hangi kartı etkilediğini toplu olarak görebiliriz. Aslında şu ana kadar pek fark etmediniz ama koordinatı verilen bir noktanın adresini ve bu adrese yazılması gereken baytı bulabilecek kadar bilgi birikimimiz oldu şu anda. Haydi hep birlikte adresin bulunması için gereken formülü çıkartalım. Bunun için ilk olarak Y koordinatıyla ilgili özel birkaç durumu inceleyip genel bir Y koordinatı formülüne varmaya çalışalım: 1. Y koordinatı 0 ile 7 arasındaysa, adres başlangıçtan 0..7 bayt ileride yer alıyor. 2. Y koordinatı 8 ile 15 arasındaysa, adres başlangıçtan 320..327 bayt ileride yer alıyor. 3. Y koordinatı 16 ile 23 arasındaysa, adres başlangıçtan 640..647 bayt ileride yer alıyor. Demek ki Y koordinatının 8'de birinin tam sayı kısmı kaç kere 320 bayt ilerleyeceğimizi belirliyor: adresY = 320*INT(Y/8)
Ve Y koordinatının geri kalan kısmı da bu adresten kaç bayt ileri gideceğimizi belirliyor: adresY = adresY + (Y-INT(Y/8)*8) Ya da daha kısa bir şekilde: adresY = adresY + (Y AND 7) Formülün Y kısmını bir araya toplarsak: adresY = 320*(INT(Y/8)) + (Y AND 7) Formülün X koordinatını ilgilendiren kısmına geçmeden önce çok renk modunda renklerin nasıl oluştuğunu ve bu sistemin X koordinatını ne şekilde etkilediğini incelemeliyiz. Demin anlattığım gibi, kartların baytlarının değeriyle oynayarak sekiz noktanın açık ya da kapalı olması gerektiğini belirleyebiliyoruz. Ama ondan daha önce de “çok renk modunda üç renkten birini seçebiliriz” demiştim. Aslında bu da tam doğru değildir. Çok renk modundayken üç renkten birini ya da renksiz konumunu (yani “nokta görülmüyor” durumunu) seçebilirsiniz. Bu da her noktaya dört değişik değer verebilmemizi gerektiriyor. Eğer nokta değeri sıfırsa nokta kapalı oluyor, birse birinci, ikiyse ikinci ve üçse de üçüncü rengi kullanıyor. Tabii ki hepinizin tahmin edebileceği gibi tek bir noktanın açık-kapalı olma durumuyla bu dört koşulu birden kontrol etmemiz imkânsız. Bu yüzden çok renk moduna geçtiğinizde noktalar, şekil 4'de de görebileceğiniz gibi, ikişer ikişer gruplanır ve bu ikili grupta bulunan bileşik değere göre renk alır. Eğer iki nokta da kapalıysa bileşik nokta kapanır, ikisi de açıksa üçüncü renk verilir, biri açık biri kapalıysa açık olanın hangisi olduğuna bakılarak birinci veya ikinci renk verilir. Aşağıdaki kısa tabloda bu durumları görebilirsiniz: KK: Nokta görülmez
Şekil-1 Grafik ekranını oluşturan kartlardan biri
KA: Birinci renk. AK: İkinci renk. AA: Üçüncü renk. Bu durumu da ikili noktaların her bir noktasına ağırlıklar vererek özetleyebiliriz. Sağdaki noktanın ağırlığı 1, soldakinin iki olursa, ve açık konumunda bu değeri dikkate alırsak: KK=0 KA=1 AK=2 AA=3 Gördüğünüz gibi ekranın x uzunluğunu 320'den 160'a düşürerek her noktanın dört adet değere sahip olabilmesini sağladık. Tabii ki bu durumda her kart 8x8'lik noktalardan değil 4x8'lik geniş noktalardan oluşmaya başladı. Şimdi formüllerimizi oluştururken bu geniş noktalara göre düşünmek çok daha mantıklı ve kolay olacaktır. Formülü oluşturmak için demin yaptığımız gibi özel durumları inceleyip birleştirelim ve genel sonuca varalım (şekil 3'ü incelemek bu konuda size yardımcı olabilir): 1. X koordinatı 0..3 arasındaysa nokta başlangıç adresinde yer alıyor. 2. X koordinatı 4..7 arasındaysa nokta başlangıç adresinden sekiz ileride yer alıyor. 3. X koordinatı 8..11 arasındaysa nokta başlangıç adresinden on altı ileride yer alıyor. Sonuç olarak, 'X koordinatının dörtte birinin tam sayı kısmı bize adresten kaç kere sekiz bayt ileri gideceğimizi belirtir.” diyebiliriz. Yani bu işlemi formüle edersek: adresX = 8*INT(X/4) yazabiliriz. X ve Y koordinatlarıyla belirlenen noktanın adresinin: ADRES = adresY + adresX olarak hesaplanabileceği ise kolayca görülebilir. Şimdi yapmamız gereken, ve bizi oldukça oyalayacak tek görevimiz kaldı. O da X koordinatından artan değere göre demin hesapladığımız adresteki baytın hangi noktaları açık konumuna getireceğimiz. Bu hesabı ilk bayt üzerinde yaparsak hiçbir değişiklik yapmadan diğer baytlara da uygulayabiliriz. Yani Y koordinatı sıfır, X koordinatı da 0 ile 3 arasında bir değer alabilmeli. Demin de söylediğim gibi X koordinatı, geniş noktanın alacağı renge göre bileşik noktaların açık veya kapalı olmasını ayarlar. Başlangıçta kolaylık olsun diye üçüncü rengi basacağımızı, yani bileşik noktaların ikisini de açık konumuna getireceğimizi düşünelim ve bu bileşik noktaların yerini bulalım. Tabii ki dört durum olduğuna göre dördünü de inceleyip genel bir formüle varabiliriz: 1. X=0 ise Değer=3*2^6 2. X=1 ise Değer=3*2^4 3. X=2 ise Değer=3*2^2 4. X=3 ise Değer=3*2^0 Dikkatimizi ilk çeken şey, X'in en büyük değerini aldığı yerde 2'nin en küçük üssünü alması. Bu durumda X'i üçten çıkarmalı ve buna göre bir işlem yapmalıyız. İkinci olarak da en sağdaki üslerin hepsinin (3-X)'in iki katı olması. Sonuç olarak: Değer = (Renk) * 2^(2*(3-X)) formülünü çıkarabiliriz. İsterseniz X'in alabileceği üç değer için bu formülün sonucunu bulun. Özel durumlarımızı sağladığını göreceksiniz. Artık geriye çok fazla şey kalmadı yapmamız gereken. İlk olarak bu değeri hazırda olan değerin yerine geçirmeliyiz. Bunu yaparken de kendisini ilgilendirmeyen noktaların hiçbirine (yani diğer üç noktaya) dokundurtmamalıyız. Eğer bu değeri doğrudan doğruya yazmaya kalkarsak diğer üç nokta otomatik olarak silinecektir. Diğer noktalara dokunmamak için ise gerekli adresteki baytı alıp, bu bileşik noktaya ait bilgileri o bayttan silmeli ve bileşik noktayı bayta eklemeliyiz. Bilgi silmek için en uygun komut AND'dir. Eğer baytın alabileceği maksimum değerden yani 255'den demin bulduğumuz Değer'i çıkartırsak, baytta bileşik noktamızın dışındaki tüm bileşik noktaların açık konumda olduğu durumu elde ederiz. Bu değeri adreste saklı olan baytla AND'lediğimiz zaman bileşik noktamıza ait bilgiyi bayttan silmiş ve başka hiçbir şeye dokunmamış oluruz. Bayta bilgiyi eklemek için ya toplama kullanırız, ya da makine dilinde toplamanın amcaoğlu olan ORA komutunu kullanırız. Boş olan noktalar üzerine yapılacak bir toplama ve ORA komutu aynı sonucu verecektir, fakat ORA komutu toplamadan çok daha hızlı işler. İşimiz bitince de baytı geri yazarız. Seçilen renge göre bileşik baytın değerini hesaplamaksa makine dilinde tüm bu hesapları karıştıracak. Bu yüzden formülleri kullanmak yerine daha hızlı çalışacak olan sağa-sola kaydırma ve tablodan okuma metodlarını kullanacağız. Bu yüzden renk konusunu formüle etmekle burada uğraşmayacağım. Şimdi asıl yapmamız gereken şey, bütün
bu formülleri makine dili bir programda uygulamaya sokabilmek. Tabii ki pek de kolay bir iş olmayacak, ama birbiriyle çarpılan değerlerin birinin sabit olması işlerimizi inanılmaz derecede kolaylaştıracak. Rutinin en başında kullanıcının verdiği X,
Y ve renk değerlerini alıyoruz. Eğer X, 0 ile 160; Y, 0 ile 200 ve renk 0 ile 2 arasında değilse hemen ILLEGAL QUANTITY ERROR verdiriyoruz. Tüm değerler izin verilen sınırlardaysa formülümüzü uygulamaya başlıyoruz: İlk iş olarak Y'nin sekizde biriyle 320'yi çarpmalıyız, Y'yi sekize bölmek çok kolay, Y koordinatını akümülatöre aldıktan sonra üç kere LSR kullanırsınız, olur biter. Asıl zor kısmı bu sayıyı 320 ile çarpmak. İlk olarak 320'yi çarpanlarına ayırmalıyız: 320 = 64 * 5
128 | 32 | 16 8 4 | 2 1
128 Ml
1 |
5s
Şekil 2 Bir bayt'ta bulunan değerin her noktayı nasıl kontrol edeceğini burada
görebilirsiniz.
Şimdi yapmanız gereken şey sonuç olacak adresi tutacak bayt çiftini ayarlamamız gerekli. Bellekte ardışık iki adres seçin ($0000-$00FF arasında) ve bunları sıfırlayın, Y/8'i buraya yazın ve iki kere sola kaydırın. Sola kaydırmak iki ile çarpmak anlamına geldiği için sayıyı dörtle çarpmış olduk şimdiden,
Daha sonra A'da bulunan Y/8'i bu iki bayta ekleyin: CLC ADC AD STA AD LDA 0 ADC STA İkinci toplama size anlamsız gelebilir, ama C bayrağının değerini de A + 1'e eklediğiniz için hayati önemi vardır. Bundan sonrası çocuk oyuncağı. İki baytlık bir alanı tam altı kere sola kaydırmak işi halledecektir: ASL AD ROL AD+1 ASL AD ROL AD+1 «4 kere daha... Buradaki ASL AD komutuyla AD adresini sola kaydırıp ilk bitini sıfır yapıyoruz, ve ROL AD+1 ile de sola kaydırıp ilk bitini AD'nın kaydırılmadan önceki son bitine eşitliyoruz. Bir de bu değere Y'nin ilk üç bitini eklersek (Y AND 7'yi bu yüzden tercih etmiştim) AdresY kısmı tamamlanmış olacak, Y koordinatını akümülatöre alın, AND $7 komutuyla ilk üç bitini alırız, ve deminki ADC komutlarıyla adrese bu değeri ekleriz. İnanılmaz derecede kolay oluyor, değil mi? Şimdi de adresX'i hesaplayıp AdresY'ye ekleyelim. X/4 için X koordinatını içeren akümülatörü toplam iki kere sağa kaydırmalıyız. Daha sonra da bunu 8'le çarpmak için üç kere sola kaydırmalıyız. Toplam olarak
X değerini bir kere sola kaydırmak bu işlemi yapacak gibi görünebilir, ama sola kaydırmadan önce ilk iki biti temizlemeliyiz, çünkü sağa kaydırma işlemi sırasında ilk iki bitin değeri sıfırlanacaktır. Bu yüzden önce AND $FC komutuyla ilk iki biti sıfırlamamız, daha sonra ASL komutu yardımıyla ikiye çarpmalıyız. Şu anda çok dikkatli olmalıyız, çünkü carry flag'inde son hesabın en yüksek değerlikli biti var. Yapacağımız işlem carry'yi etkilerse bu bilgiyi kolaylıkla kaybedebiliriz. Bu yüzden ilk olarak A + 1'e sıfır eklemeliyiz. Bu biti korumaya aldıktan
$2000
$2001 $2002
$2003
$2140
Şekil 3 $2000'den başlayan bir ekranın kart boyutlarının adresleri
sonra gönül rahatlığıyla A'yı adresY'ye ekleyebiliriz. Eğer herhangi bir şeyi atlamadıysak şu anda adresin değerini hesaplamış olmalıyız. Buraya kadar yaptığımız işlemleri programımızı inceleyerek kontrol edin. Eğer anlamadığınız yerler olursa da bana yazın. Şimdi son iş olarak gerekli biti hesaplayıp AdresY'deki yerine yazmak kaldı. Yazının başlarında bit hesaplamak için bir sürü formül hesapladık, ama bu formülleri kullanmayacağız. Bunun yerine bit çiftini baytın en soluna koyacağız ve (X AND 3)'ün sonucu kadar bu bit çiftini sağa kaydıracağız (ikişerli olarak). Bu işlemin sonucu olarak X AND 3'ün değerleri şu sonuçları verecektir:
XAND3-0 - 4011000000 XAND3-İ - 9000110000 XAND3-2- 77000001100 XAND3-3- 000000011
(% işareti sayıların ikilik düzende olduğunu belirtir.) İlk olarak X AND 3'ün değerine göre, bir tablodan değer okuyup bu değerle adresY'yi AND'lemeliyiz. Bu tabloda şu değerler bulunmalıdır:
Buraya kadar olan yerde gerekli adreste bulunan noktayı sildik. Şimdi bu adrese koyulacak noktayı, kullanıcının seçtiği rengi verecek bit çiftiyle doldurmalıyız. Bu bit çiftini de bir tablodan okuyoruz: 11000000, 01100000, 00011000, 00000110. Şimdi de okunan bu bit çiftini X AND 3'ün değeri kadar sağa kaydırmalıyız (her seferinde iki bit kaydırarak). Küçük bir döngüyle bu kaydırma işini de hallettikten sonra adresY'de bulunan değeri alıp hesapladığımız bu değerle ORA'larsak ve adresY'ye geri yazarsak noktayı da basmış oluruz. Pek yerimiz kalmadığı için rutinleri detaylı olarak inceleyemeyeceğiz, ama bu işi yapabilecek kapasitede olduğunuza inanıyorum. Eğer bu programın çok yavaş nokta koyduğunu düşünüyorsanız, kullanıcıdan data almanın yeni bir yöntemini bulmalısınız. Benim burada kullandığım yöntem her data almak istediğinizde üç adet ROM rutini kullanıyor, ve bu rutinlerin hızlı olduğunu kimse söyleyemez. Bu rutinleri kullanmamak için ya doğrudan doğruya POKE'larla çalışan bir program yaparsınız ya da yeni ROM rutinleri bularak hızlı bir şekilde data okuyabilirsiniz. Eğer hesaplarda hız istiyorsanız, en uygun kısmı 320 ile çarpma rutininin sonuçlarını bir data tablosu olarak hazırlamak (düşük değerlikli baytlar bir yerde, yüksek değerlikler başka bir yerde), sonra da Y/8'inci elemanı doğrudan doğruya bu tablodan okumak size oldukça zaman kazandıracaktır. Aslında uygun tablolar hazırlayarak tüm hesaplamalardan kurtulabilirsiniz, ama bu şekilde oldukça fazla bellek harcarsınız. Aslında bu konuda daha uzun yazmak isterdim, ama yerim dolduğu için bu aylık burada kesmem gerekiyor. Gelecek ay görüşmek üzere hoşçakalın.
Şekil 4: Noktaların ikişer ikişer gruplanması