home *** CD-ROM | disk | FTP | other *** search
- Deutsch kompatibel
-
- Bei den einfacheren und auch billigeren Laserdruckern heißt es zwar, daß sie
- HP-LaserJet-kompatibel sind, allerdings ist es Ansichtssache, wo die
- Kompatibilität beginnt und wo sie endet.
-
- Bei meinem Laserdrucker ist es jedenfalls so, daß er alles kann, was auch das
- Original verspricht, nur bei den deutschen Sonderzeichen hapert es. Entweder
- muß ich in den nicht IBM-kompatiblen Epson-Modus umschalten - geht auch nur
- manuell - oder in den Diablo-Modus - kann auch keine IBM-Zeichen - oder ich
- besorge mir für ein paar Mark Fünfzig eine FontCard, die dann das liefert,
- was nicht mehr ins EPROM gebrannt worden ist.
-
- Ich bin zwar geneigt, für so etwas Geld auszugeben, wenn ich unbedingt eine
- 3D-Schattenschrift mit Farbausgabeoption brauche, aber nur um die - bis auf
- Amtsschreiben - bei uns üblichen Umlaute und das scharfe "ß" auf den Drucker
- zu bringen wieder ein paar Mark auf den Tisch legen zu müssen - nein Danke.
-
- Glücklicherweise gibt es in DOS die Möglichkeit, Fehler in gewissem Umfang
- ausbügeln zu können. Was liegt also näher, als das obige Manko der Hardware
- durch Software auszugleichen.
-
-
- Und es gibt sie doch
-
-
- Wer die Zeichensatztabellen des HP-LaserJet und von DOS vergleicht, stellt
- schnell fest, daß eigentlich alle Zeichen, die benötigt werden vorhanden sind.
- Nur haben die Zeichenjongleure bei IBM alle Zeichen ab dem Wert 80h/128d mit
- entsprechenden Symbolen aufgefüllt, während bei Hewlett-Packard im Zeichensatz
- Roman 8, der hier zur Verfügung steht, der Bereich zwischen 80h/128d und
- 9Fh/159d ausgespart ist. Der im Original von HP verwendete Zeichensatz PC-8
- steht hier nicht zur Verfügung ( siehe oben ). Ab dem Wert A0h/160d sind dann
- auf einmal wieder alle möglichen internationalen Zeichen im HP-Zeichensatz
- vorhanden - auch die benötigten deutschen.
-
- Was liegt also näher als eine Routine zu schreiben, die die Zeichensätze
- anpaßt, das heißt das jeweilige IBM-Zeichen in das entsprechende HP-Zeichen
- umwandelt, bevor das Zeichen an den Drucker abgeht.
-
- Zum Glück gibt es im Befehlssatz der 80x86-Familie einen einfachen Befehl,
- der diese Umsetzerei von alleine erledigt ( XLAT ). Die größte Fleißarbeit
- besteht nur darin, die Zeichen in einer Tabelle mit ihrem Equivalenz
- zusammenzustellen - und weil's so schön ist, habe ich gleich alle 256
- Möglichkeiten erfaßt, die in einem Byte unterzubringen sind. Jetzt muß die
- Software nur noch "Hier" rufen, wenn sie gemeint ist.
-
-
- Da der Interrupt 17h/23d für die Ausgabe auf den Drucker via Parallelport
- zuständig ist, muß sich die Software also hier zwischenschalten. Von den
- möglichen Optionen dieses Interrupts interessiert nur die Ausgabe von Zeichen
- an den Drucker ( AH = 0 ). Fordert der Interrupt diese Aufgabe, fühlt sich
- die kleine Routine angesprochen und ersetzt das an den Drucker zu sendende
- Byte durch das in der Tabelle. Danach geht es bei der alten Interruptroutine
- weiter als ob nichts geschehen wäre.
-
- Im Prinzip wär's das schon, wenn da nicht in der Praxis wieder ein paar
- Problemchen aufkämen.
-
- Wenn Sie mit dem eingehängten Treiber Grafikbytes oder Fonts an den Drucker
- senden, übersetzt der Treiber natürlich treu-doof genauso wie bei ÄÖÜ. Um
- diesem Übel zu begegnen, habe ich über die Aufrufparameter
- D(isable) = deaktivieren und E(nable) = aktivieren die Möglichkeit eingebaut,
- den Treiber von der DOS-Ebene aus steuern zu können.
-
-
- Is' er nu' da oder nich'
-
-
- Zwangsläufig verliert man irgendwann den Überblick, ob der Treiber nun aktiv
- ist oder nicht - spätestens, wenn das Telefon geläutet hat. Also gibt es
- einen weiteren Parameter, der einen darüber I(nformiert).
-
- Wenn man gar nicht mehr weiß, wie das ganze zu bedienen ist - "?" für die
- Ausgabe eines Hilfetextes eingeben. Und damit sich der Treiber nun selbst
- noch auskennt - S(et) = Treiber laden und resident im Speicher lassen und
- R(eset) = Treiber wieder aus dem Speicher entfernen.
-
- Eigentlich wär's das nun wirklich, und der im Speicher belegte Platz benötigt
- auch nur ca. 300 Bytes - doch da war noch etwas.
-
- Wie kann ich aus meinem Applikationsprogramm, das außer ein paar Fonts und
- Makros auch noch lesbare Zeichen an den Drucker senden soll feststellen, in
- welchem Gesundheitszustand sich gerade der Treiber befindet. Deaktiviere ich
- ihn für Fonts und Makros kann ich mit Otto Reuter singen: "schau ich weg von
- dem Fleck, sind die Sonderzeichen weg". Wird der Treiber aber aktiviert, kann
- ich die Fonts und Makros vergessen. Also werden noch ein paar Bytes für eine
- Kennung und ein Flag spendiert und das ganze so plaziert, daß es aus eigenen
- Programmen eingesehen werden kann.
-
- Für die Kennung habe ich den Namen des Programms gewählt, allerdings in einer
- eigenen Schreibweise ( "LjDrV" ), damit sichergestellt ist, daß durch
- irgendwelche DOS-Einträge aus Directory oder Environment nicht der falsche
- Schluß entsteht, der Treiber sei da und dem war doch nicht. Für das Flag
- genügt ein Byte, das den Aktivitätszustand des Treibers wiederspiegelt und bei
- Bedarf verändert werden kann. Für den Wert Null gilt, die Zeichenkonvertierung
- wird nicht ausgeführt.
-
-
- Jetzt glaube ich wirklich alles zu haben, außer, daß bei Ausgabe des
- Hilfetextes "Hänschen klein" ertönt. Doch darauf habe ich verzichtet.
-
-
- Um das Programm zum Leben zu erwecken, brauchen Sie entweder einen Assembler
- oder Sie geben das Programm mit dem Debugger ein. Falls Sie sich beim Abtippen
- der Zeichensatztabelle vertun, werden Sie bald merken, wenn Ihnen Ihr Drucker
- ein Ä für ein Ü vormacht.
-
-
- page 66, 100
- title ljdrv.asm - (C) 18.11.1989, Peter F. L. Burger & TOOLBOX
- ;==============================================================================
- ; DRUCKZEICHENWANDLER FÜR LJ-DRUCKER
- ;==============================================================================
- ; LJDRV ist ein Zeichensatzanpassungsprogramm für HPLJ-Drucker und kompatible.
- ;
- ; Es werden die erweiterten Zeichen des IBM-Zeichensatzes in die des HPLJ-
- ; Zeichensatzes umgewandelt.
- ;
- ; Das Programm kann natürlich auch für ähnlich gelagerte Fälle genutzt werden.
- ;==============================================================================
- ; MAKROS ( machen das Leben leichter )
- ;==============================================================================
- dos_call macro
- int 21h ; MS-DOS Funktion aufrufen
- endm
- disp_strg macro string ; String darstellen
- mov dx, offset string
- mov ah, 09h
- dos_call
- endm
- get_ivec macro int_vec
- mov al, &int_vec ; Originalvektor lesen
- mov ah, 35h
- dos_call
- endm
- set_ivec macro int_vec, new_vec
- mov dx, offset new_vec ; neuen Vektor setzen
- mov al, &int_vec
- mov ah, 25h
- dos_call
- endm
- ;==============================================================================
- ; PROGRAMMSTART
- ;==============================================================================
- cseg segment 'code'
- assume cs:cseg, ds:cseg, es:cseg
- org 2Ch
- env_seg label word
- org 80h
- dta_len db ?
- dta_txt db ?
- org 100h
- main: jmp set_up ; Sprung zur Installationsroutine
- ;==============================================================================
- ; TABELLE FÜR ZEICHENWANDLUNG ( HPLJ-8-Bit-Code )
- ; ( Beim Abtippen gut aufpassen ! )
- ;==============================================================================
- xlat_table:
- db 000h,001h,002h,003h,004h,005h,006h,007h ; 00h..07h
- db 008h,009h,00Ah,00Bh,00Ch,00Dh,00Eh,00Fh ; 08h..0Fh
- db 010h,011h,012h,013h,014h,0BDh,016h,017h ; 10h..17h
- db 018h,019h,01Ah,01Bh,01Ch,01Dh,01Eh,01Fh ; 18h..1Fh
- db 020h,021h,022h,023h,024h,025h,026h,027h ; 20h..27h
- db 028h,029h,02Ah,02Bh,02Ch,02Dh,02Eh,02Fh ; 28h..2Fh
- db 030h,031h,032h,033h,034h,035h,036h,037h ; 30h..37h
- db 038h,039h,03Ah,03Bh,03Ch,03Dh,03Eh,03Fh ; 38h..3Fh
- db 040h,041h,042h,043h,044h,045h,046h,047h ; 40h..47h
- db 048h,049h,04Ah,04Bh,04Ch,04Dh,04Eh,04Fh ; 48h..4Fh
- db 050h,051h,052h,053h,054h,055h,056h,057h ; 50h..57h
- db 058h,059h,05Ah,05Bh,05Ch,05Dh,05Eh,05Fh ; 58h..5Fh
- db 060h,061h,062h,063h,064h,065h,066h,067h ; 60h..67h
- db 068h,069h,06Ah,06Bh,06Ch,06Dh,06Eh,06Fh ; 68h..6Fh
- db 070h,071h,072h,073h,074h,075h,076h,077h ; 70h..77h
- db 078h,079h,07Ah,07Bh,07Ch,07Dh,07Eh,020h ; 78h..7Fh
- db 0B4h,0CFh,0C5h,0C0h,0CCh,0C8h,0D4h,0B5h ; 80h..87h
- db 0C1h,0CDh,0C9h,0DDh,0D1h,0D9h,0D8h,0D0h ; 88h..8Fh
- db 0DCh,0D7h,0D3h,0C2h,0CEh,0CAh,0C3h,0CBh ; 90h..97h
- db 0EFh,0DAh,0DBh,0BFh,0BBh,0BCh,020h,0BEh ; 98h..9Fh
- db 0C4h,0D5h,0C6h,0C7h,0B7h,0B6h,0F9h,0FAh ; A0h..A7h
- db 0B9h,020h,020h,0F8h,0F7h,0B8h,0FBh,0FDh ; A8h..AFh
- db 020h,07Fh,020h,07Ch,02Bh,02Bh,02Bh,02Bh ; B0h..B7h
- db 02Bh,02Bh,07Ch,02Bh,02Bh,02Bh,02Bh,02Bh ; B8h..BFh
- db 02Bh,02Bh,02Bh,02Bh,02Dh,02Bh,02Bh,02Bh ; C0h..C7h
- db 02Bh,02Bh,02Bh,02Bh,02Bh,02Dh,02Bh,02Bh ; C8h..CFh
- db 02Bh,02Bh,02Bh,02Bh,02Bh,02Bh,02Bh,02Bh ; D0h..D7h
- db 02Bh,02Bh,02Bh,020h,020h,020h,020h,020h ; D8h..DFh
- db 020h,0DEh,020h,020h,020h,020h,075h,020h ; E0h..E7h
- db 020h,020h,020h,020h,020h,0D6h,045h,020h ; E8h..EFh
- db 020h,0FEh,020h,020h,020h,020h,020h,020h ; F0h..F7h
- db 0B3h,020h,020h,020h,020h,020h,0FCh,020h ; F8h..FFh
- ;==============================================================================
- ; VARIABLEN ( das Herz des Treibers )
- ;
- ; Die Variablen kennung und enable_flag sind so plaziert, daß sie über den
- ; Pointer vom Int 17h ( -1 bzw. -6 ) von jedem externen Programm abgefragt
- ; werden können.
- ;
- ; z. B. in Turbo Pascal 4:
- ;
- ; VAR kennung : STRING;
- ; kenn_ptr : ^BYTE;
- ; treiber_ist_da,
- ; treiber_ist_aktiv : BOOLEAN;
- ;
- ; LONGINT( kenn_ptr ) := MEML[ $0000 : $005C ]; { = @INT_17h }
- ; { Pointer^ zeigt auf trb_int }
- ; DEC( LONGINT( kenn_ptr ), 6 ); { SEG Grenzen müssen hier nicht }
- ; { beachtet werden }
- ; MOVE( kenn_ptr^, kennung[ 1 ], 5 ); { GetString }
- ; BYTE( kennung[ 0 ] ) := 5; { Length( kennung ) := 5 }
- ; treiber_ist_da := kennung = 'LjDrV';
- ;
- ; INC( LONGINT( kenn_ptr ), 5 ); { SEG Grenzen: siehe oben }
- ; treiber_ist_aktiv := kenn_ptr^ = 1;
- ;
- ; Ebenso kann der Treiber auf diese Art und Weise von einem externen Programm
- ; aktiviert bzw. deaktiviert werden ( kenn_ptr^ := 0 oder 1 ).
- ;==============================================================================
- save_int_17h dd (?) ; ursprünglicher INT 17H-Vektor
- kennung label byte
- db 'LjDrV' ; Kennung und
- enable_flag db 0 ; Flag
- ; für Abfragemöglichkeiten aus
- ; anderen Programmen
- ;==============================================================================
- ; INTERRUPT-ROUTINE INT 17H ( Beginn des residenten Codes )
- ;==============================================================================
- trb_int proc far
- or ah, ah ; ah=0: Funktion "drucke Zeichen"?
- jnz jmp_int ; nein
- cli
- cmp cs:enable_flag, 0 ; Treiber aktiv?
- jz jmp_int
- push ds
- push bx
- push cs ; DS = CS für XLAT
- pop ds
- mov bx, offset xlat_table
- xlat
- pop bx
- pop ds
- jmp_int: sti ; weiter bei ursprüngl. Interrupt
- jmp dword ptr cs:save_int_17h
- trb_int endp
- ;==============================================================================
- ; INSTALLATIONS-ROUTINE ( Beginn des transienten Codes )
- ;==============================================================================
- set_up proc near
- xor ax, ax ; Register löschen
- xor cx, cx
- mov bx, offset dta_len
- mov cl, [ bx ] ; Stringlänge im DTA-Bereich
- skip: jcxz help_trb ; DTA-Bereich zu Ende oder nichts drin
- inc bx
- dec cx ; Stringlänge - 1
- mov al, [ bx ] ; BXtes Zeichen im DTA-Bereich
- cmp al, ' '
- jz skip ; noch Leerzeichen
- and al, 5Fh ; UpCase( al )
- cmp al, 'D' ; disable - inaktiv
- jz disable_trb
- cmp al, 'E' ; enable - aktiv
- jz enable_trb
- cmp al, 'I' ; info - Statusinformation
- jz info_trb
- cmp al, 'S' ; setup - installieren
- jz setup_trb
- cmp al, 'R' ; remove - deinstallieren
- jnz help_trb
- jmp remove_trb
- help_trb: ; alles andere kommt hier an
- disp_strg help_txt
- jmp ende
- disable_trb:
- call trb_vorh ; ist Treiber da?
- jne err_0
- mov byte ptr es:[ enable_flag ], 0
- disp_strg msg_4
- jmp ende
- enable_trb:
- call trb_vorh ; ist Treiber da?
- jne err_0
- mov byte ptr es:[ enable_flag ], 1
- disp_strg msg_3
- jmp ende
- err_0: disp_strg err_msg_0
- jmp help_trb
- info_trb: call trb_vorh
- jne err_0
- cmp byte ptr es:[ enable_flag ], 1
- jz trb_aktiv
- disp_strg msg_4
- jmp ende
- trb_aktiv:
- disp_strg msg_3
- jmp ende
- setup_trb:
- call trb_vorh ; ist Treiber da?
- jne setup_weiter
- err_1: disp_strg err_msg_1 ; zweimal geht nicht
- jmp help_trb
- setup_weiter:
- mov word ptr cs:[ save_int_17h ], bx ; Originalvektor sichern
- mov word ptr cs:[ save_int_17h+2 ], es
- set_ivec 17h, trb_int ; neuen Vektor setzen
- mov byte ptr [ enable_flag ], 1
- disp_strg msg_1 ; Erfolgsmeldung
- mov dx, offset set_up
- int 27h ; bleib da
- ;==============================================================================
- remove_trb: ; Installation aufheben
- call trb_vorh ; ist Treiber da?
- jne err_0
- mov dx, word ptr es:[ save_int_17h ] ; Vektoren rücksetzen
- mov ax, word ptr es:[ save_int_17h+2 ]
- mov ds, ax
- mov al, 17h
- mov ah, 25h
- dos_call
- push es ; ES sichern
- mov ax, es:env_seg ; Environmentsegment freimachen
- mov es, ax
- mov ah, 49h
- dos_call
- pop es ; altes ES wieder aktivieren
- mov ah, 49h ; Programmsegment freimachen
- dos_call
- push cs ; DS = CS
- pop ds
- jnc remove_ok
- push ax ; Fehlermeldung
- disp_strg err_msg_2
- pop ax
- cmp al, 9
- je err_4
- err_3: disp_strg err_msg_3
- jmp term
- err_4: disp_strg err_msg_4
- term: mov al, 0ffh
- jmp ende
- remove_ok:
- disp_strg msg_2
- xor al, al
- ende: mov ah, 4ch ; Normales Programmende
- dos_call
- set_up endp
- ;==============================================================================
- ; CALLs ( sparen Programmcode )
- ;==============================================================================
- trb_vorh proc near
- get_ivec 17h ; Originalvektor lesen
- mov ax, es ; Testen, ob bereits installiert
- mov di, offset kennung
- mov si, di
- mov cx, 5
- cld
- repe cmpsb
- ret
- trb_vorh endp
- ;==============================================================================
- ; MELDUNGEN ( des Treibers Sprachrohr )
- ;==============================================================================
- msg_1 db 'LJDRV ist installiert',13,10,'$'
- msg_2 db 'LJDRV ist deinstalliert',13,10,'$'
- msg_3 db 'LJDRV ist aktiv',13,10,'$'
- msg_4 db 'LJDRV ist inaktiv',13,10,'$'
- err_msg_0 db 'LJDRV ist nicht installiert',13,10,'$'
- err_msg_1 db 'LJDRV ist bereits installiert',13,10,'$'
- err_msg_2 db 'FEHLER: Speicher kann nicht freigemacht werden.',13,10,'$'
- err_msg_3 db 'ACHTUNG: Speicherbereich zerstört - '
- db 'bitte System neu booten!',13,10,'$'
- err_msg_4 db 'Segment gehört nicht zu LJDRV.',13,10,'$'
- help_txt db 'Aufruf: LJDRV [DEIRS]',13,10
- db ' D = Treiber deaktivieren',13,10
- db ' E = Treiber aktivieren',13,10
- db ' I = Treiber Information',13,10
- db ' ? = Treiber Hilfetext',13,10
- db ' R = Treiber deinstallieren',13,10
- db ' S = Treiber installieren',13,10,'$'
- cseg ends
- ;==============================================================================
- end main