IRQ-KURS "Die Hardware ausgetrickst..." (Teil 11) ----------------------------------------
Herzlich Willkommen zum elften Teil un- seres IRQ-Kurses. Wie schon in den letz- ten Kursteilen, werden wir uns auch die- sen Monat mit der Spriteprogrammierung der besonderen Art beschäftigen. Es soll um einige Tricks gehen, mit denen man den Bildschirm auch über alle Ränder hinaus mit Gafikdaten füllen kann. Die- sen Effekt nennt man "ESCOS", der Dreh- und Angelpunkt für die Rastertricks in diesem Kursteil sein wird. 1) UNSER ZIEL In früheren Kursteilen hatten wir ja schon einmal besprochen, auf welche Art und Weise oberer und unterer, sowie lin- ker und rechter Bildschirmrand abge- schaltet werden. Wir hatten weiterhin gelernt, daß in diesen Bereichen aus- schließlich Sprites auftauchen können, die durch die abgeschalteten Ränder sichtbar sind, wo sie sonst von letzte- ren überdeckt werden. Wir hatten ebenso eine Möglichkeit kennengelernt, beide Ränder, die horizontalen und vertikalen, gleichzeitig abzuschalten, wobei wir auf sehr exaktes Timing achten mussten, da das Abschalten der linken und rechten Bildschirmbegrenzung eine hohe Genauig- keit erforderte. Desweiteren wird Ihnen aus dem letzten Kursteilen bestimmt noch die Sprite-Multiplexer-Routine in Kombi- nation mit einem Moviescroller im Kopf sein, mit der wir 104 Sprites gleichzei- tig auf den Bildschirm brachten. Wie Sie sich vielleicht erinnern, waren wir da- bei an gewisse Grenzen gebunden. So war z.B. zwischen zwei Spritezeilen immer ein Abstand von ca. 2 Rasterzeilen er- forderlich, die wir benötigten, um die Spritepointer sowie die neuen Y- Positionen der Sprites zu setzen. Des- weiteren mussten wir ein komplizierte Timingroutine benutzen, die zwischen Charakterzeilen (jede achte Rasterzeile, in der der VIC den Prozessor für eine Dauer von 42 Taktzyklen anhält) und nor- malen Rasterzeilen zu unterscheiden hat- te. In dieser Folge unseres Kurses wol- len wir nun all diese Komponenten mitei- nander verbinden und eine Möglichkeit kennenlernen, Timingprobleme durch Cha- rakterzeilenberücksichtigung, zu umge- hen. Das Endergebnis wird ein flächen- deckend (!) mit Sprites belegter Bild- schirm sein, wobei weder Leerräume zwi- schen den Sprites, noch im gesamten Bildschirmrahmen zu sehen sein werden! Wir werden also über eine Grafik verfü- gen, die, ähnlich einem Fernsehbild, die gesamte Bildröhrenfläche ausfüllt! 2) ERSTES PROBLEM: DAS TIMING Kommen wir gleich zum Kern dieses Kur- steils, dem Timing-Problem, das sich uns entgegenstellt. Möchten wir nämlich alle Bildschirmränder abschalten und gleich- zeitig auch noch die Sprites multiple- xen, so können wir programmtechnisch in "Teufels Küche" gelangen. Wir müssten berücksichtigen, daß im sichtbaren Bild- schirmfenster alle acht Rasterzeilen eine Chakaterzeile auftritt, gleichzei- tig müsste in jeder Rasterzeile der lin- ke und rechte Bildschirmrand abgeschal- tet, sowie in jeder 21. Rasterzeile die Sprites neu positioniert werden. Dabei stellen vor allem die Charakterzeilen ein großes Problem dar. Wir müssten un- terscheiden zwischen Rasterstrahlposi- tionen im oberen und unteren Bildschirm- rand und innerhalb des sichtbaren Bild- schirmfensters, und zudem noch für letz- teren Fall berücksichtigen, wann sich der VIC gerade in einer Chakaterzeile befindet und wann in einer normalen Ra- sterzeile. Dieses Problem stellte sich bei unseren Raster-Effekten nun schon häufiger in den Weg, wobei wir es meist durch einen einfachen Trick umgingen: in der Regel benutzten wir in solchen Fäl- len eine FLD-Routine, die die Charakter- zeile im fraglichen Bildschirmbereich einfach nach unten "wegdrückte", so daß wir in jeder Rasterzeile 63 Taktzyklen zur Verfügung hatten und somit ein ein- heitliches Timing programmieren konnten. Wir könnten diesen Effekt hier nun auch anwenden, jedoch gibt es speziell für diese Anwendung einen weiteren, viel einfacherern Trick, die Charakterzeilen zu umgehen: da wir ja den gesamten Bild- schirm mit Sprites füllen möchten, kön- nen wir davon ausgehen, daß wir keiner- lei Hintergrundgrafik, bzw. Textzeichen benötigen. Es gibt nun einen Trick, den wir noch nicht kennengelernt haben, mit dem wir die Charakterzeilen ganz ab- schalten können, so daß nur noch die Sprites dargestellt werden. Wie fast immer ist Register $D011, ein Dreh- und Angelpunkt der VIC-Trickeffekt-Kiste, für diesen Trick verantwortlich. Mit Bit 4 dieses Registers können wir nämlich den gesamten Bildschirm abschalten, was einer Deaktivierung des VICs gleich- kommt. Er wird durch das Löschen dieses Bits veranlasst, auf dem gesamten Bild- schirm nur noch die Rahmenfarbe darzu- stellen, und keine Charakterzeilen mehr zu lesen. Die Sprites bleiben jedoch weiterhin aktiv, obwohl sie jetzt un- sichbar sind, da sie jetzt auf dem ge- samten Bildschirm mit dem Rahmen über- deckt werden. Man könnte das Setzen und Löschen des 4. Bits von Register $D011 quasi mit dem Üffnen und Schließen eines Vorhangs vor einem Fenster vergleichen: Eine Fliege (oder unser Sprite), die sich auf der Fensterscheibe befindet, ist bei geschlossenem Vorhang nicht sichtbar. Ist letzterer jedoch geöffnet, so sieht man die Fliege, solange sie sich im sichtbaren Bereich des Fenster bewegt, und nicht unter den seitlich aufgerafften Vorhängen verschwindet. Nun, selbst wenn die Sprites noch aktiv sind, so nutzen Sie uns herzlich wenig wenn sie unsichtbar sind. Deshalb gilt es mal wieder, den VIC auszutricksen, um das gewünschte Ergebnis zu erhalten. Dies gestaltet sich in diesem Fall recht einfach: Zunächst einmal schalten wir an einer Rasterposition, an der der VIC normalerweise den oberen oder unteren Bildschirmrand zeichnet, den gesamten Bildschirm durch Löschen von Bit 4 aus Register $D011, ab. Erreicht der Raster- strahl nun Rasterzeile $30, an der ei- gentlich das sichtbare Bildschirmfenster beginnt, so prüft der VIC, ob der Bild- schirm nun ein- oder ausgeschaltet ist. In letzterem Fall deaktiviert er seine Zeichenaufbau-Schaltkreise bis zum näch- sten Erreichen dieser Rasterposition, womit er keine einzige Charakterzeile mehr liest. Anstelle dessen zeigt er in den folgenden Rasterzeilen nur noch die Farbe des Bildschirmrahmens an. Wenn wir diesen jedoch mit Hilfe einer Border- Routine bei $FA abschalten, so - oh Wun- der - zeigt uns der VIC den Bildschirm- hintergrund, auf dem sich auch die Spri- tes herumtollen dürfen! Wie bei jedem Effekt, bei dem der VIC etwas tut, was er sonst nicht tun kann, erscheint hier dann wieder der Inhalt der letzten Adresse des VIC-Speichers (normalerweise $3FFF) in Schwarz auf Hintergrundfarbe. Durch Schreiben des Wertes 0 in diese Speicherzelle können wir natürlich die Schwarzen Streifen auch abschalten und damit nur die Hintergrundfarbe anzeigen lassen. Um diese Vorgehensweise nun besser zu erläutern haben wir natürlich wieder ein Programmbeispiel auf Lager, das ich Ih- nen nun auflisten möchte. Sie finden es auf dieser MD auch als fertig ausführba- res File mit dem Namen "SPRITES-ONLY", daß Sie wie immer mit ",8,1" laden und durch ein "SYS4096" starten müssen. Kommen wir also zur Initialiserungsrou- tine des Beispiels, die bei Adresse $1000 beginnt:
INIT: sei ;IRQs sperren lda #$7f ;Alle CIA-IRQs abschalten sta $dc0d ; (CIA1) sta $dd0d ; (CIA2) bit $dc0d ;CIA1-ICR löschen bit $dd0d ;CIA2-ICR löschen
lda #$01 ;VIC-Hintergrundstriche sta $3fff ; auf 1 setzen jsr sprinit;Sprites initialiseren jsr irqinit;IRQ initialiseren
lda #$35 ;ROMs abschalte sta $01 cli ;IRQs erlauben spc:lda #$7f ;Auf SPACE-Taste sta $dc00 ; warten... lda $dc01 cmp #$ef bne spc sei ;IRQs sperren lda #$37 ;ROM wieder einschalten sta $01 jmp $fce2 ;und RESET auslösen
Alles in allem für uns keine besondere Initialisierung. Wichtig sind noch die Routinen "SPRINIT" und "IRQINIT". In ersterer initialisieren wir lediglich die Sprite-Positionen, sowie die Spri- te-Pointer und schalten alle acht Spri- tes ein. Letzere Routine ist für das Korrekte initialisieren unseres IRQs zurständig und sieht folgendermaßen aus: IRQINIT: lda #<bordirq ;IRQ-Vektor bei sta $fffe ; $FFFE/$FFFF lda #>bordirq ;auf "BORDIRQ" verbiegen sta $ffff lda #$1b ;Wert für $D011 mit gel. sta $d011 ; High-Bit f. Rasterpos. lda #$fa ;Ersten Raster-IRQ bei
sta $d012 ; Zeile $FA auslösen lda #$81 ;VIC-Raster-IRQs sta $d01a ; erlauben dec $d019 ;VIC-ICR ggf. löschen rts
Wie Sie sehen, aktivieren wir hier einen Raster-IRQ für Rasterzeile $FA, der bei Auftreten die Routine "BORDIRQ" an- springt, wo sich eine ganz gewöhnliche Routine zum Abschalten des oberen und unteren Bildschirmrandes befindet. Hier das Listing dieser Routine: BORDIRQ:
pha ;Prozessorregs. retten txa pha tya pha lda $d011 ;24-Zeilen-Darstellung and #$77 ; einschalten sta $d011 lda #$28 ;nächster IRQ bei Zeile $28 sta $d012 dec $d019 ;VIC-ICR löschen lda #<soff;Routine für nächsten IRQ sta $fffe ; "SOFF" in IRQ-Vektor lda #>soff; eintragen sta $ffff pla ;Prozessorregs. wieder vom tay ; Stapel holen und IRQ pla ; beenden tax pla rti
Wir schalten hier also an der üblichen Position auf 24-Zeilen-Darstellung he- runter, damit der VIC vergisst, den obe- ren und unteren Bildschirmrand zu zeich- nen. Gleichzeitig wird ein neuer IRQ initialisiert, der bei Erreichen von Rasterzeile $28 (acht Rasterzeilen vor Beginn des sichtbaren Bildschirmfens- ters) die Routine "SOFF" anspringen soll. Diese Routine übernimmt nun die Aufgabe, die Darstellung der Charakter- zeilen zu verhindern:
soff: pha ;Prozessorregs. retten txa pha tya pha
lda $d011 ;Bild ausschalten (durch and #$6F ; ausmaskieren von Bit4) sta $d011
lda #$32 ;nächster IRQ bei Raster- sta $d012 ; zeile $32 dec $d019 ;VIC-ICR löschen lda #<son ;Nächster IRQ soll auf sta $fffe ; Routine "SON" springen lda #>son sta $ffff pla ;Prozessorregs. wieder vom tay ; Stapel holen und IRQ pla ; beenden tax pla rti
Wie Sie sehen eine recht einfache Aufga- be: durch eine AND-Verknüpfung des $D011-Inhalts mit dem Wert $6F wird ein- fach das 4.Bit dieses Registers gelöscht. Gleichzeitig löschen wir dabei Bit 7, das ja das High-Bit der Raster- strahlposition angibt, womit wir also auch dieses Bit für den folgenden Ra- ster-IRQ bei Zeile $32 vorbereitet hät- tem. Die Routine, die hier abgearbeitet werden soll, heisst "SON" und ist für das Wiedereinschalten des Bildschirms verantwortlich. Da sich Rasterzeile $32 im sichtbaren Fenster befindet wäre so- mit der VIC überlistet und soweit ge- bracht, daß er keine Charakterzeilen mehr liest, geschweige denn darstellt. Gleichzeitig schaltet diese Routine wie- der auf die 25-Zeilen-Darstellung zurück (Bit 3 von $D011 muß gesetzt werden), damit das Abschalten des Borders auch im nächsten Rasterstrahldurchlauf funktio- niert:
SON: pha ;Prozessorregs. retten txa pha tya pha
lda $d011 ;Bild und 25-Zeilen- ora #$18 ; Darstellung einschalten sta $d011
lda #$fa ;nächster IRQ wieder bei sta $d012 ; Zeile $FA dec $d019 ;VIC-ICR löschen lda #<bordirq;Wieder "BORDIRQ" in sta $fffe ; IRQ-Vektor eintragen lda #>bordirq sta $ffff pla ;Prozessorregs. wieder vom tay ; Stapel holen und IRQ pla ; beenden tax pla rti
Nachdem Register $D011 auf den gewünsch- ten Wert zurückgesetzt wurde, wird der IRQ wieder für die Routine "BORDIRQ" bei Rasterzeile $FA vorbereitet, womit sich der Kreis schließt und unser Programm- beispiel komplett ist. 3) "ESCOS" - EINEN SCHRITT WEITER Nachdem wir nun unser Timing-Problem gelöst, und die störenden Charakter- Zeilen aus dem Weg geräumt haben, möch- ten wir wieder zu unserer eigentlichen Aufgabenstellung zurückkehren: dem bild- schirmfüllenden Darstellen von Sprites. Hierzu müssen wir, nachdem oberer und unterer Bildschirmrand, sowie die Cha- rakterzeilen abgeschaltet wurden, zusätzlich auch noch die Bildschirmrän- der links und rechts deaktivieren. Das Funktionsprinzip einer solchen Sidebor- derroutine sollte Ihnen noch aus einem der ersten Kursteile im Kopf sein: durch rechtzeitiges Umstellen von 40- auf 38- Spaltendarstellung und zurück tricksen wir den VIC nach dem selben Prinzip aus, wie wir es bei den horizontalen Bild- schirmrändern tun. Dadurch aber, daß sich der Rasterstrahl horizontal recht schnell bewegt, kommt es dabei auf ein höchst exaktes Timing an. Einen Taktzy- klus zu früh oder zu spät funktioniert die Routine schon nicht mehr. Wenn man das Ganze nun zusätzlich noch mit dem Üffnen des oberen und unteren Randes, sowie dem Abschalten der Charakter- Zeilen und einem Sprite-Multiplexer kom- binieren muß, so könnte man meinen, daß dies eine recht programmieraufwendige Sache werden kann. Doch keine Panik, die Umsetzung ist einfacher als Sie glauben. Am Besten sehen Sie sich erst einmal das Demoprogramm "ESCOS1" an. Es wird wie üblich geladen und mit SYS4096 gestar- tet, und zeigt dann einen vollends mit Sprites gefüllten Bildschirm - und zwar über alle Ränder hinaus! Um so etwas nun sebst zu programmieren, werden wir zunächst auf die organisatirischen Pro- bleme und deren Lösung eingehen: Als Erstes müssen Sie davon ausgehen, daß wir keine IRQ-Routine im eigentli- chen Sinne programmieren werden. Auf- grund des exakten Timings, das zum Ab- schalten des linken und rechten Randes notwendig ist, muß der Prozessor nahezu während des gesamten Rasterdurchlaufs damit beschäftigt sein, die richtige Rasterposition abzuwarten, um zwischen der 38- und 40-Zeilen-Darstellung hin- und herzuschalten. Dadurch wird ledi- glich ein einziger IRQ pro Rasterdurch- lauf aufgerufen, nämlich direkt zu An- fang desselben, in Rasterzeile 0. Dieser IRQ muß nun, auf die uns schon bekannte Art und Weise, "geglättet" werden, so daß kein einziger Taktzyklus Unterschied zum vorherigen Rasterdurchlauf besteht. Ab dann (durch das Glätten, das 2 Ra- sterzeilen dauert also ab Zeile 2) be- ginnt der Prozessor damit in jeder ein- zelnen Rasterzeile den Rand abzuschal- ten. Dies geschieht dadurch, daß wir nach jedem Umschalten der Spaltendar- stellung genau am Öbergangspunkt zwi- schen sichtbarem Bildschirmfenster und -rahmen, exakt 63 Taktzyklen verzögern, bevor dieser Vorgang wiederholt wird. So zumindest sähe es aus, wenn wir keine Sprites darzustellen hätten. Da wir dies jedoch tun, müssen wir weiterhin berück- sichtigen, daß der VIC zum Darstellen der Sprites den Prozessor ebenso anhal- ten muß, wie beim Lesen der Charakter- zeilen, um sich die Spritedaten aus dem Speicher zu holen. Hierbei braucht er 3 Taktzyklen für das erste Sprite, und dann jeweils 2 Zyklen für alle weiteren, eingeschalteten Sprites. Da wir alle 8 Sprites benutzen, müssen wir also noch den Betrag 3+7*2=17 von den 63 Zyklen abziehen und erhalten somit exakt 46 Taktzyklen, die der Prozessor pro Ra- sterzeile "verbrauchen" muß. (Anm. d. Red.: Bitte wählen Sie jetzt den zweiten Zeil des IRQ-Kurses aus dem Textmenu.)