Magic Disk 64

home to index to text: MD9407-KURSE-IRQ-KURS_9.1.txt
                IRQ-KURS                
     "Die Hardware ausgetrickst..."     
                (Teil 9)                

Herzlich Willkommen zum neunten Teil unseres Raster-IRQ- Kurses. In dieser Ausgabe soll es um die trickreiche Programmierung 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 Bildschirm 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 Rasterinterrupt 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 Spritepositionen 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 folgendes Programmbeispiel verdeutlichen, das Sie auf dieser MD auch unter dem Namen "16 SPRITES" 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 unteren Bildschirmhäfte, jeweils in einer Reihe. Kommen wir zunächst zur Init-Routine unsres kleinen Beispiels, deren Funktionsprinzip uns mittlerweile bekannt 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 auftreten soll, was in etwa die Mitte des sichtbaren Bildbereichs ist. Dort soll dann die Routine " IRQ1" aufgerufen werden, 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 abgeschaltet, und anschließend in eine Schleife gesprungen, die auf die SPACE-Taste wartet, und in dem Fall einen Reset auslöst. Wichtig sind nun noch die drei Kopierschleifen innerhalb der Initialisierung. 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 Spriteblock 13 mit gesetzten Pixeln, setzen die Spritepointer aller Sprites auf diesen 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 eigentlich nichts anderes, als Werte aus den Zeropageadressen von $ A0 bis $ B3 in einzelne VIC-Register zu kopieren. Damit geben wir dem VIC wir ab der Rasterposition $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 Zeropageadressen 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 Bildschirmbereiche 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, einoder auschalten, sowie X-, bzw. Y-Expandieren.
Wir haben also quasi zwei " virtuelle", oder " softwaremäßige" Sprite-VICs erschaffen, deren Register wie die des normalen VICs beschrieben werden können.
Dies können Sie übrigens mit einer Routine ab Adresse $1200 machen. Sie wird im Hauprogramm ( sh. INIT-Listing) ständig 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önnen.
Wir haben nun also 16 Sprites auf dem Bildschirm, jedoch mit der Einschränkung, daß immer nur jeweils acht im oberen 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 Sprite im oberen Bildbereich nie eine Position 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, anstelle 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 Sprites recht problemlos, solange alle weiteren Sprites in der Horizontalen voneinander 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 Bildschirm darstellbar sein sollen, und zudem 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, flexibler mit den horizontalen Sprite-Positionen umzugehen. Solch eine Routine werden wir nun realisieren. Sie ist allgemeinhin 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 Hintergrundsterne, 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 Bildschirm darzustellen, wobei wir uns möglichst wenig Sorgen über die Darstellung machen wollen. Diese Arbeit soll unsere Routine übernehmen, und automatisch die richtige Darstellung wählen.
Damit es keine Bereiche gibt, in denen gar keine Sprites dargestellt werden können, weil gerade irgendwelche Pseudo- 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 " virtuellen" VIC an, den wir so behandeln, als könne er tatsächlich 16 Sprites darstellen. 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 Betriebssystemroutinen mehr verwendet werden können, 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-$8 F 16 Bytes, die bestimmen, welche Sprites einoder ausgeschaltet sind. Eine 0 in einem dieser Bytes schaltet das entsprechende Sprite aus. Der Wert 1 schaltet es ein ($80 ist für Sprite0,$8 F für Sprite15 zuständig) .
$90-$9 F 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 untergebracht.

$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 untergebracht, 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 Darstellung später benötigt.
$ F0-$ FF Innerhalb dieses Bereichs werden Zeiger auf die Reihenfolge der Sprites angelegt. 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 Xund Y-Position durch Beschreiben der Register $99,$ A9, sowie $ B9 setzen, Farbe und Spritepointer in $ C9 und $ D9 unterbringen, und anschließend das Sprite durch Schreiben des Wertes 1 in Register $89 einschalten. Analog wird mit allen anderen Sprites verfahren, wobei die Sprite-Nummer immer der zweiten Ziffer 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önnen 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 problemlos 24, oder gar 32 Sprites auf den Bildschirm bringen können) . Rein theoretisch 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 mindestens 22 Rasterzeilen ( die Höhe eines ganzen Sprites plus eine Zeile " Sicherheitsabstand") unterhalb des ersten virtuellen Sprites liegt. Damit diese Bedingung so oft wie nur möglich erfüllt ist, verwendet man ein echtes VIC-Sprite immer zur Darstellung des nten, 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 beiden Sprites erreicht wird. Dies sollte unsere Routine in einem Bildschirmbereich tun, in dem keine Sprites angezeigt werden können, also dann, wenn der Rasterstrahl gerade dabei ist, den oberen und unteren Bildschirmrand zu zeichnen ( selbst wenn man diesen auch abschalten kann) . Nach der Sortierung können nun die Werte der ersten acht virtuellen Sprites im VIC eingetragen werden, 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. Jedesmal, 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" geladen um mittels " SYS4096" gestartet.
Die Initialierung möchte ich Ihnen diesmal 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 Organisationsarbeit durchführen soll. Hierbei 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!