Sprite-Scroller

weitersagen ...
Tweet about this on TwitterShare on FacebookShare on Google+Share on LinkedIn

CBM prg StudioEine Laufschrift mit Sprites

Ja, ich weiß, bisher habe ich das Scrolling noch nicht angerührt und auch diesmal drücke ich mich davor und zeige euch statt dessen, wie ihr eine Laufschrift mit Sprites erzeugen könnt. Aber keine Angst, ‚Level 03 – Die Fahrt zur Basis‚ benötigt Scrolling, also wird es dort auch im Laufe des Beitrags beschrieben.

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., in dem ihr den Zeichensatz so ändert, dass für einen Buchstaben mehrere Zeichen verwendet werden oder mit einem hier vorgestellten Sprite-Scroller. Dieser erlaubt es außerdem die Laufschrift in den Rahmen zu verschieben.

Ihr solltet bereits wissen, wie man aufs 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.

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 diesmal einfach vor unserem Hauptprogramm ab! Die SYS-Anweisung zum Programmstart ändern wir dementsprechend auf 2014 SYS 2624, so dass direkt zur ersten Anweisung bei main gesprungen wird.

Dann richten wir hinter main als erstes wieder unseren Rasterzeilen-Interrupt ein.

Alles ‚kalter‚ Kaffee, daher spare ich mir weitere Worte und wir fahren direkt mit der Einrichtung der Sprites fort.

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.

 

Jetzt sollte das Programm ausführbar sein. Da wir zu Beginn jedes zweite Sprite mit $FF statt $00 initialisiert haben, können wir nun die Position unseres Laufbandes kontrollieren…

Sprite 0, 2, 4 und 6 sind gelb.
Sprite 0, 2, 4 und 6 sind gelb.

Da ist also unser ‚Laufband‚. Die Sprites 0,2,4 und 6 sind direkt erkennbar. Da wir diese mit $FF initialisiert haben, werden sie als gelbe Kästen angezeigt. Dies dient nur zur Kontrolle für unsere nächsten Schritte, später initialisieren wir 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, dass müssen wir beim verschieben beachten. Die Routine zum ‘shiften‚ ist sehr simpel.

Wie ihr seht, initialisieren wir unseren Schleifenzähler (X-Register) mit den eben ermittelten 8 Zeilen / 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.

Ungefähr das obere Drittel der Sprites wandert nach links.
Ungefähr das obere Drittel der Sprites wandert nach links.

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.

Die ersten beiden Zeilen (gelbe Markierung) und das gleich kommende Label  rasterirq_exit benötigen wir nur für unseren nächsten Test, die 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 aufs erste Zeichen, damit die Laufschrift wieder von vorne beginnt, haben wir keine $00 geht es direkt bei getChar weiter.

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 ( ZP_HELPADR) 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 @loop, in X haben wir uns ja eben unser Zeichen gemerkt.

Jetzt wissen wir, wo das Zeichen zufinden ist, ZP_HELPADR zeigt direkt darauf und müssen es nur noch in die Daten von Sprite-7 kopieren.

Wie immer beim Zugriff aufs 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, dass 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  rasterirq_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

und unsere Variablen für den Text der Laufschrift und den Zeiger aufs nächste Zeichen

Wie ihr seht wird infotextpos mit $FF initialisiert, dass 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.

Da kommt ein 'D'...
Da kommt ein ‚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.

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 $00 initialisiert wird!

Löscht nun direkt hinter  rasterirq diese Zeilen

und ersetzt sie durch

Wir verringern jetzt also unseren Zähler fürs 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 rasterirq_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:

Der Text hat das Laufen gelernt.
Der Text hat das Laufen gelernt.

Bereinigt jetzt noch das Programm (ALLE Sprites mit $00 initialisieren und die Zeile sta $0400 löschen), dann habt ihr auch eine ‘saubere‚ Laufschrift.

So wollten wir es haben.
So wollten wir es haben.

 

 


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‚ aufs 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 einwenig 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.

Oberer & unterer Rahmen offen + Scroller
Oberer & unterer Rahmen offen + Sprite-Scroller

Ich verabschiede mich jetzt und wünsche euch viel Spass bei euren Projekten, allerdings nicht ohne euch auf eine etwas erweiterte Fassung für den Java-Emulator zu verweisen und ein D64-Image zum Download anzubieten.

Hier noch der Source als ACME-Version z. B. fürs C64-Studio.

Gruss, Jörn

 


Schrott!!Naja...Geht so...Ganz gut...SUPER! (4 Bewertungen | Ø 5,00 von 5 | 100,00%)

Loading...


 

<<< zurück |

 

weitersagen ...
Tweet about this on TwitterShare on FacebookShare on Google+Share on LinkedIn

9 Gedanken zu „Sprite-Scroller“

  1. 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

      1. 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!

          1. Eine Möglichkeit wäre, dass du dafür sorgst, dass beim Überlauf von infotextpos, die Adresse bei lda infotext,X um 256 erhöht und infotextpos wieder auf 0 gesetzt wird. Sobald du dann das Null-BYTE im Text findest, muss die Adresse bei lda infotext,X wieder auf die ursprüngliche zurückgesetzt werden.
          2. Optimierungsmöglichkeiten gibt es auch einige. Wie von Spider erwähnt, ist die Multiplikation nicht optimal. Unter MUL & DIV (Ganzzahl) findest du weitere Infos.
            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 zum rti.
  2. 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!

    1. 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.

  3. 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

    1. 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.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Protected by WP Anti Spam