Eine Laufschrift mit Sprites
Ja, ich weiß, bisher habe ich das Scrolling noch nicht angerührt und auch dieses Mal drücke ich mich davor und zeige euch stattdessen, wie ihr eine Laufschrift mit Sprites erzeugen könnt.
In Demos seht ihr häufig Laufschriften, die größer als die normalen 8×8 Pixel sind. Erzeugen könnt ihr solche Texte z. B., indem ihr den Zeichensatz so ändert, dass für einen Buchstaben mehrere Zeichen verwendet werden oder mit dem hier vorgestellten Sprite-Scroller. Dieser erlaubt es außerdem die Laufschrift in den Rahmen zu verschieben.
Ihr solltet bereits wissen, wie man auf das Char-ROM zugreift. Dieses wird u. a. in VIC-II: Eigener Zeichensatz beschrieben, schaut es euch bei Bedarf einfach mal an.
Die Idee
Um jetzt eine Laufschrift mit Hilfe von Sprites zu erzeugen, brauchen wir natürlich erstmal ein „Sprite-Laufband“. Dazu positionieren wir einfach sieben Sprites nahtlos nebeneinander. Diese Sprites werden in ihrer Breite verdoppelt und füllen somit den sichtbaren Bereich (der ist bekanntlich 320 Pixel breit) vollständig aus (7 * 24 * 2 = 7 * 48 = 336 Pixel). Dann holen wir uns das Aussehen des jeweiligen Zeichens aus dem Char-ROM und schieben es von rechts nach links durch die Sprites, so scrollt dann der Text über den Bildschirm.
Fangen wir doch einfach mal an, dann wird alles etwas klarer. Ich möchte im Vorwege noch darauf hinweisen, dass man den selben Effekt auch über andere Wege erreichen kann. Hier geht es aber wieder ums Verständnis und nicht um die optimale Lösung.
Das „Laufband“ vorbereiten
Die Initialisierung sollte kein Problem sein. Wir setzen sieben, in der Breite verdoppelte, Sprites, am oberen Bildschirmrand lückenlos aneinander.
;*** Startadresse *=$0801 ;*** BASIC-Zeile: 2018 SYS 2624 !word basicend-2, 2018 !byte $9e !text " 2624" !byte $00,$00,$00 basicend ;*** Platzhalter für 8 Sprites !align 63,0 ;für korrekten Start der Daten sorgen sprite0 sprite1 = sprite0+64 sprite2 = sprite1+64 sprite3 = sprite2+64 sprite4 = sprite3+64 sprite5 = sprite4+64 sprite6 = sprite5+64 sprite7 = sprite6+64 ;*** Hier beginnt erst das Hauptprogramm! (Adresse $0a40 = 2624) *=sprite7+64 main
Um die VIC-II-Probleme mit dem RAM-Bereich ab $1000 zu umgehen (schaut ggf. nochmal bei VIC-II: Speicherbereiche festlegen nach), legen wir die Spritedaten dieses Mal einfach vor unserem Hauptprogramm ab! Im Quellcode definieren wir nur acht Label sprite0…7, um leichter auf die einzelnen Spritedaten zugreifen zu können. Den Datenbereich füllen wir erst später! Die SYS-Anweisung zum Programmstart ändern wir dementsprechend auf 2018 SYS 2624, sodass direkt zur ersten Anweisung bei main gesprungen wird.
Dann richten wir hinter main als erstes wieder unseren Rasterzeilen-Interrupt ein.
;*** Hier beginnt erst das Hauptprogramm! (Adresse $0a40 = 2624) *=sprite7+64 main jsr clearSprites ;Spritedaten löschen sei ;IRQs sperren lda #<rasterIrq ;Vektor umbiegen sta $0314 lda #>rasterIrq sta $0315 lda #%00000001 ;Raster-IRQs erlauben sta $d01a lda #$7f ;Timer-IRQ sperren sta $dc0d lda $d011 ;höchstes BIT für die Rasterzeile löschen and #%01111111 sta $d011 lda #$ff ;gewünschte Zeile für den Raster-IRQ sta $d012 cli ;IRQs wieder erlauben
Alles kalter Kaffee, daher spare ich mir weitere Worte und wir fahren direkt mit der Einrichtung der Sprites fort.
;*** Sprites einrichten ;*** Startadresse berechnen ldx #sprite0/64 stx $07f8 inx stx $07f9 inx stx $07fa inx stx $07fb inx stx $07fc inx stx $07fd inx stx $07fe ;*** Y-Position für alle Sprites setzen lda #$32 sta $d001 sta $d003 sta $d005 sta $d007 sta $d009 sta $d00b sta $d00d ;*** X-Position für alle Sprites setzen lda #$18 sta $d000 lda #$48 sta $d002 lda #$78 sta $d004 lda #$a8 sta $d006 lda #$d8 sta $d008 lda #$08 sta $d00a lda #$38 sta $d00c ;*** X-Pos für Sprite 5 & 6 > 255 lda #%01100000 sta $d010 ;*** Farbe (gelb) für alle Sprites lda #$07 sta $d027 sta $d028 sta $d029 sta $d02a sta $d02b sta $d02c sta $d02d ;*** Die ersten sieben Sprites... lda #%01111111 sta $d01d ;in der Breite sta $d017 ;und Höhe verdoppeln sta $d015 ;und zum Schluß sichtbar schalten jmp * ;Endlosschleife
Der Block sieht zwar sehr umfangreich aus, er ist aber nur so lang, da wir hier immer die Werte für sieben Sprites setzen. Als erstes ermitteln wir den Beginn der Spritedaten für die Sprite-Pointer. Dann werden die Y- und X-Position gesetzt, dabei beachten wir, dass Sprite 5 und 6 eine X-Position haben, die über 255 liegt. Außerdem wird noch die Farbe aller Sprites auf gelb geändert. Zum Schluß verdoppeln wir die sieben Sprites in Breite (wichtig!) und Höhe (das ist eigentlich egal, ihr könnt auch darauf verzichten) und schalten sie sichtbar.
Das Programm bleibt zum Schluß einfach in einer Endlosschleife hängen.
Der Rasterzeileninterrupt ist aktuell nur ein Platzhalter, dort wird bisher nur der IRQ bestätigt und die Routine dann gleich wieder verlassen.
;*** Raster-IRQ rasterIrq pla ;Y- und X-Register vom Stack holen tay pla tax lda $D019 ;IRQ bestätigen sta $D019 pla ;Akku vom Stack rti ;Interrupt verlassen
Dann fehlt abschließend nur noch die Routine clearSprites. Damit wir beim ersten Start etwas sehen, lasst uns doch erstmal alle ungeraden Sprites mit #$00 tatsächlich löschen und alle geraden mit #$ff füllen, damit diese zur Kontrolle sichtbar sind.
;*** Alle Spritedaten auf 0 setzen. clearSprites ldx #62 nextByte lda #$ff sta sprite0,x sta sprite2,x sta sprite4,x sta sprite6,x lda #$00 sta sprite1,x sta sprite3,x sta sprite5,x sta sprite7,x dex bpl nextByte rts
Jetzt sollte das Programm ausführbar sein. Da wir eben jedes zweite Sprite mit $ff statt $00 initialisiert haben, können wir nun die Position unseres Laufbandes kontrollieren…
Da ist also unser Laufband. Die Sprites 0, 2, 4 und 6 werden wie geplant, direkt als gelbe Kästen angezeigt. Dies dient nur zur Kontrolle für unsere nächsten Schritte, später löschen wir natürlich alle Sprites mit $00!
Die Spritedaten durchreichen
Wir wollen gleich ins unsichtbare Sprite-7 ein neues Zeichen aufnehmen und dann diese Spritedaten durch alle Sprites schieben. Durch dieses Verschieben erhalten wir dann unser Scrolling. Diese Funktion können wir jetzt schon einbauen und testen. Wir verschieben einfach einen Teil der gelben Blöcke. Ein Standard-Zeichen im Char-ROM ist bekanntlich 8*8 Pixel groß, ein HiRes-Sprite bietet uns aber Platz für 24*21 Pixel. Wir müssen für unsere Laufschrift also nur die ersten acht Zeilen des Sprites beachten. Da ein HiRes-Sprite 24 Pixel breit ist, passen dort drei Zeichen hinein, das müssen wir beim verschieben beachten. Die Routine zum shiften ist sehr simpel.
;*** Zeichen durch die Sprites schieben shiftAll ldx #3*7 ;eine Zeile hat 3 Bytes; ein Zeichen 8 Zeilen shiftNext clc ;Carry löschen rol sprite7+2,X ;jetzt alle Spritedaten 'shiften' rol sprite7+1,X ;übers C-Flag wird das herausfallende rol sprite7,X ;Bit zum nächsten Daten-Byte übernommen rol sprite6+2,X rol sprite6+1,X rol sprite6,X rol sprite5+2,X rol sprite5+1,X rol sprite5,X rol sprite4+2,X rol sprite4+1,X rol sprite4,X rol sprite3+2,X rol sprite3+1,X rol sprite3,X rol sprite2+2,X rol sprite2+1,X rol sprite2,X rol sprite1+2,X rol sprite1+1,X rol sprite1,X rol sprite0+2,X rol sprite0+1,X rol sprite0,X dex ;das X-Register dreimal verringer dex ;da wir oben immer drei BYTEs auf einmal dex ;'shiften' bpl shiftNext ;solange positiv -> wiederholen rts ;zurück zum Aufrufer
Wie ihr seht, initialisieren wir unseren Schleifenzähler (X-Register) mit den eben ermittelten 8 Zeilen und 3 Bytes je Zeile. Da unsere Schleife läuft, solange die Prüfung positiv ist, rechnen wir aber nur 3*7! Dann löschen wir das Carry-Flag und rotieren nun einfach Zeile für Zeile alle Spritedaten durch. Dabei verschieben wir also die Daten jeweils um ein Bit, wobei das herausfallende Bit übers Carry-Flag zum nächsten Datenbyte weitergereicht wird. Mit dem verschieben beginnen wir im letzten Byte (bildlich ganz rechts) und verschieben es dann rückwärts durch alle Sprites. Sobald die Daten für Sprite-0 verschoben wurden, nehmen wir uns die nächste Zeile vor, bis alle 8 abgearbeitet wurden. Da wir immer drei Bytes aufeinmal verschieben, verringern wir das X-Register am Ende auch dreimal. Wurde alles abgearbeitet, geht es zurück zum Aufrufer.
Fügt jetzt direkt hinter dem Label rasterIrq einfach den Aufruf der neuen Funktion jsr shiftAll ;alle Spritedaten ‚shiften‘ ein und startet das Programm erneut.
Wie ihr seht, wandert der obere Teil der Sprites nach links aus dem Bildschirm. Sobald die Teil-Blöcke verschwunden sind, kommt aber erstmal nichts Neues mehr nach. Sorgen wir im kommenden Schritt also dafür, dass wir den ersten Buchstaben unserer Laufschrift einfügen.
Zeichen in die Spritedaten kopieren
Wir wollen nun das erste Zeichen unseres Textes ins Laufband einfügen und durchscrollen lassen. Fügt die kommenden Zeilen bitte direkt hinter dem Aufruf jsr shiftAll von eben ein.
lda infotextPos ;für den ersten Test nur das erste Zeichen bpl exit ;sonst gleich wieder raus... inc infotextPos ;Zeiger aufs nächste Zeichen erhöhen ldx infotextPos ;und ins X-Register holen lda infotext,X ;Zeichen in den Akku bne getChar ;falls kein Textende ($00) weiter bei getChar ldx #$00 ;sonst Zeiger aufs erste Zeichen stx infotextPos ;zurückstellen lda infotext ;und das Zeichen in den Akku holen
Die ersten beiden Zeilen (gelbe Markierung) und das gleich kommende Label exit benötigen wir nur für unseren nächsten Test. Diese müssen später wieder gelöscht werden. Da uns noch die Synchronisation fehlt, wollen wir erstmal nur das erste Zeichen unserer Laufschrift ausgeben!
Danach erhöhen wir einfach den Zeiger aufs nächste Zeichen infotextPos um eins und holen den Wert ins X-Register. Dann lesen wir das benötigte Zeichen in den Akku ein und prüfen, ob es $00 ist. Sollte dies der Fall sein, wurde das Textende erreicht und wir stellen den Wert zurück auf das erste Zeichen, damit die Laufschrift wieder von vorne beginnt, haben wir keine $00 geht es direkt bei getChar weiter.
getChar ;ein Zeichen aus dem Char-ROM holen sta $0400 ;nur zur Kontrolle ausgeben (kann später weg!) tax ;Zeichen ins X-Register lda #$00 ;Startadresse des Char-ROMs auf die Zero-Page sta ZPHELPADR lda #$d0 sta ZPHELPADR+1 nextChar ;Jetzt für jedes Zeichen, bis zum gesuchten, clc ;8-Bytes auf die Char-ROM-Adresse in der lda #$08 ;Zero-Page addieren adc ZPHELPADR sta ZPHELPADR lda #$00 adc ZPHELPADR+1 sta ZPHELPADR+1 dex bne nextChar
Bei getChar geben wir das zulesende Zeichen erstmal in der linken oberen Ecke des Bildschirms aus. Das dient nur der Kontrolle und Fehlersuche und kommt später wieder raus. Wir sichern unser Zeichen dann im X-Register und legen auf der Zero-Page ZPHELPADR die Startadresse des Char-ROMs $d000 ab. Um den Anfang unseres Zeichens zu finden, addieren wir dann solange 8 (für Bytes je Zeichen) auf diese Adresse, bis wir beim gesuchten Zeichen angekommen sind. Dabei dient das X-Register als Schleifenzähler für nextChar, in X haben wir uns ja eben unser Zeichen gemerkt.
Jetzt wissen wir, wo das Zeichen zufinden ist, ZPHELPADR zeigt direkt darauf und wir müssen es nur noch in die Daten von Sprite-7 kopieren.
lda #%11111011 ;E/A-Bereich abschalten, um aufs Char-ROM and $01 ;zugreifen zu können sta $01 ldy #$00 ;Y-Reg. für die Y-nach-indizierte-Adressierung lda (ZPHELPADR),Y ;jeweils ein BYTE aus dem Char-ROM sta sprite7+2 ;ganz nach rechts in Sprite-7 kopieren iny ;Y fürs nächste BYTE erhöhen lda (ZPHELPADR),Y sta sprite7+5 iny lda (ZPHELPADR),Y sta sprite7+8 iny lda (ZPHELPADR),Y sta sprite7+11 iny lda (ZPHELPADR),Y sta sprite7+14 iny lda (ZPHELPADR),Y sta sprite7+17 iny lda (ZPHELPADR),Y sta sprite7+20 iny lda (ZPHELPADR),Y sta sprite7+23 lda #%00000100 ;E/A-Bereich wieder aktivieren ora $01 sta $01
Wie immer beim Zugriff auf das Char-ROM, müssen wir den E/A-Bereich abschalten. Dies machen wir hier auch gleich zu Beginn. Dann kopieren wir mit acht fast identischen Anweisungen, die Daten des gesuchten Zeichens vom Char-ROM nach Sprite-7. Dabei legen wir die Daten wirklich ganz rechts im Sprite ab, das ist nicht zwingend notwendig, ich mache es hier aber so. Zum Schluß wird dann der E/A-Bereich wieder aktiviert und das Programm läuft einfach weiter. Das Label exit ist, wie oben erwähnt, nur für diesen Test wichtig und fliegt gleich wieder raus.
Jetzt benötigen wir natürlich noch die neue Konstante
ZPHELPADR = $fb
und unsere Variablen für den Text der Laufschrift und den Zeiger aufs nächste Zeichen
infotext ;Text für die Laufschrift !scr "dies ist ein spritescroller! " !scr "damit kann man z. b. eine laufschrift im rahmen bauen. " !scr "er verwendet z. zt. den standard zeichensatz des c64 als muster.... " !byte $00 ;Textende infotextPos ;Zeiger auf aktuelles Zeichen !byte $ff
Wie ihr seht, wird infotextPos mit $ff initialisiert, dies ist wichtig, damit wir zu Beginn mit dem ersten Zeichen loslegen. Wie im Source zu erkennen, wird der Zeiger aufs nächste Zeichen immer erst erhöht und dann aufs Zeichen zugegriffen.
Der nächste Test sollte jetzt von ganz rechts das erste Zeichen der Laufschrift durchschieben, hier bei mir das D.
Auch die Testausgabe oben links in der Ecke zeigt das D.
Wenn ihr den Anfangsbuchstaben eurer Laufschrift ändert, sollte dieser nun über den Bildschirm wandern. Nur das dürft ihr nicht verwenden, da dieses Zeichen die Nr. $00 hat und für unser Textende steht! Werft zur Not nochmal einen Blick aufs Char-ROM.
Jetzt müssen wir nur noch dafür sorgen, dass der Text komplett durch die Sprites gejagd wird.
Den kompletten Text durchschieben
Wir haben es fast geschafft, jetzt müssen wir nur noch dafür sorgen, dass der gesamte Text durchläuft. Um zu erkennen, wann wir den nächsten Buchstaben nachschieben müssen, brauchen wir jetzt noch einen weiteren Zähler. Fügt zu den Variablen von eben noch die folgende hinzu.
infotextBitPos !byte 0
Hier zählen wir mit, um wieviele Pixel die Spritedaten bereits verschoben wurden. Nach jeweils 8 Pixeln, holen wir uns das nächste Zeichen. Es ist wichtig, dass diese Variable mit 0 initialisiert wird!
Löscht nun direkt hinter rasterIrq diese Zeilen
jsr shiftAll ;alle Spritedaten 'shiften' lda infotextPos ;für den ersten Test nur das erste Zeichen bpl exit ;sonst gleich wieder raus...
und ersetzt sie durch
dec infotextBitPos ;wurden bereits 8-Bits 'verschoben' bpl shiftAll ;falls nein, alle Spritedaten 'shiften' lda #$07 ;sonst Zähler zurücksetzen sta infotextBitPos ;und dann nächstes Zeichen holen
Wir verringern jetzt also unseren Zähler für das Verschieben, direkt beim Auftreten des Raster-IRQs. Solange dieser positiv ist, geht es bei shiftAll weiter, sonst setzen wir den Zähler zurück und laufen automatisch zu getChar weiter.
Kopiert jetzt noch unsere shiftAll-Routine an die Stelle vom überflüssigen Label exit. Vergesst nicht den rts-Befehl am Ende von shiftAll zu löschen!
Jetzt läuft das Programm also nach dem Kopieren des nächsten Zeichens automatisch zum Verschieben weiter.
Das Programm sollte jetzt wieder startbar sein:
Bereinigt jetzt noch das Programm (in clearSprite ALLE Sprites mit $00 initialisieren und die Zeile sta $0400 löschen), dann habt ihr auch eine saubere Laufschrift.
ZPHELPADR = $fb ;*** Startadresse *=$0801 ;*** BASIC-Zeile: 2018 SYS 2624 !word basicend-2, 2018 !byte $9e !text " 2624" !byte $00,$00,$00 basicend ;*** Platzhalter für 8 Sprites !align 63,0 ;für korrekten Start der Daten sorgen sprite0 sprite1 = sprite0+64 sprite2 = sprite1+64 sprite3 = sprite2+64 sprite4 = sprite3+64 sprite5 = sprite4+64 sprite6 = sprite5+64 sprite7 = sprite6+64 ;*** Hier beginnt erst das Hauptprogramm! (Adresse $0a40 = 2624) *=sprite7+64 main jsr clearSprites sei ;IRQs sperren lda #<rasterIrq ;Vektor umbiegen sta $0314 lda #>rasterIrq sta $0315 lda #%00000001 ;Raster-IRQs erlauben sta $d01a lda #$7f ;Timer-IRQ sperren sta $dc0d lda $d011 ;höchstes BIT für die Rasterzeile löschen and #%01111111 sta $d011 lda #$ff ;gewünschte Zeile für den Raster-IRQ sta $d012 cli ;IRQs wieder erlauben ;*** Sprites einrichten ;*** Startadresse berechnen ldx #sprite0/64 stx $07f8 inx stx $07f9 inx stx $07fa inx stx $07fb inx stx $07fc inx stx $07fd inx stx $07fe ;*** Y-Position für alle Sprites setzen lda #$32 sta $d001 sta $d003 sta $d005 sta $d007 sta $d009 sta $d00b sta $d00d ;*** X-Position für alle Sprites setzen lda #$18 sta $d000 lda #$48 sta $d002 lda #$78 sta $d004 lda #$a8 sta $d006 lda #$d8 sta $d008 lda #$08 sta $d00a lda #$38 sta $d00c ;*** X-Pos für Sprite 5 & 6 > 255 lda #%01100000 sta $d010 ;*** Farbe (gelb) für alle Sprites lda #$07 sta $d027 sta $d028 sta $d029 sta $d02a sta $d02b sta $d02c sta $d02d ;*** Die ersten sieben Sprites... lda #%01111111 sta $d01d ;in der Breite sta $d017 ;und Höhe verdoppeln sta $d015 ;und zum Schluß sichtbar schalten jmp * ;Endlosschleife ;*** Alle Spritedaten auf 0 setzen. clearSprites ldx #62 lda #$00 nextByte sta sprite0,x sta sprite1,x sta sprite2,x sta sprite3,x sta sprite4,x sta sprite5,x sta sprite6,x sta sprite7,x dex bpl nextByte rts ;*** Raster-IRQ rasterIrq dec infotextBitPos ;wurden bereits 8-Bits 'verschoben' bpl shiftAll ;falls nein, alle Spritedaten 'shiften' lda #$07 ;sonst Zähler zurücksetzen sta infotextBitPos ;und dann nächstes Zeichen holen inc infotextPos ;Zeiger aufs nächste Zeichen erhöhen ldx infotextPos ;und ins X-Register holen lda infotext,X ;Zeichen in den Akku bne getChar ;falls kein Textende ($00) weiter bei getChar ldx #$00 ;sonst Zeiger aufs erste Zeichen stx infotextPos ;zurückstellen lda infotext ;und das Zeichen in den Akku holen getChar ;ein Zeichen aus dem Char-ROM holen tax ;Zeichen ins X-Register lda #$00 ;Startadresse des Char-ROMs auf die Zero-Page sta ZPHELPADR lda #$d0 sta ZPHELPADR+1 nextChar ;Jetzt für jedes Zeichen, bis zum gesuchten, clc ;8-Bytes auf die Char-ROM-Adresse in der lda #$08 ;Zero-Page addieren adc ZPHELPADR sta ZPHELPADR lda #$00 adc ZPHELPADR+1 sta ZPHELPADR+1 dex bne nextChar lda #%11111011 ;E/A-Bereich abschalten, um aufs Char-ROM and $01 ;zugreifen zu können sta $01 ldy #$00 ;Y-Reg. für die Y-nach-indizierte-Adressierung lda (ZPHELPADR),Y ;jeweils ein BYTE aus dem Char-ROM sta sprite7+2 ;ganz nach rechts in Sprite-7 kopieren iny ;Y fürs nächste BYTE erhöhen lda (ZPHELPADR),Y sta sprite7+5 iny lda (ZPHELPADR),Y sta sprite7+8 iny lda (ZPHELPADR),Y sta sprite7+11 iny lda (ZPHELPADR),Y sta sprite7+14 iny lda (ZPHELPADR),Y sta sprite7+17 iny lda (ZPHELPADR),Y sta sprite7+20 iny lda (ZPHELPADR),Y sta sprite7+23 lda #%00000100 ;E/A-Bereich wieder aktivieren ora $01 sta $01 ;*** Zeichen durch die Sprites schieben shiftAll ldx #3*7 ;eine Zeile hat 3 Bytes; ein Zeichen 8 Zeilen shiftNext clc ;Carry löschen rol sprite7+2,X ;jetzt alle Spritedaten 'shiften' rol sprite7+1,X ;übers C-Flag wird das herausfallende rol sprite7,X ;Bit zum nächsten Daten-Byte übernommen rol sprite6+2,X rol sprite6+1,X rol sprite6,X rol sprite5+2,X rol sprite5+1,X rol sprite5,X rol sprite4+2,X rol sprite4+1,X rol sprite4,X rol sprite3+2,X rol sprite3+1,X rol sprite3,X rol sprite2+2,X rol sprite2+1,X rol sprite2,X rol sprite1+2,X rol sprite1+1,X rol sprite1,X rol sprite0+2,X rol sprite0+1,X rol sprite0,X dex ;das X-Register dreimal verringer dex ;da wir oben immer drei BYTEs auf einmal dex ;'shiften' bpl shiftNext ;solange positiv -> wiederholen pla ;Y- und X-Register vom Stack holen tay pla tax lda $d019 ;IRQ bestätigen sta $d019 pla ;Akku vom Stack rti ;Interrupt verlassen infotext ;Text für die Laufschrift !scr "dies ist ein spritescroller! " !scr "damit kann man z. b. eine laufschrift im rahmen bauen. " !scr "er verwendet z. zt. den standard zeichensatz des c64 als muster.... " !byte $00 ;Textende infotextPos ;Zeiger auf aktuelles Zeichen !byte $ff infotextBitPos !byte 0
Wie im Text der Laufschrift bereits erwähnt, könnt ihr nun mit den Sprites machen was ihr wollt, u. a. auch im Rahmen anzeigen. Ihr könnt auch eigene Zeichensätze verwenden oder durch entsprechende Änderungen auf Multicolor umschalten. Wollt ihr größere Grafiken bewegen, dann müssen statt der bisherigen 8 Zeilen einfach mehr verschoben werden.
Natürlich sind auch massig Optimierungen möglich, z. B. ist es nicht so toll, immer live auf das Char-ROM zuzugreifen. Auch muss nicht zwingend pixelweise verschoben werden. Es gibt also auch noch andere Ansätze, um eine solche Laufschrift zu erzeugen. Experimentiert ein Wenig und verbessert das Programm.
Ich habe hier immer so getan, als wäre Sprite-7 für das neue Zeichen wichtig. In Wirklichkeit steht euch Sprite-7 noch voll zur Verfügung. Für das neue Zeichen wird nur ein freier Speicherblock im RAM benötigt und dieser ist vollkommen unabhängig von den Sprites. Also könnt ihr Sprite-7 z. B. um eure Laufschrift tanzen lassen.
Ich verabschiede mich jetzt und wünsche euch viel Spass bei euren Projekten, allerdings nicht, ohne euch eine etwas erweiterte Fassung
zum Download anzubieten.
Sprite-Scroller für das C64 Studio (ACME)
Gruss, Jörn
Hey, super schön erklärt – leider bekomme ich das Listing unter ACME nicht zum laufen. Hast Du es mal versucht oder weisst, warum es hakt?
Danke, Esshahn
hast du den Code denn korrekt an ACME angepasst? ACME hat eine andere Syntax bei den Pseudo-OpCodes (z. B. !byte statt einfach BYTE) und bei der Sichtbarkeit von lokalen (cheap) Labeln.
Hier findest du den Source fürs C64-Studio, der sollte mit ACME kompatibel sein.
Danke für den umgewandelten Code. Inzwischen habe ich ihn auch selbst konvertiert und zum laufen bekommen.
Ich habe aber noch weitere Fragen und hoffe Du kannst mir helfen:
1. Dein Scroller läuft nur bis 256 Zeichen und wrapt dann, wie müsste man den Code anpassen damit er mehr Zeichen anzeigt?
2. Gibt es irgendwo noch Möglichkeiten Cycles zu sparen? Hier wurde ja eine Möglichkeit angesprochen, aber mir fehlt die Erfahrung, das umzusetzen.
Bin Dir für Antwort sehr dankbar!
infotextpos
, die Adresse beilda infotext,X
um 256 erhöht undinfotextpos
wieder auf 0 gesetzt wird. Sobald du dann das Null-BYTE im Text findest, muss die Adresse beilda infotext,X
wieder auf die ursprüngliche zurückgesetzt werden.Statt die Zeichen pixelweise durch die Sprites zu shiften, könntest du die Sprites auch einfach wie beim Scrolling bewegen und dann immer nur ganze Zeichen umkopieren. Auch eine spezielle Organisation des Zeichensatzes bringt eine Beschleunigung. Statt immer alle 8 BYTEs eines Zeichens am Stück zu speichern, könnte man die Daten auch so organisieren, dass man zunächst nur das erste BYTE für alle Zeichen ablegt. Danach dann alle ‘zweiten’ BYTEs, ‘dritten’ BYTEs usw. Dann kann man sich die Positionsberechnung sogar ganz sparen. Zeitkritisch ist alles ab
getChar
bis zumrti
.Vielen Dank!
Nee, is auch okay so und alles schön didaktisch gut gemacht hier auf der Seite. Hab mich nur gewundert, weil Du ja im Multiplikation/Division Artikel bereits auf “Brute Force” vs. asl eingehst.
Auch wenn ich bislang für mich nichts Neues gefunden habe, macht es durchaus Spaß hier zu stöbern!
Deine ‘Sicht’ ist aber auch nicht verkehrt.
Eventuell sollte ich bereits behandelte Themen noch mehr einfließen lassen und dann zusätzlich auf die Grundlagen verweisen. Der Einsteiger wird dann ggf. ‘genötigt’ sich tiefer in die Materie einzuarbeiten und der Fortgeschrittene bekommt direkt ein Beispiel, das näher an der Praxis ist.
Auch wenn für dich alles kalter Kaffee ist, freut es mich, dass du hier dennoch vorbeischaust.
Der Loop bei getChar verbraucht unnötig viel Rasterzeit. Kann man auch so machen:
asl ZP_HELPADR
rol HIBYTE
asl ZP_HELPADR
rol HIBYTE
asl ZP_HELPADR
rol HIBYTE
clc
lda ZP_HELPADR+1
adc HIBYTE
sta ZP_HELPADR+1
asl entspricht *2, also asl, asl, asl entspricht *2*2*2 = *8
Deshalb steht ja auch mehrfach im Text, dass es ums Prinzip und nicht um die optimale Lösung geht.
Es wird sogar extra darauf hingewiesen, dass noch Optimierungen möglich/nötig sind.
Ich wähle für meine Erklärungen meistens den zwar langsameren, aber (zumindest in meinen Augen) leichter nachvollziehbaren Weg.
Trotzdem vielen Dank für dein Feedback, davon kann ich durchaus mehr gebrauchen. Vielleicht bin ich ja auch auf dem Holzweg und es wäre besser die Beispiele so effektiv wie möglich zu gestalten. Allerdings befürchte ich dadurch Einsteiger (für die das meiste hier gedacht ist) abzuschrecken.