;####################################################################
;# 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.
;#
;####################################################################


; -- Startadresse --

 *=$c000


;####################################################################
;#
;# Speicherstellen
;#
;####################################################################

 !addr param = $0334 ; Temporaere Speicherstelle fuer Parameter
                     ; (ungenutzte Adresse in der erweiterten
                     ; Zeropage)

; -- Alle Variablen stehen am Schluss ab $c3f0 --


;####################################################################
;#
;# 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 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
 }


;####################################################################
;#
;# Sprungtabelle
;#
;####################################################################

 jmp winit      ; $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
 jmp wsave      ; $c030
 jmp wswitch    ; $c033


;####################################################################
;#
;# Funktionen
;#
;####################################################################


; -- init --

;    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

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 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)

asciiout
 cmp #$0a   ; Zeilenumbruch
 bne +
 jsr newline
 rts
+
 cmp #$0d   ; Zeilenumbuch
 bne +
 jsr newline
 rts
+
 cmp #$93   ; Fenster loeschen
 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   ; Grossbuchstaben
 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


; -- wsave --

;    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


;####################################################################
;#
;# Variablen / Speicherstellen
;#
;####################################################################

 *=$c3e0

; -- Zeiger fuer das aktuelle Fenster --

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