Beim Drücken von PrtScreen wird im Hintergrund ein DOS-Interrupt 05h ausgelöst, welcher eine sich im BIOS (Basic Input Output System) befindliche Hardcopy-Routine aufruft. Diese eingebaute Hardcopy-Routine tut von sich aus eigentlich nicht mehr als folgendes BASIC-Programmfragment:
SUB Hardcopy FOR Zeile%=1 TO 25 FOR Spalte%=1 TO 80 LPRINT CHR$(SCREEN(Zeile%,Spalte%)); NEXT Spalte% LPRINT ' Zeilenumbruch NEXT Zeile% END SUB
Mit Hilfe des Wissens, dass Interrupt 05h ausgelöst wird, können Sie eine kleine Assembler-Routine
int 05h retf
ausführen. In der Hilfedatei QBASIC.HLP von
QBASIC.EXE befindet sich sogar bei CALL ABSOLUTE
folgendes Beispiel:
'Ruft eine Routine auf, die den Bildschirminhalt auf dem Drucker druckt DIM a%(2) DEF SEG = VARSEG(a%(0)) FOR i% = 0 TO 2 READ d% POKE VARPTR(a%(0)) + i%, d% NEXT i% DATA 205, 5, 203 : ' int 5 retf 'Code in Maschinesprache 'zum Drucken des Bildschirminhalts. CALL ABSOLUTE(VARPTR(a%(0))) DEF SEG
Mit dem QuickBASIC-Compiler können Sie sogar CALL
INTERRUPT
direkt einsetzen:
' $INCLUDE: 'qb.bi' DIM dosIntEin AS RegType, dosIntAus AS RegType CALL INTERRUPT(&H5, dosIntEin, dosIntAus)
Da INT 05h keine Parameter erwartet, muss dosIntEin
nicht
initialisiert werden. Die INT 05h-Hardcopy-Routine existiert beim PC schon seit
Beginn mit dem IBM-PC/XT.
Grafik gab es beim IBM-PC schon sehr früh (CGA- und Hercules-Karte), daher spendierten IBM und Microsoft bereits in DOS 2.x das DOS-Hilfsprogramm GRAPHICS.COM: Dieses TSR-Programm (TSR=Terminate and Stay Resident) biegt den Interrupt-Vektor 05h auf eine eigene Routine um, so dass zuerst der aktuelle Videomodus ermittelt wird, danach entsprechend entweder die BIOS-Routine im Falle von Text oder bei Grafik eben die eigene Routine aufgerufen wird.
Seit Windows 95 ist GRAPHICS.COM nicht mehr im Lieferumfang; Sie müssen daher wie unter Bezug von QuickBASIC beschrieben die Datei OLDDOS.EXE herunterzuladen und installieren. Aufrufbeispiel für einen HP DeskJet-Drucker:
C:\>graphics /b/r hpdeskjet
Ansonsten liefert in MS-DOS 6.2x
C:\>help graphics
eine ausführliche Anleitung zu GRAPHICS.COM. Allerdings hat dieser Weg einen erheblichen Nachteil: Da GRAPHICS.COM nicht mehr von Microsoft weitergepflegt wird, können Sie dementsprechend nur einige wenige, sehr alte Druckermodelle ansteuern, die Druckertreiberliste entspricht also jener vor zehn Jahren! Falls Sie noch einen solchen Drucker (z.B. IBM ProPrinter, Epson-kompatibler 9- oder 24-Nadel-Matrixdrucker, HP DeskJet der ersten Generation) besitzen (stellt jemand überhaupt noch Farbbänder zu diesen Geräten her?), dann sollte es keine Probleme geben. Falls Sie aber üblicherweise einen dieser heutzutage typischen Windows-Billigdrucker verwenden, bei welchen der Hersteller schon lange kein Programmer's Reference Manual belegt, sondern ausschliesslich über den Windows 95/98/NT-Druckertreiber des Herstellers funktioniert, dann haben Sie leider :-( Pech. Wohl könnten Sie versuchen mit Umleiten in eine Datei oder mit Systemsteuerung/Drucker/Verbinden den Anschluss auf FILE: anstelle von LPT1: umstellen, um die Druckerausgabe in eine Datei umzuleiten und mit Hilfe von Norton Commander oder einem ähnlichen Werkzeug den Inhalt dieser Druckerdatei als Hex-Dump betrachten, um den Befehlssatz herauszufinden. Allerdings dürfte dieses Vorhaben bei den heutigen Druckern kaum zu einem Erfolg führen, denn vielfach wird Datenkompression eingesetzt, ebenso mit sog. GDI-Aufrufen (GDI=Graphic Device Interface) gearbeitet, so dass Sie nur mit einem Byte-Wirrwarr begrüsst werden. Ausserdem kommt noch dazu, dass die ganz modernen Drucker bidirektional kommunizieren, um den Füllstand der Tintenpatrone beispielsweise zu melden.
Als ich vor 15 Jahren meine Hobby-Computerei startete, gab es noch keine fertigen Druckertreiber, sondern jeder Hersteller dokumentierte in Form von kleinen Beispiel-BASIC-Programmen die einzelnen Drucker-Features. Dies wandelte sich jedoch ziemlich rasch seit der Einführung von Windows 3.x, als sich eben der Personal-Computer zum reinen Anwendungsgerät hochmauserte, denn von einem Anwender, der lediglich seine Geschäftszahlen mit Harvard Graphics präsentieren möchte, wollte man keine Programmierkenntnisse verlangen, um HG aufsetzen zu können.
Allerdings haben Sie heute dank Internet eine gute Chance, mit geeigneten Suchen in AltaVista und sonstigen Suchdiensten Entwicklerunterstützung (engl. Developer Support) zu Ihrem Drucker besorgen zu können, am besten kontaktieren Sie den Hersteller selber via e-Mail oder suchen in dessen Technical Support-WWW-Bereich nach Entwicklerdokumentationen ab. Dabei sollten Sie den (meist englischsprachigen) internationalen Hauptsitz anpeilen, da die regionalen Ländervertretungen in der Regel nur Verkauf betreiben.
Nach dieser längeren Diskussion betreffend Druckerprogrammierung habe ich unbemerkt bereits die nächste Variante angeschnitten:
Für Textdruck (alle Variationen von SCREEN 0
) reicht im
Prinzip die zu Beginn dieses Artikels aufgeführte
Bildschirmausdruckroutine bereits aus:
SUB Hardcopy FOR Zeile%=1 TO 25 FOR Spalte%=1 TO 80 LPRINT CHR$(SCREEN(Zeile%,Spalte%)); NEXT Spalte% LPRINT ' Zeilenumbruch NEXT Zeile% END SUB
Allerdings hat sie noch ein paar erhebliche Mängel: LPRINT
sendet die Zeichen cooked. Um dies zu verstehen, sollten Sie
am besten Ihren Drucker in den sog.
Hex-Dump-Modus umschalten (ältere Drucker haben dies noch, die
neuen Modelle jedoch meist nicht mehr). Nun probieren Sie bitte einmal
folgendes Programm aus:
FOR i%=0 TO 255 LPRINT CHR$(i%); NEXT i%
Unsere Erwartung: Es sollten doch exakt 256 Bytes erscheinen:
0000 : 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 0010 : 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 0020 : 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F 0030 : 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F 0040 : 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 0050 : 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F 0060 : 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 0070 : 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F 0080 : 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F 0090 : 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F 00A0 : A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF 00B0 : B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF 00C0 : C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF 00D0 : D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF 00E0 : E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF 00F0 : F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
Falls Sie diesen Versuch aber richtig durchführen, werden Sie jedoch folgendes erhalten:
0000 : 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0A 0E 0010 : 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 0020 : 1F 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 0030 : 2F 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 0040 : 3F 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 0050 : 4F 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 0060 : 5F 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 0070 : 6F 0D 0A 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 0080 : 7D 7E 7F 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 0090 : 8D 8E 8F 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 00A0 : 9D 9E 9F A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC 00B0 : AD AE AF B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC 00C0 : BD BE BF 0D 0A C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA 00D0 : CB CC CD CE CF D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA 00E0 : DB DC DD DE DF E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA 00F0 : EB EC ED EE EF F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA 0100 : FB FC FD FE FF
Man stellt fest:
Dem letzteren Problem könnte man mit einem passenden WIDTH
LPRINT
begegnen (in GW-BASIC war meines Wissens 255 = unendliche Weite),
jedoch das erste Problem wäre damit noch nicht gelöst :-(. Aus diesem Grunde sollten wir LPRINT
besser durch PRINT#
ersetzen, wobei wir anstelle von
OPEN "lpt1:" FOR OUTPUT AS 1
besser
OPEN "lpt1" FOR OUTPUT AS 1
verwenden (man beachte den fehlenden Doppelpunkt!) => QBASIC
betrachtet nun das Gerät nicht mehr als Drucker, sondern als normale Datei
und unterlässt solche, wie oben gezeigte cooked-Veränderungen, es lässt also die Daten 1:1,
auch als raw bezeichnet, zum Drucker passieren :-). Zugleich haben wir jetzt den Vorteil, eben
beliebige Parallelports ansprechen zu können. Bei Netzwerkdrucker
unter Windows 95/98/NT Hinweise
zu modernen Betriebssystemumgebungen beachten.
So, dies zur Ansteuerung des Druckers aus QuickBASIC heraus. Ein Problem
haben wir jedoch noch: Der Drucker sollte jene Zeichen, welche einen ASCII-Wert
<32 besitzen, wörtlich als Grafiksymbol ausdrucken, also
CHR$(27)
sollte auch tatsächlich als Links-Pfeil erscheinen
und nicht einen Steuerbefehl (ESCape) einleiten. Im folgenden zeige ich Ihnen
dies für die beiden Drucker HP DeskJet und Fujitsu DL1100:
' Hardcopy-Routine ' für FUJITSU DL-1100 SUB Hardcopy OPEN "lpt1" FOR OUTPUT AS 1 FOR Zeile%=1 TO 25 FOR Spalte%=1 TO 80 PRINT#1, CHR$(27);"eE";RIGHT$("00"+MID$(STR$(SCREEN(Zeile%,Spalte%)),2),3); NEXT Spalte% PRINT#1, "" ' Zeilenumbruch NEXT Zeile% CLOSE 1 END SUB
Dieser 24-Nadeldrucker des Herstellers Fujitsu verlangt wörtlich zu
druckende Zeichen immer als <Esc>
"eEnnn"
mit nnn=dreistelliger
Dezimalwert.
Nun dasselbe für einen HP DeskJet, geschrieben für ein Gerät der ersten Generation aus dem Jahre 1989; ich vermute aber, dass die neueren Tintenstrahldrucker-Modelle von HP noch voll kompatibel sind:
' Hardcopy-Routine ' für HP DeskJet SUB Hardcopy OPEN "lpt1" FOR OUTPUT AS 1 PRINT#1,CHR$(27);"E";CHR$(27);"&k0W"; FOR Zeile%=1 TO 25 PRINT#1,CHR$(27);"&p80X"; FOR Spalte%=1 TO 80 PRINT#1,CHR$(SCREEN(Zeile%,Spalte%)); NEXT Spalte% PRINT#1, "" ' Zeilenumbruch NEXT Zeile% PRINT#1,CHR$(27);"&k1W";CHR$(12); CLOSE 1 END SUB
Bei den Druckern der Firma Hewlett Packard kommt ein anderes Prinzip zur
Anwendung: Angabe eines Befehls, welcher dem Drucker sagt, dass die
nächsten n Zeichen (Bytes!) eben wörtlich, d.h. ohne ihre Steuerzeichenfunktion zu drucken sind,
in diesem Fall also <Esc> "&pnX"
mit n=dezimaler Wert als ASCII-String.
Falls Ihre auszudruckenden Bildschirme viele solche Spezialzeichen verwenden und Sie sich auch schon geärgert haben, warum die Rahmenzeichendarstellung lauter solche Versätze
Unerwünschter Versatz durch ausgelassene Steuerzeichen
statt korrekt
gemacht hat, ist es genau das Richtige für Sie, mit der obigen Routine anstelle der BIOS-Routine zu arbeiten, denn die Standard-BIOS-Routine macht all diese Filterungen nicht!
Mit SCREEN(y%, x%, 1)
können Sie zusätzlich auf die
Farbinformation zurückgreifen:
Vordergrundfarbe% = SCREEN(y%, x%, 1) AND 15 Hintergrundfarbe% = SCREEN(y%, x%, 1) \ 16 AND 7 Blinkend% = SCREEN(y%, x%, 1) \ 128 ' blinkend (1) / nicht blinkend
Somit können Sie bei einem Farbdrucker sogar farbige
Hardcopies produzieren :-) Epson-kompatible
Drucker: <Esc> "rx"
mit
x=Farbe.
' Hardcopy-Routine in Farbe ' für FUJITSU DL-1100 Color ' Zu Beginn initialisieren DIM SHARED Uebersetz$(0 TO 7) FOR i%=0 TO 7 READ Uebersetz$(i%) NEXT i% DATA "0", "3", "6", "2", "5", "1", "4", "0" SUB Hardcopy OPEN "lpt1" FOR OUTPUT AS 1 FOR Zeile%=1 TO 25 FOR Spalte%=1 TO 80 PRINT#1, CHR$(27);"r";Uebersetz$(SCREEN(Zeile%,Spalte%) AND 7); PRINT#1, CHR$(27);"eE";RIGHT$("00"+MID$(STR$(SCREEN(Zeile%,Spalte%)),2),3); NEXT Spalte% PRINT#1, "" ' Zeilenumbruch NEXT Zeile% CLOSE 1 END SUB
In diesem Fall brauchen wir zum einen die Bitmap am Bildschirm auszulesen,
was mit der POINT
-Funktion kein Problem darstellt. Ebenso brauchen
wir noch die Farbpalette, und dies ist schon schwieriger, denn
QBASIC.EXE erlaubt Ihnen die Farben mit PALETTE
zu
setzen, jedoch nicht mehr auszulesen, da es keine
PALETTE(Farbnummer%)
-Funktion gibt! :-( Aber es gibt hier zwei Wege als Abhilfe:
PALETTE
im Sinne von:
DIM SHARED Rot%(0 TO 255), Gruen%(0 TO 255), Blau%(0 TO 255) DIM SHARED VMod% ' VMod%=1 : SCREEN-Modi 1,2,7,8 ' VMod%=2 : SCREEN-Modi 0,9 ' VMod%=3 : SCREEN-Modi 11,12,13 SUB RGBPalette (FaNr%, Rot%, Gruen%, Blau%) ' Die Farbanteile müssen, wie z.B. beim GIF-Grafikformat üblich, ' als Werte 0-255 angegeben werden, also (255,255,255) = weiss ' (0,0,0)=schwarz, (128,128,128) = grau usw. Rot%(FaNr%) = r% ' speichern! Gruen%(FaNr%) = g% Blau%(FaNr%) = b% SELECT CASE VMod% CASE 3 ' VGA-Grafikmodus mit 262144 Farben r% = (Rot% AND 252) \ 4 g% = (Gruen% AND 252) \ 4 b% = (Blau% AND 252) \ 4 PALETTE FaNr%, CLNG(r%) + 256& * CLNG(g%) + 65536 * CLNG(b%) CASE 2 ' EGA-Grafikmodi mit 64 Farben r% = (Rot% AND 192) \ 64 r% = (Rot% AND 192) \ 64 g% = (Gruen% AND 192) \ 64 b% = (Blau% AND 192) \ 64 PALETTE FaNr%, (r% AND 1) * 32 + (r% AND 2) * 2 + (g% AND 1) * 16 + (g% AND 2) + (b% AND 1) * 8 + b% \ 2 CASE 1 ' Bildung CGA-Farbe: Dominierender Farbton bestimmen Cga% = (Rot% AND 128) \ 32 + (Gruen% AND 128) \ 64 + (Blau% AND 128) \ 128 ' Selektion, ob hellerer oder dünklerer Farbton darstellen h% = (Rot% AND 127) + (Gruen% AND 127) + (Blau% AND 127) - 192 Cga% = Cga% - 8 * (h% > 0) PALETTE FaNr%, Cga% END SELECT END SUBBei dieser Variante dürfen Sie in Ihrer Anwendung einfach keine Farbe mit
PALETTE
direkt setzen, sondern müssen immer dieses
Unterprogramm dafür verwenden. Der Trick besteht darin, dass wir die
Farben in den drei Arrays Rot%(), Gruen%() und Blau%() speichern, so
dass sie uns für ein Hardcopy zur Verfügung stehen.PALETTE()
-Funktion müssen wir direkt auf die Routinen des
VGA-BIOS zurückgreifen. Dazu existiert INT 10h, Funktion 10h,
Unterfunktionen 15h (einzelne) und 17h (mehrere), um die RGB-Werte aus den
sog. DAC-Registern auslesen. DAC bedeutet
Digital Analog
Converter und ist derjenige Chip auf der Grafikkarte,
welcher aus den digitalen RGB-Werten das Farbsignal für den VGA-Monitor
produziert.SCREEN
-Bildschirmmodi 0,1,2,7,8 und 9 brauchen wir
noch zusätzlich INT 10h, Funktion 10h, Unterfunktion 07h (einzelne) und
09h (alle Farben) sowie 19h, um den Palettenindex mit DAC-Registergruppe
auslesen zu können. In der BMP-Bilddatei-Bibliothek
finden Sie in der Datei BMP_LIB.BAS, Prozedur SUB
LiesPalette (p&(), ind%, anz%)
den vollständigen Algorithmus
ausprogrammiert.Als Anregung für einen Bildschirmausdruck im Grafikmodus folgt eine
sehr einfach gehaltene Schwarzweiss-Hardcopy-Routine für einem Epson
LX-800 (9-Nadeldrucker mit ESC/P-Standardbefehlssatz), wobei als
Bildschirmmodus SCREEN 2
(nur schwarzweiss) verwendet wird:
' Hardcopy-Routine für einen Epson LX-800-Drucker ' Hauptprogramm: Etwas Grafik und Text zeichnen SCREEN 2 pi! = 4! * ATN(1!) sw! = 2! * pi! / 73! FOR i% = 1 TO 73 w! = CSNG(i%) * sw! LINE (400!, 100!)-(400! + 228! * COS(w!), 100! + 95! * SIN(w!)) NEXT i% PRINT "Und nun noch" PRINT "etwas Text..." Hardcopy WHILE INKEY$ = "" WEND SCREEN 0 SUB Hardcopy OPEN "lpt1" FOR OUTPUT AS 1 PRINT #1, CHR$(27); "@"; ' Drucker initialisieren PRINT #1, CHR$(27); "A"; CHR$(8) ' 8/72" Zeilenvorschub FOR y% = 0 TO 192 STEP 8 PRINT #1, CHR$(27); "L"; MKI$(640); ' Grafikmodus aktivieren (640 Pixel) FOR x% = 0 TO 639 w% = 0 FOR y1% = y% TO y% + 7 w% = 2 * w% + POINT(x%, y1%) ' hier wird pixelweise ausgelesen NEXT y1% PRINT #1, CHR$(w%); ' und die Daten an den Drucker geschickt NEXT x% PRINT #1, "" ' Neue Zeile NEXT y% PRINT #1, CHR$(27); "2"; ' Wieder Standard-Zeilenvorschub von 1/6" CLOSE 1 END SUB