Magic Disk 64

home to index to text: MD9403-KURSE-IRQ-KURS_5.1.txt
             Interrupt-Kurs             
     "Die Hardware ausgetrickst..."     
                (Teil 5)                

Nachdem wir im letzten Monat ja schon kräftig mit schillernden Farbund Sinuswellenrasterroutinen um uns geworfen haben, möchten wir uns auch in dieser Ausgabe der MD einem sehr trickreichen Beispiel eines Raster-IRQs zuwenden: der FLD-Routine.
1) FLD - EIN ZAUBERWORT FÖR RASTERFREAKS Die Abkürzung " FLD" steht für " Flexible Line Distance", was übersetzt soviel bedeutet wie " beliebig verschiebbarer Zeilenunterschied" . Diese, zunächst vielleicht etwas verwirrende, Bezeichnung steht für einen Rastereffekt, der vom Prinzip und der Programmierung her extrem simpel ist, jedoch ungeahnte Möglichkeiten in sich birgt. Um zu wissen, welcher Effekt damit gemeint ist, brauchen Sie sich lediglich einmal anzuschauen, was passiert, wenn Sie im MD-Hauptmenu einen neuen Text laden, oder einen gelesenen Text wieder verlassen:
der Textbildschirm scheint hier von unten her hochgezogen, bzw. nach unten hin weggedrückt zu werden. Und genau das tut nun eine FLD-Routine. Hierbei sei darauf hingewiesen, daß es sich dabei nicht um irgendeine Programmierakrobatik handelt, bei der aufwendig hinund herkopiert und rumgescrollt werden muß, sondern um eine einfache, ca.150 Byte große, Rasterroutine! Der Trick des Ganzen liegt wie so oft bei der Hardware des 64 ers, die wieder einmal beispielhaft von uns " veräppelt" wird. Denn eigentlich sollte sie nicht dazu in der Lage sein, einen solchen Effekt zu erzeugen!
Wie funktioniert nun diese Routine? Wie Sie vielleicht wissen, kann in den unteren drei Bits von Register 17 des VICs($ D011), ein vertikaler Verschiebeoffset für die Bildschirmdarstellung eingetragen werden. In der Regel benutzt man diese Bits um ein vertikales Softscrolling zu realisieren. Je nach dem welcher Wert dort eingetragen wird ( von 0 bis 7), kann die Darstellung des sichtbaren Bildschirms um 0 bis 7 Rasterzeilen nach unten verschoben werden. Lässt man diese Werte nacheinander durch das Register laufen, und kopiert man daraufhin den Inhalt des Bildschirms von der 2 . Textzeile in die 1 . Textzeile, so entsteht ein Softscrolling nach unten. Der Wert, der dabei in den unteren drei Bits von $ D011 steht gibt dem VIC an, ab welchem vertikalen Bildschirmoffset er damit anfangen soll, die nächste Textzeile aufzubauen. Wie wir aus dem letzten Kursteil noch wissen, geschieht dies ab Rasterzeile 41 und jeweils in jeder achten, folgenden Zeile. Wird nun ein vertkaler Verschiebeoffset angegeben, so verzögert der VIC diesen Zeitpunkt um die angegebene Anzahl Rasterzeilen ( maximal 7) . Steht in der Vertikalverschiebung z. B. der Wert 1, so muß der VIC also noch eine Rasterzeile warten, bis er die nächste Charakterzeile aufzubauen hat.
Der Trick der FLD-Routine liegt nun darin, daß Sie in jeder Rasterzeile, diesen Charakterzeilenanfang vor dem Rasterstrahl " herschiebt", so daß dieser eigentlich nie die gesuchte Anfangszeile erreichen kann - zumindest nicht solange, wie unsere FLD-Routine ihm vortäuscht, noch nicht den Anfang dieser Zeile erreicht zu haben! Wie einfach das alles geht, soll Ihnen folgendes Programmbeispiel verdeutlichen. Sie finden es auf dieser MD unter dem Namen " FLD-DEMO1" und müssen es absolut ( mit ",8,1") laden und wie alle unsere Programmbeispiele mit " SYS4096" starten:

init:  sei         ;IRQs sperren        
       lda #$7f    ;CIA-Timer abschalten
       sta $dc0d   ; (SYS-IRQ)          
       lda $dc0d   ;Und CIA-ICR löschen 
       lda #$f8    ;Zeile $f8 ist IRQ-  
       sta $d012   ; Auslöser           
       lda $d011   ;7.Bit Rasterzeile   
       and #$7f    ; löschen            
       sta $d011   ; u. zurückschr.     
       lda #$01    ;VIC löst Raster-IRQs
       sta $d01a   ; aus                
       ldx #<(IRQ2);IRQ-Vektor auf      
       ldy #>(IRQ2); eigene Routine     
       stx $0314   ; verbiegen          
       sty $0315                        
       lda #$00    ;Zählregister        
       sta $02     ; löschen            
       lda #$ff    ;Leerbereich auf     
       sta $3fff   ; schwarz setzen     
       cli         ;IRQs freigeben      
verz:  rts         ;Und Ende            
---                                     
irq1:  lda #$10    ;Vert. Verschiebung  
       sta $d011   ;gleich 0            
       lda $02     ;Zähler laden        
       beq lab2    ;Wenn 0, überspringen
       ldx #$00    ;Zählreg. löschen    
lab1:  clc         ;Carry f.Add.löschen 
(!)    lda $d011   ;Verschiebung holen  
(!)    adc #$01    ; +1                 
(!)    and #$07    ; unt. Bits ausmask. 
(!)    ora #$10    ; Bit 4 setzen       
(!)    sta $d011   ; u. zurückschr.     
       dec $d019   ;VIC-IRQ freigeben   
       jsr verz    ;Verzögern...        
       jsr verz                         
       lda $d012   ;Strahlpos < Bild-   
       cmp #$f6    ; schrimende?        
       beq lab2    ;Ja, also überspr.   
       inx         ;Zähler+1            
       cpx $0002   ;Zähler=Endwert?     
       bne lab1    ;Nein, also weiter   
lab2:  lda #$f8    ;Rasterz. $f8 ist    
       sta $d012   ; nächster IRQ-Ausl. 
       dec $d019   ;VIC-IRQs freigeb.   
       lda #$78    ;IRQ-Vektor auf IRQ2-
       sta $0314   ; Routine verbiegen  
       ldx #$0e    ;Bildschirmfarben    
       ldy #$06    ; zurücksetzen       
       stx $d020                        
       sty $d021                        
       jmp $febc   ;IRQ beenden         
---                                     
irq2:  lda #$10    ;Vertikal-Versch.    
       sta $d011   ; init.              
       lda #$71    ;Rasterz. $71 ist    
       sta $d012   ; IRQ-Auslöser       
       dec $d019   ;VIC-IRQs freigeben  
       lda #$30    ;IRQ-Vektor auf IRQ1-
       sta $0314   ; routine verbiegen  
       lda $dc00   ;Portreg lesen       
       lsr         ;Akku in Carry rot.  
       bcs lab3    ;C=1? Wenn ja, weiter
       dec $02     ;Sonst Joystick hoch 
lab3:  lsr         ;Akku in Carry rot.  
       bcs lab4    ;C=1? Ja, also weiter
       inc $02     ;Sonst Joyst. runter 
lab4:  jmp $ea31   ;SYS-IRQ und Ende    

Die Beschreibung der Initialisierungsroutine können wir uns sparen, da wir ihren Aufbau ja schon von anderen Programmbeispielen her kennen. Wichtig ist nur, daß wir hier Rasterzeile $ F8 als IRQ-Auslöser festlegen, und die zweite IRQ-Routine (" IRQ2") in den IRQ-Vektor eintragen. Ansonsten wird hier auch noch der FLD-Zeilenzähler in Speicherzelle $02 gelöscht, sowie der Wert $ FF in letzte Adresse des VIC-Bereichs geschrieben. Die Bedeutung dieser Adresse kennen wir noch von unserer Borderroutine aus dem letzten Kursteil: ihr Inhalt wird in schwarzer Farbe immer an allen Stellen auf dem Bildschirm angezeigt, an denen wir den Rasterstrahl mit unseren Interrupts austricksen, was ja auch hier der Fall ist. Möchten wir an solchen Stellen die Hintergrundfarbe sehen, so müssen wir den Wert $00 hineinschreiben.
Die Routine " IRQ2" wird nun immer einmal pro Bildschirmaufbau aufgerufen. Sie bereitet die eigentliche FLD-Routine vor, die ab der Rasterzeile $71 ausgelöst werden soll. Gleichzeitig beinhaltet diese Routine eine Joystickabfrage, mit der wir das Zählregister in Adresse $02 ändern können. Auf diese Weise kann mit dem Joystick die FLD-Lücke ab Rasterzeile $71, je nach Wunsch, vergrößert oder verkleinert werden. Abschließend biegt diese IRQ-Routine den IRQ-Vektor auf die eigentliche FLD-IRQ- Routine (" IRQ1") und ruft den System-IRQ auf, den wir in der Init-Routine ja abgeschaltet hatten und nun " von Hand" ausführen müssen.
Hiernach ist nun " IRQ1" am Zug. Kern der Routine ist die Schleife zwischen den beiden Labels " LAB1" und " LAB2" . Am wichtigsten sind hierbei die fünf Befehle die ich Ihnen mit Ausrufungszeichen markiert habe. Hier wird zunächst der Inhalt des Registers $ D011 gelesen, in dem der vertikale Verschiebeoffset zu finden ist, und 1 auf diesen Wert hinzuaddiert. Da dabei auch ein Öberlauf in das 3 . Bit des Registers stattfinden kann, das ja nicht mehr zur vertikalen Verschiebung herangezogen wird, müssen wir mit dem folgenden AND-Befehl alle Bits außer den unteren dreien ausmaskieren, und mittels ORA, das 3 . Bit wieder setzen, da es steuert, ob der Bildschirm ein, oder ausgeschaltet sein soll, und deshalb immer gesetzt sein muß. Anschließend wird der neue Wert für $ D011 wieder zurückgeschrieben. Da diese Verschiebungsänderung nun auch in jeder folgenden Zeile auftreten soll, solange bis der Zeilenzähler abgelaufen ist, müssen mit dem Rest der Routine die 63 Taktzyklen, die der Rasterstrahl zum Aufbau einer Rasterzeile braucht, verzögert werden. Eine Unterscheidung in normale Rasterzeilen und Charakterzeilen, in denen der Prozessor vom VIC ja für 42 Taktzyklen angehalten wird, und die Schleife deshalb weniger verzögern muß, braucht diesmal nicht durchgeführt werden, da wir durch das " voruns- Herschieben" der nächsten Charakterzeile deren Aufbau ja solange verhindern, bis die Schleife der FLD-Routine beendet ist. Dies ist dann der Fall, wenn entwe- der der Zähler im X-Register bis auf 0 gezählt wurde, oder aber die Rasterzeile $ F6 erreicht wurde, ab der der untere Bildschirmrand beginnt.
Ab dem Label " LAB2", wird nun wieder Rasterzeile $ F8 für " IRQ2" als Interruptauslöser festgelegt. Zusätzlich verbiegen wir den IRQ-Vektor auf diese Routine zurück. Dabei wird in unserem Beispiel lediglich das Low-Byte geändert, da beide Routinen ja an einer Adresse mit $10 xx anfangen, und somit die High-Bytes der beiden Routinenadressen immer den Wert $10 haben. Zum Schluß wird wieder auf den Teil der Betriebssystemroutine ($ FEBC) gesprungen, der die Prozessorregister vom Stack zurückholt und den Interrupt beendet.
Die Art und Weise, wie wir hier die Vertikalverschiebung vor dem Rasterstrahl herschieben mag etwas umständlich anmuten. Tatsächlich gibt es hier auch noch andere Möglichkeiten, die in den Bei- spielprogrammen " FLD-DEMO2", und " FLD-DEMO3" benutzt wurden. Sauberer ist die Lösung des Zeilenproblems, wenn man das Register, das die aktuelle Rasterzeile enthält ($ D012), als Zähler verwendet.
Wir müssen hier lediglich die Rasterposition auslesen, ihren Wert um 1 erhöhen, die unteren drei Bits ausmaskieren und das 4 . Bit in diesem Register wieder setzen. Selbiges wird durch die folgende Befehlsfolge durchgeführt:

clc                                     
lda $d012                               
adc #$01                                
and #$07                                
ora #$10                                
sta $d011                               

Noch schneller geht das, wenn man den illegalen Opcode " ORQ" verwendet. Er addiert 1 auf den Akku hinzu und verodert gleichzeitig das Ergebnis mit dem Operandenwert. Die Befehlsfolge ist dann nur noch vier Zeilen lang:

lda $d012                               
and #$07                                
orq #$10                                
sta $d011                               

Selbst wenn diese Methode kürzer ist, als die zuvorgenannte, ist es dennoch nicht ratsam sie zu verwenden, da " ORQ" wie gesagt ein illegaler, also inoffizieller, Assemblerbefehl ist, und deshalb von den meisten Assemblern und Disassemblern nicht erkannt wird. Zudem können Laufzeitunterschiede oder gar Fehlfunktionen bei verschiedenen Produktionsversionen des 6510- Prozessors vorkommen, so daß ein Programm mit einem solchen illegalen Opcode nicht auf jedem C64 lauffähig sein muß. Wer es wirklich kurz will, der sollte über eine Tabelle die benötigten Zeilendaten holen, wie das im Beispiel " FLD-DEMO3" der Fall ist. Hier wurde eine Tabelle bei Adresse$1200 abgelegt, die den jeweils entsprechenden Wert für jede einzelne Rasterzeile enthält. Die eigentlichen FLD-Befehle verkürzen sich damit auf die beiden folgenden Zeilen:

lda $1200,x                             
sta $d011                               

Die Lösung des Problems über eine Tabelle beinhaltet gleichzeitig auch noch den Vorteil, daß wir viel flexibler die FLD-Effekte einsetzen können. So ist es damit sehr einfach möglich, mehrere Charakterzeilen zu verschieben, wie das im " FLD-DEMO3" der Fall ist. Dieses Beispielprogramm beginnt übrigens ausnahmsweise an Adresse $1100, weswegen es nicht wie sonst mit " SYS4096", sondern durch ein " SYS4352" aufgerufen werden muß.
Alles in allem sollten Sie sich die drei Beispiele ruhig einmal mit einem Disassembler oder Speichermonitor anschauen um ihre Funktionsweise zu verstehen. Mit FLD erzielbare Effekte sind sehr vielseitig und sie sollten schon ein wenig damit herumexperimentieren. Weiterhin gibt es einige Rastereffekte die durch eine FLD-Routine stark vereinfacht programmiert werden können, oder sogar ohne sie gar nicht möglich wären, weswegen ein gründliches Verständnis der Materie sehr von Vorteil bei anderen Rastereffekten sein kann.
2) TIMINGPROBLEME UND TAKZYKLENMESSER Wie wir wieder einmal bewiesen haben, ist die Rasterprogrammierung eine Sache, bei der es auf absolut exaktes Timing ankommt. Noch haariger wird das im nächsten Kursteil ersichtlich, wo wir Ihnen eine Sideborder-Routine vorstellen werden. Wird diese Routine auch nur einen Taktzyklus zu früh oder zu spät ausgeführt, so funktioniert sie schon nicht mehr. Deswahlb wollen wir uns nun erst wieder ein wenig in die Theorie stürzen und Verfahrensweisen zur Ermittlung der Laufzeit eines Programms vorstellen.
Wie Sie mittlerweile nun oft genug mitbekommen haben, braucht der Rasterstrahl zum Aufbau einer Rasterzeile genau 63 Taktzyklen. Innerhalb dieser Zeit müssen wir den Prozessor immer irgendwie beschäftigen, damit wir rechtzeitig zum Beginn der nächsten Rasterzeile eine weitere Änderung vornehmen können. Hinzu kommt, daß wir bei eigeschaltemem Textmodus und Rastereffekten im sichtbaren Bildschirmbereich beachten müssen, daß jede achte Rasterzeile, jeweils am Beginn einer Charakterzeile, der VIC den Prozessor für 42 Taktzyklen anhält, damit er die, in den folgenden acht Rasterzeilen darzustellenden, Zeichen generieren kann. Somit bleiben für den Prozessor für solch eine Rasterzeile nur noch 21 Taktzyklen Rechenzeit. Um nun ein exaktes Timing zu erreichen müssten wir eigentlich die Laufzeiten eines je- den einzelnen Befehls einer Raster-Routine zusammenaddieren um herauszufinden, ob eine Routine schnell, bzw. langsam genug, abgearbeitet wird. Das kann unter Umständen eine sehr aufwendige Sache werden, da hierbei ewig lang Befehlstabellen mit Zyklenangaben gewälzt werden müssten, und bei jeder kleinen Änderung neue Verzögerungsbefehle in die Routine eingefügt, oder aus ihr entfernt werden müssten.
Damit Sie die Zyklenzahlen selbst zur Hand haben, habe ich Ihnen am Ende dieses Kurses in einer Tabelle alle Prozessor- Befehle in allen möglichen Adressierungsarten aufgelistet. Um also von Hand die Laufzeit einer Routine zu berechnen können Sie dort nachschlagen.
Noch einfach geht das Abwägen der Laufzeit jedoch mit einem Programm. Wir können uns hier die Möglichkeit zunutze machen, daß mit den Timern der CIAs einzelne Zyklen gezählt werden können. Ich habe Ihnen hierzu ein Zyklenmessprogramm geschrieben, das es Ihnen ermöglicht, eine eigene Routine bezüglich ihrer Laufzeit zu testen. Es heißt " Cyclecount" und ist ebenfalls auf dieser MD zu finden. Das Programm ist in der Lage, Routinen mit einer Dauer von maximal 65490 Taktzyklen zu stoppen. Laden Sie es hierzu mit LOAD" CYCLECOUNT",8,1 in den Speicher und schreiben Sie Lowund High-Byte der zu testenden Routine in die Adressen 828/829($033 c/$033 d) . Die zu messende Routine muß mit einem " BRK"- Befehl beendet werden. Rufen Sie nun das Programm mit einem " SYS49152" auf. Cyclecount gibt Ihnen daraufhin den ermittelten Zyklen-Wert auf dem Bildschirm aus. Das Programm benutzt dabei den Timer A von CIA-B zum Messen der Zyklen. Es initialisert diesen Timer mit dem Wert $ FFFF, startet ihn und ruft daraufhin die zu testende Routine auf.
Zuvor wird der BRK-Vektor bei Adresse $0316/$0317 auf eine eigene Routine ver- bogen. Wird die zu testende Routine nun mit einem BRK-Befehl beendet, so wird sofort zur Auswertungsroutine von Cyclecount verzweigt, die den Timer wieder anhält und den in den Timerregistern enhaltenen Wert von $ FFFF subtrahiert.
Zudem müssen 45 Zyklen abgezogen werden, die hauptsächlich zur Ausführung des JMP-Befehls auf die zu testende Routine und durch den beendenden BRK-Befehl verbraucht wurden, und nicht mitgezählt werden dürfen.
( Anm. d. Red. : Bitte wählen Sie jetzt den 2 . Teil des Kurses aus dem Textmenu.)

Valid HTML 4.0 Transitional Valid CSS!