Sprites

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

CBM prg Studio
SPRITES, die kann man nicht trinken

Hier schauen wir uns die Sprites unter BASIC an, wer den Assembler-Beitrag sucht findet diesen unter Sprites (ASM).

Sprites sind frei bewegliche Objekte. Diese bestehen bei einem einfarbigen Highres-Sprite aus 24 Punkten (horizontal / X-Richtung) und 21 Zeilen (vertikal / Y-Richtung). Bei einem Multi-Color Sprite sind es „nur„ 16 Punkte bei gleichbleibenden 21 Zeilen, das schauen wir uns aber erst unter Assembler genauer an. Der C64 kann standardmäßig 8 Sprites (von 0 bis 7 durchnummeriert) gleichzeitig darstellen, ihr ahnt es sicher schon, unter Assembler sind mehr Sprites möglich (Raster-Interrupts, Sprite-Multiplexing). Das Besondere an den Sprites war für die damalige Zeit, dass man sich keine Gedanken um den Hintergrund machen musste. Der VIC (Video Interface Controller; der Graphic-Chip {sozusagen die Grafikkarte} im C64) „rettet„ alles was das Sprite verdeckt hat und stellt es automatisch wieder her, sobald das Sprite weg ist. Auch eine Kollisionserkennung ist möglich.

Was soll unser Programm nun können? Wir möchten einen Sprite anzeigen und in der Mitte des Bildschirms immer von links nach rechts hin und her wandern lassen bis eine beliebige Taste gedrückt wird.

Als erstes müssen wir festlegen, wie unser Sprite aussehen soll. Nehmen wir einfach eine Art Smiley. Das Sprite kann man z. B. auf Karopapier zeichnen.

Das würde dann ungefähr so aussehen:

Entwurf des Smileys.
Entwurf des Smileys.

Nun müssen wir für jeden 8-Bit-Block (s. Binäreszahlensystem) den Wert des Bytes ermitteltn. Dazu einfach den Block als Binärzahl interpretieren (X = Bit auf 1 gesetzt) und in das Dezimalsystem umrechnen.

Diese Zahlen notieren wir und dann neben jeder Zeile

Dezimalwerte
Dezimalwerte

und übernehmen diese später in unser BASIC-Programm.

Alternativ können wir uns auch einen Sprite-Editor schreiben, das würde hier aber den Rahmen sprengen.

Ich biete hier einen Mittelweg an. Wir nutzen einfach eine Tabellenkalkulation. Ich habe eine Excel-Tabelle „SpriteEditor.xls“ erstellt, die als kleiner Sprite-Editor fungiert.


Wer kein Excel hat, kann z. B. auch Apache OpenOffice nehmen.

Wenn ihr euren Sprite damit erstellt sieht das Ergbnis ungefähr so aus:

Der Sprite-Editor
Der Sprite-Editor

Links seht ihr unsere Arbeitsfläche, leere Felder werden als 0 interpretiert, ein beliebiges Zeichen (auch SPACE!!) wird als 1 erkannt. In der Mitte findet ihr die Dezimalwerte für jedes der drei Bytes. Rechts könnt ihr die Startadresse eingeben ab der die DATA-Zeilen in euern BASIC-Programm erscheinen sollen. Darunter findet ihr die fertigen Programmzeilen, die ihr später einfach per Copy&Paste ins Programm übernehmen könnt, sofern ihr mit einem Windows-Editor arbeitet. Programmiert ihr direkt auf dem C64 bzw. im Emulator müsst ihr diese Zeilen abtippen.

Fangen wir damit an, unseren Sprite anzuzeigen:

CHR$() (Character / Zeichen) gibt das Zeichen zu einem Code aus der ASCII-Tabelle (American Standard Code for Information Interchange, daher „aski„ und nicht „ASC-2„!!) wieder, z. B. gibt PRINT CHR$(65) ein A aus dem Bildschirm (zukünftig mit BS abgekürzt) aus. Da der C64 eine ältere ASCII-Normung (von 1963) verwendet (beim PC wird die Fassung von 1967 verwendet) wird er dort auch PETSCII genannt. In dieser Tabelle sind also alle Zeichen hinterlegt, die der C64 darstellen kann (unter Assembler werden wir diesen Zeichensatz auch verändern).
Gibt man nun z. B. den Code 147 auf dem BS aus, so werden alle Zeichen auf dem BS gelöscht.

Hier werden sog. Variablen definiert. Variablen kann man sich als Schubladen vorstellen. Man legt etwas in eine Schublade bennent diese und wenn man später wieder etwas braucht kann man einfach sagen „Gib mir den Inhalt von Schublade ‚xyz‚.„

Beim Umgang mit Variablen unter BASIC gibt es einiges zu Beachten.
BASIC kennt drei verschiedene Datentypen für Variablen, der Datentyp wird über das letzte Zeichen festgelegt. Variablennamen müssen mit einem Buchstaben beginnen, können nach dem ersten Zeichen aber auch Zahlen enthalten.

zahl : steht am Ende kein % oder $, so wird die Variable als Fließkommazahl verwendet, d. h. ihr könnt in einer solchen Variable z. B. 2012, 3.1415 oder auch -8974.33 speichern. Wichtig ist das die Nachkommastellen mit Punkt . als Trennzeichen eingegeben werden.

zahl% : Mit einem Prozentzeichen % am Ende erzeugt ihr eine vorzeichenbehaftete 16-Bit große Ganzzahl, diese hat einem Wertebereich von -32768 bis 32767. Liegt eine Zahl außerhalb dieses Bereiches kommt es zu einem Programmfehler.

zahl$ : Steht das Dollarzeichen $ am Ende kann die Variable Strings (Texte und Zeichen) speichern.  Bei der Zuweisung muss der Text in Anführungszeichen „„ eingeschlossen sein. Hier ist z. B. „Hallo„, „123„ oder ähnliches möglich. Wichtig ist, dass z. B. „123„ ein Text ist und sich nicht direkt damit rechnen läßt!

Varibalennamen können theoretisch beliebig lang sein, aber es werden nur die ersten beiden Zeichen zur Unterschiedung von Variablen gleichen Typs verwendet!!!!
Die Variablen S% und SP% sind also unterschiedlich, aber SPORT% und SPIEL% sind für das C64 BASIC gleich!!! Wohingegen SP, SP% und SP$ unterschieden werden, da sie auch verschiedene Typen haben. Außerdem dürfen im Variablennamen keine Schlüsselwörte (also gültige BASIC-Befehle) vorkommen. FORTSCHRITT, ROTOR oder BREMEN führen z. B. zu einem Fehler, da in ihnen BASIC-Befehle vorkommen. In ROTOR stecken mit TO und OR sogar gleich zwei.

Was bringen uns Variablen nun? Nehmt mal an ihr habt ein Programm geschrieben und verwendet häufig die Farbe rot um etwas hervorzuheben. Nun habt ihr es euch aber anders überlegt und wollt rot verwenden, um Fehler anzuzeigen. Zum Hervorheben möchtet ihr nun gelb nehmen, also müsstet ihr alle entsprechenden Stellen im Programm suchen und eine 2 (Farb-Nr. für rot) gegen eine 7 für gelb tauschen. Da BASIC kein Suchen&Ersetzen bietet (was hier auch problematisch wäre) ist es sehr mühsam und fehleranfällig diese Änderung vorzunehmen. Mit einer Variablen z. B. HV% für HerVorheben, bräuchten wir nur eine 2 gegen die 7 tauschen.

Genug zu den Variablen, wo waren wir…?

Hier sind also unsere vier Variablen:
vic=53248 : wird als Fließkommazahl verwendet, da 53248 den Wertebereich einer Ganzzahl sprengt. 53248 ist die Basisadresse des VIC, wir werden auf einzelne Register (Speicherzellen) zugreifen, in dem wir zu dieser Basisadresse die Nr. des entsprechenden Registers addieren.

sb%=13 : Hier merken wir uns den Speicherblock, an dem der VIC unsere Spritedaten finden kann. Wir müssen für jeden Sprite mit einem Byte angeben, wo die Daten zu finden sind. Ein Spriteblock umfasst 64 Byte => 24 Punkte => 3 Byte * 21 Zeilen = 63 Byte + 1 Byte als Platzhalter. Durch das Byte für den Speicherblock ergeben sich also 256 * 64Byte = 16kb Speicher in denen wir unsere Datenablegen können. Hier ist vorallem unter BASIC Vorsicht geboten, damit wir uns nicht Speicher nehmen, der z. B. von BASIC selbst benötigt wird! Der 13. Block liegt im Kassettenpuffer und kann gefahrlos genommen werden.

sp=sb%*64 : Hier errechnen wir uns die Speicherposition (s. oben) an der wir unsere Spritedaten ablegen möchten. Dazu multiplizieren wir, wie eben, die Nr. des Speicherblocks (hier 13) mit 64 (max. 64 Byte je Sprite) und kommen so in diesem Beispiel auf 832. Also werden unsere Spritedaten ab der 832. Speicherstelle zu finden sein.

sn%=0 : Dort steht drin, welches Sprite wir benutzen. In unserem Fall einfach das erste mit den Nr. 0.

FOR NEXT: Eine sog. FOR-Schleife. Die Anweisungen zwischen FOR und NEXT werden so oft ausgeführt, wie es die Werte links und rechts vom TO angeben (auf STEP verzichte ich hier)
Wir möchten die nächsten Anweisungen (bis zum NEXT) für jedes der 63 Bytes unseres Sprites einmal ausführen. Die Variable x nimmt hier nacheinander alle Werte von 0 bis 62 an.

READ: Dateneinlesen (hier aus unseren Datazeilen ab Zeile 10000 s. weiter unten)
Da wir uns innerhalb der FOR-Schleife befinden, wird bei jedem Durchlauf mit READ das nächste Byte aus unseren Datazeilen ab Zeile 10000 gelesen und in der Variablen b% gespeichert.

Hier schreiben wir unser in Zeile 40 gelesenes Byte b% in den Speicher. Wie ihr euch erinnert, steht in sp (s. oben) die Speicherposition ab der wir unsere Spritedaten ablegen wollen. Da x nacheinander die Werte 0 bis 62 annimmt, legen wir die Bytes durch das sp+x hintereinander beginnend in Speicherzelle 832 ab.

NEXT: s. FOR
Hier endet unsere FOR-Schleife, das Programm springt zurück zur Zeile 30, erhöht x um eins und wenn x kleiner/gleich 62 ist, wird die Schleife erneut ausgeführt, anderenfalls geht es in Zeile 70 weiter. Die Angabe der Schleifen-Variablen (hier das x) hinter dem NEXT ist optional. Gerade bei längeren und/oder verschachtelten Schleifen erhöht es aber die Lesbarkeit, ich rate daher dazu dies mit anzugeben.

Im Register 21 steht, welche der acht Sprites aktiv sind. Dazu muss das Bit für den entsprechen Sprite gesetzt werden. Wir verwenden Sprite 0 (vgl. sn%) und müssen daher also das erste Bit auf eins setzen. Ihr müsst also das Bit für Sprite-Nr. + 1 setzen. Dies berechnen wir hier durch 2 (Basis des Binärsystems) hoch Sprite-Nr. sn% also 2^0 = 1. Wir ignorieren, dass es evtl. weitere Sprites gibt und schreiben dies direkt ins Register.

Wir müssen dem VIC noch mitteilen, in welchem Block unsere Spritedaten liegen. Das haben wir uns ja bereits in sb% gemerkt. Diese Information wird für jedes Sprite am Ende des Bildschirmspeichers hinterlegt. Dieser beginnt normalerweise (man kann ihn auch verschieben) ab Speicherzelle 1024 und er ist 1kb, also 1024 Byte groß. Addieren wir diese Beiden Infos kommen wir auf 2048; davon die max. Spriteanzahl abziehen und wir landen bei 2040. Ab 2040 müssen wir also den Speicherbereich sb% für das jeweilige Sprite speichern, damit der VIC unsere Spritedaten auch findet.

Wir rechnen uns die Mitte des BS aus. Der C64 hat 320 Punkte in horizontaler X-Richtung und 200 Punkte in vertikaler Y-Richtung. Oben/links in der Ecke befindet sich der Punkt X=0 : Y=0 unten/rechts X=319 : Y=199. Jetzt könntet ihr den Fehler machen und nur diese beiden Werte in die Berechnung der Mitte heranziehen, aber wir müssen den Rahmen noch beachten, Sprites verschwinden dahinter. Links ist der Rahmen 24 Punkte Breit, oben 50. Um das Sprite genau in der Mitte zu platzieren, müssen wir natürlich auch die Breite und Höhe unseres Sprites beachten, da die Positionsangabe sich immer auf die obere/linke Ecke des Sprites bezieht. Also berechnen wir z. B. die X-Position mit (Bildschirmbreite – Spritebreite) geteilt durch 2 + Rahmenbreite_links, die Y-Position erklärt sich nun wohl selbst.

Ab Register 0 finden wir für jedes der acht Sprites die X- & Y-Position. Wir schreiben also unsere errechnete Position in die ensprechenden Register.

Jetzt müssen wir noch die Spritedaten aus der Excel-Tabelle ins Programm einfügen, dann speichern, auf DoPrg.cmd ziehen und die *.prg-Datei auf den Emulator ziehen.
Uund siehe da….

Das erste Sprite.
Das erste Sprite.

Das Sprite bleibt uns, obwohl das Programm beendet wurde, erhalten. Das liegt, daran, dass wir den VIC aufgefordert haben Sprite-0 zu zeichnen, aber am Programmende müssen wir dies auch wieder Rückgängig machen. Also ergänzen wir das Programm, damit es auf eine Eingabe wartet und das Sprite dann deaktiviert.

GET: Ein Zeichen aus dem Tastaturpuffer holen und in der angegebenen Variable (hier s$) speichern.
Sollte es kein Zeichen geben, läuft das Programm direkt weiter, es wartet also nicht. Wir möchten aber warten, daher Prüfen wir dies in der nächsten Zeile. Außerdem habe ich dies in Zeile 5000 geschrieben, um etwas Platz für spätere Ergänzungen zu haben. Wie erwähnt, ist eine Blockbildung durchaus ratsam.

IF THEN: Eine sog. Bedingung; WENN <diesese Bedingung zutrifft> DANN <tue das…>
Wir prüfen ob in s$ etwas steht, sprich eine Taste gedrückt wurde. Ist dies nicht der Fall, dann ist s$ leer. Einen leeren String kann man durch zwei direkt aufeinanderfolgende Anführungszeichen „„ darstellen, es darf KEIN Leerzeichen zwischen den „„ stehen! Ist s$ leer, dann wird die Anweisung hinter dem THEN ausgeführt. Wir springen hier mit einem GOTO einfach wieder zum GET in Zeile 5000 und schauen erneut nach, ob eine Taste gedrückt wurde. Wenn s$ ein Zeichen enthält, fährt das Programm einfach mit der nächsten Zeile (hier 5020) fort, OHNE die hinter THEN stehende Anweisung auszuführen.

Das kennen wir schon, jetzt schalten wir unseren Sprite durch eine 0 wieder ab. Genau genommen schalten wir alle Sprites aus, da wir direkt eine 0 wegschreiben, aber wie oben kümmern wir uns nur um unser Sprite und schalten gnadenlos alles ab. 😉

Wenn ihr das Programm nun bereitstellt, so wartet es bis eine Taste gedrückt wurde und schaltet das Sprite anschließend wieder ab.

Sollte euch die Wartezeit auf einen Tastendruck zu langweilig sein, dann nehmen wir uns am ersten Programm ein Beispiel und fügen etwas Farbe hinzu.

Ab Register 39 finden wir die Farbe für jedes Sprite. Schreiben wir zufällig (das sollte vom ersten Programm bekannt sein) etwas hinein und starten das Programm, so blinkt unser Sprite fröhlich vor sich hin.

Bevor etwas Bewegung ins Spiel kommt, noch mal unser Listing bis zu diesem Punkt:

Um das Sprite zu bewegen müssen wir unser Programm ergänzen und ändern.

Wir definieren eine neue Variable lr% für links/rechts. Um unser Sprite nach links wandern zu lassen, müssen wir von der X-Position die ja in xp gespeichert ist, etwas abziehen, nach rechts muss etwas addiert werden. Wir belegen lr% zunächst mit -1 und lassen das Sprite nach links wandern.

Auch wenn der Mathematiker hier die Augen verdreht. So bewegen wir das Sprite. Diese Anweisung besagt nichts Anderes, als speichere in xp das Ergebnis von xp + lr% ab. Zu xp wird also der Inhalt von lr% addiert und dann direkt wieder in xp abgelegt. Da lr% zur Zeit negativ ist (wir haben ja -1 hineingeschrieben), verringer sich xp hier natürlich (erinnert euch an den Mathematik-Unterricht).

Da wir das Sprite ja am linken Rand apprallen lassen wollen, sollten wir dies auch kontrollieren. Wir prüfen also mit einer IF-Abfrage ob das Sprite den linken Rand erreicht hat IF xp<=24 (WENN xp kleiner/gleich 24 ist, der Rahmen hat bekanntlich diese Breite s. o.) THEN xp=24 (DANN setze xp auf 24). Fügt man hinter die THEN-Anweisung mit dem Doppelpunkt : weitere Befehle ein, so werden diese auch nur ausgeführt, wenn die THEN-Anweisung ausgeführt wird. Also : lr%=-lr% (bedeutet soviel wie: UND kehre das Vorzeichen von lr% um). Um das Vorzeichen umzukehren, damit das Sprite in die andere Richtung läuft, hätten wir auch lr%=lr%*-1 schreiben können, also wie man das aus dem Mathe-Unterricht kennt, mal minus 1. Unsere Schreibweise ist quasi die Kurzform davon.

Wie in Zeile 100 schreiben wir hier ins zugehörige Register die aktuelle X-Position xp.

Jetzt müssen wir noch die Zeile 5010 ändern.

Das GOTO springt nun nach Zeile 200 statt 5000 wie bisher!

Wenn ihr das Programm bereitstellt und startet läuft zunächst alles wie gewünscht. Aber sobald das Sprite sich dem rechten Rand nähert kommt es zu einem Fehler und das Programm wird beendet. Das liegt daran, dass die X-Position mittlerweile größer als 255 ist. Das Register für die X-Position kann aber nur ein Byte aufnehmen. Hier kommt ein weiteres Register zum Zuge. Im Register 16 steht jedem Sprite ein weiteres BIT zur Speicherung der X-Position zur Verfügung. Somit kommt man auf 9 BIT, das ermöglicht Zahlen von 0 bis 511.

Ergänzen wir das Programm um den rechten Rand zu erreichen und davon abzuprallen.

Wie bei der Prüfung auf den linken Rand in Zeile 210, prüfen wir hier, ob xp größer oder gleich 320 plus dem rechten Rand (24) minus der breite unseres Sprites (auch 24) ist. Hier können wir natürlich +24-24 weglassen, das hebt sich ja auf. Wenn das der Fall ist setzten wir xp auf den maximal Wert und kehren wieder das Vorzeichen von lr% um.

Da wir bei einer X-Position xp von über 255 die Register vic+sn% und 16 füllen müssen nehmen wir uns zwei Variablen xrp (XRegisterPosition) das wir zunächst mit dem Wert aus xp füllen und hb% für (höchstes bit) das erstmal auf null gesetzt wird.

Wir müssen jetzt prüfen, ob die X-Position (jetzt in temporär in xrp) größer als 255 ist. Ist dies der Fall, dann setzen wir in hb% das Bit für unseren Sprite (wieder mal 2^sn%) und speichern in unserer temporären X-Position xrp den unter 256 liegenden Teil ab.

Als nächstes müssen wir die Zeile 250 ändern:

Die X-Position wird jetzt mit xrp statt xp ins zuständige Register geschrieben.

Das höchste BIT muss wie erwähnt ins Register 16.

Die Zeilen 230 bis 260 haben es in sich, wenn man Neueinsteiger ist. Also versuche ich mal zu erklären was genau geschieht.

Wenn wir das Programm jetzt starten läuft unser Programm wie zu Beginn geplant.

Dies streift natürlich nur die Möglichkeiten des C64, es gibt noch viel mehr zu entdecken.

Ich hoffe der interessierte Einsteiger hat etwas Blut geleckt. BASIC bietet noch sehr viel mehr, aber seine Einfachheit wird durch eine sehr langsame Ausführungsgeschwindigkeit erkauft. Wer mehr mit BASIC machen möchte, dem Empfehle ich das Handbuch zum C64. Ab Kapitel 3 / Seite 32 gibt es eine Einführung in BASIC. Da ich nicht glaube, dass das Interresse an BASIC sehr groß ist, verzichte ich auf weitere Beispiele und gehe zum Assembler über. Sollte wiedererwarten Bedarf an weiterführenden Beispielen bestehen, einfach mal Posten und ich schaue, was ich machen kann.

Zum Schluß unser komplettes Sprite-Programm. Eine letzte Neuerung habe ich da auch noch.

REM:Remark (Bemerkung) hiermit lassen sich Kommentare ins Programm aufnehmen. Dann fällt es euch nach zwei Jahren leichter zu verstehen, was das eigentlich sollte.
Ich habe hier nochmal alle Register, die wir verwendet haben und zwei noch nicht verwendete aufgelistet. Ihr könnt ja mal schauen, was man mit denen anstellen kann. Außerdem empfiehlt es sich das Programm weiter zu verändern, z. B. auch in Y-Richtung eine Bewegung auszuführen.

 

Da ihr bis hier durchgehalten habt, muss ich jetzt noch eine Kleinigkeit gestehen: Wir hätten uns das Leben mit dem CBM Program Studio sehr viel leichter machen können. Falls ihr euch dieses Programm noch nicht angesehen habt, schaut noch mal bei den „Benötigten Tools“ vorbei. Das CBM prg Studio bietet neben der BASIC-Unterstützung z. B. auch einen Sprite-Editor.


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

Loading...


<<< zurück | weiter >>>

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

7 Gedanken zu „Sprites“

  1. > Wie unter Zahlensysteme nachzulesen, können wird dies ganz einfach ins
    > Dezimalsystem umrechnen 2^9 = 256 unsere X-Position
    2^9 = 512

    Vielen Dank für das tutorial! Genial! Da kommen Erinnerungen hoch.
    Mir geht’s ähnlich, SW-dev und mit 64er begonnen. Immer schon 64er assembler lernen wollen, jetzt ist es endlich soweit.

  2. „Jetzt könntet ihr den Fehler machen und nur diese beiden Werte in die Berechnung der Mitte heranziehen, aber wir müssen den Rahmen noch beachten, Sprites verschwinden dahinter.“

    Das stimmt nicht. Sprites bewegen sich in einem Raum, der nicht dem Raum innerhalb des Rahmens entspricht.

    1. Nichts Anderes sollte mein Text eigentlich ausdrücken. Da die Sprites hinter dem Rahmen verschwinden, ist es doch eigentlich klar, dass deren Koordinatensystem, nicht dem innerhalb des Rahmens entspricht.

      Detaillierter wird darauf unter Sprites (ASM) eingegangen. Da gibt es dann auch eine schöne Grafik:

  3. Servus!

    Vielen dank für die mühe und Arbeit die du in diese Projekt investiert hast. Sehr guter Arbeit!. Weiter so…

    Eine kleine Ergänzung:

    245 if xrp>=255 then hb%=0 : xrp=xp

    hb% sollte wieder auf 0 zurückgestellt werden damit der Sprite sich wieder im xpr<255 Bereich bewegen kann.

    Vg,
    Daniel

    1. Hallo Daniel,
      danke für dein Lob.

      Deinen Hinweis kann ich aber nicht ganz einordnen.

      In Zeile 230 wird das doch bereits gemacht:
      230 xrp=xp : hb%=0

      und nur falls die Position größer 255 ist, werden die Werte wieder korrigiert
      240 if xrp>255 then hb%=2^sn% : xrp=xp-256

      Ich habe das Programm trotzdem zur Kontrolle nochmal kopiert und ausgeführt:
      Das Sprite bewegt sich wie geplant. Es wandert immer über den gesamten Bildschirm hin und her.


      Allerdings ist mir beim Testen aufgefallen, dass die Zeilen-Nr. 8 doppelt vorkam und dass die Zeile 220 direkt hinter 210 gewandert war (es fehlte der Zeilenumbruch). Diese beiden Punkte habe ich eben korrigiert.

Schreibe einen Kommentar

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

Protected by WP Anti Spam