Erstellt: 28. September 2014 (zuletzt geändert: 9. Dezember 2020)

Char Pad: Tilemaps erstellen & anzeigen

Mit Char Pad einen Level erstellen und im Programm anzeigen

C64 Studio & AMCE

Eine häufig genutzte Methode um Levelkarten zu erstellen, ist die Verwendung von Tiles. Dabei werden aus einzelnen Zeichen größere „Bauteile“ (die sog. Tiles) erstellt, um aus diesen dann wiederum den Level zusammenzubauen.

Beim C64 Studio gibt es zwar einen Mapeditor, der sagt mir allerdings nicht sonderlich zu.

Ein sehr beliebtes Tool, um Zeichensätze, Tiles und Tilemaps zu erstellen, ist das Char Pad (hier findet ihr übrigens auch das ebenfalls beliebte Sprite Pad).

Für den Programmierteil weiter unten, solltet ihr euch schon mit den Beiträgen VIC-II: Speicherbereiche festlegen und VIC-II: Grafikmodes – Text beschäftigt haben.

Char Pad: Crashkurs

Zunächst solltet ihr euch das Char Pad v1.83 herunterladen. Das Programm muss nicht installiert werden, ihr könnt es einfach entpacken und schon ist es einsatzbereit. Sobald alles entpackt ist, startet das Char Pad direkt mal.

Das Char Pad
Das Char Pad

Werfen wir nun einen Blick auf einige wichtige Einstellungen.

CharPad_002
Die ersten Schritte
  1. Begebt euch zum Tiles-Tab.
    Hier werden die einzelnen Tiles (Bauteile) erstellt und verwaltet.
  2. Dies ist der Editor, in dem ihr eurer Tiles am Stück zeichnen könnt.
  3. Das ist die Liste mit eueren Tiles.
  4. Hier seht ihr die einzelnen Zeichen, aus denen die Tiles zusammengebaut werden.

    Die Tiles im Char Pad können 1×1, 2×2, 3×3, 4×4 oder 5×5 Zeichen groß sein. Als erstes solltet ihr euch also überlegen, welche Tilegröße ihr benötigt und diese einstellen.CharPad_003
  5. Tilegröße festlegen
  6. und übernehmen
    CharPadAni_01Wie ihr seht, ändern sich alle drei Anzeigefenster abhängig von der gewählten Größe.

    CharPad_004
  7. Hier legt ihr fest, wie die Farben vergeben werden sollen. Das schauen wir uns gleich genauer an.
  8. Entscheidet euch für einen Multicolor oder einfarbigen Zeichensatz.
  9. Mit diesem Haken könnt ihr die Hilfslinien an- und abschalten.

    Ich nehme nun eine Tilegröße von 4 und entscheide mich für einen Multicolor Zeichensatz.
    Um die drei verschiedenen Farb-Einstellungen (7.) zu untersuchen wähle ich zunächst „Global“ und zeichne zwei einfache Muster mit der Hauptfarbe „Pen – RAM Colour“.
    CharPad_005Im Editor (2.) seht ihr das aktuell gewählte Tile aus dem Tile Set (3.). Das Schöne am Char Pad ist, dass ihr direkt im Editor euer großes Tile zeichnen könnt. Das große Bauteil wird dabei automatisch auf einzelne Zeichen verteilt. Diese findet ihr ganz rechts im Character Set (4.).

    Nun aber zu den Farben:
    Die beiden Multicolor-Farben sind, wie ihr wisst, immer für alle Zeichen identisch. Genau das Gleiche gilt auch für die Hauptfarbe, wenn ihr „Global“ auswählt.
    CharPad_006
  10. Ändern wir die Farbe „Pen – RAM Colour“ von grün auf gelb, dann ändert sich direkt die Farbe aller Tiles.

    CharPad_007
  11. Denkt daran, dass ihr bei Multicolor auch hochauflösende Zeichen verwenden könnt. Klickt ihr bei aktivem Multicolor eine Farbe aus der oberen Reihe an, dann verdoppelt sich die Auflösung in X-Richtung im Editor.

    Ändert nun die Einstellung unter „RAM colouring“ (7.) auf „Per Tile“. Ab jetzt könnt ihr für jedes einzelne Tile eine Farbe vergeben.
    CharPad_008Lasst euch nicht davon irritieren, dass sich auch die Farbe aller Zeichen ganz rechts (4.) ändert! Dies ist normal und hat keine Auswirkung auf den Export.

    Werfen wir abschließend noch einen Blick auf die letzte Einstellung von „RAM colouring“ (7.) „Per Tile Cell“.
    CharPad_009
  12. Wie ihr seht, könnt ihr mit dieser Einstellung Farben für jede einzelne Zelle des Tiles vergeben. Außerdem steht euch dann noch eine dritte Hilfsfunktionen zur Verfügung.
    Cell Selector: Zellen des Tiles markieren, um Material oder Farben am Block zu setzen.
    Cell Marker – Material: Hiermit könnt ihr zusätzliche Kennzeichen zum Tile angeben, auf die ihr dann im Programm reagiert. Damit könnt ihr z. B. allen Stellen des Tiles, die tödlich sind, die Nummer 1 zuweisen. Im Source müsst ihr dann nur kontrollieren, ob der Spieler ein Tile-Zeichen mit dem Material 1 betreten hat, anstatt auf zig verschiedene Zeichencodes zu prüfen.
    Cell Marker – RAM colour: Damit ändert ihr die Farbe der einzelnen Zellen.
  13. Compress options
    Durch einen Klick auf Compress könnt ihr doppelte Zeichen entfernen lassen. Normalerweise legt das Charpad für jedes Tile eigene Zeichen an.
    CharPad_010Hier seht ihr, dass das erste und dritte Tile, bis auf die Farben in der Mitte, eigentlich identisch sind. Trotzdem werden rechts (4.) die Zeichen für die beiden Tiles doppelt angezeigt. Klickt ihr nun auf „Compress“ (13.) und habt die Haken wie auf dem Bild gesetzt, dann verschwinden fast alle doppelten Zeichen.
    CharPad_011Es bleiben jetzt nur noch die Tiles mit einer abweichenden Farbe übrig. Selbst diese können wir noch zusammenfassen. Dazu müssen wir nur den Haken bei „Copy colours to character attributes“ entfernen.
    CharPad_012Hiermit könnt ihr also die benötigten Zeichen reduzieren.
  14. Wechselt nun zum Tab „Map“
    CharPad_013Hier auf diesem Tab baut ihr nun, aus den eben erstellten Tiles, euren Level.
  15. Der große Bereich in der Mitte ist euer Map-Editor.
  16. Die verfügbaren Tiles findet ihr rechts in der Liste.
  17. Legt hier die größes eures Ausgabebildschirms fest. Beim C64 also 40×25.
  18. Hier gebt ihr an, wie groß die Map werden soll. Ihr könnt für scrollende Spiele oder für Spiele bei denen der Bildschirm umgeschaltet wird, auch Maps erstellen, die größer als der Bildschirm sind. Gebt hier einfach die Kartengröße als Tiles x Tiles an. Hier findet ihr auch den Speicherbedarf eurer Map.
  19. Natürlich könnt ihr eure Arbeit auch speichern. Klickt dazu auf den Menüpunkt File und wählt „Save Project As…“ aus.
    CharPad_014Das Programm speichert sämtliche Änderungen (Zeichensatz, Tiles, Map…).

Unter „File“ (19.) findet ihr auch das Gegenstück -> „Open Project…“. Diesen Menüpunkt verwenden wir jetzt, um ein mitgeliefertes Beispiel zu öffnen. Klickt also auf „Open Project…“ und begebt euch im folgenden Dialog ins „Char Pad“ Verzeichnis. Dort gibt es den Ordner „Examples“ und darunter „Metal Warrior – 4×4“. Wählt dann die Datei MW – L1.ctm aus…

CharPad_015
Die Metal Warrior Tiles.
CharPad_016
Die Metal Warrior Map

Schaut euch mal die Tiles und die Map in Ruhe an.

Wir wollen nun ein Programm schreiben, dass diese Daten verarbeiten kann, bzw. zumindest den ersten Bildschirm anzeigt. Dazu müssen wir erstmal alle benötigten Daten exportieren. Wählt unter „File“ (19.) den Punkt „Export Data…“ aus.
CharPad_017

  1. Unter „Export Items“ legt ihr fest, welche Daten ihr braucht. Wir nehmen hier alle.
  2. Für die Attribute (Farben) müssen wir festlegen, wie diese exportiert werden sollen. Wir wählen hier „Per Tile“. Wie ihr weiter oben seht, wurde diese Einstellung auch unter „Tiles – RAM colouring“ gewählt. Die Einstellung wirkt sich auf die Größe der „Attribute Table“ aus.
  3. Hier seht ihr den Speicherbedarf für die einzelnen Dateien.
  4. Wir wählen für den Export das „Raw Data“-Format, da wir nur die reinen Daten, ohne eine Ladeadresse benötigen.

    Nach einem Klick auf OK werdet ihr nach den Dateinamen für die einzelnen Files gefragt. Ich belasse es für unser Beispiel bei den vorgeschlagenen Namen: chars.raw, tiles.raw, map.raw und attribs.raw.

Bevor wir nun anfangen unser Programm zu schreiben, sollten wir erstmal klären, wie die Dateien aufgebaut sind und wie sie zueinander in Bezug stehen.

Aufbau der Dat(ei)en

Wir haben nun also unsere vier Dateien: chars.raw, tiles.raw, map.raw und attribs.raw. Deren Namen sagen eingentlich schon wo sich was befindet, aber hier nochmal als Grafik:

CharPad_X_00Wir lesen gleich aus der map.raw die Tile-Nr. aus. Diese ist ein Byte groß. Dabei müssen wir beachten, dass die Map 100 Tiles breit ist, wir aber nur 10 Tiles brauchen, um die gesamte Bildschirmbreite auszufüllen. Die Höhe der Map beträgt 15, wovon wir nur 5 für die Bildschirmhöhe benötigen.
Mit der Tile-Nr. aus map.raw, schauen wir in tiles.raw nach (Pfeil 1), wie das Tile aufgebaut ist. Dort finden wir die einzelnen Zeichen (Pfeil 2), aus denen das Tile besteht. Alle 16 Zeichen (wir haben hier 4×4 große Tiles) sind dort hinterlegt (s. Tile-Nr. 25). Diese Zeichen entsprechen einfach unseren Screencodes aus der Datei chars.raw (Pfeil 3 & 4).

Bleiben noch die Farben:
In der Datei attribs.raw werden die Farben exportiert. Der Aufbau hängt von euren Farb- und Exporteinstellungen ab.

Attribute Table – Per Tile
Hier wird für das gesamte Tile ein Farb-Byte in die attribs.raw geschrieben. Die Farbe könnt ihr dann ganz einfach mit der Tile-Nr. aus dieser Datei auslesen.
Dateigröße in Bytes = Anzahl der Tiles (hier 128 Bytes)

Attribute Table – Per Tile Cell
Hier wird für jede Zelle des Tiles eine eigene Farbe nach attribs.raw geschrieben. Der Aufbau der Datei ähnelt dann dem der tiles.raw. Ihr könnt einfach parallel zum jeweiligen Zeichencode die Farbe aus der attribs.raw lesen.
Dateigröße in Bytes = Anzahl Tiles * Tile size² (hier 128*4*4 = 2048 Bytes)

Attribute Table – Per Character
Bei dieser Exportmethode wird für jedes Zeichen eine Farbe in die Datei attribs.raw geschrieben. Ihr könnt also mit dem Zeichencode auf die attribs.raw zugreifen.
Dateigröße in Bytes = Anzahl der Zeichen (hier 256)

Einen Sonderfall stellt das RAM colouring – Global dar.
Da hier für alle Zeichen die selbe Farbe verwendet wird, brauchen wir die attribs.raw eigentlich nicht. Die Farbe können wir direkt für alle Zeichen ins Farb-RAM kopieren.

Wo findet man das „Material“?

Möchtet ihr die „Material“-Möglichkeit nutzen, dann fragt ihr euch evtl. wie die Datei heißt, in der diese Daten stehen. Man nutzt hier den Umstand aus, dass der C64 nur 16 Farben beherrscht. Daher wird für das Farb-Byte nur das untere Nibble benötigt. Das „Material“ findet ihr also in der attribs.raw im oberen Nibble des jeweiligen Farb-Bytes.

Programmerstellung

Wir sollten erstmal überlegen, wie unser Programm arbeiten muss.

  • VIC-II – Speicheraufteilung vornehmen
  • Multicolor-Textmodus aktivieren und MC-Farben setzen
  • Den Bildschirm mit 10 x 5 Tiles füllen. Dies entspricht 40 Zeichen und 20 Zeilen.

Den Anfang machen einige Konstanten…

MAPWIDTH        = 100               ;Map: Anz. Tiles in X-Richtung
MAPHEIGHT       = 15                ;Map: Anz. Tiles in Y-Richtung
TILEWIDTH       = 4                 ;Tile: Anz. Zeichen (Breite)
TILEHEIGHT      = 4                 ;Tile: Anz. Zeichen (Höhe)
TILESIZE        = TILEWIDTH*TILEHEIGHT ;Tile: Anz. der Zeichen fürs ganze Tile
SCREENTILES_X   = 10                ;SCR: Anz. Tiles in X-Richtung
SCREENTILES_Y   = 5                 ;SCR: Anz. Tiles in Y-Richtung
SCREENRAM       = $0400             ;SCR: Adresse des Bildschirmspeichers
COLORRAM        = $d800             ;SCR: Adresse des Farb-RAMs

ZP_TILESY       = $02               ;Hilfsvariablen auf der Zeropage
ZP_TILESX       = $03
ZP_TILENO       = $04
ZP_TILEHEIGHT   = $05

ZP_MAPADR       = $10               ;Hilfsadressen (2 Bytes) auf der Zeropage
ZP_SCREENADR    = $12
ZP_COLORRAMADR  = $14
ZP_TILEADR      = $16

Diese halten das Programm flexibel, sodass man später leichter andere Map- / Tilegrößen verarbeiten kann.

Die ersten beiden Punkte aus der obigen Todo-Liste sollten für euch eine Kleinigkeit sein…

!zone main
;*** Startadresse 
*=$0801
;** BASIC-Zeile: 2018 SYS 2062
 !word main-2, 2018 
 !byte $9e
 !text " 2062"
 !byte $00,$00,$00

main
 lda #147                           ;Bildschrim löschen
 jsr $ffd2
 lda #6                             ;blau für          
 sta $d020                          ;Rahmen und
 sta $d021                          ;Hintergrund
 lda $d018                          ;Zeichensatz
 and #%11110001                 
 ora #%00001000                     ;nach $2000
 sta $d018                          ;legen
 lda $d016                          ;Multicolor
 ora #%00010000                     ;Textmodus
 sta $d016                          ;setzen
 lda #15                            ;hellgrau
 sta $d022                          ;für erste MC-Farbe
 lda #14                            ;hellblau
 sta $d023                          ;für die zweite MC-Farbe
 jsr drawMap                        ;Bildschirmausgabe
 jmp *                              ;Endlosschleife

Wir löschen hier einfach mal kurz den Bildschirm, setzten die bekannten Farben und nehmen die oben erwähnten Einstellungen vor. Dann wird zu drawMap gesprungen, um die Karte zu zeichnen. Zum Schluß bleibt das Programm in einer Endlosschleife hängen.

Die aus dem Char Pad exportierten Daten binden wir direkt in unser Programm ein. Dabei machen wir es uns für dieses Beispiel recht einfach und legen die Daten des Zeichensatzes „chars.raw“, direkt an der von uns gewählten Adresse $2000 ab. Damit sparen wir uns das Kopieren des Zeichensatzes. Fügt also, hinter der Endlosschleife, die nächsten Zeilen ein.

tiles
 !bin "tiles.raw"

colors
 !bin "attribs.raw"

map
 !bin "map.raw"

*=$2000
characters
 !bin "chars.raw"
Bildschirm zeichnen

Um jetzt den gesamten Bildschirm zu füllen, brauchen wir 10 Tiles für die Breite. Ein Tile ist hier vier Zeichen breit, macht also 40 Zeichen, genau unsere Bildschirmbreite beim C64. Für die Höhe werden nur 5 Tiles (= 20 Zeilen) benötigt. Schaut euch die Karte nochmal im Map-Editor an. Sie enthält übereinander drei Abschnitte der Strecke. Der Level ist also in Wirklichkeit eigentlich 300 Tiles (also 30 komplette Bildschirme) breit und 5 Tiles hoch.

Hier könnt ihr auch einen Vorteil der Tile-Technik erkennen. Wie ihr beim Export gesehen habt, benötigen wir 2048+2048+1500+128=5724 BYTEs für den Level. Würdet ihr immer ganze Bildschirme mit einzelnen Zeichen verwenden, dann bräuchten wir 2048+40*20*2*30=50048 Bytes (Zeichensatz + Bildschirmbreite * Bildschirmhöhe * 2 für Screen & Color-RAM * Levelbreite in Bildschirmen), also mehr als neunmal soviel!

Der Einfachheit halber zerlege ich das „Problem“ jetzt in zwei Teile. In der Funktion drawMap wird die Datei map.raw ausgelesen. Hier wird also immer das nächste Tile gesucht. Dieses wird dann an die zweite Funktion drawTile übergeben, die das Tile endgültig auf den Bildschirm bringt. Der folgende Source dient wieder nur als erstes Beispiel und läßt viel Raum für Verbesserungen. Es wird nur der erste Bildschirm der Map dargestellt, ihr könnt das Programm später ja noch verbessern. Beginnt also direkt hinter der Endlosschleife jmp * mit den beiden Funktionen.

!zone drawMap
drawMap
 lda #<map                          ;Adresse der Map
 sta ZP_MAPADR                      ;auf die Zeropage
 lda #>map
 sta ZP_MAPADR+1

Zu Beginn holen wir uns die Adresse, an der die map.raw im Speicher liegt und merken uns diese auf der Zeropage.

 lda #0                             ;Zähler Tiles in Y-Richtung
 sta ZP_TILESY                      ;auf der Zeropage merken
.loop
 lda #SCREENTILES_X-1               ;Anz. Tiles für BS in X-Richtung (-1) 
 sta ZP_TILESX                      ;auf der Zeropage merken
.loop1
 ldy ZP_TILESX                      ;Position des nächsten Tiles ins Y-Reg.
 lda (ZP_MAPADR),Y                  ;Tile-Nr. aus der 'map.raw' holen
 jsr drawTile                       ;Tile zeichnen
 dec ZP_TILESX                      ;Zähler für Tiles in X-Richtung verringern
 bpl .loop1                         ;solange positiv -> nochmal

Dann verwenden wir zwei Hilfsvariablen auf der Zeropage als Schleifenzähler. In ZP_TILESY beginnen wir die Tilezeile (Y-Richtung) von 0 an hochzuzählen. In ZP_TILESX zählen wir die Anzahl der Tiles in X-Richtung herunter. Dann holen wir hinter .loop1 zunächst die X-Position des Tiles ins Y-Register. Damit holen wir uns jetzt per Y-nach-indizierter-Adressierung aus der Map die nächste Tile-Nr.

CharPad_018Dann springen wir mit der Tile-Nr. nach drawTile. Sobald wir von dort zurückkehren, verringern wir ZP_TILESX, um das nächste Tile in dieser Zeile zubekommen. Solange ZP_TILESX positiv ist, springen wir wieder nach .loop1. So arbeiten wir erstmal rückwärts alle Tiles in der Zeile ab.

 lda #MAPWIDTH                      ;Start für die nächste Map-Zeile errechnen
 clc                                ;Carry löschen
 adc ZP_MAPADR                      ;LSB der MAP-Startadresse addieren
 sta ZP_MAPADR                      ;und speichern
 lda #0                         
 adc ZP_MAPADR+1                    ;ggf. Carry zum MSB der MAP-Startadresse addieren
 sta ZP_MAPADR+1                    ;und speichern
 inc ZP_TILESY                      ;Zähler für Tiles in Y-Richtung erhöhen
 lda ZP_TILESY                      ;zum Prüfen in den Akku
 cmp #SCREENTILES_Y                 ;mit der Anz. Tiles in Y-Richtung vergleichen
 bne .loop                          ;solange ungleich -> nochmal
 rts                                ;zurück zum Aufrufer

Haben wir die gesamte Zeile von rechts nach links ausgegeben, dann müssen wir zum Anfang der nächsten Zeile springen. Dazu addieren wir zu unserer Hilfsadresse ZP_MAPADR einfach die Breite der Map. Also MAPWIDTH in den Akku holen, Carry-Flag löschen, zu ZP_MAPADR addieren und wieder zurückschreiben. Dann noch das C-Flag zum MSB addieren. Danach erhöhen wir ZP_TILESY (unseren Zähler für die Zeilen) und prüfen, ob dieser die Anzahl der Tiles in Y-Richtung SCREENTILES_Y für die Bildschirmausgabe erreicht hat. Falls nicht, geht es zurück nach .loop, um die nächste Zeile auszugeben. Zum Schluß wird die Funktion einfach verlassen.

Nun ist unsere eben bereits gesehene Funktion drawTile an der Reihe. Hier möchte ich nicht nur die Berechnung der Positionen zur Laufzeit zeigen, sondern eine Alternative anbieten. Wir beschleunigen unsere Funktion etwas, indem wir Hilfstabellen benutzen.

drawTile
 sta ZP_TILENO                      ;Tile-Nr. in der Zeropage merken
 ldy ZP_TILESY                      ;Tile-zeile (Y-Richtung) ins Y-Reg.
 lda screenRow_LSB,Y                ;LSB aus Hilfstabelle holen
 sta ZP_SCREENADR                   ;und auf die Zeropage damit
 sta ZP_COLORRAMADR                 ;auch fürs ColorRAM merken
 lda screenRow_MSB,Y                ;MSB aus der Hilfstabelle holen
 sta ZP_SCREENADR+1                 ;und auf der Zeropage merken

In ZP_TILENO merken wir uns die aktuelle Tile-Nr. Dann machen wir es uns ganz einfach! Die in ZP_TILESY stehende Y-Position des Tiles in der map.raw nutzen wir auch für die Ausgabe. Dies klappt so natürlich nur für den ersten Bildschrim, wenn wir ihn ganz links beginnen. Hier besteht also für euch Handlungsbedarf, wenn ihr die Routine später weiterverwenden möchtet. Die Position holen wir ins Y-Register und lesen damit aus unserer Hilfstabelle screenRow_LSB das LSB für die aktuelle Zeile. Dieses speichern wir in ZP_SCREENADR für den Bildschirmspeicher und in ZP_COLORRAMADR für das Color-RAM. Dann holen wir uns das MSB aus der Tabelle screenRow_MSB und speichern es bei ZP_SCREENADR+1.

 ldx ZP_TILESX                      ;Offset in X-Richtung 
 lda screenCol_Offset,X             ;aus der Hilfstabelle holen
 clc                                ;Carry-Flag löschen
 adc ZP_SCREENADR                   ;und zum LSB addieren
 sta ZP_SCREENADR                   ;Ergebnis merken
 sta ZP_COLORRAMADR                 ;auch für das ColorRAM
 lda ZP_SCREENADR+1                 ;MSB von der Zeropage holen
 adc #0                             ;ggf. das C-Flag addieren
 sta ZP_SCREENADR+1                 ;und speichern
 adc #$d4                           ;MSB-Offset fürs ColorRAM addieren
 sta ZP_COLORRAMADR+1               ;und ab auf die Zero-Page

Um nun die korrekte Startadresse zu berechnen, müssen wir natürlich noch wissen, an welcher X-Position die Ausgabe beginnen soll. Wir nutzen hier wieder den Wert, der auch in drawMap genutzt wurde und lesen die X-Position ZP_TILESX ins X-Register ein. Damit holen wir dann den Offset vom linken Rand aus der Hilfstabelle screenCol_Offset. Diesen Wert müssen wir dann noch zu unserer Startadresse ZP_TILESX addieren. Auch hier speichern wir fürs Farb-RAM das LSB ebenfalls in ZP_COLORRAMADR. Sobald wir die Startadresse berechnet haben, müssen wir noch das MSB für das Farb-RAM anpassen. Nach sta ZP_SCREENADR+1 ;und speichern befindet sich im Akku das MSB für den Bildschrimspeicher. Da wir diesen fest bei $0400 gelassen haben, müssen wir nur $d4 addieren, um den Beginn des Color-RAMs $d800 zu erhalten. Das MSB speichern wir dann natürlich bei ZP_COLORRAMADR+1.

 lda #<tiles                        ;Startadresse der Tiles 
 sta ZP_TILEADR                     ;auf die Zeropage
 lda #>tiles
 sta ZP_TILEADR+1
 ldx ZP_TILENO                      ;Tile-Nr. ins X-Register
 beq .skip                          ;falls 0 -> weiter bei .skip
.loop
 lda #TILESIZE                      ;Anzahl der Zeichen je Tile
 clc                                ;zur Startadresse der Tiles
 adc ZP_TILEADR                     ;addieren
 sta ZP_TILEADR
 lda ZP_TILEADR+1
 adc #0
 sta ZP_TILEADR+1
 dex                                ;Tile-Nr. im X-Reg. verringern
 bne .loop                          ;falls nicht 0 -> nochmal zu .loop
.skip

Jetzt müssen wir natürlich noch ermitteln, wo die Zeichendaten für das aktuelle Tile beginnen. Dazu merken wir uns die Staradresse der Tiles in ZP_TILEADR auf der Zeropage. Anschließend holen wir uns die aktuelle Tile-Nr. aus ZP_TILENO ins X-Register und prüfen, ob die Nr. gleich Null ist. Falls ja, müssen wir keinen Offset berechnen und springen nach .skip. Ist die Tile-Nr. aber gößer Null, dann addieren wir in einer Schleife immer die Tilegröße TILESIZE zur Startadresse der Tiles in ZP_TILEADR. Dieser Punkt sollte unbedingt überdacht werden! Ihr könnt z. B. wie in MUL & DIV (Ganzzahl) beschrieben, die Multiplikation verwenden oder ihr greift erneut auf eine Hilfstabelle zurück (das zeige ich gleich nochmal).

 lda #TILEHEIGHT                    ;Tilehöhe 
 sta ZP_TILEHEIGHT                  ;auf der Zeropage merken
.loop1
 ldy #TILEWIDTH-1                   ;Tilebreite (-1) in Y-Register
.loop2
 lda (ZP_TILEADR),Y                 ;Zeichen vom Tile holen
 sta (ZP_SCREENADR),Y               ;und ausgeben
 ldx ZP_TILENO                      ;Tile-Nr. ins X-Register
 lda colors,X                       ;damit die Farbe holen
 sta (ZP_COLORRAMADR),Y             ;und ins Farb-RAM kopieren
 dey                                ;Y verringern
 bpl .loop2                         ;solange positiv -> nochmal zu .loop2

Jetzt können wir mit der Ausgabe beginnen.
Noch mal zur Erinnerung:

  • ZP_SCREENADR : Startadresse der aktuellen Zeichenzeile für die Bildschirmausgabe
  • ZP_COLORRAMADR : Startadresse der aktuellen Zeichenzeile im Farb-RAM
  • ZP_TILEADR : Startadresse der aktuellen Tilezeile ab tiles
  • ZP_TILENO : aktuelle Tile-Nr.

Als Letztes merken wir uns nun in ZP_TILEHEIGHT noch die Tilehöhe TILEHEIGHT. Dann holen wir uns hinter .loop1 die Tilebreite TILEWIDTH ins Y-Register. Bei .loop2 holen wir uns das nächste Tile-Zeichen ZP_TILEADR in den Akku und geben es auf den Bildschirm aus ZP_SCREENADR. Dann brauchen wir noch die Farbe, also holen wir uns die aktuelle Tile-Nr. ZP_TILENO ins X-Register. Da wir uns beim Export für „Attribute Table – Per Tile“ entschieden haben, können wir die Farbe direkt ab colors mit der Tile-Nr. auslesen. Die so ermittelte Farbe kopieren wir dann ins Farb-RAM. Danach verringern wir das Y-Register und wiederholen den Block .loop2, solange es positiv ist.

Sobald die komplette Zeile des Tiles verarbeitet wurde, müssen wir noch den Beginn der nächsten ermitteln.

 lda #40                            ;Start der nächsten Bildschirmzeile berechnen
 clc                                ;Carry löschen
 adc ZP_SCREENADR                   ;40 Zeichen zum LSB der Bildschirmadresse addieren
 sta ZP_SCREENADR                   ;und merken
 sta ZP_COLORRAMADR                 ;auch fürs COLORRAM
 lda ZP_SCREENADR+1                 ;ggf. Carry zum
 adc #0                             ;MSB addieren
 sta ZP_SCREENADR+1                 ;Ergebnis speichern
 adc #$d4                           ;Offset fürs Farb-RAM addieren
 sta ZP_COLORRAMADR+1               ;und speichern

Beginnen wir mit der Bildschirm- & Color-RAM-Adresse. Da die Bildschirmausgabe (und damit auch das Farb-RAM) 40 Zeichen breit ist, holen wir und die 40 erstmal in den Akku. Dann löschen wir das C-Flag für die Addition und addieren den Wert auf das LSB der Bildschirmadresse an ZP_SCREENADR. Das Ergebnis schreiben wir wieder zurück und übernehmen es auch für das LSB des Farb-RAMs ZP_COLORRAMADR. Dann addieren wir noch das eventuell gesetzte Carry-Flag zum MSB der Bildschirmadresse ZP_SCREENADR+1 und addieren anschließend noch den Offset $d4 für das MSB des Farb-RAMs ZP_COLORRAMADR+1.

Außerdem müssen wir auch noch den Beginn der nächsten Zeichenzeile des Tiles ZP_TILEADR erhöhen.

 lda #TILEWIDTH                     ;Anz. Zeichen in X-Richtung
 adc ZP_TILEADR                     ;zur Startadresse der Tiles addieren
 sta ZP_TILEADR                     ;und speichern
 lda #0
 adc ZP_TILEADR+1
 sta ZP_TILEADR+1
 dec ZP_TILEHEIGHT                  ;Tilehöhe verringern
 bne .loop1                         ;falls nicht 0 -> nochmal zu @loop1
 rts                                ;zurück zum Aufrufer

Dazu addieren wir nach bekanntem Muster einfach die Tilebreite TILEWIDTH zur Tileadresse ZP_TILEADR. Abschließend verringern wir die Tilehöhe in ZP_TILEHEIGHT um eins und springen, solange dies nicht Null ist, wieder nach .loop1. Ansonsten wird die Funktion verlassen.

Das wars auch schon, jetzt brauchen wir nur noch unsere Tabellen.

screenRow_LSB                       ;Hilfstabelle (LSB)
 !byte <(SCREENRAM+40*TILEHEIGHT*0) ;Hier wird für jede Tilezeile, die auf dem 
 !byte <(SCREENRAM+40*TILEHEIGHT*1) ;Bildschirm ausgegeben wird, das LSB der 
 !byte <(SCREENRAM+40*TILEHEIGHT*2) ;Startadresse gespeichert.
 !byte <(SCREENRAM+40*TILEHEIGHT*3) 
 !byte <(SCREENRAM+40*TILEHEIGHT*4) 
   
screenRow_MSB                       ;Hilfstabelle (MSB)
 !byte >(SCREENRAM+40*TILEHEIGHT*0) ;Wie eben, nur für das MSB
 !byte >(SCREENRAM+40*TILEHEIGHT*1)
 !byte >(SCREENRAM+40*TILEHEIGHT*2)
 !byte >(SCREENRAM+40*TILEHEIGHT*3) 
 !byte >(SCREENRAM+40*TILEHEIGHT*4) 

Wie ihr seht, nehmen wir für screenRow_… einfach die Startadresse des Bildschirmspeichers und addieren für jede Tilezeile 40*TILEHEIGHT*Zeile hinzu.

screenCol_Offset                    ;Hilfstabelle
 !byte TILEWIDTH*0                  ;Hier steht der Offset vom linken Rand
 !byte TILEWIDTH*1                  ;für jede Tilespalte, die auf dem Bildschirm
 !byte TILEWIDTH*2                  ;ausgegeben wird.
 !byte TILEWIDTH*3
 !byte TILEWIDTH*4
 !byte TILEWIDTH*5
 !byte TILEWIDTH*6
 !byte TILEWIDTH*7
 !byte TILEWIDTH*8
 !byte TILEWIDTH*9

In screenRow_… steht also an welcher Adresse die jeweilige Tilezeile beginnt. In der Breite benötigen wir 10 Tiles (zu je 4 Zeichen). Also legen wir in der Tabelle screenCol_Offset einfach den Offset vom linken Rand ab. Dazu müssen wir nur TILEWIDTH mit der jeweiligen X-Position (die Zählung beginnt bei 0) des Tiles multiplizieren.

Jetzt sollte sich das Programm erstellen lassen und auf dem Bildschirm der erste Screen erscheinen.

MAPWIDTH        = 100               ;Map: Anz. Tiles in X-Richtung
MAPHEIGHT       = 15                ;Map: Anz. Tiles in Y-Richtung
TILEWIDTH       = 4                 ;Tile: Anz. Zeichen (Breite)
TILEHEIGHT      = 4                 ;Tile: Anz. Zeichen (Höhe)
TILESIZE        = TILEWIDTH*TILEHEIGHT ;Tile: Anz. der Zeichen fürs ganze Tile
SCREENTILES_X   = 10                ;SCR: Anz. Tiles in X-Richtung
SCREENTILES_Y   = 5                 ;SCR: Anz. Tiles in Y-Richtung
SCREENRAM       = $0400             ;SCR: Adresse des Bildschirmspeichers
COLORRAM        = $d800             ;SCR: Adresse des Farb-RAMs

ZP_TILESY       = $02               ;Hilfsvariablen auf der Zeropage
ZP_TILESX       = $03
ZP_TILENO       = $04
ZP_TILEHEIGHT   = $05

ZP_MAPADR       = $10               ;Hilfsadressen (2 Bytes) auf der Zeropage
ZP_SCREENADR    = $12
ZP_COLORRAMADR  = $14
ZP_TILEADR      = $16



!zone main
;*** Startadresse 
*=$0801
;** BASIC-Zeile: 2018 SYS 2062
 !word main-2, 2018 
 !byte $9e
 !text " 2062"
 !byte $00,$00,$00

main
 lda #147                           ;Bildschrim löschen
 jsr $ffd2
 lda #6                             ;blau für          
 sta $d020                          ;Rahmen und
 sta $d021                          ;Hintergrund
 lda $d018                          ;Zeichensatz
 and #%11110001                 
 ora #%00001000                     ;nach $2000
 sta $d018                          ;legen
 lda $d016                          ;Multicolor
 ora #%00010000                     ;Textmodus
 sta $d016                          ;setzen
 lda #15                            ;hellgrau
 sta $d022                          ;für erste MC-Farbe
 lda #14                            ;hellblau
 sta $d023                          ;für die zweite MC-Farbe
 jsr drawMap                        ;Bildschirmausgabe
 jmp *                              ;Endlosschleife



!zone drawMap
drawMap
 lda #<map                          ;Adresse der Map
 sta ZP_MAPADR                      ;auf die Zeropage
 lda #>map
 sta ZP_MAPADR+1
 
 lda #0                             ;Zähler Tiles in Y-Richtung
 sta ZP_TILESY                      ;auf der Zeropage merken
.loop
 lda #SCREENTILES_X-1               ;Anz. Tiles für BS in X-Richtung (-1) 
 sta ZP_TILESX                      ;auf der Zeropage merken
.loop1
 ldy ZP_TILESX                      ;Position des nächsten Tiles ins Y-Reg.
 lda (ZP_MAPADR),Y                  ;Tile-Nr. aus der 'map.raw' holen
 jsr drawTile                       ;Tile zeichnen
 dec ZP_TILESX                      ;Zähler für Tiles in X-Richtung verringern
 bpl .loop1                         ;solange positiv -> nochmal

 lda #MAPWIDTH                      ;Start für die nächste Map-Zeile errechnen
 clc                                ;Carry löschen
 adc ZP_MAPADR                      ;LSB der MAP-Startadresse addieren
 sta ZP_MAPADR                      ;und speichern
 lda #0                         
 adc ZP_MAPADR+1                    ;ggf. Carry zum MSB der MAP-Startadresse addieren
 sta ZP_MAPADR+1                    ;und speichern
 inc ZP_TILESY                      ;Zähler für Tiles in Y-Richtung erhöhen
 lda ZP_TILESY                      ;zum Prüfen in den Akku
 cmp #SCREENTILES_Y                 ;mit der Anz. Tiles in Y-Richtung vergleichen
 bne .loop                          ;solange ungleich -> nochmal
 rts                                ;zurück zum Aufrufer


!zone drawTile
drawTile
 sta ZP_TILENO                      ;Tile-Nr. in der Zeropage merken
 ldy ZP_TILESY                      ;Tile-zeile (Y-Richtung) ins Y-Reg.
 lda screenRow_LSB,Y                ;LSB aus Hilfstabelle holen
 sta ZP_SCREENADR                   ;und auf die Zeropage damit
 sta ZP_COLORRAMADR                 ;auch fürs ColorRAM merken
 lda screenRow_MSB,Y                ;MSB aus der Hilfstabelle holen
 sta ZP_SCREENADR+1                 ;und auf der Zeropage merken
 ldx ZP_TILESX                      ;Offset in X-Richtung 
 lda screenCol_Offset,X             ;aus der Hilfstabelle holen
 clc                                ;Carry-Flag löschen
 adc ZP_SCREENADR                   ;und zum LSB addieren
 sta ZP_SCREENADR                   ;Ergebnis merken
 sta ZP_COLORRAMADR                 ;auch für das ColorRAM
 lda ZP_SCREENADR+1                 ;MSB von der Zeropage holen
 adc #0                             ;ggf. das C-Flag addieren
 sta ZP_SCREENADR+1                 ;und speichern
 adc #$d4                           ;MSB-Offset fürs ColorRAM addieren
 sta ZP_COLORRAMADR+1               ;und ab auf die Zero-Page
 lda #<tiles                        ;Startadresse der Tiles 
 sta ZP_TILEADR                     ;auf die Zeropage
 lda #>tiles
 sta ZP_TILEADR+1
 ldx ZP_TILENO                      ;Tile-Nr. ins X-Register
 beq .skip                          ;falls 0 -> weiter bei .skip
.loop
 lda #TILESIZE                      ;Anzahl der Zeichen je Tile
 clc                                ;zur Startadresse der Tiles
 adc ZP_TILEADR                     ;addieren
 sta ZP_TILEADR
 lda ZP_TILEADR+1
 adc #0
 sta ZP_TILEADR+1
 dex                                ;Tile-Nr. im X-Reg. verringern
 bne .loop                          ;falls nicht 0 -> nochmal zu .loop
.skip
 lda #TILEHEIGHT                    ;Tilehöhe 
 sta ZP_TILEHEIGHT                  ;auf der Zeropage merken
.loop1
 ldy #TILEWIDTH-1                   ;Tilebreite (-1) in Y-Register
.loop2
 lda (ZP_TILEADR),Y                 ;Zeichen vom Tile holen
 sta (ZP_SCREENADR),Y               ;und ausgeben
 ldx ZP_TILENO                      ;Tile-Nr. ins X-Register
 lda colors,X                       ;damit die Farbe holen
 sta (ZP_COLORRAMADR),Y             ;und ins Farb-RAM kopieren
 dey                                ;Y verringern
 bpl .loop2                         ;solange positiv -> nochmal zu .loop2
 lda #40                            ;Start der nächsten Bildschirmzeile berechnen
 clc                                ;Carry löschen
 adc ZP_SCREENADR                   ;40 Zeichen zum LSB der Bildschirmadresse addieren
 sta ZP_SCREENADR                   ;und merken
 sta ZP_COLORRAMADR                 ;auch fürs COLORRAM
 lda ZP_SCREENADR+1                 ;ggf. Carry zum
 adc #0                             ;MSB addieren
 sta ZP_SCREENADR+1                 ;Ergebnis speichern
 adc #$d4                           ;Offset fürs Farb-RAM addieren
 sta ZP_COLORRAMADR+1               ;und speichern 
 lda #TILEWIDTH                     ;Anz. Zeichen in X-Richtung
 adc ZP_TILEADR                     ;zur Startadresse der Tiles addieren
 sta ZP_TILEADR                     ;und speichern
 lda #0
 adc ZP_TILEADR+1
 sta ZP_TILEADR+1
 dec ZP_TILEHEIGHT                  ;Tilehöhe verringern
 bne .loop1                         ;falls nicht 0 -> nochmal zu @loop1
 rts                                ;zurück zum Aufrufer


 
screenRow_LSB                       ;Hilfstabelle (LSB)
 !byte <(SCREENRAM+40*TILEHEIGHT*0) ;Hier wird für jede Tilezeile, die auf dem 
 !byte <(SCREENRAM+40*TILEHEIGHT*1) ;Bildschirm ausgegeben wird, das LSB der 
 !byte <(SCREENRAM+40*TILEHEIGHT*2) ;Startadresse gespeichert.
 !byte <(SCREENRAM+40*TILEHEIGHT*3) 
 !byte <(SCREENRAM+40*TILEHEIGHT*4) 
   
screenRow_MSB                       ;Hilfstabelle (MSB)
 !byte >(SCREENRAM+40*TILEHEIGHT*0) ;Wie eben, nur für das MSB
 !byte >(SCREENRAM+40*TILEHEIGHT*1)
 !byte >(SCREENRAM+40*TILEHEIGHT*2)
 !byte >(SCREENRAM+40*TILEHEIGHT*3) 
 !byte >(SCREENRAM+40*TILEHEIGHT*4) 

screenCol_Offset                    ;Hilfstabelle
 !byte TILEWIDTH*0                  ;Hier steht der Offset vom linken Rand
 !byte TILEWIDTH*1                  ;für jede Tilespalte, die auf dem Bildschirm
 !byte TILEWIDTH*2                  ;ausgegeben wird.
 !byte TILEWIDTH*3
 !byte TILEWIDTH*4
 !byte TILEWIDTH*5
 !byte TILEWIDTH*6
 !byte TILEWIDTH*7
 !byte TILEWIDTH*8
 !byte TILEWIDTH*9


tiles
 !bin "tiles.raw"

colors
 !bin "attribs.raw"

map
 !bin "map.raw"

*=$2000
characters
 !bin "chars.raw"
So sollte das Erbebnis aussehen.
So sollte das Erbebnis aussehen.
Tabellen mit dem C64 Studio und ACME

C64 Studio & AMCE

Hier nun, wie oben angekündigt, ein etwas anderer Weg, um die Tabellen zu erzeugen. Das C64 Studio erlaubt euch die Verwendung von !for-Schleifen. Damit könnt ihr ganz einfach Tabellen, vom Assembler erstellen lassen. Werft einen Blick auf den Source, das sollte als Erklärung eigentlich schon reichen.
Außerdem findet ihr hier auch noch eine weitere Tabelle tilePos_…. Diese enthält die Startadresse des Tiles ab dem Label tiles. Dann brauchen wir nicht mehr umständlich zu rechnen, sondern können einfach mit der Tile-Nr. aus ZP_TILENO auf die Tabelle zugreifen und das LSB & MSB holen. Schaut euch die Funktion drawTile mal genauer an.
Die Syntax für die !for-Schleifen unterscheiden sich zwischen dem C64 Studio und ACME geringfügig.

C64 Studio:

tilePos_LSB
  !for x = 0 to TILECOUNT-1
    !byte <(tiles+x*TILESIZE)
  !end

ACME:

tilePos_LSB
  !for x, 0, TILECOUNT-1 {
    !byte <(tiles+x*TILESIZE)
  }

Das folgende Listing enthält die Syntax vom C64 Studio!

MAPWIDTH        = 100               ;Map: Anz. Tiles in X-Richtung
MAPHEIGHT       = 15                ;Map: Anz. Tiles in Y-Richtung
TILECOUNT       = 128               ;Anzahl der Tiles insgesamt
TILEWIDTH       = 4                 ;Tile: Anz. Zeichen (Breite)
TILEHEIGHT      = 4                 ;Tile: Anz. Zeichen (Höhe)
TILESIZE        = TILEWIDTH*TILEHEIGHT ;Tile: Anz. der Zeichen fürs ganze Tile
SCREENTILES_X   = 10                ;SCR: Anz. Tiles in X-Richtung
SCREENTILES_Y   = 5                 ;SCR: Anz. Tiles in Y-Richtung
SCREENRAM       = $0400             ;SCR: Adresse des Bildschirmspeichers
COLORRAM        = $d800             ;SCR: Adresse des Farb-RAMs

ZP_TILESY       = $02               ;Hilfsvariablen auf der Zeropage
ZP_TILESX       = $03
ZP_TILENO       = $04
ZP_TILEHEIGHT   = $05

ZP_MAPADR       = $10               ;Hilfsadressen (2 Bytes) auf der Zeropage
ZP_SCREENADR    = $12
ZP_COLORRAMADR  = $14
ZP_TILEADR      = $16



!zone main
;*** Startadresse 
*=$0801
;** BASIC-Zeile: 2018 SYS 2062
 !word main-2, 2018 
 !byte $9e
 !text " 2062"
 !byte $00,$00,$00

main
 lda #147                           ;Bildschrim löschen
 jsr $ffd2
 lda #6                             ;blau für          
 sta $d020                          ;Rahmen und
 sta $d021                          ;Hintergrund
 lda $d018                          ;Zeichensatz
 and #%11110001                 
 ora #%00001000                     ;nach $2000
 sta $d018                          ;legen
 lda $d016                          ;Multicolor
 ora #%00010000                     ;Textmodus
 sta $d016                          ;setzen
 lda #15                            ;hellgrau
 sta $d022                          ;für erste MC-Farbe
 lda #14                            ;hellblau
 sta $d023                          ;für die zweite MC-Farbe
 jsr drawMap                        ;Bildschirmausgabe
 jmp *                              ;Endlosschleife



!zone drawMap
drawMap
 lda #<map                          ;Adresse der Map
 sta ZP_MAPADR                      ;auf die Zeropage
 lda #>map
 sta ZP_MAPADR+1
 
 lda #0                             ;Zähler Tiles in Y-Richtung
 sta ZP_TILESY                      ;auf der Zeropage merken
.loop
 lda #SCREENTILES_X-1               ;Anz. Tiles für BS in X-Richtung (-1) 
 sta ZP_TILESX                      ;auf der Zeropage merken
.loop1
 ldy ZP_TILESX                      ;Position des nächsten Tiles ins Y-Reg.
 lda (ZP_MAPADR),Y                  ;Tile-Nr. aus der 'map.raw' holen
 jsr drawTile                       ;Tile zeichnen
 dec ZP_TILESX                      ;Zähler für Tiles in X-Richtung verringern
 bpl .loop1                         ;solange positiv -> nochmal

 lda #MAPWIDTH                      ;Start für die nächste Map-Zeile errechnen
 clc                                ;Carry löschen
 adc ZP_MAPADR                      ;LSB der MAP-Startadresse addieren
 sta ZP_MAPADR                      ;und speichern
 lda #0                         
 adc ZP_MAPADR+1                    ;ggf. Carry zum MSB der MAP-Startadresse addieren
 sta ZP_MAPADR+1                    ;und speichern
 inc ZP_TILESY                      ;Zähler für Tiles in Y-Richtung erhöhen
 lda ZP_TILESY                      ;zum Prüfen in den Akku
 cmp #SCREENTILES_Y                 ;mit der Anz. Tiles in Y-Richtung vergleichen
 bne .loop                          ;solange ungleich -> nochmal
 rts                                ;zurück zum Aufrufer


!zone drawTile
drawTile
 sta ZP_TILENO                  ;Tile-Nr. in der Zeropage merken
 ldy ZP_TILESY                  ;Tile-zeile (Y-Richtung) ins Y-Reg.
 lda screenRow_LSB,Y            ;LSB aus Hilfstabelle holen
 sta ZP_SCREENADR               ;und auf die Zeropage damit
 sta ZP_COLORRAMADR             ;auch fürs ColorRAM merken
 lda screenRow_MSB,Y            ;MSB aus der Hilfstabelle holen
 sta ZP_SCREENADR+1             ;und auf der Zeropage merken
 ldx ZP_TILESX                  ;Offset in X-Richtung
 lda screenCol_Offset,X         ;aus der Hilfstabelle holen
 clc                            ;Carry-Flag löschen
 adc ZP_SCREENADR               ;und zum LSB addieren
 sta ZP_SCREENADR               ;Ergebnis merken
 sta ZP_COLORRAMADR             ;auch für das ColorRAM
 lda ZP_SCREENADR+1             ;MSB von der Zeropage holen
 adc #0                         ;ggf. das C-Flag addieren
 sta ZP_SCREENADR+1             ;und speichern
 adc #$D4                       ;MSB-Offset fürs ColorRAM addieren
 sta ZP_COLORRAMADR+1           ;und ab auf die Zero-Page
 ldx ZP_TILENO                  ;Tile-Nr. ins X-Register
 lda tilePos_LSB,X              ;damit die Startadresse holen
 sta ZP_TILEADR                 ;und auf der Zero-Page speichern
 lda tilePos_MSB,X
 sta ZP_TILEADR+1
 lda #TILEHEIGHT                ;Tilehöhe
 sta ZP_TILEHEIGHT              ;auf der Zeropage merken
.loop1
 ldy #TILEWIDTH-1               ;Tilebreite (-1) in Y-Register
.loop2
 lda (ZP_TILEADR),Y             ;Zeichen vom Tile holen
 sta (ZP_SCREENADR),Y           ;und ausgeben
 ldx ZP_TILENO                  ;Tile-Nr. ins X-Register
 lda colors,X                   ;damit die Farbe holen
 sta (ZP_COLORRAMADR),Y         ;und ins Farb-RAM kopieren
 dey                            ;Y verringern
 bpl .loop2                     ;solange positiv -> nochmal zu @loop2
 lda #40                        ;Start der nächsten Bildschirmzeile berechnen
 clc                            ;Carry löschen
 adc ZP_SCREENADR               ;40 Zeichen zum LSB der Bildschirmadresse addieren
 sta ZP_SCREENADR               ;und merken
 sta ZP_COLORRAMADR             ;auch fürs COLORRAM
 lda ZP_SCREENADR+1             ;ggf. Carry zum
 adc #0                         ;MSB addieren
 sta ZP_SCREENADR+1             ;Ergebnis speichern
 adc #$D4                       ;Offset fürs Farb-RAM addieren
 sta ZP_COLORRAMADR+1           ;und speichern
 lda #TILEWIDTH                 ;Anz. Zeichen in X-Richtung
 adc ZP_TILEADR                 ;zur Startadresse der Tiles addieren
 sta ZP_TILEADR                 ;und speichern
 lda #0
 adc ZP_TILEADR+1
 sta ZP_TILEADR+1
 dec ZP_TILEHEIGHT              ;Tilehöhe verringern
 bne .loop1                     ;falls nicht 0 -> nochmal zu @loop1
 rts                            ;zurück zum Aufrufer


screenRow_LSB                       ;Hilfstabelle (LSB)
 !for row = 0 to SCREENTILES_Y-1    ;Hier wird für jede Tilezeile, die auf dem 
   !byte <(SCREENRAM+40*TILEHEIGHT*row) ;Bildschirm ausgegeben wird, das LSB der 
 !end                               ;Startadresse gespeichert.
 
screenRow_MSB                       ;Hilfstabelle (MSB)
 !for row = 0 to SCREENTILES_Y-1    ;Wie eben, nur für das MSB
   !byte >(SCREENRAM+40*TILEHEIGHT*row)
 !end

screenCol_Offset                    ;Hilfstabelle
 !for col = 0 to SCREENTILES_X-1    ;Hier steht der Offset vom linken Rand
   !byte TILEWIDTH*col              ;für jede Tilespalte, die auf dem Bildschirm
 !end                               ;ausgegeben wird.

tilePos_LSB
  !for x = 0 to TILECOUNT-1
    !byte <(tiles+x*TILESIZE)
  !end
tilePos_MSB
  !for x = 0 to TILECOUNT-1
    !byte >(tiles+x*TILESIZE)
  !end


tiles
 !bin "tiles.raw"

colors
 !bin "attribs.raw"

map
 !bin "map.raw"

*=$2000
characters
 !bin "chars.raw"

So nun könnt ihr mal weiter daran arbeiten, eure Tilemaps auf den Bildschirm zu bringen. Wie bereits gesagt, hier sind noch einige Verbesserungen nötig bzw. ratsam. Versucht erstmal das Programm an andere Map- und Tilegrößen anzupassen. Exportiert auch mal die Farben in einem anderen Format und ändert das Programm. Dann solltet ihr euch überlegen, wie ihr z. B. einen beliebigen Ausschnitt der Map auf den Bildschirm bringt. Zu guter Letzt stehen natürlich auch noch Punkte wie Bildschirmumschaltung oder Scrolling auf dem Programm.

Es gibt viel zu tun, pac… ihr wisst schon 😉 .


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

Loading...


Zurück

10 Gedanken zu „Char Pad: Tilemaps erstellen & anzeigen“

  1. Hallo Jörn,
    in der aktuellen Version des CharPad gibt es beim Export nicht mehr die vier Dateien,
    sondern fünf. Chars, Tiles, Map und CharSetAttributes und TileSetAttributs.
    Wie unterscheiden diese beiden sich von Deiner beschriebenen “einen” Attributs Datei?
    Danke!
    Chris

  2. Mal eine Frage:

    Wieviel Rasterzeit benötigt deine Routine?
    Ich bin selbst aktuell daran eine Tile-MAP Darstellung zu programmieren (bin Anfänger). Aktuell verbrät meine Routine ca. 100 Rasterzeilen für eine Darzustellen Tilezeile (2×2 CharTiles bei 50 Tiles breitem Screen (ein “virtueller Screen mit 50×30 Zeichen)).
    Deswege würde mich mal interessieren, wie deine läuft…

    1. Hi,
      da der Beitrag nur das Prinzip verdeutlichen soll, habe ich die Rasterzeit nie beachtet / berechnet. Da hier also die Geschwindigkeit nicht im Vordergrund stand, würde ich das Ganze, für den Praxiseinsatz, nochmal überdenken. Ich kann jetzt auch nicht mit Zeiten aus anderen Projekten dienen, sorry.
    2. Hallo Dirk,
      Ich bastel auch gerade an sowas. Ich habe es hinbekommen, 10 Tile-Zeilen in rund 26 ms zu zeichnen (mit einstellbarer Farbe pro Tile). Wenn man knapp hinter dem Rasterstrahl anfängt zu zeichnen geht es sich aus ohne dass einen der Rasterstrahl überholt. Was es für mich schwer gemacht hat sind die “ungeraden” Fällen, wo eine Zeile oder Spalt mit halben 2×2-Blöcken auftritt.

  3. Um beim Thema zu bleiben: was wäre eine effiziente Methode herauszufinden “wo” auf dem Bildschirm eine Kollision, zB zwischen einem Sprite und einem Char mit dem Material $F stattgefunden hat?

    Rechnet man die X,Y Sprite Position bei Laufzeit um, in Char Zeile/Spalte? Dann wenn eine Kollision stattfindet, weiss man anhand von Zeile/Spalte in der Map, um welchen Char es sich handelt, und mit dem Wert in den Attribs lesen, ob das Material, msb, von diesem Char gesetzt ist?

    Das scheint mir ein langer weg. Vielleicht geht es einfacher. 🙂

    1. Eine Umrechnung der Sprite-Position brauchst du. Dabei kannst du natürlich wieder auf Tabellen zurückgreifen.

      Wie man mit diesen Infos weiter verfährt, hängt nun davon ab, wie die Map aufgebaut ist und wie sie verwendet wird.
      Du kannst das Material beim Char Pad z. B. in die einzelnen Zeichen kopieren lassen und dann als Attribute exportieren. Dann kannst du mit dem über die Sprite-Position ermittelten Zeichencode einfach darauf zugreifen.

      Verwendest du eher statische Bildschirme, kannst du z. B. auch eine Kollisionsmap einsetzen. Dazu kopierst du die Attribute / das Material parallel zum Color-RAM in einen weiteren Speicherbereich und kontrollierst dort mit der berechneten Zeichenposition welches Attribut vorliegt.

      Natürlich kann man auch vom Zeichen ‘zurück’ über das Tile zum Attribut / Material gehen, das ist aber schon sehr umständlich.

      1. Vielen Dank für die Info. Ich werde deinen Vorschlag ausprobieren eine separate Kollisionsmap aufzustellen. (die neue Benennung der ZP-Konstanten ist auch jetzt ganz klar, danke) 😉

  4. Super Tutorial, wie immer. Sehr komplett und hat viele offene Fragen beantwortet. Ich gebe es aber zu, erst beim zweiten Anlauf alles verstanden zu haben, da ich mich immer wieder in den ähnlich benannten Konstanten verlor (ZP_Adr/Help1,2,3..). Mit doppelter Konzentration ging es aber. 😉

    1. Man wächst mit seinen Aufgaben 😉

      War mir beim Schreiben auch schon bewußt, dass diese Namensgebung ‘grenzwertig’ ist.
      Habe mich jetzt für andere Namen entschieden und hoffe damit wird es etwas lesbarer.

Schreibe einen Kommentar

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

Protected by WP Anti Spam