home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga MA Magazine 1998 #6
/
amigamamagazinepolishissue1998.iso
/
coders
/
assembler-kurs
/
lektionen
/
lektion7.txt
< prev
next >
Wrap
Text File
|
1977-12-31
|
92KB
|
2,007 lines
ASSEMBLERKURS - LISTING 7
In dieser Lektion werden wir über die Sprites, den Joystick und den
logischen Operationen des 68000ers wie AND, OR, EOR,NOT,LSR,ROL, etc
sprechen.
Erinnert euch, "V LISTINGS3" zu tippen, um die .raw - Files, die sich in
dieser Directory befinden, laden zu können. Dort befinden sich auch die
Listings aus dieser Lektion.
Die Sprites sind grafische Objekte mit einer präzisen Größe, maximal 16
Pixel breit, die sich unabhängig von den Bitplanes bewegen lassen. Der
Mauspointer z.B. ist ein solcher Sprite, er wird vom Betriebssystem
verwaltet. Er kann sich überall bewegen, ohne sich um die "darunter"
liegenden Bitplanes Gedanken machen zu müssen.
Die Sprites könnte man als "Geister"-Bilder auffassen, die sich "über" den
Bitplanes bewegen, aber achtung: nicht alles, was sich bewegt, sind
Sprites! Denn es können maximal 8 davon dargestellt werden, da es nur 8
Pointer in der Copperlist für sie gibt:
COPPERLIST:
SpritePointers:
dc.w $120,0,$122,0 ; Pointer für Sprite 0
dc.w $124,0,$126,0 ; Pointer für Sprite 1
dc.w $128,0,$12a,0 ; "" "" "" 2
dc.w $12c,0,$12e,0 ; "" "" "" 3
dc.w $130,0,$132,0 ; "" "" "" 4
dc.w $134,0,$136,0 ; "" "" "" 5
dc.w $138,0,$13a,0 ; "" "" "" 6
dc.w $13c,0,$13e,0 ; "" "" "" 7
Die Pointer auf die Sprites heißen SPRxPT (an Stelle der "x" kommt
natürlich die Nummer des Sprites: wir haben also SPR0PT, SPR1PT, ...,
SPR7PT, und wenn wir von SPRxPT sprechen, meinen wir generell alle 8
Pointer). Bis jetzt haben wir sie in der Copperlist immer auf NULL
gesetzt, damit sie uns nicht stören, denn wenn sie "frei rumlaufen" haben
wir sie immer auf unseren Bildern sitzen.
Die Sprites sind vom Rest des Screens isoliert, fast so, als wären sie in
einem durchsichtigen Couvert über dem Bildschirm, sie sind immer in
Lowres, auch wenn der Screen in HiRes oder Interlace arbeitet. Ein Beweis
dafür, daß sie mit den Bitplanes nichts zu tun haben ist, daß man sie,
wenn wir sie bewegen wollen, nicht löschen und weiter hinten neuzeichnen
müssen, wie es der Fall wäre, wenn wir ein Stück Grafik in einem Bitplane
bewegen möchten.
Um einen Sprite zu bewegen, brauchen wir nur seine Koordinaten ändern,
indem wir mit wenigen und schnellen Befehlen auf einige dafür bestimmten
Bytes am Anfang der Spritestruktur selbst zugreifen. Wenn die Sprites
nicht ausreichen, um Raumschiffe und Figuren in einem Spiel darzustellen,
dann verwenden wir den Blitter, um Grafikblöcke (Bob) zu kopieren. Wir
werden später darauf zurückkommen. Wie schon erwähnt, ist die maximale
Breite eines Sprite 16 Pixel, während die Höhe beliebig sein kann, auch
den ganzen Bildschirm, 256 Zeilen. Um z.B. das Monster am Ende des Level
darzustellen, könnten wir z.B. alle 8 Sprites zusammenkleben und somit
16*8=128 Pixel erreichen. Dieses Monster wäre aber recht farblos, da zur
Zeit die Sprites nur 3 Farben haben. Die vierte ist der "durchsichtige"
Teil, wo also der Hintergrund durchscheint, die darunterliegenden
Bitplanes.
Die Charakteristik der Sprites ist, daß sie einfach herzustellen und zu
animieren sind. Denn man kann sie einfach mit einem Malprogramm malen,
Hauptsache, sie sind nicht breiter als 16 Pixel und sind in drei Farben
plus Hintergrund gehalten. Danach einfach durch den IFF-Konverter jagen
und in SPRITE konvertieren.
Oder sie können direkt in Binär gezeichnet werden, wie wir schon bei den
8x8 Fonts gesehen haben:
- Plane 1 - - Plane 2 - ; Die Überlagerung dieser
; zwei "Planes" aus Bits
dc.w %0111110000000000,%0111110000000000 ; bestimmt die Farbe. Das
dc.w %1000001000000000,%1111111000000000 ; ist der Default - Maus-
dc.w %1111010000000000,%1000110000000000 ; pointer des Kickstart 1.3
dc.w %1111101000000000,%1000011000000000 ; Erkennt ihr ihn wieder??
dc.w %1111110100000000,%1001001100000000
dc.w %1110111010000000,%1010100110000000
dc.w %0100011101000000,%0100010011000000
dc.w %0000001110100000,%0000001001100000
dc.w %0000000111100000,%0000000100100000
dc.w %0000000011000000,%0000000011000000
dc.w %0000000000000000,%0000000000000000
dc.w 0,0 ; Zwei auf NULL gesetzte Word signalisierne das
; Ende des Sprite.
In diesem Fall ist die Breite 16 Pixel, und nicht 8 wie bei den Fonts,
weil wir ihn in einem Word (dc.w) und nicht in einem Byte zeichnen.
Weiters hat er 3 Farben plus die Durchsichtige, also insgesamt 4, wie ein
Bild mit 2 Bitplanes, es braucht also ein Paar von "Planes", genau wie bei
den Bitplanes. Ihre Überlagerung bestimmt die Farbe eines jeden Punktes.
Sie kann wie folgt sein:
Plane 1 - Plane 2
Binär: 0 - 0 = COLOR 0 (Durchsichtig)
Binär: 1 - 0 = COLOR 1
Binär: 0 - 1 = COLOR 2
Binär: 1 - 1 = COLOR 3
Denn mit 2 Planes ergeben sich vier Überlagerungsmuster: %00, %01, %10,
%11.
Um die Position des Sprite zu bestimmen, müssen wir nichts anderes tun als
die X und Y-Koordinate in den ersten Bytes des Sprite selbst einzufügen.
Der Sprite besteht nämlich vor den Daten des Bildes aus vier Bytes, oder
zwei Words, den sogenannten KONTROLLWORDS, und da hinein kommen die
Positionskoordinaten auf dem Bildschirm. Um genauer zu sein, das erste
Byte, VSTART genannt, beinhaltet die vertikale Position des Spriteanfangs;
das zweite Byte hingegen die horizontale Position (HSTART). Das Dritte
bekommt das Ende des Sprites, vertikal gesehen: um diesen Wert zu finden,
ganz einfach die Höhe des Sprites mit der Anfangskoordinate des selben
addieren und rein damit. Das Resultat ist nach Adam Riese das Ende des
Sprites.
Im vierten Byte kommen die Spezialfunktionen, wie immer verweise ich auf
später.
VSTART und HSTART (Vertical Start und Horizontal Start) sind also die
Koordinaten der linken, oberen Ecke des Sprites:
#....
.....
.....
.....
.....
VSTOP hingegen ist die vertikale Position des Endes des Sprite:
.....
.....
.....
.....
##### -> Zeile, in der der Sprite aufhört.
Um z.B. einen Sprite an Position XX=$90 und YY=$50 anzuzeigen, der 20
Pixel lang ist, werden wir so vorgehen:
;AYAX EY - AY=Anfang Y, AX=Anfang X, EY=Ende Y
SPRITE:
dc.w $5090,$6400 ;Y=$50, X=$90, Höhe= $50+20, alos $64
; hier beginnen die Daten der 2 Plaenes des Sprite:
dc.w %0000000000000000,%0000110000110000
dc.w %0000000000000000,%0000011001100000
...
dc.w 0,0 ; Ende des Sprite
Das erste Byte, VSTART, ist auf $50, das Zweite, HSTART, auf $90, und das
Dritte, die vertikale Position des Spriteendes, auf $64, also $50 + 20.
Also die Anfangsposition + Länge des Sprite. Das vierte Byte lassen wir
vorerst auf 0. Ich sage euch gleich schon, daß das HSTART einen Sprite um
jeweils 2 Pixel verstellt, wenn wir also $51 eingeben, werden wir auf $52
gelangen. Es sind nur Zweierschritte möglich. Wir werden aber sehen, wie
unter Verwendung des SpeizialBytes Nr. 4 diese Situation in den Griff zu
bekommen ist, und das ein Scroll von einem Pixel möglich ist. Was den
vertikalen Scroll angeht, VSTART und VSTOP besorgen uns schon einen
schönen 1-Pixel-Schritt. Das einzige Limit ist die Videozeile $FF, darüber
hinaus kommen wir nur, wenn wir ein Bit im vierten Byte verwenden. Am
Anfang werden wir aus Gründen der Einfachheit nur die Bytes VSTART, VSTOP
und HSTART verwenden, und die Zeierschritte in der Horizontalen hinnehmen.
Erst später werden wir sehen, wie wir "flüssigere" Scrolls hinkriegen.
Erinnert euch also an die Eigenschaft, das z.B. mit einem:
ADDQ.B #1,HSTART
der Sprite um 2 Pixel, und nicht um einen, verstellt wird.
Um auf die 3 Bytes VSTART/HSTART/VSTOP zuzugreifen, könnte man etwa so
tun:
MOVE.B #$50,SPRITE ; VSTART = $50
MOVE.B #$90,SPRITE+1 ; HSTART = $90
MOVE.B #$64,SPRITE+2 ; VSTOP = $64 ($50+20)
Oder man kann ein Label für jedes Byte definieren, und somit alles klarer
gestalten:
SPRITE:
VSTART: ; Beginn Anfangsposition VERTIKAL
dc.b $50
HSTART: ; Beginn Anfangsposition HORIZONTAL
dc.b $90
VSTOP:
dc.b $64 ; Endposition VERTIKAL
dc.b $00 ; Byte für Spezialanwendungen, im Moment auf 0
; hier beginnen die Daten für die Planes des Sprite:
dc.w %0000000000000000,%0000110000110000
dc.w %0000000000000000,%0000011001100000
...
dc.w 0,0 ; Ende des Sprite
In diesem Fall würden wir auf die Label VSTART, VSTOP und HSTART agieren:
ADDQ.B #1,HSTART ; bewegt den Sprite um 2 Pixel nach rechts
; (2 Pixel und nicht 1, wie oben erklärt)
SUBQ.B #1,HSTART ; bewegt den Sprite um 2 Pixel nach links
Um den Sprite nach Oben oder Unten zu bewegen müssen wir uns aber
erinnern, beide Bytes VSTART und VSTOP zu verändern, denn es ist klar, daß
in diesem Fall das erste Pixel wie auch das Letzte seine Position ändert:
ADDQ.B #1,VSTART ; \ Bewegt den Sprite um 1 Pixel nach Unten
ADDQ.B #1,VSTOP ; /
SUBQ.B #1,VSTART ; \ Bewegt den Sprite um 1 Pixel nach Oben
SUBQ.B #1,VSTOP ; /
Zur Wiederholung, das ist die Struktur des Sprite:
Erstes Kontrollword, zweites Kontrollword
erste Zeile (.w) des Plane 1, erste Zeile (.w) des Plane 2
zweite Zeile (.w) des Plane 1, zweite Zeile (.w) des Plane 2
dritte Zeile (.w) des Plane 1, dritte Zeile (.w) des Plane 2
vierte Zeile (.w) des Plane 1, vierte Zeile (.w) des Plane 2
fünfte Zeile (.w) des Plane 1, fünfte Zeile (.w) des Plane 2
...
DC.W 0,0 ; die letzte Zeile muß zwei Nullen enthalten
Die Daten des Sprite sind in Plane 1 und Plane 2 unterteilt, einzig und
alleine, um anzuzeigen, daß bei deren Überlagerung die 3 Farben plus
Transparent zu Stande kommen, analog wie bei den Bitplanes, es ist aber
nicht damit zu verwechseln!
DIE FARBEN DER SPRITE
Um die Farben der Sprites zu definieren müssen die gleichen Register
verwendet werden wie bei den Bitplanes, da der Amiga nur 32 Farbregister
besitzt. Die Ingeneure haben gedacht, den Sprites die Farben vom 16 bis
zum 31 zuzuteilen, somit können die Sprites, wenn das Bild nicht in in 5
Bitplanes -oder 32 Farben - ist, andere benutzen als das Bild selbst.
Ansonsten haben sie 16 Farben gemeinsam mit dem darunterliegenden Bild.
Schauen wir uns mal an, wie wir die Farben des ersten Sprite hinbekommen:
(Die Sprites sind von ß bis 7 durchnumeriert)
COLOR 0 des Sprite 0 = TRANSPARENZ, wird nicht definiert
COLOR 1 des Sprite 0 = COLOR17 ($dff1a2)
COLOR 2 des Sprite 0 = COLOR18 ($dff1a4)
COLOR 3 des Sprite 0 = COLOR19 ($dff1a6)
Color 0, oder die vierte, ist die Transparenz, sie wird nicht definiert.
Sehen wir vor dem weitermachen, das erste Beispiel einer Anzeige eines
Sprite in Listing7a.s In diesem Beispiel wird der erste Sprite
angepointet, die anderen sieben beliben auf NULL. Um einen Sprite
anzupointen wird gleich vorgegangen wie bei den Bitplanes, da sie die
Pointer gleich funktionieren wie bei den Bitplanes:
MOVE.L #MEINSPRITE,d0 ; Adresse des Sprite in d0
LEA SpritePointers,a1 ; Pointer in der Copperlist
move.w d0,6(a1)
swap d0
move.w d0,2(a1)
Ich erinnere daran, daß um ein Sprite anzuzeigen, mindestens ein Bitplane
"eingeschaltet" sein muß, ohne Bitplane keine Sprites. Des Weiteren wird
ein Sprite "abgeschnitten", wenn er außerhalb des Videofensters gelangt,
das zuvor mit DIWSTART und DIWSTOP definiert wurde, er kann nur innerhalb
angezeigt werden. Um ihn z.B. in der Mitte eines 320x256 Screens
anzuzeigen, auf Position 160x128, ist weiters zu beachten, daß die erste
Koordinate links oben nicht 0,0 ist, sondern $40, $2c, deswegen muß $40
zur X-Koordinate und $2c zur YKoordinate addiert werden. $40+160, $2c+128
entprechen also den Koordinaten 160, 128 in einem Screen 320x200 ohne
Overscan.
Da wir noch nicht die 1-Pixel-Kontrolle über die Sprites haben was die
horiziontale Positionierung angeht, müssen wir nicht 160 addieren, sondern
160/2, wenn wir die Mitte anpeilen wollen:
HSTART:
dc.b $40+(160/2) ; Positionierung in der Mitte des Screen
...
Hier ein Schema des Screens, in dem der sichtbare Teil, also das
Videofenster, hell dargestellt ist, und des gesamten Schirms, außerhalb
des Randes, der bei 0,0 beginnt, mit ###. Beachtet daß das Videofenster
bei $40 XX und $2c YY beginnt.
(0,0) __
\
\
+---------------------------+
|###########################|
/\ |###########################|
|| |###+-------------------+###|
|| |###| $40,$2c |###| __ Rand des sichtbaren
|| |###| ______ |###| / Teil (Videofenster)
|| |###| /Sprite\ |###|/
|| |###| |++XX++| |###/
|| |###| \/\/\/\/ |##/|
|###| |#/#|
Y-Achse |###| |/##|
|###| |###|
|| |###| |###|
|| |###| |###|
|| |###| |###|
|| |###| |###|
|| |###+-------------------+###|
\/ |###########################|
|###########################|
+--------------------------+
<----- Y-Achse ----->
Die horizontale Position des Sprites kann von 0 bis 447 gehen, es ist aber
logisch, daß sie bei einem Screen zu 320 Pixel Breite von 64 bis 383 geht.
Die vertikale hingegen kann von 0 bis 262 gehen, aber um auf einem
PAL-Screen sichtbar zu sein (256 Zeilen), muß sie von 44 ($2c) bis zum
Ende des Screens reichen, also bis 44+256=300 (12c). Bis jetzt haben wir
nur die Zone bis $FF erreicht, wir werden später sehen, wie wir bis $12c
kommen.
In Listing7b.s wird ein Sprite am Bildschirm rauf und runter gejagt, das
geschieht mit ADD´s und SUB´s auf die zwei Kontrollword.
In Listing7c.s wird der Sprite horizontal bewegt, indem er die Werte aus
einer vordefinierten Tabelle liest anstatt ADD und SUB anzuwenden. In
Listing7d.s wird er in Vertikal zum Springen gebracht. In Listing7e.s
werden die zwei Koordinaten (XX und YY) von zwei verschiedenen Tabellen
kontrolliert, um eine Kreisbewegung, Ellipse, etc. zu erzeugen. In diesem
Beispiel wird auch erklärt, wie man sich eigene Tabellen erstellt!
Bevor ihr weitermacht, ladet die Beispiele in einen anderen Buffer und
führt sie aus, VERSTEHT sie und lest die Kommentare.
Bis jetzt haben wir nur einen Sprite angezeigt, sehen wir nun, was wir
wissen müssen, um alle 8 auf den Monitor zu bringen.
Zum Anfang mal festgehalten: jeder Sprite hat eine individuelle Position,
er ist in keiner Weise an die anderen gebunden, da jeder ein eigenes
VSTART, VSTOP und HSTART in seinen zwei AnfangsWord hat. Was aber die
Farben angeht (und andere Eigenschaften, wie etwa Kollisionen, die wir
später sehen werden), da sind die Sprites nicht ganz unabhängig, sie sind
zu Zweierpaaren zusammengehängt. Es gibt also 4 Paare von 2 Sprites:
Sprite0+Sprite1, Sprite2+Sprite3, Sprite4+Sprite5, und schlußendlich
Sprite6+Sprite7. Wenn ich von nun an von "Spritepaar" sprechen werde,
meine ich nicht ein x-beliebiges Paar, aber genau eines von diesen vieren.
Bei den Farben hängen sie zusammen, d.h. jeweils ein Paar hat die gleiche
Palette, die Paare untereinander können verschiedene Farben haben. Wir
wissen, daß die 3 Farben des Sprite0 mit den Registern COLOR17, COLOR18
und COLOR19 definiert werden. Diese 3 Farben gelten also auch für seinen
"Bruder", dem Sprite1.
Jedes Paar hat eine andere Palette, da es insgesamt 16 Register dafür
gibt, vom 16 bis zum 31. Wenn wir davon ausgehen, daß jeder Sprite 4
Farben hat ( 1 davon Transparent), dann bräuchten wir 8*4=32 Register. Wir
haben aber nur 16. Da wir nun aber 8 Sprites mit 4 Farben haben, hier eine
Tabelle, die angibt, welcher Sprite welche Farbe bekommt:
Sprite Binärwert Farbregister:
------ -------------- ------------------
Paar 1: 0 o 1 00 Nicht verwendet da Transparent
01 Color17 - $dff1a2
10 Color18 - $dff1a4
11 Color19 - $dff1a6
Paar 2: 2 o 3 00 Nicht verwendet da Transparent
01 Color21 - $dff1aa
10 Color22 - $dff1ac
11 Color23 - $dff1ae
Paar 3: 4 o 5 00 Nicht verwendet da Transparent
01 Color25 - $dff1b2
10 Color26 - $dff1b4
11 Color27 - $dff1b6
Paar 4: 6 o 7 00 Nicht verwendet da Transparent
01 Color29 - $dff1ba
10 Color30 - $dff1bc
11 Color31 - $dff1be
Machen wir ein praktisches Beispiel: um in der Copperlist die Farben der 8
Sprites zu definieren ist folgendes notwendig:
dc.w $1A2,$F00 ; color17, - COLOR1 der sprite0/1 -ROT
dc.w $1A4,$0F0 ; color18, - COLOR2 der sprite0/1 -GRÜN
dc.w $1A6,$FF0 ; color19, - COLOR3 der sprite0/1 -GELB
dc.w $1AA,$FFF ; color21, - COLOR1 der sprite2/3 -WIEß
dc.w $1AC,$0BD ; color22, - COLOR2 der sprite2/3 -WASSER
dc.w $1AE,$D50 ; color23, - COLOR3 der Sprite2/3 -ORANGE
dc.w $1B2,$00F ; color25, - COLOR1 der Sprite4/5 -BLAU
dc.w $1B4,$F0F ; color26, - COLOR2 der Sprite4/5 -VIOLETT
dc.w $1B6,$BBB ; color27, - COLOR3 der Sprite4/5 -GRAU
dc.w $1BA,$8E0 ; color29, - COLOR1 der Sprite6/7 -HELLGRÜN
dc.w $1BC,$a70 ; color30, - COLOR2 der Sprite6/7 -BRAUN
dc.w $1BE,$d00 ; color31, - COLOR3 der Sprite6/7 -DUNKELROT
BEMERKUNG: Wenn ihr als Hintergrund ein Bild in 2,4,8 oder 16 Farben habt,
dann gibt´s keine Probleme, aber wenn ihr einen Bildschirm in 32 Farben,
also 5 Bitplanes, aktiviert, dann müßt ihr bedenken, daß die letzten 16
Farben von Bild und Sprites geteilt werden. Die Farbe muß also für´s Bild
stimmen, genauso wie für die Sprites, die Farben müssen "Multifunktionell"
sein.
DIE VIDEOPRIORITÄT ZWISCHEN DEN SPRITES
Wenn zwei oder mehr Sprites am Bildschirm angezeigt werden, dann kann es
leicht vorkommen, daß sie sich überlappen. In diesem Fall wird der Sprite
mit der niedrigeren Priorität überdeckt. Die Prioritäten sind immer die
gleichen, der Sprite mit der kleineren Nummer hat Vorrang gegenüber einem
mit Höhere. Daraus folgt, daß Sprite0 alle anderen Sprites überdecken
kann, und Sprite7 von allen anderen überdeckt werden kann. Hier ein
kleines Schema:
_______
| |
___|___7 |
| |___|
__|___6 |
| |__|
__|___5 |
| |__|
___|___4 |
| |___|
___|___3 |
| |___|
___|___2 |
| |___|
___|___1 |
| |___|
| 0 |
|_______|
Überprüft das, indem ihr Listing7f.s in einen anderen Buffer ladet und
ausführt. Er zeigt 8 Sprites an, und nach dem Druck auf die linke
Maustaste werden sie übereinandergelegt, um die Prioritäten hervorzuheben.
Rechte Taste um auszusteigen.
"ATTACHED"-SPRITES
Es existiert auch eine Methode, um die Sprites in Zweierpaaren
zusammenzuschweißen, einen über dem anderen. Damit halbiert sich die
Anzahl der verfügbaren Sprites, aber es sind als Trost 16 Farben möglich
(na ja, 15 + Transparent). Sie können folgendermaßen kombiniert werden:
SPRITE0+SPRITE1 - Sprite ATTACCHED Nummer 1
SPRITE2+SPRITE3 - Sprite ATTACCHED Nummer 2
SPRITE4+SPRITE5 - Sprite ATTACCHED Nummer 3
SPRITE6+SPRITE7 - Sprite ATTACCHED Nummer 4
In der Praxis werden die selben Sprites verbündet, die im Normalmodus
schon eine Verbindung eingegangen sind, die der selben Palette. Die vier
"Attached"-Sprites besitzen alle die gleiche Palette, da ja nur mehr 16
Farben übrig sind, von COLOR16 bis COLOR31.
Die ATTACHED-Sprites funktionieren auf folgende Art und Weise:
normalerweise hat ein Sprite maximal 4 Überlappungsmöglichkeiten, also %00
für die Transparenz, %01, %10, %11 für die drei Farben. Im Attached-Modus
werden die Planes des Sprite übereinandergelegt und ergeben somit 16
mögliche Situationen, denn 2 Planes vom ersten Sprite plus 2 Planes vom 2
Sprite ergeben 4 Planes, oder %0000 bis %1111, also 16 statt 4
Möglichkeiten. In der folgenden Tabelle werden die 16 möglichen
Überlappungsmuster aufgelistet, daneben die dazugehörige Farbe.
Sprite Binär Nummer des
Farbe wert Farbregisters
------- ------ --------------
0 0000 Color16 - NICHT VERWENDET, TRANSPARENT
1 0001 Color17 - $dff1a2
2 0010 Color18 - $dff1a4
3 0011 Color19 - $dff1a6
4 0100 Color20 - $dff1a8
5 0101 Color21 - $dff1aa
6 0110 Color22 - $dff1ac
7 0111 Color23 - $dff1ae
8 1000 Color24 - $dff1b0
9 1001 Color25 - $dff1b2
10 1010 Color26 - $dff1b4
11 1011 Color27 - $dff1b6
12 1100 Color28 - $dff1b8
13 1101 Color29 - $dff1ba
14 1110 Color30 - $dff1bc
15 1111 Color31 - $dff1be
In der COPPERLIST müssen sie also so definiert werden:
dc.w $1A2,$F00 ; color17, FARBE 1 für die Attached-Sprites
dc.w $1A4,$0F0 ; color18, FARBE 2 für die Attached-Sprites
dc.w $1A6,$FF0 ; color19, FARBE 3 für die Attached-Sprites
dc.w $1A8,$FF0 ; color20, FARBE 4 für die Attached-Sprites
dc.w $1AA,$FFF ; color21, FARBE 5 für die Attached-Sprites
dc.w $1AC,$0BD ; color22, FARBE 6 für die Attached-Sprites
dc.w $1AE,$D50 ; color23, FARBE 7 für die Attached-Sprites
dc.w $1B0,$D50 ; color24, FARBE 7 für die Attached-Sprites
dc.w $1B2,$00F ; color25, FARBE 9 für die Attached-Sprites
dc.w $1B4,$F0F ; color26, FARBE 10 für die Attached-Sprites
dc.w $1B6,$BBB ; color27, FARBE 11 für die Attached-Sprites
dc.w $1B8,$BBB ; color28, FARBE 12 für die Attached-Sprites
dc.w $1BA,$8E0 ; color29, FARBE 13 für die Attached-Sprites
dc.w $1BC,$a70 ; color30, FARBE 14 für die Attached-Sprites
dc.w $1BE,$d00 ; color31, FARBE 15 für die Attached-Sprites
Um zwei Sprites zu vermählen muß nur das Bit 7 des zweiten Kontrollword
des ungeraden Sprite im Paar auf 1 gesetzt werden (also Bit 7 im
berühmt-berüchtigten vierten Byte der Spezialfunktionen). Um z.B. die
Sprites 0 und 1 zusammenzuschließen, einfach dieses Bit im Sprite1 setzen,
um Sprite 4 und 5 zu koppeln Bit 7 im Byte 4 des Sprite 5. Es ist wohl
einleuchtend, daß die Sprites die gleichen Koordinaten haben müssen, damit
die vier Planes genau übereinanderliegen, sonst wird wohl kein richtiges
Überlagerungsmuster entstehen! Machen wir ein Beispiel: um die Sprites 0
und 1 zusammenzubringen, müssen wir das Bit 7 des vierten Words im Sprite1
auf High (1) setzen:
SPRITE0:
VSTART0: ; Anfangsposition VERTIKAL
dc.b $50
HSTART0: ; Anfangsposition HORIZONTAL
dc.b $90
VSTOP0:
dc.b $64 ; Endposition VERTIKAL
dc.b $00 ; Bei geraden Sprites muß Bit 7 nicht gesetzt werden.
; ab hier beginnen die Daten für die zwei Planes des Sprite
dc.w %0000000000000000,%0000110000110000
dc.w %0000000000000000,%0000011001100000
...
dc.w 0,0 ; Ende Sprite0
SPRITE1:
VSTART1: ; Anfangsposition VERTIKAL
dc.b $50
HSTART: ; Anfangsposition HORIZONTAL
dc.b $90
VSTOP:
dc.b $64 ; Endposition Vertikal
;76543210
dc.b %10000000 ; BIT 7 GESETZT! ATTACCHED MODE
; für Sprite 0/1
; ab hier beginnen die Daten für die 2 Planes des Sprite
dc.w %0000000000000000,%0000110000110000
dc.w %0000000000000000,%0000011001100000
...
dc.w 0,0 ; Ende Sprite1
Um also die Sprites "Attached" zu gestalten, einfach das siebte Bit im
vierten Byte der ungeraden Sprites auf 1 setzen, also in den sprites 1,3,5
und 7.
Um sich einen Sprite in 16 Farben zu schaffen muß man ihn zuerst mit einem
Malprogramm zeichnen und dann mit dem IFF-Konverter KefCon konvertieren,
denn es wäre recht schwierig, so über dem Daumen gepeilt die 16 Farben
richtig zu kalkulieren, da es 4 Planes sind, die zusammen 16 Möglichkeiten
ergeben!
Ladet und führt dann Listing7g.s aus. Es zeigt einen Sprite in 16 Farben
an, im Attached-Modus. Darin ist auch beschrieben, wie man einen Sprite
mit dem KefCon herstell, sei er nun zu 4 oder 16 Farben.
Es ist möglich, gleichzeitig Sprites in 4 und in 16 Farben darzustellen.
Z.B. könnten Sprite 0 und 1 Attached sein und die anderen nicht, oder jede
beliebige Kombination.
Im Beispiellisting Listing7h.s werden 4 Attached-Sprites angezeigt, jeder
von ihnen in einer unabhängigen Bewegung.
Hier angekommen werdet ihr euch sicher fragen, wieso immer noch nicht das
Problem der zwei Pixel pro Schritt nicht eliminiert wurde, und wir immer
noch nicht Pixel für Pixel uber den Schirm gleiten können. Gut, es ist an
der Zeit, das in den Griff zu bekommen. Aber dazu müssen wir vorher noch
einen neuen Befehl des 68000 lernen, welcher auf den einzelnen Bits einer
Zahl operiert: --- LSR ---
Dieses Kürzel steht für "LOGIC SHIFT RIGHT", oder "LOGISCHER SCROLL DER
BITS NACH RECHTS". Anders ausgedrückt, wenn eine Binärzahl in d0 %00111
ist, dann wird sie nach einem LSR #1,d0 folgendermaßen aussehen: %00011,
nach einem LSR #2,d0 so: %00001.
Auf die gleiche Art wird ein %00110010 nach einem LSR #1,d0 zu %00011001,
und nahc einem LSR#5,d0 wäre nur mehr ein %00000001 erhalten. Die Zahl
wird also binär betrachtet, es ist so, als wären sie auf einem Tischtuch,
an dem wir ziehen: wenn wir um #1 ziehen, dann bewegen wir das Tischtuch
mitsamt seinen BitTellern darauf, und das erste in der Reihe wird zu Boden
fallen... Wenn man zuviel zieht, dann kommen alle BitTeller über den
Tischrand und man hat den Tisch abgeräumt (auf NULL gesetzt). Aber was hat
dieser Befehl mit dem Byte HSTART zu tun ???
Das Probelm ist folgendes: wie ihr wißt, gibt es weitaus mehr horizontale
Positionen als nur $FF (255), man denke nur daran, daß ein LowRes-Screen
320 Pixel breit ist. Wenn wir eine Zahl erreichen wollen, die über dem 255
ist (8 Bit, vom 0 bis zum 7) dann müssen wir ein Bit mehr zu Verfügung
haben: somit kommen wir nicht mehr nur bis maximal %11111111 ($ff),
sondern bis %111111111, oder 511. Das wäre ja super für das HSTART! Aber
wohin geben wir dieses zusätzliche Bit?? Die Scherzkekse von Ingeneuren
haben gedacht, es wäre im famosen vierten KontrollByte gut aufgehoben, in
dem wir auch die Sprites zusammenpappen. Nun gut, das siebte Bit ist für
den Attached-Modus reserviert, dann haben wir ja noch die sechs unteren
frei. Also nehmen wir Bit 0 für diese Aufgabe, und machen dieses Bit zum
NIEDERWERTIGSTEN Bit der horizontalen Koordinate, oder anders, zum Bit 9
der Koordinate. Damit wird das Kontrollbyte wie folgt aufgeteilt:
;876543210 ; Zahl mit 9 bit, für die HSTART - Koordinate
%111111111
\_____/ \/
| |
8 hochwert. |
Bits im |
Byte HSTART |
|
|
|
|
|
Bit 0 der
Zahl zu 9 Bit,
steht im Bit 0
des vierten
Kontrollbyte
Wenn ihr das niederwertige Bit einer 9 Bit breiten Zahl entfernt, dann
werdet ihr immer eine gerade Zahl erhalten, da dieses Bit immer auf 0 ist.
Denn eine Zahl ist immer dann ungerade, wenn dieses lezte Bit auf 1 steht,
testet mal mit "?100" und "?101". Ihr werdet feststellen, daß gerade
Zahlen immer eine NULL hinten stehen haben, ungerade eine Eins. Bis jetzt
konntne wir also in Schritten zu zwei Pixel scrollen, und wir mußten aus
diesem Grund auch immer nur die Hälfte des Effektivwertes in HSTART
einsetzen. Um auch die ungeraden Adressen zu erreichen und um die reale
Adresse als Input zu geben müssen wir die Koordinate in niederwertiges Bit
und hochwertiges Byte aufteilen. Danach dieses Bit in die richtige Stelle
und das Byte hineinschreiben. Um dies zu tun, stellt euch vor, ihr habt
die ungerade Adresse 35: (%00100011). Als erstes müssen wir kontrollieren,
ob das Bit 0 des vierten Kontrollbytes gesetzt werden muß oder nicht.
Dafür müssen wir nur mit einem BTST kontrollieren, ab unsere Koordinate
das Bit 0 auf 1 hat oder nicht, und dann dementsprechend reagieren: nehmen
wir an, wir haben die Koordinate in d0:
btst #0,D0 ; niederwertiges Bit der X-Koordinate =0?
beq.s BitNiederNULL
bset #0,MeinSprite+3 ; Setzen das niederw. Bit von HSTART
bra.s PlaceCoords
BitNiederNull:
bclr #0,MeinSprite+3 ; Löschen das niederw. Bit von HSTART
PlaceCoords:
....
Nun haben wir das niederwertige Bit von HSTART gesetzt (oder gelöscht),
nun kommt der Rest der Zahl an die Reihe, die höherwertigen 8 Bit. Aber es
gibt ein Problem: die Zahl hat 9 Bit, und wir brauchen nur 8! Und hier
kommt die Anweisung LSR ins Spiel!!! Denn sie läßt die Bits der Zahl alle
um eins nach rechts rutschen, somit verschwindet das 9. Bit und wir haben
die anderen 8 schon an der richtigen Stelle. Schauen wir uns an, wie die
Routine PlaceCoords weitergeht:
lsr.w #1,D0 ; SHIFTEN 1 Bit nach rechts, oder "verschieben"
; den Wert von HSTART, um ihn in den in das
; Byte von HSTART geben zu können, ohne dem
; niederwertigen, 9. Bit.
move.b D0,HSTART ; Setzen diesen Wert XX ins Byte HSTART
rts
In diesem Fall hatten wir die Koordinate %00100011 (35), so sieht sie nach
dem LSR #1,d0 aus: %00010001!!!! Das Byte ist so, wie es in HSTART kommen
muß.
In Listing7i.s wird diese Routine angewandt, um endlich einen Sprite schön
flüssig und gleichmäßig über den Bildschirm zu schicken, wie es nur der
Amiga kann.
Jetzt ist es an der Zeit, auch noch das letzte Limit zu eliminieren, das
in vertikaler Richtung: in dieser Richtung konnten wir zwar schon in
Einerschritten gehen, aber nur bis Zeile $FF. Um dieses zu beseitigen,
haben die Erfinder des Amiga bei VSTART/VSTOP eine andere Technik
angewandt, als bei HSTART: eigentlich braucht auch VSTART/VSTOP eine Zahl
mit neun Bit, aber anstatt das niederwertigste haben sie hier das
hochwertigste "herausgeführt". Somit ist in VSTART/VSTOP jede Zahl bis $FF
gültig, also bis 256, danach muß das neunte Bit gesetzt werden, um darüber
hinaus zu kommen, und wo wird das wohl liegen? Genau, im vierten
Kontrollbyte. Nach $FF kommt $100, $101 usw., der Zählvorgang beginnt
wieder von vorne, nur mit dem neunten Bit auf High. Sehen wir nun, wie wir
eine Routine machen können, die im Grunde genommen gleich ist wie die im
horizontalen Sinn, die also mit der realen Koordinate startet (es ist ein
Word notwendig) und sie dann in hochwertiges Bit und niederwertiges Byte
zerteilt. Achtung, hier haben wir auch VSTOP zu modifizieren, nur mit
VSTART ist nix getan!! Ach ja, habe fast vergessen: das hochwertige Bit
von VSTOP ist in Bit 1 des vierten Kontrollbytes zu finden, VSTART in Bit 2:
MOVE.w (A0),d0 ; kopiert das Word aus der Tabelle in d0
ADD.W #$2c,d0 ; addiert den Offset vom Anfang des Screens
MOVE.b d0,VSTART ; kopiert das Byte in VSTART
btst.l #8,d0 ; Zahl größer als $FF?
beq.s NichtVSTARTSET
bset.b #2,MeinSprite+3 ; Setzt das Bit 8 von VSTART (Zahl > $FF)
bra.s ToVSTOP
NichtVSTARTSET:
bclr.b #2,MeinSprite+3 ; Löscht das Bit 8 von VSTART (Zahl < $FF)
ToVSTOP:
ADD.w #13,D0 ; Addiere die Länge des Sprite um die
; Endposition festzulegen (VSTOP)
move.b d0,VSTOP ; Setze den richtigen Wert in VSTOP
btst.l #8,d0
beq.s NichtVSTOPSET
bset.b #1,MeinSprite+3 ; Setzt das Bit 8 von VSTOP (Zahl > $FF)
bra.w VstopFIN
NichtVSTOPSET:
bclr.b #1,MeinSprite+3 ; Löscht das Bit 8 von VSTOP (Zahl < $FF)
VstopFIN:
rts
Diese Routine funktioniert auf die gleiche Weise wie die vorherige, was
das Setzen des "zusätzlichen" Bits angeht, unterscheidet sich aber darin,
daß sie auf VSTART wie auf VSTOP zugreifen muß, und daß das LSR fehlt (ist
hier überflüssig).
Ihr könnt sie testen, ladet dazu Listing7l.s.
Da wir nun die volständige Kontrolle über die Sprites haben, werden wir
deren Kontrollroutinen etwas optimisieren: als erstes werden wir eine
universelle Kontrollroutine für Sprites erstellen, so, daß wir nicht für
jeden der 8 Sprites jedesmal alles neuschreiben müssen. Es ist eine
Routine mit Parametern notwendig, die als Eingang die Adresse des
gewünschten Sprites und dessen X-Y-Koordinaten erhält. Damit brauchen wir
für jeden Sprite nur mehr ein "BSR ROUTINE" machen, und nicht mehr alles
neuschreiben. Wir können dann diese Routine jedesmal recyclen, wenn wir
Sprites bewegen wollen, eventuell nur kleine Änderungen anbringen. Ein
Beispiel einer solchen Routine findet ihr in Listing7m.s Die
Universalroutine heißt UniBewSprite, und damit sie funktioniert müssen wir
ihr, außer der Adresse des zu bewegenden Sprites und dessen neuen
Koordinaten, auch die Höhe des Sprites übermitteln. Damit errechnet die
Routine den Wert für VSTOP.
Diese Werte werden ihr mittels einiger Register übermittelt: Werte in
Register schreiben, Routine aufrufen, fertig. Um genau zu sein, kommt die
Adresse des Sprite in a1, seine Höhe ins Register d2, die Y-Koordinate in
d0 und die X-Koordinate in d1. Die der Routine "übermittelten" Koordinaten
sind die Werte für einen Screen in 320x256. Die Routine kümmert sich
selbst um das "zentrieren" des Sprites am Monitor, indem sie $40 zur
X-Koordinate und $2c zur Y-Koordinate dazuzählt. Weiters kümmert sie sich
darum, das niederwertige Bit von HSTART und das hochwertige Bit von
VSTART/VSTOP zu setzen.
Kurzum:
;
; Eingangsparameter von UniBewSprite:
;
; a1 = Adresse des Sprite
; d0 = Vertikale Y-Position des Sprites auf dem Screen (0-255)
; d1 = Horizontale X-Position des Sprites auf dem Screen (0-320)
; d2 = Höhe des Sprite
Da wir nun diese Routine haben, die sich ein für alle Mal aller Probleme
annimmt, können wir uns daran machen, sie für einige Applikationen zu
verwenden, die uns mit den Sprites vertrauter macht. Bevor ihr aber
weitermacht, ladet Listing7m.s und wehe dem, wer in LEKTION7.TXT
weiterliest oder andere Listings ladet, bevor er es nicht VOLLSTÄNDIG
verstanden hat. Da die Routine in allen folgenden Beispielen auftreten
wird, wäre es sehr unwirtschaftlich, weiterzumachen, bevor eine Routine
verstanden wurde, die wir dauernd antreffen werden.
In Listing7n.s sehen wir einen Sprite auf dem Bildschirm, der geradlinige
"Schußlinien" einschlägt. Die Koordinaten stammen nicht aus einer Tabelle,
sondern werden nach und nach berechnet. Dadurch bewegt sich der Sprite mit
einer konstanten Geschwindigkeit. Ladet es und führt es aus, wir werden
sehen, wie man einen Sprite an den Ecken des Bildschirmes abprallen lassen
kann.
In Listing7o.s sehen wir hingegen zwei Sprites, die beide von der
Universalroutine bewegt werden. Es ist ein ausgezeichnetes Beispiel, wie
unsere Routine verschieden Sprites, auch verschiedener Größe, problemlos
bewegen kann. Wenn wir keine Parameter verwenden würden, dann müßten wir
für jeden Sprite alles neu schreiben, eine unnütze Zeit- und
Speichervergeudung.
In Listing7p.s sehen wir, immer mit der Universalroutine, wie wir Objekte
kreiren können, die größer sind als 16 Pixel, indem wir Sprites
zusammenflicken. Achtung! Das ist was anderes als die "ATTACHED"- Sprites!
Bei denen werden zwei Sprites genau übereinandergelegt, sie haben die
exakt gleichen Koordinaten, und das Bit "attach" ist in den ungeraden
Sprites gesetzt. Bei "aneinandergereihten" Sprites hingegen ahndelt es
sich um einen "Zusammenschluß" von zwei oder mehreren Sprites, die
nebeneinander oder untereinander auf den Screen gezeichnet werden.
Zusammen ergeben sie dann ein Gesamtbild. Sie müssen genau mit den Rändern
aneinandergereiht werden, wenn wir nicht wollen, daß es aussieht, daß sie
"zerfallen". Es muß kein spezielles Bit gesetzt werden, es handelt sich
nur um normale Sprites, die nebeneinander aufgestellt sind. Hier ein
Beispiel mit einem Raumschiff, das aus zwei Sprites besteht:
(128,65) (128,65) (144,65)
|_ _ _ __ _ _ _ |_ _ _ _ _ _ __|__ _ _ _ _ _ _
| / \ | | / | \ |
__/____\__ / \
| | | | | / | \ |
| | ____/___________\____
| |__________| | | | | | |
\ / | |
|_ _ _\__/_ _ _| | | | | |
| |
| |__________|__________| |
\ /
| \ | / |
\ /
|_ _ _ _ _ _\__|__/_ _ _ _ _ _|
Sprite 0 Sprite 1
Mit dieser Technik lassen sich Ende-Level-Monster schaffen, die bis zu 128
Pixel breit sind (16*8), wenn ihr die Sprites zu 3 Farben verwendet, oder
64 Pixel breit (16*4), wenn ihr Attached-Sprites mit 15 Farben einsetzt.
Wenn das Monster länger als breit ist, z.B. "Humanoid", dann könnt auch
die ganze Länge des Screens verwenden, denn dort gibt´s bekanntlich keine
Limits. In der vertikalen Richtung können wir dann auch die Farben mit dem
Copper vertauschen, und ihm somit die Schuhe anders färben als die Jeans.
MOUSE UND JOYSTICK
Jetzt haben wir gesehen, wie wir den Amiga die Spites bewegen lassen,
wieso bewegen nicht wir sie? Natürlich unter Verwendung einer Maus oder
eines Joysticks. Bevor wir aber lernen, mit diesen Apparaten umzugehen,
müssen wir einige weitere Befehle lernen, die uns Bitmanipulationen in
Registern erlauben: NOT, AND, OR, EOR.
Diese Befehle operieren auf den einzelnen Bits eines Registers (oder einer
Speicherzelle), sei es für das Quellregister wir für das Zielregister. So
z.B. behandeln diese Anweisungen ein Byte nicht als eine Zahl, die aus 8
Bit besteht, sondern wie 8 Bits, die unabhängig voneinander in der Gegend
rumstehen. Das bedeutet, daß die Auswirkung, die ein Befehl auf ein Bit
hat, unabhängig von den weiteren Bits im Register ist.
Als erstes sehen wir uns das NOT an. Es funktioniert nur auf einen
Operanden, dessen Effekt ist das vertauschen der Bits des Operanden. 1
wird durch 0 ersetzt, und 0 durch 1. Wenn wir z.B. im Register d0 die Zahl
%01001100 haben, dann wird ein
NOT.B d0
folgendes Resultat bringen: %10110011.
Die 3 weiteren Instruktionen hingegen arbeiten mit 2 Operanden, einem
Quelloperand und einem Zieloperand. Sie führen die Operation zwischen den
beiden Operanden aus und schreiben das Ergebnis in den Zieloperand. Die
Operationen (die sich von Befehl zu Befehl unterscheiden) sind
Bit-für-Bit, d.h., sie werden zwischen einem Bit im QuellOP und dem
entsprechenden ZielOP durchgeführt. Ein D0 AND D1 entspricht in etwa so
was:
(Bit 0 von D0) AND (Bit 0 von D1)
(Bit 1 von D0) AND (Bit 1 von D1)
(Bit 2 von D0) AND (Bit 2 von D1) und so weiter für alle Bit in D0 und D1
Schauen wir, was das AND auf sich hat. Wir untersuchen seine Arbeitsweise
auf zwei einzelnen Bits. Da ein Bit entweder 0 oder 1 ist, ergeben sich
daraus 4 Fälle:
0 AND 0 = 0
0 AND 1 = 0
1 AND 0 = 0
1 AND 1 = 1
AND gibt als Resultat dann und nur dann 1, wenn das Bit des ersten UND des
zweiten Operanden 1 ist. In allen anderen Fällen werden wir 0 erhalten.
Dreimal dürft ihr raten, was AND auf deutsch heißt: UND. Man könnte die
Funktionsweise auch so übersetzen: "SIND DAS ERSTE UND DAS ZWEITE BIT AUF
1? WENN JA, ANTWORTE MIT 1, ANSONSTEN MIT 0". Ein AND kann nützlich sein,
um gewisse Bits in einer Zahl auf NULL zu setzen:
AND.W #%1111111111111011,LABEL
Es wird das Bit 2 der Zahl in LABEL löschen, da es das einzige auf NULL
gesetzte Bit im Operand ist, und somit das einzige sein wird, das im
Zieloperand geändert wird. Denn die anderen sind auf 1, und werden somit
nichts am Resultat ändern: Wenn das Zielbit 0 war, dann wird ein 0 AND 1
nichts ändern, es ergibt immer 0. Genauso wird eine 1 nichts ändern: 1 AND
1 = 1. Dort, wo aber 0 steht, wird immer auf 0 gesetzt, egal, ob das
Zielbit 1 oder 0 war. Einige Beispiele:
1111001111 AND 0011001100 = 0011001100 - Keine Änderung
1101011011 AND 0001110001 = 0001010001 - 1 Bit gelöscht(auf 0 gesetzt)
1111101101 AND 0011111111 = 0011101101 - 2 Bit gelöscht
Diese Operation des Löschens heißt MASKIERUNG:
AND #%11110000,LABEL (%11110000 ist die MASKE, denn es ist so, als
würde man eine Schablone aus NULLEN über die Zahl
in Label geben. In diesem Fall werden die ersten
vier Bits rechts "verhüllt" wie bei einem Mädchen,
das ein Muttermal mit Schminke "verhüllt". Dieses
Mal ist wie eine 1, die vom Nuller wie von der
Schminke überdeckt wird, also gelöscht wird.
Das OR hingegen verhält sich so:
0 OR 0 = 0
0 OR 1 = 1
1 OR 0 = 1
1 OR 1 = 1
In diesem Fall reicht es, daß eines der zwei Bits auf 1 ist, um als
Resultat eine 1 zu geben. Das Resultat ist also immer 1, außer wenn beide
Bits auf 0 sind. Auch hier kann es helfen zu wissen, daß die Übersetzung
von OR aus dem englischen "ODER" heißt. "ENTWERDER DAS EINE ODER DAS
ANDERE BIT MUß 1 SEIN, UM ALS RESULTAT EINE 1 ZU LIEFERN. ANSONSTEN 0".
Dieses ist nützlich, im Gegensatz zum AND, um einige Bits in einem Byte zu
SETZEN, sie also auf 1 (High) setzen. Einige Beispiele:
0000000001 OR 1101011101 = 1101010001 - Keine Änderung
1000000000 OR 0010011000 = 1010011000 - 1 Bit gesetzt
0001111000 OR 1111100000 = 1111111000 - 2 Bit gesetzt
In diesem Fall ist es so, als würde sich das Mädchen von vorhin statt der
rosa Pinselei (den Nullen) über den schwarzen Malen (den Einsen) jetzt
einen schwarzen Punkt setzen, um ein falsches Mal zu bekommen, wie es z.B.
Marilyn Monroe über der Lippe hatte. Oder als würde sich ein farbiges
Mädchen (vollstandig auf 1) rosa schminken, um weiß auszusehen (wie
Michael Jackson), also vollständig auf NULL zu sein, und nur dort eine
Stelle freizulassen, wo die Zahl im OR auf 1 ist, und die schwarze Stelle
hervortreten lassen.
Das EOR, oder Exklusives OR, setzt nur dann auf 1, wenn entweder das erste
oder das zweite Bit auf 1 sind, niemals aber wenn es beide sind, wie es
beim OR der Fall ist:
0 EOR 0 = 0
0 EOR 1 = 1
1 EOR 0 = 1
1 EOR 1 = 0 ; Hier ist der Unterschied zum OR: denn 1 OR 1 = 1!
Einige Beispiele:
0000000001 EOR 1101011101 = 1101010000 - 1 Bit gelöscht
1000000000 EOR 0010011000 = 1010011000 - 1 Bit gesetzt
Dieser letzte Befefl wird uns guten Dienst beim Lesen des Joysticks
erweisen. Wie ihr wißt besitzt der Amiga 2 Ports, an denen Joystick oder
Mouse angeschlossen werden können. An jeden der zwei können unabhängig
entweder Mouse oder Joystick angeschlossen werden. Für jeden Port
existiert ein Hardwareregister, das gelesen werden kann, um den Zustand
der Mouse/Joystick zu erfähren. Port 0 (an dem normalerweise die Mouse
hängt) wird durch Register JOY0DAT ausgelesen ($dff00a), Port 1 mittels
JOY1DAT ($dff00c). Als erstes sehen wir, wie wir den Joystick abfragen.
Wir beziehen uns dabei auf das Register JOY1DAT, denn daran ist meistens
der Joystick angeschlossen. Es funktioniert auf JOY0DAT auf genau die
gleiche Weise.
Wir können uns einen Joystick wie einen Apparat mit vier Schaltern
vorstellen (einen pro Richtung), von denen jeder zwei Zustände annehmen
kann: geschlossen (1) oder offen (0), jenachdem, ob der Hebel in die
jeweilige Richtung gedrückt wird oder nicht. Um also zu wissen, wohin er
zeigt, müssen wir den Zustand der Schalter abfragen. Für zwei dieser
Schalter ist die Abfrage sehr simpel, es wird einfach ein Bit im Register
JOY1DAT gesetzt:
- Das Bit 1 von JOY1DAT ist der Status des Schalters "Rechts"
- Das Bit 9 von JOY1DAT ist der Status des Schalters "Links"
Wenn das Bit auf 1 ist, dann ist der betroffenen Schalter geschlossen,
ansonsten ist er offen. Was die beiden anderen Richtungen angeht, sie sind
nicht direkt als einzelnes Bit eingetragen. Um sie zu erfahren, müssen wir
eine kleine Rechenoperation mit einem EOR durchführen:
- Der Status des Schalters "Oben" ist das Resultat aus einem EOR zwischen
dem Bit 8 und Bit 9 des Registers
- Der Status des Schalters "Unten" ist das Resultat aus einem EOR zwischen
dem Bit 0 und Bit 1 des Registers.
Auch hier gilt, daß ein Bit 1 bedeutet, daß der Schalter geschlossen ist,
ein Bit auf 0 daß er offen ist. Da wir nun den Status des Joysticks
abfragen können, können wir auch Sprites auf dem Bildschirm bewegen.
Ladet Listing7q.s in einen anderen Textbuffer und führt es aus.
Nun kommen wir zur Maus.
Wenn wir an einen der zwei Ports eine Maus anschließen, dann verhalten
sich die Register anders als bei einem Joystick. Wenn wir nun das Register
JOY1DAT ansehen (genauso auch das Register JOY0DAT...), dann bemerken wir,
daß das hochwertige Byte dazu verwendet wird, um vertikale Bewegungen zu
registrieren, und das niederwertige Byte für horizontale Bewegungen. Jedes
Byte stellt eine Zahl dar (von 0 bis 255), die sich je nach Bewegung der
Mouse verändert.
- Das hochwertige Byte verringert die Zahl, wenn die Maus nach oben bewegt
wird und erhöht den Inhalt, wenn die Maus nach unten bewegt wird.
- Das niederwertige Byte verringert die dargestellte Zahl, wenn die Maus
nach links bewegt wird, und erhöht sie, wenn sie nach rechts bewegt wird.
Nun schauen wir, wie wir diese Informationen so ausnutzen können, um einen
Sprite zu bewegen. Die erste Methode, die einen in den Sinn kommt, ist
jene, einfach die Werte der zwei Bytes JOYxDAT als Koordinaten für den
Sprite zu verwenden. Es ist eigentlich sehr praktisch, denn auch die
Koordinaten der Sprites verringern sich nach Oben und nach Links hin, und
erhöhen sich nach Rechts und nach Unten.
Diese Methode hat aber den Nachteil, daß wir mit einem Byte bekanntlich
maximal bis 255 kommen, der Bildschirm aber 320 Pixel breit ist. Ladet
Listing7r1.s und testet diese Methode. Eine ein bißchen komplexere Methode
wird in Listing7r2.s präsentiert. Sie eliminiert das Limit der 255 Pixel.
Die Beschreibung der Methode befindet sich im Listing selbst. Lest den
angehängten Kommentar.
Da wir nun wissen, wie wir einen Pfeil am Bildschirm bewegen können, ist
es nur mehr einfach, ein Intuition-System zu simulieren, man kann also ein
Kontrollpanel mit Knöpfen und Schaltern machen, die mit einem Pfeil
(Sprite) an-und ausgeschalten werden können, wenn damit über einem solchen
ist. Durch Auslesen der Koordinaten des Sprites und Vergleich mit den
Kooridinaten der Buttons ist das ein Kinderspiel. Versucht es mal selbst.
In fortgeschritteren Lektionen werden wir ein Listing dieser Art sehen.
WIEDERVERWENDUNG DER SPRITES
Die Technik der Wiederverwendung der Sprites erlaubt uns, mehr als die 8
erlaubten Sprites gleichzeitig am Bildschirm anzuzeigen. Praktisch gesehen
wird ein Sprite dazu verwendet, verschiedene Objekte auf verschiedenen
Höhen anzuzeigen. Wenn wir z.B. einen Sprite verwenden, um einen Alien im
oberen Teil des Monitors anzuzeigen, dann können wir den selben
wiederverwenden, um weiter unten das Raumschiff des Spielers zu zeichnen.
Das einzige Limit bei der Wiederwerwertung eines Sprites liegt darin, daß
die beiden Objekte auf verschiedenen Höhen liegen müssen. Es ist nicht
möglich, auf der selben Zeile zwei Objekte anzuzeigen, die mit dem selben
Sprite gezeichnet werden. Des Weiteren muß die erste Zeile des
nachfolgenden Sprites mindestens eine Zeile Abstand von der letzten Zeile
des vorigen Sprites haben. Horizontal gibt keine Einschränkungen. Das Bild
ilustriert die Situation vielleicht etwas besser:
Ausschnitt des Screens
________________________
| | Jedes Bild in diesem Ausschnitt
| _ | des Bildschirmes ist mit dem
| _|_|_ | selben Sprite gezeichnet.
| \___/ _ _|_ _ Jedes kann horizontal frei
| _ _ _ _ _ _ _ _ _ _|_ _ <-- positioniert werden, aber es muß
| _/_\_ | mindestens eine Bildschirmzeile
| |_____| | zwischen der letzten Zeile eines
| \_/_ _ _ _ _ _ _ _ _|_ _ Sprites und der ersten Zeile
| _ _ _ _ _ _ _|_ _ <-- des wiederverwendeten liegen.
| /\ |
| \/ | Die Pfeile zeigen die freie
| | Zeilen zwischen den Objekten an.
| |
|________________________|
Wie schon gesagt, gibt es kein Limit was die horizontale Position angeht,
weger die Anzahl, wie oft man einen Sprite wiederverwendet. Hauptsache,
man hält sich an die oben genannte Regel. Ein Sprite kann so oft
wiederverwendet werden, wie man will. Diese Technik kann bei jedem Sprite
angewandt werden, und unabhängig unter den verschiedenen Sprites: so kann
Sprite 0,3 und 4 nur einmal verwendet werden, Sprite 1 drei mal, Sprite 2
vier mal, und die Sprites 56 und 7 überhaupt nicht. Spielt alles keine
Rolle.
Diese Technik anzuwenden ist recht einfach, es bedarf nur einer Änderung
in der Struktur des Sprites.
Normalerweise kommen am Ende eines Sprites, nach den Daten, aus denen er
besteht, zwei Words mit Inhalt 0. Sie markieren das Ende der Struktur. Um
ihn wiederzuverwenden setzen wir statt dieser zwei Word eine neue Struktur
ein, die ein weiteres Bild beschreibt, nur weiter unten im Screen. Wenn
man dann noch ein drittes Mal das Spiel wiederholen will, dann noch eine
Struktur angehängt, usw., bis dann am Ende die zwei Word mit 0 kommen. Sie
markieren dann das Ende der letzten Wiederverwendung:
SPRITE-STRUKTUR
___________________________ - -
| | VSTART_1, HSTART_1 | |
|___________________________|
| | VSTOP_1 und bits | |
|___________________________|
| |
___________________________
| | plane 1, Zeile 1 | |
|___________________________|
| | plane 2, Zeile 1 | |
|___________________________| Daten der ersten
| |- - - Verwendung des
------ sprite
| ------ |
------
| ___________________________ |
| plane 1, letzte Zeile |
| |___________________________| |
| plane 2, letzte Zeile |
| |___________________________| |
- -
___________________________ - -
| | VSTART_2, HSTART_2 | | Daten der zweiten
|___________________________| Wiederverwendung des Sprite.
| | VSTOP_2 e bit | |- - - Die vertikale Anfangs-
|___________________________| position muß mindestens
| | eine Zeile Abstand von
___________________________ der letzten Zeile der
| | | | vorherigen Verwendung haben.
|___________________________|
| | | |
|___________________________|
| |
------
| ------ |
------
| ___________________________ |
| |
| |___________________________| |
| |
\|/ |___________________________| |
- -
_ _
_____ |
_____ |- - - Weitere Verwendungen
_____ _ _|
___________________________ _ _
| 0 | | Zwei auf 0 gesetzte
|___________________________| |_ _ _ Word, sie markieren
| 0 | | das Ende der letzten
|___________________________|_ _| Wiederverwertung
Zu bemerken, daß die einzelnen Wiederverwertungen des Sprites in
Reihenfolge eingesetzt werden müssen, angefangen vom am höchsten
positionierten Sprite bis hin zum niedersten am Bildschirm. Somit muß
jedes VSTART größer sein als das VSTOP des Vorgängers.
Sehen wir z.B. eine solche Struktur in der Praxis. Hier wird der Sprite 2
mal wiederverwertet.
MEINSPRITE:
VSTART_1:
dc.b $50 ; Position erste Verwendung
HSTART_1:
dc.b $40+12
VSTOP_1:
dc.b $58
dc.b $00
dc.w %0000001111000000,%0111110000111110 ; "Form"-Daten der ersten
dc.w %0000111111110000,%1111001110001111 ; Verwendung
dc.w %0011111111111100,%1100010001000011
dc.w %0111111111111110,%1000010001000001
dc.w %0111111111111110,%1000010001000001
dc.w %0011111111111100,%1100010001000011
dc.w %0000111111110000,%1111001110001111
dc.w %0000001111000000,%0111110000111110
VSTART_2: ; Position zweite Verwendung
dc.b $70 ; BEMERKT, daß VSTART_2 > VSTOP_1
HSTART_2:
dc.b $40+20
VSTOP_2:
dc.b $78
dc.b $00
dc.w %0000001111000000,%0111110000111110 ; "Form"-Daten der zweiten
dc.w %0000111111110000,%1111001110001111 ; Verwendung
dc.w %0011111111111100,%1100010001000011
dc.w %0111111111111110,%1000001110000001
dc.w %0111111111111110,%1000010001000001
dc.w %0011111111111100,%1100010001000011
dc.w %0000111111110000,%1111001110001111
dc.w %0000001111000000,%0111110000111110
dc.w 0,0 ; Ende der letzten Verwendung
Die Technik der Wiederverwendung, wenn richtig angewandt, kann zur
Vervielfachung der bewegten Objekte in einem Shoot´em´up führen. In einem
Spiel, in dem sich die Feinde z.B. horizontal bewegen:
/--___
\--
/--___
\--
/--___
()- \--
/\___o - - - - - -
||||--o - - - - - - /--___
|||| \--
//\\
// \\
------------------------------------------------------------
Da dieses feindliche Geschwader aus Objekten besteht, die eines über dem
anderen stehen, die sich nie überkreuzen, haben wir noch ganze sieben
Sprites für Player 1 und eventuelle Bomben zur Verfügung.
Ein Beispiel davon bekommt ihr in Listing7s.s, wo wir "16" Sprites
gleichzeitig anzeigen. Ladet und studiert es euch.
In unserem Kurs konnte dann auch einer der klassischen Effekte nicht
fehlen, der vor einigen Jahren sehr in Mode war: das "Starfield", die
Sterne, die sich horizontal bewegen.
Diese Sterne werden durch recycelte Sprites dargestellt. Wir stellen euch
mit Listing7t1.s, Listing7t2.s und Listing7t3.s drei Versionen davon vor.
Die Wiederverwendung von Sprites kann auch auf "Attached" angewandt
werden, genauso wie auf "normalen". In Listing7t4.s sehen wir zirca das
gleiche wie in den Starfields, nur mit farbigen Bällen an Stelle der
Sterne.
- - - -
DAS DUAL PLAYFIELD MODE
Bevor wir weitere Charakteristiken der Sprites aufzählen machen wir kurz
einen Abstecher und vertiefen das Kapitel des Dual Playfields. Wir haben
schon in Lektion 4 angedeutet, daß es sich dabei um einen Spezialmodus in
der Grafik handelt, der es uns erlaubt, zwei Screens übereinander
anzuzeigen. Sie heißen jeweils PLAYFIELD1 und PLAYFIELD2. Aber was
bedeutet, daß zwei Screens "übereinander" liegen? Praktisch gesehen hat
jedes Playfield eine "durchsichtige" Farbe, durch die wir durchschauen
können, was drunter vor sich geht. Es ist mit dem COLOR0 der Sprites
durchaus vergleichbar. Eigentlich ist das "durchsichtige" gar keine Farbe,
es ist eher wie ein "Loch" im Playfield zu verstehen. Die anderen Farben
des Playfields hingegen verhalten sich normal. Eines der Playfields
erscheint über dem anderen (ist nur auszuwählen, welches), und dessen
NICHT durchsichtigen Farben überdecken das darunterliegende Playfield. Das
Durchsichtige hingegen verhält sich wie gesagt wie ein Loch und läßt das
drunterliegende durchscheinen.
Die maximale Anzahl von Bitplanes, die ein Playfield haben kann, ist drei
in Lowres und 2 in Highres. Praktisch werden die 6 Bitplanes des Amiga in
zwei Gruppen zu 3 aufgeteilt, jede dieser Gruppen ist ein Playfield. Das
Playfield 1 besteht aus den ungeraden Bitplanes, also aus den Planes 1, 3
und 5, das Playfield 2 aus den geraden, also 2, 4 und 6.
Natürlich ist es nicht immer notwendig, alle Bitplanes zu verwenden. Wir
können unabhängig voneinander den zwei Playfields die Bitplanes zuteilen,
die wir wollen. Die Anzahl der zu verwendenden Bitplanes geben wir auf die
gleiche Art und Weise an, wie bei den "normalen" Grafikmodi. In die Bits
14-12 des BPLCON0 ($dff100), BPU2, BPU1 und BPU0 genannt, kommt die Anzahl
der insgesamt verwendeten Bitplanes für die Playfields. Je nachdem, welche
Zahl wir hier eintragen, teilt die Hardware die Bitplanes folgendermaßen
auf:
Anzahl der verwendeten BPL. | Bitplanes für | Bitplanes für
(bit BPU di BPLCON0) | Playfield 1 | Playfield 2
----------------------------|---------------------|-------------------
| |
0 | keines | keines
| |
1 | Plane 1 | keines
| |
2 | Plane 1 | Plane 2
| |
3 | Plane 1,3 | Plane 2
| |
4 | Plane 1,3 | Plane 2,4
| |
5 | Plane 1,3,5 | Plane 2,4
| |
6 | Plane 1,3,5 | Plane 2,4,6
Wie ihr seht, hat Playfield 1 immer gleichviel oder mehr Planes als das
Field 2, und Playfield 2 maximal eines weniger als Field 1. Es ist nicht
möglich, 3 Planes dem Playfield1 und ein einziges dem Playfield2
zuzuordnen.
Analog wie bei den Standart-Grafikmodi bestimmt die Überlagerung der
Bitplane die Farbe eines jeden Pixel. Die Übereinstimmung zwischen
Bitplane-Kombination und Farbregistern ist aber etwas verschieden, die
Tabelle gibt genaueren Aufschluß darüber:
PLAYFIELD 1
Wert | Wert | Wert | ausgewählte
Plane 5 | Plane 3 | Plane 1 | Farbe
----------------------------------------------------
| | |
0 | 0 | 0 | durchsichtig
| | |
0 | 0 | 1 | COLOR01
| | |
0 | 1 | 0 | COLOR02
| | |
0 | 1 | 1 | COLOR03
| | |
1 | 0 | 0 | COLOR04
| | |
1 | 0 | 1 | COLOR05
| | |
1 | 1 | 0 | COLOR06
| | |
1 | 1 | 1 | COLOR07
PLAYFIELD 2
Wert | Wert | Wert | ausgewählte
Plane 6 | Plane 4 | Plane 2 | Farbe
----------------------------------------------------
| | |
0 | 0 | 0 | durchsichtig
| | |
0 | 0 | 1 | COLOR09
| | |
0 | 1 | 0 | COLOR10
| | |
0 | 1 | 1 | COLOR11
| | |
1 | 0 | 0 | COLOR12
| | |
1 | 0 | 1 | COLOR13
| | |
1 | 1 | 0 | COLOR14
| | |
1 | 1 | 1 | COLOR15
Nun wißt ihr, wie das Dual Playfield Mode funktioniert. Nur etwas wißt ihr
noch nicht... wie man dieses aktiviert!!
Ganz einfach, Bit 10 im Register BPLCON0 auf 1 setzen. Wie wir schon
gesagt haben, ist es möglich, auszuwählen, welches Playfield über dem
anderen erscheinen soll. Man sagt, daß ein Playfield, das über einem
anderen erscheint, höhere Priorität hat. Es gibt ein Bit, das diese
Priorität bestimmt, es ist das Bit 6 des Registers BPLCON2 ($dff104): wenn
es auf 0 ist, dann erscheint Playfield 1 über dem 2, ansonsten Playfield 2
über dem ersten.
Ein Beispiel dafür gibt es in Listing7u.s
PRIOTITÄT ZWISCHEN SPRITES UND PLAYFIELDS
Wir haben schon die Priorität zwischen den einzelnen Sprites
kennengelernt. Wenn sich zwei Sprites also überlappen, dann wird der mit
der kleineren Zahl über dem anderen erscheinen. Des weiteren haben wir
gerade gesehen, wie man die Priorität der Playfields im Dual Playfield
Mode untereinander bestimmt. Nun bleibt uns nur noch die Priorität
zwischen Playfield und Sprites zu besprechen. Als erstes vermerke ich, daß
die Sprites immer über der Farbe 0 erscheinen. Für die anderen Farben wird
die Priorität im Register BPLCON2 kontrolliert. Es ist möglich, diese
unabhängig für die geraden und ungeraden Planes zu setzen. Das ist im
Dual-Playfield-Modus sehr nützlich, da es uns damit ermöglicht, jedem
Playfield eine verschiedene Priorität gegenüber den Sprites zu geben. Im
Standartmodus hingegen ist es vorteilhafter, den geraden und ungeraden
Planes die gleiche Priorität gegenüber den Sprites zu geben. Das Register
BPLCON2 enthält einige Bits, in denen der Prioritätslevel der geraden und
ungeraden Planes gesetzt werden kann. Die Bits 0 bis zwei enthalten den
Prioritätslevel für die ungeraden Bitplanes (Playfield 1 im Dual
Playfieldmode), die Bits 3 bis 5 hingegen den Level für die geraden
Bitplanes (Playfield2). Und so werden die Prioritäten verteilt:
Wir beziehen uns dabei auf ein beliebiges Playfield, da sie untereinander
gleich funktionieren. Was die Priorität Sprites-Playfield angeht, da
werden die Sprites als Paare betrachtet (0-1,2-3,4-5 und 6-7). Wie wir
wissen, ist die Priorität unter den Sprites (und deswegen auch unter den
Paaren) fix:
MAXIMALE PRIORITÄT PAAR 1 (SPRITES 0 UND 1)
PAAR 2 (SPRITES 2 UND 3)
PAAR 3 (SPRITES 4 UND 5)
MINIMALE PRIORITÄT PAAR 4 (SPRITES 6 UND 7)
Wir können nun unseren Prioritätslevel für die Playfields innerhalb diesen
Grenzen einfügen: entweder über der maximalen Priorität der Sprites,
unterhalb der minimalen oder zwischen zwei Paaren. Es ist somit aber nicht
möglich, ein Playfield unter dem Paar 4 und über dem Paar 2 anzuzeigen, da
das zweite Paar über dem vierten ist. Das Gegenteil ist aber möglich. Hier
eine Tabelle mit allen Möglichkeiten, die uns in der Prioritätsvergabe
gebotren sind: (Durch die Einstellungen in BPLCON2 )
CODE | 000 | 001 | 010 | 011 | 100 |
----------------------------------------------------------------------------
PRI. MAX | PLAYFIELD | PAAR 1 | PAAR 1 | PAAR 1 | PAAR 1 |
| PAAR 1 | PLAYFIELD | PAAR 2 | PAAR 2 | PAAR 2 |
| PAAR 2 | PAAR 2 | PLAYFIELD | PAAR 3 | PAAR 3 |
| PAAR 3 | PAAR 3 | PAAR 3 | PLAYFIELD | PAAR 4 |
PRI. MIN | PAAR 4 | PAAR 4 | PAAR 4 | PAAR 4 | PLAYFIELD |
Wie aus der Tabelle ersichtlich, wenn wir z.B. die Sprites 0,1,2 und 3
(also Paar 1 und 2) übder dem Playfield anzeigen wollen, und die
restlichen Paare unterhalb, dann werden wir in BPLCON2 den Code %010
setzen. Dieser Code kommt dann im BPLCON2 entweder in die Bits 0-2, wenn
wir uns auf das Playfield1 im Dual-Playfield-Mode beziehen, oder in die
Bits 3 bis 5 für Playfield2. Wenn wir einen "normalen" Screen verwenden,
ohne Dual Playfield, dann müssen wir diesen Code zweimal schreiben, einmal
in Bit 0-2 und einmal in 3-5.
In Listing7v1.s sehen wir, wie wir die Priorität in einem "normalen"
Screen setzen.
In Listing7v2.s hingegen wird ein Dual Playfield verwendet.
KOLLISIONEN
Die Hardware des Amiga stellt dem Programmierer ein System zur Verfügung,
das es erlaubt, Kollisionen (Zusammenstöße) zwischen Sprite und Sprite,
Sprite und Playfield und zwei Playfields zu registrieren. All diese
Kollisionstypen werden mit nur zwei Registern verwaltet: CLXDAT ($dff00e),
das ein Nur-Lese-Register ist, in dem die Kollisionen signalisiert werden,
und CLXCON ($dff098), das ein Kontrollregister ist, mit dem die Art der
Registrierung der Kollisionen verändert werden kann. Beginnen wir damit,
die Struktur dieser Register zu beschreiben.
Die Bit von CLXDAR verhalten sich wie Kollisionsmelder. Jedes Bit ist
einer ganz bestimmten Kollision zugeteilt. Wenn sich nun eine solche
bestimmte Kollision zuträgt, dann nimmt dieses jeweilige Bit den Wert 1
an. Wenn die Kollision nicht mehr besteht, dann springt es auf 0 zurück.
In der folgenden Tabelle werden die einzelnen Bit von CLXDAT genauer unter
die Lupe genommen:
VERWENDUNG DER BIT VON CLXDAT
Bit 15 nicht verwendet
Bit 14 Kollision zwischen PAAR 3 und PAAR 4
Bit 13 Kollision zwischen PAAR 2 und PAAR 4
Bit 12 Kollision zwischen PAAR 2 und PAAR 3
Bit 11 Kollision zwischen PAAR 1 und PAAR 4
Bit 10 Kollision zwischen PAAR 1 und PAAR 3
Bit 9 Kollision zwischen PAAR 1 und PAAR 2
Bit 8 Kollision zwischen Playfield 2 und PAAR 4
Bit 7 Kollision zwischen Playfield 2 und PAAR 3
Bit 6 Kollision zwischen Playfield 2 und PAAR 2
Bit 5 Kollision zwischen Playfield 2 und PAAR 1
Bit 4 Kollision zwischen Playfield 1 und PAAR 4
Bit 3 Kollision zwischen Playfield 1 und PAAR 3
Bit 2 Kollision zwischen Playfield 1 und PAAR 2
Bit 1 Kollision zwischen Playfield 1 und PAAR 1
Bit 0 Kollision zwischen Playfield 1 und Playfield 2
Das Register CLXCON hat folgende Struktur:
VERWENDUNG DER BIT VON CLXCON
Bit 15 aktiviert Sprite 7
Bit 14 aktiviert Sprite 5
Bit 13 aktiviert Sprite 3
Bit 12 aktiviert Sprite 1
Bit 11 aktiviert Bit-plane 6
Bit 10 aktiviert Bit-plane 5
Bit 9 aktiviert Bit-plane 4
Bit 8 aktiviert Bit-plane 3
Bit 7 aktiviert Bit-plane 2
Bit 6 aktiviert Bit-plane 1
Bit 5 Kollisionswert Bit-plane 6
Bit 4 Kollisionswert Bit-plane 5
Bit 3 Kollisionswert Bit-plane 4
Bit 2 Kollisionswert Bit-plane 3
Bit 1 Kollisionswert Bit-plane 2
Bit 0 Kollisionswert Bit-plane 1
(Bemerkung: wo "aktiviert" steht ist gemeint, daß die REGISTRIERUNG DER
KOLLISIONEN aktiviert wurde: wenn z.B. das Bit 15 von CLXCON den Wert 0
hat, dann heißt es nicht, daß Sprite 7 nicht am Bildschirm erscheinen
kann, sondern nur, daß die Kollisionen, die mit Sprite 7 zu tun haben,
nicht registriert, vermerkt werden)
Wir werden die Bedeutung dieser Bits nach und nach erklären. Beginnen wir
mit den Kollisionen zwischen Sprite und Sprite. Sofort vorweggenommen,
auch bei den Sprite-kollisionen werden die Sprites in Paaren behandelt. Es
ist nur möglich, einen Zusammenstoß zwischen Sprites aus verschiedenen
Paaren zu registrieren, und nicht zwischen zwei aus dem selben Paar. Es
ist z.B. unmöglich, eine Kollision zwischen Sprite 0 und 1 zu erkennen.
Wenn die Sprites aber verschiedenen Paaren angehören, dann ändert sich
alles: bei einem Überlappen der Sprites 0 und 2 wird Bit 9 von CLXDAT auf
High (1) gesetzt (Kollision zwischen Paar 1 und 2). Auch wenn sich eine
Kollision zwischen Sprite 1 und 2 ereignet, wird Bit 9 auf 1 springen, da
Sprite 1 ja auch zum selben Paar wie Sprite 0 gehört, dem Paar 1. Das muß
aber nicht immer so sein.
Denn die Zusammenstöße zwischen Sprites mit geradem Index (Sprite 0,2,4,6)
werden immer registriert, bei ungeraden Sprites hingegen können wir
entscheiden ob ja oder nein. Um auch die ungeraden zu signalisieren müssen
wir das dementsprechende Bit in CLXCON aktivieren. Ihr könnt in der obigen
Tabelle sehen, welche es sind. Die ungeraden Sprites können unabhängig
voneinander aktiviert werden. Einen oder mehrere ungerade Sprites einzeln
aktivieren zu können bringt Vor- und Nachteile mit sich. Betrachten wir
z.B. nur das Paar 1 und 2 und nehmen wir an, wir haben weder Sprite 1 noch
Sprite 3 aktiviert. Wenn sich nun eine Kollision zwischen Sprite 0 und 2
ergibt, wird Bit 9 in CLXDAT auf 1 gehen. Wenn diese Kollision aber
zwischen Sprite 1 und 2, zwischen 0 und 3 oder zwischen 1 und 3 ereignet,
dann passiert nichts, und wir können nicht sagen, ob eine Kollision
stattgefunden hat.
Nehmen wir hingegen an, einen der ungeraden Sprites aktiviert zu haben,
den Sprite 1 zum Beispiel. In diesem Fall ergeben die Kollisionen zwischen
Sprite 0 und 2 und zwischen 1 und 2 einen High-Pegel im Bit 9 von CLXDAT.
Eine Kollision zwischen Sprite 0 und 3 oder 2 und 3 bleibt weiter
ergebnislos. In diesem Fall ergibt sich ein Nachteil gegenüber der vorigen
Situation, in der Sprite 1 nicht aktiviert war. Denn wenn vorhin Bit 9
gesetzt war, waren wir sicher, daß es einen Zusammenstoß zwischen Sprite 0
und 2 gegeben hat. Im derzeitigen Fall hingegen kann es der 0 und der 2
oder der 1 und der 2 sein. Es gibt keine Möglichkeit, das Rätsel durch
lesen des Registers CLXDAT zu lüften. Wenn Sprite 1 deaktiviert ist,
Sprite 3 aber aktiviert, dann haben wir eine analoge Situation zur gerade
besprochenen, nur anstatt auf Sprite 1 bezieht sich das Bit 9 nun
zusätzlich auf das Sprite 3: Kollisionen zwischen Sprite 0 und 2 bzw.
zwischen 0 und 3 werden registriert, aber wir haben wieder keine
Möglichkeit, sie zu unterscheiden.
Zum Schluß gibt es noch die letzte Kombination, in sowohl Sprite 1 wie
auch Sprite 3 aktiviert sind. Nun werden die Kollisionen 0-2, 0-3, 1-2 und
1-3 signalisiert, und wir haben wieder keine Unterscheidungsmöglichkeit.
Ein Beispiel von Spritekollisionen, mit den ungeraden ausgeschaltet,
gindet ihr in Listing7w1.s. Ladet und verifiziert die
Funktionstüchtigkeit.
Ein Beispiel zwischen Sprites mit einem ungeraden, deaktiviertem Sprite
gibt´s in Listing7w2.s. Ihr werdet bemerken, daß das Beispiel, so wie ich
es vorsetze, nicht funktioniert; um es auszuführen müßt ihr die im
Kommentar beschriebenen Modifizierungen anbringen. Um hier zu
unterscheiden, ob es sich bei der Kollision um die mit dem aktivierten,
ungeraden Sprite oder dem geraden handelt, wird eine Technik angewandt,
die die Positionen der jeweiligen Sprites vergleicht.
Nun kommen wir zur Kollision zwischen Sprite und Playfield. Es ist
möglich, einen Zusammenstoß zwischen einem Spritepaar und einer oder
mehreren Farben des Playfields zu registrieren. Auch hier wird wieder in
Paaren gearbeitet, und nicht mit den einzelnen Sprites. Die Aktivierung
der ungeraden Sprites mit den Bits im Register CLXCON hat auch hier seine
Gültigkeit.
Die Registrierung der Kollisionen läuft aber jeweils anders ab, ob wir wir
nun einen normalen Screen oder ein Dual-Playfield verwenden. Bei einem
normalen Screen zeigen die Bits von 1 bis 4 von CLXDAT die Kollision
zwischen einem Spritepaar und der Farbe (oder den Farben) an, die wir dazu
ausgewählt haben. Das Bit 1 zeigt die Kollision zwischen dem Playfield und
dem Paar 1 an, das Bit 2 zwischen Playfield und Paar 2, Bit 3 zwischen
Playfield und Paar 3 und Bit 4 zwischen Playfield und Paar 4. Die Bit von
5 bis 8 werden nicht verwendet.
Im Dual-Playfield-Mode ist es möglich, einen Zusammenstoß zwischen einem
der 2 Playfields und einem Spritepaar zu registrieren, und die Bits von
CLXDAT werden verwendet, wie in der Tabelle des Registers CLXDAT
angeführt: die Bit 1 bis 4 zeigen eine Kollision zwischen Playfield 1 und
einem Spritepaar an, während die Bits von 5 bis 8 für das Playfield 2 und
den Spritepaaren verantwortlich sind. Um die Farben auszuwählen, die ein
"Klingeln" in CLXDAT auslösen sollen, brauchen wir das Register CLXCON.
Beginnen wir mit einer einzigen Farbe.
Die Bits von 6 bis 11 von CLXCON zeigen an, welche Bitplanes für die
Kollisionen aktiviert sind. Im Falle, daß wir Kollisionen eines Sprites
mit einer einzigen Farbe registrieren wollen müssen wir alle Bitplanes
aktivieren, die angezeigt werden. Die Wahl der Farbe, die eine Kollision
auslösen soll, wird durch einsetzen der Zahl des Registers in die Bits von
0 bis 5 in CLXCON erziehlt, das die dementsprechende Farbe beinhaltet.
Nehmen wir z.B. an, wir haben einen normalen Screen mit 16 Farben (4
Bitplanes), und wir dir Kollisionen der ungeraden Sprites nicht
berücksichtigen. Wenn wir nun die Kollision zwischen einen Sprite und der
Farbe 13 registrieren wollen, dann kommt in das Register CLXCON der Wert
111111
5432109876543210
$03cb=%0000001111001101
Schauen wir und die Bedeutung der Bits etwas genauer an. Die Bit von 12
bis 15 deaktivieren die ungeraden Sprites. Von den Bits von 6 bis 11 sind
nur Bit 6,7,8 und 9 auf eins. Das deutet an, daß nur die Bitplanes von 1
bis vier aktiviert sind. Die Bits von 0 bis 5 enthalten die Zahl
%001101=13, also die Farbe 13 ist auserwählt. Im Dual-Playfield-Modus ist
die Situation die selbe, nur werden dort alle verwendeten Bitplanes
aktiviert und es werden Kollisionen mit 2 Farben gleichzeitig aktiviert:
Wenn wir z.B. zwei Playfileds zu jeweils 8 Farben haben, und die
Registrierung der Kollisionen mit Farbe 7 des Playfield 1 und Farbe 2 des
Playfield 2 wollen, dann werden wir in CLXCON folgende Zahl schreiben:
111111
5432109876543210
$0fbb=%0000111111011101
Diese Kombination zeigt an, daß alle Bitplanes zum kollisionsanzeigen
aktiviert sind (alle Bit von 6 bis 11 sind auf 1). Weiters ist die "Zahl"
der Farbe für Playfield 1 aus den Bits 0, 2 und 4 zusammengesetzt, die
zusammengeschoben die Zahl %111=7 ergeben. Die Bits, die die Farbe für
Playfield 2 ergeben, sind 1, 3 und 5, die wiederum zusammengeschoben
%010=2 ergeben.
Zu Bemerken ist, daß die Kollision eines Sprites mit einer Farbe des
Playfield 1 das Setzen eines anderne Bits in CLXDAT zur Folge hat als eine
eines Sprites mit einer Farbe von Playfield 2. Zum Beispiel, wie ihr aus
der Tabelle des Registers CLXDAT lesen könnt, ergibt eine Kollision
Sprite0Playfield1 Bit 1 in CLXDAT einen High-Zustand auf Bit 1 in CLXDAT,
eine Kollision Sprite0-Playfield2 hingegen einen High-Zustand auf Bit5 von
CLXDAT. Es ist auch möglich, die Kollision eines Sprites mit mehr als
einer Farbe zu registrieren, auch wenn nur in einigen Sonderfällen. Um zu
verstehen, wie das geht, muß man sich die Binärdarstellung der Zahlen der
Farbregister vor Augen halten.
Es gibt, wie ihr wißt, 32 Farbregister, die die Numerierung von 0 bis 31
haben. Die Möglichkeit, Kollisionen mit 2 Farben gleichzeitig erkennen zu
können, beruht darauf, daß die Darstellung einiger Binärzahlen ähnlich
ist. Nehmen wir zum Beispiel die Zahlen 2 und 21. Binär gesehen ist
2=%00010, 21 hingegen %10101 (wir betrachten 5 Bit, um Zahlen bis 31
schreiben zu können). Wie ihr seht, sind sie total verschieden. Es besteht
also keine Möglichkeit, diese Farben gleichzeitig zu erkennen.
Betrachten wir nun 22 und 23. Wir erkennen, daß sie in binärer Form so
aussehen: 22=%010110 und 23=%010111. Sie unterscheiden sich nur in einem
Bit, dem niederwertigsten (ganz links). In diesem Fall ist es möglich,
Kollisionen mit beiden Farben zu registrieren. Denn der Wert des
niederwertigsten Bit (das in diesem Fall die Farben unterscheidet) ist vom
Bitplane1 gegeben. Wenn wir Bitplane 1 NICHT zur Kollisionserkennung
aktivieren, dann werden nur die Werte der Bitplanes 2,3,4 und 5 (wir
befinden uns auf einem Screen zu 32 Farben, also 5 Bitplanes) betrachtet,
und der Wert, der durch Bitplane 1 angenommen wird, hat keinen Einfluß.
Wir werden in CLXCON also folgenden Wert schreiben:
111111
5432109876543210
CLXCON= %0000011110010110
Das bedeutet, daß die Kollision basierend auf den aktivierten Bitplanes
erkannt wird (also den Planes 2,3,4 und 5), genauer gesagt wenn unser
Sprite über einem Pixel mit
Bitplane 1=(0 oder 1), weil nicht aktiviert
Bitplane 2=1
Bitplane 3=1
Bitplane 4=0
Bitplane 5=1
steht.
Wie wir gesehen haben, erfüllen die Darstellung von 22=%010110 und
23=%010111 diese bestimmte Forderung. Es werden also beide Farben in der
Kollisionsregistrierung einbezogen. Beachtet, daß das Bitplane, das wir
NICHT aktiviert haben (das erste) genau diesem ersten Bit entspricht, das
die Unterscheidung zwischen 22 und 23 ausmacht.
Diese Technik ist auf bei allen Farbpaaren anwendbar, bei denen sich die
Binärdarstellung nur in einem Bit unterscheiden. So z.B. auch die Zahlen
8=%001000 und 9=%001001, die sich im niederwerigsten Bit unterscheiden.
Also werden wir auch hier das Bitplane 1 deaktivieren, wenn wir diese
beiden Farben zur Erkennung verwenden wollen. Wenn wir hingegen die Farben
10=%001010 und 14=%001110 verwenden wollen, dann bemerken wir, daß sich
die beiden Zahlen im Bit 2 (wir notieren die Zahlen von rechts nach links,
bei 0 beginnend) unterscheiden, das dem Bitplane 3 entspricht. Um nun
Kollisionen zwischen einem Sprite und diesen zwei Farben zu registrieren,
müssen wir das Bitplane 3 deaktivieren, und dem CLXCON also folgenden Wert
zuspielen:
111111
5432109876543210
CLXCON= %0000011011001010 ; Bit 8=0 Bitplane 3 NICHT aktiviert
Wenn wir 2 Bitplanes ausschalten können wir 4 Farben in die Kollisionen
einbeziehen. Im Prinzip ist es immer das Gleiche. Nehmen wir zum Beispiel
die Farben:
1=%00001
3=%00011
5=%00101
7=%00111
Sie haben alle die Bit 0, 3 und 4 gleich, unterscheiden sich aber in der
Kombination der Bits 1 und 2. Wollen wir nun eine Kollision zwischen
Sprites und diesen vier Farben wahrnehmen, dann werden wir Bitplane 2 und
3 deaktivieren.
Wenn wir 3 Bitplanes ausschalten, können wir 8 Farben erkennen, bei 4
abgeschaltenen Bitplanes 16.
Auch im Dual-Playfield-Mode ist es möglich, für jedes Playfield einige
Bitplanes zu deaktivieren, um somit die Kollision zwischen einem Sprite
und mehr als einer Farbe eines Playfields zu signalisieren. Erinnern wir
uns aber, daß wenn wir die Kollision zwischen einem Sprite und zwei Farben
erkennen müssen, bei der jedoch eine dem Playfield 1 gehört und die andere
dem Playfield2, es nicht notwendig ist, dieses Spiel zu machen, da wir in
CLXDAT für jedes Playfield ein Bit haben, das und erlaubt, gleichzeitig
eine Kollision mit beiden Playfields zu erkennen.
In Listing7x1.s sehen wir ein Beispiel von Kollision zwischen Sprite und
Playfield im "Standard-Modus".
In Listing7x2.s hingegen haben wir ein Beispiel im Dual-Playfield-Modus.
In beiden Lisitngs sind im Kommentar Beispiele aufgezeigt, wie man mehr
als eine Farbe für die Kollisionen verwenden kann.
Der letzte Typ von Kollision ist der zwischen Playfield 1 und Playfield2,
klarerweise in Dual-Playfield-Mode. Es ist möglich, Kollisionen zwischen
einer oder mehrerer Farben des Playfield1 und einer oder mehrerer Farben
des Playfield 2 zu erkennen, indem wir nur einige der Bitplanes
aktivieren, genauso wie wir es mit den Sprite-Playfield-Kollisionen getan
haben. Wenn eine Kollision zwischen zwei Playfields stattgefunden hat,
dann wird Bit 0 in CLXDAT auf 1 gesetzt.
Ein Beispiel von dem Typ sehen wir in Listing7x3.s.
DIREKTE VERWENDUNG DER SPRITEREGISTER
Sehen wir nun eine andere Methode, mit der wir Sprites verwenden können.
Bis jetzt haben wir die Sprites generiert, indem wir die Register SPRxPT
verwendet haben, also Pointer auf unsere Datenstrukturen (Spritestruktur),
die alle nötigen Informationen zum Erstellen des gewünschten Sprite
enthalten. Es gibt aber eine alternative Methode, wir werden sie "Direkte
Verwendung der Sprite" nennen. Diese Art ist in den meisten Fällen nicht
von Vorteil, aber manchmal kann sie nützlich sein.
Um zu verstehen, um was es hier geht müssen wir das Thema der
Spritedarstellung etwas vertiefen. Wenn wir in ein SPRxPT-Register die
Adresse einer Spritestruktur schreiben setzen wir eine automatische
Prozedur in Gang, die es uns erlaubt, diese Sprites effektiv zu sehen. Die
Daten, die Position und Form des Sprites angeben, werden automatisch -
mittels eines Hardwaremechanismus´ Namens DMAin eigene Register gegeben,
anderen als dir SPRxPT. Es ist genau dieses Schreiben der Daten in diese
Register, die es ermöglichen, die Sprites WIRKLICH zu sehen. Vom DMA,
einem sehr wichtigen Instrument des Amiga, werden wir in einer nächsten
Lektion sprechen. Im Moment reicht uns zu wissen, was für eine Rolle er
bei den Sprites spielt. Er verhält sich ähnlich wir ein Briefträger.
Stellt euch vor, die Datenstruktur eures Sprites, die ihr im Speicher
erstellt habt, ist wie ein Haufen Briefe, die an verschiedene Empfänger
(Register) kommen. Der DMA übernimmt nun die Aufgabe, diese Briefe
zuzustellen, er sortiert sie auch. Die direkte Verwendung der Sprite
besteht nun genau darin, die Daten direkt in die richtigen Register zu
schreiben, oder anders ausgedrückt, indem wir die Briefe selbst beim
Empfänger abgeben, und so dem Briefträger DMA die Arbeit wegnehmen. Da der
DMA aber gratis arbeitet, werdet ihr euch nun fragen, für was das gut sein
soll. Wie schon gesagt, normalerweise
bringt es keine Vorteile. Aber manchmal schon.
Schauen wir uns an, wie diese Technik funktioniert. Wie schon gesagt, die
Daten werden direkt in einige Register geschrieben. Es gibt vier Register
pro Sprite, SPRxPOS, SPRxCTL, SPRxDATA und SPRxBATB genannt. Statt dem x
kommt wie immer die Nummer des gewünschen Sprite. Die Adressen dieser
Register hängen vom Sprite ab, auf den wir uns beziehen. Wir können sie
mit einer einfachen Formel berechnen. Mit "x" geben wir eine Nummer des
Sprites an, sie liegt zwischen 0 und 7.
Adresse SPRxPOS = $dff140+(x*8)
Adresse SPRxCTL = $dff142+(x*8)
Adresse SPRxDATA = $dff144+(x*8)
Adresse SPRxDATB = $dff146+(x*8)
Ihr könnt sie aber auch mit dem Help des ASMONE ("=C") herausfinden.
Nun beschreiben wir die Verwendung dieser Register. Die "Form" eines
Sprites kommt in die Register SPRxDATA und SPRxDATB, die die zwei kleinen
"Bitplanes" des Sprites darstellen. SPRxDATB ist das Plane 2). Diese
Register haben die gleiche Aufgabe wie die Wordpaare, die eine Zeile eines
Sprites definieren. Bemerkt aber, daß für jeden Sprite nur Register für
EINE Zeile zur Verfügung stehen. Die horizontale Position eines Sprites
ist, wie ihr wißt, aus 9 Bit zusammengesetzt, H0, H1,... , H8 genannt.
Diese 9 Bit sind in zwei Register unterteilt: das Bit H0, also das
niederwertige, befindet sich im Bit 0 des Registers SPRxCTL. Die anderen 8
hingegen im niederwertigen Byte des Registers SPRxPOS. Kurzum, was die
horizontale Position angeht, so verhalten die Register prakitsch gleich
wie die zwei Kontrollword in der Spritestruktur. Die vertikale Position
wird mit dieser Technik aber nicht bestimmt, denn die Sprites verhalten
sich recht eigenartig.
Um einen Sprite anzuzeigen muß er vorher aktiviert werden. Das passiert,
wenn man in das Register SPRxDATA schreibt.
Einmal aktivert wird der Sprite auf jeder Zeile angezeigt, immer in der
von uns zugeteilten horizontalen Position. Die Form ist immer die gleiche,
also jene, die in SPRxDATA und SPRxDATB steht.
Wenn diese zwei Register also nicht ständig verändert werden, dann hat der
Sprite in jeder Zeile immer das gleiche Aussehen. Der Sprite wird
angezeigt bis er nicht deaktivert wird, indem ins Register SPRxCTL
geschrieben wird.
Um einen Sprite anzuzeigen, der in jeder Zeile anders ist, müssen wir also
eine Copperlist schreiben, die ungefähr so aussieht: (Wir nehmen an, den
Sprite 0 bei VSTART=$40, VSTOP=$60 und HSTART=$160 anzeigen zu wollen)
dc.w $4007,$fffe ; WAIT - Warte auf Zeile VSTART
dc.w $140,$0080 ; SPR0POS - horizontale Position
dc.w $142,$0000 ; SPR0CTL
dc.w $146,$0e70 ; SPR0DATB - Spriteform Zeile 1, Plane 2
dc.w $144,$03c0 ; SPR0DATA - Spriteform Zeile 1, Plane 1
; des Weiteren aktiviert es den Sprite,
; deswegen wird es als letztes geschrieben
dc.w $4107,$fffe ; WAIT - Warte auf Zeile VSTART+1
dc.w $146,$0a70 ; SPR0DATB - Spriteform Zeile 2, Plane 2
dc.w $144,$0300 ; SPR0DATA - Spriteform Zeile 2, Plane 1
dc.w $4107,$fffe ; WAIT - Warte auf Zeile VSTART+2
dc.w $146,$0a7f ; SPR0DATB - Spriteform Zeile 3, Plane 2
dc.w $144,$030f ; SPR0DATA - Spriteform Zeile 3, Plane 1
; wiederhole für jede Zeile Y
; dc.w $40+Y07,$fffe ; WAIT - Warte auf Zeile VSTART+Y
; dc.w $146,DATENY2 ; SPR0DATB - Spriteform Zeile Y, Plane 2
; dc.w $144,DATENY1 ; SPR0DATA - Spriteform Zeile Y, Plane 1
; an Stelle Von DATENY1 und DATENY2 kommen die Formdaten der Sprite.
dc.w $6007,$fffe ; WAIT - Warte auf Zeile VSTOP
dc.w $142,$0000 ; SPR0CTL - deaktiviere Sprite
Wie ihr gesehen habt, ist bei längeren Sprite eine sehr lange und
komplizierte Copperlist notwendig. Hier empfielt es sich auf jeden Fall,
den DMA zu verwenden. Nehmen wir aber an, wir möchten einen Sprite
anzeigen, der in jeder Zeile gleich ist. Z.B. eine Säule. In dieser
Situation würde unsere Copperlist kurz und einfach:
(Wir nehmen an, den Sprite 0 bei VSTART=$40, VSTOP=$60 und HSTART=$160
anzeigen zu wollen)
dc.w $4007,$fffe ; WAIT - Warte auf Zeile VSTART
dc.w $140,$0080 ; SPR0POS - horizontale Position
dc.w $142,$0000 ; SPR0CTL
dc.w $146,$0e70 ; SPR0DATB - Spriteform Zeile 1, Plane 2
dc.w $144,$03c0 ; SPR0DATA - Spriteform Zeile 1, Plane 1
; des Weiteren aktiviert es den Sprite,
; deswegen wird es als letztes geschrieben
dc.w $6007,$fffe ; WAIT - Warte auf Zeile VSTOP
dc.w $142,$0000 ; SPR0CTL - deaktiviert Sprite
Ihr bemerkt, daß unsere Copperlist, außer daß sie kurz ist, mit der Höhe
des Sprite nicht verändert wird.
Wenn wir aber den DMA verwendet hätten, dann hätten wir für die ganze
Länge des Sprite die gleichen Zeilen schreiben müssen. Das hätte unter
anderem auch mehr Speucher gekostet. Denkt zum Beispiel an den Fall, eine
100 Zeilen hohe Säule anzeigen zu müssen. Wenn wir den DMA verwenden
würden, dann sähe es so aus:
Spritestruktur:
dc.b VSTART,HSTART,VSTOP,0
dc.w $ffff,$0ff0 ; Zeile 1
dc.w $ffff,$0ff0 ; Zeile 2
dc.w $ffff,$0ff0 ; Zeile 3
dc.w $ffff,$0ff0 ; Zeile 4
dc.w $ffff,$0ff0 ; Zeile 5
dc.w $ffff,$0ff0 ; Zeile 6
dc.w $ffff,$0ff0 ; Zeile 7
dc.w $ffff,$0ff0 ; Zeile 8
.... und so weiter bis:
dc.w $ffff,$0ff0 ; Zeile 99
dc.w $ffff,$0ff0 ; Zeile 100
dc.w 0,0 ; Ende Sprite
Mit der direkten Verwendung der Sprite hingegen reicht eine einfache
Coperlist:
dc.b VSTART,7,$ff,$fe ; WAIT - Warte auf Zeile VSTART
dc.w $140
dc.b $00,HSTART ; SPR0POS - horizontale Position
dc.w $142,$0000 ; SPR0CTL
dc.w $146,$ffff ; SPR0DATB - Spriteform Zeile 1, Plane 2
dc.w $144,$0ff0 ; SPR0DATA - Spriteform Zeile 1, Plane 1
; des Weiteren aktiviert es den Sprite,
; deswegen wird es als letztes geschrieben
dc.b VSTOP,7,$ff,$fe ; Warte auf Zeile VSTOP
dc.w $142,$0000 ; SPR0CTL - deaktivert Sprite
Ein einfaches Beispiel der direkten Verwendung der Sprites findet ihr in
Listing7y1.s.
Im Programm Listing7y2.s hingegen werden durch die direkte Verwendung
Balken erzeugt, die gleich denen sind, die mit dem Copper gemacht wurden.
Mit der Technik der direkten Verwendung der Sprites ist es auch möglich,
den selben Sprite mehrmals auf der gleichen Zeile zu verwenden. Die
Methode wird in Listing7y3.s besser erklärt und angewandt. Die Sprites,
die mehrmals auf der selben Zeile angezeigt werden, heißen auch
MULTIPLEXED, also "gemultiplext". Wie wir also gesehen haben, hat der
Amiga zwar nur 8 Sprites, aber der Assembler erlaubt es und, sie zu
vervielfachen und ihnen auch mehr Farben als standartmäßig vorgesehen zu
geben, indem wir die Palette mehrmals horizontal ändern. Es ergeben sich
damit zwar ziemlich lange Copperlisten, aber es rentiert sich sicherlich.
Eine Entwicklung dieser Idee bringt und dann dazu, einen Screen
vollständig mit Sprites zu gestalten, ein Beispiel in Listing7y4.s.
Um so etwas zu gestalten müssen extrem lange Copperlist geschrieben
werden, und um sie verständlicher zu machen werden SYMBOLE oder EQUATES
verwendet, es ist eine Direktive des Assembler, die es uns gestattet, zu
einer bestimmten Zahl einen x-beliebigen Namen zuzuteilen. Beim
Assemblieren wird der Name dann durch die dementsprechenden Zahl ersetzt.
Machen wir ein Beispiel: wir wollen auf das Register COLOR0 zugreifen,
das, wie wir wissen, die Adresse $dff180 hat. Wir können es entweder so
schreiben:
move.w #$123,$dff180
Aber wenn wir möchten, dann auch auf diese Art:
COLOR0 EQU $dff180 ; Definition eines Symboles
move.w #$123,COLOR0
Wir haben praktisch definiert, daß wenn der ASMONE das Symbol COLOR0
findet, er es mit $dff180 ersetzen soll. Es ist fast wie ein Label, wir
müssen uns einen Namen erfinden, ihn ganz links schreiben, ohne
Leerzeichen, aber es braucht keinen Doppelpunkt. Im Wahrheit kann man sie
auch setzen, genauso wie die Labels sie haben können oder nicht; es hängt
vom Assembler ab. EQU bedeutet ÄQUIVALENT ZU. Die meisten Assembler
akzeptieren auch das = an Stelle der EQU für die Definition. Machen wir
ein weiteres Beispiel:
NUMERLOOP = 10
MOVEQ #NUMERLOOP-1,d0
Loop:
clr.l (a0)+
dbra d0,NUMERLOOP
rts
Mit diesem Mini-Listing haben wir 10 Longwords auf 0 gesetzt. Der Vorteil
der EQUATES besteht darin, daß wir sie alle am Anfang des Programmes
setzen können, und somit einfaches Spiel haben, wenn wir gewisse Werte
verändern wollen. Es ist auch Möglich, mathematische Operationen mit den
Symbolen durchzuführen:
BytesProZeile = 40
AnzahlZeilen = 256
BitplanePlatz = BytesProZeile*AnzahlZeilen
...
section plane,bss_C
Bitplane:
ds.b BitplanePlatz
Im Listing gilt BitplanePlatz 10240, oder 40*256. In Listing7y4.s werden
Symbole für die Copperlist definiert.
Zum Schluß werden wir noch den Screen mit den Sprites scrollen lassen. Wir
nützen das aus, um noch zwei weitere Befehle des 68000ers kennenzulernen:
ROR und ROL. Listing7y5.s
ANIMATION DER SPRITES
Wir beenden diese Lektion mit der Beschreibung über die Animation der
Sprites. Wir betrachten nun wieder "normale" Sprites, also jene, die mit
den SPRxPT und dem DMA erzeugt werden. Um einen Sprite zu animieren müssen
wir ihn jedesmal neuzeichnen, wenn er angezeigt wird. Jede Form, die der
Sprite annimmt, wird auch "Fotogramm der Animation" genannt.
Normalterweise wird eine Animation so gemacht, daß eine Sequenz abläuft,
die immer wiederholt wird. Denkt z.B. an ein Männchen, das am Bildschirm
läuft; ihr werdet bemerken, daß alle Schritte gleich sind.
Um ein Männchen zu animieren müssen wir eine bestimmte Anzahl von
Fotogrammen zeichnen, die in Sequenz (hintereinander) gesehen einen
kompletten Schritt ergeben. Wenn das Männchen einen Schritt gemacht hat,
beginnt es mit einem neuen: jetzt werden wieder die gleichen Fotogramme
angezeigt wie vorhin. Durch wiederholen dieses Schrittes kann das Männchen
laufen, wie lange wir wollen, wir brauchen aber nur eine limitierte Anzahl
von Bildern (Es ist klar, daß Fotogramme ja im Endeffekt Bilder sind, und
somit Speicher brauchen, und wir somit versuchen sollten, so wenig wie
möglich davon zu brauchen). Bis hierher gilt des Gesagte für jedes
animierte Objekt, es ist also gut, wenn ihr es euch merkt, auch für
später, wenn wir Animationen mit dem Blitter erzeugen werden. Im Moment
behandeln wir nur Animationen mittels Sprite. Das bedeutet, wir haben
einen Sprite, der sich am Bildschirm bewegt und jedesmal neugezeichnet
wird, wenn er Form wechselt. Man geht dabei so vor: für jedes Fotogramm
erzeugt man einen Spritestruktur, und jedesmal wenn der Sprite
neugezeichnet wird, pointen wir mit dem Register SPRxPT auf ein anderes
Fotogramm (also auf eine andere Datenstruktur). Die Position des Sprites
wird jedesmal in die Struktur des Fotogrammes geschrieben, auf das SPRxPT
dann zeigt.
Ein Beispiel davon gibt´s in Listing7z.s
Dieses Beispiel ist auch das Ende von Lektion7 und somit von DISK1 des
Kurses.
*****************************************************************************
Die Disketten 2 und 3 (in Italienisch) wurden im Moment der
"Drucklegung" (November 95) fertiggestellt. Sie behandeln die
folgenden Argumente:
- BLITTER (Copymode, Linemode und Fill)
- Interrupt, CIAA/CIAB, laden von Disk, Tastatur
- Audio
- Kompatibilität und Optimierung des 68000
Weiters ist auch Disk 4 fast fertig, sie behandelt das AGA-Chipset
und den 68020.
Es sind auch Lektionen im Bereich 3D und Demo Style in
Vorbereitung, weiters auch die Programmierung von Videospielen.
Aber wenn mir niemand hilft, werden sie nie das Licht der Welt
erblicken. Wenn ihr wollt, daß die Disks 2/3/4 ins Deutsche
mutieren, dann kann ich euch nur sagen, daß desto mehr Mark
anschwirren, desto schneller sie übersetzt werden, hehehe :) Also
schickt mindestens 10 Märkli entweder als Postanweisung oder in
einem verschlossenem Brief an den vertrauten Übersetzer, der
Halbe-Halbe mit dem Autor teilt:
Martin De Tomaso
Nicolodistr. 24/3
39100 BOZEN
ITALY ; internet: mdetomas@inf.unitn.it
Das bedeutet aber nicht, daß die Disks 2/3 automatisch bei euch
zuhause landen wenn sie fertig sind, es würde uns nur ermutigen,
weiterzumachen. Wenn ihr euch registrieren wollt, dann schickt 25
Mark (wie immer mit einer Postanweisung oder flüssig), und fügt
auch noch folgende Infos hinzu:
Name, Nachname, Adresse, Amiga-Modell, das zuhause am Werk ist
Optional: Alter, e-mail, Handle (Wenn ihr aus der Demo-Scene kommt).
Wie schon erwähnt, wird Hilfe für die Kapitel
3D-Effekte/Fraktale/Warp/ Voxel/Zoom/etc, wie auch Games, benötigt.
Es betrifft sowohl Texte wie auch Listings. Es braucht nichts
Wundervolles, es reichen simple Dinge, die einfach zu kapieren sind
und gut kommentiert sein sollten. Egal wenn sie langsam und nix
Besonders sind. Ihr könnt sofort loslegen, Kommentare bitte in
Deutsch oder Englisch!
Ein anderes Motiv, weswegen ihr uns unserer Ruhe berauben dürft
(nicht so ernst nehmen..!), ist die Fähigkeit, GUT ins Englische
oder sonst irgend eine Sprache übersetzen zu können. In diesem Fall
habt ihr Anrecht auf einen guten Prozentsatz des Profits (30%-50%,
das handeln wir uns dann aus) des Ertrages der von euch übersetzten
Version. Unter GUT verstehe ich die Übersetzung des GANZEN Kurses,
auch der LABEL und der dummen Sprüche, und ohne Fehler. Weiter ohne
die TABS in Spaces zu verwandeln und ohne die 79 Kolonnen zu
überschreiten.
Fabio Ciucci (Randy/Ram Jam) & Martin De Tomaso
Hello's to all the German Amiga Scene groups!!! I hope to see much demos
out from EX-SWAPPERs, EX-GFX ARTISTs, EX-MUSICIANs!!!
Just read this tutorial between one swap and another, or between painting &
composing!!!!
A special hello to the R.O.M. editors (Touchstone, please write an article
about this tutorial!!!!)