Keep watching the sky
Nach einer etwas längeren Vorbereitungszeit, soll nun ein blauer Himmel über dem Canyon entstehen.
Da die Volour den Sinkflug von Space Commander Rick Razor bemerkt haben und in erhöhter Alarmbereitschaft sind haben sie auch noch ein Laser-Raster über den Canyon gelegt, so dass das Landungsschiff den Canyon nicht mehr verlassen kann!
ACHTUNG: Das Wissen aus den Beiträgen Interrupts, Ein erster NMI und besonders alles, was zu Der Rasterzeileninterrupt gehört, wird vorausgesetzt!!
Das „Rahmen“-Programm
Wer sich das Programm aus dem letzten Beitrag Landung 001 im ZIP-File einmal angesehen hat, wird das folgende Listing schon kennen. Hier gibt es nichts wirklich Neues, alles wurde so ähnlich schon beim Puzzle gezeigt.
;########################################################################### ;### Autor : Jörn Kierstein ### ;### Erstellt: 04.01.2014 ### ;### IDE : C64 Studio 5.3 ### ;### ### ;### (c) 2014, 2018 by www.retro-programming.de ### ;########################################################################### !to "landung.prg",cbm ;*** Konstanten einbinden !source "..\_Includes_\system_const.asm" !source "..\_Includes_\love_const.asm" !zone BASIC_Start ;*** Startadresse *=$0801 ;*** BASIC-Zeile !word main-2 !word 2018 !text $9E," 2062",$00,$00,$00 !zone Main ;******************************************************************************* ;*** Einstieg ins Assemblerprogramm ;******************************************************************************* main jsr initLander ;Level starten gameloop jmp gameloop ;Hauptschleife !zone initLander ;******************************************************************************* ;*** Leveldaten zurücksetzen ;******************************************************************************* ;*** Übergabe: - ;******************************************************************************* ;*** Rückgabe: - ;******************************************************************************* ;*** ändert : A, X, Y, SR ;******************************************************************************* initLander jsr drawCanyon rts !zone drawCanyon ;******************************************************************************* ;*** Den Canyon zeichnen ;******************************************************************************* ;*** Übergabe: - ;******************************************************************************* ;*** Rückgabe: - ;******************************************************************************* ;*** ändert : A, X, Y, SR ;******************************************************************************* drawCanyon lda #COLOR_ORANGE sta VIC_BORDERCOLOR lda #COLOR_DARKGREY sta VIC_BACKGROUNDCOLOR ;*** Canyon anzeigen ldx #<screenCanyon ;LSB des Canyonbildes ins X-Register ldy #>screenCanyon ;MSB des Canyonbildes ins Y-Register jsr drawScreen ;Bild ausgeben rts ;zurück ;******************************************************************************* ;*** Allgemeine Funktionen einbinden ;******************************************************************************* !source "..\_Includes_\love_lib.asm" !zone Data ;******************************************************************************* ;*** Daten ;******************************************************************************* ;*** Der Canyon screenCanyon !media "Canyon.charscreen",charcolor
Neu sind hier eigentlich nur die !source-Anweisungen, die aber auch schon in Landung 001 erwähnt wurden. Sie dienen nur dazu, das Programm etwas übersichtlicher zu halten, indem wir, wie bereits erwähnt, mehrfach verwendete Konstanten und Routinen auslagern.
Der Rest sollte eigentlich kein Problem darstellen. Es wird direkt zum Start nach initLander gesprungen, um die Leveldaten zurückzustellen. Aktuell wird nur der Canyon gezeichnet. Auch die Funktionen fürs Zeichnen wurden beim Puzzle bereits verwendet. Nach der Rückkehr vom Zeichnen, bleibt das Programm einfach in einer Endlos-Schleife bei gameloop hängen.
Den Raster-IRQ einrichten
Um die Rahmenfarbe unterschiedlich einzufärben, wird jetzt fast 1:1 das Flaggen-Beispiel verwendet. Der einzige Unterschied ist die Verteilung der Farben und dass eine Farbe blinkt.
Dazu werden drei Konstanten eingefügt:
RASTERIRQ_SKY = $00 ;ab hier die Himmelfarbe RASTERIRQ_LASER = $30 ; das 'Laser-Raster' RASTERIRQ_CANYON = $32 ; die Canyon-Farbe
In diesen Konstanten steht jeweils, wo die Farbe für den Himmel, Laser und Canyon beginnen soll.
Danach wird die Initialisierung, wie unten gelb markiert, eingefügt.
main jsr initLander ;Level starten ;*** Raster-IRQ einrichten sei ;IRQs sperren lda #<landerIRQ ;Routine unserer Rasten-Routine sta JT_IRQVECTOR ;im den RAM-Vector speichern lda #>landerIRQ ;auch das MSB sta JT_IRQVECTOR+1 lda #%00000001 ;Dem VIC-II mitteilen, dass er sta VIC_IRQMASK ;Raster-IRQs auslösen soll lda #RASTERIRQ_SKY ;Erster Raster-IRQ für den Himmel sta VIC_RASTERROWPOS lda VIC_CONTROLREG1 ;Höchstes BIT für die Rasterzeile and #%01111111 ;löschen sta VIC_CONTROLREG1 cli ;IRQs wieder erlauben gameloop jmp gameloop ;Hauptschleife
Die neuen Konstanten wurden zu system_const.asm hinzugefügt, das ja zu Beginn mittels !source eingebunden wurde.
Das Programm sperrt zunächst alle Interrupts und legt die Adresse der Interrupt-Routine landerIRQ im RAM-Vector JT_IRQVECTOR = $0314 / 15 ab. Dann wird die aktuelle Interruptmaske VIC_IRQMASK = $d01a so gesetzt, dass der VIC-II erstmal nur Raster-IRQs erzeugt. Anschließend muss der VIC-II noch wissen, wann er überhaupt einen Rasterinterrupt auslösen soll. Der Himmel beginnt bei Zeile 0 RASTERIRQ_SKY = $0, also diesen Wert ins Register für die Rasterzeile VIC_RASTERROWPOS = $d012 schreiben. Da die Rasterzeile aber neun Bits benötigt, zur Sicherheit auch noch dieses löschen. Es befindet sich in Bit 7 vom Kontrollregister 1 VIC_CONTROLREG1 = $d011. Schon ist die Initialisierung des Raster-IRQs abgeschlossen und die Interrupts können wieder freigegeben werden.
Der eigentliche Rasterinterrupt
Jetzt fehlt nur noch die landerIRQ-Routine. Diese soll IRQs vom VIC-II verarbeiten und andere an die System-Funktion weiterleiten.
!zone landerIRQ ;******************************************************************************* ;*** Interrupthandler ;*** ;*** Rasterinterrupts werden hier verarbeitet, ;*** andere Interrupts werden an die System-Routine $EA31 weitergeleitet. ;******************************************************************************* ;*** Übergabe: - ;******************************************************************************* ;*** Rückgabe: - ;******************************************************************************* ;*** ändert : - ;******************************************************************************* landerIRQ lda VIC_IRQSTATUS ;prüfen ob der IRQ vom VIC-II kam bmi doRasterIRQ ;falls ja zur Raster-Routine lda CIA1_IRQCONTROL ;sonst CIA-IRQ bestätigen cli ;Interrupts erlauben jmp ROM_IRQFUNCTION ;und zur System-Funktion springen
Die Funktion holt sich direkt den Interruptstatus VIC_IRQSTATUS = $d019 in den Akku. Falls der VIC-II einen Interrupt ausgelöst hat, dann ist dort das höchste Bit gesetzt, also kann mit einer Prüfung, ob der Wert, der in den Akku geholt wurde, negativ ist, unterschieden werden, ob die Routine zur System-Funktion springt oder den Interrupt selbst verarbeitet. Ist der Akku nicht negativ, dann werden die CIA1-Interrupts durch lesen des CIA1-Kontrollregisters CIA1_IRQCONTROL = $dc0d bestätigt, weitere Interrupts über cli erlaubt und schließlich zur ROM-Routine gesprungen ROM_IRQFUNCTION = $ea31.
doRasterIRQ sta VIC_IRQSTATUS ;VIC-IRQ bestätigen lda VIC_RASTERROWPOS ;aktuelle Rasterzeile in den Akku bne .laser ;Wenn nicht 0, dann auf 'Laser' prüfen lda #COLOR_LIGHTBLUE ;sonst hellblau in den Akku sta VIC_BORDERCOLOR ;die Rahmenfarbe auf darauf setzen lda #RASTERIRQ_LASER ;nächsten Raster-IRQ beim Laser sta VIC_RASTERROWPOS jmp .exit ;und IRQ verlassen
Wenn das höchste BIT in VIC_IRQSTATUS gesetzt war, landet das Programm bei doRasterIRQ. Aktuell findet keine weitere Unterscheidung statt, welcher IRQ es eigentlich war, da z. Zt. nur Raster-IRQs vom VIC-II möglich sind. Das Verarbeiten der VIC-Interrupts muss diesem mitgeteilt werden, dazu wird der aktuelle Register-Inhalt wieder nach VIC_IRQSTATUS zurückgeschrieben, dieser befindet sich ja noch im Akku. Anschließend wird die aktuelle Rasterzeile in den Akku geladen. Hier habe ich mir einen Vergleich mit cmp gespart, setzt man RASTERIRQ_SKY aber auf einen anderen Wert als 0, dann muss natürlich ein Vergleich her. So reicht es jetzt allerdings mit bne zu prüfen, ob der Rasterstrahl in Zeile 0 ist, wenn nicht, wird zur nächsten Prüfung nach .laser gesprungen. Sonst setzt das Programm die Rahmenfarbe auf hellblau und teilt dem VIC dann mit, dass der nächste Raster-IRQ beim erreichen der Zeile in RASTERIRQ_LASER gewünscht ist. Das wars dann und es geht zum .exit.
.laser cmp #RASTERIRQ_LASER ;Aktuelle Rasterzeile=Beginn des Lasers? bne .canyon ;falls nicht, weiter zum Canyon inc LaserFlash ;'Farbe' fürs Laser-Raster erhöhen, lda LaserFlash ;in den Akku holen sta VIC_BORDERCOLOR ;und Rahmenfarbe damit setzen lda #RASTERIRQ_CANYON ;nächsten Raster-IRQ beim Canyon sta VIC_RASTERROWPOS jmp .exit ;IRQ verlassen
Wenn der Strahl nicht in der Startzeile für den Himmel ist, dann landet man bei .laser. Hier wird geprüft, ob der Rasterstrahl in der Zeile für die Lasersperre RASTERIRQ_LASER ist. Falls nicht, geht es zum letzten Abschnitt nach .canyon, sonst läuft fast alles wie eben ab. Aber da die Lasersperre gefährlich wirken soll, wird sie zum Blinken gebracht. Nun klappt es natürlich nicht, einfach $d020 zu erhöhen. Da ab Zeile 0 ja die Rahmenfarbe immer auf hellblau gesetzt wird, würde sich durch ein inc VICBORDERCOLOR die Farbe immer nur auf grau (14 für hellblau +1 = 15 für hellgrau) ändern. Daher wird hier eine Variable im Speicher LaserFlash laufend erhöht und als Rahmenfarbe verwendet. Dann wird noch dem VIC die Zeile für den letzten Raster-IRQ RASTERIRQ_CANYON mitgeteilt und zum .exit gesprungen.
.canyon lda #COLOR_ORANGE ;hier beginnt auf jeden Fall der Canyon sta VIC_BORDERCOLOR ;also Canyon-Farbe in den Rahmen lda #RASTERIRQ_SKY ;und nächsten Raster-IRQ für den Himmel sta VIC_RASTERROWPOS
Traf die Position des Rasterstrahls bei keiner der vorigen Prüfungen zu, dann geht es bei .canyon weiter. Hier wird nun einfach die Rahmenfarbe, auf die des Canyons gesetzt und für den nächsten Raster-IRQ wieder der Beginn des Himmels eingetragen. Danach läuft das Programm direkt zu .exit weiter.
.exit pla ;Register zurückholen tay ;Y pla tax ;X pla ;Akku rti ;IRQ verlassen
Beim EXIT werden einfach die Register wieder vom Stack geholt und der Interrupt mit rti verlassen.
Die Hilfsvariable LaserFlash für das Blinken des Lasers, wurde weiter unten, bei Data eingefügt.
LaserFlash !byte $00 ;Rahmenfarbe fürs Laser-Raster
Jetzt sieht alles wie gewünscht aus:
;########################################################################### ;### Autor : Jörn Kierstein ### ;### Erstellt: 04.01.2014 ### ;### IDE : C64 Studio 5.3 ### ;### ### ;### (c) 2014, 2018 by www.retro-programming.de ### ;########################################################################### !to "landung.prg",cbm ;*** Konstanten einbinden !source "..\_Includes_\system_const.asm" !source "..\_Includes_\love_const.asm" RASTERIRQ_SKY = $00 ;ab hier die Himmelfarbe RASTERIRQ_LASER = $30 ; das 'Laser-Raster' RASTERIRQ_CANYON = $32 ; die Canyon-Farbe !zone BASIC_Start ;*** Startadresse *=$0801 ;*** BASIC-Zeile !word main-2 !word 2018 !text $9E," 2062",$00,$00,$00 !zone Main ;******************************************************************************* ;*** Einstieg ins Assemblerprogramm ;******************************************************************************* main jsr initLander ;Level starten ;*** Raster-IRQ einrichten sei ;IRQs sperren lda #<landerIRQ ;Routine unserer Rasten-Routine sta JT_IRQVECTOR ;im den RAM-Vector speichern lda #>landerIRQ ;auch das MSB sta JT_IRQVECTOR+1 lda VIC_IRQMASK ;Raster-IRQs auslösen soll ora #%00000001 ;Dem VIC-II mitteilen, dass er sta VIC_IRQMASK ;Raster-IRQs auslösen soll lda #RASTERIRQ_SKY ;Erster Raster-IRQ für den Himmel sta VIC_RASTERROWPOS lda VIC_CONTROLREG1 ;Höchstes BIT für die Rasterzeile and #%01111111 ;löschen sta VIC_CONTROLREG1 cli ;IRQs wieder erlauben gameloop jmp gameloop ;Hauptschleife !zone initLander ;******************************************************************************* ;*** Leveldaten zurücksetzen ;******************************************************************************* ;*** Übergabe: - ;******************************************************************************* ;*** Rückgabe: - ;******************************************************************************* ;*** ändert : A, X, Y, SR ;******************************************************************************* initLander jsr drawCanyon rts !zone drawCanyon ;******************************************************************************* ;*** Den Canyon zeichnen ;******************************************************************************* ;*** Übergabe: - ;******************************************************************************* ;*** Rückgabe: - ;******************************************************************************* ;*** ändert : A, X, Y, SR ;******************************************************************************* drawCanyon lda #COLOR_ORANGE sta VIC_BORDERCOLOR lda #COLOR_DARKGREY sta VIC_BACKGROUNDCOLOR ;*** Canyon anzeigen ldx #<screenCanyon ;LSB des Canyonbildes ins X-Register ldy #>screenCanyon ;MSB des Canyonbildes ins Y-Register jsr drawScreen ;Bild ausgeben rts ;zurück !zone landerIRQ ;******************************************************************************* ;*** Interrupthandler ;*** ;*** Rasterinterrupts werden hier verarbeitet, ;*** andere Interrupts werden an die System-Routine $EA31 weitergeleitet. ;******************************************************************************* ;*** Übergabe: - ;******************************************************************************* ;*** Rückgabe: - ;******************************************************************************* ;*** ändert : - ;******************************************************************************* landerIRQ lda VIC_IRQSTATUS ;prüfen ob der IRQ vom VIC-II kam bmi doRasterIRQ ;falls ja zur Raster-Routine lda CIA1_IRQCONTROL ;sonst CIA-IRQ bestätigen cli ;Interrupts erlauben jmp ROM_IRQFUNCTION ;und zur System-Funktion springen doRasterIRQ sta VIC_IRQSTATUS ;VIC-IRQ bestätigen lda VIC_RASTERROWPOS ;aktuelle Rasterzeile in den Akku bne .laser ;Wenn nicht 0, dann auf 'Laser' prüfen lda #COLOR_LIGHTBLUE ;sonst hellblau in den Akku sta VIC_BORDERCOLOR ;die Rahmenfarbe auf darauf setzen lda #RASTERIRQ_LASER ;nächsten Raster-IRQ beim Laser sta VIC_RASTERROWPOS jmp .exit ;und IRQ verlassen .laser cmp #RASTERIRQ_LASER ;Aktuelle Rasterzeile=Beginn des Lasers? bne .canyon ;falls nicht, weiter zum Canyon inc LaserFlash ;'Farbe' fürs Laser-Raster erhöhen, lda LaserFlash ;in den Akku holen sta VIC_BORDERCOLOR ;und Rahmenfarbe damit setzen lda #RASTERIRQ_CANYON ;nächsten Raster-IRQ beim Canyon sta VIC_RASTERROWPOS jmp .exit ;IRQ verlassen .canyon lda #COLOR_ORANGE ;hier beginnt auf jeden Fall der Canyon sta VIC_BORDERCOLOR ;also Canyon-Farbe in den Rahmen lda #RASTERIRQ_SKY ;und nächsten Raster-IRQ für den Himmel sta VIC_RASTERROWPOS .exit pla ;Register zurückholen tay ;Y pla tax ;X pla ;Akku rti ;IRQ verlassen ;******************************************************************************* ;*** Allgemeine Funktionen einbinden ;******************************************************************************* !source "..\_Includes_\love_lib.asm" !zone Data ;******************************************************************************* ;*** Daten ;******************************************************************************* LaserFlash !byte $00 ;Rahmenfarbe fürs Laser-Raster ;*** Der Canyon screenCanyon !media "Canyon.charscreen",charcolor
Oops, da war ich doch etwas redseliger als geplant. Beim nächsten Mal geht es dann wieder um Sprites. Das Landungsschiff wird in die Szene aufgenommen.
Schritt 12 - Landung 002