← ST-Computer 02 / 1992

Hardcopy? Aber bitte Auflösungsunabhängig

Programmierpraxis

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:

  • Ausdruck in verschiedenen Größen (ohne auf das Kontrollfeld als Accessory angewiesen zu sein)
  • Ausdruck in verschiedenen Qualitätsstufen
  • keine Belegung eines Accessory-Platzes
  • installieren und vergessen

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
Heinrich Emmerl