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

Puzzle 009

Mal verliert man, mal gewinnen die Anderen 😉

C64 Studio

Wir wollen nun einen Timer einbauen. Ich habe mich, entgegen der ersten Planung, dazu entschieden, auf Interrupts beim Puzzle zu verzichten. Wir werden uns also selbst um alles kümmern müssen.

Vorbereitungen

Als Erstes habe ich unseren Puzzle-Bildschirm so geändert, dass wir rechts einen Zeitbalken erhalten. Mein Zeitbalken ist vier Zeichen breit und 20 Zeilen hoch. Ganz unten wird darauf hingewiesen, was passiert, wenn die Zeit abgelaufen ist.

Der Zeitbalken
Der Zeitbalken

Um später Sieg oder Niederlage anzuzeigen, fügen wir einfach zwei Funktionen zu unserem Programm, hinter der Routine puzzleMainLoop, hinzu.

;*******************************************************************************
;*** Der Spieler hat gewonnen
;*******************************************************************************
;*** Ãœbergabe: -
;*******************************************************************************
;*** Rückgabe: -
;*******************************************************************************
;*** ändert  : A, SR
;*******************************************************************************
!zone puzzleSolved
puzzleSolved
 lda #COLOR_GREEN                   ;grün für den Sieg
 sta VICBORDERCOLOR                 ;als Rahmenfarbe
 jmp *                              ;Endlosschleife



;*******************************************************************************
;*** Der Spieler hat verloren
;*******************************************************************************
;*** Ãœbergabe: -
;*******************************************************************************
;*** Rückgabe: -
;*******************************************************************************
;*** ändert  : A, SR
;*******************************************************************************
!zone gameOver
gameOver
 lda #COLOR_RED                     ;Beim Ablauf der Zeit, rot
 sta VICBORDERCOLOR                 ;als Rahmenfarbe
 jmp *                              ;Endlosschleife

Wie ihr seht, machen die beiden Funktionen erstmal nichts Anderes, als den Rahmen grün beim Lösen des Puzzles bzw. rot bei Ablauf der Zeit einzufärben. Stutzig werdet ihr evtl. beim Befehl jmp *. Nach dem * folgt meistens eine vorzeichenbehaftete Zahl, wie bei *-3, die die Sprungweite angibt. So könnt ihr z. B. Label sparen, indem ihr direkt die gewünschte Anzahl Bytes für den Sprung angebet. Meistens wird dies mit bedingten Sprüngen verwendet, diese benützen bekanntlich die relative Adressierung.
Mit * ohne weitere Angaben greift man auf die Adresse des Befehls zu, wir springen hier also direkt wieder zum jmp * und produzieren eine Endlosschleife. Die Verwendung von *-3 kann natürlich zu Problemen führen. Fügt ihr Zeilen hinzu oder entfernt / ändert welche, müsst ihr daran denken, auch diese Sprünge zu kontrollieren und ggf. die Byte-Anzahl anzupassen.

Die Zeit läuft ab

Wir wollen nun den Zeitbalken in regelmäßigen Abständen verringern, dazu benötigen wir zunächst zwei neue Variablen. Fügt diese einfach, wie so häufig, hinter calc16Bit ein.

;*** Restzeit, bis zur Niederlage
timer
 !byte $14

;*** Timerintervall
timerStep
 !byte $02

In timer steht unsere verbleibende Restzeit. Da unser Zeitbalken 20 Zeilen hoch ist, lassen wir timer auch mit 20 beginnen. Um flexibler zu sein, fügen wir noch die Variable timerStep ein. Wir prüfen gleich, ob die hier hinterlegte Anzahl Sekunden verstrichen ist, um dann unseren timer herunterzuzählen.

Wir benötigen noch zwei neuen Konstanten, am Besten hinter CIA1_B:

CIA1_CLOCK_TENTH    = $dc08         ;Adresse zehntel Sekunden (Uhrzeit) im CIA1
CIA1_CLOCK_SECONDS  = $dc09         ;Adresse Sekunden (Uhrzeit) im CIA1

Diese Konstanten zeigen auf die Speicherstellen für die Sekunden und zehntel Sekunden der internen Uhr des C64, aus dem CIA1. Man kann diese nicht nur lesen, sondern auch setzen. Genau dies machen wir gleich. Wir setzen die Uhr auf Null und prüfen, ob die von uns gewünschte Zeit verstrichen ist. Da wir nur die Sekunden benötigen, setzen wir auch nur die Werte für die Sekunden und zehntel auf Null.

Fügen wir als nächstes, hinter die Routinen fürs Gewinnen / Verlieren eine weitere drawTimer, zum Zeichnen unseres Zeitbalkens, ein.

;*******************************************************************************
;*** Zeitbalken zeichnen, wenn timerStep * 1 Sekunde abgelaufen sind
;*** und das Zeitintervall timer herunterzählen.
;*******************************************************************************
;*** Ãœbergabe: -
;*******************************************************************************
;*** Rückgabe: -
;*******************************************************************************
;*** ändert  : A, X, Y, SR
;*******************************************************************************
!zone drawTimer
drawTimer
 lda CIA1_CLOCK_SECONDS             ;Sekunden in den Akku
 cmp timerStep                      ;ist unser Zeitintervall verstrichen?
 bne .exit                          ;falls nicht, wieder zurück
 lda #<(COLORRAMADR+32)             ;Startadresse in die Zero-Page
 sta ZP_COLORRAMADR
 lda #>(COLORRAMADR+32)
 sta ZP_COLORRAMADR+1
 lda #$14                           ;Unser Zeitbalken ist 20 Einheiten hoch
 sec                                ;Carry für SBC setzen
 sbc timer                          ;akt. Timer abziehen
 tax                                ;und nach X kopieren
 dec timer                          ;aktuellen Timer herunterzählen
 bmi gameOver                       ;wenn die Zeit um ist -> GAME OVER
.loop
 lda ZP_COLORRAMADR                 ;Startadresse fürs Ändern der jeweiligen
 clc                                ;Zeile (neue Farbe) errechnen
 adc #$28                           ;Dazu addieren wir für jedes verstrichene
 sta ZP_COLORRAMADR                 ;Zeitinterval 40 auf die Zero-Page-Adresse
 lda ZP_COLORRAMADR+1               ;um eine Zeile nach unten zu wandern.
 adc #$00                           ;ggf. auch das MSB korrigieren
 sta ZP_COLORRAMADR+1
 dex                                ;Schleifenzähler verringern
 bpl .loop                          ;und weiter addieren, solange positiv
 lda COLOR_DARKGREY                 ;Neue Farbe in den Akku
 ldy #$03                           ;der Balken ist max. vier Zeichen breit
.loop1
 sta (ZP_COLORRAMADR),Y             ;Farbe setzen
 dey                                ;nächstes Zeichen
 bpl .loop1                         ;solange Y positiv ist
 lda #$00                           ;nur die Sekunden 
 sta CIA1_CLOCK_SECONDS             ;wieder auf Null stellen
.exit
 rts                                ;zurück

Wenn ihr euch die Funktion anseht, sollte als Erstes auffallen, dass wir den Zeitbalken überhaupt nicht zeichnen. Er wird mit unserem Spielfeld ausgegeben, wir ändern nur die Farbe, aber schauen wir uns das mal im Detail an.
Zunächst holen wir uns die Sekunden in den Akku und vergleichen diese mit dem in timerStep hinterlegten Wert. Stimmen die nicht überein, dann sind wir erstmal fertig. Ist das Intervall erreicht, dann legen wir die Adresse des Farb-RAMs auf der Zero-Page ab. Dabei addieren wir gleich 32, damit unsere Startadress eine Zeile über dem Beginn des Farbbalkens liegt. Als nächstes berechnen wir, wie viele Intervalle bereits verstrichen sind. Dazu holen wir die max. Anzahl (20) in den Akku und ziehen den Wert aus timer ab. Anschließend kopieren wir die errechnete Anzahl ins X-Register, für unsere folgende Schleife. Vorher verringern wir timer und springen zu gameOver, falls die Zeit abgelaufen ist. Wenn noch Zeit übrig ist, addieren wir für jedes verstrichene Intervall 40 auf die Zero-Page-Adresse, um unsere zu ändernde Zeile zu finden. Sobald wir diese haben, laden wir den Akku mit der neuen Farbe und das Y-Register mit 3 (wir prüfen auf positiv und ändern so vier Zeichen). Mit der Y-nach-indizierten Adressierung, ändern wir jetzt die Farbe der vier Zeichen des Balkens (das gilt auch für die Spitze, obwohl dort nur zwei Zeichen verwendet werden). Zum Schluß setzen wir die Sekunden wieder auf Null und verlassen dann die Funktion.

Den Aufruf der drawTimer-Funktion fügt ihr direkt vor das jmp puzzleMainLoop in puzzleMainloop ein (s. gelbe Zeile).

puzzleMainLoop                      ;Start der Endlosschleife
 jsr checkInput                     ;Joysticks prüfen & Eingaben verarbeiten
 jsr drawTimer                      ;ggf. den Zeitbalken aktualisieren
 jmp puzzleMainLoop                 ;Endlosschleife

Damit unsere Uhr korrekt startet, sollten wir vor puzzleMainLoop die Sekunden und zehntel Sekunden auf Null setzen.

 lda #$00                           ;Uhr auf NULL stellen
 sta CIA1_CLOCK_SECONDS             ;Sekunden und
 sta CIA1_CLOCK_TENTH               ;Zehntel reichen uns

Bevor wir das Programm testen, sollten wir noch eine letzte Kleinigkeit ändern. Sucht in checkInput nach der Zeile jsr performInput und ändert den Abschnitt wie hier markiert:

 beq .exit                          ;wenn die Richtung verboten ist, ENDE
 jsr performInput                   ;hier ist alles OK, also verarbeiten
 jsr checkPuzzle                    ;nach jedem Zug prüfen, ob es gelöst wurde
 beq .exit                          ;steht eine 0 im Akku, dann nicht gelöst
 jmp puzzleSolved                   ;Das Puzzle wurde gelöst!
.exit
 rts                                ;zurück

Damit haben wir es mal wieder geschafft, wenn ihr das Programm startet, dann sollte die Zeit ablaufen und das Programm aufs Gewinnen / Verlieren reagieren.

Die Zeit läuft ab, trotzdem gewonnen!

Das Puzzle ist nun fast fertig. Im nächsten Beitrag werden wir noch dafür sorgen, dass der Spieler es nochmal versuchen kann.


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

Loading...


ZurückWeiter

2 Gedanken zu „Puzzle 009“

  1. Servus!

    Ich muss zuerst mal ein Kompliment für deine Arbeit aussprechen. Das ist mit Abstand das Beste, das ich je gesehen habe. Wenn es so was auch noch für den Amiga gäbe, dann wären die langweiligen Sonntag Nachmittage Geschichte 😉

    Ich habe da eine Frage, der Timer sollte ja eigentlich warten, bis der Sekundenzähler auf 2 steht, so wie es unser timer Intervall vorgibt, das wären dann gesamt 40 Sekunden. Bei mir läuft das aber derart schnell ab, dass in ca. 10 Sekunden alles vorbei ist…
    Liegt das daran, dass Vice viel schneller läuft als ein originaler C64?

    Ich schaffe es nicht, den Monitor von Vice geschickt mit Breakpoints aus CBM prg Studion zu beschicken. Wenn du mal Zeit hast, mit der du nicht weißt was anzufangen 😉 könntest du darüber berichten. Ich denke da wäre nicht nur mir geholfen.

    Nochmals Danke für deine Arbeit, schlichtweg GEWALTIG!!!!

    1. Danke für die ‘Blumen‘.

      VICE kann zwar schneller laufen, das musst du aber im Menü einstellen. Normalerweise steht in der Titelleiste die Geschwindigkeit. Dort sollte 100% 50fps stehen (es kann durchaus mal um 3% schwanken). Tritt das Problem mit deinem eigenen Source oder auch mit ‘meinem’ D64-Image auf?

      Ich ziehe mittlerweile das C64 Studio vor. Dort kannst du direkt in der IDE tracen, das erleichtert vieles.

Schreibe einen Kommentar

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

Protected by WP Anti Spam