Erstellt: 9. Mai 2013 (zuletzt geändert: 1. November 2021)

Puzzle 005

Den Hintergrund bewegen

C64 Studio

Eben haben wir unser Puzzle so geändert, dass wir die Sprites über Tabellen positionieren, aber der Hintergrund des jeweiligen Puzzleteils bliebt unverändert. Das sollten wir jetzt schleunigst ändern.

Vorbereitung

Wir wollen einfach die Quarate für den Spritehintergrund abhängig von unserer Tabelle tileOrder, in der ja die Puzzleteile für das jeweilige Feld zufinden sind, zeichnen. Um jetzt die Teile in das jeweilige Feld zu zeichnen, benötigen wir natürlich deren Position. Werfen wir dazu noch mal einen Blick auf unser Spielfeld:

Die Startpositionen der Puzzlehintergründe

Der Grafik könnt ihr nun entnehmen, wo (Spalte X, Zeile Y) jeweils die obere/linke Ecke eines Feldes liegt. Dass hier alles Symetrisch ist, ist übrigens reiner Zufall bzw. meinem Symetriewahn geschuldet 🙂 . Wir werden nun wieder die Tabelle tileOrder von hinten nach vorne abklappern. Dadurch beginnen wir im 9. Feld, setzen dann die 5*5 Zeichen und kommen dann zu Feld 8, 7, usw. Ich habe bewußt auf jegliche Optimierung verzichtet und hoffe, dass ich euch nun im Source das genaue Vorgehen erklären kann. Die obige Grafik werde ich für meine Ausführungen gleich noch häufiger bemühen.

Eine weitere Funktion hinzufügen

Fügt die Routine am Besten hinter dem rts von spriteSetPositions ein.

;*******************************************************************************
;*** Hintergründe der einzelnen Puzzleteile zeichnen
;*** gerade Sprite-Nr.   = heller Hintergrund
;*** ungerade Sprite-Nr. = dunkler Hintergrund
;*** $ff                 = freies Feld
;*******************************************************************************
;*** Übergabe: Abhängig von tileorder:
;*******************************************************************************
;*** Rückgabe: -
;*******************************************************************************
;*** ändert  : A, X, Y, SR
;*******************************************************************************
!zone drawTiles
drawTiles
 lda #<(SCREENRAMADR+(15*40+15))    ;ZP-Adresse für den BS-Speicher
 sta ZP_SCREENRAMADR                ;auf die linke obere Ecke
 lda #>(SCREENRAMADR+(15*40+15))    ;des 9. Feldes setzen
 sta ZP_SCREENRAMADR+1
 lda #<(COLORRAMADR+(15*40+15))     ;ZP-Adresse für das Color-RAM
 sta ZP_COLORRAMADR                 ;auf die linke obere Ecke
 lda #>(COLORRAMADR+(15*40+15))     ;des 9. Feldes setzen
 sta ZP_COLORRAMADR+1

Hinter drawTiles beginnen wir zunächst damit, die Adresse der linken oberen Ecke des neunten Feldes zu ermitteln und auf der Zero-Page zu speichern. Die Variablen sollten euch aus der Funktion drawScreen bekannt vorkommen. Um die Adresse zu berechnen, werft noch mal einen Blick auf die Grafik. Wie ihr seht, beginnt das 9. Feld in Zeile 15 (denkt daran, dass auch hier die Zählung bei 0 beginnt)! Wir können also einfach 15 * 40 Zeichen überspringen. Danach stehen wir auf dem ersten Zeichen, von Zeile 15. Da das Feld in Spalte 15 (auch hier startet die Zählung bei 0) beginnt, müssen wir noch 15 weitere Zeichen überspringen (denkt dran, wir standen bereits auf dem 1. Feld), somit landen wir also bei einem Offset von 600 + 15 = 615 oder $267. Diese Werte könntet ihr auch direkt einsetzen, ich finde es aber schöner, den Assembler die Berechnung ausführen zu lassen. Denkt daran, dass solche Berechnungen keinen Einfluss auf die Ausführungsgeschwindigkeit des fertigen Programms haben. Der Assembler übernimmt bei der Programmerstellung einfach nur das Ergebnis der Formel. Da wir auch die Farbe setzten müssen, addieren wir diesen Offset nicht nur zur Startadresse des Bildschimspeichers, sondern auch zu der des COLOR-RAM.

Zunächst benötigen wir eine neue Konstante ZP_LOOPHELPER, die wir für alle möglichen Schleifen-Operationen verwenden können. Für eine etwas bessere Performance verwenden wir die Zero-Page, wir reservieren uns mal die nächsten 8-Bytes ab der Adresse, auf die die Konstante verweist, sprich die letzten acht Bytes der Zero-Page.

Also einfach die Konstante oben hinzufügen…

ZP_LOOPHELPER       = $f8     ;Die letzten 8 BYTES für Schleifen reservieren

…und dann geht es weiter, mit unserer Routine.

 ldx #$08                          ;max. 9 Felder
.loop
 txa                               ;X-Register auf dem
 pha                               ;Stack merken
 ldy #COLOR_GREY                   ;Erstmal grau ins Y-Register
 lda tileOrder,X                   ;was steht im Feld -> Akku
 bmi .skip                         ;wenn freies Feld -> .skip
 and #%00000001                    ;gerades oder ungerades Sprite?
 bne .skip1                        ;wenn ungerade, Farbe behalten -> .skip1
 ldy #COLOR_LIGHTGREY              ;Hintergrund für gerades Sprite
.skip1
 lda #160                          ;invertiertes Leerzeichen in den Akku
 jmp .skip2
.skip                              ;freies Feld
 lda #102                          ;Karriertes Zeichen in den Akku
 ldy #COLOR_DARKGREY
.skip2
 sta ZP_LOOPHELPER                 ;Zeichen auf die Zero-Page
 sty ZP_LOOPHELPER+1               ;Farbe auf die Zero-Page

Als nächstes laden wir unsere Schleifenvariable für die Felder ins X-Register. Da wir das X-Register aber gleich für etwas Anderes benötigen, sichern wir es hinter .loop erstmal auf dem Stack. Im Y-Register wollen wir uns die Farbe für das jeweilige Puzzleteil merken, wir gehen zu Beginn von grau für die ungeraden Puzzleteile aus. Dann holen wir uns das Teil, dass auf dem letzten Feld liegt (wir schauen uns ja gerade den ersten Durchlauf der Schleife an und beginnen beim letzten Feld) in den Akku. Ist der Wert negativ, handelt es sich um das leere Feld und wir springen zu .skip. Dort laden wir das Zeichen für das karrierte leere Feld in den Akku und die Farbe dunkelgrau ins Y-Register. Ist der Wert nicht negativ, dann prüfen wir, ob es sich um ein gerades oder ungerades Puzzleteil handelt. Ist es ungerade, springen wir direkt zu .skip1; ist es gerade, ändern wir die Farbe im Y-Register auf hellgrau und landen dann auch bei .skip1. Hier wird nun das invertierte Leerzeichen in den Akku geladen und wir springen zu .skip2. Dort haben wir im Akku also unser Zeichen und im Y-Register die Farbe für das Puzzleteil. Da wir die beiden Register brauchen, speichern wir deren Werte auf der Zero-Page und beginnen mit dem Zeichnen.

 ldx #$04                           ;Zeile ins X-Register
.loop1
 ldy #$04                           ;Spalte in Y-Register
.loop2
 lda ZP_LOOPHELPER                  ;Zeichen holen
 sta (ZP_SCREENRAMADR),Y            ;ab in den BS-Speicher
 lda ZP_LOOPHELPER+1                ;Farbe holen
 sta (ZP_COLORRAMADR),Y             ;und ins Farb-RAM
 dey                                ;Spalte verringern
 bpl .loop2                         ;solange positiv nächstes Zeichen ausgeben

Da wir unser X-Register für die Hauptschleife auf dem Stack gesichert haben, können wir es jetzt für eine weitere Schleife benutzten. Wir legen in X die Anzahl der Zeilen und im Y-Register die Anzahl der Zeichen je Zeile ab. Da unsere Puzzleteile 5*5 Zeichen groß sind, schreiben wir also jeweils eine 4 (wir prüfen mit bpl) ins Register. Dann holen wir das Zeichen in den Akku und schreiben es per Y-nach-indizierter Adressierung in den Bildschirmspeicher, das Gleiche machen wir danach mit der Farbe und dem COLOR-RAM. Dann verringern wir das Y-Register und vervollständigen die Zeile.

Sobald die Y-Schleife abgearbeitet wurde, haben wir die erste Zeile von rechts nach links gezeichnet. Jetzt müssen in der darunterliegenden Zeile weitermachen. Dazu ist es wichtig, dass ihr euch erinnert, dass wir bei ZP_SCREENRAMADR die Adresse der linken oberen Ecke des 9. Feldes gespeichert haben ($0400 + $267).

 clc                     
 lda ZP_SCREENRAMADR                ;LSB der BS-Speicherpostion holen
 adc #$28                           ;eine Zeile (40 BYTES) addieren
 sta ZP_SCREENRAMADR                ;und wieder speichern
 bcc .skip3                         ;wenn kein Carry weiter springen
 inc ZP_SCREENRAMADR+1              ;sonst MSB erhöhen

Wir addieren jetzt auf das LSB unserer Adresse in ZP_SCREENRAMADR die 40 Zeichen, für eine ganze Zeile. Wenn das Carry-Flag gelöscht ist, springen wir weiter, sonst erhöhen wir das MSB um eins.

.skip3                              ;das Gleiche wie eben, nur fürs Farb-RAM
 clc
 lda ZP_COLORRAMADR
 adc #$28
 sta ZP_COLORRAMADR
 bcc .skip4
 inc ZP_COLORRAMADR+1
.skip4
 dex                                ;Zeilenzähler verringern
 bpl .loop1                         ;solange positiv, nächste Zeile

Auch beim Farb-RAM addieren wir 40. Zum Schluß verringern wir das X-Register, in dem wir hier ja unsere Zeilen zählen und zeichnen noch die nächsten vier Zeilen.

Jetzt ist ein komplettes Feld fertig, wir müssen also zum Beginn des nächsten wechseln. Wenn wir z. B. gerade das 9. Feld gezeichnet haben, dann befinden wir uns nun in der Zeile 20 (nach jeder Zeile haben wir 40 addiert) und in Spalte 15 (die Zeichen für eine Zeile werden absteigend von rechts nach links ausgegeben). Wir müssen nun also fünf Zeilen nach oben und dann noch 5 Zeichen nach links (also 5 * 40 + 5 = 205 Zeichen abziehen)…

 sec
 lda ZP_SCREENRAMADR                ;nun die Startposition vom Feld
 sbc #$CD                           ;links berechnen, dazu 205 abziehen
 sta ZP_SCREENRAMADR                ;da wir eben bereits 40 addiert haben
 bcs .skip5
 dec ZP_SCREENRAMADR+1
.skip5                              ;auch fürs Farb-RAM
 sec
 lda ZP_COLORRAMADR
 sbc #$CD
 sta ZP_COLORRAMADR
 bcs .skip6
 dec ZP_COLORRAMADR+1

…und genau das hat dieser Abschnitt für den Bildschirmspeicher und das Farb-RAM gemacht.

Kommen wir endlich zum Schluss von drawTiles

.skip6
 pla                                ;Stack wieder zurück
 tax                                ;ins X-Register
 cmp #$06                           ;untere 3 Felder fertig?
 beq .rowUp                         ;falls ja, einen Block nach oben
 cmp #$03                           ;mittlere drei Felder fertig?
 bne .skip7                         ;falls nein, nächstes Feld
 jmp .rowUp                         ;sonst einen Block nach oben
.skip7
 dex                                ;X (jetzt wieder Feld-Nr.) verringern
 bpl .loop                          ;schon alle 9 Felder verarbeitet?
 rts                                ;zurück

Jetzt holen wir unseren Zähler für die Felder wieder vom Stack (s. oben) und kontrollieren dann, ob wir alle drei Felder in der unteren oder mittleren Reihe gezeichnet haben. Sollte das der Fall sein, dann müssen wir zum Beginn des 6. oder 3. Feldes (s. Grafik ganz oben) springen. Zum Schluß zählen wir die Feld-Nr. im X-Register herunter und beginnen wieder weiter oben mit dem nächsten Feld oder sind fertig und verlassen die Routine.

Solltet ihr euch jetzt fragen, warum .rowUp hinter dem rts folgt, dann könnt ihr den Block ja gerne mal vors .skip7 verschieben und euch ggf. nochmal die Branch-Befehle anschauen.

.rowUp
 sec
 lda ZP_SCREENRAMADR                ;immer wenn drei Felder gezeichnet wurden,
 sbc #$b9                           ;müssen wir einen Block nach oben, dazu 
 sta ZP_SCREENRAMADR                ;185 abziehen 
 bcs .skip8                         ;Achtung oben wurden bereits 205 subtrahiert!
 dec ZP_SCREENRAMADR+1
.skip8                              ;auch hier wieder das gleiche fürs Farb-RAM
 sec
 lda ZP_COLORRAMADR
 sbc #$b9
 sta ZP_COLORRAMADR
 bcs .skip7
 dec ZP_COLORRAMADR+1
 jmp .skip7                         ;weiter mit dem nächsten Feld

Haben wir z. B. die untere Reihe (Feld 9, 8 und 7) vollständig gezeichnet, dann befinden wir uns in Zeile 15 in der ersten Spalte. Wie ihr euch erinnert, haben wir nach jedem Feld den Beginn des links davon liegenden berechnet, das wurde auch nach Feld 7 gemacht und wir sind dann im Niemandsland gelandet. Um jetzt zur linken oberen Ecke von Feld 6 zu gelangen müssen wir vier ganze Zeilen und 25 Zeichen, also (4 * 40 + 25 = 185 Zeichen) abziehen. Das machen wir natürlich, wie jedes Mal, für den Bildschirmspeicher und das Farb-RAM. Von hier müssen wir fürs nächste Feld per jmp .skip7 zurück zur Hauptroutine springen.

Damit wir die Tiles auch sehen, fehlt natürlich noch der Aufruf von drawTiles. Fügt in die Funktion loadSprites, direkt hinter jsr spriteSetPositions die folgende Zeile ein:

 jsr drawTiles                      ;Hintergründe zeichnen

Damit wäre dies auch erledigt. Wer nun fürchtet, dass das alles zu langsam ist, es sieht ja schließlich sehr aufwendig aus, den kann ich beruhigen: Es mag nicht sonderlich schnell sein, für diesen Zweck ist es aber mehr als ausreichend. Wer sich das Projekt herunterlädt, der findet im Source sogar eine Funktion slowDown, mit der ihr die Ausgabe verlangsamen könnt, damit ihr nochmal genau seht, wie die Felder gezeichnet werden. Ihr müsst nur oben im Source, vor der Konstanten SLOWDOWN, das Semikolon löschen oder vom Diskettenimage „LOVE SLOW“ laden.

Da wir die Felder jetzt selbst zeichnen, habe ich sie aus unserem Bildschirm „Background.charscreen“ im Char Screen Editor wieder gelöscht.

Das Spielfeld im Editor entfernt.

Wenn ihr das Programm jetzt mit dem Ende von Puzzle 004 vergleicht, dann seht ihr unseren Fortschritt.

Vergleich mit dem vorherigen Beitrag.

Da sind wir auch schon wieder am Ende angelangt, als nächstes sollten wir das Puzzle endlich mal vom Programm automatisch durchwürfeln lassen.

Unsere Routine ist jetzt so groß geworden, dass ein bedingter Sprung
an den Anfang zu .loop über 128 Bytes entfernt wäre, wenn wir
.rowUp auch noch in die Hauptschleife aufnehmen. Aber das
Ende der Funktion ist nicht so weit entfernt und wir können daher
unsere benötigten Zeilen dahin auslagern.

Hier wieder der Download:


Schrott!!Naja...Geht so...Ganz gut...SUPER! (7 Bewertungen | Ø 4,86 von 5 | 97,14%)

Loading...


ZurückWeiter

5 Gedanken zu „Puzzle 005“

  1. Der Build in dieser Phase geht schon wieder nicht mehr im CBM Prg Studio 3.0. Sachen wie “lda #<SCREENRAMADR+$267" werden als invalid operand angezeigt, und die cheap labels funktionieren hier auch nicht mehr.
    Ich dachte ursprünglich es läge an meinem Code, jedoch bekomme ich mit deiner Datei genau die selben fehler wenn ich einen Build machen will.

    1. Es macht die Sache zwar nicht besser, aber es ist kein Problem von 3.0.0.

      Die beschriebenen Fehler gehen auf Umstellungen zurück, die ab Version 2.8.0 Einzug ins CBM prg Studio hielten.
      Das meiste habe ich damals überarbeitet, aber anscheinend nicht alles. Sorry!

      Damit du schnell weiter kommst…
      Um das ‘lda #>SCREENRAMADR+$267‘-Problem zu beheben füge einfach die Zeile ‘Operator Calc‘ am Programmbeginn ein.

      Die Sichtbarkeit der Cheap-Label wurde ab 2.8.0 geändert. Daher führt

      @loop:
      ...
      beq neuesLabel:
      ...
      neuesLabel:
      ...
      bne @loop:

      zu einem Fehler. Cheap-Label sind nur bis zum nächsten ‘normalen’ Label sichtbar. Du kannst also einfach aus neuesLabel: ein CheapLabel @neuesLabel: machen.

      Auch hier ist es wieder interessant, wie viele die Seite in der Zwischenzeit gelesen und das ZIP heruntergeladen haben, ohne etwas zu sagen.
      Daher vielen Dank für die Rückmeldung! Ich schaue es mir demnächst nochmal an und korrigiere die entsprechenden Passagen und Downloads.

        1. Gern geschehen, die Beispiele sollen ja funktionieren, sonst macht das alles keinen Sinn.

          Ich habe jetzt auch den Rest des Puzzles überarbeitet (es betraf nur noch die Downloads).

Schreibe einen Kommentar

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

Protected by WP Anti Spam