;####################################################################
;# Programm: Sammlung von Funktionen fuer die Bildschirmausgabe
;#
;# Assembler: [[ACME]]
;# Assemblierung: acme -f cbm -o output.prg output.a
;#
;# Speicher: Belegt den Bereich $c000 bis $c3ff
;#
;# Aufruf mit: Ab der Adresse $c000 ist eine Sprungtabelle, ueber
;# die jede Funktion mit [[jsr]] aufgerufen werden kann
;#
;# Beschreibung: Eine Sammlung an Funktionen zur Bildschirmausgabe.
;# Sie ermoeglicht die Auswahl von fuenf verschiedenen
;# festgelegten Bereichen (Fenster), die fuer die
;# Ausgabe, das Loeschen und das Scrolling gelten.
;#
;# Die fuenf festgelegten Bereiche, die als Fenster
;# ausgewaehlt werden koennen, sind der gesamte Bild-
;# schirm, sowie die linke, rechte, obere und untere
;# Haelfte des Bildschirms.
;#
;# Im ausgewaehlten Fenster sind folgende Aktionen
;# moeglich:
;# * Loeschen 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,
;# binaerer und dezimaler Form
;#
;# Die Zeilen bzw. Zeichen, die beim Scrolling aus dem
;# Fenster verschwinden, werden nicht gespeichert.
;# Ausserdem unterstuetzt das Scrolling nicht die
;# Anpassung des Farbspeichers, d.h. die Funktionen
;# sind fuer eine monochrome Bildausgaben gedacht.
;#
;####################################################################
; 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 fuer 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
}
; Standardinitialisierung: ein grosses Fenster, das geloescht wird
winit
jsr wselall
jsr delwin
rts
; -- wselall --
; Ein grosses Fenster auswaehlen;
; Akku wird ueberschrieben
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 auswaehlen;
; Akku wird ueberschrieben
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 auswaehlen;
; Akku wird ueberschrieben
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 auswaehlen;
; Akku wird ueberschrieben
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 auswaehlen;
; Akku wird ueberschrieben
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 --
; Loescht die aktuelle Zeile mit Leerzeichen;
; Akku und Y-Register werden ueberschrieben;
; 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 --
; Loescht das aktuelle Fenster mit Leerzeichen
; der wlnp, sowie wx und wy werden zurueckgesetzt;
; Akku und Y-Register werden ueberschrieben
delwin
+wsel ; Zeilenzeiger, x, y zuruecksetzen
-
jsr delline
+add_line_wlnp ; Zeiger auf Zeile erhoehen
ldy wy
iny
sty wy
cpy wrows
bcc - ; Schleife wird bei wy <= wrows fortgesetzt
beq -
+wsel ; Zeilenzeiger, x, y wieder zuruecksetzen
rts
; -- hscroll --
; Zeile horizontal um ein Zeichen scrollen:
; Akku und Y-Register werden ueberschrieben;
; verwendet die Zeropage-Adressen $61 bis $64;
; wird von chrout aufgerufen
hscroll
sec ; Addition +1 wird ueber gesetztes Carry-Bit gemacht
lda wlnp
sta $61 ; $61/$62 zeigt auf das erste Zeichen der Zeile
adc #0 ; eins hinzu addieren (ueber 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 Durchgaenge
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 ueberschrieben;
; 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 durchfuehren
beq +
iny ; ansonsten wx um eins erhoehen
sty wx
rts
+
jsr hscroll
; wlx nicht erhoehen
rts
; -- vscroll --
; Fensterinhalt um eine Zeile nach oben verschieben;
; Akku, X- und Y-Register werden ueberschrieben;
; 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 Durchgaenge
lda $61 ; 1. Zeiger um eine Zeile erhoehen
clc
adc #40
sta $61
lda $62
adc #0
sta $62
lda $63 ; 2. Zeiger um eine Zeile erhoehen
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 geloescht
rts
; -- newline --
; Zeilenumbruch durchfuehren
; Wenn die letzte Zeile des Fensters die aktuelle Zeile
; ist, wird ein Scrolling durchgefuehrt.
; Akku und Y-Register werden ueberschrieben, 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 erhoehen ...
sty wy
+add_line_wlnp ; Zeiger auf Zeile erhoehen
rts
+
jsr vscroll
rts
; -- hexout --
; Gibt den Wert im Akku in hexadezimaler Form aus;
; Akku, X- und Y-Register (von chrout) werden ueberschrieben
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
; Gibt den Wert im Akku in binaerer Form aus;
; Akku, X- und Y-Register (von chrout) werden ueberschrieben;
; die Speicherstelle param wird veraendert
binout
sta param ; Wert ablegen
ldx #$07 ; Zaehler von 7 bis 0
bin_loop ; Den Wert bitweise von links nach rechts durchgehen
txa ; Zaehler sichern
pha
lda param ; Das hoechste Bit in den Carry-Flag befoerdern
asl
sta param
bcc + ; Abhaengig vom Ergebnis '0' oder '1' ausgeben
lda #$31
bne ++
+
lda #$30
++
jsr chrout
pla ; Zaehler 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 aus;
; bei gesetztem Carry werden Werte > 127 als negative
; Zahlen ausgeben;
; Akku, X- und Y-Register werden ueberschrieben;
; die Speicherstelle param wird verwendet
decout
sta param ; Wert sichern
bcc +
bpl +
; bei negativem Wert mit Zweier-Komplement in positiven
; Wert wandeln und Minuszeichen ausgeben
eor #$ff
adc #0 ; Carry ist noch gesetzt (+1)
sta param
lda #'-'
jsr chrout
lda param
+
cmp #$0a ; ist der Wert < 10
bcc dig1 ; weiter mit 1er-Stellen
cmp #$64 ; ist der Wert < 100
bcc dig10 ; weiter mit 10er-Stellen
; 100-Stellen
ldx #$00 ; dargestellte Ziffer (Startwert '0')
sec
-
sbc #$64 ; 100 abziehen
bcc + ; Abbruchbedingung: Wert wird negativ
sta param ; verringerten Wert speichern
inx ; dargestellte Ziffer um eins erhoehen
jmp -
+
txa
ora #$30 ; Bildschirmcode setzen
jsr chrout
; 10er-Stellen
dig10
ldx #$00 ; dargestellte Ziffer (Startwert '0')
lda param
cmp #$0a ; Restwert < 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 erhoehen
jmp -
+
txa
ora #$30 ; Bildschirmcode setzen
jsr chrout
; 1er-Stellen
; Der Restwert aus der Speicherstelle param kann
; direkt ausgegeben werden.
dig1
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 ueberschrieben
; Es werden folgende ASCII-Werte unterstuetzt:
;
; - $0a/#10, $0d/#13: Zeilenumbruch
; - $20-$3f/#32-#63: Satzzeichen und Ziffern
; - $40-$5f/#64-#95: Gross- (Font 1) bzw. Kleinbuchstaben (Font 2)
; - $90-$7f/#96-#127: Grafikzeichen 1 (Font 1) bzw. Grossbuchstaben
; (Font 2)
; - $a0-$bf/#160-#191: Grafikzeichen 2
; - $93/#147: Aktuelles Fenster loeschen
;
; 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)
; - Grossbuchstaben (Bit 6 invertieren und dadurch loeschen)
; %010 00000 ($40) bis %010 11111 ($5f)
; %000 00000 ($00) bis %000 11111 ($1f)
; - Grafikzeichen 1 (Bit 5 inverieren und dadurch loeschen)
; %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)
; Speichert die Daten des aktuellen Fensters im
; Zwischenspeicher ab
wsave
lda wtlp
sta stlp
lda wtlp+1
sta stlp+1
lda wlnp
sta slnp
lda wlnp+1
sta slnp+1
lda wcols
sta scols
lda wrows
sta srows
lda wx
sta sx
lda wy
sta sy
rts
; -- wswitch --
; Tauscht die Daten des aktuellen Fensters mit dem
; Zwischenspeicher aus. So koennen zwei Fenster im Wechsel
; benutzt werden. Bevor wswitch verwendet werden kann, muss
; vorher einmal wsave benutzt worden sein, damit im
; Zwischenspeicher gueltige Werte stehen!
wswitch
lda stlp ; wtlp <-> stlp
pha
lda wtlp
sta stlp
pla
sta wtlp
lda stlp+1 ; wtlp+1 <-> stlp+1
pha
lda wtlp+1
sta stlp+1
pla
sta wtlp+1
lda slnp ; wlnp <-> slnp
pha
lda wlnp
sta slnp
pla
sta wlnp
lda slnp+1 ; wlnp+1 <-> slnp+1
pha
lda wlnp+1
sta slnp+1
pla
sta wlnp+1
lda scols ; wcols <-> scols
pha
lda wcols
sta scols
pla
sta wcols
lda srows ; wrows <-> srows
pha
lda wrows
sta srows
pla
sta wrows
lda sx ; wx <-> sx
pha
lda wx
sta sx
pla
sta wx
lda sy ; wy <-> sy
pha
lda wy
sta sy
pla
sta wy
rts
wtlp ; Zeiger auf das aktuelle Fenster (top/left pointer)
!by 0, 0
wlnp ; Zeiger auf die aktuelle Zeile (line pointer)
!by 0, 0
; -- Parameter fuer das aktuelle Fenster --
; Die Anzahl der Spalten und Zeilen variiert je nach Fenstertyp:
;
; - ein grosses 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.
wcols ; Anzahl Spalten (cols; 39 oder 19)
!by 0
wrows ; Anzahl Zeilen (rows; 24 oder 12 oder 11)
!by 0
wx ; Aktuelle X-Position (Start mit 0; bis max. wcols)
!by 0
wy ; Aktuelle Y-Position (Start mit 0; bis max. wrows)
!by 0
; -- Zwischenspeicherung der Fensterdaten fuer Fensterwechsel --
stlp ; Zeiger auf das Fenster
!by 0, 0
slnp ; Zeiger auf die aktuelle Zeile
!by 0, 0
scols ; Anzahl Spalten
!by 0
srows ; Anzahl Zeilen
!by 0
sx ; X-Position
!by 0
sy ; Y-Position
!by 0