Magic Disk 64

home to index to html: MD9407-KURSE-IRQ-KURS_9.1.html
                IRQ-KURS                
     "Die Hardware ausgetrickst..."     
                (Teil 9)                
----------------------------------------
Herzlich  Willkommen  zum  neunten  Teil
unseres  Raster-IRQ-Kurses.  In   dieser
Ausgabe  soll es um die trickreiche Pro-
grammierung von Sprites gehen,  die  wir
bisher  ja  nur  am  Rande angeschnitten
hatten. Durch Raster-IRQs ist es nämlich
möglich, mehr als acht Sprites  auf  den
Bildschirm zu zaubern! Wir wollen hierzu
eine    sogenannte   Sprite-Multiplexer-
Routine kennenlernen, mit der wir bis zu
16 Sprites (fast) frei  über  den  Bild-
schirm bewegen können!                  
1) DAS FUNKTIONSPRINZIP                 
Zunächst wollen wir klären, wie  man  im
Allgemeinen   die   Anzahl  der  Sprites
erhöht. Das Prinzip ist  höchst  simpel:
dadurch nämlich, daß man einen Rasterin-
terrupt z.B. in der Mitte des sichtbaren
Bildschirms auslöst,  hat  man  die  Mö-
glichkeit  die  Sprite-Register  des VIC
neu zu beschreiben, um z.B. neue Sprite-
positionen  und Spritepointer zu setzen.
Bevor der Rasterstrahl  nun  wieder  die
obere  Bildschirmhälfte erreicht, können
dann wieder die Daten  der  ersten  acht
Sprites  in  den VIC geschrieben werden.
Auf diese Weise kann man in der  oberen,
sowie in der unteren Bildschirmhälfte je
acht  Sprites  darstellen.  Wie  einfach
dieses Prinzip funktioniert soll folgen-
des  Programmbeispiel verdeutlichen, das
Sie auf dieser MD auch unter  dem  Namen
"16SPRITES"  finden,  und  wie immer mit
",8,1" laden  und  durch  ein  "SYS4096"
starten.  Nach  dem  Start werden Sie 16
(!) Sprites auf  dem  Bildschirm  sehen,
acht in der oberen und acht in der unte-
ren Bildschirmhäfte,  jeweils  in  einer
Reihe.  Kommen  wir  zunächst  zur Init-
Routine unsres kleinen Beispiels,  deren
Funktionsprinzip  uns  mittlerweile  be-
kannt sein sollte:                      
Init:sei         ;IRQs sperren          
     lda #6      ;Farbe auf 'blau'      
     sta $d020                          
     sta $d021                          
     lda #$7f    ;Alle Interruptquellen 
     sta $dc0d   ; von IRQ- und NMI-CIA 
     sta $dd0d   ; sperren ggf. aufge-  
     bit $dc0d   ; tretene IRQs frei-   
     bit $dd0d   ; geben                
     lda #$94    ;Rasterzeile $94 als   
     sta $d012   ; IRQ-Auslöser         
     lda $d011   ; festlegen            
     and #$7f                           
     sta $d011                          
     lda #$01    ;Rasterstrahl ist      
     sta $d01a   ; Interruptquelle      
     lda #<irq1  ;Vektor auf erste IRQ- 
     sta $fffe   ; Routine verbiegen    
     lda #>irq1                         
     sta $ffff                          
     ldy #19     ;Pseudo-Sprite-Register
lo1: lda vic1,y  ; in Zeropage von      
     sta $80,y   ; von $80 bis $C0      
     lda vic2,y  ; kopieren             
     sta $a0,y                          
     dey                                
     bpl lo1                            
     ldy #62     ;Spriteblock Nr. 13    
     lda #$ff    ; mit $FF auffüllen    
lo2: sta 832,y                          
     dey                                
     bpl lo2                            
     ldy #7      ;Spritepointer aller   
lo3: lda #13     ; Sprites auf Block 13,
     sta 2040,y  ; sowie Farbe aller    
     lda #1      ; Sprites auf 'weiß'   
     sta $d027,y ; setzen               
     dey                                
     bpl lo3                            
     lda #$35    ;ROM abschalten        
     sta $01                            
     cli         ;IRQs freigeben        
lo4: jsr $1200   ;Bewegunsroutine aufr. 
     lda $dc01   ;SPACE-Taste abfragen  
     cmp #$ef                           
     bne lo4                            
     lda #$37    ;Wenn SPACE, dann ROM  
     sta $01     ; wieder einschalten   
     jmp $fce2   ; und RESET auslösen.  
Hier  schalten  wir nun also wie gewohnt
alle Interruptquellen der  CIA  ab,  und
aktivieren  den Raster-IRQ, wobei dieser
das erste Mal in Rasterzeile $94 auftre-
ten  soll,  was  in  etwa  die Mitte des
sichtbaren Bildbereichs ist.  Dort  soll
dann  die Routine "IRQ1" aufgerufen wer-
den,  deren  Adresse  in  den  Hard-IRQ-
Vektor bei $FFFE/$FFFF geschrieben wird.
Damit  der  Prozessor beim Auftreten des
IRQs  auch  tatsächlich  unsere  Routine
anspringt, wird zuvor noch das ROM abge-
schaltet,  und  anschließend   in   eine
Schleife  gesprungen, die auf die SPACE-
Taste wartet, und in dem Fall einen  Re-
set  auslöst.  Wichtig sind nun noch die
drei Kopierschleifen innerhalb der  Ini-
tialisierung.  Die  erste  davon ("LO1")
kopiert nun zunächst  eine  Tabelle  mit
Sprite-Register-Werten,  die am Ende des
Programms stehen,  in  die  Zeropage  ab
Adresse  $80. Was es damit auf sich hat,
sehen wir später. Die zweite und  dritte
Schleife  füllen  dann  noch den Sprite-
block 13 mit  gesetzten  Pixeln,  setzen
die Spritepointer aller Sprites auf die-
sen Block,  sowie  die  Farbe  Weiß  als
Spritefarbe.                            
Sehen wir nun, was die  Interruptroutine
"IRQ1" macht:                           
IRQ1:pha         ;Prozessorregister     
     txa         ;retten                
     pha                                
     tya                                
     pha                                
     lda #0      ;Farbe auf 'schwarz'   
     sta $d020                          
     sta $d021                          
     lda #$fc    ;Rasterzeile $FC soll  
     sta $d012   ; nächster IRQ-Auslöser
     lda #<irq2  ; sein, wobei Routine  
     sta $fffe   ; "IRQ2" angesprungen  
     lda #>irq2  ; werden soll          
     sta $ffff                          
     dec $d019   ;VIC-ICR freigeben     
     lda $a0     ;X-Pos. Sprite 0       
     sta $d000   ; setzen               
     lda $a1     ;Y-Pos. Sprite 0       
     sta $d001   ; setzen               
     lda $a2     ;Ebenfalls für Sprite 1
     sta $d002                          
     lda $a3                            
     sta $d003                          
     lda $a4      ;Sprite 2             
     sta $d004                          
     lda $a5                            
     sta $d005                          
     lda $a6      ;Sprite 3             
     sta $d006                          
     lda $a7                            
     sta $d007                          
     lda $a8      ;Sprite 4             
     sta $d008                          
     lda $a9                            
     sta $d009                          
     lda $aa      ;Sprite 5             
     sta $d00a                          
     lda $ab                            
     sta $d00b                          
     lda $ac      ;Sprite 6             
     sta $d00c                          
     lda $ad                            
     sta $d00d                          
     lda $ae      ;Sprite 7             
     sta $d00e                          
     lda $af                            
     sta $d00f                          
     lda $b0      ;Hi-Bits der X-Pos.   
     sta $d010    ; aller Sprites setzen
     lda $b1      ;Sprite-Enable setzen 
     sta $d015    ; (welche sind an/aus)
     lda $b2      ;X-Expansion setzen   
     sta $d017                          
     lda $b3      ;Y-Expansion setzen   
     sta $d01d                          
     lda #6       ;Farbe wieder 'blau'  
     sta $d020                          
     sta $d021                          
     pla          ;Prozessor-Regs.      
     tya          ; zurückholen         
     pla                                
     txa                                
     pla                                
     rti          ;Und IRQ beenden...   
Wie Sie sehen, tut die  Routine  eigent-
lich  nichts  anderes, als Werte aus den
Zeropageadressen von $A0 bis $B3 in ein-
zelne  VIC-Register  zu  kopieren. Damit
geben wir dem VIC wir ab der Rasterposi-
tion  $94 also einfach neue Spritewerte.
Gleiches  macht  nun  auch  die  Routine
"IRQ2", die an Rasterzeile $FC ausgelöst
wird,  nur daß sie die Werte aus den Ze-
ropageadressen von $80 bis  $93  in  den
VIC-Kopiert.  In  den  beiden  genannten
Zeropage-Bereichen haben wir also  quasi
eine   Kopie   der  wichtigsten  Sprite-
Register für jeweils zwei  Bildschirmbe-
reiche  untergebracht, deren Inhalte je-
weils an Rasterzeile $94 und $FC in  den
VIC  übertragen  werden.  Verändern  wir
diese Werte nun innerhalb der  Zeropage,
so  können  wir  jedes der 16 sichtbaren
Sprites einzeln bewegen, ein-  oder  au-
schalten,  sowie X-, bzw. Y-Expandieren.
Wir haben also quasi  zwei  "virtuelle",
oder  "softwaremäßige"  Sprite-VICs  er-
schaffen, deren  Register  wie  die  des
normalen VICs beschrieben werden können.
Dies  können Sie übrigens mit einer Rou-
tine ab Adresse $1200 machen.  Sie  wird
im  Hauprogramm (sh. INIT-Listing) stän-
dig aufgerufen, womit ich Ihnen die  Mö-
glichkeit der Spritebewegung offenhalten
wollte.  Im  Beispiel  steht  an  dieser
Adresse nur ein "RTS",  das  Sie  jedoch
mit  einer eigenen Routine ersetzen kön-
nen.                                    
Wir haben nun also 16  Sprites  auf  dem
Bildschirm,  jedoch  mit  der Einschrän-
kung, daß immer nur jeweils acht im obe-
ren  und  unteren  Bildschirmbereich er-
scheinen  dürfen.  Setzen  wir  die   Y-
Position  eines  Sprites aus dem unteren
Bereich auf eine Zahl kleiner  als  $94,
so  wird es nicht mehr sichtbar sein, da
diese Position ja erst dann in  den  VIC
gelangt,  wenn der Rasterstrahl schon an
ihr vorbei ist. Umgekehrt darf ein Spri-
te  im oberen Bildbereich nie eine Posi-
tion größer als $94 haben. Ausserdem ist
noch ein weiterer Nachteil  in  Kauf  zu
nehmen:  das Umkopieren der Register ist
zwar schnell,  da  wir  absichtlich  mit
Zeropageadressen  arbeiten,  auf die der
Zugriff schneller  ist,  als  auf  Low-/
High-Byte-Adressen  (2  Taktzyklen, ans-
telle von 3),  jedoch  dauert  es  immer
noch knappe 4 Rasterzeilen, in denen gar
keine Sprites dargestellt werden können,
da  es  dort  zu  Problemen kommen kann,
wenn der VIC teilweise schon  die  Werte
der   oberen  und  der  unteren  Sprites
enthält.                                
2) DIE OPTIMIEREUNG                     
Wie  Sie  in  obigem Beispiel sahen, ist
die Programmierung von mehr als 8  Spri-
tes  recht problemlos, solange alle wei-
teren Sprites in der  Horizontalen  von-
einander   getrennt  dargestellt  werden
können. Was nun aber, wenn Sie z.B.  ein
Action-Spiel  programmieren  möchten, in
dem mehr als acht Sprites auf dem  Bild-
schirm  darstellbar sein sollen, und zu-
dem auch noch möglichst kreuz  und  quer
beweglich  sein  müssen? Für diesen Fall
brauchen wir eine  etwas  intelligentere
Routine,  die  es uns ermöglicht, flexi-
bler  mit   den   horizontalen   Sprite-
Positionen umzugehen. Solch eine Routine
werden wir nun realisieren. Sie ist all-
gemeinhin  unter  dem   Namen   "Sprite-
Multiplexer"  bekannt.  Wer  sich nichts
darunter  Vorstellen  kann,  der  sollte
sich  auf  dieser  MD einmal das Double-
Density-Demo anschauen, in dem die  Hin-
tergrundsterne,  sowie  die Meteore, die
über den Bildschirm  huschen  auf  diese
Art und Weise dargestellt werden.       
Kommen wir zunächst zum Grundprinzip der
Multiplex-Routine. Mit ihr soll  es  uns
möglich  sein,  16 Sprites auf dem Bild-
schirm darzustellen, wobei wir  uns  mö-
glichst  wenig  Sorgen über die Darstel-
lung machen wollen.  Diese  Arbeit  soll
unsere  Routine  übernehmen, und automa-
tisch die richtige  Darstellung  wählen.
Damit  es  keine Bereiche gibt, in denen
gar  keine  Sprites  dargestellt  werden
können,  weil  gerade irgendwelche Pseu-
do-VIC-Daten kopiert werden, sollte  Sie
zusätzlich  auch  noch möglichst schnell
sein, bzw. den Zeitpunkt der anfallenden
Werteänderungen   im   VIC    sorgfältig
auswählen können.                       
Um nun all diesen Anforderungen zu genü-
gen,  legen  wir  uns wieder einen "vir-
tuellen" VIC an, den wir  so  behandeln,
als könne er tatsächlich 16 Sprites dar-
stellen. Seine Register sollen wieder in
der Zeropage zu finden sein,  damit  die
Zugriffe   darauf  schneller  ausgeführt
werden können. Hierzu  belegen  wir  die
obere  Hälfte der Zeropage mit den benö-
tigten Registerfunktionen. Beachten  Sie
bitte, daß in dem Fall keine Betriebssy-
stemroutinen mehr verwendet werden  kön-
nen,  da diese nämlich ihre Parameter in
der Zeropage zwischenspeichern und somit
unseren VIC verändern würden!  Hier  nun
zunächst  eine  Registerbelegung unseres
Pseudo-VICs:                            
$80-$8F                                 
16  Bytes, die bestimmen, welche Sprites
ein- oder ausgeschaltet sind. Eine 0  in
einem dieser Bytes schaltet das entspre-
chende Sprite aus. Der Wert  1  schaltet
es  ein  ($80  ist  für Sprite0, $8F für
Sprite15 zuständig).                    
$90-$9F                                 
Diese  16  Bytes halten das Low-Byte der
X-Koordinaten der 16 Sprites.           
$A0-$AF                                 
In  diesen  16 Bytes sind die High-Bytes
der X-Koordinaten der 16 Sprites  unter-
gebracht.                               
$B0-$BF                                 
Hier   sind   nacheinander    alle    Y-
Koordinaten der 16 Sprites zu finden.   
$C0-$CF                                 
Diese Register legen die Farben  der  16
Sprites fest.                           
$D0-$DF                                 
Hier werden die  Spritepointer  unterge-
bracht, die angeben, welcher Grafikblock
durch ein Sprite dargestellt wird.      
$E0-$EF                                 
Dies ist ein Puffer für die Y-Positionen
der 16 Sprites. Er wird für die Darstel-
lung später benötigt.                   
$F0-$FF                                 
Innerhalb dieses Bereichs werden  Zeiger
auf  die  Reihenfolge  der Sprites ange-
legt. Mehr dazu später.                 
Die hier angegebenen Register können nun
von uns genauso  verwendet  werden,  als
könne  der  VIC  tatsächlich  16 Sprites
darstellen. Möchten wir also z.B. Sprite
Nr.9 benutzen, so müssen wir zunächst X-
und  Y-Position  durch  Beschreiben  der
Register  $99,  $A9,  sowie  $B9 setzen,
Farbe und Spritepointer in $C9  und  $D9
unterbringen, und anschließend das Spri-
te durch Schreiben des Wertes 1 in Regi-
ster  $89  einschalten.  Analog wird mit
allen anderen Sprites  verfahren,  wobei
die Sprite-Nummer immer der zweiten Zif-
fer des passenden Registers entspricht. 
Wie  muß  unsere  Multiplex-Routine  nun
vorgehen,  um  dem  VIC  tatsächlich  16
unabhängige  Sprites  zu  entlocken? Man
greift hier, wie bei  allen  Raster-IRQ-
Programmen, zu einem Trick: Wie wir bis-
her  gesehen  hatten,  ist  die  einzige
hardwaremäßige   Beschränkung,  die  uns
daran hindert,  mehr  als  acht  Sprites
darzustellen,  die X-Koordinate. Es kön-
nen also immer maximal acht Sprites  mit
derselben   Y-Koordinate   nebeneinander
stehen. Daran können wir  auch  mit  den
besten  Rastertricks  nichts  ändern. In
der Horizontalen, können wir  aber  sehr
wohl  mehr  als acht Sprites darstellen,
und das  eigentlich  beliebig  oft  (das
erste  Beispielprogramm  hätte auch pro-
blemlos 24, oder gar 32 Sprites auf  den
Bildschirm bringen können). Rein theore-
tisch kann ja  ein  Sprite,  das  weiter
oben  am Bildschirm schon benutzt wurde,
weiter unten ein weiteres Mal  verwendet
werden.  Einzige  Bedingung  hierzu ist,
daß das zweite, virtuelle,  Sprite  min-
destens  22 Rasterzeilen (die Höhe eines
ganzen Sprites plus eine Zeile  "Sicher-
heitsabstand") unterhalb des ersten vir-
tuellen Sprites liegt. Damit  diese  Be-
dingung  so  oft wie nur möglich erfüllt
ist, verwendet man ein echtes VIC-Sprite
immer zur Darstellung des  n-ten,  sowie
des  <n+8>-ten  virtuellen  Sprites. Das
echte Sprite0 stellt also das  virtuelle
Sprite0,  sowie  das  virtuelle  Sprite8
dar. Da die Y-Koordinaten beider Sprites
jedoch beliebig sein können, müssen  wir
zuvor  eine  interne  Sortierung  der Y-
Koordinaten  vornehmen,   so   daß   der
größtmögliche  Abstand zwischen den bei-
den Sprites erreicht wird.  Dies  sollte
unsere  Routine  in  einem Bildschirmbe-
reich tun, in dem  keine  Sprites  ange-
zeigt werden können, also dann, wenn der
Rasterstrahl  gerade dabei ist, den obe-
ren und unteren Bildschirmrand zu zeich-
nen  (selbst  wenn  man  diesen auch ab-
schalten kann). Nach der Sortierung kön-
nen  nun  die Werte der ersten acht vir-
tuellen Sprites im VIC eingetragen  wer-
den,  wobei gleichzeitig ermittelt wird,
in welcher Rasterzeile Sprite0  zu  Ende
gezeichnet  ist.  Für  diese Rasterzeile
wird  ein  Raster-Interrupt  festgelegt,
der  die  Werte  für  Sprite8 in den VIC
eintragen soll, und zwar in die Register
des "echten" Sprite0.  Ebenso  wird  mit
den  Sprites  von 9-15 verfahren. Jedes-
mal, wenn das  korrespondierende  Sprite
(n-8)  zu Ende gezeichnet wurde, muß ein
Rasterinterrupt auftreten, der die Werte
des neuen Sprites schreibt.             
Kommen wir nun  jedoch  zu  der  Routine
selbst.  Sie finden Sie übrigens auch in
den  beiden  Programmbeispielen  "MULTI-
PLEX1"  und  "MULTIPLEX2" auf dieser MD.
Beide wierden wie üblich mit ",8,1"  ge-
laden  um  mittels  "SYS4096" gestartet.
Die Initialierung möchte ich Ihnen dies-
mal  ersparen, da Sie fast identisch mit
der obigen ist. Wichtig ist,  daß  durch
sie  zunächst ein Rasterinterrupt an der
Rasterposition $F8  ausgelöst  wird,  an
der  unsere Multiplex-Routine ihre Orga-
nisationsarbeit durchführen soll.  Hier-
bei  wird  in  die  folgende IRQ-Routine
verzweigt, die in den Programmbeispielen
an Adresse $1100 zu finden ist:         
BORD:pha         ;Prozessor-Regs.       
     txa         ; retten               
     pha                                
     tya                                
     pha                                
     lda #$e0    ;Neuen Raster-IRQ      
     sta $d012   ; für Zeile $E0        
     lda #$00    ; und Routine "BORD"   
     sta $fffe   ; festlegen            
     lda #$11                           
     sta $ffff                          
     dec $d019   ;VIC-IRQs freigeben    
     jsr PLEX    ;Multiplexen           
     jsr SETSPR  ;Sprites setzen        
     pla         ;Prozessorregs. wieder 
     tay         ; zurückholen und      
     pla         ; IRQ beenden          
     tax                                
     pla                                
     rti                                
  (Bitte wählen Sie nun den 2. Teil des 
      IRQ-Kurses aus dem Textmenu!)     
Valid HTML 4.0 Transitional Valid CSS!