Bu sayfadaki yazılar
Sayfanın metni
Sayfa 24
Pur
Vedat Hallaç
Makine Dilinin Püf Noktaları:
Selam sevgili okuyucular. Bu ay da demo veya intro programlamak için gereken bilgileri vermeye devam ediyoruz. Bu ayki konumuz ise makine dilinde sinüs hareketlerinin yapılması. Matematikle ilgileniyorsanız 'Sinüs' formülünü kullanarak birçok ilginç hareket tanımlayabileceğinizi biliyorsunuzdur. Mesela xorta=rxCOS(B) formüllerini kullanarak ve B (beta)'ya 0 ile 360 arası değerler vererek (xorta, yorta) koordinatını merkez alan r yarıçaplı bir daire çizebilirsiniz. Tabii ki makine dilinde sinüs ve kosinüs formüllerini hesaplatmak zaman alacağından bir-iki blokluk sinüs tabloları hazırlayarak bu zaman kaybından kurtulabiliriz. Bu tabloları hazırlarken 360 derecelik bölgenin sinüs değerlerini oldukça iyi bir yaklaşımla 256 bayta indirirsek X veya Y registerini kullanarak bu tabloya ait değerleri rahatlıkla okuyabiliriz. Makine dilinde floating point aritmetiği (kayan nokta aritmetiği: kesirli sayılarla ilgili işlemleri içeren aritmetiğe verilen ad.) kullanmak da çok değerli olan zamanımızın büyük kısmını harcayacağından sadece bize gereken 'rxSIN(B)' değerlerinin tam sayı karşılıklarını içeren bir tablo hazırlamamız gerekiyor. Tabii ki ayrıca bir de 'rxCOS(B)' tablosu hazırlamamız gerekmiyor. Çünkü COS(B)=SIN(90-B) formülünü rahatlıkla kullanabiliriz burada. Ama sinüs tablomuz 256 bayt olduğundan rxCOS(B)=rxSIN(90*256/360-B) veya TxSIN($40-B) formülünden hesaplanacaktır. Daha açık bir dil kullanırsak 256 baytlık tablonuzdan B elemanını okuyacağınıza $40-B elemanını okursak açısının kosinüsünü elde ederiz. (Herhalde çoğunuz "Neden $40-B değil de $40+B elemanını okuduk?"
diye soruyorsunuzdur. Kosinüs fonksiyonu bu noktada simetrik olduğundan bu iki elemanın arasında fark yoktur.) 10 FOR R=0 TO 255 20 A=100*SIN(R*2*PI/256) 30 POKE 49408+R,A 40 NEXT (PI: pi işareti) (1/128*PI ile r derecesini radyana çeviriyoruz) Yukarıdaki programda 100 birim genliğinde 256 elemanlık bir sinüs tablosunu $C400'de yaratan bir BASIC programı görüyorsunuz. İşte bu tür tabloların avantajı bu noktada doğmaktadır. Basic programınızın yarattığı tablonun fonksiyonu ne kadar karmaşık olursa olsun makine dili programınızın hızını etkilemeyecektir. (Sadece sinüs ve kosinüs gibi basit fonksiyonların değil, X^2+2X+XxSIN(X^2+2X^2) gibi karmaşık fonksiyonların tabloları da işinize yarayacaktır.) - Bizim programımız da 100 birim genliğinde bir sinüs tablosu (y koordinatları için) ve 0'dan 320'ye doğrusal olarak artan ve tekrar doğrusal olarak 0'a inen başka bir tablo (x koordinatları için) kullanmaktadır. X koordinatları için kullandığımız tablo, topların ekran kenarına geldikleri zaman sekmesini sağlamaktadır. Zıplama efektini elde etmek için ise 256 baytlık sinüs tablosunun yalnızca ilk 128 baytını kullanıyoruz.
Klavyeyle gezinmek için ← ve → tuşları.
Sayfa 25
Artık bu basit sinüs efekti yaratıcı programın incelemesine geçelim: Başla Sei Ldy #$00 Yaz Lda $C400,Y Sta $07F8,Y İny Cpy #$08 Bne Yaz satırlarıyla sprite'larımızın grafik datalarının bellekteki yerini ayarlıyoruz. Benim kullandığım grafik tek tip olduğu için bütün sprite pointer'larını $2000 adresini gösterecek şekilde ayarladım. Fakat size kolaylık olsun diye bıraktığım Adc #$00 komutunu Adc #$01'e çevirerek sprite'larınızın her birinin ayrı bir bellek alanını göstermesini sağlayabilirsiniz. ... arasında bildiğiniz komutlarla interrupt vektörlerini ayarlıyoruz. Daha sonra sprite'ları açıyor ve hepsini multi-color moduna geçiriyoruz ve son olarak da sprite ve ekran renklerini ayarlıyoruz. Daha sonra programımızın asıl bölümüne geliyor sıra. IRA Lsr $D019 Ldx $C0FF Lsr $D019 komutu da Inc $D019 komutuyla aynı işlevi yapar. Ldx $C0FF komutuyla ilk sprite'ın y koordinatı tablosunda kaldığı yeri (bir başka deyişle sinüs açısını) X registerine alıyoruz. Ldy #$00 Lda $C400,X Clc Adc #$60 Sinüs tablomuz $C400 adresinde bulunduğuna göre Lda $C400,X komutu A=100 x sin(x) komutunun görevini üstlenir (çok daha hızlı olarak). Bu değeri $60 ile toplayarak zıplamanın ekranın en altında gerçekleşmesini sağlıyoruz. Sta $D001,Y İny Bu sinüs değerini 1. sprite'ın Y koordinatı adresine yerleştirdikten sonra ikinci sprite'a yazabilmek için Y'yi iki artırıyoruz. :
Txa Clc Adc $C0FD And #$7F Tax $C0FD adresinde bulunan değeri X registerine ekleyerek ikinci sprite'ın birincisinden belli bir derece sonra görünmesini sağlıyoruz ve AND #$7F komutuyla bu sayıyı 0-127 aralığına hapsediyoruz. Cpy #$08 Bne Döngü Bu işlemleri sekiz sprite için de tekrar ettiğimizde ard arda dizilmiş sprite'lar kaplıyor ekranımızı (şimdilik y koordinatında). Lda $C0FF Clc Adc $C0FB And #$7F Sta $C0FF gelecek interrupt'ta sprite'larımız $C0FB adresindeki değer kadar ileride (tabii ki derece olarak) görünecekler.
Ldx $C0FE Ldy #$00 Sty $D010 Her şeyden önce x koordinat tablosu göstericimizi X registerina alıyoruz. Daha sonra da sprite'ların x koordinatlarının 9. bitlerini 0 yapıyoruz (sadece ihtiyacı olanları açmak için. Eğer bu işlemi şimdi yapmazsak, bütün sprite'ların koordinatları ya hep 0-255 ya da
256-511 arasında bulunur. Döngü2 Lda $C200,Y
Sta $D000,Y X tablosunun low (alçak) bayt elemanlarını x koordinatının ilk sekiz bitine saklıyoruz. Burada dikkat etmeniz gereken şey iki baytlık tabloları low (alçak) bayt tablosu ve high (yüksek) bayt tablosu olarak ikiye ayırmam ve böylece elemanlara erişimi kolaylaştırmam. Lda $C300,Y Beq Atla Lda $C0E0,Y Ora #$01 Sta $D010 Şimdi de sıra high baytlarda. Tabii ki sıfır olan high baytlarla işimiz yok. Eğer başta tüm 9. bitleri sıfırlamasaydık şimdi x koordinatlarının high baytları sıfır olan sprite'ların 9. bitlerini de temizlememiz gerekecekti. Eğer high baytlar sıfır değilse o zaman o koordinata sahip olan sprite'ın 9. bitini bir yapmamız gerekiyor. Bu işlemi yaparken de hızdan kazanmak için bir hile yapıyor ve bu değerleri $C0E0 adresinden başlayan $10'luk bir tabloya yerleştiriyoruz. Böylece Y registeri üzerinden indeksli okuma ile $D010 adresinin kaçıncı bitini 1 yapacağımızı öğreniyoruz. Atla İny Txa Adc $C0FC Tax İkinci sprite için gerekli x tablosu pointeri ayarlamasını yapıyoruz. Yani X registeri değerini $C0FC adresindeki değer kadar artırıyoruz. Y registerini iki artırınca da işlem yaptığımız sprite'ın bir sonrakinin x koordinatının değiştirilmesini sağlıyoruz. Cpy #$08 Bne Döngü2 sekiz sprite için bu işlemleri tekrarlıyoruz. Lda $C0FE Clc Adc $C0FA Sta $C0FE gelecek interrupt için x tablosunun pointerini istenen miktarda artırıyoruz. Ve JMP $EA31 komutuyla interrupt'ı terkediyoruz. Programın kullandığı bazı adreslere koyacağınız değerlerle ekrandaki hareketi olduğu gibi değiştirebilirsiniz. Bu adresler: $C0FA (X hızı): Her interrupt girişinde X koordinatı göstericisinin artış miktarı. Bu değeri ne kadar artırırsanız sprite'larınız x ekseninde o kadar hızlı hareket ediyor gibi görünecektir. $C0FB (Y hızı): Sprite'ların sinüs hareketi üzerindeki hızlarını ayarlar. Güzel bir görüntü için X hızına yakın (bir veya iki eksiği iyi oluyor) bir değer seçin. $C0FC (X takibi): İki sprite'ın x koordinatı tablosu değerleri arasındaki fark. Bu değeri artırdıkça sprite'lar x ekseni üzerinde birbirlerinden uzaklaşırlar. $C0FD (Y takibi): İki sprite'ın y koordinatı tablosu (sinüs tablosu) değerleri arasındaki fark. Bu değer arttıkça sprite'lar y ekseninde birbirinden uzaklaşır. Eğer $C0FC ve $C0FD adreslerine sıfır değeri verirseniz ekranda sadece tek bir sprite görürsünüz. Yani tüm sprite'lar üst üste görünürler. Bu program hakkında tüm anlatabileceklerim bu kadar. Bundan sonrasını sizin deneyerek ve inceleyerek öğrenmeniz gerekiyor. Tabii bunun için önce döküm ekindeki programı yazmanız şart. Gelecek aya kadar hoşçakalın.