Makine Diline Merhaba (9)
Hatırlayacaksınız geçen bölümün sonunda direkt modda şu satırı girmiştik:
SYS 50176: POKE 198,0: WAIT
198,1: SYS 50192
Satırı verdiğinizde ekranda renk cümbüşü içinde, randomize şekiller göreceksiniz. Herhangi bir tuşa dokunduğunuzda ise normal yazı ekranına döneceksiniz. Grafik modunda iken gördüğünüz saçma ve düzensiz şekiller, bilgisayarın ilk açıldığında oluşmuş rastgele değerlerdi. Demek ki şimdiki görevimiz, hem grafik ekranını hem buna ait renk ekranını silmek olacak. BASIC'te bu sorunu çözmek için, içinde POKE komutu bulunduran bir döngü yetecektir. Grafik ekranımızdaki her noktayı silmek istiyoruz. Bunun için bu bölgedeki her biti, daha da genellersek her byte'i sıfırlamamız gerekiyor. Döngümüzün başlangıç ve bitiş sınırları $E000'dan, SFFFF'e kadar olacaktır. (Aslında, bitiş adresimiz $FFFF değil $FF3F olacaktır. Çünkü grafik ekranımız 8192 değil 8000 byte büyüklüğünde. Fakat, bu noktada bunun bir önemi yok!) BASIC'- teki satırımız şöyle bir şey olacaktır:
FOR 1-57344 TO 65535:POKE
1,0:NEXT
BASIC'te mümkün olabilecek en yüksek hızı elde edebilecek şekilde formüle edilmiş bu satırın yürümesi aşağı yukarı 30 saniye sürüyor. Bu programın makine dilindeki karşılığının çok daha hızlı yürüyeceği aşikâr,
Hatırlayacağınız gibi, biz zaten bir döngü programlamıştık. O örneğin yaptığı işlem, karakter setini olduğu gibi ekrana çıkarmaktı. Tüm döngü
256 kere dönülüyordu; bu yüzden bu problemin çözümünde X- ve Y- indeks registerleri rahat rahat yetiyordu. Fakat, biz burada 8000 byte'ı silmek istiyoruz. İlk akla gelen çözüm olarak içiçe geçmiş iki döngü kulla: nabileceğimiz akla geliyor. Her zamanki gibi, gene önce BASIC'teki örneğimiz:
AD
FOR X-0TO31
FOR Y-0TO255
POKE AD *4 Y,0
NEXT Y
NEXT X
Basit bir deyişle, 8192 byte'lik bölgeyi 32 tane 256'şar byte'lik sayfalara (Page) ayırdık. Y-döngüsü içinde her seferinde 256 byte siliniyor. Bunun hemen arkasından AD değişkeninin değeri 256 kadar artırılıyor. Böylece bir sonraki sayfaya geçilip, oranın da silinmesi sağlanıyor. Büyük döngü 32 kere dönülüyor. Sonuç olarak 8192 byte'ı siliyoruz. Burada, fazladan silinen 192 byte'ın hiç bir önemi olmadığı için, program basit ve sade olarak kurulmuştur. Şimdi, bu döngüleri bir de makine dilinde formüle edelim:
100- .EÇ AD — $E000
110- LDA O
120- LDX O
130- LDY * O
140- STA AD,Y
150- INY
160- BNE ETİKETİ
170- AD 4 $0100
180- INX
190- ;X 32 OLDU MU?
200- ;DEĞİLSE 130'UNCU
SATIRA SIÇRA
Şimdiye kadar edindiğimiz tecrübelere göre üç aşağı, beş yukarı bö: le bir şey yazmamız gerekir. Hattâ, ETİKETİ 'in 140'ıncı satırın başında yeralacağını bile şimdiden söyleyebiliriz. Fakat, AD değişkeninin değerinin böyle artmayacağı besbelli. Şimdi geçen derslere dönüp indirekt indeksli adresleme tipini hatırlayalım. Burada, asıl adres sıfırıncı sayfadaki iki byte'lik göstergeç ve Y-registerinden elde ediliyordu. Bu kullanımla göstergecin değerinin $100 artırmak da mümkün hale geliyor. X-registerinin 32 değeri ile karşılaştırılması ve eşitsizlik durumunda 130'uncu satıra sıçraması için kullanmamız gereken komutları da hatırladınız herhalde?..
100 - ;AD'YE $E000
DEĞERİNİ
ALMA
110 - LDA : O
120 - LDX O
130 - ETİKET2 LDY * O
140 - ETİKETİ STA (AD),Y
150 - INY
160 - BNE ETİKETİ
170 - - AD 4 $0100
Rİ
180 - INX
190 - CPX # 32
200 - BNE ETİKET2
İndirekt indeksli adreslemede, AD değişkeni iki byte'lık bir göstergeç olmalı; yukarıdaki gibi absolut değer olmamalı. Sıfırıncı sayfadan kendimize uygun iki (boş!) byte bulmamız gerekmekte. $FA ve $FB adresleri, bu iş için ideal! Döngüye girmeden önce göstergecimiz $E000 değeri ile yüklenmelidir. Bunun için, düşük değerli byte (800) SFA'ya, yüksek değerli byte (SEO) SFB'ye gitmelidir. Dikkatli okurlarımız hemen farketmiş olacaklardır; göstergecimizin değerini $100 artırmak için yüksek değerli kısmı barındıran byte'ın değerini bir artırmak yetecektir. Programımızın sonuna bir de RTS ekledik mi, hiç bir eksiğimiz kalmayacak:
90- .BA $C420
100- LDA # < ($E000)
102- STA $FA
104- LDA # >($E000)
106- STA $FB
110- LDA O
120- LDX O
130- ETİKET2 LDY : O
140- ETİKETİ STA ($FA),Y
150- INY
160- BNE ETİKETİ
170- INC $FB
180- INX
190- CPX # 32
200- BNE ETİKET2
210- RTS
Bu parçanın, başlangıç adresi $C420'dir. Hatırlayacağınız gibi, ilk iki programımızın hemen arkasından gelen boş adresti bu. Programımızı assembler'dan geçirdikten sonra şöyle bir görüntüye bürünür:
C420 A9 00 LDA * $00
C422 85 FA STA $FA
C424 A9 EO LDA $E0
C426 85 FB STA $FB
C428 A9 00 LDA $00
C42A A2 00 LDX $00
C42C A0 00 LDY * $00
C42E 91 FA STA ($FA),Y
C430C8 INY
C431 DO FB BNE $C42E
C433 E6 FB INC $FB
C435 E8 INX
C436 E0 20 CPX $32
C438 DO F2 BNE $C42C
C43A 60 RTS
Bu programı biraz daha kısaltmak mümkündür. Mesela, register'lerin hepsini ilk satırda sıfırlamak ve bunu transfer komutlarıyla yapmak (TAY, TAX gibi); bunlardan başka, döngünün içinde X-registerini 0'dan 32'ye değil de 32'den 0'a doğru geriye saydırırsak döngüden çıkmak için CMP $00 komutuna gerek kalmayacağı için, hem programın uzunlu- Bundan tasarruf edeceğiz, hem de kısmen hız sağlayacağız. Fakat, bu değişikliklerle programın veriminde gözle görülür bir artış sağlamayacağı için bu değişiklikleri sadece öneri ve bilgi olarak bırakalım. İlk iki programımızın da o anda hafızada bulunduğunu varsayarak ufak bir deneme yapalım. Aşağıdaki satır şimdiye kadar yazdığımız programların bize güzel bir gösterisini yapacaktır:
SYSS0176:POKE198,0:WAIT198,
1:SYSS0192
Satırı verip RETURN tuşuna bastığımızda ekran grafik moduna geçer ve bekler. Herhangi bir tuşa bastığımızda ise ekran silinir. Tabii, bu işlem gözümüzle takip edemeyeceğimiz bir hızda gerçekleşecektir (tüm silme işlemi 90 ms kadar sürecektir). BA-SIC'te yazdığımız program bu iş için yarım dakika civarında zaman harcıyordu. Bir kere daha herhangi bir tuşa bastığımızda ise grafik modunu terkederiz. Şu seviyede bir de renk bilgisini silecek programı yazmak o kadar zor gelmese gerek. Programı yazmak kolay da, önce renk ram'inin silinmesi ile ne kasdettiğinizi görmeniz gerekiyor. gerekiyor. Çünkü biliyoruz, renk ram'inin aslında silinecek bir durumu yoktur. Yapılacak işlem, bu belleği ilk değerlemek, yani başlangıç renk değerlerine geri getirmek. Tuna Ertemalp'in GRAFİK adlı yazı dizisini takip edenleriniz bilirler: Renk ram'ine ait her bir bellek elemanı 8*8'lik bir grafik alanı için gerekli olan renk bilgisini içerir. Renk bilgisi şöyle oluşturulur: Yanık bitler için üst taraftaki dört bitin belirttiği renk, sönük bitler için alt taraftaki dört bitin belirtti Yani 8*8'lik bir bölge içinde toplam iki renk kullanabiliyoruz. Bu renkler de renk ram'inin o bölgeye karşılık gelen byte'ıinin içeriğinde saklı. Buna göre, biz tüm renk ram'ini $10 değeriyle doldurursak, sönük bitlerde siyah, yanık bitlerde beyaz renginin görünmesini sağlarız. Biz bu bilgiyi programın bir parçası değil de, bir parametre olarak bırakalım. Yani, biz altprogramımızı hangi akü değeriyle çağırırsak, renk ram'i o değerle doldurulacaktır. Başlangıç adresi olarak $C440'i seçelim. Programımız şöyle bir şey olacaktır:
100 — AD -
110 — ADI-
120 — $C440
130 — LDY
140 — STY ADI
150 — (SCO00)
160 — STY AD
LDX 4
180 —ETİKETİ STA (AD),Y
190 — INY
200 — BNE LABELİ
210 — INC ADI
220 — DEX
230 — BNE LABELİ
340 — RTS
Gördüğünüz gibi bir önceki programda yaptığımız önerilere burada uyduk. Yani programlama stilimizi geliştiriyoruz. Şimdi, yazdığımız bu program modülünü deneyebiliriz.
Başlangıç adresimiz $C440, yani 50240 olacaktır. Fakat, programımızı SYS 50240 komutuyla başlatmadan önce akümülatöre renk bilgisini koymamız gerekiyordu. Bunun için, önceden POKE 780,16 komutunu veririz. Böylece, ekranda istediğimiz siyah ve beyaz kombinasyonlarını yaratırız. Yazdığımız tüm modüllerin ortaklaşa çalışmalarını görmek için direkt modda:
SYSS0176:POKE198,0:WAIT198,1:
SYS50208:POKE198,0:WAIT198,1:
SYS50240
satırını veririz. Önce grafik moduna geçeriz, ardından ekran silinir, bunun erhangi bir grafik programı farklı görevler yerine getiren değişik altprogramlar içerir.
arkasından renk ekranı silinir. Normal moda geçmek için (RUN/STOP-RESTORE) tuşlarını kullanın.
Şimdiye kadar dört adet değişik altprogram yaptık. Hepsinin de başh başına bir görevi vardı ve bu görevlerini başarıyla yerine getiriyorlar. Fakat, grafik denince esas akla gelen altprogram, herhangi bir yere nokta koyabilen veya silebilen bir altprogram oluyor. Bu programı yaratırken birçok değişik programlama tekniğini kullanabilmek için de fırsat bulacağız. Örneğin, mantıksal ve aritmetik işlemlerin kullanımı gibi. Her zaman yaptığımız gibi, programlama tekniklerini bir an unutarak yapacağımız işin ana hatlarını belirleyelim. Bunun için, başvuracağımız ilk kaynak gene Tuna Ertemalp'in, GRAFİK adlı yazı dizisi olacaktır. Her ne kadar, grafikle ilgili birçok işlemin detayları o yazıda bulunsa da, biz gene de burada uğraştığımız olayın ana hatlarını belirleyeceğiz. Grafik noktalarının hafızada nasıl yerleştiklerini incelemekle işe başlayalım. Grafik noktaları her ne kadar normal yazı ekranından daha sıksa, gene de genel yapı olarak çok büyük benzerlikler vardır. Bunların başında, grafik ekranının da, normal yazı ekranı gibi 40 kolon ve 25 satıra bölünmüş olmasıdır. Aşağıdaki şemada, grafik ekranına ait bellek elemanlarının hangi gruplamalarla biraraya geldiğini gö-
0.kolon 1.kolon... 39.kolon o 8 312 1 9 313 10 314 3 11 315 0.satır 4 12 316 5 138 317 6 14 318 T 15 319 7680 — 7688 — 7992 7681 7689 7993 7682 — 7690 799â 7683 — 7691 7995 24. satır 7684 7692 7996 7685 — 7693 — 7997 7686 7694 7998 7687 7695 7999
Yani yazı ekranında bir karakter için ayrılmış olan bölge, 8 byte'lik grupları barındırıyor. Her bir byte'- ın sekiz biti ise grafik ekranının tek tek noktalarını oluşturuyor. Yedinci bit en soldaki, sıfırıncı bit ise en sağdaki noktayı oluşturuyor. Şematik bir gösterimle:
76543210
10001100
Byte'ımızın içeriği $8C yani 10001100 olduğu zaman, sol taraftan saydığımızda, birinci, beşinci ve altıncı noktalar yanık olacaktır. Diğerleri sönük kalacaktır. Grafik ekranımızdaki noktalara erişmek için bunların X- ve Y-koordinatlarını kullanacağız. X-koordinatımız 0'dan 319'a, Y-koordinatımız 0'dan 199'a kadar uzanacaktır. Şimdi biz öyle bir algoritma bulmalıyız ki, bizden bu koordinatları aldığında bize gerekli olan bellek elemanının adresini ve bu eleman içinde bizim uğraşmamız gereken bitin numarasını hesaplasın. Bu işlemleri önce BASIC'te formüle edelim.
ZI