Magic Disk 64

home to index to text: MD9501-KURSE-IRQ-KURS_15.txt
                IRQ-KURS                
     "Die Hardware ausgetrickst..."     
                (Teil 15)               

Im letzten Kursteil hatten wir uns einen ganz besonderen Rastertrick angeschaut.
Anhand einer VSP-Routine hatten wir gelernt, wie einfach es ist, den Bildschirm des C64 HARDWAREMÄSSIG, also ohne den Prozessor mit großen Bildschirm-Verschiebe- Aktionen zu belasten, nach oben und unten zu scrollen. Dabei unterschied sich die VSP-Routine von einer FLD-Routine in nur einem NOP-Befehl, der das nötige Timing erzeugte, um den gewünschten Effekt auszulösen. Der FLD-Effekt war, wie wir gesehen hatten maßgeblich daran beteiligt, daß der VIC das Lesen von einigen Charakterzeilen vergaß, weswegen wir in der Lage waren, einzelne Bereiche im Video-RAM zu überspringen, und so den Bildschirm beliebig nach oben und unten zu scrollen. In die- sem Kursteil soll es nun um einen nahen Verwandten von VSP gehen. Wir werden den HSP-Effekt besprechen." HSP" steht für " Horizontal Screen Position" und setzt den VSP-Effekt quasi auf die Horizontale um. Mit ihm können wir DEN GESAMTEN BILDSCHIRM problemlos um bis zu 320 Pixel nach rechts verschieben, wobei wir den Prozessor nur läppische 3 Rasterzeilen lang in Anspruch nehmen müssen. Damit werden schnell scrollende Balleroder Jump' n Run Spiele, wie auf dem Amiga oder dem Super-NES von Nintendo auch auf dem C64 möglich!
1) ZUM PRINZIP VON HSP Wir erinnern uns: Um die VSP-Routine funktionsfähig zu machen, mussten wir den FLD-Effekt so anwenden, daß wir dem VIC vorgaukelten, sich auf das Lesen der nächsten Rasterzeile vorzubereiten und seinen internen Lesezähler hochzuzählen, um die richtige Charakterzeilen-Adresse anzusprechen. Als er nun seinen Lesevorgang durchführen wollte machten wir ihn durch FLD-Wegdrücken der Charakterzeilen glauben, daß er doch noch nicht die entsprechende Zeile erreicht hatte. Somit übersprang er mehrere Adressen und somit auch Charakterzeilen. Wir ließen ihn also die Charakterzeilen zu spät lesen, was zu dem gewünschten Effekt führte.
Einen ähnlichen Trick können wir nun auch für die HSP-Routine anwenden. Wie wir ja wissen, so liest der VIC ab der Rasterposition $30 alle 8 Rasterzeilen die 40 Zeichen, die in den nächsten 8 Rasterzeilen zu sehen sein sollen, aus dem Video-RAM, um sie anschließend anzuzeigen. Durch den FLD-Effekt haben wir nun schon oft genung die erste Charakterzeile auf dem Bildschim vor ihm hergeschoben, so daß der VIC diese Zeile verspätet erreichte, und somit der Bildschirm nach unten weggedrückt wurde. Der Witz ist, daß dieser Trick nun auch in her Horizontalen funktioniert! Denn so- bald der VIC merkt, daß er sich in einer Charakterzeile befindet, in der er Zeichendaten zu Lesen und Anzuzeigen hat, beginnt er auch unverzüglich mit dieser Arbeit, und das ohne noch auf die horizontale Rasterposition zu achten, um ggf. festzustellen, daß er sich garnicht am Zeilenanfang befindet! Wenn wir nun also eine Art FLD-Routine einsetzen, die nur für einen Teil der aktuellen Rasterzeile vorschreibt, daß selbige noch keine Charakterzeile ist, und dann mitten innerhalb dieser Zeile von uns wieder auf normale Darstellung zurückgeschaltet wird, so fängt der VIC auch prompt mittendrin damit an die Charakterdaten zu lesen und sofort auf den Bildschirm zu bringen. Verzögern wir also nach einem FLD bis zur Mitte der Rasterzeile, und schalten dann wieder zurück, so wird der Video-RAM Inhalt um exakt 20 Zeichen nach rechts versetzt auf dem Bildschirm dargestellt, wobei die 20 letzten Zeichen, die ja nicht mehr in diese Text- zeile passen, automatisch erst in der nächsten Zeile erscheinen. Es kommt sogar noch besser: aufgrund eines internen Timers des VIC, der das Lesen der Charakterzeilen mitbeeinflusst ( es sei den wir tricksen ihn aus) führt der VIC den Lesefehler in JEDER WEITEREN Charakterzeile ebenso durch, so daß es genügt, lediglich am Bildschirmanfang einmal um einen bestimmten Wert zu verzögern, um DEN GESAMTEN Bildschirm wie gewünscht nach rechts versetzt darzustellen! ! !
Um den Trick nun umzusetzen müssen wir wiefolgt vorgehen: zunächst schalten wir in $ D011 den vertikalen Verschiebe-Offset ( wird in den untersten 3 Bits festgelegt) auf 1, so daß für den VIC die erste Charakterzeile erst eine Rasterzeile nach Beginn des sichtbaren Bildschirmfensters folgt. Dieser Beginn liegt normalerweise in Rasterzeile $30 .
Durch die Verschiebung legen wir die erste vom VIC zu lesende Charakterzeile jedoch in Rasterzeile $31 . Verzögern wir nun jedoch in Rasterzeile $30 um eine bestimmte Anzahl Taktzyklen, und schalten wir dann die horizontale Verschiebung mittendrin wieder auf 0 zurück, so merkt der VIC plötzlich, daß er sich doch schon in einer Charakterzeile befindet und fängt einfrig damit an die Charakterdaten zu lesen und auf dem Bildschirm darzustellen. Wohlgemerkt obwohl er sich schon nicht mehr am Zeilenanfang befindet, sondern mitten innerhalb dieser Rasterzeile! Für jeden Taktzyklus, den wir mehr verzögern, stellt der VIC die Charakterdaten um jeweils ein Zeichen ( also 8 Pixel) weiter rechts dar. Schalten wir also 10 Takte nach Beginn des linken Bildrandes die vertikale Verschiebung ab, so wird die Charakterzeile exakt 10 Zeichen nach rechts versetzt gezeichnet. Dies setzt sich, wie oben schon erwähnt, über den gesamten Bildschirm, also auch für die folgenden 24 weiteren Charakterzeilen, fort, womit auch der gesamte Bildschirm um 10 Zeichen versetzt angezeigt wird!
2) DIE UMSETZUNG Wie das Ganze dann aussieht, können Sie sich in den Programmbeispielen " HSP1" und " HSP2" anschauen. Sie werden beide mit ",8,1" geladen und durch SYS4096 gestartet. HSP2 unterscheidet sich von HSP1 nur darin, daß das Zeichenwirrwarr, das durch den HSP-Effekt in der ersten Rasterzeile zu sehen ist, mit einem schwarzen Rasterbalken unsichtbar gemacht wurde.
Kommen wir nun also zu der Routine, die uns diesen Effekt erzeugt. Sie ist vom Aufbau eigentlich recht einfach.
Zunächst einmal befindet sich eine Initialisierungsroutine ab Adresse $1000, die wie die meisten unserer IRQ-Routinen das ROM abschaltet, um den IRQ-Vektor am Speicherende verwenden zu können, und anschließend einen Border-Raster- IRQ initialisiert, der uns zunächst einmal, wie immer, den unteren und oberen Bildschirmrand abschaltet. Sie steht ab Adresse $1200 im Speicher und ist gefolgt von einer Routine zum Auslesen des Joysticks, sowie der Routine " Control", die die Joystick-Daten auswertet, den Bildschirmverschiebeoffset berechnet und damit die HSP-IRQ- Routine beeinflusst.
Selbige ist ab Adresse $1100 zu finden.
Dir Border-IRQ- Routine legt diese Adresse in den IRQ-Vektoren bei $ FFFE/$ FFFF ab, und gibt dem VIC vor, den nächsten Raster-IRQ in Rasterzeile $2 D auszulösen.
Wird unsere HSP-Routine nun in dieser Rasterzeile aufgerufen, so glätten wir zunächst den IRQ, nach der uns mittlerweile altbekannten Methode. Ab dem Label " Onecycle" steht nun unsere eigentliche Routine, die wiefolgt aussieht:
onecycle:
lda #$19 ; Bildsch.1 Zeile nach sta $ d011 ; unten scrollen

      ldy #$08   ;Verzögerungsschleife  
wy    dey        ; um den richtigen     
      bne wy     ; Moment abzupassen    
      jsr cycles ;Verzögerung 12 Takte  
      lda #$18   ;Wert f. 1 Zeile zurück
redu1 beq redu2  ;2 oder 3 Take verz.   
redu2 bne tt     ;ans Ende verzweigen   
      nop        ;20 NOPs die für das   
      nop        ; Timing später SEHR   
      nop        ; WICHTIG sind, ob-    
      nop        ; wohl sie hier        
      nop        ; übersprungen werden!!
      nop                               
      nop                               
      nop                               
      nop                               
      nop                               
      nop                               
      nop                               
      nop                               
      nop                               
      nop                               
      nop                               
      nop                               
      nop                               
      nop                               
      nop                               
tt    sta $d011  ;Wert schreiben        

lda #$0 e ; Bildschirmfarben setsta $ d020 ; zen ( innerhalb dieser lda #$06 ; Befehle wird die Chasta $ d021 ; rakterz. gelesen!) Zu Beginn unserer IRQ-Routine wird zunächst also eine vertikale Verschiebung um eine Rasterzeile in $ D011 eingetragen ( Bits 0-2 enthalten den Wert %001=$01) . Dadurch, daß der HSP-IRQ in Rasterzeile $2 D aufgerufen wurde, und daß die IRQ-Glättung 2 Rasterzeilen verbraucht, befinden wir uns nun also in Rasterzeile $2 F. Wir verzögern nun noch mit der folgenden Zählschleife und dem JSR-Befehl um eine knappe weitere Zeile, wobei exakt die Position abgepasst wird, an der sich der Rasterstrahl genau am Anfang des linken Randes des sichtbaren Bildschirms befindet ( so wie bei der Routine zum Abschalten der seitlichen Bildschirmränder) . Hierbei gehöhrt der " LDA #$18"- Befehl ebenfalls zur Verzögerung. Er initialisirt den Akku jedoch gleichzeitig schon mit dem Wert vor, den wir in $ D011 schreiben müssen, um die vertikale Verschiebung wieder abzuschalten ( die unstersten drei Bits enthalten den Wert 0) . Nun folgt wieder eine sehr ausgeklügelte Verzögerung, um Taktgenau eine ganz bestimmte Rasterposition abzupassen. Beachten Sie bitte, daß die Routine an dieser Stelle von der oben schon erwähnten " Control"- Routine modifiziert wird, um die jeweils gewünschte Bildschirmverschiebung zu erreichen. Wir haben hier nun zwei Branch-Befehle, die jeweils mit Labels versehen sind, und denen 20 NOP-Befehle folgen. Zum Schluß steht dann der STA-Befehl, mit dem der Wert des Akkus in $ D011 eingetragen wird, um nach der gewünschten Verzögerung den VIC zum Lesen der Charakterzeile zu bewegen. Schauen wir uns nun zunächst die beiden Branches und deren Bedeutung an:
lda #$18 ; Wert f.1 Zeile zurück redu1 beq redu2 ;2 oder 3 Take verz.
redu2 bne tt ; ans Ende verzweigen Durch den vorherigen LDA-Befehl, der einen Wert ungleich Null lädt, wissen wir, daß das Zero-Flag in jedem Fall gelöscht ist. Ausserdem scheint der " BEQ REDU2"- Befehl unsinnig zu sein, zumal er exakt zum nächsten Befehl weiterspringt.
Wie aber auch schon bei unserer IRQ-Glättungsroutine hat dieser Branch-Befehl die Aufgabe, exakt einen Taktzyklus lang zu verzögern. Ein Branch-Befehl benötigt mindestens zwei Takte um ausgeführt zu werden. Trifft die abgefragte Bedingung nicht zu, so wird gleich beim nächsten Befehl fortgefahren( so wie das hier auch der Fall ist) .
Trifft die Bedingung jedoch zu, so dauert der Branch-Befehl einen Taktzyklus länger, in dem der Prozessor den Sprung-Offset auf den Programmzähler aufaddieren muß. Soll nun um eine gerade Anzahl Zyklen verzögert werden, weil der Bildschirm um eine gerade Anzahl Zeichen verschoben werden soll, so steht hier ein BEQ-Befehl, der nur 2 Zyklen verbraucht. Soll eine ungerade Anzahl verzögert werden, so wird von der Routine " Control", im Label " REDU1" der Opcode für einen BNE-Befehl eingetragen, womit die Routine einen Taktzyklus länger dauert, und somit auch ungerade verzögert. Der nun folgende BNE-Befehl ist immer wahr und verzögert somit immer 3 Taktzyklen ( da dies eine ungerade Zahl ist, verhält es sich also eigentlich umgekehrt mit der Änderung des BEQ-Befehls für gerade und ungerade Verzögerung) . Bei diesem Befehl wird von " Control" die Sprungadresse modifiziert. Je nach dem, wieviele weitere Zyklen verzögert werden müssen, trägt die Routine einen Offset auf die folgenden NOP-Befehle ein. Der Befehl verzweigt dann nicht mehr auf das Label " TT", wo der Akkuinhalt nach $ D011 geschrieben wird, sondern auf einen NOP-Befehl davor. Einer dieser Befehle verzögert dann immer um 2 Taktzyklen. Hierzu sollten wir einen Blick auf die Routine " Contol" werfen:
control:

    ldx #$d0      ;Opcode für BNE in    
    stx redu1     ; REDU1 ablegen       
    lda xposhi    ;X-Verschiebungs-     
    sta xposhib   ; zähler kopieren     
    lda xposlo    ; (Low- und High-     
    sta xposlob   ;  byte)              
    and #$08      ;Bit 3 v. XLo ausmask.
    bne co1       ;<>0, dann            
    ldx #$f0      ;Opcode für BEQ in    
    stx redu1     ; REDU1 ablegen       
co1 lsr xposhib   ;X-Verschiebung       
    ror xposlob   ; (16 Bit) durch 4X   
    lsr xposhib   ; Rotieren nach rechts
    ror xposlob   ; mit 16 dividieren   
    lsr xposhib                         
    ror xposlob                         
    lsr xposhib                         
    ror xposlob                         
    sec           ;Subtraktion vorber.  
    lda #$14      ;20 NOPS              
    sbc xposlob   ; minus XPos/16       
    sta redu2+1   ;als Sprungoffset für 
                  ; BNE bei REDU2       

lda xposlo ; Alte X-Versch. laden and #$07 ; unterste 3 Bits isol.
ora #%00011000 ; Standard-Bits setzen sta softmove+1 ; in hor. Scroll eintr.
rts Zu Allererst trägt die Control-Routine, die innerhalb des Border-IRQs aufgerufen wird, in das Label " REDU1" den Wert $ D0 ein, der dem Opcode für den BNE-Befehl entspricht. Hieran anschließend werden die Inhalte der Labels " XPOSLO" und " X-POSHI" in die Labels " XPOSLOB" und " X-POSHIB" kopiert, was für die Offset-Berechnung später notwendig ist. Diese Labels sind im Source-Code des Programms auf die Zeropage-Adressen $02,$03,$04 und $05 vordefiniert." XPOSLO" und " X-POSHI" enthalten einen 16- Bit Zähler für die horziontale Bildschirmverschiebung, die von der Joystickabfrage, die ebenfalls während des Border-IRQs aufgerufen wird, den Joystickbewegungen entsprechend hoch oder runter gezählt wird.
Dieser Zähler kann also einen Wert zwischen 0 und 320 enthalten. Da mit der HSP-Routine der Bildschirm nur in Schritten von einzelnen Zeichen ( also 8 Pixeln) versetzt werden kann, muß unsere Routine zum weichen Scollen des Bildschirms auch noch den horizontalen Verschiebeoffset des Bildschirms verändern.
Diese Verschiebung wird in Register$ D016 des VICs festgelegt, wobei die untersten 3 Bits unseres XPOS-Wertes in die untersten 3 Bits dieses Registers gelangen müssen. Die Bits 3-9 des XPOS-Wertes enthalten nun ( dreimal nach rechts verschoben) die Anzahl Zeichen, und somit auch Taktzyklen, die die HSP-Routine verzögern muß, damit der VIC die Charakterzeile erst an der gewünschten Position liest. Ist also das 3 . Bit ( das das 0 . Bit der Anzahl ist) gesetzt, so muß eine ungerade Anzahl Zeichen verzögert werden. In dem Fall enthält das Label " REDU1" schon den richtigen Wert, nämlich den Opcode für den BNE-Befehl, der zusammen mit dem BNE-Befehl bei " RE-DU2" sechs ( also eine gerade Anzahl) Taktzyklen verbraucht. Ist Bit 3 gelöscht, so enthält der Akku nach dem " AND #$08"- Befehl den Wert 0 und es wird vor dem Weitergehen im Programm der Opcode für den BEQ-Befehl in " REDU1" eingetragen. Damit wird in der HSP-Routine um 2+3=5( also eine ungerade Anzahl) Taktzyklen verzögert. Nun muß noch ermittelt werden, wieviele zusätzliche NOPs zur Verzögerung notwendig sind. Da ein NOP-Befehl immer 2 Taktzyklen braucht, wird nur die halbe Anzahl NOPs benötigt, um enstsprechend viele Zeichen ( Taktzyklen) lang zu verzögern. Da zudem noch die 3 Bit Verschiebeoffset in XPOS stehen, muß dieser Wert durch 16 dividiert werden, um den gewünschten Wert zu erhalten. Diese Berechnung wird an der Kopie von XPOS, also in den Labels " X-POSLOB" und " XPOSHIB" durchgeführt.
Nachdem im Low-Byte dieser Register hiernach die Anzahl der benötigten NOPs stehen, kann nun die Sprungadresse für den BNE-Befehl bei " REDU2" berechnet werden. Da Branch-Befehle immer nur ein Byte mit dem relativen Offset zum aktuellen Programmzähler enthalten, müssen wir also lediglich angeben, wieviele NOPs übersprungen werden müssen. Dies wird durch die Gesamtanzahl NOPs minus der benötigten Anzahl NOPs errechnet, und in Adresse " REDU2"+1 eingetragen.
Die Verzögerung sollte nun also sauber funktionieren.
Zu guter Letzt wird noch der horizontale Verschiebeoffset für das horzontale Softscrolling ermittelt, indem aus " XPOSLO" die untersten drei Bits ausmaskiert werden. Da diese Bits im Register $ D016 landen müssen, das auch für die 38/40- Zeichendarstellung und den Multicolormodus zuständig ist, setzen wir die entsprechenden Bits mit dem folgenden OR-Befehl. Der resultierende Wert wird in das Label " SOFTMOVE"+1 eingetragen, das in der HSP-Routine kurz vor dem oben gezeigten Codestück steht:

softmove lda #$00                       
         sta $d016                      

Hier wird also lediglich der Verschiebeoffset in den Operanden des LDA-Befehls eingetragen, der dann in der HSP-Routine die Bildschirmverschiebung jeweils wie benötigt in $ D016 einträgt.
Damit hätten wir alle Einzelheiten der HSP-Routine besprochen. Wie Sie sehen funktioniert sie sogar noch einfacher als die VSP-Routine. Vielleicht experimentieren Sie einmal ein wenig mit den Programmbeispielen und versuchen einen horizontalen Endlosscroller daraus zu machen. Der HSP-Effekt funktioniert übrigens genauso wie VSP auch mit HI-RES- Grafik. Im nächsten Kursteil werden wir auch das noch sehen, und die erstaunlichste IRQ-Raster- Routine kennenlernen die je entdeckt wurde: die AGSP-Routine nämlich, die eine Kombination aus HSP und VSP darstellt, und mit der es problemlos möglich ist den kompletten Bildschirm in ALLE Richtungen zu scrollen, ohne große Kopieraktionen mit dem Prozessor durchführen zu müssen! ! !

                                 (ih/ub)

Valid HTML 4.0 Transitional Valid CSS!