Nun wirds noch bunter…
In den vorangegangenen Beiträgen haben wir die offiziellen Grafikmodes des C64 kennengelernt. Es gibt aber noch eine Reihe Software-Grafikmodi. Schauen wir uns doch hier mal einen ersten dieser benutzerdefinierten Modi an.
Ihr solltet bereits mit den Beiträgen VIC-II: Grafikmodes – BITMAP (Multi-Color), VIC-II: Speicherbereiche festlegen und Der Rasterzeileninterrupt vertraut sein und wissen, wie man den Oberen & unteren Rand öffnen kann!
Flexible Line Interpretation (FLI)
Bevor wir vorwärts, Richtung FLI gehen, werfen wir noch mal einen Blick zurück auf den MultiColor-Bitmap-Modus. Wie wir gesehen haben, bietet der MC-Modus durch Halbierung der Auflösung in X-Richtung, die Möglichkeit mehr Farben zu verwenden. Außerdem ist der Bildschirm trotz Bitmap-Modus immer noch in 40*25 Zellen (bzw. Zeichen) zu je 4*8 Pixeln unterteilt. Durch setzen der Bit-Paare innerhalb einer jeden 4*8-Pixel großen Zelle können wir eine von vier Farben wählen. Die folgende Tabelle sollte euch bekannt sein.
%00 - Hintergrundfarbe aus $d021 %01 - Farbe lt. oberen 4-Bits des Bildschirmspeichers %10 - Farbe lt. unteren 4-Bits des Bildschirmspeichers %11 - Farbe lt. Farb-RAM ab $d800
Der FLI-Modus ist vom Prinzip identisch mit dem normalen MultiColor-Bitmap-Modus, nur dass er es uns ermöglicht pro 4*8 Pixel-Zelle sämtliche 16 Farben zu verwenden.
Wie kommen wir an mehr Farben?
Da uns auch im FLI-Modus nur zwei Bits je Farbe zur Verfügung stehen, stellt sich die Frage, wie man so bis zu 16 verschiedene Farben je Zelle verwenden kann? Wir müssen zur Laufzeit die festgelegten Farben ändern und dort kommt Der Rasterzeileninterrupt ins Spiel. Das Ändern der Hintergrundfarbe würde uns niemals für jedes Zeichen gelingen, dies scheidet also somit schon mal aus. Da sich das Farb-RAM immer an der Adresse $d800 befindet, müssten wir für jede Rasterzeile, für jede Zeichen-Zeile, einen neuen Wert dorthin schreiben. Auch dies ist unmöglich! Bleibt also nur der Bildschirmspeicher!! Von dort wird im Bitmap-Modus bei %01 bzw. %10 bekanntlich die Farbe für das Pixel geholt. Wo ist hier nun aber der Unterschied zum Color-RAM? Müssten wir nicht auch hier für jede Rasterzeile erneut Farbwerte in den BS-Speicher schreiben?? Theoretisch schon, es gibt aber auch eine schnellere Lösung (Tipp VIC-II: Speicherbereiche festlegen).
In Etappen zum FLI-Bild
Um die Theorie etwas aufzulockern, entwickeln wir jetzt eine FLI-Anzeigefunktion. Beginnen wir damit, die Speicherbereiche festzulegen, den Bitmap-Modus zu aktivieren und richten noch einen stabilen Raster-IRQ ein. Wir wählen die VIC-Bank 1, also den Bereich von $4000 – $7fff für unser Beispiel. Dort legen wir zu Beginn (also bei $4000) direkt das SCREENRAM ab. Die Bitmap landet in den zweiten 8KB, beginnend bei $6000. Das Basis-Programm solltet ihr mittlerweile selbst hinbekommen, daher verzichte ich auf weitere Ausführungen.
Mit einer Bitmap beginnen
RASTER = 45 ;hier den 1. Raster-IRQ auslösen SCREENRAM = $4000 ;BITMAP-Farben (wenn 'Pixel' %01 oder %10) BITMAPADDR = $6000 ;Start der Bitmap COLORRAM = $d800 ;Beginn des Farb-RAMs ;*** Startadresse *=$0801 ;** BASIC-Zeile: 2018 SYS 2062 !word main-2, 2018 !byte $9e !text " 2062" !byte $00,$00,$00 !zone main main ;*** Speicheraufteilung lda #%00000011 ;Datenrichtung für Bit 0 & 1 des Port-A sta $dd02 ;zum Schreiben freigeben lda #%00000010 ;Bank-1 sta $dd00 ;auswählen lda $d018 ;VIC-II Register 24 in den Akku holen and #%00001111 ;das SCREENRAM in den ersten 1KB Block! ora #%00001000 ;mit Bit-3 den Beginn des Bitmapspeichers sta $d018 ;(hier den zweiten 8KB-Block) festlegen ;*** MultiColor-Bitmap-Modus lda $d011 ;VIC-II Register 17 in den Akku ora #%00100000 ;Bitmap-Modus über Bit-5 sta $d011 ;einschalten lda $d016 ;VIC-II Register 22 in den Akku ora #%00010000 ;Multi-Color über Bit-4 sta $d016 ;aktivieren ;*** Rasterzeileninterrupt einrichten sei ;IRQs sperren lda #<MyIRQ ;Adresse unserer Routine in sta $0314 ;den RAM-Vektor lda #>MyIRQ sta $0315 lda #%00000001 ;Raster-IRQs vom VIC-II aktivieren sta $d01a lda #RASTER ;Hier soll unsere Linie erscheinen sta $d012 lda $d011 ;Zur Sicherheit höchste Bit and #%01111111 ;für die Rasterzeile löschen sta $d011 lda #%01111111 ;Timer-IRQs abschalten sta $dc0d lda $dc0d lda #%0000001 ;evtl. aktiven Raster-IRQ bestätigen sta $d019 cli ;Interrupts erlauben jmp * ;Endlosschleife !zone MyIRQ ;*** Raster-IRQ an Pagegrenze ausrichten, damit die Sprünge passen !align 255,0 MyIRQ ;*** Wenn wir hier landen, sind bereits 38-45 Taktzyklen ;*** in der aktuellen Rasterzeile (RASTER) vergangen! ;*** zweiten IRQ einrichten ;*** da die Zeit bei aktivierten ROM nicht reicht, ;*** können wir den 2. Raster-IRQ erst in der übernächsten Zeile bekommen lda #<DoubleIRQ ;(2 TZ) 2. Raster-IRQ einrichten sta $0314 ;(4 TZ) lda #>DoubleIRQ ;(2 TZ) sta $0315 ;(4 TZ) tsx ;(2 TZ) Stackpointer im X-Reg. retten stx DoubleIRQ+1 ;(4 TZ) und fürs zurückholen sichern! nop ;(2 TZ) nop ;(2 TZ) nop ;(2 TZ) lda #%00000001 ;(2 TZ) 1. Raster-IRQ später bestätigen ;------ ;26 TZ ;*** Jetzt sind 64-71 Taktzyklen vergangen und wir sind ;*** aufjedenfall in nächsten Rasterzeile (RASTER+1)! ;*** Verbraucht wurden 1-8 TZ inc $d012 ;(6 TZ) 2. IRQ in der übernächsten Zeile (RASTER+2)!!! ; $D012 wurde bereits automatisch erhöht sta $d019 ;(4 TZ) IRQ bestätigen cli ;(2 TZ) Interrupts für den 2. Raster-IRQ ; wieder freigeben ;*** Wir befinden uns in Rasterzeile RASTER+1 und ;*** haben bisher 13-20 Zyklen verbraucht ;*** etwas Zeit verschwenden... ldx #$08 ; 2 TZ .loop dex ;8 * 2 TZ = 16 TZ bne .loop ;7 * 3 TZ = 21 TZ ;1 * 2 TZ = 2 TZ ; ------ ; 41 TZ ;*** Bis hier sind 54-61 Taktzyklen vergannen, jetzt auf den IRQ warten... ;*** Der nächste Rasterinterrupt wird während dieser NOPs auftreten! nop ;2 TZ (55) nop ;2 TZ (57) nop ;2 TZ (59) nop ;2 TZ (51) nop ;2 TZ (63) nop ;2 TZ (65) DoubleIRQ ;*** Wir sind nun in Rasterzeile RASTER+2 und ;*** haben bisher genau 38 oder 39 Taktzyklen benötigt!! ;*** Wir können so sicher sein, da der IRQ während der NOPs auftrat. ;*** Jetzt exakt soviele Taktzyklen 'verschwenden', wie in ;*** dieser Zeile noch zu verarbeiten sind (also 24 oder 25). ldx #$00 ;(2 TZ) Platzhalter für 1. Stackpointer txs ;(2 TZ) Stackpointer vom 1. IRQ wiederherstellen nop ;(2 TZ) nop ;(2 TZ) nop ;(2 TZ) nop ;(2 TZ) bit $01 ;(3 TZ) ldx $d012 ;(4 TZ) lda #$01 ;(2 TZ) weiß schonmal in den Akku cpx $d012 ;(4 TZ) sind wir noch in Rasterzeile 22? ;------ ;25 TZ = 63 oder 64 TZ!!! beq MyIRQMain ;(3 TZ) wenn JA einen letzten Takt 'verschwenden' ;(2 TZ) sonst einfach weiterlaufen... !zone MyIRQMain ;*** Wir beginnen also immer exakt nach 3 TZ in der dritten Rasterzeile (RASTER+3) ;*** nach dem 1. Raster-IRQ (den hatten wir ja in für Zeile RASTER festgelegt) MyIRQMain ;*** Aufräumarbeiten lda #<MyIRQ ;Original IRQ-Vektor setzen sta $0314 lda #>MyIRQ sta $0315 lda #RASTER ;Original Rasterzeile setzen sta $d012 lda #%00000001 ;IRQ bestätigen sta $d019 jmp $ea81 ;zum Ende des 'Timer-Interrupts' springen
Das Programm ist so zwar lauffähig, zeigt aber bisher nur ein zufälliges Bild, abhängig von der Speicherkonfiguration.
Nun wollen wir eine von uns definierte Musterausgabe generieren. Dazu löschen wir als Erstes die kompletten 16KB, der von uns gewählten VIC-II-Bank. Fügt die neue Funktion clearALL einfach direkt hinter der Zeile jmp * ;Endlosschleife ein.
!zone clearAll clearALL ldx #64 ;Seitenzähler | 64 * 256 Bytes = 16KB ldy #0 ;Byte-Zähler tya ;0 fürs Löschen in den Akku .loop ;die 16KB-Bank beginnt mit dem BS-Speicher sta SCREENRAM,Y ;löschen dey ;Byte-Zähler verringern bne .loop ;solange nicht 0 nochmal -> .loop inc .loop+2 ;MSB von 'sta SCREENRAM,Y' erhöhen dex ;Seitenzähler verringern bne .loop ;solange nicht 0 nochmal -> .loop .loop1 sta COLORRAM,Y ;jetzt noch das COLORRAM löschen sta COLORRAM+256,Y sta COLORRAM+512,Y sta COLORRAM+744,Y dey ;Byte-Zähler verringern bne .loop1 ;solange nicht 0 nochmal -> .loop1 rts ;zurück zum Aufrufer
Wie ihr seht, ist die Schleife sehr einfach. Die Hauptschleife bei .loop setzt jedes einzelne Byte der 16KB VIC-Bank auf 0. Da die Bank direkt mit unserem Bildschirmspeicher bei SCREENRAM beginnt, starten wir mit dem Löschen dort. Nach je 256 Bytes, erhöhen wir dann das MSB der Adresse um eins, bis alle 64 Pages gelöscht wurden. Zum Schluß setzen wir bei .loop1 auch noch das COLOR-RAM auf 0.
Jetzt müssen wir nur noch die neue Funktion clearALL aufrufen, außerdem setzen wir noch die Rahmen- und Hintergrundfarbe und deaktivieren die Sprites. Fügt die folgenden Zeilen direkt vor jmp * ;Endlosschleife ein.
lda #6 ;dunkelblau sta $d020 ;als Rahmenfarbe lda #0 ;schwarz sta $d021 ;für den Hintergrund sta $d015 ;zur Sicherheit alle Sprites deaktivieren jsr clearALL ;die komplette 16KB-VIC-II-Bank löschen
Jetzt sollte unsere Anzeige schon mal sauber sein…
Nun bringen wir noch unser Testmuster auf den Bildschirm. Wir zeichnen dazu drei Zeilen mit je vier Zeichen, die jeweils eine Farbkombination enthalten und eine Zeile die alle möglichen Kombinationen enthält. Fügt die nächste Funktion drawPattern hinter der von eben ein.
!zone drawPattern drawPattern ldx #7 ;Schleifenzähler | 8 Bytes je 'Zeichen' .loop lda #%01010101 ;Muster für die erste Zeile sta BITMAPADDR+80,X ;4 'Zeichen' sta BITMAPADDR+88,X sta BITMAPADDR+96,X sta BITMAPADDR+104,X lda #%10101010 ;Muster für die zweite Zeile sta BITMAPADDR+400,X ;4 'Zeichen' sta BITMAPADDR+408,X sta BITMAPADDR+416,X sta BITMAPADDR+424,X lda #%11111111 ;Muster für die dritte Zeile sta BITMAPADDR+720,X ;4 'Zeichen' sta BITMAPADDR+728,X sta BITMAPADDR+736,X sta BITMAPADDR+744,X lda #%00011011 ;Muster für die vierte und letzte Zeile sta BITMAPADDR+1040,X ;4 'Zeichen' sta BITMAPADDR+1048,X sta BITMAPADDR+1056,X sta BITMAPADDR+1064,X dex ;Schleifenzähler verringern bpl .loop ;solange positiv nochmal -> .loop ldx #3 ;Schleifenzähler Farben für 4 'Zeichen' .loop1 lda #%11000011 ;Farbe grau und türkis sta SCREENRAM+10,X ;in den BS-Speicher sta SCREENRAM+50,X sta SCREENRAM+90,X sta SCREENRAM+130,X lda #7 ;Gelb sta COLORRAM+10,X ;ins Farb-RAM sta COLORRAM+50,X sta COLORRAM+90,X sta COLORRAM+130,X dex ;Schleifenzähler verringern bpl .loop1 ;solange positiv nochmal -> .loop1 rts ;zurück zum Aufrufer
Die Funktion ist total simpel: Bei .loop zeichnen wir unser Testmuster und bei .loop1 setzen wir die benötigten Farben im Bildschirmspeicher und Farb-RAM.
Sobald ihr direkt hinter jsr clearALL ;die komplette 16KB-VIC-II-Bank löschen! die neue Funktion mit jsr drawPattern ;unser Muster zeichnen aufruft und das Programm startet, erscheint das Muster auf dem Bildschirm.
Auch wenn es nicht danach aussieht, dies ist nun unsere MultiColor-Bitmap, mit dem Testmuster. Wir sehen hier nochmal den uns bekannten Fakt, dass ein MC-Bild in einem 4*8 Pixel großem Feld (die Zapfen unten am Muster) nur vier verschiedene Farben aufweisen kann.
Um nun doch mehr Farben, durch das eingangs erwähnte Ändern der Farbinformationen aus dem Bildschirmspeicher zu erreichen, müssen wir noch mal einen Blick auf die Funktionsweise des VIC-II und die Rasterzeilen werfen:
Wie wir wissen gibt es normale Rasterzeilen und die sog. Bad Lines. Diese benötigt der VIC, um die Daten für den Ausgabebereich vorzubereiten. Das macht er immer zu Beginn einer neuen Zeichen-Zeile, also jede achte Rasterzeile vom Anfang des Ausgabebereichs an.
Hier können wir nun ansetzen:
Um mehr Farben zu erreichen, müssen wir selbst für noch mehr Bad Lines sorgen!
Nur dann liest der VIC die neuen Daten und unsere Ausgabe ändert sich. Wie bereits erwähnt, reicht die Zeit aber nicht um die 1000 Bytes des Bildschirmspeichers zu kopieren. Aber wir können in der Zeit die Adresse für den Bildschirmspeicher ändern! Durch dieses Vorgehen erhöht sich natürlich auch unser Speicherbedarf. Wie wir gleichen sehen, brauchen wir dann bis zu 7KB mehr!
OK, schauen wir uns das mal genauer an und sorgen dafür, dass unser Testbild bunter wird.
Für mehr Farben sorgen
Beginnen wir damit, hinter SCREENRAM sieben weitere Speicherbereiche mit den gewünschten Farben einzurichten. Unser Ziel (für diesen ersten Versuch) ist es, folgende Ausgabe mit unserem Muster zu erreichen.
Wir wollen im oberen Bereich alle 16 Farben untereinander darstellen (pro Zeichen also 8 verschiedene Farben) und unten (in den Zapfen) verwenden wir in jedem 4*8-Pixel großen Zeichen sogar alle 16 Farben aufeinmal.
Dazu benötigen wir acht verschiedene Bildschirmspeicher. Jeder dieser Speicher wird für eine Rasterzeile einer Zeichen-Zeile aktiviert. Werft einfach einen Blick aufs folgende Bild, ich hoffe dann wird es klarer.
Rechts steht immer welches Screen-RAM in der jeweiligen Zeile aktiv ist. Schreiben wir also eine kleine Funktion, die die benötigten Bildschirmspeicher mit den entsprechenden Farben anlegt.
!zone initColors initColors ldy #0 ;Schleifenzähler für die acht Farbbereiche .loop ldx #3 ;Schleifenzähler Farben für 4 'Zeichen' lda testColors,Y ;Farbe holen .loop1 sta SCREENRAM+10,X ;in den BS-Speicher sta SCREENRAM+50,X sta SCREENRAM+90,X sta SCREENRAM+130,X dex ;Schleifenzähler (Farben) verringern bpl .loop1 ;solange positiv nochmal -> .loop1 inc .loop1+2 ;MSB für die zusätzlichen Bereiche erhöhen inc .loop1+2 ;MSB für die zusätzlichen Bereiche erhöhen inc .loop1+2 ;MSB für die zusätzlichen Bereiche erhöhen inc .loop1+2 ;MSB für die zusätzlichen Bereiche erhöhen inc .loop1+5 inc .loop1+5 inc .loop1+5 inc .loop1+5 inc .loop1+8 inc .loop1+8 inc .loop1+8 inc .loop1+8 inc .loop1+11 inc .loop1+11 inc .loop1+11 inc .loop1+11 iny ;Schleifenzähler (Farbbereiche) verringern cpy #8 bne .loop ;solange positiv nochmal -> .loop ldx #3 ;Schleifenzähler Farben für 4 'Zeichen' lda #7 ;Gelb .loop2 sta COLORRAM+10,X ;ins Farb-RAM sta COLORRAM+50,X sta COLORRAM+90,X sta COLORRAM+130,X dex ;Schleifenzähler verringern bpl .loop2 ;solange positiv nochmal -> .loop2 rts ;zurück zum Aufrufer
Wir gehen hier mit Y als Schleifenzähler, alle 8 Bildschirmspeicher durch. Dann holen wir uns aus der Tabelle testColors die benötigte Farbe und verwenden das X-Register um die vier Zeichen in einer Zeile zu füllen. Mit den sta SCREENRAM…,X Anweisungen füllen wir die vier Zeilen am Stück. Danach erhöhen wir das MSB aller sta SCREENRAM…,X-Anweisungen um 1024 (daher jeweils vier inc .loop1+…). Wurden alle acht Bildschirmspeicher vorbereitet, wird zum Schluß noch das Farb-RAM mit der Farbe gelb gefüllt und schon geht es zurück zum Aufrufer.
Löscht das bisherige Setzen der Farben aus drawPattern! Entfernt dafür alles nach bpl .loop ;solange positiv nochmal -> .loop und vor rts ;zurück zum Aufrufer.
Legt jetzt noch am Ende des Quellcodes die Tabelle testColors an.
testColors !byte %00001000, %00011001, %00101010, %00111011 !byte %01001100, %01011101, %01101110, %01111111
Laßt uns initColors direkt vor jsr drawpattern ;unser Muster zeichnen mit jsr initColors ;die Farben setzen aufrufen. Startet ihr anschließend das Programm, dann sieht unsere Anzeige etwas anders aus.
In der Tabelle testColors sind jetzt andere Farben als weiter oben definiert, daher sieht unsere Anzeige nicht mehr wie eben aus.
Um gleich die Adresse für den Bildschirmspeicher schnell ändern zu können, sollten wir uns eine weitere Tabelle anlegen. Wir legen in dieser Tabelle direkt die für jede Rasterzeile benötigten Werte für das VIC-II-Register 24 $d018 ab. Dort wird bekanntlich über die Bits 7-4 festgelegt, wo sich der Bildschirmspeicher befinden soll. Wir lassen die Tabelle gleich vom Assembler bei der Programmerstellung anlegen.
Um die Taktzyklen besser unter Kontrolle zu haben, richten wir die Tabelle an der Pagegrenze aus.
!align 255,0 d018Values !for x = 0 to 31 !for y = 0 to 7 !byte %10000*y+%1000 !end !end
Die Funktion zum Füllen ist wieder recht simpel. Wir legen in der Tabelle direkt die später benötigten Werte für $d018 ab. Wir nutzen dazu zwei !for-Schleifen, die vom Assembler bei der Programmerstellung ausgewertet werden, sie kosten uns also keine Zeit während der Programmausführung auf dem C64. Für $d018 benötigen wir im oberen Nibble die Position des Bildschirmspeichers und im unteren müssen wir daran denken die Speicherposition für die Bitmap zu setzen. Die innere y-Schleife, legt immer acht Bytes an, die die benötigten Werte für $d018 enthalten. Durch die äußere x-Schleife wiederholen wir das ganze 32 mal und füllen somit die komplette Page.
Ein Start würde nichts ändern, schließlich wird die Tabelle d018Values nur gefüllt, aber bisher nicht genutzt. Wollt ihr die Daten kontrollieren, dann könnt ihr direkt vor jmp * zum Testen mal die beiden folgenden Zeilen eingeben.
lda d018Values+0 sta $d018
Erhöht ihr den addierten Wert beim lda jetzt jedes Mal um 1 und startet das Programm, dann könnt ihr alle Farbkombinationen abrufen. Sobald ihr bei 7 angelangt seid, könnt ihr aufhören, danach geht es wieder von vorne los.
Vergesst nicht die beiden Zeilen wieder zu löschen!
Wie lösen wir eine Bad-Line aus?
Nun ist es an der Zeit, dass wir uns mal fragen, wie wir überhaupt eine Bad-Line auslösen? Eine Bad-Line tritt immer dann auf, wenn die unteren drei Bits der Rasterzeile mit den drei Bits für das vertikale Scrolling übereinstimmt! Wir können nun durch Veränderung der Scrollwerte, im VIC-Register 17 $d011, dafür sorgen, dass jede Rasterzeile zu einer Bad-Line wird! Auch hier sollten wir eine Tabelle, nennen wir sie d011Values 😉 , einsetzen. Fügt das Label ganz am Schluß, hinter dem d018Values-Block ein. Bytes brauchen wir diesmal nicht zu reservieren, hiernach kommt eh nichts mehr. Auch ein ALIGN 256 ist nicht nötig, da die Tabelle d018Values bereits an der Pagegrenze ausgelegt ist und sie exakt 256 Bytes belegt.
Wir können d011Values wieder ganz einfach vom Assembler füllen lassen:
d011Values !for x = 0 to 31 !for y = 0 to 7 !byte %111000 + y !end !end
Das erste FLI-Bild
Jetzt können wir im Raster-IRQ die Tabellen auslesen und für unser erstes FLI-Bild sorgen. Sucht das Label MyIRQMain und fügt direkt dahinter den folgenden Block ein.
MyIRQMain ;*** etwas Zeit vertrödeln... inc $d020 ;(6 TZ) hier zur Kontrolle mal die Rahmenfarbe ändern dec $d020 ;(6 TZ) bit $01 ;(3 TZ) nop ;(2 TZ) ldx #0 ;(2 TZ) Schleifenzähler für die Rasterzeilen ;*** der nächste Befehl wird direkt zu Beginn der 1. sichtbaren Zeile aufgerufen .loop lda d018Values+1,X ;(4 TZ) Wert für $d018 aus der Tabelle holen sta $d018 ;(4 TZ) und speichern lda d011Values+1,X ;(4 TZ) Wert für $d011 aus der Tabelle holen sta $d011 ;(4 TZ) und speichern inx ;(2 TZ) Schleifenzähler erhöhen cpx #199 ;(2 TZ) Wurde Zeile 199 erreicht? bne .loop ;(3 TZ) falls nicht -> .loop (daher 3 TZ) ;===== ;23 TZ nur noch Bad-Lines!!!
Zu Anfang verbummeln wir einfach wieder etwas Zeit, dann nutzen wir das X-Register als Schleifenzähler, um unsere Rasterzeilen abzuklappern. Wir holen ab .loop jedes Mal die Werte für $d018 und $d011 aus den zugehörigen Tabellen und füllen die Register. Dann wird der Schleifenzähler erhöht und sobald wir Zeile 199 erreicht haben, verlassen wir die Schleife. Wichtig ist, dass die Schleife exakt soviele Taktzyklen benötigt, wie wir in einer Bad Line zur Verfügung haben, nämlich 23.
Ein Start zeigt euch unser erstes FLI-Bild. Die gelbe Linie über dem Ausgabebereich dient nur zur Veranschaulichung, hier beginnen wir damit Zeit zu verbummbeln.
Aber irgendwie passt etwas nicht…
Unsere Anzeige ist vollkommen aus dem Tritt!?
Dies liegt daran, dass wir $d011 ändern, aber nicht wieder zurückstellen!
Wir sollten $d011 also noch in unserem Raster-IRQ zurücksetzen.
Sucht das Label DoubleIRQ. Kurz dahinter findet ihr vier nop-Anweisungen. Kommentiert drei davon aus und setzt das Register 17 $d011 zurück (s. gelb markierte Zeilen):
DoubleIRQ ;*** Wir sind nun in Rasterzeile RASTER+2 und ;*** haben bisher genau 38 oder 39 Taktzyklen benötigt!! ;*** Wir können so sicher sein, da der IRQ während der NOPs auftrat. ;*** Jetzt exakt soviele Taktzyklen 'verschwenden', wie in ;*** dieser Zeile noch zu verarbeiten sind (also 24 oder 25). ldx #$00 ;(2 TZ) Platzhalter für 1. Stackpointer txs ;(2 TZ) Stackpointer vom 1. IRQ wiederherstellen nop ;(2 TZ) ;nop ;(2 TZ) ;nop ;(2 TZ) ;nop ;(2 TZ) lda #%00111000 ;(2 TZ) $d011 wieder sta $d011 ;(4 TZ) zurücksetzen bit $01 ;(3 TZ) ldx $d012 ;(4 TZ) lda #$01 ;(2 TZ) weiß schonmal in den Akku cpx $d012 ;(4 TZ) sind wir noch in Rasterzeile 22? ;------ ;25 TZ = 63 oder 64 TZ!!! beq MyIRQMain ;(3 TZ) wenn JA einen letzten Takt 'verschwenden' ;(2 TZ) sonst einfach weiterlaufen...
Jetzt sieht es schon besser aus.
Wie so oft 😉 , gibt es aber noch ein weiters Problem!
Schaut euch mal den Beginn des Musters an…
Es fehlen drei Zeilen! Das Muster sollte mit schwarz, weiß und rot beginnen. Wir müssen hier daran denken, dass wir die Anzeige scrollen, um Bad-Lines zu erzeugen. Dadurch wandert ein Teil der Anzeige unter den Rahmen. Also öffnen wir den Rahmen doch einfach. Wie es der Zufall will, sind wir nach unserer Schleife von eben, direkt an der richtigen Stelle dafür. Ihr braucht also hinter ;23 TZ nur noch Bad-Lines!!! nur die beiden folgenden Zeilen einfügen.
lda #%01000110 ;Rahmen öffnen sta $d011
Jetzt könnt ihr das Programm starten und wir sind fast am Ziel.
Wer genau hinschaut sieht, dass die erste Zeile gelb und nicht schwarz ist. Dies liegt daran, dass wir $d018 ebenfalls noch initialisieren müssen.
Fügt direkt hinter den Zeilen von eben folgendes ein:
lda d018Values ;Für den nächsten Durchlauf wieder mit der sta $d018 ;richtigen Page starten
So, jetzt ist es endlich vollbracht!
Schaut ihr euch das Muster in der Vergrößerung an, dann seht ihr, dass wir unser ganz oben ausgegebenes Ziel erreicht haben.
Hier noch das komplette Listing zur Kontrolle…
RASTER = 45 ;hier den 1. Raster-IRQ auslösen SCREENRAM = $4000 ;BITMAP-Farben (wenn 'Pixel' %01 oder %10) BITMAPADDR = $6000 ;Start der Bitmap COLORRAM = $d800 ;Beginn des Farb-RAMs ;*** Startadresse *=$0801 ;** BASIC-Zeile: 2018 SYS 2062 !word main-2, 2018 !byte $9e !text " 2062" !byte $00,$00,$00 !zone main main ;*** Speicheraufteilung lda #%00000011 ;Datenrichtung für Bit 0 & 1 des Port-A sta $dd02 ;zum Schreiben freigeben lda #%00000010 ;Bank-1 sta $dd00 ;auswählen lda $d018 ;VIC-II Register 24 in den Akku holen and #%00001111 ;das SCREENRAM in den ersten 1KB Block! ora #%00001000 ;mit Bit-3 den Beginn des Bitmapspeichers sta $d018 ;(hier den zweiten 8KB-Block) festlegen ;*** MultiColor-Bitmap-Modus lda $d011 ;VIC-II Register 17 in den Akku ora #%00100000 ;Bitmap-Modus über Bit-5 sta $d011 ;einschalten lda $d016 ;VIC-II Register 22 in den Akku ora #%00010000 ;Multi-Color über Bit-4 sta $d016 ;aktivieren ;*** Rasterzeileninterrupt einrichten sei ;IRQs sperren lda #MyIRQ sta $0315 lda #%00000001 ;Raster-IRQs vom VIC-II aktivieren sta $d01a lda #RASTER ;Hier soll unsere Linie erscheinen sta $d012 lda $d011 ;Zur Sicherheit höchste Bit and #%01111111 ;für die Rasterzeile löschen sta $d011 lda #%01111111 ;Timer-IRQs abschalten sta $dc0d lda $dc0d lda #%0000001 ;evtl. aktiven Raster-IRQ bestätigen sta $d019 cli ;Interrupts erlauben lda #6 ;dunkelblau sta $d020 ;als Rahmenfarbe lda #0 ;schwarz sta $d021 ;für den Hintergrund sta $d015 ;zur Sicherheit alle Sprites deaktivieren jsr clearALL ;die komplette 16KB-VIC-II-Bank löschen jsr initColors ;die Farben setzen jsr drawPattern ;unser Muster zeichnen jmp * ;Endlosschleife !zone clearAll clearALL ldx #64 ;Seitenzähler | 64 * 256 Bytes = 16KB ldy #0 ;Byte-Zähler tya ;0 fürs Löschen in den Akku .loop ;die 16KB-Bank beginnt mit dem BS-Speicher sta SCREENRAM,Y ;löschen dey ;Byte-Zähler verringern bne .loop ;solange nicht 0 nochmal -> .loop inc .loop+2 ;MSB von 'sta SCREENRAM,Y' erhöhen dex ;Seitenzähler verringern bne .loop ;solange nicht 0 nochmal -> .loop .loop1 sta COLORRAM,Y ;jetzt noch das COLORRAM löschen sta COLORRAM+256,Y sta COLORRAM+512,Y sta COLORRAM+744,Y dey ;Byte-Zähler verringern bne .loop1 ;solange nicht 0 nochmal -> .loop1 rts ;zurück zum Aufrufer !zone drawPattern drawPattern ldx #7 ;Schleifenzähler | 8 Bytes je 'Zeichen' .loop lda #%01010101 ;Muster für die erste Zeile sta BITMAPADDR+80,X ;4 'Zeichen' sta BITMAPADDR+88,X sta BITMAPADDR+96,X sta BITMAPADDR+104,X lda #%10101010 ;Muster für die zweite Zeile sta BITMAPADDR+400,X ;4 'Zeichen' sta BITMAPADDR+408,X sta BITMAPADDR+416,X sta BITMAPADDR+424,X lda #%11111111 ;Muster für die dritte Zeile sta BITMAPADDR+720,X ;4 'Zeichen' sta BITMAPADDR+728,X sta BITMAPADDR+736,X sta BITMAPADDR+744,X lda #%00011011 ;Muster für die vierte und letzte Zeile sta BITMAPADDR+1040,X ;4 'Zeichen' sta BITMAPADDR+1048,X sta BITMAPADDR+1056,X sta BITMAPADDR+1064,X dex ;Schleifenzähler verringern bpl .loop ;solange positiv nochmal -> .loop rts ;zurück zum Aufrufer !zone initColors initColors ldy #0 ;Schleifenzähler für die acht Farbbereiche .loop ldx #3 ;Schleifenzähler Farben für 4 'Zeichen' lda testColors,Y ;Farbe holen .loop1 sta SCREENRAM+10,X ;in den BS-Speicher sta SCREENRAM+50,X sta SCREENRAM+90,X sta SCREENRAM+130,X dex ;Schleifenzähler (Farben) verringern bpl .loop1 ;solange positiv nochmal -> .loop1 inc .loop1+2 ;MSB für die zusätzlichen Bereiche erhöhen inc .loop1+2 ;MSB für die zusätzlichen Bereiche erhöhen inc .loop1+2 ;MSB für die zusätzlichen Bereiche erhöhen inc .loop1+2 ;MSB für die zusätzlichen Bereiche erhöhen inc .loop1+5 inc .loop1+5 inc .loop1+5 inc .loop1+5 inc .loop1+8 inc .loop1+8 inc .loop1+8 inc .loop1+8 inc .loop1+11 inc .loop1+11 inc .loop1+11 inc .loop1+11 iny ;Schleifenzähler (Farbbereiche) verringern cpy #8 bne .loop ;solange positiv nochmal -> .loop ldx #3 ;Schleifenzähler Farben für 4 'Zeichen' lda #7 ;Gelb .loop2 sta COLORRAM+10,X ;ins Farb-RAM sta COLORRAM+50,X sta COLORRAM+90,X sta COLORRAM+130,X dex ;Schleifenzähler verringern bpl .loop2 ;solange positiv nochmal -> .loop2 rts ;zurück zum Aufrufer !zone MyIRQ ;*** Raster-IRQ an Pagegrenze ausrichten, damit die Sprünge passen !align 255,0 MyIRQ ;*** Wenn wir hier landen, sind bereits 38-45 Taktzyklen ;*** in der aktuellen Rasterzeile (RASTER) vergangen! ;*** zweiten IRQ einrichten ;*** da die Zeit bei aktivierten ROM nicht reicht, ;*** können wir den 2. Raster-IRQ erst in der übernächsten Zeile bekommen lda #DoubleIRQ ;(2 TZ) sta $0315 ;(4 TZ) tsx ;(2 TZ) Stackpointer im X-Reg. retten stx DoubleIRQ+1 ;(4 TZ) und fürs zurückholen sichern! nop ;(2 TZ) nop ;(2 TZ) nop ;(2 TZ) lda #%00000001 ;(2 TZ) 1. Raster-IRQ später bestätigen ;------ ;26 TZ ;*** Jetzt sind 64-71 Taktzyklen vergangen und wir sind ;*** aufjedenfall in nächsten Rasterzeile (RASTER+1)! ;*** Verbraucht wurden 1-8 TZ inc $d012 ;(6 TZ) 2. IRQ in der übernächsten Zeile (RASTER+2)!!! ; $D012 wurde bereits automatisch erhöht sta $d019 ;(4 TZ) IRQ bestätigen cli ;(2 TZ) Interrupts für den 2. Raster-IRQ ; wieder freigeben ;*** Wir befinden uns in Rasterzeile RASTER+1 und ;*** haben bisher 13-20 Zyklen verbraucht ;*** etwas Zeit verschwenden... ldx #$08 ; 2 TZ .loop dex ;8 * 2 TZ = 16 TZ bne .loop ;7 * 3 TZ = 21 TZ ;1 * 2 TZ = 2 TZ ; ------ ; 41 TZ ;*** Bis hier sind 54-61 Taktzyklen vergannen, jetzt auf den IRQ warten... ;*** Der nächste Rasterinterrupt wird während dieser NOPs auftreten! nop ;2 TZ (55) nop ;2 TZ (57) nop ;2 TZ (59) nop ;2 TZ (51) nop ;2 TZ (63) nop ;2 TZ (65) DoubleIRQ ;*** Wir sind nun in Rasterzeile RASTER+2 und ;*** haben bisher genau 38 oder 39 Taktzyklen benötigt!! ;*** Wir können so sicher sein, da der IRQ während der NOPs auftrat. ;*** Jetzt exakt soviele Taktzyklen 'verschwenden', wie in ;*** dieser Zeile noch zu verarbeiten sind (also 24 oder 25). ldx #$00 ;(2 TZ) Platzhalter für 1. Stackpointer txs ;(2 TZ) Stackpointer vom 1. IRQ wiederherstellen nop ;(2 TZ) lda #%00111000 ;(2 TZ) $d011 wieder sta $d011 ;(4 TZ) zurücksetzen bit $01 ;(3 TZ) ldx $d012 ;(4 TZ) lda #$01 ;(2 TZ) weiß schonmal in den Akku cpx $d012 ;(4 TZ) sind wir noch in Rasterzeile 22? ;------ ;25 TZ = 63 oder 64 TZ!!! beq MyIRQMain ;(3 TZ) wenn JA einen letzten Takt 'verschwenden' ;(2 TZ) sonst einfach weiterlaufen... !zone MyIRQMain ;*** Wir beginnen also immer exakt nach 3 TZ in der dritten Rasterzeile (RASTER+3) ;*** nach dem 1. Raster-IRQ (den hatten wir ja in für Zeile RASTER festgelegt) MyIRQMain ;*** etwas Zeit vertrödeln... inc $d020 ;(6 TZ) hier zur Kontrolle mal die Rahmenfarbe ändern dec $d020 ;(6 TZ) bit $01 ;(3 TZ) bit $01 ;(3 TZ) ldx #0 ;(2 TZ) Schleifenzähler für die Rasterzeilen ;*** der nächste Befehl wird direkt zu Beginn der 1. sichtbaren Zeile aufgerufen .loop lda d018Values+1,X ;(4 TZ) Wert für $d018 aus der Tabelle holen sta $d018 ;(4 TZ) und speichern lda d011Values+1,X ;(4 TZ) Wert für $d011 aus der Tabelle holen sta $d011 ;(4 TZ) und speichern inx ;(2 TZ) Schleifenzähler erhöhen cpx #199 ;(2 TZ) Wurde Zeile 199 erreicht? bne .loop ;(3 TZ) falls nicht -> .loop (daher 3 TZ) ;===== ;23 TZ nur noch Bad-Lines!!! lda #%01000110 ;Rahmen öffnen sta $d011 lda d018Values ;Für den nächsten Durchlauf wieder mit der sta $d018 ;richtigen Page starten ;*** Aufräumarbeiten lda #MyIRQ sta $0315 lda #RASTER ;Original Rasterzeile setzen sta $d012 lda #%00000001 ;IRQ bestätigen sta $d019 jmp $ea81 ;zum Ende des 'Timer-Interrupts' springen testColors !byte %00001000, %00011001, %00101010, %00111011 !byte %01001100, %01011101, %01101110, %01111111 !align 255,0 d018Values !for x = 0 to 31 !for y = 0 to 7 !byte %10000*y+%1000 !end !end d011Values !for x = 0 to 31 !for y = 0 to 7 !byte %00111000 + y !end !end
So schön es auch ist, den FLI-Modus nun endlich ergründet zu haben, so langweilig ist unser Beispiel. Also zum Schluß noch ein Ausflug in die Praxis, wo wir übrigens auf ein weiteres Problem stoßen werden…
Wie sieht es in der Praxis aus?
Um mal schnell an ein Bild im FLI-Format zu kommen, habe ich eins konvertiert. Schauen wir uns erst mal die Unterschiede zu einer normalen Multi-Color-Bitmap an.
Nehmen wir dieses Bild als Ausgangspunkt…
…und konvertieren es, dann erhalten wir als Multi-Color-Bitmap folgendes Ergebnis…
…als FLI sieht das Ganze gleich viel besser aus…
Das Bild musste etwas beschnitten werden, damit es Verzerrungsfrei in 320*200 Pixel passt.
Um den Unterschied noch etwas besser zu erkennen, hier beide Bilder im direkten Vergleich.
Wie ihr seht, bietet das FLI-Bild mehr Details, dies wird durch die zusätzlichen Farben ermöglicht.
Diese Bilder sind übrigens direkt das Ergebnis der Konvertierung, ohne jegliche Nachbearbeitung. In der Praxis würdet ihr natürlich noch Hand anlegen und das Bild weiter optimieren.
Laßt uns nun das konvertierte FLI-Bild mit unserem Programm von eben anzeigen. Dazu brauchen wir nur wenige Änderungen. Ich beschreibe hier kurz die Schritte, gleich folgt dann das komplette Listing.
Zunächst kann alles weg, was mit dem Muster zusammenhängt. Wir benötigen nur das Aufbauen der Tabellen! Dann binden wir am Ende des Sourcecodes einfach unser Yoda-Bild ein. Das Bild ist im FLI-Graph 2.2 (dies ist ein C64-Programm, mit dem ihr FLI-Grafiken erstellen könnt) Format gespeichert, daher binden wir das Bild ab $3b00 ins Programm ein. Dies hat den Vorteil, dass wir nur noch das Farb-RAM kopieren müssen (siehe copyToColorRAM im folgenden Listing) und schon erscheint Meister Yoda auf dem Bildschirm.
RASTER = 45 ;hier den 1. Raster-IRQ auslösen SCREENRAM = $4000 ;BITMAP-Farben (wenn 'Pixel' %01 oder %10) BITMAPADDR = $6000 ;Start der Bitmap COLORRAM = $d800 ;Beginn des Farb-RAMs ;*** Startadresse *=$0801 ;** BASIC-Zeile: 2018 SYS 2062 !word main-2, 2018 !byte $9e !text " 2062" !byte $00,$00,$00 !zone main main ;*** Speicheraufteilung lda #%00000011 ;Datenrichtung für Bit 0 & 1 des Port-A sta $dd02 ;zum Schreiben freigeben lda #%00000010 ;Bank-1 sta $dd00 ;auswählen lda $d018 ;VIC-II Register 24 in den Akku holen and #%00001111 ;das SCREENRAM in den ersten 1KB Block! ora #%00001000 ;mit Bit-3 den Beginn des Bitmapspeichers sta $d018 ;(hier den zweiten 8KB-Block) festlegen ;*** MultiColor-Bitmap-Modus lda $d011 ;VIC-II Register 17 in den Akku ora #%00100000 ;Bitmap-Modus über Bit-5 sta $d011 ;einschalten lda $d016 ;VIC-II Register 22 in den Akku ora #%00010000 ;Multi-Color über Bit-4 sta $d016 ;aktivieren ;*** Rasterzeileninterrupt einrichten sei ;IRQs sperren lda #<MyIRQ ;Adresse unserer Routine in sta $0314 ;den RAM-Vektor lda #>MyIRQ sta $0315 lda #%00000001 ;Raster-IRQs vom VIC-II aktivieren sta $d01a lda #RASTER ;Hier soll unsere Linie erscheinen sta $d012 lda $d011 ;Zur Sicherheit höchste Bit and #%01111111 ;für die Rasterzeile löschen sta $d011 lda #%01111111 ;Timer-IRQs abschalten sta $dc0d lda $dc0d lda #%0000001 ;evtl. aktiven Raster-IRQ bestätigen sta $d019 jsr copyToColorRAM ;Daten ins Farb-RAM kopieren lda #0 ;dunkelblau sta $D021 ;für den Hintergrund lda #0 ;schwarz sta $D020 ;als Rahmenfarbe sta $D015 ;zur Sicherheit alle Sprites deaktivieren lda #$ff ;Muster für das Phantom-Byte sta BITMAPADDR+$1fff ;setzen cli ;Interrupts erlauben jmp * ;Endlosschleife !zone copyToColorRAM copyToColorRAM ldx #0 ;Schleifenzähler | 256 BYTEs .loop lda flifile+$100,X ;Farb-RAM Wert holen sta COLORRAM,X ;und speichern lda flifile+$200,X ;für die nächsten 3 Pages wiederholen sta COLORRAM+$100,X lda flifile+$300,X sta COLORRAM+$200,X lda flifile+$400,X sta COLORRAM+$300,X dex ;Schleifenzähler verringern bne .loop ;falls NICHT 0 -> .loop rts ;sonst zurück zum Aufrufer !zone MyIRQ ;*** Raster-IRQ an Pagegrenze ausrichten, damit die Sprünge passen !align 255,0 MyIRQ ;*** Wenn wir hier landen, sind bereits 38-45 Taktzyklen ;*** in der aktuellen Rasterzeile (RASTER) vergangen! ;*** zweiten IRQ einrichten ;*** da die Zeit bei aktivierten ROM nicht reicht, ;*** können wir den 2. Raster-IRQ erst in der übernächsten Zeile bekommen lda #<DoubleIRQ ;(2 TZ) 2. Raster-IRQ einrichten sta $0314 ;(4 TZ) lda #>DoubleIRQ ;(2 TZ) sta $0315 ;(4 TZ) tsx ;(2 TZ) Stackpointer im X-Reg. retten stx DoubleIRQ+1 ;(4 TZ) und fürs zurückholen sichern! nop ;(2 TZ) nop ;(2 TZ) nop ;(2 TZ) lda #%00000001 ;(2 TZ) 1. Raster-IRQ später bestätigen ;------ ;26 TZ ;*** Jetzt sind 64-71 Taktzyklen vergangen und wir sind ;*** aufjedenfall in nächsten Rasterzeile (RASTER+1)! ;*** Verbraucht wurden 1-8 TZ inc $d012 ;(6 TZ) 2. IRQ in der übernächsten Zeile (RASTER+2)!!! ; $D012 wurde bereits automatisch erhöht sta $d019 ;(4 TZ) IRQ bestätigen cli ;(2 TZ) Interrupts für den 2. Raster-IRQ ; wieder freigeben ;*** Wir befinden uns in Rasterzeile RASTER+1 und ;*** haben bisher 13-20 Zyklen verbraucht ;*** etwas Zeit verschwenden... ldx #$08 ; 2 TZ .loop dex ;8 * 2 TZ = 16 TZ bne .loop ;7 * 3 TZ = 21 TZ ;1 * 2 TZ = 2 TZ ; ------ ; 41 TZ ;*** Bis hier sind 54-61 Taktzyklen vergannen, jetzt auf den IRQ warten... ;*** Der nächste Rasterinterrupt wird während dieser NOPs auftreten! nop ;2 TZ (55) nop ;2 TZ (57) nop ;2 TZ (59) nop ;2 TZ (51) nop ;2 TZ (63) nop ;2 TZ (65) DoubleIRQ ;*** Wir sind nun in Rasterzeile RASTER+2 und ;*** haben bisher genau 38 oder 39 Taktzyklen benötigt!! ;*** Wir können so sicher sein, da der IRQ während der NOPs auftrat. ;*** Jetzt exakt soviele Taktzyklen 'verschwenden', wie in ;*** dieser Zeile noch zu verarbeiten sind (also 24 oder 25). ldx #$00 ;(2 TZ) Platzhalter für 1. Stackpointer txs ;(2 TZ) Stackpointer vom 1. IRQ wiederherstellen nop ;(2 TZ) ;nop ;(2 TZ) ;nop ;(2 TZ) ;nop ;(2 TZ) lda #%00111000 ;(2 TZ) $d011 wieder sta $d011 ;(4 TZ) zurücksetzen bit $01 ;(3 TZ) ldx $d012 ;(4 TZ) lda #$01 ;(2 TZ) weiß schonmal in den Akku cpx $d012 ;(4 TZ) sind wir noch in Rasterzeile 22? ;------ ;25 TZ = 63 oder 64 TZ!!! beq MyIRQMain ;(3 TZ) wenn JA einen letzten Takt 'verschwenden' ;(2 TZ) sonst einfach weiterlaufen... !zone MyIRQMain ;*** Wir beginnen also immer exakt nach 3 TZ in der dritten Rasterzeile (RASTER+3) ;*** nach dem 1. Raster-IRQ (den hatten wir ja in für Zeile RASTER festgelegt) MyIRQMain ;*** ein paar TZ vertrödeln, da wir durchs Scrolling bereits in einer Bad-Line sind! ;(3 TZ) Hier beginnter der stabile Raster-IRQ ldx #0 ;(2 TZ) Schleifenzähler initialisieren bit $01 ;(3 TZ) bit $01 ;(3 TZ) bit $01 ;(3 TZ) nop ;(2 TZ) nop ;(2 TZ) ;*** der nächste Befehl wird direkt zu Beginn der 1. sichtbaren Zeile aufgerufen .loop lda d018Values+1,X ;(4 TZ) Wert für $d018 aus der Tabelle holen sta $d018 ;(4 TZ) und speichern lda d011Values+1,X ;(4 TZ) Wert für $d011 aus der Tabelle holen sta $d011 ;(4 TZ) und speichern inx ;(2 TZ) Schleifenzähler erhöhen cpx #199 ;(2 TZ) Wurde Zeile 199 erreicht? bne .loop ;(3 TZ) falls nicht -> .loop (daher 3 TZ) ;===== ;23 TZ nur noch Bad-Lines!!! lda #%01000110 ;Rahmen öffnen sta $d011 ;*** Aufräumarbeiten lda #<MyIRQ ;Original IRQ-Vektor setzen sta $0314 lda #>MyIRQ sta $0315 lda #RASTER ;Original Rasterzeile setzen sta $d012 lda #%00000001 ;IRQ bestätigen sta $d019 jmp $ea81 ;zum Ende des 'Timer-Interrupts' springen !align 255,0 d018Values !for x = 0 to 31 !for y = 0 to 7 !byte %10000*y+%1000 !end !end d011Values !for x = 0 to 31 !for y = 0 to 7 !byte %111000 + y !end !end ;*** FilGraph 2.2 Dateiformat ;*** $3b00 Hintergrundfarbe je Rasterzeile ;*** $3c00 Farb-RAM ;*** $4000 Bildschirmspeicher (8 mal) ;*** $6000 die Bitmap *=$3b00 flifile !bin "Yoda_1.fli",,2
Packt die Yoda-Bilder bitte direkt ins Verzeichnis mit eurem Quellcode. Die Bilder findet ihr hier im ZIP…
Yoda - FLI-Bilder
Startet nun das obige Programm und ihr seht unser nächstes Problem.
Wie ihr seht, haben wir am linken Rand falsche Farben. Dies kommt durch unser pausenloses erzeugen der Bad-Lines. Bis die greifen, dauert es einfach etwas. Um das Problem zu beheben, kann man z. B. Sprites verwenden, die diesen Bereich überdecken und ansatzweise die richtigen Farben anzeigen könnten. Dies würde aber nur nach massiven Änderungen an unserem Programm klappen. Die einfachste Lösung ist es, das Bild auf der linken Seite zu bereinigen. Wir entfernen dazu alle Pixel auf der linken Seite auf einer Breite von exakt drei Zeichen.
Bindet einfach Yoda_2.fli ein und erstellt das Programm erneut:
So sieht die Anzeige doch besser aus, oder? Es fehlen zwar links die Bildinformationen, aber es sieht einfach schöner aus.
Falls euch der linke Rand wichtiger, als der rechte ist, dann könnt ihr das Bild natürlich auch einfach mit einem Grafikprogramm etwas verschieben. Yoda_3.fli zeigt, wie das aussehen könnte. Dabei habe ich das Logo aber noch gerettet.
So nun seid ihr für FLI gewappnet. Da der FLI-Modus sehr viel Rechenzeit und Arbeitsspeicher verbraucht, eignet er sich in erster Linie für Demos, Titelbilder oder evtl. für sehr statische Spiele (z. B. die Umsetzung von Brett- oder Kartenspielen).
Noch einige Anmerkung
Ihr könnt den Speicherbedarf und die Rechenzeit reduzieren, wenn ihr FLI-Bilder erzeugt, die z. B. nur jede zweite oder gar vierte Zeile neue Farben benötigen.
Wenn ihr euch das FLI-Graph-Format noch mal anseht…
;*** FilGraph 2.2 Dateiformat ;*** $3b00 Hintergrundfarbe je Rasterzeile ;*** $3c00 Farb-RAM ;*** $4000 Bildschirmspeicher (8 mal) ;*** $6000 die Bitmap
…dann wundert ihr euch evtl. über den ersten Block. Ab $3b00 findet ihr für jede Rasterzeile eine eigene Hintergrundfarbe für $d021! Ihr könnt die FLI-Routine nämlich auch so bauen, dass ihr in jeder Rasterzeile eine andere Hintergrundfarbe verwendet! Dadurch habt ihr theroretisch die Möglichkeit 25 verschiedene Farben (16 über die Bildschirmspeicher + 8 über die Hintergrundfarbe + 1 aus dem Farb-RAM) je 4*8 Pixel großem Zeichen anzuzeigen. Es bleibt natürlich bei den max. 16 Farben, die der C64 beherrscht, aber durch die neuen Kombinationsmöglichkeiten lassen sich dann sehr viel schönere Grafiken erzeugen und anzeigen.
Wenn ihr euch mit dem FLI-Modus beschäftigt, läuft euch evtl. auch noch der IFLI-Modus (Interlaced Flexible Line Interpretation) über den Weg. Dabei wird der MCI-Modus (MultiColor Interlaced) mit dem eben behandelten FLI-Modus kombiniert. Dazu sollten wir uns aber erstmal im nächsten Beitrag, mit dem MCI-Modus beschäftigen.
Hi Jörn, Many thanks for this awesome step by step tutorial.
however I’m facing an issue while trying to reproduce the intermediate steps :
I just can’t obtain the intermediate result for “Unser erste FLI-Bild.”
I get the exact same result as what i get in next step while opening the borders. And of course i can get the final result.
Trying to go back from the final result, I can produced the previous state (where borders have been opened). However i can get the exact same result for the previous step called “Unser erste FLI-Bild.”.
So this is quite a bit annoying to understand the whole process.
May you be so kind to post the full code which produces the result of “Unser erste FLI-Bild.” ?
Thanks !
Olivier.
Hi Olivier.
I checked the description and it was faulty.
I’m sorry for that!
I changed the description behind „Das erste FLI-Bild“.
Later I will work through it again and check if everything is working as intended.
Greetings Jörn
Many Thanks Jörn !!!!
I will review again and let you know if obviously everything is matching as expected !
Actually since i can’t speak german, I’m spending a lot of time translating all your tutorials to english ( I’m french and i use deepL to translate your tutorials) – for private usage of course – and also doing a little bit of conversion to make the code work with Kickasm (The assembler I’m used to). I want to thank you a lot for sharing this information with so many detailed explainations ! It’s so much fun to finally understand such topics ! Thanks again !
Olivier.
Hi Jörn,
I have just checked the modifications you have done and i’m happy because it all perfectly makes sense and work as expected 🙂
I noticed you have modified the main full code, and i think there’s a little quirk at line 259 :
;*** Aufräumarbeiten
lda #MyIRQ
sta $0315
Shouldn’t it be ? :
lda #MyIRQ
sta $0315
Again thanks a lot !
Olivier.
Ah it looks like there’s a problem with COPY PASTE, trying again :
At line 259 you have :
“lda #MyIRQ”
“sta $0315 ”
shouldn’t it be :
” lda #MyIRQ”
” sta $0315″
Sorry, but I can’t find any missing spaces.
Hallo Jörn,
eine Kleinigkeit stimmt im Text nicht, denn dort steht: “Für $d018 benötigen wir im oberen Nibble die Adresse des Bildschirmspeichers und im unteren müssen wir daran denken, den MultiColor-Modus zu aktivieren.”
Im unteren Nibble von $d018 steht allerdings tatsächlich die Position der Bitmap; in diesem Fall bedeutet der Wert #%1000 im unteren Nibble, dass die Bitmap bei $6000 liegt. Mit dem MultiColor-Modus hat $d018 nichts zu tun.
Hallo Simon,
vielen Dank für den Hinweis. Ich habe den Text eben korrigiert.
Gruß,
Jörn
Hallo Jörn,
mit was hast’n das Bild jeweils als MC- und FLI-Bitmap konvertiert?
Vielen Dank & Grüße!
Carsten
ich habe dafür Timanthes 3.0 verwendet.