Als vor Jahren die ersten ST-Modelle auf den Markt kamen, standen die Besitzer VON 24-Nadeldruckern bald im Regen. Die Hardcopy-Routine des Betriebssystems liess ihnen keine andere Wahl, als sich fremde Programme zu beschaffen, die mit dieser Sorte von Druckern klarkamen. Schnell überschwemmten einfache, aber auch sehr luxuriöse Programme dieser Art den PD-Bereich.
Sicher nennt mittlerweile schon jeder ST-Benutzer mindestens solch ein Hardcopy-Programm sein Eigentum. Lange Zeit krähte kein Hahn mehr nach einer Hardcopy-Routine, das Thema war abgehakt und vergessen. Mit dem Aufkommen der Low-Cost-Grafikkarten (z.B. OVERSCAN) stellt sich erneut eine Schwierigkeit ein: Die höhere Auflösung ist für die meisten derartigen Programme ein Problem. Statt des erwarteten Bildschirminhalts schwärzen lediglich wirre Streifenmuster das Papier. Niemand konnte wohl vorausahnen, wie schnell und einfach die Grenzen des festen 32k-Bildschirms gesprengt werden würden.
Auch beim Verfasser füllen zahlreiche Hardcopy-Programme die PD-Schachteln. Unter den gängigen war keines zu finden, das mit der höheren Monochromauflösung zurechtkam. Selbst die entsprechende völlig neu gestaltete Routine des (vielleicht zu unrecht) viel gelobten KAOS 1.4.2 arbeitet mit festen Bildschirmgrenzen. Das der OVERSCAN-Software beigepackte OVER_24N.PRG erfüllte nicht in jeder Hinsicht die Erwartungen, die an ein solches Programm gestellt wurden:
So entstand an einigen verregneten Sommernachmittagen das folgende Programm für ST-Rechner mit erweiterten Bildschirmformaten im Monochrommodus. Es wird als letztes im AUTO-Ordner gestartet und mit der üblichen Tastenkombination ALT-HELP aufgerufen. Es stellt sich auf die jeweilige Auflösung selbst ein, natürlich auch auf die Originalauflösung 640 x 400. Es bietet drei Druckgrößen in jeweils zwei unterschiedlichen Qualitätsstufen, was für die problemlose Dokumentation des Bildschirmgeschehens in den meisten Fällen ausreicht, wenn man nicht gerade zu den Tosterproduzenten’ gehört.
Das mit dem GFA-Assembler erstellte Programm enthält zunächst die bekannten Programmzeilen, um es resident im Speicher zu halten. Es ‘verbiegt’ in der üblichen Weise den Hardcopy-Vektor auf ‘sich selbst’ (Adresse Start:) und lauert auf das Eintreffen der Hardcopy-Anforderung in der Systemvariable $4EE. Anschließend werden die Werte ermittelt, die von der herkömmlichen Bildschirmdarstellung im 32k-Format abweichen. Dazu werden LINE-A-Variablen benutzt, die ja angeblich auch dann noch Gültigkeit haben, wenn in etwaigen künftigen TOS-Versionen die LINE-A-Funktionen nicht mehr enthalten sein werden.
Hier die Werte, die das Programm benötigt, der Reihe nach:
log_base: Adresse des Bildschirmspeichers (TOS sei Dank ebenso einfach zu ermitteln wie in der Originalauflösung)
bytpline: Anzahl der Bytes pro Bildschirmzeile einschließlich eventueller unsichtbarer Füll-Bytes bei den Grafikerweiterungen
scanlins: Anzahl der Bildschirmzeilen (y-Auflösung)
xresolut: Anzahl Punkte pro Zeile (x-Auflösung)
colpline: tatsächlich sichtbare Bytes pro Zeile (ohne Füll-Bytes)
Zur Information des Benutzers erscheint nach jeder Betätigung von ALT-HELP eine Menüinformation in der rechten oberen Bildschirmecke:
###########################
# HARDCOPY AUF EPSON LQ #
# #
# Kleinformat 180 dpi: 1 #
# Normalformat 180 dpi: 2 #
# Querformat 180 dpi: 3 #
# Kleinformat 360 dpi: 4 #
# Normalformat 360 dpi: 5 #
# Querformat 360 dpi: 6 #
# Abbruch 0 #
# #
# -Bitte Ziffer drücken!- #
###########################
Der dafür benötigte Bildschirmbereich wird vorher in einen Puffer gerettet und nach dem Drücken der gewünschten Taste wieder restauriert. Das Programm verzweigt dann zu den drei unterschiedlichen Darstellungen, im Listing durch die Labels klein:, normal: und quer: gekennzeichnet.
Es wäre nun müßig, die einzelnen Programmschritte Schritt für Schritt ausführlich zu diskutieren. Im Listing finden sich entsprechende Hinweise in den Kommentaren. In jedem Programmteil wird durch entsprechende Abfragen sichergestellt, daß der sichtbare Bildschirmbereich nicht überschritten wird. Sollte dies der Fall sein, werden ‘überhängende’ Bits gelöscht, damit keine schwarzen Streifen oder gar Speichermüll auf dem Papier erscheinen.
Im Format ‘klein’ beginnt das Programm in der linken unteren Bildschirmecke und arbeitet sich Zeile für Zeile nach oben durch. Dabei gelangen jeweils drei Bytes zusammenhängend an die 24 Nadeln des Druckers.
Im Normalformat liegt der Startpunkt am Bildschirm links oben. In der Vertikalen werden die Bildpunkte verdoppelt, in der Horizontalen verdreifacht, damit bei der gewählten Druckdichte keine Verzerrungen entstehen.
Beim Querformat schließlich beginnt das Programm auf dem Bildschirm oben rechts. Jeder Bildpunkt pro Zeile wird dreifach ausgegeben, jede Bildzeile wiederum sechsfach. So entsteht eine unverzerrte Vergrößerung.
In jedem Programmabschnitt wird eine komplette Druckzeile jeweils vor dem Ausdruck erst in einem Puffer zwischengespeichert. In der Auflösung ‘360 dpi’ kann so nach Ausführen eines 1/360-Zoll-Vorschubs die gleiche Zeile erneut ausgegeben werden, was eine schwärzere Druckqualität zur Folge hat.
Leider haben sich die großen Druckerhersteller immer noch nicht auf kompatible Steuercodes einigen können. Im Listing sind bei den Unterprogrammen lf47_360: und lf1_360: die passenden Werte für EPSON-kompatible Drucker angegeben. Besitzer von NEC-kompatiblen Druckern finden ‘ihre’ Werte als Kommentare vor.
Wenn man ‘nur’ einen DIN-A4-Drucker hat, sollte man beim Ausdruck höherer Bildschirmauflösungen vorsichtig sein. Der gute alte SM 124 des Verfassers verkraftet eben noch 704 x 480 Bildpunkte. Im Normal- und Querformat passen die Hardcopies dieser Größe gerade noch aufs Papier, wobei man bei Querformat bereits Endlospapier verwenden muß. Einzelne Blätter lassen sich in diesem Format nicht mehr ganz bedrucken. Zur Illustration hier eine Hardcopy in der Originalauflösung 640 x 400, daneben eine in der Größe 704 x 480.
;Hardcopy in verschiedenen Modi, monochrom, auflösungsunabhängig
;Programm resident (für EPSON LQ bzw. NEC)
xbios equ 14
gemdos equ 1
pea patch
move.w #38,-(sp) ; Supervisor
trap #xbios
addq.l #6,sp
clr.w -(sp)
move.l #ende-start+1000,-(sp)
move.w #$31,-(sp) ; keep process
trap #gemdos ; Programm resident halten
patch: movea.l $456,a0 ; Hardcopy-Vektor
adda.l #28,a0 ; (vblqueue+28)
move.l #start,(a0) ; auf dieses Programm umlenken
rts
start: tst.w $4ee ; Hardcopy gewünscht?
beq.s hcopy
rts
hcopy: move.w #3,-(sp) ; logbase
trap #xbios
addq.l #2,sp
move.l d0,log_base ; Bildschirmadresse merken
.DC.w $a000 ; Line-A-Init
addq.l #2,a0 ; Bildschirmwerte ermitteln
move.w (a0),bytpline ; Bytes pro Zeile (inkl. Rücklauf)
subq.l #6,a0
move.w (a0),scanlins ; Zeilen pro Bild
subq.l #8,a0
move.w (a0),xresolut ; hor. Auflösung
suba.l #32,a0
move.w (a0),colpline ; sichtbare Bytes pro Zeile
movea.l log_base,a0 ; Bildbereich retten
lea.l put(pc),a1
clr.l d4
move.w bytpline,d4
subi.w #27,d4 ; Breite von 27 Bytes abziehen
move.l #223,d3 ; Höhe 224 Zeilen
pufzeile: move.l #26,d2 ; Breite 27 Bytes
pufbytes: move.b (a0)+,(a1)+
dbra d2,pufbytes
adda.l d4,a0
dbra d3,pufzeile
taste: pea menue ; Menü ausgeben
move.w #$9,-(sp)
trap #gemdos
addq.l #6,sp
move.w #1,-(sp) ; auf Tastendruck warten
trap #gemdos
addq.l #2,sp
move.b d0,format ; Taste merken
movea.l log_base,a0 ; Bildbereich restaurieren
lea.l put(pc),a1
clr.l d4
move.w bytpline,d4
subi.w #27,d4
move.l #223,d3
scrzeile: move.l #26,d2
scrbytes: move.b (a1)+,(a0)+
dbra d2,scrbytes
adda.l d4,a0
dbra d3,scrzeile
cmpi.b #"0",d0 ; welche Taste?
beq exit
cmpi.b #"1",d0
beq klein
cmpi.b #"4",d0
beq klein
cmpi.b #"2",d0
beq normal
cmpi.b #"5",d0
beq normal
cmpi.b #"3",d0
beq quer
cmpi.b #"6",d0
beq quer
bra taste
exit: move.w #-1,$4ee ; Hardcopy beendet
rts
;---------FORMATE 1 UND 4----------
klein: clr.l d1
clr.l d2
move.w bytpline,d2
clr.l d3
clr.l d7
movea.l log_base,a3 ; ganz unten links beginnen
move.w scanlins,d3
subq.w #1,d3
mulu.w bytpline,d3
nextcol1: adda.l d3,a3
lea.l put(pc),a1
move.w scanlins,d1 ; Zähler für Bildzeilen
subq.w #1,d1
nextlin1: move.b (a3)+,(a1)+
move.b (a3)+,(a1)+
move.b (a3)+,(a1)+
suba.l d2,a3 ; eine Zeile hoch
suba.l #3,a3
dbra d1,nextlin1
bsr druckez1
bsr lf1_360
cmpi.b #"1",format ; bei Format 1 nur 1 x drucken
beq.s zeilferl
bsr druckez1 ; Format 4, 2x drucken
zeilferl: bsr lf47_360
subq.w #3,colpline ; wieder drei columns geschafft!
bmi.s ende1
tst.w $4ee ; ist Hardcopy noch erwünscht?
bne.s ende1
addq.l #3,a3
adda.l d2,a3
bra nextcol1
ende1: bsr newline
bra exit
;---------FORMATE 2 UND 5---------
normal: movea.l log_base,a3
movea.l a3,a4 ; a4 enth. maximal zulässige Bildadresse
clr.l d2 ;=logbase + Bytes pro Zeile x Scanlines
move.w bytpline,d2
mulu.w scanlins,d2
adda.l d2,a4
move.w bytpline,d2
clr.l d4
clr.l d6
move.l #12,d5
mulu.w bytpline,d5
nxtplin2: lea.l put(pc),a2 ; drei fertige Bytes in den Puffer
move.w colpline,d6
nextcol2: moveq.l #7,d7 ; 8 Bits testen
nextrow2: moveq.l #11,d0 ; 12 Bits bearbeiten
nextbyt2: asl.l #2,d3 ; D3 Zwischenspeicher für Dotkette
cmpa.l a3,a4 ; Bildadresse schon über Endwert?
bls.s no1_2 ; dann keine Bits mehr ausgeben
btst.b d7,(a3) ; prüfen, ob Bit gesetzt ist (1)
beq.s no1_2 ; 00 hinten lassen
ori.l #3,d3 ; 11 hinten anreihen
no1_2: adda.l d2,a3 ; darunterliegendes Byte holen
dbra d0,nextbyt2
move.b d3,2(a2) ; drei Bytes in den Ausgabepuffer
lsr.l #8,d3
move.b d3,1(a2)
lsr.l #8,d3
move.b d3,{a2)
addq.l #3,a2
suba.l d5,a3 ; wieder in die oberste Zeile zurück
dbra d7,nextrow2
adda.l #1,a3 ; nächste Bytekolumne rechts daneben
dbra d6,nextcol2
bsr druckez2
bsr lf1_360
cmpi.b #"2",format ; bei Format 2 nur 1 x drucken
beq.s zeilfer2
bsr druckez2 ; Format 5, 2 x drucken
zeilfer2: bsr lf47_360
cmpa.l a3,a4 ; Bildadresse schon über Endwert?
bls.s ende2 ; dann abbrechen
tst.w $4ee ; Hardcopy noch gewünscht?
bne.s ende2
adda.l d5,a3 ; in die nächste 12Bit-Zeile
clr.l d0
move.w colpline,d0
suba.l d0,a3 ; an den Zeilenanfang zurückstellen
subq.l #1,a3
bra nxtplin2
ende2: bsr newline
bra exit
;---------FORMATE 3 UND 6-----------
quer: clr.l d1
movea.l log_base,a3
move.w colpline,d1
adda.l d1,a3 ; oben rechts beginnen
move.w bytpline,d1
clr.l d4
clr.l d5
move.w bytpline,d5 ; Zeilen mal Spalten
mulu.w scanlins,d5
clr.l d6
nextrow3: lea.l put(pc),a2
move.w scanlins,d6 ; soviel Zeilen im RAM umrechnen
subq.l #1,d6
nxtbyte3: clr.b d2 ; Maske auf 1.Bit
moveq.l #7,d0 ; 8 Bits des Bildschirmbytes testen
nextbit3: asl.l #3,d3
tst.w colpline ; schon alle sichtbaren col. fertig?
bmi.s no1_3 ; dann kein Bit mehr testen
btst.b d2,(a3)
beq.s no1_3 ; 000 hinten lassen
ori.l #7,d3 ; 111 hinten einfüllen
no1_3: addq.b #1,d2 ; Maske auf nächstes Bit
dbra d0,nextbit3
move.b d3,2(a2) ; eine Spalte 2x in Puffer (dopp.Dichte!)
move.b d3,5(a2)
lsr.l #8,d3
move.b d3,1(a2)
move.b d3,4(a2)
lsr.l #8,d3
move.b d3,(a2)
move.b d3,3(a2)
addq.l #6,a2
adda.l d1,a3 ; nächste Zeile im RAM
dbra d6,nxtbyte3
bsr druckez3
bsr lf1_360
cmpi.b #"3",format ; bei Format 3 nur 1 x drucken
beq.s zeilfer3
bsr druckez3 ; Format 6, 2 x drucken
zeilfer3: bsr lf47_360
subq.w #1,colpline ; wieder eine column geschafft!
bmi.s ende3
tst.w $4ee ; ist Hardcopy noch erwünscht?
bne.s ende3
suba.l d5,a3 ; nächste Reihe links daneben im RAM
subq.l #1,a3
bra nextrow3
ende3: bsr newline
bra exit
; —-------UNTERPROGRAMME----------
out: move.w d7,-(sp)
move.w #5,-(sp) ; ein Zeichen an den Drucker
trap #gemdos
addq.l #4,sp
rts
lf47_360: move.w #27,d7 ; 1 x Linefeed 47/360 inch ausgeben
; ;NEC: move.w #28,d7
bsr.s out
move.w #43,d7
; ;NEC: move.w #51,d7
bsr.s out
move.w #47,d7
bsr.s out
bra.s newline
lf1_360: move.w #27,d7 ; 1 x Linefeed 1/360 inch auageben
; ;NEC: move.w #28,d7
bsr.s out
move.w #43,d7
; ;NEC: move.w #51,d7
bsr.s out
move.w #1,d7
bsr.s out
newline: move.w #13,d7
bsr.s out
move.w #10,d7
bsr.s out
rts
druckez1: move.w scanlins,d1 ; Ausgeben einer Druckzeile im Kleinformat
move.l d1,d5
subq.l #1,d1
lea.l put (pc),a2
move.w #27,d7
bsr out
move.w #42,d7
bsr out
move.w #39,d7
bsr out
move.b d5,d7 ; Drucker auf Anzahl Bytes vorbereiten
bsr out ; lobyte
ror.l #8,d5
move.b d5,d7
bsr out ; hibyte
nextdrl1: move.b (a2)+,d7 ; Daten senden
bsr out
move.b (a2)+,d7
cmpi.w #1,colpline ; wenn 2 col. Überhang, diese löschen
bge.s drucken
clr.l d7
drucke11: bsr out
move.b (a2)+,d7
cmpi.w #1,colpline ; wenn 1 col. Überhang, diese löschen
bgt.s drucke21
clr.l d7
drucke21: bsr out
dbra d1,nextdrl1
rts
druckez2: move.w xresolut,d1 ; Ausgeben einer Druckzeile im Normalformat
move.l d1,d4
subq.l #1,d1
lea.l put(pc),a2
move.l #27,d7 ; Drucker vorher,
bsr out
move.l #42,d7
bsr out
move.l #38,d7
bsr out
move.b d4,d7 ; lobyte
bsr out
ror.l #8,d4 ; hibyte
move.b d4,d7
bsr out
nextdrl2: move.b (a2)+,d7
bsr out
move.b (a2)+,d7
bsr out
move.b (a2)+,d7
bsr out
dbra d1,nextdrl2
rts
druckez3: move.w scanlins,d6 ; Ausgeben einer Druckzeile im Querformat
move.w d6,d4
mulu.w #2,d4 ; es werden 2 x Anzahl Scanlines ausgeg.
subq.l #1,d6
lea.l put(pc),a2 ; aus Puffer
move.l #27,d7 ; Drucker auf Anzahl Bytes vorbereiten
bsr out
move.l #42,d7
bsr out
move.l #33,d7
bsr out
move.b d4,d7
bsr out ;lobyte
ror.l #8,d4
move.b d4,d7 ;hibyte
bsr out
nextdrl3: move.b (a2)+,d7
bsr out
move.b (a2)+,d7
bsr out
move.b (a2)+,d7
bsr out
move.b (a2)+,d7
bsr out
move.b (a2)+,d7
bsr out
move.b (a2)+,d7
bsr out
dbra d6,nextdrl3
rts
/---------DATENBEREICH-----------
log_base: .DS.l 1
bytpline: .DS.w 1
scanlins: .DS.w 1
xresolut: .DS.w 1
colpline: .DS.w 1
format: .DS.b 1
.EVEN
menue: .DC.b 27,"H"
.DC.b "###########################",13, 10
.DC.b "# HARDCOPY AUF EPSON LQ #",13,10
.DC.b "# Heinrich Emmerl #",13,10
.DC.b "# #",13,10
.DC.b "# Kleinformat 180 dpi: 1 #",13,10
.DC.b "# Normalformat 180 dpi: 2 #",13,10
.DC.b "# Querformat 180 dpi: 3 #",13,10
.DC.b "# Kleinformat 360 dpi: 4 #",13,10
.DC.b "# Normalformat 360 dpi: 5 #",13,10
.DC.b "# Querformat 360 dpi: 6 #",13,10
.DC.b "# Abbruch : 0 #",13,10
.DC.b "# #",13,10
.DC.b "# -Bitte Ziffer drücken!- #",13,10
.DC.b "###########################",27,"H",0
.EVEN
put: .DS.l 1512 ; Puffer für Bildschirmbereich (Menü!)
ende: .EVEN