Kategorie: Programmieren
Tiles on Screen
Guten Morgen, Klasse!
Wir haben heute viel vor, es könnte sein, dass wir auch fünf Minuten in die Pause reinmachen werden, aber mit so Elite-Schülern wie euch weiß ich, dass ich das schaffen kann!
Heute wird gekachelt.
Heute bringen wir Tiles auf den Bildschirm.
Dazu schreiben wir ganz viel in unsere Blanko.asm.
Holt sie alle doch schonmal raus.
Falls ihr nicht weiterkommt, lasse ich die Lösung hier auf dem Lehrertisch liegen.
Für die Schlaumeier: Ja, wir machen das heute ohne DMA. DMA machen wir nächstes Mal.
So.
Als erstes sucht ihr euch euer Arbeitsmaterial aus. Ich habe euch hier drei Schablonen mit Tilesets vorbereitet. Ihr könnt hier sehen, wie die Schablonen aussehen, und dann kann jeder sich das entsprechende Paket runterladen, mit dem er gern kacheln möchte. Aber jeder nur eins!

- Tileset.png (12.85 KiB) 22568 mal betrachtet
So.
Jetzt entpackt jeder mal seine ZIP-Datei und packt sie in seinen Blanko-Ordner.
Wer möchte, kann sie auch in einen gesonderten Ordner tun.
Jetzt gehen wir zu unserer Blanko.asm-Datei über.
Wir werden als erstes dafür sorgen, dass diese Grafikdateien beim Kompilieren mit in die ROM integriert werden.
Dazu schreiben wir folgendes an das Ende der Datei; ganz ans Ende, noch jenseits des ".ENDS":
Code: Alles auswählen
.BANK 0 SLOT 0
.ORG 0
.SECTION "Bilddaten"
Tileset:
.INCBIN "Tileset1.pic"
Palette:
.INCBIN "Tileset1.clr"
Tilemap:
.INCBIN "Tilemap.bin"
.ENDS
Jetzt möchten wir über das reden, was gerade passiert ist.
Wir erstellen eine neue Sektion auf Bank 1, in Slot 0 (also im einzigen Slot, den wir haben, der exakt so groß ist wie die Bank... wir hatten das schon mal), ab Byte numero 0 in dieser Bank (".ORG 0"). Dann geben wir dieser Sektion einen Namen... wozu auch immer. Aber wir tuns. So.
Jetzt binden wir die erste Datei ein und setzen ein Label davor.
Dateien bindet man mit den Befehlen "INCLUDE" und "INCBIN" ein.
Wie ich bereits erwähnt habe, handelt es sich hierbei um WLA-Befehle und nicht um Assembler-Befehle, denn es ist ein Befehl für das Programm, das die ROM erstellt,
wie es die ROM zu erstellen hat.
Der Unterschied zwischen "INCLUDE" und "INCBIN" ist, dass "INCLUDE" für Textdateien ist, "INCBIN" für Binärdateien. Wenn du eine Datei öffnest und nur viele Sonderzeichen und Leerstellen siehst, verwendest du "INCBIN". Kannst du mit einem Texteditor die Datei öffnen und findest sinnhaften Text, z. B. vorzugsweise Assembler-Code, benutzt du "INCLUDE".
Da die Grafikdateien aus meinen Zip-Archiven Binärdateien sind (wers nicht glaubt, kann sie sich gern im Texteditor anschauen), verwenden wir "INCBIN". Davor setzen wir jeweils ein Label: Die Dateien werden beim Kompilieren direkt hintereinander gesetzt. Direkt hinter dem letzten Byte der ersten Datei kommt der erste Byte der nächsten Datei. Das Label hilft uns dann, die genaue Adresse des Anfangs der entsprechenden Dateien ansprechen zu können.
Noch in Wort zu den Dateiendungen:
Diese sind beliebig. Speziell, wenn du ein Programm nur für dich schreibst. Du kannst deine Dateien auch "EXE" oder "DOC" nennen; solange
der Inhalt richtig ist, sollte es funktionieren, da der Dateiname und seine Endung nur der eindeutigen Idenfizierung dient. Mehr nöcht. Das Bild-Umwandlungsprogramm meines Vertrauens (PCX2SNES) erstellt immer "CLR"-Dateien für Paletten und "PIC"-Dateien für Bitplanes, daher diese Dateiendungen. Die Tilemap nennen wir in diesem Beispiel "MAP"-Datei, ich nenne sie normalerweise immer "INC"-Datei (weil: Datei für INClude, woll?). Es macht keinen Unterschied. Nur erwarte gehobene Augenbrauen (und einiges anderes...), wenn du sie wirklich "EXE" o. ä. nennst.
---
Nun gehen wir zum Hauptteil über. Wir schreiben jetzt nach dem "INITSNES" und vor dem Forever-Loop.
Dabei kann man von drei Teilen sprechen:
- Die Bildschirmeinstellungen
- Der Übertragung der Grafikdaten in den VRAM
- Den Abschluss
Beginnen wir mit den
Bildschirmeinstellungen.
Was passiert im Theater, bevor das Bühnenbild umgebaut wird? (Ihr wisst doch noch, was ein Theater ist, oder?)
Richtig! Der Vorhang fällt. Also machen wir auch das Licht aus.
Wie ich schon einmal erwähnte, sind die Begriffe der define.asm entnommen, die vom großartigen
d4s stammt und ohne den es hier keinen Tutorialschreiber für euch gäbe. Nein, er ist
nicht mein Vater!
Die Begrifflichkeiten ersetzen eigentlich folgenden Zahlenwert bzw. Registeradresse:
Hierzu sei noch einmal erwähnt: Der Most Significant Bit, also "die höchste Stelle des Binärzahlenwerts" des Bytes, den man in Register $2100 schreibt, macht den "Forced Blank", also quasi die Sicherung für den Lichtschalter. Der Lichtschalter selbst sind die vier "unteren" Bits, 0-F entscheiden über die Bildschirmhelligkeit. Da wir den "Forced Blank"-Schalter auf "ein" schalten ($80 = %1000.0000), isset nu zappenduster und unsere digitalen Bühnenschieber vermögen es, das neue Szenenbild aufzubauen.
Nächster Schritt: Wir setzen den BG-Mode auf 1. Ohne die define.asm sähe es so aus (dieser Code funktioniert natürlich auch
mit define.asm):
Dazu sei gesagt: Bei Register $2105 entscheiden die untersten drei Bits über den BG Mode (3 Bits können die Zahlen 0 bis 7 abbilden, folglich schreibt man dort eine 1 für Mode 1, eine 3 für Mode 3 und eine 7 für Mode 7), der vierte Bit (also... Bit 3 bei einer Zählung von 0 bis 7) entscheidet im Mode 1 darüber, ob BG3 vor BG1 und BG2 sein soll oder nicht (brauchen wir hier nicht, weil wir eh erst nur mit BG1 arbeiten wollen).
Die anderen vier Bits entscheiden über die Größe der Tiles auf den 4 potenziell vorhandenen BGs: Ist z. B. Bit 7 gesetzt, hat BG4 16x16 große Tiles, sind die Bits 4, 5 und 6 geleert (also auf Null), haben (entsprechend in derselben Reihenfolge) BG1, BG2 und BG3 8x8 große Tiles.
Wir benutzen hier nur BG1 mit 16 Farben und 8x8 großen Tiles, also setzen wir in diesem Register nur den BG-Mode auf 1 und gut ist.
Auch bekannt als:
In diesem Register aktivieren wir BG1 auf dem "Mainscreen". Das ist deshalb nötig, weil das Nachbar-Register ($212D) für den "Subscreen" zuständig ist, namentlich: Color-Math-Halbtransparenz-Effekt-Tralala. Das machen wir nächstes Schuljahr. Sofern ihr alle die Versetzung schafft. Aber da mache ich mir außer bei dem kleinen Timo ansonsten bei keinem von euch Gedanken. Und jetzt hör auf, den Prittstift zu essen, Timo!
In TMAIN (und dementsprechend TSUB) kann man die vier BGs und die Objektebene (= Sprites) hinzuschalten.
Soweit hierzu. Das waren zunächst die "tatsächlichen" Bildschirmeinstellungen.
Rekapitulieren wir:
Nach dem Forced Blank kamen folgende Register zum Einsatz:
BGMODE aka. $2105 für den BG-Mode und eventuell die Tile-Größe und die BG3-Priority
TMAIN aka. $212C für die Aktivierung des BG1 auf dem Mainscreen
Jetzt kommt der zweite Teil der Bildschirmeinstellungen, nämlich die Einstellungen des VRAM. Bevor wir gleich die Daten in den VRAM übertragen, sagen wir jetzt dem SNES, wo es im VRAM nachschauen muss, um bestimmte Daten zu finden. Also schreiben wir als nächstes:
oder:
BG1SC/$2107 entscheidet über die Größe der Tilemap und deren Adresse. Die Größe wird in den beiden niedrigsten Bits angegeben. Da wir beide auf 0 setzen, heißt das, die Tilemap ist 32x32 Pixel groß. Setzt man Bit 0, hat der BG 64 Tiles in horizontaler, bei Bit 1 in vertikaler Dimension. Setzt man diese beiden Bits, muss man dann vier 32x32-Tiles-Tilemaps hochladen; statt eine große 64x64 Tilemap schreibt man vier 32x32 Tilemaps als Flicken und legt sie hintereinander im VRAM ab, das zusammennähen übernimmt das SNES.
Die restlichen sechs Bits dienen der Adresse der Tilemap im VRAM. Dabei sind die Startpositionen "gestaffelt", dazu eine Veranschaulichung:
Ich sagte, die oberen sechs Bit sind für die Adresse zuständig.
Grafisch ausgedrückt: %0000.00xx setzt die Adresse für die Tilemap zu VRAM-Adresse $0000. Was xx ist, ist ja auch wurscht, da gehts ja nur um die Größe der Tilemap (siehe vorherigem Absatz).
Jetzt nehme ich mal spaßeshalber den nächsthöheren Wert: %0000.01xx. Jetzt sucht der SNES die Tilemap bei $0400 im VRAM.
%0000.10xx = $0800 (im VRAM)
%0000.11xx = $0C00 (im VRAM)
Und so weiter.
Wie gesagt, "gestaffelt", man kann die Tilemaps nur in einem "Raster" von $400 Byte ablegen.
Der Vorteil ist: Das ist recht leicht zu merken. Wenn man %0000.0100 in Hexadezimal umschreibt, hat man $04. Speichert man das in BG1SC, sucht das SNES die Tilemap ab $0400 im VRAM.
Speicher ich $08 in BG1SC, sucht es die Tilemap ab $0800.
Speicher ich, wie wir es jetzt hier tun, $10 in BG1SC, sucht es die Tilemap in $1000.
Das System verstanden?
$2C = $2C00 im VRAM.
$80 = $8000 im VRAM.
$05 = ... naaa? Richtig! Tilemap ab $0400 im VRAM + 32x64-Tiles-Tilemap! ($05 = %0000.0101)
Also, das System ist klar, aber obacht, man ist versucht, aus diesem $04er-Schrittmuster auszubrechen, und schon sind sie da, die Bugs.
oder
oder aber auch, wenn man ein paar Prozessorarbeitsschritte zu verschenken hat:
Mit $210B wird die Adresse des Tilesets angeben. Dabei sind die unteren vier Bits für die Adresse des Tilesets für BG1 zuständig, die oberen vier für BG2. Man kann, wie hier verwandt, für beide BGs dasselbe Tileset gebrauchen, dann setzt man bei beiden dieselbe Zahl ein. Normalerweise wäre hier wohl die Zahl unterschiedlich. Man kann aber, wie in diesem Fall, auch dann dieselbe Zahl einsetzen, wenn man nur einen von beiden BGs gebraucht...
Die Schrittweite ist hier immer folgende tausender. Also: #$41 in $210B lässt das SNES das Tileset für BG1 ab $1000 im VRAM suchen, und das Tileset für BG2 sollte bestenfalls bei $4000 und folgenden Speicheradressen liegen.
Und damit haben wir auch schon alles eingestellt und kommen jetzt schon zum zweiten Teil!
Falls es aber wen wundert: Die Grafikdaten werden doch in
drei Teile zerschnippelt, warum müssen wir nur
zwei Sachen im VRAM einstellen? Ganz einfach: Das dritte waren die Farben, und die Einstellung der Paletten für die Tiles sind Bestandteil der Tilemap. Aber gut mitgedacht! Sowas lob ich mir! Eine aufgeweckte Schülerschaft! Darum seid ihr auch alle meine Eliteschüler!
... na ja, bis auf Timo, natürlich. Es wird nicht im Unterricht gegessen, Timo!
Zweiter Teil:
Datenübertragung
Die Datenübertragung beginnen wir mit dem Transfer der Farben. Dazu verwenden wir eine Schleife.
Für die absoluten Programmieranfänger:
Schleife nicht wie bei den Turnschuhen (auch wenn die meisten von euch sie können und nicht mehr auf Klettverschluss-Schuhe angewiesen sind... euer Lehrer trägt die wirklich nur aufgrund von modischen Aspekten), eher Schleife wie bei Zeitschleife, you know?, Bill Murray, Murmeltier...?
Eine Schleife ist eine bestimmte Folge von Befehlen, die eine bestimmte oder unbestimmte Anzahl von Durchläufen wiederholt wird. Eigentlich ist es schon viel zu spät, das zu erklären, denn wir kennen ja bereits den Forever-Loop. Das ist eine Schleife. Allerdings ohne Ende. Jetzt schreiben wir eine Schleife
mit Ende.
Jetzt gehts wirklich ganz tief rein ins Programmieren, und es kommen einige neue Befehle.
Wir schreiben:
Code: Alles auswählen
STZ CGADD
LDX #$0000.w
TransferCycle0:
LDA Palette.w, X
STA CGDATA.w
INX
LDA Palette.w, X
STA CGDATA.w
INX
CPX #$0080.w
BNE TransferCycle0
Zunächst STZ in CGADD. Zu deutsch: Speichere null in CGADD. Zu englisch: Store Zero in CGADD.
CGADD gehört zu CGRAM, und da denken wir zurück an das Beispiel mit dem Haus mit Aufzug und Treppe. Wir drücken den Aufzugsknopf für "Erdgeschoss", und gleich fangen wir an, durch die Aufzugstür Datenpakete zu schleudern.
Aber zuerst laden wir in X den Wert "$0000". Wir machen das X-Register leer. X dient uns jetzt als Zähler, dessen Inhalt darüber entscheidet, ob die Schleife noch einmal wiederholt werden soll oder nicht.
Als nächstes setzen wir ein Label namens "TransferCycle0", hier soll unser Loop, unsere Schleife, beginnen (wenn es auf Englisch "Loop" heißt, warum hab ichs dann eigentlich "TransferCycle" genannt...? Egal).
Im nächsten Schritt benutzen wir einen uns unbekannten Befehl. "LDA Palette.w, X". Wir kennen zwar "LDA", aber das hier ist eine ganz eigene Variante von "LDA". Wir kennen "LDA" zum Laden von festen Zahlen ("LDA #$00.b", "LDA #$1200.w", oder "LDA #BG_MODE_MODE_1.b") und wir können mit LDA auch den Inhalt von Registern auslesen (z. B. "LDA $4210").
Wenn ich schreibe "LDA Adresse, X", dann nimmt der Prozessor den Inhalt von X, addiert ihn auf "Adresse", und lädt den Inhalt.
AUFGABE:
Code: Alles auswählen
LDA #$0A.b
STA $0001.w
LDA #$0B.b
STA $0002.w
LDA #$0C.b
STA $0003.w
LDX #$0001.w
LDA $0002.w, X
Welcher Wert befindet sich am Ende in A?
Lösung:
Gut. Zurück zum Programm. Jetzt nehmen wir das Label "Palette" und nehmen X, unseren Counter, um weiterzugehen. "Palette" haben wir vorhin vor das "INCBIN" der clr-Datei geschrieben. Beim ersten Durchlauf der Schleife wird jetzt der erste Byte der Grafikdaten geladen (da ja X = #$0000 ist und somit 0 auf die Adresse des "Palette"-Labels addiert wird) - es ist übrigens ein Byte, weil der Akkumulator ja bereits die ganze Zeit 8-Bit-groß ist.
Jetzt speichern wir in CGDATA, also "werfen" ein Datenpaket durch die Aufzugstür. Dann machen wir "INX", das steht für "Increment X", also "Mach den Wert von X um eins größer".
Diese drei Befehle wiederholen wir jetzt noch einmal*. Durch das INX lädt er jetzt beim zweiten Mal "LDA Palette.w, X" den zweiten Byte (weil: Palette hat sich nicht bewegt, aber X ist ja bereits eins größer geworden). So weit, so gut.
Nach dem zweiten Inkrementieren schreiben wir "CPX #$0080.w".
Compare X. Vergleiche X (mit)...
Der Inhalt des X-Registers wird mit dem nebenstehenden Wert verglichen. Wenn die Werte gleich sind, wird im Statusregister ein Flag gesetzt (das Zero-Flag, um genau zu sein).
Die Gegenstücke für die anderen beiden Register heißen übrigens CPY und CMP.
Zum Abschluss schreiben wir "BNE TransferCycle0".
BNE steht für "Branch Not Equal", grob (und verständlich) übersetzt: "Nimm die Abzweigung, wenn das Ergebnis nicht gleich ist". Der Befehl guckt, ob das entsprechende Flag (das Zero-Flag, um genau zu sein) im Statusregister gesetzt ist, wenn ja, geht es normal mit dem nächsten Befehl weiter, wenn nicht, springt es zum Label.
Das Gegenteil von BNE ist BEQ ("Branch Equal").
Fassen wir noch einmal zusammen:
Wir setzen den Counter auf null, dann steigen wir in den Schleifenbereich ein: Zweimal laden wir in Abhängigkeit von der Höhe des Counters einen Byte aus den Paletten-Daten und speichern den Wert in CGDATA und erhöhen dann den X-Wert, danach gucken wir, ob X bereits $80 (= 128) erreicht hat**, und wenn nicht, wiederholen wir die Schleife.
Übrigens müssen wir den Wert in CGADD nicht bearbeiten. Wenn zweimal ein Byte in CGDATA geschrieben wird, also "ein vollständiger Farb-Datensatz" geschrieben wurde, wird der Wert in CGADD automatisch inkrementiert. Das hatte ich beiläufig beim Fahrstuhl-Beispiel erwähnt gehabt, falls man sich erinnert. Ich weise noch einmal darauf hin: Das wird erst inkrementiert, wenn zwei Byte geschrieben wurden.
Okay, damit haben wir das abgeschlossen. Wir sind zwar immer noch beim Datentransfer, aber als nächstes muss doch noch eine Einstellung getroffen werden:
In schnöden Zahlen ausgedrückt:
Warum treffe ich diese Einstellung erst jetzt?
Eine beliebte Möglichkeit, sich das Hirn beim Bugfixing zu brechen, sind Bugs, die nur auf echter Hardware und den guten Emulatoren (wie BSNES) auftreten, nicht aber bei den vereinfachten, vereinfachenden Emulatoren (an deren Spitze ZSNES steht,
den man nicht zu verwenden hat!). Und eine gute Möglichkeit, solche Bugs zu produzieren, ist die Reihenfolge, wie man Befehle ausführt. Scheinbar gibt es bestimmte Befehle der Bildschirmeinstellungen, die auf echter Hardware einen Forced Blank auslösen. Deswegen sollte man den Bildschirm immer zu allerletzt wieder einschalten (es sei denn, man möchte wie ich tagelang nach dem Grund suchen, weshalb das Bild schwarz bleibt). Deshalb habe ich mir angewöhnt, alle Einstellungen immer erst zu treffen, wenn ich sie tatsächlich auch brauche, auch wenn es unübersichtlich werden mag (für mehr Übersichtlichkeit kann man ja Leerzeilen und Kommentare setzen, wie man will). Darum diese Einstellung erst jetzt.
Was bewirkt diese Einstellung?
Das... ist eigentlich ganz einfach. Mit dem VRAM kommuniziert man wie mit dem CGRAM. Man hat zwei 8-Byte-große nur-beschreibbare-das-heißt-nicht-auslesbare Register für die Adresse und dasselbe für die Daten. Logischerweise hat man beim VRAM zwei Adressregister statt nur eins, da der CGRAM ja nur 256 Plätze zum Ansprechen braucht (#$00-#$FF), während der Adressraum vom VRAM ja 16-Bit-groß ist (d. h. eine vierstellige Hexadezimalzahl... wir haben eingestellt, dass die Tiles bei $0000 und die Tilemaps bei $1000 liegen, du erinnerst dich?).
Mit dem Most Significant Bit im Register VMAIN/$2115 stellt man ein, wann der Inhalt der Adress-Register erhöht werden soll:
0 = Beim Schreiben in VMDATAL/$2118
1 = Beim Schreiben in VMDATAH/$2119
Der Unterschied ist: Wenn ich ein 8-Bit-großes Register habe und in $2118 schreibe, schreibe ich nur in $2118. Habe ich ein 16-Bit großes Register, schreibt es die unteren acht Bit in $2118 und die oberen in $2119. Dementsprechend, auch wenn das jetzt sehr kompliziert ist, muss mach sich eigentlich nur eins merken:
Diese Einstellung (#VMAIN_INCREMENT_MODE in VMAIN, bzw. #$80 in $2115) muss man machen, wenn man mit einem 16-Bit-Register Daten übertragen will. Wenn man mit einem 8-Bit-Register übertragen will, macht man das nicht.
Machen wir weiter:
Code: Alles auswählen
LDX #$0000.w
STX VMADDL.w
TransferCycle1:
LDY Tileset.w, X
STY VMDATAL.w
INX
INX
CPX #$0200.w
BNE TransferCycle1
LDX #$1000.w
STX VMADDL.w
LDX #$0000.w
TransferCycle2:
LDY Tilemap.w, X
STY VMDATAL.w
INX
INX
CPX #$0800.w
BNE TransferCycle2
Das sieht nach viel Code auf einmal aus, aber das ist im Endeffekt ganz viel altbekanntes. Wir laden in X wieder Null und speichern es in die VRAM-Adress-Register (da VMADDL/$2116 nur 8-Bit groß ist, werden automatisch die oberen 8-Bit von X in VMADDH/$2117 gespeichert). Jetzt beginnt der zweite TransferCycle:
Es wird ein Doppelbyte vom Tileset geladen und in VMDATAL gespeichert; da Y wie X 16-Bit-groß ist, ist es hier wie bei den Adress-Registern: Die untere Hälfte des Doppelbytes kommt in VMDATAL/$2118, die obere in VMDATAH/$2119. Dann wird X zweimal inkrementiert (klar, weil wir ja auch einen
Doppelbyte in einem Zug übertragen haben), und der ganze Spaß wird mit 512 Bytes gemacht, denn sobald X $200 (= 512) erreicht hat, ist das Ganze vorbei.
Wenn man jetzt im Explorer bei der .pic-Datei nachschaut (Rechtsklick, Eigenschaften), sieht man da auch, dass die Datei tatsächlich exakt 512 Bytes groß ist.
Der zweite Teil ist dasselbe wie der erste, nur dass zuerst $1000 in VMADDL gespeichert wird, wir also die Anfangsadresse auf $1000 setzen. Danach muss man nochmal $0000 in X laden, um den richtigen Counterwert zu haben.
Wenn euch aufgefallen ist, habe ich keine Tilemap-Datei mitgeliefert. Die müsst ihr gleich selbstschreiben. Aber machen wir erst das hier fertig.
Kommen wir zum dritten Teil, dem
Abschluss.
In der klassischen Musik spräche man von der Coda.
Zu deutsch: Bildschirm an, gib vollen Saft drauf.
Fertig. Jetzt schreiben wir noch die Tilemap. Dazu nehmt ihr euren Hexeditor, ich kann euch HxD empfehlen, falls ihr den nicht schon habt.
Jetzt erstellst du eine neue Datei in dem Ordner (ist egal, was für eine; nimm z. B. eine neue TXT-Datei) und benenn sie um, wie du deine Tilemap nennen möchtest. Jetzt öffnest du diese Datei mit dem Hexeditor. IN HxD gibt es eine schöne Einstellung, die du so treffen kannst:

- 01.jpg (145.58 KiB) 22568 mal betrachtet
Wähle 64 aus. Das ist die Einstellung, ab wie viel Byte ein Zeilenumbruch in HxD angezeigt werden soll. Bei 64 stimmt es mit dem Zeilenumbruch überein, der bei einer Tilemap ist - soll heißen: Bei einem Zeilenumbruch nach 64 Byte sind die Daten eines Tiles über den Daten des Tiles, dass am Ende auch wirklich auf dem Bildschirm dargestellt werden.
Und jetzt... fang an zu schreiben.
Wie die Daten für die Tilemap aufgebaut sind, habe ich im vorherigen Teil beschrieben. Um dir zu helfen: Du kannst folgende Dinge schreiben: 01 00 oder 02 00 oder 03 00. Entsprechend dem wird dann jeweils das Tile eingefügt, was der Nummer entspricht (guck oben auf die Übersicht der Tilesets für die Reihenfolge der Tiles).
Und wenn du fertig bist... speicher alles, kompilier... und fertig!
Und, ach ja, prüf am besten, ob die Dateinamen der Tilesets auch mit den Dateinamen im INCBIN-Teil des Sourcecodes übereinstimmen!
~~~
*
Der Schlaumeier mag quaken: "Warum zweimal dieselben Befehle? Schreiben wir nicht eine Schleife, um nicht immer dieselben Befehle hintereinander zu kleistern?"
Ja, stimmt. Aber:
Wir erinnern uns an den 2x8-Bit-Karton, was das Register CGDATA ja ist. Euer Lehrer weiß, dass diese Register gern zickig werden, wenn sie nicht sofort nach den ersten 8 Bit den Rest ihrer Daten erhalten; wenn sie es nicht tun, schmeißen sie den ersten Teil sehr bald weg und tun so, als hätten sie nie etwas erhalten. Und da euer Lehrer nicht geneigt ist, große Experimente durchzuführen, ob dem so ist, wenn man nur eine Hälfte eines Farbdatensatzes überträgt und dann eine etwaige Schleifenwiederholung prüfen lässt, macht er das lieber so.
**
Der Schlaumeier mag quaken: "Aber eine Palette mit 16 Farben hat doch niiie im Leben 128 Byte Größe!"
Richtig. Wenn du die clr-Dateien mit einem Hex-Editor öffnest, siehst du da ganz viele Nullen. Wenns dich stört, änders doch. Du könntest sogar die ganzen Nullen rauslöschen; wenn du dann trotzdem den Loop bis $80 laufen lässt, werden sogar die ersten Bytes der nachfolgenden Datei (der zur Zeit noch nicht existierenden Tilemap-Datei) in den CGRAM übertragen, was total wurstige (und höchstwahrscheinlich recht dunkle) Farben ergeben wird. Aber, solange du deine Tilemap ordentlich schreibst und immer nur Palette 0 verwendest, müsste alles glatt gehen. Wobei ich bezweifle, dass du dazu in der Lage bist, Timo. Ich möchte mal beim nächsten Elternabend mit deinen Eltern sprechen.
WETTBEWERB! WETTBEWERB! WETTBEWERB!
Das da oben ist ne Menge Holz, also bitte ich darum: Wenn das wer liest und was nicht versteht:
Bitte fragt. Bitte postet Verständnisfragen hier. Nur so kann der Kram besser werden.
Und ansonsten, wenn jemand das alles ausprobiert:
Bitte zeigt die Ergebnisse. Ich würde mir eine hübsche kleine Galerie wünschen, was man alles dadraus basteln kann.
