ASSEMBLER'da YENİ UFUKLAR
Retro bilgisayarEvet, geçen ay kaldığımız yerden devam ediyoruz. Bu bölümde geçen ay anlattığım CWAIT, CMOVE ve CEND komutlarının pratikte nasıl kullanıldıklarını göreceğiz. Bu komutlar 68000'in komutları arasında değiller. Bu yüzden de bu komutları programımızda kullanmaya kalkıştığımızda, hex karşılıklarını programımıza data satırları olarak eklemeliyiz. Bunu da ya direkt dc.b kullanarak, ya da makro kullanarak yapabiliriz. Biliyorsunuz, MSeka'nın makro özelliği sayesinde dilerseniz yeni komutlar yaratabilirsiniz. Makroları daha tanımayanlara, geçen ayki Master-Seka yazısını okumalarını öneririm. Unutmayın, makroları her zaman source'unuzun başına girmelisiniz. Her neyse, gelelim kendimize gerekli olacak olan üç komutu yaratmaya. CWAIT: MACRO IF NARG=2 dc.b ?2,?1,$ff,$fe ELSE FAIL ENDM CMOVE: MACRO
IF NARG=2
dew ?2,?4
ELSE FAİL
ENDM
CEND:
MACRO
IF NARG=0
dew $fffffffe
ELSE FAİL
ENDM
Evet, üç makromuz da hazır. Peki bunları nasıl kullanacağız?
Gayet basit, normal komutları nasıl kullanıyorsak aynı şekilde.
İşte aşağıda birkaç ufak örnek:
CMOVE $0f00,$0180
CWAIT $07,$40
CWAIT $01,$f0
CMOVE $1200,$0100
CEND
Bunları hemen MSeka'da yazmaya kalkışmayın. Bir sonuç el-
de edemezsiniz. Dilerseniz şimdi bu üç yeni komutumuzu nasıl*
kullanacağımızı görelim:
* CMOVE <data>,<reg>: Bu komut yardımıyla herhangi bir copper register'ına istediğimiz değeri yazabiliriz. Komutu oluşturan makroya bir göz atarsak, komuta ikiden farklı sayıda parametre girersek MSeka'nın bu source'u FAIL komutu yüzünden compile etmeyeceğini görürüz. Makro içerisinde gelen parametreleri neden ters yerleştirdiğimi anlatmayacağım. Zaten geçen ayki yazıyı dikkatli okuyup öğrenenler bunu bilirler.
* CWAIT <x>,<y>: Tarama ışının istenilen ekran koordinatına gelmesi beklenir. Bu ekran komutu, iki byte olarak x, y koordinatını
"" şeklinde girmelisiniz. x koordinatı her zaman çift sayı olmalıdır; aksi halde programınız hatalı çalışır. Burada da parametreleri makroda ters yerleştirdim. Bu komut da, aynı bir önceki gibi, ikiden farklı parametre sayısında hata mesajı verilmesine sebep olur.
* CEND: Copper listesinin sonunu belirler. Parametre kullanılmamalıdır, aksi halde FAIL hata mesajını verir.
Evet, bu komutları programımıza nasıl ekleyeceğimize birazdan geleceğim. Fakat ilkönce bir şeyi açıklığa kavuşturmak istiyorum. Bu, copper listemizi copper cipine nasıl aktaracağımız. Burada sistem rutinlerinden de faydalanabiliriz, copper registerlarından da. İlki kullanılacağı zaman graphics.library'nin açılmış olmasını gerektirir. Oysa çok fazla hafıza harcayacak programlarda bu bir dezavantajdır, ikincisinde ise copper listesini set edebiliriz, yani copper cipine listemizi tanıtabiliriz. Fakat programdan çıkış olduğunda eski copper listesini aktif hale getirmek için graphics.library'yi başlangıçta mutlaka açmalı ve o anki copper'ın adresini saklamalıyız. Ancak bundan sonra copper listesini set eder ve programdan çıkışta da yine graphics.library'yi kullanarak eski copper'ı aktif yapabiliriz. Bunlardan hangisini kullanacağınız artık size kalmış; benden açıklaması. Ben bütün örnek programlarda ilkini kullandım.

* Copper listesinin library yardımıyla set edilmesi: Bunun için graphics.library'deki GFXBASE structure'ını kullanacağız. Bu structure'daki gereksiz kısımları atlıyorum. İsteyen assembler köşesini takip ederek structure açıklamalarını inceleyebilir. GfxBase structure'ı, graphics.library açıldığında base adresinden sonrasında bulunan structure'dır. GfxBase structure'ında 50. (=$32) ofsetteki long-word, aktif olan copper'ın adresini gösterir. Programımızda biz sadece bu adresteki bilgiyi longword olarak bir değişkene (örneğin SysCopper) kopyalarız. Ardından da 50. ofsetteki longword'e kendi copper listemizin başlangıç adresini yazarız. Bunu da ister label kullanarak, istersek de direkt adres yazarak yapabiliriz. Direkt adres yazarken, copper listesinin bu adreste bulunduğunu garantilemeliyiz. Bunu da, source'ta copper listesinden önce ORG ve LOAD komutlarını yazarak ya da copper listemizi word word o adresten sonrasına kopyalayarak yapabiliriz. Eh, bir hayli opsiyon var değil mi? Bunlardan en basiti, hiç kuşkusuz label kullanarak. Aşağıdaki satırları programınıza eklerseniz, copper'ı hiç sorun olmadan set ve reset edebilirsiniz, move.l GfxBase,a6 ; graphics.library başlangıcı move.l 50(a6),SysCopper ; sistem copper'ını saklar, move.l #Copper,50(a6) ;bizim copper'ı aktif yapar. .
Bunun bir de çıkışı var. Çıkış rutininize de şu satırları ekleme-
niz gerekmekte:
move.l GfxBase,a6
; graphics.library başı.
move.l SysCopper,50(a6) ; Eski copper'ı aktif hale getirir.
* Copper listesinin copper yardımıyla set edilmesi: Bu yöntemde listemizi set etmek için copper'ı kullanıyoruz. Copper register'larında iki register bu işimizi görmekte. Bunlardan ilki olan $0080 ve $0082'ye copper listemizin adresini yazıyoruz. Hardware Reference Manual kitabında bu adreslerin ismi COP1LCH ve COP1LCL olarak geçmekte. Buradan adresimizin üst word'ünün $0080 register'ına, alt word'ün de $0082'ye yazılacağını anlayabiliyoruz. Fakat bunu daha kısa yapmanın yolu, adresimizi $0080 register'ına direkt longword olarak yazmamız. Bu şekilde zaten üst longword $0080'e, alt longword'da $0082'ye gidecektir. Eee, daha ne bekliyoruz? Copper listemizin adresini yazdık; fakat copper cipi, bu adresin aktif copper olarak kullanılmasının da onaylanmasını ister. Bunu da $0088 register'ına herhangi bir değer yazarak yapabiliriz. Bu register'ın ismi COPJMP1. Register bir strobe register'ı. Bu, buraya yapılacak herhangi bir işlemin copper'ı aktif hale getireceğini tanımlar, move.l #Copper,$dff080 ; Copper adresini yaz. clr.w $dff088 ; Copper cipine yeni listeyi bildir. Copper'ı aktif hale getirdik. Peki programdan çıkmak istediğimiz zaman nasıl eski copper'ı set edeceğiz? Bunun için demin de belirttiğim gibi programımızın başında eski copper adresini sak-
ASSEMBLER'da YENİ UFUKLAR
Cem GENCER lamamız gerekli. Bu yüzden az yukarıdaki satırların başına bir de şu satırları eklemeliyiz:
move.l GfxBase,a6 ; graphics.library başlangıcı move.l 50(a6),SysCopper ; sistem copper'ını saklar. Programın çıkış rutinine geldiğimizde de aşağıdaki satırları eklememiz gerekmekte:
move.l GfxBase,a6 ; graphics.library başı. move.l SysCopper,50(a6) ; Eski copper'ı aktif hale getirir. Sizin de anlayabileceğiniz gibi iki yöntem arasında tek fark, copper'ın nasıl set edildiğinde yoğunlaşıyor. Geri kalan satırlar tümüyle aynı.
Copper listemizi nasıl aktif hale getirip kapayabileceğimizi böylece öğrenmiş olduk. Şimdi gelelim esas konumuz olan copper listemizde yukarıda bahsettiğim komutların kullanımına. Bunun için vermiş olduğum listeye bakmanız yeterli. Listenin en sonunda COPPER label'ından sonra gelen kısım tümüyle bizim yeni copper listemiz. Dilerseniz burayı hemen bir inceleyelim.
Copper listesinin ilk satırındaki komut, $5200 değerini $0100 register'ına atıyor. Bunun anlamı mı ne? $0100 register'ının ismi BPLCON0. Bu register yardımıyla ekranın bazı önemli bilgilerini tanımlayabiliriz. $5200'ün anlamı 5 bitplane ($5000) ve composite renkli video sinyali (+$200) anlamına gelmekte. Bu register'daki diğer bitlerin anlamlarını, Doğan Türküler Hardware köşesinde diğer copper register'larıyla beraber açıklayacak. Biz şimdilik BPLCON0'ı bitplane adedimizi seçmek için kullanıyoruz. Örneğin 3 bitplane için buraya yazmamız gereken değer $3200 olmalıydı.
Bundan sonraki dört satır ile DIWSTRT ($008e), DIWSTOP ($0090), DDFSTRT ($0092) ve DDFSTOP ($0094) register'larına belli değerler yazıyoruz. Bu değerler yardımıyla ekran büyüklüğünü etkileyebiliriz. Örneğin DIWSTRT ve DIWSTOP ile oynayarak Overscan ekran yaratabiliriz. Bunu ilerde kullanacağımız için pek değinmiyorum. Yalnız verdiğim değerlerin normal bir 320*256 ekran yaratmak için kullanılan değerler olduğunu söylemeliyim.
$0108 ve $010a register'ları BPL1MOD (tek numaralı bitplane'ler için modulo) ve BPL2MOD (çift numaralı bitplane'ler için modulo) isimlerini taşımaktalar. Buraya yazacağımız değerler, bir sonraki satırın adresini bulmak için her ekran satırı sonunda ekran adresine eklenecek modulo değerlerini belirler. Verdiğim örnekte bitplane'ler hafızada alt alta yer aldığı için modulo değerleri 0 olmalıdır. Eğer bitplane'lerimiz özel bir amaç için yan yana dizilmişlerse, her iki modulo'yu da uygun değerlere ayarlamalıyız. İlerde blitter konusuna girdiğimizde hızdan kazanmak için bitplane'leri bu şekilde kullanacağız.
Copper listemizde bundan sonraki 10 satır ile bitplane adreslerini tanımlayabiliriz. $00e0 ve $00e2 (BPL1PTH ve BPL1PTL) ilk bitplane'in high ve low word'ünü taşır. Aynı şekilde diğer bitplane'ler tanımlandıktan sonra renkleri tanımlıyoruz. Bunun için renk register'larına istediğimiz renk değerini $0rgb olarak yazıyoruz, r, rengin kırmızı oranını, g ve b'de yeşil ve mavi oranlarını belirler. Renk register'ları $0180'den sonra gelen 32 word'dur (COLOR00-COLOR31). Verdiğim örnekte bütün renk register'larını sıfırladım. Programınızda resim ya da yazı kullanacağınız zaman, bunlara ait renkleri bu register'lara yazmalısınız.
Bundan sonra gelen satırlar sprite datalarının yerlerini tanımlamak için kullanılır. Fakat biz programımızda hiç sprite kullanmadığımızdan bunları da sıfırlıyoruz. Sprite kullanılacağı zaman, sprite'ın hafızada bulunduğu adresin üst ve alt word'ünü bu adreslere (SPRxPTH ve SPRxPTL) yazmalısınız. Sprite'lar hakkında şimdilik bir noktaya daha değineceğim. Mouse pointer'ı da bir sprite'dır. Sprite 0'ın adresini sıfırladığımız zaman sprite pointer'ı yok olacaktır. Bu yüzden programınızda mouse'u kapamak istiyorsanız sadece sprite 0 adreslerini 0 yapmanız yeter.
Şimdi dilerseniz programımızın kalan bir-iki ufak bilinmeyenini de açıklayalım. Programın Loop label'ından sonraki iki satırı, ekran tarama ışınının ekran sonuna gelip gelmediği kontrol edilir. Bunun için $dff006 register'ının içeriğine bakılır. Bu register'ın ismi VHPOSR'dir ve ekran tarama ışınının o anki konumunu gösterir. Eğer ekran tarama ışını 256. satıra (=$ff) geldiyse programımıza devam edilir. Aksi halde bekleme devam eder. Bu iki satırın amacı şu: Programlarımızda hareketli şeyler kullanmaya başlayınca bazen ekranda hareket eden kısımların titremeye başladıklarını görürsünüz. Bu, ekran tarama ışınının tüm hareketleri aynı anda gösteremeyeceğindendir. Bu durumda işlemlere başlamak için ekran tarama ışınının ekran sonuna varması beklenir. Bütün ekrandaki hareket işlemleri bu sayede ekran tarama ışını ekran dışında olduğu zamanlar yapılır. Ekran tarama ışınımız, ekranı 0. satırdan itibaren yeniden taramaya başladığı zaman da bütün değişen pikselleri update edecektir. Bu şekilde ekrandaki ufak tefek titremeleri de kaldırabiliriz. İlerde intro ve demo yazarken bu yöntemi umduğumuzdan da çok kullanacağız.
Mouse label'ından sonraki satırlarda $bfe001'in 6. bitini kontrol ediyoruz ve bu bit 0 ise programın çıkış rutinine geçiyoruz. Bu bit, mouse'un sol tuşunun konumunu gösterir. 6. bit 0 değerini taşıyorsa mouse'un sol tuşuna basıldı demektir. Mouse'un sol tuşuna basılmamış ise Loop label'ına geri dönülür. Devamlı tekrar edilmesini istediğimiz rutinleri bu yüzden Mouse label'ından hemen öncesine koymalıyız. Bu rutinler, mouse tuşuna basılana kadar tekrar edilir.
Hay allah, ne güzel tam öğrenmeye başlamışken yerimiz yine bitti. Programın başka açıklanacak yeri de kalmadı zaten. Gelecek ay, copper listesini kullanarak ufak bir rutin yazmaya ne dersiniz? Hem bu şekilde copper listesinin esas kullanılış şeklini de tam olarak görebilirsiniz. Hepinize bol byte'lı günler.
; >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; >>>>GELISMIS ASSEMBLER - ÖRNEK PROGRAM <«
; >>>
TEMEL RUTİN
. <«
; >>>
by :Cem GENCER /1992
<«
; >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Q:
movem.l a0-a6/d0-d7,-(a7)
bsr.s Init
Loop:
cmp.b #Sff,$dff006
bne.s Loop
«
Mouse:
btst
#6,$bfe001
bne.s Loop
beq.s Final
; >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; >»
Turn alt-rutinler buradan sonra ...
<«
; >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Final:
lea SysCop,a0
move.l (a0),50(a6)
movem.l (a7)+,a0-a6/d0-d7
rts
Init:
move.l 4,a6
lea GfxName,a1
jsr -408(a6)
move.l d0,GfxBase
move.l GfxBase,a6
lea SysCop,a0
lea Copper.a1
move.l 50(a6),(a0)
move.l al,50(a6)
rts
; >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; >» Tüm datalar buradan sonra ... >> ; >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
GtxName:
dc.b 'graphics.library',0,0
SysCop:
dc.l 0
GfxBase:
dc.l 0
Copper:
CMOVE $5200,$0100
CMOVE $3a81,$008e
CMOVE $40cl,$0090
CMOVE $0038,$0092
CMOVE $00d0,$0094
CMOVE $0000,$0108
CMOVE $0000,$010a
CMOVE
$0007,$00e0
CMOVE
$0000,$00e2
CMOVE
$0007,$00e4
CMOVE
$2800,$00e6
CMOVE
$0007,$00e8
CMOVE
$5000,$00ea
CMOVE
$0007,$00ec
CMOVE
$7800,$00ee
CMOVE
$0007,$00f0
CMOVE
$a000,$00f2
CMOVE
$0000,$0180
CMOVE
$0000,$0182
CMOVE
$0000,$0184
CMOVE
$0000,$0186
CMOVE
$0000,$0188
CMOVE
$0000,$018a
CMOVE
$0000,$018c
CMOVE
$0000,$018e
CMOVE
$0000,$0190
CMOVE
$0000,$0192
CMOVE
$0000,$0194
CMOVE
$0000,$0196
CMOVE
$0000.$0198
CMOVE
$0000,$019a
CMOVE
$0000,$019c
CMOVE
$0000,$019e
CMOVE
$000a$01a0
CMOVE
$0000.$50la2
CMOVE
$0000,$01a4
CMOVE
$0000,$01a6
CMOVE
$0000,$01a8
CMOVE
$0000,$01aa
CMOVE
$0000,$01ac
CMOVE
$0000,$01ae
CMOVE
$0000,$01b0
CMOVE
$0000,$01b2
CMOVE
$0000,$01b4
CMOVE
$0000.$01b6
CMOVE
$0000,$01b8
CMOVE
$0000,$01ba
CMOVE
$0000,$01bc
CMOVE
$0000,$01be
CMOVE
$0000,$0120
CMOVE
$0000,$0122
CMOVE
$0000,$0124
CMOVE
$0000,$0126
CMOVE
$0000,$0128
CMOVE
$0000,$012a
CMOVE
$0000,$012c
CMOVE
$0000,$012e
CMOVE
$0000,$0130
CMOVE
$0000,$0132
CMOVE
$0000,$0134
CMOVE
$0000,$0136
CMOVE
$0000,$0138
CMOVE
$0000,$013a
CMOVE
$0000,$013c
CMOVE
$0000,$013e
CEND