; RSX - Erweiterung  "SCRIPT"  von Armin Mueller
;
; SCRIPT [,f] ,xp ,yp ,a$    : Text zeichnen 
; ZOOM ,xv ,yv ,xl ,yl ,prop : Textgroesse festlegen
; SCROLL [,n]                : Bildschirm scrollen
; SIZE ,xl ,yo ,br ,ho ,n ,m : Scrollfenster festlegen
;
;
; Systemroutinen 
;
ORG #a000 ; Programmstart
ENT $ ; Einsprung
;
rsxini:EQU #BCD1 ; KL LOG ON EXTENSION
romver:EQU #B915 ; KL TASTE ROM
romsel:EQU #B906 ; KL LOWER ROM  
setcol:EQU #BBDE ; GRA SET PEN
move:EQU #BBC0 ; GRA MOVE ABSOLUTE
drawr:EQU #BBF9 ; GRA LINE RELATIVE
getmat:EQU #BBA5 ; (Adresse einer Zeichenmatrix holen)
patch:EQU #AC04 ; (Indirection fuer Fehlerausgabe)
error1:EQU #AE34 ; (diverse Routinen und Adressen
error2:EQU #DD3F ;  beim CPC 464 fuer die
error3:EQU #B0C2 ;  Patch - Routine)
error4:EQU #FB21
;
; RSX einbinden
;
start:LD BC,beftab ; Anfang der RSX - Befehlstabelle
LD HL,kernel ; Adresse fuer Verkettung der RSX
CALL rsxini ; RSX einbinden
LD A,#C9 ; RET an den Anfang setzen
LD (start),A
CALL romver ; Romversion ermitteln:
LD A,H ; Fertig, falls kein CPC 464
AND A
RET NZ
LD A,(patch) ; Wenn der Patch schon belegt ist,
CP #C9 ; z.B. mit dem Emulator, auch fertig
RET NZ
LD A,#C3 ; Sonst mit 'JP unloc' 
LD (patch),A ; die Patch - Routine aktivieren
LD HL,unloc ; (siehe da)
LD (patch+1),HL
RET ; Ende der RSX-Einbindung
;
; Anfangswerte fuer ZOOM
;
ylinie:DEFW 2 ; Liniendicke
xlinie:DEFW 2
yvergr:DEFW 4 ; Vergroesserung
xvergr:DEFW 4
yvgneg:DEFW -4 ; Zweierkomplement der Vergoesserung
xvgneg:DEFW -4
;
; Parameter - Ablage 
; Die Parameter werden von den Befehlen benoetigt.
; Sie ueberschreiben aber die RSX Einbindung, da diese
; nur ein Mal aufgerufen wird. (26 Byte gespart)
;
kernel:EQU start+1 ; Verkettung der RSX-Tabellen
matrix:EQU ylinie-26 ; Matrix des aktuellen Zeichens
text:EQU matrix+9 ; Zeiger auf den auszugebenden Text
ypos:EQU text+2 ; Y-Position des Textes
xpos:EQU ypos+2 ; X-Position
farbe:EQU xpos+2 ; Farbe
punkt1:EQU farbe+2 ; Speichert, welche Nachbarpunkte
punkt2:EQU punkt1+1 ; verbunden werden muessen
punkt3:EQU punkt2+1
punkt4:EQU punkt3+1
maske:EQU punkt4+1 ; Maske fuer die Zeichenmatrix
zeile:EQU maske+1 ; Aktuelle Zeile der Zeichenmatrix
proptn:EQU zeile+2 ; Proportion des Textes
;
; Tabelle der RSX - Befehle
;
beftab:DEFW namen ; Zeiger auf die Namen
JP script ; Programmteile (analog zu den Namen)
JP scroll
JP zoom
JP size
namen:DEFM "SCRIP" ; Namen der Befehle
DEFB "T"+#80
DEFM "SCROL"
DEFB "L"+#80
DEFM "ZOO"
DEFB "M"+#80
DEFM "SIZ"
DEFB "E"+#80
DEFB 0 ; Ende der Tabelle
;
; Routine legt Vergroesserung fest
;
zoom:CP #05 ; Fuenf Parameter ? 
RET NZ ; Abbruch, falls nicht
PUSH IX ; HL mit IX laden
POP HL ; (alter Zeiger auf die Parameter)
LD DE,proptn ; Zeiger auf die neue Parameterablage
LD BC,#000A ; Parameter 10 Bytes lang
LDIR ; Parameter kopieren
;
LD HL,proptn ; Proportion direkt an die
LD DE,pro+1 ; entsprechende Stelle im 
LDI ; Hauptprogramm kopieren
LDI
;
LD B,#02 ; Die X- und Y-Liniendicke
declop:LD A,(HL) ; jeweils um 1 erniedrigen
AND A
JR Z,nicht ; (minimal auf 0),
DEC A
nicht:LD (HL),A ; damit das Verhaeltnis zur
INC HL ; Vergroesserung stimmt
INC HL
DJNZ declop
;
LD HL,ylinie ; Die 2 Lieniendicken und
LD BC,#041F ; Vergroesserungen bearbeiten:
zoomlp:LD A,(HL) ; Die Lowbytes verdoppeln, da die
SLA A ; Vergroesserung nur in Zweierschritten
AND C ; moeglich ist, dann auf max. #1F
LD (HL),A ; begrenzen
INC HL
LD (HL),#00 ; Die Hibytes loeschen
INC HL
DJNZ zoomlp ; Das Ganze 4 mal durchfuehren
;
LD HL,(yvergr) ; Von den beiden Vergroesserungen
CALL swaphl ; jeweils noch eine negative
LD (yvgneg),HL ; Ausfuehrung anfertigen
LD HL,(xvergr)
CALL swaphl
LD (xvgneg),HL
RET ; Fertig
;
; Hauptprogramm: vergroessern
;
script:PUSH IX ; Die Parameter nach 'text' kopieren
POP HL
LD DE,text
LD BC,#0008
LDIR
;
CP #03 ; Falls 3 Parameter
JR Z,nofarb ; -> Programm sofort ausfuehren
CP 4 ; Falls nicht 4 Parameter
RET NZ ; -> Abbruch
LD A,(farbe) ; sonst vorher noch
CALL setcol ; die Zeichenfarbe bestimmen
;
nofarb:LD BC,#0008 ; Die Y-Vergroesserung * 8
LD DE,(yvergr) ; zur Y-Position des Textes addieren,
CALL mult ; damit die linke untere Ecke des
LD DE,(ypos) ; Textes auf die Koordinaten zeigt
ADD HL,DE ; (Im Gegensatz zu "TAG" ist das 
LD (ypos),HL ; praktischer)
;
LD HL,(text) ; HL mit dem Zeiger auf den Textkopf
LD A,(HL) ; laden und schauen, wie lang der
AND A ; Text ist
RET Z ; -> Falls 0 Abbruch
INC HL
LD E,(HL) ; sonst den Anfang der Zeichenfolge
INC HL ; indirekt in HL laden
LD D,(HL)
EX DE,HL
LD B,A ; ... und ihre Laenge merken
;
string:PUSH BC ; Textlaenge und -adresse merken
PUSH HL
LD A,(HL) ; das naechste Zeichen holen 
CALL getmat ; schauen, wo die Zeichenmatrix liegt
CALL NC,romsel ; und eventuell das ROM aktivieren,
LD DE,matrix ; danach bei 'matrix' ablegen
LD BC,#0008
LDIR ; um die Abtastung zu erleichern, wird
XOR A ; unter die Matrix noch eine leere
LD (DE),A ; Zeile gesetzt   
;
LD D,A ; Vorbereitung zur Hauptschleife:
LD E,A ; Die X-Koordinate wird auf 0 gesetzt,
LD A,#80 ; die Maske zur Abtastung der Matrix
LD (maske),A ; ist am Anfang &x10000000
xloop:LD HL,#0008 ; ... und die Y-Koordinate 8
yloop:PUSH HL ; Koordinaten retten
PUSH DE
LD BC,matrix-1 ; Aktuelle Matrixzeile berechnen
ADD HL,BC
LD (zeile),HL
LD A,(maske) ; Und falls auf dieser Matixstelle
AND (HL) ; ein Punkt gesetzt ist ...
POP DE
POP HL
CALL NZ,zeichn ; -> die Zeichenroutine aufrufen
DEC L ; den Punkt unterhalb bearbeiten,
JR NZ,yloop ; wenn die Spalte noch nicht fertig ist
LD A,(maske) ; sonst die Maske eine Spalte
SRL A ; weiter rotiern
LD (maske),A
INC E ; die X-Koordinate erhoehen
LD A,8 ; und pruefen, ob die
CP E ; letzte Spalte erreicht ist
JR NZ,xloop ; sonst naechste Spalte zeichnen
;
pro:LD DE,8 ; Die Breite der Matrix (Proportion)
LD BC,(xvergr) ; mit der X-Verg. multiplizieren
CALL mult ; und zur alten X-Position dazuzaehlen,
LD BC,(xpos) ; um die X-Pos. fuer das
ADD HL,BC ; naechste Zeichen zu erhalten
LD (xpos),HL
;
POP HL ; den Zeiger auf das naechste
INC HL ; Zeichen setzen
POP BC ; so lange wiederholen, bis alle
DJNZ string ; Zeichen auf dem Bildschirm sind
RET ; Fertig!
;
; Unterprogramm zeichnen
;
zeichn:PUSH HL ; Koordinaten retten
PUSH DE
;
PUSH HL
LD HL,(zeile) ; Auf der Matrix testen,
LD A,(maske) ; ob der Punkt links vom aktuellen
SLA A ; Punkt gesetzt ist
AND (HL)
LD (punkt2),A ; Das Ergebnis merken
;
INC HL ; Punkt unten
LD A,(maske)
AND (HL)
LD (punkt1),A
;
LD A,(maske) ; Punkt links unten  
SLA A
AND (HL)
LD (punkt3),A

LD A,(maske) ; Punkt rechts unten
SRL A
AND (HL)
LD (punkt4),A
POP HL
;
PUSH HL ; Die X-Position auf der Matrix (DE)
LD BC,(xvergr) ; mit der X-Vergr. (BC) multiplizieren,
CALL mult
LD BC,(xpos) ; dann die X-Koordinate des Textes
ADD HL,BC ; addieren, um die Bildschirmkoordinate
POP DE ; des Punktes zu erhalten
EX DE,HL
;
PUSH DE
CALL swaphl ; Das Vorzeichen der Y-Position auf 
EX DE,HL ; der Matrix (HL) tauschen und mit der
LD BC,(yvergr) ; Y-Vergr. multiplizieren,
CALL mult
LD BC,(ypos) ; dann die Y-Koordinate des Textes
ADD HL,BC ; addieren, um die Bildschirmkoordinate
POP DE ; des Punktes zu erhalten 
;
LD A,(xlinie) ; Zaehler X auf X-Liniendicke setzen
liniex:PUSH DE ; Koordinaten und Zaehler retten
PUSH HL
PUSH AF
;
LD A,(ylinie) ; Zaehler Y auf Y-Liniendicke setzen
liniey:PUSH AF ; Zaehler und Koordinaten retten
PUSH HL
PUSH DE
;
LD A,(punkt1) ; Schauen, ob Punkt unten
AND A ; belegt ist
JR Z,askp2 ; -> weiter, falls nicht
CALL move ; sonst Linie vom aktuellen 
LD DE,#0000 ; Matrixpunkt mit der Laenge yvgneg
LD HL,(yvgneg) ; zum Punkt unten ziehen
CALL drawr
;
askp2:LD A,(punkt2) ; Schauen, ob Punkt links belegt ist
AND A
JR Z,askp3 ; -> weiter, falls nicht
POP DE ; sonst Koordinaten holen
POP HL
PUSH HL
PUSH DE
CALL move ; Linie vom aktuellen Matrixpunkt 
LD HL,#0000 ; zum Punkt links ziehen
LD DE,(xvgneg)
CALL drawr
;
askp3:LD A,(punkt3) ; Schauen, ob Punkt links unten
AND A ; belegt ist
JR Z,askp4 ; -> weiter, falls nicht
POP DE ; sonst Koordinaten holen
POP HL
PUSH HL
PUSH DE
CALL move ; Linie vom aktuellen Matrixpunkt
LD DE,(xvgneg) ; zum Punkt links unten ziehen
LD HL,(yvgneg)
CALL drawr
;
askp4:LD A,(punkt4) ; Schauen, ob Punkt rechts unten
AND A ; belegt ist  
JR Z,endy ; -> weiter, falls nicht
POP DE ; sonst Koordinaten holen
POP HL
PUSH HL
PUSH DE
CALL move ; Linie vom aktuellen Matrixpunkt
LD DE,(xvergr) ; zum Punkt rechts unten ziehen
LD HL,(yvgneg)
CALL drawr
;
endy:POP DE ; Koordinaten und Zaehler Y holen
POP HL
POP AF
AND A ; wenn Zaehler Y=0
JR Z,endx ; -> weiter mit Zaehler X
LD I,A ; Sonst Zaehler Y merken
POP AF ; Zaehler X holen
PUSH AF
AND A
JR Z,zlinie ; wenn Zaehler x=0 oder 
LD BC,(xlinie) ; Zahler x=Liniendicke X, 
CP C ; dann -> Zaehler Y vermindern
JR Z,zlinie
XOR A ; sonst muss Zaehler Y=0 sein
ADD HL,BC ; (Optimierung), Koord. korrigieren
JR liniey ; Schleife noch mal
;
zlinie:LD A,I ; Zaehler Y zurueckholen
DEC A ; Zaehler vermindern
DEC A
INC HL ; Bildschirmkoordinate erhoehen
INC HL
JR liniey ; Schleife noch mal
;
endx:POP AF ; Zaehler X und Koordinaten holen
POP HL
POP DE
AND A
JR Z,finish ; fertig, wenn Zaehler=0
DEC A ; sonst Zaehler vermindern
DEC A
INC DE ; Koordinate erhoehen
INC DE
JP liniex ; Schleife noch mal
;
finish:POP DE ; Koordinaten auf der Matrix holen
POP HL
RET ; Zurueck zum Hauptprogramm
;
; universelle Scroll-Routine
;
scroll:CP #01 ; Ein Parameter?
JR NZ,ziel ; falls nicht, nur ein Mal scrollen
LD A,(IX+0) ; sonst Zaehler mit 
scrlop:AND A ; Anzahl der Scrolls laden
RET Z ; Fertig, wenn Zaehler=0
PUSH AF ; Zaehler merken
LD B,#F5
frame:IN A,(C) ; Warten auf Frame fly back
RRA
JR NC,frame
CALL ziel ; Scroller aufrufen
POP AF
DEC A ; Zaehler vermindern
JR scrlop ; noch Mal 
;
; eigentlicher Scroll
;
ziel:LD DE,#C000 ; Adresse der oberen Bildschirmzeile
quelle:LD HL,#C800 ; Adresse der Zeile darunter
laenge:LD BC,#C750; Anzahl Zeilen und Breite einer Zeile
DI ; Beschleunigung
copy:PUSH BC ; alles merken
PUSH DE
PUSH HL
LD B,#00 ; Anzahl Zeilen ausblenden
LDIR ; Zeile umkopieren
POP HL ; Register zurueck
POP DE
POP BC
EX DE,HL
CALL nexthl ; Naechste Zeile von DE
EX DE,HL
CALL nexthl ; Naechste Zeile von HL
DJNZ copy ; Nochmal bis B=0
;
blanks:LD B,#01 ; Anzahl der aufzufuellenden Zeilen
fill:LD H,D ; DE ein Byte rechts von HL
LD L,E
INC DE
PUSH BC ; Register merken
PUSH HL
patter:LD (HL),#00 ; Erstes Byte mit Muster fuellen
anzahl:LD BC,#004F ; Breite der restlichen Zeile in BC
LDIR ; restliche Zeile fuellen
POP HL ; Register holen
POP BC
CALL nexthl ; Naechste Zeile von HL
EX DE,HL
DJNZ fill ; Nochmal bis B=0
EI ; Interrupts wieder zulassen
RET ; fertig
;
; Hilfsprogramm: Groesse des Scrolls patchen
;
size:CP #06 ; Sechs Parameter?
RET NZ ; -> Abbruch wenn nicht
LD A,(IX+2) ; Scrollschritt muss >0 sein
AND A
RET Z
LD A,(IX+6) ; Breite mindestens 2
CP 2
RET C
ADD A,(IX+10) ; X+Breite hoechstens 80
CP 82
RET NC
LD A,(IX+4) ; Hoehe mindestens 1
AND A
RET Z
ADD A,(IX+8) ; Y+Hoehe hoechstens 25 
CP 27
RET NC
LD A,(IX+8) ; Zaehler mit Y laden
LD HL,#C000 ; Anfang des Bildschirms
LD BC,#0050 ; Offset fuer 8 (Grafik-) Zeilen
addy:DEC A ; Zur Startadresse so oft das Offset
JR Z,addx ; dazuzaehlen, bis A=0
ADD HL,BC
JR addy
addx:LD D,A ; schliesslich noch X addieren
LD E,(IX+10)
DEC DE
ADD HL,DE ; heraus kommt die Adresse der
LD (ziel+1),HL ; Oberen Zeile
LD A,(IX+2) ; Schittweit bei Anzahl der zu 
LD (blanks+1),A ; fuellenden Zeilen direkt eintragen, 
LD B,A
PUSH BC ; aber vorerst merken
addste:CALL nexthl ; Bildschirmadresse weiterrechnen
DJNZ addste ; bis B=0
LD (quelle+1),HL ; dann als untere Zeile eintragen
LD A,(IX+6) ; die Breite auch direkt eintragen
LD (laenge+1),A
DEC A ; zu fuellen ist aber 1 Byte weniger
LD (anzahl+1),A
LD A,(IX+4) ; Die Hoehe*8 abzueglich der Schritt-
SLA A ; weite ergibt die Anzahl der Zeilen,
SLA A ; die umkopiert werden muessen
SLA A
POP BC
SUB B
LD (laenge+2),A
LD A,(IX+0) ; Das Muster direkt eintragen
LD (patter+1),A
RET ; Fertig!
;
; 16-Bit Multiplikation: HL = BC * DE  
;
mult:AND A ; Carry=0
LD HL,#0000 ; Ergebnis mit 0 ansetzen
LD A,#10 ; 16 Bit zu Multiplizieren
mult1:ADD HL,HL ; Ergebnis nach links rotieren
RL E ; 1. Faktor nach links rotieren
RL D
JR NC,mult2 ; weiter, falls kein Uebertrag
ADD HL,BC ; sonst 2. Faktor addieren
JR NC,mult2
INC DE ; Ueberlauf vermerken
mult2:DEC A ; noch mal, bis A=0
JR NZ,mult1
RET ; Fertig!
;
; Patch - Routine, um das "à" zu sparen
;
unloc:LD A,E
CP #0D ; Type mismatch ?
RET NZ ; -> Nein
LD HL,(error1) ; Aktuelle Statementadresse
CALL error2 ; Blanks ueberlesen
CP #7C ; RSX-Querstrich ?
JR Z,unloc2 ; oder
CP #83 ; CALL-Token ?
RET NZ ; -> Nein
unloc2:POP HL
LD HL,(error3) ; "à" nachahmen
CALL error4
SCF
RET
;
; naechste Bildschirmzeile in HL
;
nexthl:LD A,H ; Offset fuer eine Zeile (#800)
ADD A,#08 ; addieren
LD H,A
RET NC
SUB #40 ; falls Uebertrag, Offset fuer ganzen
LD H,A ; Bildspeicher (#4000) subtrahieren
LD A,L ; und Offset fuer 8 Zeilen (#50)
ADD A,#50 ; addieren
LD L,A
RET NC
INC H ; falls Uebertrag, H korrigieren
RET ; Endgueltig fertig
;
; Vorzeichenwechsel in HL
;
swaphl:PUSH DE ; DE retten
XOR A ; Carry = 0
EX DE,HL
LD HL,#0000
SBC HL,DE ; gewuenschte Zahl von 0 abziehen
POP DE
RET ; Fertig!