ASSEMBLER'da YENİ UFUKLAR
Retro bilgisayarSelam! İşte yine sizlerle beraberiz. Bu ay, sprite'ları biraz daha karıştıracağız. Geçen ay bir sprite'ı ekranda nasıl göstereceğimizi öğrenmiştik. Bu ay ise aynı şekilde 8 sprite'ı birden ekranda göstermek için yapılması gerekenleri inceleyeceğiz. Aynı zamanda da sprite'ları hareket ettirmeyi de göreceğiz. Dilerseniz vermiş olduğum programı hemen yazıp kaydedin.
Programı incelemeye başlamadan önce, size yine biraz teorik bilgi vermemde fayda var. Aslında burada yeni bilgiler yerine, geçen ay kazandığınız bilgileri biraz tazeleyeceğim. Hatırlarsanız sprite koordinatları sprite datalarının ilk üç byte'ında bulunmaktaydı. Bunlar sırayla VSTART, HSTART ve VSTOP idi. İstediğimiz bir sprite'ı hareket ettirmek aslında gayet basit. Örneğin sprite'ı sağa doğru hareket ettirmek istediğimizi varsayalım. Bunun için her ekran taramada sprite datalarındaki ikinci byte'ı bir artırmamız yetmekte. Bu şekilde HSTART'ın aldığı değer her ekran taranmasında 1 artacak. Sprite'lar da her ekran taramasında tekrar çizildiğinden sprite'ımız ekran tarama ışınına senkron olarak sağa doğru hareket edecektir. Sprite hızını artırmak için her seferinde 1 değil de başka bir sayı eklememiz yeterli. Fakat yüksek bir sayı kullanırsanız bu sefer de sprite titremeye başlayacak. Yavaşlatmak için de her seferinde HSTART'a 1/2 ya da 1/4 değer eklemek gerekmekte. Fakat Assembler'da sadece tam sayıları hiçbir uğraş olmadan kullanabildiğimizden 1/2 ya da 1/4 değerlikli bir sayı elde etmek için değişik bir yöntem uyguluyoruz: HSTART değerini ekran tarama ışını 2 ya da 4 defa ekranı tam olarak taradığında 1 artırıyoruz. Nasıl, basit değil mi? Tarama ışınıyla senkronizasyonu karıştırmamak için sprite hız değerini 2'nin katları (2, 4, 8, 16, ...) ya da askatları (1/2, 1/4, 1/8, 1/16, ...) olarak seçmemizde fayda var.
Sprite'ı yukarıya ya da aşağı doğru hareket ettirmek için de aynı yöntemi uyguluyoruz. Fakat burada sadece VSTART değerini değiştirmiyoruz. Aynı zamanda VSTOP'u da VSTART'la aynı oranda artırmalı ya da azaltmalıyız. Aksi halde sprite'ımız bazen bozuk gözükebilir, bazen de ekranda sprite'ın altında bir sürü pislik oluşabilir. Bu pislikler aslında sprite datalarından hemen sonra gelen bilgilerdir. Sprite yüksekliği (VSTOP-VSTART) her seferinde değişik olacağından sprite'ın altındaki pisliklerin de yüksekliği otomatik olarak değişecektir. Bazen de negatif değere ulaşılacağından (VSTART, VSTOP'dan büyük olduğu durumlarda) ekranda sprite yerine sadece pislik gözükebilir.
Şimdi biraz da sprite'ların değişik bir özelliği hakkında bilgi vermek istiyorum: Extended (attached) sprite. Bunun hakkında detaylı bilgiyi aşağıda bulabilirsiniz.
Bildiğiniz gibi sprite'lar normalde 4 renk ve 16 piksel genişlikle sınırlıdırlar. Fakat bazı durumlarda 32 piksel genişliğinde ya da 16 renkli sprite'lar da olmasını isteriz. Programımızda sprite'ların hepsini kullanmadığımız durumlarda bu mümkün olmakta. 32 piksel genişlikli sprite'lar için iki sprite'ı yanyana kullanmamız yeterli olabilir. Sprite koordinatlarını da değiştirirken her iki sprite'ın koordinatlarını da aynı anda değiştirmemiz gerekmekte. Fakat hala 16 rengi nasıl oluşturabileceğimizi bilmiyoruz.
Copper çipi sprite'ları ikili gruplar halinde işlemekte. Bu yüzden de sprite renkleri 4 grup olarak sınırlıdır. Her bir renk grubu ardarda gelen iki sprite'ın rengini gösterir. Renk register'lerinin üst yarısı ($dff1a0-$dff1be) aynı zamanda sprite'ların da renklerini taşıdığını düşünürsek her sprite renginin aşağıdaki listede yer alan renk register'lerinde bulunması gerektiğini de kabul etmişiz demektir.
Grup 0 : Sprite 0, 1 - $dff1a0, $dff1a2, $dff1a4, $dff1a6 Grup 1 : Sprite 2, 3 - $dff1a8, $dff1aa, $dff1ac, $dff1ae Grup 2 : Sprite 4, 5 - $dff1b0, $dff1b2, $dff1b4, $dff1b6

Grup 3 : Sprite 6, 7 - $dff1b8, $dff1ba, $dff1bc, $dff1be
Extended sprite modunda renk register'lerinin üst yarısı komple bu genişletilmiş sprite için kullanılmakta. Ne de olsa genişletilmiş sprite 16 renkten oluşmakta. Dilerseniz tüm sprite'ları genişletilmiş sprite olarak gösterebilirsiniz. Fakat bu durumda sprite sayısı 4 ile sınırlı kalacaktır. Ya da sadece bir-iki genişletilmiş sprite kullanıp gerisini normal sprite olarak da kullanabilirsiniz. Fakat bu durumda tüm renk register'lerini birbirleriyle çakışmayacak şekilde ayarlamalısınız.
Peki extended sprite'ı nasıl mı kullanacağız? Gayet basit; extended sprite'ı oluşturan grubun sprite'larını her zaman üstüste gelecek şekilde koordinatlarını belirlememiz gerekmekte. Ayrıca extended sprite'ı oluşturan ikinci sprite'ın dördüncü byte'ındaki (SPRxCTL) ATTACH bitini (7. bit) de set etmemiz gerekmekte. Renkleri oluşturmak için de her iki sprite'ın bitplane'lerini üstüste getirmemiz gerekmekte. Renk kodunu aşağıdaki tablodan rahatlıkla bulabiliriz.
Renk no Spr0 Spr1 Renk register'i
0 00 00 $dff1a0 1 01 00 $dff1a2 2 10 00 $dff1a4 3 11 00 $dff1a6 4 00 01 $dff1a8 5 01 01 $dff1aa 6 10 01 $dff1ac 7 11 01 $dff1ae 8 00 10 $dff1b0 9 01 10 $dff1b2 10 10 10 $dff1b4 11 11 10 $dff1b6 12 00 11 $dff1b8 13 01 11 $dff1ba 14 10 11 $dff1bc 15 11 11 $dff1be
Ne yazık ki bu aylık yerim dolmaya başladığından ancak gelecek ay size extended sprite ile ilgili bir örnek verebileceğim. Şimdilik bu ay vermiş olduğum örneği incelemeye başlayalım. Programın genelini anlamanız o kadar zor değil. Hemen göze çarpan kısımlar arasında ekran tarama ışınını kontrol etmediğimdir. Sprite hareket ettirme rutinini işlemek biraz zaman aldığından bir de ekran tarama ışınını bekletseydim ekrandaki tüm sprite'lar titremeye başlıyordu. Fakat hiç bekleme olmadan da sprite'ları hareket ettirmeye kalkıştığımda sprite'lar epey yüksek bir hızla hareket ediyordu. Bu yüzden ben de ekran tarama ışınını bekleteceğim yerde boş bir döngü yaratarak 800'den -1'e kadar saydırma rutini ekledim. Bu şekilde sprite'lar tam istediğim bir hızla hareket etmekteler. Sprite'ları biraz daha hızlandırmak için bu değeri azaltmanız kafi olacaktır. Aynı şekilde yavaşlatmak için de bu değeri artırmanız gerekmekte. Fakat bu değeri fazla düşük ya da yüksek seçerseniz sprite'lar ya titrer, ya da çok hızlı hareket eder.
ASSEMBLER'da YENİ UFUKLAR
Göze çarpan diğer bir konu da program başında ve sonunda iki library fonksiyonu kullanmış olmam. Bunlar Exec library'sinden Forbid ve Permit. Her ikisinin de kullanım şeklini ve amaçlarını bu ayki Amiga ve Assembler köşesinde detaylı olarak açıkladım.
Programın CreateSprites altrutini ilk sprite datalarının sprite'ın görüntüsünü tanımlayan kısmını tüm sprite'lara kopyalamakta. Hemen ardından da 8 sprite'ın adresleri copper listesine yazılmakta. Dilerseniz siz 8 değişik sprite görüntüsü kullanın. Fakat bunun için CreateSprites altrutinin ilk kısmını silerek her sprite datasını ayrı olarak girmeniz gerekmekte. Aynı zamanda sprite renklerini de doğru girmeniz lazım.
Ana döngü içerisinde demin de belirttiğim gibi ufak boş bir döngü ve sprite hareket ettirme rutinine bir dallanma (bsr MoveSprites) bulunmakta. MoveSprites rutinine baktığımızda burada ilk önce SprNo değişkeninin içeriğinin d7 register'ine kopyalandığını görmekteyiz. Bu değer o an hareket ettirilecek sprite numarasıdır ve MoveSprite rutininden çıkmadan hemen önce her seferinde bir artırılmakta. SprNo, 8 olduğunda da tekrar sıfırlanmakta. Bu şekilde her seferinde sadece bir sprite'ı hareket ettirmekteyiz. Tüm 8 sprite'ı da aynı anda hareket ettirmek çok zaman alacaktı. Nedeni de her seferinde sprite y koordinatlarının bir tablodan okunmasından kaynaklanmakta.
Dediğim gibi, sprite y koordinatı bir data tablosundan okunmakta. Fakat tüm y koordinatlarını aynı tablodan okumaya kalkıştığımızda tüm 8 sprite'ın y pozisyonları aynı olur. Bunu engellemenin iki yöntemi var; ilki gayet basit. Her sprite için ayrı bir data tablosu çıkarmak. Fakat bu şekilde bir rutin dergimizde fazla yer kaplayacağından dergimizin editörü Mahmure Abla, kızarak evime gizlice girip source disketlerimi formatlayabilir. Bu yüzden ben ikinci yöntemi kullanmak zorunda kaldım. Bu şekilde siz de yeni bir teknik daha görebilirsiniz.
Tüm sprite'larımız nasıl olsa sinüzoidal bir hareket yapacaklar. Ben de sadece bir sinüs tablosu kullanmaya karar verdim. Fakat bu sinüs tablosunun 8 ayrı yerini gösteren 8 ayrı pointer kullanarak her sprite'ın y koordinatının farklı olmasını sağladım. MoveSprites rutininde de her seferinde sprite numarasına denk düşen pointer'ın gösterdiği değer okunmakta. Eğer bu değer $ff'den farklıysa, direkt olarak VSTART'a yazılmakta. Bu değere 14 (sprite yüksekliği) ekleyerek de VSTOP değerini bulmaktayız. Değer $ff olduğunda ise sprite numarasına denk düşen pointer'ın tekrar sinüs tablosunun başını göstermesi sağlanmakta. HSTART, yani sprite'ın yatay koordinatı da her seferinde 1 artırılmakta. Rutinin sonunda da kullanılan pointer 1 artırılarak bir sonraki kullanım için hazırlanmakta.
Gayet basit, değil mi? Programın açıklaması da burada bittiğine göre bana artık sadece size hoşça kalın demekten başka bir şey kalmıyor.
; >>>>>>>>>>>>><<<<<<<<<<<<<<
; >GELISMIS ASSEMBLER KURSU<
; >>SPRITE DEMO PROGRAMI 02<<
;>>
byCemGENCER-1992
<<
; >>>>>>>>>>>>><<<<<<<<<<<<<<
Q:
movem.l
d0-d7/a0-a6,-(a7)
move.l
4,a6
jsr
-132(a6)
lea
GFXNAME,a1
jsr
-408(a6)
move.l
d0,GFXBASE
move.l
#64,d7
lea
$45000,a0
CLEAR:
clr.b
(a0)+
dbf
d7,CLEAR
bsr.L
CREATECOPPERS
bsr.L
CREATESPRITES
LOOP:
move.l
#800.d2
IRQ:
dbf
d2,IRQ
bsr.L
MOVESPRITE
MOUSE:
btst
#6,$bfe001
bne.s
LOOP
bra.s
FİN
FIN:
move.l
GFXBASE,a0
add.l
#50,a0
move.l
OLDCOPPER,(a0)
move.l
4,a6
jsr
-138(a6)
movem.l
(a7)+,d0-d7/a0-a6
rts
CREATECOPPERS:
lea
NEWCOPPER+172,a0
move.l
#$4007fffe,d1
move.l
#$0180000f,d2
move.l
#15,d7
CREATECOPI:
move.l
d1,(a0)+
move.l
d2,(a0)+
add.l
#$050000O0,d1
sub.l
#$00000001,d2
dbf
d7,CREATECOP1
move.l
dl,(a0)+
move.l
#$01800000,(a0)+
move.l
#$a007fffe,d1
move.l
#$01800000,d2
move.l
#15,d7
CREATECOP2: *
move.l
d1,(a0)+
move.l
d2,(a0)+
add.l
#$05000000,d1
add.l
#$00000001,d2
dbf
d7,CREATECOP2
move.l
d1,(a0)+
move.l
#$01800000,(a0)+
move.l
GFXBASE,a0
add.l
#50,a0
move.l
(a0),OLDCOPPER
move.l
#NEWCOPPER,(a0)
rts
CREATESPRITES:
lea
SPRITE1+4,a1
move.l
#6,d6
NEXTSPRITE:
lea
SPRITE0+4,a0
move.l
#29,d7
COPYSPRITE:
move.w
(a0)+,(a1)+
dbf
d7,COPYSPRITE
add.l
#4,al
dbf
d6,NEXTSPRITE
clr.l
d5
lea
SPRITEO,a0
move.l
#7,d7
ASSIGNSPRITLS:
lea
NEWCOPPER.a1
move.l
a0,d0
move.l
a0,d1
swap
d1
and.l
#$ffff.d0
and.l
#$ffff.d1
move.w
dl,6(al,d5)
move.w
d0,10(a1.d5)
add.l
#8,d5
add.l
#64,a()
dbf
d7,ASSIGNSPRITES
rts
MOVESPRITE:
move.l
SPRNO.d7
move.l
d7,d6
asl.w
#2,d6
clr.l
d0
CONT:
^^^
lea
SINUSPOINTERS,a0
move.l
(a0,d6),a0
move.b
(a0),d0
cmp.b
#$ff,d0
beq.s
SINUSEND
lea
SPRITES,a1
move.l
(a1,d6),a2
move.b
d0,0(a2)
add.b
#14,d0
move.b
d0,2(a2)
add.b
#1,1(a2)
lea
SINUSPOINTERS,a0
add.l
#1,(a0,d6)
add.l
#1,SPRNO
move.l
SPRNO,d7
cmp.l
#8,d7
blt.s
LESS
move.l
#0,SPRNO
LESS:
Rts
SINUSEND:
lea
SINUSPOINTERS,a0
lea
SINUSTAB,a1
move.l
a1,(a0,d6)
bra.s
CONT
; >>>>>>>>>>>>><<<<<<<<<<<<<
; > Turn dataları buradan sonra ... <
; >>>>>>>>>>>><<<<<<<<<<<<<<
NEWCOPPER:
dc.w $0100,$1200
dc.w $0120,$0000,$0122,$0000
dc.w $0124,$0000,$0126,$0000
dc.w $0128,$0000,$012a,$0000
dc.w $012c,$0000,$012e,$0000
dc.w $0130,$0000,$0132,$0000
dc.w $0134,$0000,$0136,$0000
dc.w
$0138,$00O0,$013a,$0000
dc.w
$013c,$0000,$013e,$0O00
dc.w
$0102,$0000,$0104,$0000
dc.w
$0092,$0038,$0094,$00d0
dc.w
$008e,$2c81,$0090,$2cc1
dc.w
$0180,$0000,$0182,$0000
dc.w
$01a0,$0000,$01a2,$0888
dc.w
$01a4,$0E20,$01a6,$0600
dc.w
$01a8,$0000,$01aa,$0888
dc.w
$01ae$0E20,$01ae,$0600
dc.w
$01b0,$0000,$01b2,$0888
dc.w
$01b4,$0E20,$01b6,$0600
dc.w
$01b8,$0000,$01ba,$0888
dc.w
$01bc,$0E20,$01be,$0600
dc.w
$00e0,$0004,$00e2,$1000
blk.w
[4*17],0
blk.w
[4*17],0
dc.w
$ffff,$fffe
SPRITES:
dc.l SPRITE0,SPRITE1,SPRITE2,SPRITE3
dc.l SPRITE4,SPRITE5,SPRITE6,SPRITE7
SPRITE0:
dc.b 150,112,164,0
dc.w $c380,$7c00,$e000,$0200,$f900
dc.w $lf00,$73c0,$le00,$d230,$fe00
dc.w $9b88,$ff00,$c9c4,$ffc4,$c4fe
dc.w $fffe,$fc7f,$07cf,$fe7d,$03ff
dc.w $0203,$03ff,$fffe,$fffe,$f870
dc.w $fT80,$0000,$0000,$0000,$0000
SPRITE 1:
dc.b 150,133,164,0
blk.w 30,0
SPRITE2:
dc.b 150,196,164,0
blk.w 30,0
SPRITE3:
dc.b 150,206,164,0
blk.w 30,0
SPRITE4:
dc.b 150,114,164,0
blk.w 30,0
SPRITE5:
dc.b 150,76,164,0
blk.w 30,0
SPRITE6:
dc.b 150,81,164,0
blk.w 30,0
SPRITE7:
dc.b 150,173,164,0
blk.w 30,0
GFXNAME:
dc.b 'graphics.library',0,0
GFXBASE:
del 0
OLDCOPPER:
del 0
SPRNO:
del 0
SINUSPOINTERS:
del SINUSTAB+[20*0],SINUSTAB+[20* 1 ]
del SINUSTAB+[20*2],SINUSTAB+[20*3]
del SINUSTAB+[20*4],SINUSTAB+[20*5]
del SINUSTAB+[20*6],SINUSTAB+[20*7]
SINUSTAB:
dc.b $92, $94, $96, $99, $9B, $9D,
$A0, $A2, $A4. $A7
dc.b $A9, $AB, $AD, $AF, $B2, $B4,
$B6, $B8, $BA, $BC
dc.b $BE, $C0, $C2, $C3, $C5, $C7,
$C8, $CA, $CB, $CD
dc.b $CE, $D0, $D1, $D2, $D3. SD5,
$D6, $D7, $D7, $D8
dc.b $D9, $DA, $DA, $DB, $DB. SDC,
$DC, $DC, $DC,$DC
dc.b $DC, $DC, $DC, $DC, $DC. SDB.
$DB, $DB, $DA,$D9
dc.b $D9, $D8, $D7, $D6, $D5, SD4.
$D3, $D2, $D0, $CF
dc.b $CE, $CC, $CB, $C9, $C8, SC6.
$C4,$C2,$C1,$BF
dc.b $BD, $BB, $B9, $B7, $B5, $B3.
$B1,$AE,$AC, $AA
dc.b $A8, $A5, $A3, $A1, $9E, $9C
$9A, $97, $95, $93
dc.b $91, $8F, $8D, $8A, $88, $86, $83,
$81,$7F,$7C
dc.b $7A, $78, $76, $73, $71, $6F, $6D,
$6B, $69, $67
dc.b $65, $63, $62, $60, $5E, $5C, $5B,
$59, $58, $56
dc.b $55, $54, $52, $51, $50, $4F, $4E,
$4D, $4C, $4B
dc.b $4B, $4A, $49, $49, $49, $48, $48,
$48, $48, $48
dc.b $48, $48, $48, $48, $48, $49, $49,
$4A, $4A, $4B
dc.b $4C, $4D, $4D, $4E, $4F, $51,
$52, $53, $54, $56
dc.b $57, $59, $5A, $5C, $5D, $5F,
$61, $62, $64, $66
dc.b $68, $6A, $6C, $6E, $70, $72, $75,
$77, $79, $7B
dc.b $7D, $80, $82, $84, $87, $89, $8B,
$8E, $90, $92
dc.b $ff