Bütün programcıların ilk kabusu zx spectrum'un ekran haritasının garipliğinden kaynaklanır. Bir üst başlıkta hafıza'nın yapısını şöyle özetlemiştim:
[--ROM: 0-16383 --][--BITMAP: 16384-22527--][--ATTRIBUTES:22528-23295--][--BOŞ ALAN (Basic)--][--UPPER RAM:32768-65536--]
Bu başlıkta BITMAP + ATTRIBUTES bloklarını kapsayan "Ekran Tamponu"nu anlatmaya çalışacağım.
* Zx Spectrum'da sadece 1 ekran modu vardır.
* Zx Spectrum'da ekran çözünürlüğü 256x192 pikseldir.
* Zx Spectrum Paleti 8 renk barındırır. Fakat bu palet Amiga'daki EHB modu gibidir. 8 renk vardır, ve her rengin bir ton açığı da mevcuttur.
* Ekranın pikselleri oluşturan kısmı ile renklerin tutulduğu kısımı ayrıdır. Bu bölümlere Bitmap ve Attrib bölümleri diyelim. Bitmap 16384'de başlar, attrib 22528'de.
* Bitmap bölümü 6144 byte büyüklüğündedir, doğrusal değildir ve kendi içinde 2048byte'lık 3 eşit parçaya bölünmüştür.
* Attrib bölümü 768 byte uzunluğundadır ve doğrusal bir dizilimi vardır.
* Her 8 pixel 1 byte ile ifade edilir. Ekranın genişliği 256 pixel olduğuna göre, her satır 32 byte alan kaplar.
Zx Spectrumda, örneğin sol üst (x,y) 0,0 pixel koordinatını boyamak istersek 16384 numaralı adresi kullanmamız gerek. Eğer 1 karakter sağa gitmek istersek, o zaman 1 byte ilerleriz= 16385. 32 byte ilerlersek (32 karakter eder) satır sonuna varmış oluruz.
Eğer 0,0 pozisyonundan bir satır aşağı, 0,1 pixeline gitmek istersek, mantık olarak 32 byte ilerlememiz gerekir. Fakat Hayır. Zx Spectrumda 32 pixel ilerlediğinizde 8 pixel aşağı ulaşmış olursunuz(bir alttaki karakter). Bir pixel inmek için, adrese 256 byte eklemek gerekiyor.
Aslında makine dilini düşünürsek bunun açıklaması çok mantıklı. Zx Spectrum'un ekran hafızasını 16 bitlik bir rakamla adresliyoruz. Bunun için genelde 16bitlik bir register kullanılır. Örneğin, HL. HL aslında iki adet 8 bitlik register'ın eşlenmiş hali. HL=16384 olduğunda, H=64, L=0 değerindedir. Eğer "INC L" operatörüyle, L'yi 1 arttırırsak, yukarıdaki örnekteki gibi, bir sonraki karaktere ulaşırız. L'yi arttırmaya devam edersek ve satır sonuna gelirsek, L'yi tekrar arttırdığımızda otomatik olarak satır başına gelmiş oluruz. Yani L registeri karakter pozisyonunu ayarlıyor. H ise büyük çarpan olduğu için H'yi bir arttırdığımızda adrese 256 değerini ekliyor oluyoruz. Dolayısıyla "INC H" komutu, L'nin gösterdiği Karakterin içinde dikey olarak gezmemizi sağlıyor. Diyelim ki ekrana bir harf basacaksınız, o zaman L'li ayarlayarak karakterimizi seçiyoruz. Sonra H'yi 7 kere arttırarak karakterin oluştuğu 8 pixel satırını da boyuyoruz.
Eğer ekran hafızası lineer olarak dizilmiş olsaydı, bir pixel aşağı gitmek için adrese 32 eklememiz gerekecekti. 32 ekleyebilmek için büyük olasılıkla başka bir 16bit registerde 32 değerini tutmamız gerekecekti. Yani basit bir INC H yapmak yerine, PUSH DE; LD DE,32; ADD HL,DE ; POP DE; Burada yaklaşık 40tstate var. Halbuki aynı iş bu ekran diziliminde sadece 4 tstate tutuyor.
Evet, dikkat ederseniz, sadece L'yi arttırarak, ekranda bulunan tüm karakterleri sırayla gezebiliyoruz. Attrib alanı da bu düzeneğe uyuyor. Her bir karakterlik alan için 1 byte'lık renk alanı ayrılmış durumda. Yani pixelleyeceğiniz karakter'in ilk pixelinin adresini bulduğunuzda, aynı zamanda (sadece H'yi arttırarak) onun renk bilgisinin yerini de bulmuş oluyorsunuz.
Görüldüğü gibi zx spectrum'un ekran dizilimi tamamen karakter (metin) işlemlerini hızlıca yapmak üzere tasarlanmıştır. Clive Sinclair'in acorn BBC'ye kaptırdığı "eğitim bilgisayarı" olma hedefi yüzünden böyle bir düzeneğe sahip olduğu söyleniyor.
Malesef bu düzenek piksel tabanlı hesaplamalar yapanlar için pek de kolay değil. X'de sorun yok ama Y hareketinde 2048'byte'lık bloklar problem oluyor. (Eğer Sprite'ınız iki blok'un kesiştiği alanda ise sorun var). Piksel koordinatlarının adres karşılığını çabukça hesaplamanın en hızlı yöntemi bir tablo kullanmak.
Eğer değerli tstate'leri harcayıp adresi kendim hesaplattırayım diyorsanız Arjun'un yazdığı kodu veriyorum. Kodu bir fonksiyon olarak kullanmak için baştaki "hesapla" ve en sonraki "ret"i ben ekledim:
hesapla
;Girdiler: B reg = X koord, C reg = Y koordinat
;Çıktılar: HL = ekran adresi, A = pixel pozisyonu
; Adresin üst byte'ını hesapla ve H yazmaçına yükle
ld a,c
and %00000111
ld h,a
ld a,c
rra
rra
rra
and %00011000
or h
or %01000000
ld h,a
; Adresin alt byte'ını hesapla ve L yazmaçına yükle
ld a,b
rra
rra
rra
and %00011111
ld l,a
ld a,c
rla
rla
and %11100000
or l
ld l,a
; Pixel pozisyonunu bul ve Akümülatöre yükle
ld a,b
and %00000111
ret
Örneğin yukarıdaki kodu, 65,83 koordinatını pixellemek için kullanalım:
org 32768 ; programın başlangıç adresi
ld b,65 ; X kkordinatını B'ye yüklüyoruz
ld c,83 ; Y koordinatını C'ye
call hesapla ; Arjun'un hesaplama kodunu çağırıyoruz, bize HL ve A değerlerini veriyor
LD (HL),A ; Direkt olarak pixel'i boyayalım (pixelin altındaki bilgi silinir).
ret ; Basic'e dönelim
Yukarıdaki kodu Context ile F9, F10 yaptıktan sonra, specemu'da RANDOMIZE USR 32768 komutuyla çalıştırmanız lazım.
Yine bilgileri tamamlamak amacıyla tabloya bakalım.
Eğer tablo kullanmak isterseniz şu tabloya ihtiyacınız olacak, tabloyu hazırlayan ASM'dir. Bu tablodan daha basit(ve akıllı) tablolar da kullanmak mümkün, bu mesaj acemi kullanıcılara göre hazırlanmış olduğu için ufak adımlarla başlıyoruz.
; --------------------------------------------------------------------------
; Satır konum tablosu.
lpt defw 16384+(32*0)+(256*0)
defw 16384+(32*0)+(256*1)
defw 16384+(32*0)+(256*2)
defw 16384+(32*0)+(256*3)
defw 16384+(32*0)+(256*4)
defw 16384+(32*0)+(256*5)
defw 16384+(32*0)+(256*6)
defw 16384+(32*0)+(256*7)
defw 16384+(32*1)+(256*0)
defw 16384+(32*1)+(256*1)
defw 16384+(32*1)+(256*2)
defw 16384+(32*1)+(256*3)
defw 16384+(32*1)+(256*4)
defw 16384+(32*1)+(256*5)
defw 16384+(32*1)+(256*6)
defw 16384+(32*1)+(256*7)
defw 16384+(32*2)+(256*0)
defw 16384+(32*2)+(256*1)
defw 16384+(32*2)+(256*2)
defw 16384+(32*2)+(256*3)
defw 16384+(32*2)+(256*4)
defw 16384+(32*2)+(256*5)
defw 16384+(32*2)+(256*6)
defw 16384+(32*2)+(256*7)
defw 16384+(32*3)+(256*0)
defw 16384+(32*3)+(256*1)
defw 16384+(32*3)+(256*2)
defw 16384+(32*3)+(256*3)
defw 16384+(32*3)+(256*4)
defw 16384+(32*3)+(256*5)
defw 16384+(32*3)+(256*6)
defw 16384+(32*3)+(256*7)
defw 16384+(32*4)+(256*0)
defw 16384+(32*4)+(256*1)
defw 16384+(32*4)+(256*2)
defw 16384+(32*4)+(256*3)
defw 16384+(32*4)+(256*4)
defw 16384+(32*4)+(256*5)
defw 16384+(32*4)+(256*6)
defw 16384+(32*4)+(256*7)
defw 16384+(32*5)+(256*0)
defw 16384+(32*5)+(256*1)
defw 16384+(32*5)+(256*2)
defw 16384+(32*5)+(256*3)
defw 16384+(32*5)+(256*4)
defw 16384+(32*5)+(256*5)
defw 16384+(32*5)+(256*6)
defw 16384+(32*5)+(256*7)
defw 16384+(32*6)+(256*0)
defw 16384+(32*6)+(256*1)
defw 16384+(32*6)+(256*2)
defw 16384+(32*6)+(256*3)
defw 16384+(32*6)+(256*4)
defw 16384+(32*6)+(256*5)
defw 16384+(32*6)+(256*6)
defw 16384+(32*6)+(256*7)
defw 16384+(32*7)+(256*0)
defw 16384+(32*7)+(256*1)
defw 16384+(32*7)+(256*2)
defw 16384+(32*7)+(256*3)
defw 16384+(32*7)+(256*4)
defw 16384+(32*7)+(256*5)
defw 16384+(32*7)+(256*6)
defw 16384+(32*7)+(256*7)
defw 18432+(32*0)+(256*0)
defw 18432+(32*0)+(256*1)
defw 18432+(32*0)+(256*2)
defw 18432+(32*0)+(256*3)
defw 18432+(32*0)+(256*4)
defw 18432+(32*0)+(256*5)
defw 18432+(32*0)+(256*6)
defw 18432+(32*0)+(256*7)
defw 18432+(32*1)+(256*0)
defw 18432+(32*1)+(256*1)
defw 18432+(32*1)+(256*2)
defw 18432+(32*1)+(256*3)
defw 18432+(32*1)+(256*4)
defw 18432+(32*1)+(256*5)
defw 18432+(32*1)+(256*6)
defw 18432+(32*1)+(256*7)
defw 18432+(32*2)+(256*0)
defw 18432+(32*2)+(256*1)
defw 18432+(32*2)+(256*2)
defw 18432+(32*2)+(256*3)
defw 18432+(32*2)+(256*4)
defw 18432+(32*2)+(256*5)
defw 18432+(32*2)+(256*6)
defw 18432+(32*2)+(256*7)
defw 18432+(32*3)+(256*0)
defw 18432+(32*3)+(256*1)
defw 18432+(32*3)+(256*2)
defw 18432+(32*3)+(256*3)
defw 18432+(32*3)+(256*4)
defw 18432+(32*3)+(256*5)
defw 18432+(32*3)+(256*6)
defw 18432+(32*3)+(256*7)
defw 18432+(32*4)+(256*0)
defw 18432+(32*4)+(256*1)
defw 18432+(32*4)+(256*2)
defw 18432+(32*4)+(256*3)
defw 18432+(32*4)+(256*4)
defw 18432+(32*4)+(256*5)
defw 18432+(32*4)+(256*6)
defw 18432+(32*4)+(256*7)
defw 18432+(32*5)+(256*0)
defw 18432+(32*5)+(256*1)
defw 18432+(32*5)+(256*2)
defw 18432+(32*5)+(256*3)
defw 18432+(32*5)+(256*4)
defw 18432+(32*5)+(256*5)
defw 18432+(32*5)+(256*6)
defw 18432+(32*5)+(256*7)
defw 18432+(32*6)+(256*0)
defw 18432+(32*6)+(256*1)
defw 18432+(32*6)+(256*2)
defw 18432+(32*6)+(256*3)
defw 18432+(32*6)+(256*4)
defw 18432+(32*6)+(256*5)
defw 18432+(32*6)+(256*6)
defw 18432+(32*6)+(256*7)
defw 18432+(32*7)+(256*0)
defw 18432+(32*7)+(256*1)
defw 18432+(32*7)+(256*2)
defw 18432+(32*7)+(256*3)
defw 18432+(32*7)+(256*4)
defw 18432+(32*7)+(256*5)
defw 18432+(32*7)+(256*6)
defw 18432+(32*7)+(256*7)
defw 20480+(32*0)+(256*0)
defw 20480+(32*0)+(256*1)
defw 20480+(32*0)+(256*2)
defw 20480+(32*0)+(256*3)
defw 20480+(32*0)+(256*4)
defw 20480+(32*0)+(256*5)
defw 20480+(32*0)+(256*6)
defw 20480+(32*0)+(256*7)
defw 20480+(32*1)+(256*0)
defw 20480+(32*1)+(256*1)
defw 20480+(32*1)+(256*2)
defw 20480+(32*1)+(256*3)
defw 20480+(32*1)+(256*4)
defw 20480+(32*1)+(256*5)
defw 20480+(32*1)+(256*6)
defw 20480+(32*1)+(256*7)
defw 20480+(32*2)+(256*0)
defw 20480+(32*2)+(256*1)
defw 20480+(32*2)+(256*2)
defw 20480+(32*2)+(256*3)
defw 20480+(32*2)+(256*4)
defw 20480+(32*2)+(256*5)
defw 20480+(32*2)+(256*6)
defw 20480+(32*2)+(256*7)
defw 20480+(32*3)+(256*0)
defw 20480+(32*3)+(256*1)
defw 20480+(32*3)+(256*2)
defw 20480+(32*3)+(256*3)
defw 20480+(32*3)+(256*4)
defw 20480+(32*3)+(256*5)
defw 20480+(32*3)+(256*6)
defw 20480+(32*3)+(256*7)
defw 20480+(32*4)+(256*0)
defw 20480+(32*4)+(256*1)
defw 20480+(32*4)+(256*2)
defw 20480+(32*4)+(256*3)
defw 20480+(32*4)+(256*4)
defw 20480+(32*4)+(256*5)
defw 20480+(32*4)+(256*6)
defw 20480+(32*4)+(256*7)
defw 20480+(32*5)+(256*0)
defw 20480+(32*5)+(256*1)
defw 20480+(32*5)+(256*2)
defw 20480+(32*5)+(256*3)
defw 20480+(32*5)+(256*4)
defw 20480+(32*5)+(256*5)
defw 20480+(32*5)+(256*6)
defw 20480+(32*5)+(256*7)
defw 20480+(32*6)+(256*0)
defw 20480+(32*6)+(256*1)
defw 20480+(32*6)+(256*2)
defw 20480+(32*6)+(256*3)
defw 20480+(32*6)+(256*4)
defw 20480+(32*6)+(256*5)
defw 20480+(32*6)+(256*6)
defw 20480+(32*6)+(256*7)
defw 20480+(32*7)+(256*0)
defw 20480+(32*7)+(256*1)
defw 20480+(32*7)+(256*2)
defw 20480+(32*7)+(256*3)
defw 20480+(32*7)+(256*4)
defw 20480+(32*7)+(256*5)
defw 20480+(32*7)+(256*6)
defw 20480+(32*7)+(256*7)
; --------------------------------------------------------------------------
Çöplük
Bunları belki sonra düzenlerim diye burada bırakıyorum, yukarıdaki metnin içindeydiler, çıkarmak için aşağı aldım, ama belki lazım olabilir.
Zx Spectrum'un "garip" 3 bölümlü buffer diziliminin basit bir sebebi var. Detaya inmek gerekirse, Ekran adreslemesini 16bit ile yapıyoruz. Bunun sonucu olarak ister istemez üç parçalı yapıyla karşılaşyoruz: Örneğin, 16-BIT ekran adresini şöyle bölümleyelim:
010BBİİİ SSSÜÜÜÜÜ
Burada harflere denk düşen bitler ekranın şu bölümlerine ulaşmayı sağlıyor:
BB = 0-2 Ekran Bloğu (2048byte'lık Bloklar)
SSS = 0-7 Her blok içindeki dikey karakter satırları (karakter Satırı)
ÜÜÜÜÜ = 0-31 Yatay eksende her bir karakter (karakter sÜtunu)
İİİ = 0-7 Her bir karakter içindeki dikey Satırlar (iç satırar)
* Attrib bölümündeki renklerin temsili şöyledir:
--Attrib bölümündeki her 1 byte'lık bilgi, Bitmap alanındaki 8x8 piksellik bir alanı boyamak için kullanılır.
--Attrib bölümündeki 1 byte'lık bilginin içeriği şöyledir: 3bit Ön plan rengi, 3bit arka plan rengi , 1 bit Parlaklık, 1 bit yanıp sönme bilgisi.