home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.uni-stuttgart.de/pub/systems/acorn/
/
Acorn.tar
/
Acorn
/
acornet
/
fun
/
mags
/
hl-02-93.arc
/
!HL-02_93_Text_Text50
< prev
next >
Wrap
Text File
|
1993-05-31
|
12KB
|
213 lines
ÿÿÿÿÿÿÿÿÿÿ Tips'n'Trix ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
Diesmal haben wir in unserer Assemblerecke einen ganz besonderen Leckerbissen
fⁿr Euch vorbereitet. Wie bereits in der letzten Ausgabe angekⁿndigt, geht's
diesmal um Sprites.
Sprites lassen - im Gegensatz zu ihren weniger beliebten Kollegen, den Shapes -
an bestimmten Stellen den Grafikhintergrund durchscheinen. Besonders deutlich
wird das bei Actiongames α la SWIV: Wⁿrde man dort die bewegten Objekte als
Shapes erscheinen lassen, umgΣbe diese ein unsch÷ner, quadratischer und zumeist
pechschwarzer Block. Ja richtig, Shapes entsprechen dem, was man sieht, wenn
man beispielsweise mit !Paint eine kleine Grafik auf den Schirm pixeln wⁿrde.
Nun, Shapes auf den Bildschirm zu plotten, erfordert kein besonders gro▀es
programmiertechnisches Geschick: Man lΣdt einfach ein paar Register mit
Grafikdaten und bringt diese dann ⁿber ein STM in den Bildschirmspeicher.
Nach wenigen Taktzyklen gilt dann: Operation geglⁿckt, Patient tot. Hm, oder
so Σhnlich. Zumindest wird ein eventueller Grafikhintergrund komplett
ⁿberschrieben, das Shape erscheint viereckig.
Bei Sprites sieht die Sache anders aus: Dort mu▀ erst Pixel fⁿr Pixel geprⁿft
werden, ob der entsprechende Punkt transparent (=durchscheinend) oder gesetzt
ist. Im ersten Fall darf der betreffende Pixel nicht geplottet werden,
andernfalls schon. HΣufig wΣhlt man fⁿr diese durchscheinenden Spritepixel die
Farbe schwarz.
Man sieht also, da▀ beim Plotten von Sprites ein ziemlich gro▀er Aufwand
betrieben werden mu▀, um ein ansprechendes Ergebnis zu erzielen. Da die
Jungs in den Entwicklungslaboratorien von Commodore anno dazumal wu▀ten, da▀
ihre Kunden das niemals auf die Reihe bekommen wⁿrden, verpa▀ten sie ihren
Computern (C64 & Amiga) kurzerhand jede Menge Hardwaresprites. Wie der Name es
schon andeutet, ⁿbernimmt hier die Hardware die ⁿble Arbeit beim
Spriteplotten. Der Programmierer spart hier also viel Rechenzeit ein. Leider
hat die Sache auch einen Haken: Die Gr÷▀e der Sprites und die Anzahl der
Farben ist begrenzt. Und weil die findigen Entwickler von Acorn den
Programmierern einen m÷glichst gro▀en Freiraum lassen wollten, spendierten sie
dem Archie auch nur ein einziges Hardwaresprite: den Mauspointer. Dieser ist
maximal 32 Pixel breit, dafⁿr aber beliebig hoch, und enthΣlt zu allem
▄berflu▀ auch noch drei Farben (die vierte Farbe ist transparent) ! Diese
au▀ergew÷hnlich reichliche Ausstattung an grafischen Grundlagen (ê Ironie) hat
es bewirkt, da▀ das Hardwaresprite auf dem Archie noch nie "zweckentfremdet"
wurde; mir ist kein Fall bekannt, in dem durch das Hardwaresprite
beispielsweise ein Gegner in einem Actionspiel dargestellt wurde. Lieb und
brav wie ein unbescholtener Bⁿrger verrichtet unser Hardwaresprite seine
treuen Dienste als solider Mauspointer. Und das soll auch ruhig so bleiben.
Bevor wir nun in die Grundlagen der Spriteprogrammierung einsteigen, noch was
wichtiges: Wenn im folgenden von Sprites die Rede ist, sind damit NICHT die
herk÷mmlichen Sprites im Systemformat gemeint ! Gemeint sind die puren
Spritegrafik-Daten, also ohne Name, Gr÷▀enangabe, usw. Wie man aus normalen
Spritedateien reine Grafikdateien macht, erfahrt Ihr in einem der beigelegten
Listings ("SpriteHack").
Wagen wir also den ersten Schritt in Richtung Spriteprogrammierung. Angenommen,
R0 zeigt auf das Sprite, und R1 enthΣlt die Adresse, an die das Sprite
geplottet werden soll. Nehmen wir weiter an, da▀ wir uns in Mode 13 befinden
und da▀ das Sprite die Gr÷▀e 32x32 Pixel hat. Dann k÷nnte eine Spriteroutine
folgenderma▀en aussehen:
; R0-Sprite, R1-Plotadresse
; R2-SpaltenzΣhler, R3-ZeilenzΣhler
MOV R3,#32 ; 32 Zeilen
.zeile_auslesen MOV R2,#0 ; R2 = 0..31
.spalte_auslesen LDRB R4,[R0],#1 ; lese ein Byte
CMP R4,#0 ; Byte schwarz ?
STRNEB R4,[R1,R2] ; nein -> dann plotte
ADD R2,R2,#1 ; erh÷he ZΣhler
CMP R2,#31 ; Ende der Zeile ?
BMI spalte_auslesen ; nein, nΣchster Pixel
ADD R1,R1,#320 ; nΣchste Bildschirmzeile
SUBS R3,R3,#1 ; fertig ?
BGT zeile_auslesen ; nein, nΣchste Zeile auslesen
Eine wahrhaft sch÷ne Routine. Leider hat sie einen gro▀en Nachteil: Sie
verbraucht pro Sprite durchschnittlich ganze 15000 Taktzyklen ! Das wⁿrde
bedeuten, da▀ wir maximal 8 Sprites gleichzeitig darstellen k÷nnten (bei
50 Hertz)... Aber in Spielen, Demos, usw. schaffen's die Leute doch auch
immer wieder, ein paar Sprites mehr auf dem Monitor herumspringen zu lassen !
Der Flaschenhals bei unserer Routine ist das lΣstige, byteweise Lesen und
Schreiben eines jeden einzelnen Spritepixels. Zumindest das Lesen k÷nnen wir
optimieren, indem wir nun jeweils 32 Pixel (=8 words) gleichzeitig lesen,
die einzelnen Bytes ausmaskieren und dann wie oben verfahren. Zum Ausmaskieren
ben÷tigen wir aber eine Maske (!), und fⁿr einen einzelnen Pixel hat diese
den Wert %11111111 (binΣr), &FF (hexadezimal) oder 255 (dezimal).
Also:
; R0-Sprite, R1-Plotadresse
; R2-ZeilenzΣhler
MOV R3,#&FF ; Bytemaske
MOV R2,#32 ; ZeilenzΣhler
.zeile_plotten LDMIA R0!,{R4-R11} ; hole 32 Spritebytes
ANDS R12,R3,R4 ; maskiere 1. Pixel aus (1. Wort)
STRNEB R12,[R1,#0] ; plotte, wenn Pixel gesetzt
ANDS R12,R3,R4,LSR#8 ; maskiere 2. Pixel aus
STRNEB R12,[R1,#1] ; plotte, wenn Pixel gesetzt
ANDS R12,R3,R4,LSR#16 ; maskiere 3. Pixel aus
STRNEB R12,[R1,#2] ; s.o.
ANDS R12,R3,R4,LSR#24 ; maskiere 4. Pixel aus
STRNEB R12,[R1,#3] ; s.o.
ANDS R12,R3,R5 ; maskiere 5. Pixel aus (2. Wort)
STRNEB R12,[R1,#4] ; plotte, wenn Pixel gesetzt
ANDS R12,R3,R5,LSR#8 ; maskiere 6. Pixel aus (2. Wort)
STRNEB R12,[R1,#5] ; ...
...
usw. bis:
...
ANDS R12,R3,R11,LSR#24 ; maskiere 32. Pixel aus (8. Wort)
STRNEB R12,[R1,#31] ; plotten, wenn Pixel gesetzt
ADD R1,R1,#320 ; eine Bildschirmzeile runter
SUBS R2,R2,#1 ; fertig ?
BNE zeile_plotten ; nein, nΣchste Zeile
Diese Routine hat schon eine recht ansehliche Performance. Sie ist zwar
ziemlich lang (ca. 260 Bytes) , verbraucht aber nur noch 4000-5000 Taktzyklen
pro Sprite. Das entspricht etwa 20-30 Sprites pro VSync, was doch schon ganz
beachtlich ist.
Trotzdem kann man auch hier noch krΣftig optimieren. Erreichen k÷nnen wir dies,
indem wir auch fⁿr das Plotten der Spritedaten nicht das STRB-Kommando
verwenden, sondern das zeitgⁿnstige STM. Wie das ?
Der Trick bei der Sache ist, da▀ wir nun dem Sprite eine vordefinierte Maske
beilegen. Auf ein Spritewort folgt ein entsprechendes Maskenwort, dann wieder
ein Spritewort, ein Maskenwort, usw. Fⁿr jeden Spritepixel existiert in der
Maske ein "Maskenpixel". Wie schon in der obigen Routine, enspricht auch hier
ein Maskenwert von 255 dem Pixelzustand "gesetzt".
Beim Plotten gehen wir nun wie folgt vor: ZunΣchst werden Spritegrafik und der
entsprechende Hintergrund geladen. Nun werden ⁿber logische Operationen mit
der Maske aus dem Hintergrund genau die Pixel gel÷scht, die im Sprite
"gesetzt" sind. Wir erhalten also auf dem Hintergrund ein genaues Schattenbild
des Sprites. Jetzt verknⁿpfen wir ⁿber logische Operationen erneut den
Hintergrund, diesmal aber mit der Spritegrafik: Der Schatten wird mit Farbe
gefⁿllt. Im Schlu▀akt bringen wir das Ergebnis mit einem STM-Kommando zurⁿck
auf den Bildschirm und k÷nnen uns der nΣchsten Spritezeile zuwenden.
Programmtechnisch bedeutet das folgendes:
; R0-Sprite, R1-Plotadresse
; R2-ZeilenzΣhler
MOV R2,#32
.zeile_plotten LDMIA R1,{R3-R6} ; hole Hintergrund (4 Worte)
LDMIA R0!,{R7-R14} ; hole Sprite (7,9,11,13)
& Maske (8,10,12,14)
BIC R3,R3,R8 ; 1. Hintergrundwort masken
BIC R4,R4,R10 ; 2. Hintergrundwort
BIC R5,R5,R12 ; 3. Hintergrundwort
BIC R6,R6,R14 ; 4. Hintergrundwort
ORR R3,R3,R7 ; 1. Hintergrundwort mit Sprite
verknⁿpfen
ORR R4,R4,R9 ; 2. Hintergrundwort
ORR R5,R5,R11 ; 3. Hintergrundwort
ORR R6,R6,R13 ; 4. Hintergrundwort
STMIA R1!,{R3-R6} ; Resultat plotten
LDMIA R1,{R3-R6} ; das Ganze nochmal
LDMIA R0!,{R7-R14}
BIC R3,R3,R8:BIC R4,R4,R10
BIC R5,R5,R12:BIC R6,R6,R14
ORR R3,R3,R7:ORR R4,R4,R9
ORR R5,R5,R11:ORR R6,R6,R13
STMIA R1!,{R3-R6}
ADD R1,R1,#288 ; eine Zeile runter
SUBS R2,R2,#1 ; fertig ?
BNE zeile_plotten ; nein, nΣchste Zeile
Leider ist die Routine so noch nicht ganz vollstΣndig. Wie mancher von Euch
bereits wissen wird, kann man mit den STM/STR/LDM/LDR-Kommandos nur ganze
W÷rter (sogenannte ALIGNte Adressen = durch vier teilbar) sinnvoll ansprechen.
Es kann aber durchaus passieren, da▀ ein Sprite an eine Adresse geplottet
werden mu▀, die NICHT durch vier teilbar ist. Dieses Problem umgeht man
dadurch, da▀ man im Speicher vier Sprites vorliegen hat, die jeweils um
einen Pixel gegeneinander verschoben sind. Bevor man nun das Sprite plottet,
ermittelt man, welche "inner word"-Position das Sprite hat (liegt zwischen
null und drei), und wΣhlt dann dementsprechend das richtige Sprite zum Plotten
aus. NΣheres dazu findet Ihr im beigelegten Demolisting im Directory
"Examples". Ach ja, das Verschieben der vier Sprites geschieht im Demo mit
einem Trick: Zu Beginn wird beim Laden der Spritedaten von Diskette das
Spritefile einfach an verschiedene Adresse geladen ! Aber das Erstellen der
Maske wird dann doch in Assembler vorgenommen. Schaut Euch alles in Ruhe an,
der Durchblick kommt irgendwann von allein.
Aber fⁿr Eure Arbeit werdet Ihr auch belohnt, denn Ihr besitzt dann eine
Spriteroutine, die sich gewaschen hat. Pro Sprite ben÷tigt sie nur noch ca.
2000 Taktzyklen, was fⁿr ungefΣhr 50 Sprites pro VSync locker ausreicht. Die
Nachteile der Mask&Plot-Methode will ich nicht verschweigen: Der Speicher-
platzbedarf ist enorm, denn pro Sprite braucht man immerhin vier
"geshiftete" Sprites und vier "geshiftete" Masken, was den ben÷tigten Speicher
auf das Achtfache des Originals ansteigen lΣ▀t. Au▀erdem mⁿssen die drei
linken Pixelspalten des Ur-Sprites leer (schwarz) sein, damit das Shiften
richtig funktioniert. Aus einem 32x32-Sprite wird also effektiv ein 29x32-
Sprite.
- Tim Juretzky -
PS: Fⁿr Insider sei noch gesagt, da▀ sich sogar die Mask&Plot-Technik noch
verfeinern lΣ▀t. Der Zeitgewinn liegt zwar "nur" bei 15 Prozent, aber bei
kritischen Routinen ist sogar das schon eine Menge. Vielleicht gelingt es
ja einem von Euch, bis zum nΣchsten Mal eine entsprechende Routine zu
prΣsentieren... ?!?