Erstellt: 23. Dezember 2012 (zuletzt geändert: 27. November 2018)

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). Das Beispiel ist schon etwas anspruchsvoller, falls ihr nicht alles versteht, ist es halb so wild. Hier geht es eigentlich nur um einige Grundbegriffe aus der Programmierung (z. B. was ist eine Variable oder Schleife). Ich wollte es nur etwas interessanter gestalten.

Was sind Sprites überhaupt?

Allgemein unterscheidet man zwischen Soft- und Hardware-Sprites. Wir reden hier von den Hardware Sprites. Diese heißen so, da sie direkt von den Chips (der Hardware) des Computers zur Verfügung gestellt werden. Kurz gesagt, sind Sprites nichts weiter als freibewegliche Objekte. Ihr könnt sie also problemlos über den Bildschirm bewegen bzw. an jeder beliebigen Stelle anzeigen. Sprites lassen sich nur im Hauptbereich des C64 Bildschirms anzeigen, sie verschwinden hinter dem Rahmen. Mit ausgefeilten Assembler-Tricks kann man z. B. den Rahmen ausblenden, damit sind dann auch dort Sprites sichtbar.

Beim C64 gibt es zwei verschiedene „Arten“ von Hardware Sprites:
HiRes-Sprites: Diese sind einfarbig. Je Sprite steht nur eine Farbe, für alle sichtbaren Punkte, zur Verfügung. Nicht gesetzte Punkte sind transparent, an den Stellen ist dann der Hintergrund sichtbar.
Ein HiRes-Sprite besteht in der X-Richtung (Breite / horizontal) aus 24 Punkten und aus 21 Zeilen in der Y-Richtung (Höhe / vertikal).

Multi-Color-Sprites: Dies sind mehrfarbige Sprites. Neben der Transparenz für nicht gesetzte Punkte, hat man die Möglichkeit drei verschiedene Farben zu verwenden. Allerdings sind zwei dieser Farben für alle Multi-Color-Sprites, die ihr einsetzt, identisch! Nur eine Farbe lässt sich individuell je Multi-Color-Sprite festlegen.
Die Farben werden allerdings durch eine Halbierung der Auflösungen in X-Richtung (Breite / horizontal) erkauft! Ein Multi-Color-Sprite hat nur 12 Punkte in der X-Richtung (Breite / horizontal). Es bleibt aber in der Y-Richtung (Höhe / vertikal) unverändert bei 21 Zeilen.
Multi-Color-Sprites schauen wir uns allerdings erst unter Assembler genauer an.

Der C64 kann standardmäßig 8 Hardware Sprites (von 0 bis 7 durchnummeriert) gleichzeitig darstellen. Ihr ahnt es evtl. schon, unter Assembler sind durch entsprechende Tricks mehr Sprites möglich (Rasterzeilen-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 Grafik-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 Hardware-Kollisionserkennung, zwischen Sprites untereinander und von Sprites mit dem Hintergrund, 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.

Ein Sprite entwerfen

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 Abschnitt den Wert des Bytes ermitteln. Dazu gehen wir einfach von oben nach unten Zeilenweise (1-21) vor und interpretieren den jeweiligen Block-Abschnitt (1-3) als Binärzahl (s. Binäreszahlensystem). Ein X bedeutet, dass das Bit auf 1 gesetzt wird. Die Binärzahl rechnen wir dann in das Dezimalsystem um.

Diese Zahlen notieren wir neben jeder Zeile…

Dezimalwerte
Dezimalwerte

Die ermittelten Zahlen können wir später direkt in unser BASIC-Programm übernehmen.

Alternativ können wir uns auch einen Sprite-Editor schreiben, das würde hier aber den Rahmen sprengen. Man könnte natürlich auch einen fertigen Sprite-Editor (z. B. aus dem C64 Studio oder das SpritePad) einsetzen.

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 das kostenlose LibreOffice nehmen.

Wenn ihr euer 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 eurem BASIC-Programm erscheinen sollen. Darunter findet ihr die fertigen Programmzeilen, die ihr später einfach abtippen könnt.

Das Programm

Fangen wir damit an, unser Sprite anzuzeigen:

10 PRINT CHR$(147);
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 auf dem Bildschirm (künftig häufig 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 (man kann diesen Zeichensatz auch verändern).
Gibt man nun z. B. den Code 147 auf dem BS aus, so wird der komplette BS gelöscht.

20 VIC=53248 : SB%=13 : SP=SB%*64 : SN%=0
Hier werden sog. Variablen definiert. Variablen kann man sich als Schubladen vorstellen. Man legt etwas in eine Schublade, benennt diese und wenn man später wieder etwas braucht, kann man einfach sagen „Gib mir den Inhalt aus der Schublade mit dem Namen xyz.“

Beim Umgang mit Variablen, beim C64 BASIC, gibt es noch einiges zu beachten.
Das 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. Schauen wir uns hier mal Variablen mit dem Namen WERT an.

WERT : 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, dass die Nachkommastellen mit einem Punkt . als Trennzeichen eingegeben werden. Ihr dürft also nicht das bei uns gebräuchliche Komma verwenden!

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

WERT$ : Steht ein 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 Welt!“, “123“ oder ähnliches möglich. Wichtig ist, dass z. B. “123“ ein Text ist und sich nicht direkt damit rechnen läßt!

Beachtet, dass Variablen nur Werte, passend zu ihrem Datentyp aufnehmen können. Gebt ihr z. B. A = “HALLO“ ein, kommt es zu einem Laufzeitfehler während der Ausführung, da A in diesem Fall nur Fließkommazahlen aufnehmen kann. Gebt ihr aber A% = 10.5 ein, kann das eine schwer zu findende Fehlerquelle sein. Hier gibt es nämlich keinen Laufzeitfehler, aber da A% nur Ganzzahlen, also ohne Nachkommastellen speichern kann, gehen diese verloren. A% enthält also einfach 10.

Variablennamen können theoretisch beliebig lang sein, allerdings werden nur die ersten beiden Zeichen zur Unterscheidung 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$ wieder unterschiedlich sind, da sie auch verschiedene Typen haben. Außerdem dürfen im Variablennamen keine Schlüsselworte (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 lieber gelb nehmen. Ihr müsstet also jetzt 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 an einer Stelle die 2 gegen eine 7 tauschen.

Erstmal genug zu den Variablen, wo waren wir…?

20 VIC=53248 : SB%=13 : SP=SB%*64 : SN%=0
Hier werden also vier Variablen festgelegt…

VIC=53248 : Hier wird VIC als Fließkommazahl verwendet, da 53248 den Wertebereich einer Ganzzahl sprengt. 53248 bzw. $D000 ist die Basisadresse des VIC. Wir werden gleich auf einzelne VIC-Register (Speicherzellen) zugreifen, indem wir zu dieser Basisadresse die Nr. des entsprechenden Registers addieren.
SB%=13 : Damit 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 Daten ablegen 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 hier gefahrlos genommen werden.
SP=SB%*64 : Hiermit 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.

30 FOR X=0 TO 62
FORNEXT : Dies ist der Beginn, einer sog. FOR-Schleife. Die Anweisungen zwischen FOR und dem gleich in Zeile 60 folgendem NEXT, werden so oft ausgeführt, wie es die Werte links und rechts vom TO angeben (auf ein STEP verzichte ich hier).
Wir möchten die nächsten Anweisungen (bis zum NEXT) für jedes der 63 Bytes unseres Sprites jeweils einmal ausführen. Die Variable X nimmt hier nacheinander alle Werte von 0 bis 62 an.

40 READ B%
READ : Daten einlesen, hier aus unseren DATA-Zeilen ab Programmzeile 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 Programmzeile 10000 gelesen und in der Variablen B% gespeichert.

50 POKE SP+X,B%
POKE haben wir ja bereits kennengelernt. 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.

60 NEXT X
NEXT : siehe FOR
Hier endet unsere FOR-Schleife. Das Programm erhöht X um eins und solange X kleiner bzw. gleich 62 ist, springt es zurück zur Zeile 30. Anderenfalls wird die Schleife verlassen und es geht 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, die Schleifenvariable mit anzugeben.

70 POKE VIC+21,2↑SN%
Im VIC-Register 21 steht, welche der acht Sprites aktiv sind. Dazu muss das Bit für den entsprechen Sprite auf 1 gesetzt werden. Wir verwenden Sprite 0 (vgl. SN%) und müssen daher also Bit-0 auf eins 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.

80 POKE 2040+SN%,SB%
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. Der Bildschirmspeicher beginnt normalerweise (man kann ihn auch verschieben) ab Speicherzelle 1024 bzw. $0400 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 bzw. $07FB. Ab 2040 müssen wir also den Speicherbereich SB% für das jeweilige Sprite ablegen, damit der VIC unsere Spritedaten auch findet.

90 XP=(320-24)/2+24 : YP=(200-21)/2+50
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 für 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.

100 POKE VIC+SN%, XP
110 POKE VIC+1+SN%, YP

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 solltet ihr am besten erstmal speichern! Immer wenn ihr mit POKEs arbeitet, solltet ihr vor dem Start das Programm sichern!! POKE ändert direkt den Inhalt von Speicherzellen und wenn ihr die falsche erwischt, kann das unangenehme Folgen haben.

Probiert bei Gelegenheit z. B. mal folgende Zeile aus:
10 PRINT “HALLO“ : POKE 2048,1
Startet das Programm mit RUN und alles sieht gut aus. Versucht dann direkt es erneut zu starten und ihr bekommt eine Fehlermeldung.

Nach dem Speichern können wir jetzt unser Programm starten und 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.

5000 GET S$
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 den Befehl in Zeile 5000 geschrieben, um etwas Platz für spätere Ergänzungen zu haben. Wie erwähnt, ist eine Blockbildung durchaus ratsam.

5010 IF S$=““ THEN GOTO 5000
IFTHEN : Dies ist eine sog. Wenn-Dann-Bedingung WENN <die Bedingung zutrifft> DANN <tue dies…>
Wir prüfen hier, ob in S$ etwas steht, also ob 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 nun 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. Sobald 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.

5020 POKE VIC+21, 0
Diese Zeile kennen wir vom Prinzip schon, jetzt schalten wir unseren Sprite durch eine 0 einfach 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 ausprobiert, dann 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.

5005 POKE VIC+39+SN%, RND(1)*16
Ab dem VIC-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:

10 PRINT CHR$(147);
20 VIC=53248 : SB%=13 : SP=SB%*64 : SN%=0
30 FOR X=0 TO 62
40 READ B%
50 POKE SP+X,B%
60 NEXT X
70 POKE VIC+21,2↑SN%
80 POKE 2040+SN%,SB%
90 XP=(320-24)/2+24 : YP=(200-21)/2+50
100 POKE VIC+SN%, XP
110 POKE VIC+1+SN%, YP
5000 GET S$
5005 POKE VIC+39+SN%,RND(1)*16
5010 IF S$="" THEN GOTO 5000
5020  POKE VIC+21,0
10000 DATA 0,126,0
10001 DATA 3,255,192
10002 DATA 7,255,224
10003 DATA 31,255,248
10004 DATA 28,255,56
10005 DATA 62,255,124
10006 DATA 127,247,254
10007 DATA 127,247,254
10008 DATA 255,251,255
10009 DATA 255,253,255
10010 DATA 255,253,255
10011 DATA 255,243,255
10012 DATA 255,255,255
10013 DATA 127,255,254
10014 DATA 127,255,254
10015 DATA 63,126,252
10016 DATA 31,189,248
10017 DATA 31,195,248
10018 DATA 7,255,224
10019 DATA 3,255,192
10020 DATA 0,126,0

Um das Sprite zu bewegen, müssen wir unser Programm ergänzen und teilweise ändern.
120 LR%=-1
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 dadurch das Sprite nach links wandern.

200 XP=XP+LR%
Auch wenn der Mathematiker hier die Augen verdreht, so berechnen wir die neue Spriteposition, um es zu bewegen. 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 Mathematikunterricht).

210 IF XP<=24 THEN XP=24 : LR%=-LR%
Da wir das Sprite ja am linken Rand abprallen lassen wollen, müssen wir kontrollieren, ob das Sprite die entsprechende Position erreicht hat. Wir prüfen 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 XP auf 24 setzen). 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 bedeutet : LR%=-LR% soviel wie: UND kehre zusätzlich 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 Matheunterricht kennt, mal minus 1. Unsere Schreibweise ist quasi die Kurzform davon.

250 POKE VIC+SN%, XP
Wie in Zeile 100 schreiben wir hier ins zugehörige Register die aktuelle X-Position, die ja in XP gespeichert ist.

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

5010 IF S$=““ THEN GOTO 200
Das GOTO springt nun nach Zeile 200 statt 5000 wie bisher!

Wenn ihr das Programm nun 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.

Hier kommt es zum Fehler.

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. Um jetzt Positionen über 255 zu erreichen, 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.

220 IF XP>=320+24-24 THEN XP=320+24-24 : LR%=-LR%
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 der Rand erreicht wurde, setzten wir XP auf den maximal Wert und kehren wieder das Vorzeichen von LR% um.

230 XRP=XP : HB%=0
Da wir bei einer X-Position XP von über 255, die beiden Register VIC+SN% und VIC+16 füllen müssen, nehmen wir uns zwei weitere Variablen (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.

240 IF XRP>255 THEN HB%=2↑SN% : XRP=XP-256
Wir müssen nun prüfen, ob die X-Position (jetzt temporär in XRP) größer als 255 ist. Sobald dies der Fall ist, 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:
250 POKE VIC+SN%, XRP
Die X-Position wird jetzt mit XRP statt XP ins zuständige Register geschrieben.

260 POKE VIC+16,HB%
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.

Startbedingung XP=255

230 XRP wird auf 255 gesetzt : HB% auf Null
240 XRP ist nicht größer als 255 also nichts machen
250 nach VIC+0 unsere 255 aus XRP schreiben
260 HB% ist Null, also setzen wir das Register 16 auf Null

HB%  Register X-Pos.
0 | 1 1 1 1 1 1 1 1

Beim nächsten Durchlauf erfolgt XP=XP+1, also ist dann XP=256

230 XRP wird auf 256 gesetzt : HB% auf Null
240 XRP ist größer als 255, also HB% auf 2↑SN% (hier 1) setzten und
    XRP um 256 verringern, wir benötigen hier nur den unter 256 liegenden
    Rest. Da unsere X-Position aktuell 256 ist, bleibt Null
    übrig.
250 für die X-Position schreiben wir eine Null (Rest in XRP) weg
260 da HB% jetzt 1 ist, wird das höchste BIT gesetzt

HB% Register X-Pos.
1 | 0 0 0 0 0 0 0 0
Wie unter Zahlensysteme nachzulesen, können wird dies ganz einfach ins
Dezimalsystem umrechnen.

Beim nächsten Durchlauf erhalten wir dann 257 und das sähe so aus:
HB% Register X-Pos.
1 | 0 0 0 0 0 0 0 1

So verarbeitet der VIC also die X-Positon der Sprites. Er bildet aus
zwei Registern eine neunstellige Binärzahl.

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

Dies war jetzt evtl. ein Wenig viel, für den Anfang. Es streift natürlich auch nur die Möglichkeiten des C64, es gibt noch sehr 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.
0 REM VIC-BASISADRESSE = 53248
REM : Remark (Bemerkung) – Hiermit lassen sich Kommentare ins Programm aufnehmen. Damit 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.

0 REM VIC-BASISADRESSE = 53248
1 REM REGISTER FUNKTION
2 REM  0      X-POS VON SPRITE 0
3 REM  1      Y-POS VON SPRITE 0
4 REM  2 - 15 X/Y-POS VON SPRITE 1-7
5 REM 16      HOECHSTES BIT DER X-POS
6 REM 21      SPRITE N SICHTBAR
7 REM 23      SPRITE N IN Y-RICHTUNG GROESSER
8 REM 29      SPRITE N IN X-RICHTUNG GROESSER
9 REM 39 - 46 FARBE SPRITE N
10 PRINT CHR$(147);
20 VIC=53248 : SB%=13 : SP=SB%*64 : SN%=0
30 FOR X=0 TO 62
40 READ B%
50 POKE SP+X,B%
60 NEXT X
70 POKE VIC+21,2↑SN%
80 POKE 2040+SN%,SB%
90 XP=(320-24)/2+24 : YP=(200-21)/2+50
100 POKE VIC+SN%, XP
110 POKE VIC+1+SN%, YP
120 LR%=-1
200 XP=XP+LR%
210 IF XP<=24 THEN XP=24 : LR%=-LR% 
220 IF XP>=320+24-24 THEN XP=320+24-24 : LR%=-LR%
230 XRP=XP : HB%=0
240 IF XRP>255 THEN HB%=2↑SN% : XRP=XP-256
250 POKE VIC+SN%, XRP
260 POKE VIC+16,HB%
5000 GET S$
5005 POKE VIC+39+SN%,RND(1)*16
5010 IF S$="" THEN GOTO 200
5020 POKE VIC+21,0
10000 DATA 0,126,0
10001 DATA 3,255,192
10002 DATA 7,255,224
10003 DATA 31,255,248
10004 DATA 28,255,56
10005 DATA 62,255,124
10006 DATA 127,247,254
10007 DATA 127,247,254
10008 DATA 255,251,255
10009 DATA 255,253,255
10010 DATA 255,253,255
10011 DATA 255,243,255
10012 DATA 255,255,255
10013 DATA 127,255,254
10014 DATA 127,255,254
10015 DATA 63,126,252
10016 DATA 31,189,248
10017 DATA 31,195,248
10018 DATA 7,255,224
10019 DATA 3,255,192
10020 DATA 0,126,0

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

Loading...


ZurückWeiter

21 Gedanken zu „Sprites“

  1. Hallo!
    ich suche die Speicheradressen der 8 möglichen Sprites in Basic.
    Register 2040 bis 2047 ist mir bekannt.
    Wo soll ich die DATA-Zeilen einlesen lassen?
    z.B: 704 bis 767
    768 bis 831
    aber wo beginnen die anderen Speicherplätze ?
    Ich würde gerne ein Basic -Spiel mit 8 Sprites schreiben.
    Kannst du mir weiterhelfen?

    1. Hallo Wolfgang,

      Block 11 (ab 704) ist ja noch ok, aber ab 768 sind doch die BASIC-Vectoren zu finden. Dort kannst du eigentlich nichts ablegen.
      Es lassen sich noch Block 13 (wie im Beispiel ab 832) und 14 nutzen, aber dann wirst du wohl aufs BASIC-RAM ausweichen müssen.

      Alternativ kannst du natürlich auch vom BASIC aus die VIC-Bank umschalten.
      Ob das Sinnvoll ist, hängt von deinem Projekt ab, da du dann vor weiteren Problemen stehst.

      Gruß,
      Jörn

        1. Um mehr Speicher für deine Sprites unterhalb von BASIC zu erhalten, kannst du das BASIC im Speicher hochschieben. Das ist eigentlich ganz einfach:

          1. Speichere dein BASIC-Programm mit all den DATA-Zeilen erstmal ab.

          2. Starte den C64 neu oder schreib NEW. Jetzt schreibst du ein kleines Programm (nicht ausführen, aber mit RETURN bestätigen!):

          2020 POKE44,16:RUN

          3. Jetzt schreibst du ohne Programm

          POKE44,16:POKE16^3,0:NEW

          Damit wird das BASIC nach oben verschoben, und zwar (von $0801) nach $1000 = 16^3. Der BASIC-Stub in Zeile 2020 ist jetzt scheinbar weg. Allerdings liegt der noch am alten BASIC-Start bei $0801. Den kannst du gerade nur nicht mehr sehen.

          4. Lade dein Programm ganz normal mit LOAD”…”,8. Dieses wird jetzt ab dem neuen BASIC-Start ab $1001 abgelegt.

          Nun ist der Speicherplatz ab $0810 = 8*256 + 16 frei (zwischen $0801 und $080F liegt der 2020-BASIC Stub), und du kannst ihn bis $1000 für deine Sprites verwenden. Um das Programm wieder abzuspeichern, schreibe

          POKE44,8:SAVE”…”,8

          Damit setzt du wieder den alten BASIC-Start und speicherst alles ab $0801 ab, also auch den 2020-BASIC Stub. Wenn du nun den C64 resettest und das neu abgespeicherte Programm ganz normal lädst, steht in der LIST nur der BASIC-Stub. Mit RUN wird dieser ausgeführt und setzt zunächst das BASIC wieder hoch und startet das Programm dann ab $1001.

          Das Gute dabei ist auch, dass du die ganzen blöden DATA-Zeilen wegschmeißen kannst, denn beim Abspeichern wird alles ab $0801 abgespeichert, also der BASIC-Stub mitsamt all deinen Sprite-Bytes. Damit sparst du dir das Einlesen der DATA-Zeilen mit READ und damit eine Menge Zeit. Super, oder?

  2. Moin moin,

    vielen Dank fuer diese wertvolle Seite und das Teilen der Informationen! 🙂

    Als 79er bin auch ich, unter anderem, mit einem C64 aufgewachsen.
    Leider fehlte es im kindlichen Kopf an Verstaendnis und ich hatte gehofft dass es jetzt besser ist.

    Ich habe das Programm abgeschrieben auf meinem C64 ohne Datasette und ohne 1541, ich kann also nicht speichern.

    Wenn ich es nun mit RUN starten moechte erhalte ich lediglich den Fehler
    “?OUT OF DATA ERROR IN 40”

    Ich habe den Syntax mehrfach verglichen, sowohl am Bildschirm als auch in ausgedruckter Form (die neumodische Schleuder steht in einem anderen Raum als der Brotkasten).
    Leider finde ich keinen Fehler in der Abschrift, selbst bei akribischster Vergleichsweise.

    Mangelnde Problemkommentare deuten allerdings auf einen solchen Fehler hin…
    Ich bin ratlos, gibt es einen Weg mir zu helfen oder mich in die richtige Richtung zu schubsen?

    Ich bin schon so weit, dass ich an folgende “Fehlerquellen” “glaube”:
    – die fehlende Speichermoeglichkeit bereitet Schwierigkeiten,
    -die Variable B% ist in meiner Abschrift nirgends definiert (woher weiss Basic dass es den Wert der Daten ab Zeile 10000 findet?)

    Aber auch hier wieder, ich glaube nicht dass es an den beiden Moeglichkeiten liegen kann, eben da es keine anderen Kommentatoren mit Problemen dieser Richtung gibt…

    Vielen Dank fuer Ihre Zeit und eine eventuelle Antwort.

    Mit freundlich verzweifeltem Gruss
    Henry

    1. Hallo Henry,
      lustigerweise habe ich das Programm heute Nachmittag selbst nochmal eingegeben, da es lief, gehe ich von einem Tippfehler aus.

      Der Fehler „?OUT OF DATA ERROR IN 40“ bedeutet, dass das READ B% in Zeile 40 häufiger aufgerufen wird, als DATA-Einträge vorhanden sind.

      Dies kann jetzt eigentlich nur zwei Gründe haben:

    2. Die Schleife in Zeile 30 stimmt nicht, dort muss 30 FOR X=0 TO 62 stehen.
    3. Es fehlen Werte bei den DATA-Zeilen. Ab der Zeile 10000 muss es 21 DATA-Zeilen mit je drei Werten geben. Also insg. 63 Werte.
    4. Falls die Schleife bei dir passt, kannst du zum Test einfach mal die Zeile 11111 DATA 0,0,0,0,0,0,0,0,0,0,0,0,0 am Ende einfügen und es nochmal versuchen. Überschüssige DATA-Werte sind kein Problem, es dürfen nur nicht zu wenige sein. Das Programm sollte dann laufen, das Sprite wird aber evtl. nicht korrekt dargestellt. Du müsstest dann die DATA-Zeilen nochmal genau kontrollieren und die fehlerhafte Zeile korrigieren.

      Unter BASIC müssen Variablen nicht gesondert definiert werden. Das READ B% definiert die Ganzahl-Variable B%, da sie bis dahin noch nicht verwendet wurde und der POKE-Befehl in Zeile 50 nutzt den Wert aus B% dann einfach wieder.

      Viel Spass noch,
      Jörn

      1. Hi,
        ich habe das Programm eben zur Sicherheit nochmal in VICE eingegeben und es läuft.

        Falls du möchtest und das Programm auf einem D64 Image gespeichert hast, kannst du mir das ja an die E-Mail-Adresse aus dem Impressum schicken.
        Alternativ ginge evtl. auch Screenshot oder Foto, dann schaue ich mal drüber.

        Gruß,
        Jörn

      2. TYPE MISMATCH deutet auf ein fehlerhaftes Zeichen hin – Beispiel $ statt % .. ich gehe jetzt also davon aus, dass du versehentlich LR$ statt LR% geschrieben hast. 😉

  3. Hallo bin anfänger, habe C64Studio v5.8.exe + WinVice am laufen.
    Der Editor mekert bereits bei den Zeilen:
    70 POKE VIC+21,2^SN%
    240 IF XRP>255 THEN HB%=2^SN% : XRP=XP-256

    er mag den Exponent nicht 🙁
    Hab ich jetzt durch * ersetzt aber es tut sich nichts 🙁
    Was mache ich falsch?

    1. Hallo Daniel,
      ich tippe mal, dass du den Quellcode einfach nur kopiert hast, statt ihn abzutippen. Beim C64 ist es der Pfeil ↑ nach oben, statt dem ^.
      Ich habe den Text jetzt zwar angepasst, sodass dort überall der Pfeil nach oben ↑ steht, du wirst aber dennoch nach dem kopieren die Zeile manuell ändern müssen. Lösche im C64 den Pfeil und drücke dann ^ auf der Tastatur.

  4. Ich kann Dir gar nicht sagen wie Dankbar ich bin, diese Seite gefunden zu haben. Du erklärst das wirklich großartig. Nach all den Jahren bin ich mir sicher jetzt doch mein Spiel schreiben zu können, sowie ich es schon in meiner Kindheit hätte machen wollen. Ich bleibe dran und danke noch einmal. Ich habe das Gefühl wieder Kind zu sein, aber jetzt mit einem besseren Verständnis für den guten alten C64. Und dem Gefühl ich kann es wirklich schaffen!

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

  6. “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:

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

      1. Hallo Jörn.

        Du hast 100% recht, ich hatte einen Typo;

        230 xrp=xp : hp%=0

        statt

        230 xrp=xp hb%=0

        …I hang my head in shame!

        danke!

Schreibe einen Kommentar

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

Protected by WP Anti Spam