bildschirmausgabe.asm
Aus C64-Wiki
Zur Navigation springenZur Suche springenQuelltext[Bearbeiten | Quelltext bearbeiten]
;#################################################################### ;# Programm: Sammlung von Funktionen für die Bildschirmausgabe ;# Dateiname: bildschirmausgabe.asm ;# ;# Assembler: ACME ;# Assemblierung: acme -f cbm -o output.prg bildschirmausgabe.asm ;# ;# Laden mit: LOAD"output.prg",8,1 ;# ;# Aufruf mit: Ab der Adresse $c000 ist eine Sprungtabelle, über ;# die jede Funktion mit jsr aufgerufen werden kann ;# ;# Quelltext: * Kleinschreibung (bis auf Kommentare) ;# * nur Leerzeichen, keine Tabulatoren ;# * Labels immer in eigener Zeile ;# * Befehle zwei Zeichen eingerückt ;# * Kommentare i.d.R. nach den Befehlen ;# ;# Beschreibung: Eine Sammlung an Funktionen zur Bildschirmausgabe. ;# Sie ermöglicht die Auswahl von fünf verschiedenen ;# festgelegten Bereichen (Fenster), die für die ;# Ausgabe, das Löschen und das Scrolling gelten. ;# ;# Die fünf festgelegten Bereiche, die als Fenster ;# ausgewählt werden können, sind der gesamte Bild- ;# schirm, sowie die linke, rechte, obere und untere ;# Hälfte des Bildschirms. ;# ;# Im ausgewählten Fenster sind folgende Aktionen ;# möglich: ;# * Löschen des gesamten Fensters ;# * Vert. Scrolling des gesamten Fensters nach oben ;# * Horiz. Scrolling der aktuellen Zeile nach links ;# * Ausgabe einzelner Zeichen (direkt als Bildschirm- ;# code und ASCII) ;# * Zeilenumbruch (mit vertikalem Scrolling) ;# * Ausgabe eines 8-Bit-Werts in hexadezimaler, ;# binärer und dezimaler Form ;# ;# Die Zeilen bzw. Zeichen, die beim Scrolling aus dem ;# Fenster verschwinden, werden nicht gespeichert. ;# Außerdem unterstützt das Scrolling nicht die ;# Anpassung des Farbspeichers, d.h. die Funktionen ;# sind für eine monochrome Bildausgaben gedacht. ;# ;#################################################################### ; -- Startadresse -- *=$c000 ;#################################################################### ;# ;# Speicherstellen ;# ;#################################################################### !addr param = $0334 ; Temporäre Speicherstelle für Parameter ; (ungenutzte Adresse in der erweiterten ; Zeropage) ; -- Zeiger für das aktuelle Fenster -- !addr wtlp = $cf00 ; Zeiger auf das aktuelle Fenster ; (top/left pointer) !addr wlnp = $cf02 ; Zeiger auf die aktuelle Zeile ; (line pointer) ; -- Parameter für das aktuelle Fenster -- ; Die Anzahl der Spalten und Zeilen variiert je nach Fenstertyp: ; ; - ein großes Fenster (40 Spalten, 25 Zeilen) ; - ein halbes Fenster links (20 Spalten, 25 Zeilen) ; - ein halbes Fenster rechts (20 Spalten, 25 Zeilen) ; - ein halbes Fenster oben (40 Spalten, 13 Zeilen) ; - ein halbes Fenster unten (40 Spalten, 12 Zeilen) ; ; Die Werte werden um eins verringert, weil wx und wy bei null ; beginnen. !addr wcols = $cf04 ; Anzahl Spalten (cols; 39 oder 19) !addr wrows = $cf05 ; Anzahl Zeilen (rows; 24 oder 12 oder 11) !addr wx = $cf06 ; Aktuelle X-Position ; (Start mit 0; bis max. wcols) !addr wy = $cf07 ; Aktuelle Y-Position ; (Start mit 0; bis max. wrows) ;#################################################################### ;# ;# Makros ;# ;#################################################################### ; -- add_line_wlnp -- ; Addiert #40 zum Zeiger auf die aktuelle Zeile (wlnp) hinzu; ; wird von delwin und newline verwendet !macro add_line_wlnp { clc lda wlnp adc #40 sta wlnp lda wlnp+1 adc #$00 sta wlnp+1 } ; -- wsel -- ; Setzt für das aktuelle Fenster den Zeiger auf die ; aktuelle Zeile, sowie x und y auf die Startwerte; ; wird von wselall, wselleft, wselright, wseltop, ; wselbottom und delwin verwendet !macro wsel { lda #0 sta wx sta wy lda wtlp sta wlnp lda wtlp+1 sta wlnp+1 } ;#################################################################### ;# ;# Sprungtabelle ;# ;#################################################################### jmp init ; $c000 jmp wselall ; $c003 jmp wselleft ; $c006 jmp wselright ; $c009 jmp wseltop ; $c00c jmp wselbottom ; $c00f jmp delline ; $c012 jmp delwin ; $c015 jmp hscroll ; $c018 jmp chrout ; $c01b jmp vscroll ; $c01e jmp newline ; $c021 jmp hexout ; $c024 jmp binout ; $c027 jmp decout ; $c02a jmp asciiout ; $c02d ;#################################################################### ;# ;# Funktionen ;# ;#################################################################### ; -- init -- ; Standardinitialisierung: ein großes Fenster, das gelöscht wird init jsr wselall jsr delwin rts ; -- wselall -- ; Ein großes Fenster auswählen; ; Akku wird überschrieben wselall lda #$00 ; Zeiger auf Bildschirmspeicher: $0400 sta wtlp lda #$04 sta wtlp+1 lda #39 ; 40 (-1) Spalten sta wcols lda #24 ; 25 (-1) Zeilen sta wrows +wsel rts ; -- wselleft -- ; Ein halbes Fenster links auswählen; ; Akku wird überschrieben wselleft lda #$00 ; Zeiger auf Bildschirmspeicher: $0400 sta wtlp lda #$04 sta wtlp+1 lda #19 ; 20 (-1) Spalten sta wcols lda #24 ; 25 (-1) Zeilen sta wrows +wsel rts ;-- wselright -- ; Ein halbes Fenster rechts auswählen; ; Akku wird überschrieben wselright lda #$14 ; Zeiger auf Bildschirmspeicher: $0414 (1024 + 20) sta wtlp lda #$04 sta wtlp+1 lda #19 ; 20 (-1) Spalten sta wcols lda #24 ; 25 (-1) Zeilen sta wrows +wsel rts ; -- wseltop -- ; Ein halbes Fenster oben auswählen; ; Akku wird überschrieben wseltop lda #$00 ; Zeiger auf Bildschirmspeicher: $0400 sta wtlp lda #$04 sta wtlp+1 lda #39 ; 40 (-1) Spalten sta wcols lda #12 ; 13 (-1) Zeilen sta wrows +wsel rts ; -- wselbottom -- ; Ein halbes Fenster unten auswählen; ; Akku wird überschrieben wselbottom lda #$08 ; Zeiger auf Bildschirmspeicher: $0608 (1024 + 13*40) sta wtlp lda #$06 sta wtlp+1 lda #39 ; 40 (-1) Spalten sta wcols lda #11 ; 12 (-1) Zeilen sta wrows +wsel rts ; -- delline -- ; Löscht die aktuelle Zeile mit Leerzeichen; ; Akku und Y-Register werden überschrieben; ; benutzt die Zeropage-Adressen $61 und $62; ; wird von delwin aufgerufen delline lda wlnp sta $61 lda wlnp+1 sta $62 ldy wcols lda #$20 - sta ($61),y dey bpl - ; Schleifenabbruch erst bei Y < 0 rts ; -- delwin -- ; Löscht das aktuelle Fenster mit Leerzeichen ; der wlnp, sowie wx und wy werden zurückgesetzt; ; Akku und Y-Register werden überschrieben delwin +wsel ; Zeilenzeiger, x, y zurücksetzen - jsr delline +add_line_wlnp ; Zeiger auf Zeile erhöhen ldy wy iny cpy wrows sty wy bcc - ; Schleife wird bei wy <= wrows fortgesetzt beq - +wsel ; Zeilenzeiger, x, y wieder zurücksetzen rts ; -- hscroll -- ; Zeile horizontal um ein Zeichen scrollen: ; Akku und Y-Register werden überschrieben; ; verwendet die Zeropage-Adressen $61 bis $64; ; wird von chrout aufgerufen hscroll sec ; Addition +1 wird über gesetztes Carry-Bit gemacht lda wlnp sta $61 ; $61/$62 zeigt auf das erste Zeichen der Zeile adc #0 ; eins hinzu addieren (über gesetztes Carry, s.o.) sta $63 ; $63/$64 zeigt auf das zweite Zeichen der Zeile lda wlnp+1 sta $62 adc #0 ; (16-Bit-Addition) sta $64 ldy #$00 ; von links nach rechts die Zeile durchgehen - lda ($63),y ; Zeichen nach links verschieben sta ($61),y iny cpy wcols bne - ; Abbruchbedingung: Y = wcols ; d.h. Spaltenanzahl-1 Durchgänge lda #$20 ; ein Leerzeichen ans Ende der Zeile schreiben ldy wcols sta ($61),y rts ; -- chrout -- ; Gibt den Wert im Akku (Bildschirmcode) aus. Wenn das ; Zeilenende erreicht ist, wird horizontal gescrollt. ; Y-Register wird überschrieben; ; verwendet die Zeropage-Adressen $61/$62 ; (beim Scrolling auch die Adressen $63/$64): ; wird von hexout, binout, decout und asciiout aufgerufen chrout pha ; Bildschirmcode sichern lda wlnp sta $61 lda wlnp+1 sta $62 ldy wx pla ; Bidlschirmcode wieder holen sta ($61),y cpy wcols ; Wenn Ende der Zeile erreicht ist, Scrolling durchführen beq + iny ; ansonsten wx um eins erhöhen sty wx rts + jsr hscroll ; wlx nicht erhöhen rts ; -- vscroll -- ; Fensterinhalt um eine Zeile nach oben verschieben; ; Akku, X- und Y-Register werden überschrieben; ; die letzte Zeile wird zur aktuellen Zeile; ; verwendet die Zeropageadressen $61-$64; ; wird von newline aufgerufen vscroll clc lda wtlp sta $61 ; 1. Zeiger zeigt auf die erste Zeile adc #40 sta $63 ; 2. Zeiger auf die zweite Zeile lda wtlp+1 sta $62 adc #0 sta $64 ldx wrows scroll_loop ; 1. Schleife: alle Zeilen um eins nach oben verschieben ldy wcols - ; 2. Schleife: alle Zeichen der Zeile durchgehen lda ($63),y sta ($61),y dey bpl - ; Abbruch 2. Schleife erst bei Y < 0 dex beq + ; Abbruchbedingung 1. Schleife: X = wrows ; d.h. Zeilenanzahl-1 Durchgänge lda $61 ; 1. Zeiger um eine Zeile erhöhen clc adc #40 sta $61 lda $62 adc #0 sta $62 lda $63 ; 2. Zeiger um eine Zeile erhöhen clc adc #40 sta $63 lda $64 adc #0 sta $64 jmp scroll_loop + lda $63 ; $63/$64 zeigt auf die letzte Zeile sta wlnp ; diese wird zur aktuellen Zeile lda $64 sta wlnp+1 jsr delline ; und ihr Inhalt gelöscht rts ; -- newline -- ; Zeilenumbruch durchführen ; Wenn die letzte Zeile des Fensters die aktuelle Zeile ; ist, wird ein Scrolling durchgeführt. ; Akku und Y-Register werden überschrieben, beim ; Scrolling auch das X-Register newline lda #$00 ; wx auf 0 setzen sta wx ldy wy cpy wrows ; wenn Ende des Fenster erreicht ist, dann scrollen beq + iny ; Zeile um eins erhöhen ... sty wy +add_line_wlnp ; Zeiger auf Zeile erhöhen rts + jsr vscroll rts ; -- hexout -- ; Gibt den Wert im Akku in hexadezimaler Form aus; ; Akku, X- und Y-Register (von chrout) werden überschrieben hexout pha ; Wert sichern lsr ; die oberen 4 Bits nach unten holen lsr lsr lsr tax ; passendes Zeichen laden und ausgeben lda hex_digits,x jsr chrout pla ; Wert wieder holen and #$0f ; die unten 4 Bits isolieren tax ; passendes Zeichen laden und ausgeben lda hex_digits,x jsr chrout rts hex_digits ; Bildschirmcodes Ziffern 0-9 !by $30, $31, $32, $33, $34, $35, $36, $37, $38, $39 ; Bildschirmcodes Buchstaben A-F !by $01, $02, $03, $04, $05, $06 ; -- binout -- ; Gibt den Wert im Akku in binärer Form aus; ; Akku, X- und Y-Register (von chrout) werden überschrieben; ; die Speicherstelle param wird verändert binout sta param ; Wert ablegen ldx #$07 ; Zähler von 7 bis 0 bin_loop ; Den Wert bitweise von links nach rechts durchgehen txa ; Zähler sichern pha lda param ; Das höchste Bit in den Carry-Flag befördern asl sta param bcc + ; Abhängig vom Ergebnis '0' oder '1' ausgeben lda #$31 bne ++ + lda #$30 ++ jsr chrout pla ; Zähler wieder holen tax cpx #$04 ; nach 4 Bits ein Leerzeichen ausgeben bne + lda #$20 jsr chrout + dex bpl bin_loop ; Abbruchbedingung X < 0 rts ; -- decout -- ; Gibt den Wert im Akku in dezimaler Form mit ; führenden Nullen aus; ; Akku, X- und Y-Register werden überschrieben; ; die Speicherstelle param wird verwendet decout sta param ; Wert sichern ; 100-Stellen ldx #$00 ; dargestellte Ziffer (Startwert '0') cmp #$64 ; ist der Wert < 100 bcc + ; '0' ausgeben (X ist noch null) sec - sbc #$64 ; 100 abziehen bcc + ; Abbruchbedingung: Wert wird negativ sta param ; verringerten Wert speichern inx ; dargestellte Ziffer um eins erhöhen jmp - + txa ora #$30 ; Bildschirmcode setzen jsr chrout ; 10er-Stellen ldx #$00 ; dargestellte Ziffer (Startwert '0') lda param cmp #$0a ; ist der Wert < 10 bcc + ; '0' ausgeben (X ist noch null) sec - sbc #$0a ; 10 abziehen bcc + ; Abbruchbedingung: Wert wird negativ sta param ; verringerten Wert speichern inx ; dargestellte Ziffer um eins erhöhen jmp - + txa ora #$30 ; Bildschirmcode setzen jsr chrout ; 1er-Stellen ; Der Restwert aus der Speicherstelle param kann ; direkt ausgegeben werden. lda param ora #$30 ; Bildschirmcode setzen jsr chrout rts ; -- asciout -- ; Gibt das den im Akku enthaltenen ASCII-Wert aus ; Akku, X- und Y-Register werden überschrieben ; Es werden folgende ASCII-Werte unterstützt: ; ; - $0a/#10, $0d/#13: Zeilenumbruch ; - $20-$3f/#32-#63: Satzzeichen und Ziffern ; - $40-$5f/#64-#95: Groß- (Font 1) bzw. Kleinbuchstaben (Font 2) ; - $90-$7f/#96-#127: Grafikzeichen 1 (Font 1) bzw. Großbuchstaben ; (Font 2) ; - $a0-$bf/#160-#191: Grafikzeichen 2 ; - $93/#147: Aktuelles Fenster löschen ; ; Bei allen anderen ASCII-Werten wird ein Fragezeichen ausgegeben. ; Umwandlungen von ASCII nach Bildschirmcode: ; ; - Satzzeichen und Ziffern (kene Umwandlung) ; %001 00000 ($20) bis %001 11111 ($3f) ; - Großbuchstaben (Bit 6 invertieren und dadurch löschen) ; %010 00000 ($40) bis %010 11111 ($5f) ; %000 00000 ($00) bis %000 11111 ($1f) ; - Grafikzeichen 1 (Bit 5 inverieren und dadurch löschen) ; %011 00000 ($60) bis %011 11111 ($7f) ; %010 00000 ($40) bis %010 11111 ($5f) ; - Grafikzeichen 2 (Bits 7 und 6 invertieren) ; %101 00000 ($a0) bis %101 11111 ($bf) ; %011 00000 ($60) bis %011 11111 ($7f) asciiout cmp #$0a ; Zeilenumbruch bne + jsr newline rts + cmp #$0d ; Zeilenumbuch bne + jsr newline rts + cmp #$93 ; Fenster löschen bne + jsr delwin rts + pha ; ASCII-Wert sichern lsr ; die 3 oberen Bits nach unten holen lsr lsr lsr lsr tax ; und zur Auswertung ins X-Register schieben pla ; ASCII-Wert wieder holen cpx #$01 ; Satzzeichen und Ziffern bne + jsr chrout rts + cpx #$02 ; Großbuchstaben bne + eor #$40 jsr chrout rts + cpx #$03 ; Grafikzeichen 1 bne + eor #$20 jsr chrout rts + cpx #$05 ; Grafikzeichen 2 bne + eor #$c0 jsr chrout rts + lda #$3f ; ansonsten Fragezeichen ausgeben jsr chrout rts
Demo-Programm[Bearbeiten | Quelltext bearbeiten]
;#################################################################### ;# Programm: Demo-Programm für Bildschirmausgabe ;# mit Basic-Starter ;# Dateiname: bildschirmdemo.asm ;# ;# Assembler: ACME ;# Assemblierung: acme -f cbm -o output.bas bildschirmdemo.asm ;# ;# Laden mit: NEW ;# LOAD"output.bas",8 ;# ;# Start mit: RUN ;# ;# Quelltext: * Kleinschreibung (bis auf Kommentare) ;# * nur Leerzeichen, keine Tabulatoren ;# * Labels immer in eigener Zeile ;# * Befehle zwei Zeichen eingerückt ;# * Kommentare i.d.R. nach den Befehlen ;# ;# Beschreibung: Das Programm gibt nacheinander folgendes aus: ;# - im linken Fenster die Hexwerte 0-255 ;# - im rechten Fenster die Binärwerte 0-255 ;# - im oberen Fenster die Dezimalwere 0-255 ;# - im unteren Fenster die ASCII-Werte 32-127 ;# ;#################################################################### ;#################################################################### ;# ;# Basic-Starter ;# ;#################################################################### *=$0801 ; -- Basic-Startzeile erzeugen: 10 SYS 2061 -- !by $0b, $0a ; Zeiger zur zweiten Zeile !by $0a, $00 ; Zeilennummer (10) !by $9e ; Token für SYS !by $32, $30, $36, $31 ; 2061 in ASCII !by $00 ; Ende der Basic-Zeile !by $00, $00 ; Zweite Basic-Zeile und zugleich Ende des ; Basic-Programms ;#################################################################### ;# ;# Hauptprogramm (Start bei $080d/#2061) ;# ;#################################################################### ; -- Speicherstellen -- !addr cnt = $0335 ; Zähler (ungenutzte Speicherstelle in der ; erweiterten Zeropage) ; -- Start -- jsr $c000 ; init ; -- Im linken Fenster Hexadezimalwerte ausgeben -- jsr $c006 ; wselleft jsr $c015 ; delwin ldx #0 ; Hexwerte 0-255 - stx cnt lda #$24 ; '$' jsr $c01b ; chrout txa jsr $c024 ; hexout jsr $c021 ; newline ldx cnt inx bne - ; -- Im rechten Fenster Binärwerte ausgeben -- jsr $c009 ; wselright jsr $c015 ; delwin ldx #0 ; Binärwerte 0-255 - stx cnt lda #$25 ; '%' jsr $c01b ; chrout txa jsr $c027 ; binout jsr $c021 ; newline ldx cnt inx bne - ; -- Im oberen Fenster Dezimalwerte ausgeben -- jsr $c00c ; wseltop jsr $c015 ; delwin ldx #0 ; Dezimalwerte 0-255 - stx cnt txa jsr $c02a ; decout jsr $c021 ; newline ldx cnt inx bne - ; -- Im unteren Fenster ASCII-Werte ausgeben -- jsr $c00f ; wselbottom lda #147 ; Bildschirmhälfte löschen jsr $c02d ; asciiout ldx #$20 ; ASCII-Zeichen 32-63 - stx cnt txa jsr $c02d ; asciiout ldx cnt inx cpx #$40 bne - lda #10 ; Zeilenumbruch jsr $c02d ; asciiout ldx #$40 ; ASCII-Zeichen 64-95 - stx cnt txa jsr $c02d ; asciiout ldx cnt inx cpx #$60 bne - lda #13 ; Zeilenumbruch jsr $c02d ; asciiout ldx #$20 ; ASCII-Zeichen 32-95 mit horiz. Scrolling - stx cnt txa jsr $c02d ; asciiout ldx cnt inx cpx #$60 bne - lda #13 ; Zeilenumbruch jsr $c02d ; asciiout ldx #$60 ; ASCII-Zeichen 96-127 - stx cnt txa jsr $c02d ; asciiout ldx cnt inx cpx #$80 bne - rts