Einen Text pixelweise anzeigen
Da es zur Zeit viel zu heiß ist und ich mich nur schwer konzentrieren kann, kommt hier mal wieder ein sehr simpler Effekt.
Möchte man den Blick auf eine Textzeile ziehen, dann ist es evtl. nicht so vorteilhaft, diese direkt als ganzes anzuzeigen. Befinden sich mehrere Infos auf dem Bildschirm, dann geht die Zeile evtl. unter. In solchen Fällen bietet es sich an, etwas Bewegung ins Spiel zubringen und so den Blick darauf zulenken.
Eine Textzeile zeichenweise ausgeben
Diese Aufgabe sollte eigentlich jeder selbst bewerkstelligen können, da sie aber Basis für unseren Effekt ist hier ein kleines Beispielprogramm.
;*** Startadresse *=$0801 ;*** BASIC-Zeile: 2018 SYS 2062 !word main-2, 2018 !byte $9e !text " 2062" !byte $00,$00,$00 main sei ;IRQs sperren lda #<irq ;LSB sta $0314 lda #>irq ;MSB sta $0315 ;in den RAM-Vektor jsr clrScreen ;BS löschen cli ;Interrupts wieder erlauben jmp * ;Endlosschleife ;*** eigene Interrupt-Routine irq dec charCounter ;Warteschleife verringern bne irq0 ;wenn größer null -> irq0 ldx charPos ;Zeichenposition ins X-Reg. lda infotext,X ;Zeichen in den Akku beq irqExit ;NULL = Ende! sta $0428,X ;Zeichen ausgeben lda #$08 ;Warteschleife wieder sta charCounter ;zurücksetzen inc charPos ;Zeichenposition erhöhen irq0 irqExit jmp $ea31 ;weiter zur System-Routine ;*** Bildschirm löschen clrScreen ldx #$00 ;Schleifenzähler (256 Zeichen je Page) lda #" " ;alles mit Leerzeichen füllen nextChar sta $0400,X ;1. Page des BS-Speichers sta $0500,X ;2. Page sta $0600,X ;3. Page sta $0700-24,X ;4. Page (-24 wegen der Sprite-Register) dex ;Schleifenzähler verringern bne nextChar ;Solange größer 0 -> nextChar rts ;sonst, zurück ;Textzeile $00 = ENDE infotext !scr "* (c) 2018 by www.retro-programming.de *" !byte 0 ;Zähler für Zeichenbreite = 8 Pixel charCounter !byte 8 ;Nr. des nächsten Zeichen charPos !byte 0
Für das Timing hängen wir uns in den bekannten Timer-Interrupt und legen die Adresse unserer IRQ-Routine ganz einfach an $0314/15 ab, außerdem löschen wir noch den Bildschirm clrScreen.
Die Interrupt-Funktion ist dann relativ simpel. Da 60 Zeichen in der Sekunde doch etwas schnell ist, verzögern wir die Ausgabe mit einem kleinen Zähler charCounter. Erreicht dieser null, dann holen wir die Position des nächsten Zeichens charPos ins X-Register und laden damit das zugehörige Zeichen aus unserem Text infotext in den Akku. Nur solange das Zeichen größer null ist, geben wir es auf dem Bildschirm aus, setzen unseren Zähler charCounter zurück und erhöhen charPos. Dann wird zur Systemroutine nach $ea31 gesprungen. Stört euch bitte nicht am Label irq0:, dies benötigen wir erst beim nächsten Schritt.
Startet ihr das Programm jetzt, dann erscheint unsere Textzeile zeichenweise. Unterlegt ihr jedes Zeichen mit einem Ton (z. B. dem Geräusch einer Schreibmaschine) habt ihr schon einen netten Effekt.
Wie werden die Zeichen jetzt pixelweise angezeigt?
Um ehrlich zu sein, gar nicht! Wir täuschen ein pixelweises Einblenden nur vor. Dazu platzieren wir ein Sprite, dass die selbe Farbe wie der Hintergrund und die Größe eines Zeichens hat, direkt über der Stelle, an der das Zeichen ausgegeben wird. Dann geben wir das Zeichen, wie oben im Beispielprogramm, an dieser Stelle aus. Das Zeichen verschwindet hinter dem Sprite und ist erstmal unsichtbar. Jetzt bewegen wir das Sprite und lassen so das Zeichen „sanft“ erscheinen. Dies wiederholen wir für alle Zeichen. Das hört sich jetzt evtl. komplizierter an, als es ist. Prinzipiell haben wir nämlich eben schon alle Vorbereitungen getroffen.
Also ergänzen wir unser Progrämmchen einfach…
Fügt ganz zu Beginn z. B. vor das sei folgenden Aufruf hinzu, um unser Sprite vorzubereiten.
jsr initSprite ;Sprite setzen
Die benötigte Funktion sieht dann so aus:
;*** Das Sprite zum Verdecken der Zeichen initSprite lda #sprite/64 ;Nr. des 64-BYTE-Blocks sta $07f8 ;für Sprite-0 setzen lda $d021 ;Sprite-0 Farbe wie Hintergrund sta $d027 ;für Sprite-0 lda #$10 ;Start für X- sta $d000 lda #$3a ;und Y-Position sta $d001 lda #$00 sta $d017 ;zur Sicherheit doppelte Höhe sta $d01d ;und Breite deaktivieren sta $d01b ;Sprite VOR dem Hintergrund sta $d010 ;höchstes BIT auf Null setzen lda #%00000001 ;Sprite-0 sichtbar sta $d015 ;schalten rts ;zurück
Die Nr. des 64-Byte-Blocks für das Sprite lassen wir wieder vom Assembler berechnen. Da wir die ersten 16KB verwenden, können wir das so machen. Verschiebt ihr den sichtbaren Bereich des VIC-II, dann müsst ihr, bei dieser Art der Berechnung, noch eine Kleinigkeit beachten, das sollte euch aber selbst einfallen. Die Einrichtung des Sprites sollte soweit bekannt sein. Einzige Besonderheiten sind, dass wir die Spritefarbe auf die Hintergrundfarbe $d021 setzen und die Startposition so wählen, dass das Sprite in der zweiten Textzeile erscheinen wird.
Jetzt müssen wir unsere IRQ-Routine noch anpassen. Dazu sind zunächst folgende drei Zeilen nötig, die direkt hinter dem bisher unnötigen Label irq01 eingefügt werden.
irq0 inc $d000 ;X-Position erhöhen bne irqExit ;solange < 256 -> RAUS inc $d010 ;sonst höchstes BIT setzen irqExit
Hier erhöhen wir einfach bei jedem IRQ die X-Position und sorgen natürlich auch dafür, dass ggf. das höchste BIT für die X-Position gesetzt wird. Da wir unsere Zählschleife immer mit 8 starten, ist schon alles perfekt aufeinander abgestimmt. Ein Zeichen ist schließlich genau 8-Pixel breit. Das Sprite befindet sich so für jedes Zeichen an der richtigen Stelle und wir haben den Eindruck, dass der Text ganz soft eingeblendet wird.
Dann wollen wir noch dafür sorgen, dass die Spritebewegung auch wieder gestoppt wird, sobald der gesamte Text ausgegeben wurde. Ändert dazu das beq irqExit weiter oben, in beq irqStop und fügt folgenden Block hinter das Ende der Interrupt-Funktion ein.
irqExit jmp $ea31 ;weiter zur System-Routine irqStop dec $d015 ;Sprite ausblenden lda #$31 ;Adresse der System-Routine sta $0314 lda #$ea sta $0315 ;nach $0314/15 jmp $ea31 ;und ein letztes Mal zur System-Routine
Hier wird einfach das Sprite abgeschaltet und der RAM-Vektor zurück auf die Adresse der System-Routine gesetzt.
Natürlich brauchen wir noch ein passendes Sprite. Das legen wir einfach mit Byte-Anweisungen fest. Wir brauchen schließlich nur ein 8×8 Pixel großes, einfarbiges Sprite.
;*** Das 8x8 Pixel große Sprite *=((*-1)/64)*64+64 sprite !byte $ff,$00,$00 !byte $ff,$00,$00 !byte $ff,$00,$00 !byte $ff,$00,$00 !byte $ff,$00,$00 !byte $ff,$00,$00 !byte $ff,$00,$00 !byte $ff,$00,$00 !byte $00,$00,$00 !byte $00,$00,$00 !byte $00,$00,$00 !byte $00,$00,$00 !byte $00,$00,$00 !byte $00,$00,$00 !byte $00,$00,$00 !byte $00,$00,$00 !byte $00,$00,$00 !byte $00,$00,$00 !byte $00,$00,$00 !byte $00,$00,$00 !byte $00,$00,$00
Wie gewohnt sorgen wir dafür, dass das Sprite an einer durch 64 teilbaren Adresse im Speicher liegt. Für die Turbo Assembler-Nutzer, habe ich hier mal eine andere Möglichkeit verwendet, ein Align zu emplementieren: *=((*-1)/64)*64+64. Diese Zeile entspricht einem !align 63,0 vom C64 Studio bzw. ACME (also dem Ausrichten an der nächsten durch 64 teilbaren Adresse).
Denkt daran, dass der Stern * nicht nur zur Multiplikation dient, sondern auch für die aktuelle Adresse, die der Assembler für diese Zeile ermittelt hat, steht.
;*** Startadresse *=$0801 ;*** BASIC-Zeile: 2018 SYS 2062 !word main-2, 2018 !byte $9e !text " 2062" !byte $00,$00,$00 main jsr initSprite ;Sprite setzen sei ;IRQs sperren lda #<irq ;LSB sta $0314 lda #>irq ;MSB sta $0315 ;in den RAM-Vektor jsr clrScreen ;BS löschen cli ;Interrupts wieder erlauben jmp * ;Endlosschleife ;*** eigene Interrupt-Routine irq dec charCounter ;Warteschleife verringern bne irq0 ;wenn größer null -> irq_0: ldx charPos ;Zeichenposition ins X-Reg. lda infotext,X ;Zeichen in den Akku beq irqStop ;NULL = Ende! sta $0428,X ;Zeichen ausgeben lda #$08 ;Warteschleife wieder sta charCounter ;zurücksetzen inc charPos ;Zeichenposition erhöhen irq0 inc $d000 ;X-Position erhöhen bne irqExit ;solange < 256 -> RAUS inc $d010 ;sonst höchstes BIT setzen irqExit jmp $ea31 ;weiter zur System-Routine irqStop dec $d015 ;Sprite ausblenden lda #$31 ;Adresse der System-Routine sta $0314 lda #$ea sta $0315 ;nach $0314/15 jmp $ea31 ;und ein letztes Mal zur System-Routine ;*** Bildschirm löschen clrScreen ldx #$00 ;Schleifenzähler (256 Zeichen je Page) lda #" " ;alles mit Leerzeichen füllen nextChar sta $0400,X ;1. Page des BS-Speichers sta $0500,X ;2. Page sta $0600,X ;3. Page sta $0700-24,X ;4. Page (-24 wegen der Sprite-Register) dex ;Schleifenzähler verringern bne nextChar ;Solange größer 0 -> nextChar rts ;sonst, zurück ;*** Das Sprite zum Verdecken der Zeichen initSprite lda #sprite/64 ;Nr. des 64-BYTE-Blocks sta $07f8 ;für Sprite-0 setzen lda $d021 ;Sprite-0 Farbe wie Hintergrund sta $d027 ;für Sprite-0 lda #$10 ;Start für X- sta $d000 lda #$3a ;und Y-Position sta $d001 lda #$00 sta $d017 ;zur Sicherheit doppelte Höhe sta $d01d ;und Breite deaktivieren sta $d01b ;Sprite VOR dem Hintergrund sta $d010 ;höchstes BIT auf Null setzen lda #%00000001 ;Sprite-0 sichtbar sta $d015 ;schalten rts ;zurück ;*** Textzeile $00 = ENDE infotext !scr "* (c) 2018 by www.retro-programming.de *" !byte 0 ;*** Zähler für Zeichenbreite = 8 Pixel charCounter !byte 8 ;*** Nr. des nächsten Zeichen charPos !byte 0 ;*** Das 8x8 Pixel große Sprite *=((*-1)/64)*64+64 sprite !byte $ff,$00,$00 !byte $ff,$00,$00 !byte $ff,$00,$00 !byte $ff,$00,$00 !byte $ff,$00,$00 !byte $ff,$00,$00 !byte $ff,$00,$00 !byte $ff,$00,$00 !byte $00,$00,$00 !byte $00,$00,$00 !byte $00,$00,$00 !byte $00,$00,$00 !byte $00,$00,$00 !byte $00,$00,$00 !byte $00,$00,$00 !byte $00,$00,$00 !byte $00,$00,$00 !byte $00,$00,$00 !byte $00,$00,$00 !byte $00,$00,$00 !byte $00,$00,$00
Jetzt seid ihr wieder gefragt. Hängt z. B. hinter den unduchsichtigen Bereich noch ein Schachbrettmuster oder eine andere Formen, um das Erscheinen zu verändern. Warum muss das Sprite unsichtbar sein? Es könnte auch ein Auto oder Flugzeug sein, hinter dem der Text erscheint. Verwendet animierte oder mehrere Sprites, um weitere Effekte zu erzielen und kombiniert dies z. B. mit dem Color-Cycling. Natürlich lässt sich diese Technik auch verwenden, um Texte verschwinden zu lassen.
Ein erweitertes Beispiel könnt ihr im D64-Image herunterladen…
Einen Text sanft anzeigen