Es weihnachtet…
Da wir heute den 2. Advent haben und ich mich langsam in die Winterpause verabschiede, wollen wir noch kurz für etwas weihnachtliche Stimmung auf dem C64 sorgen.
Schneefall auf dem C64
Wir wollen es also auf dem C64 schneien lassen. Für die Schneeflocken verwenden wir ein Sprite, das in X- & Y-Richtung verdoppelt wird. Also ein neues Projekt erstellt und los gehts…
RASTER = $30 ;Hier den 1. Raster-IRQ auslösen WAITINIT = $0a ;Schneefallgeschwindigkeit ;*** Startadresse *=$0801 ;*** BASIC-Zeile: 2013 SYS 2064:NEW !word main-2, 2013 !byte $9e !text " 2064:" !byte $a2,$00,$00,$00 main sei ;IRQs sperren lda #<myIRQ ;Adresse unserer Routine in sta $0314 ;den RAM-Vektor lda #>myIRQ sta $0315 lda #%00000001 ;Raster-IRQs vom VIC-II aktivieren sta $d01a lda #RASTER ;Hier soll unsere Interrupt auftreten sta $d012 lda $d011 ;Zur Sicherheit höchstes BIT and #%01111111 ;für die Rasterzeile löschen sta $d011 lda #%01111111 ;Timer-IRQs abschalten sta $dc0d lda $dc0d ;zur Sicherheit bestätigen lda #%00000001 ;Sicherheitshalber auch den sta $d019 ;Raster-IRQ bestätigen jsr initSprites ;Sprites vorbereitsn lda #$0d ;hellgrün sta $d020 ;als Rahmenfarbe lda #$05 ;grün sta $d021 ;für den Hintergrund jsr setCharColor ;Zeichenfarbe auf Rot setzen cli ;Interrupts erlauben rts ;zurück zum BASIC
Der Anfang beinhaltet wieder die Einrichtung eines Rasterzeilen-Interrupts, die von uns gewünschte Rasterzeile wird über die Konstante RASTER bestimmt. Außerdem initialisieren wir die Sprites jsr initSprites und sorgen mit roter Zeichenfarbe jsr setCharColor und grünem Hintergrund $d021 sowie Rahmen $d020 für etwas Weihnachtsflair.
Sprites initialisieren
initSprites lda #sprite/64 ;Nr. für das Sprite ermitteln sta $07f8 ;pro 'Zeile' 7 Sprites, immer dasselbe sta $07f9 sta $07fa sta $07fb sta $07fc sta $07fd sta $07fe lda #$01 ;weiß für die 7 Sprites sta $d027 sta $d028 sta $d029 sta $d02a sta $d02b sta $d02c sta $d02d XPOS=24 lda #XPOS ;X-Position sta $d000 lda #XPOS+48 sta $d002 lda #XPOS+(2*48) sta $d004 lda #XPOS+(3*48) sta $d006 lda #XPOS+(4*48) sta $d008 XPOS2=(XPOS+(5*48))-256 ;neue X-Posi ab Position 256 (wg. max. X-BIT) lda #XPOS2 sta $d00a lda #XPOS2+48 sta $d00c lda #%01100000 ;max. X für Sprite 5&6 setzen sta $d010 lda #$00 sta $d01c ;alle einfarbig sta $d01b ;und vor der Hintergrundgrafik lda #%01111111 sta $d01d ;doppelte Breite sta $d017 ;und Höhe sta $d015 ;sichtbar schalten rts ;zurück zum Aufrufer
Hier bereiten wir unsere Sprites vor. Um die gesamte Anzeige, die bekanntlich 320 x 200 Punkte aufweist, mit Sprites zu füllen, benötigen wir, bei doppelter Größe, sieben Sprites in der X-Richtung (48 * 7 = 336) und fünf in Y-Richtung (5 * 42 = 210). Sorgen wir also dafür, dass die Sprites, für die jeweilige Zeile, korrekt positioniert und eingerichtet werden.
Mit lda #sprite/64 berechnen wir die Nr. des 64-Byte-Blocks für unser Sprite. Da wir für alle sieben dasselbe Sprite verwenden, schreiben wir diese Adresse für die Sprites 0 bis 6 in die Sprite-Pointer. Dann setzten wir die Farbe dieser Sprites auf weiß und setzen auch noch die X-Position auf die richtigen Werte. Die Sprites beginnen links mit Sprite-0 ab dem Pixel 24 und dann folgt alle 48 Bildpunkte das nächste Sprite. Beachten müssen wir natürlich, dass die letzten beiden Sprites an einer X-Position über 255 beginnen.
Abschließend sorgen wir noch für die Anzeige von einfarbigen Sprites, die vor dem Hintergrund erscheinen und in X- & Y-Richtung verdoppelt sind.
Da die Sprites später nur noch auf der Y-Achse verschoben werden, reicht es diese Funktion einmalig beim Programmstart aufzurufen.
Zeichenfarbe setzen
setCharColor ldx #$00 ;256 Schleifendurchläufe lda #$02 ;Zeichenfarbe: rot nextColor sta $d800,X ;1. Page im Color-RAM sta $d900,X ;2. sta $da00,X ;3. sta $dB00-24,X ;4. Page (24 Zeichen weniger) dex bne nextColor sta $286 ;auch die aktuelle Zeichenfarbe auf rot rts ;zurück zum Aufrufer
Um die Zeichenfarbe auf rot zu setzen, schreiben wir einfach ins COLOR-RAM für jedes Zeichen den Wert für rot. Die Schleife solltet ihr vom BS-Löschen kennen, nur dass wir hier eben ins Farb-RAM schreiben. Damit auch neue Zeichen rot erscheinen, schreiben wir den Wert abschließend auch nach $286, hier merkt sich das Betriebssystem die aktuelle Zeichenfarbe.
Der Interrupt
myIRQ ldx RowCnt ;aktuelle Schnee-Zeile ins X-Register lda YPos,X ;neue Y-Position clc ;Sprites etwas nach dem IRQ-Anzeigen adc #$02 sta $d001 ;für die 7 Sprites sta $d003 sta $d005 sta $d007 sta $d009 sta $d00b sta $d00d lda #%00000001 ;IRQ bestätigen sta $d019 cpx #$04 ;wurden alle Sprite-Zeilen dargestellt? beq Exit ;falls ja -> @exit: inx ;sonst erhöhen stx RowCnt ;und speicheren lda YPos,X ;nächste IRQ-Zeile holen sta $d012 ;Zeile für nächsten Rasterinterrupt pla ;dann Register wiederherstellen tay pla tax pla rti ;und IRQ verlassen
Unser Rasterzeilen-Interrupt holt sich zunächst die aktuelle Zeile ins X-Register. Dann werden alle sieben Sprites auf die passende Y-Position gesetzt. Die IRQ-Zeile haben wir der Einfachheit in einer Tabelle bei YPos abgelegt. Für die Sprites addieren wir noch einen kleinen Offset von zwei Zeilen. Die ermittelte Position wird natürlich für alle Sprites gesetzt. Dann bestätigen wir den Raster-IRQ und prüfen, ob dies die letzte Spritezeile war. Falls ja geht es bei Exit weiter, sonst erhöhen wir RowCnt, holen die nächste Rasterzeile und verlassen den Interrupt. Wir lösen also für jede Spritezeile alle 42 Zeilen einen neuen Interrupt aus.
exit lda #$00 ;alle Sprite-Zeilen wurden dargestellt sta RowCnt ;also auf 0 zurücksetzen lda #RASTER sta $d012 ;Zeile für nächsten Rasterinterrupt
Wurden alle Spritezeilen dargestellt, dann beginnen wir wieder von vorne. Dazu wird RowCnt zurück auf 0 gestellt und der Rasterzeilen-Interrupt auf die erste Zeile laut unserer Konstanten RASTER gesetzt.
Die Schneeflocken sollen sich natürlich noch etwas bewegen, dazu kopieren wir die Spritedaten einfach, wie beim Color-Cycling zyklisch. Um die Animation zu timen, verwenden wir einen weiteren Zähler Wait.
dec Wait ;Warteschleife verringern bne toSystem ;falls nicht abgelaufen -> toSystem lda #WAITINIT ;sonst Startwert sta Wait ;zurücksetzen ldy Sprite+63 ;letztes BYTE des Sprites merken ldx #63 ;63 BYTES verschieben nextByte lda Sprite-1,X ;BYTE holen sta Sprite,X ;und kopieren dex ;Schleifenzähler verringern bne nextByte ;solange größer 0 -> nextByte sty Sprite ;sonst, letztes BYTE an die erste Stelle toSystem jmp $ea31 ;zur System-Routine
Wir verringern Wait also einfach. Ist der Wert größer null, dann verlassen wir den Interrupt bei toSystem direkt Richtung System-Routine. Wenn der Zähler auf 0 geht, dann setzen wir ihn wieder auf den Anfangswert, kopieren die Spritedaten und laufen ebenfalls zum Sprung zur System-Routine durch.
Variablen
Am Ende folgen natürlich noch unsere Variablen und die Spritedaten.
Wait !byte WAITINIT ;Wartezeit für die Schneegeschwindigkeit YPos ;Sprite-Y Positionen !byte RASTER,RASTER+42 !byte RASTER+84,RASTER+126 !byte RASTER+168 RowCnt !byte 0 ;aktuelle Sprite-Zeile !align 63,0 Sprite ;das 'Schnee'-Sprite !byte $00,$00,$00 !byte $40,$00,$00 !byte $00,$00,$00 !byte $00,$00,$00 !byte $00,$80,$10 !byte $00,$00,$00 !byte $00,$00,$00 !byte $00,$00,$00 !byte $00,$01,$00 !byte $00,$00,$00 !byte $00,$00,$00 !byte $08,$00,$00 !byte $00,$20,$00 !byte $00,$00,$00 !byte $00,$00,$00 !byte $00,$00,$40 !byte $00,$00,$00 !byte $00,$08,$00 !byte $00,$00,$00 !byte $10,$00,$00 !byte $00,$00,$04 !byte $01
Hier findet ihr auch unsere Tabelle mit den Y-Positionen (ok, eigentlich ist es die Rasterzeile für den Interrupt).
Schneeflöckchen, Weißröckchen…
Ich hoffe euch ist aufgefallen, dass wir hier 35 Sprites gleichzeitig verwenden.
Um die Stimmung noch etwas zu steigern findet ihr im Download eine Fassung mit Musik. Dazu sind, neben dem Musikstück, nur zwei weitere Befehle notwendig.
Weihnachten 2013
Ich mache nun etwas Pause und hoffe das erste Jahr auf dieser Seite hat euch gefallen und ihr besucht sie auch 2014 wieder.
Frohe Weihnachten und einen guten Rutsch ins neue Jahr,
Jörn