← ST-Computer 02 / 1993

Drucker-Spooler: Flexibel & schnell

Programmierpraxis

In letzter Zeit hatte ich hĂ€ufig lĂ€ngere Texte zu erstellen und Ă€rgerte mich ĂŒber die langen Wartezeiten beim Druck.

Zu dieser Zeit stieß ich auf einen Artikel zur Beschleunigung der Druckausgabe [1]. Wichtig in diesem Artikel ist auch die Information, daß es fĂŒr eine umfassende Lösung nicht genĂŒgt, die entsprechende BIOS-Funktion durch die eigene Druckroutine zu ersetzen, sondern daß dies auch fĂŒr die GEMDOS-Druckfunktion geschehen muß.

Das genĂŒgte mir allerdings nicht. Was ich brauchte, war ein schneller Drucker-Spooler, ich kramte in meinen Unterlagen und fand einen weiteren wichtigen Artikel!?]. Der dort beschriebene Spooler vermeidet unnötigen Leerlauf, indem immer dann, wenn der Drucker bereit ist zur Aufnahme des nĂ€chsten Bytes, ein entsprechender Interrupt ausgelöst wird, der dem Drucker das nĂ€chste Byte aus dem Druckpuffer liefert. Periodische Abfragen, ob der Drucker bereit ist, entfallen also. Die Details der MFP-Programmierung werden eingehend beschrieben; hilfreich fĂŒr das VerstĂ€ndnis ist auch [3].

Ganz zufrieden war ich aber immer noch nicht. Das angegebene Programm bewirkt, daß ohne Spooler gedruckt wird, wenn die druckende Anwendung die Ausgabe per GEMDOS-Druckfunktion erledigt. Vor allem aber ist die GrĂ¶ĂŸe des Druckpuffers fest vorgegeben. Da ich fĂŒr unterschiedliche Anwendungen unterschiedliche Disketten nutze, hĂ€tte ich gerne auf jeder Diskette eine andere, der Anwendung angemessene GrĂ¶ĂŸe des Druckpuffers. Einen sehr großen Druckpuffer benötige ich nur bei der Diskette mit der Textverarbeitung und der mit einem Grafikprogramm. Bei den anderen Anwendungsdisketten möchte ich auf den Spooler nicht verzichten, benötige aber nur kleine Puffer. Daß der Spooler als Accessory konzipiert ist, ich - wegen der begrenzten Anzahl der Accessories und aus GrĂŒnden der Kompaktheit - jedoch ein Programm im AUTO-Ordner vorziehe, ist hingegen eher Geschmackssache.

Einmal entschlossen, meinen eigenen Drucker-Spooler zu schreiben, wollte ich dann noch einige mir wichtige Eigenschaften realisieren. Mich hat schon immer der 30-Sekunden-Timeout-Wert fĂŒr die Druckausgabe gestört, der etwa beim Drucken vom Desktop aus voll zuschlĂ€gt. Bei Einsatz meines Spoolers gibt es sofort eine Fehlermeldung beim Druck, wenn der Drucker nicht bereit ist, unabhĂ€ngig davon, wie die Druckausgabe in der druckenden Anwendung realisiert ist. Einen gestarteten Druck wollte ich ferner jederzeit abbrechen können, und zwar auf die natĂŒrlichste Weise durch Ausschalten des Druckers (fĂŒr 10 Sekunden).

Die GrĂ¶ĂŸe des Druckpuffers wird ĂŒber den Programmnamen eingestellt: das Programm heißt SPOOLxxx.PRG, wobei xxx die GrĂ¶ĂŸe des Druckpuffers in KB darstellt (immer dreistellig angeben!). Die Entnahme von Betriebsparametern aus dem Programmnamen habe ich [4] entlehnt, wo von dieser Technik reichlich Gebrauch gemacht wird.

Das Programm steht ĂŒblicherweise im AUTO-Ordner, und zwar als erstes von allen den Druck beeinflussenden Programmen (da die Druckausgabe per Hardware-Programmierung realisiert ist, wird nicht auf die BIOS- bzw. GEMDOS-Druckroutine zurĂŒckgegriffen und damit auch nicht auf andere eingehĂ€ngte Druckroutinen). Systemvoraussetzung ist TOS 1.2 oder höher.

Programmiert wurde mit Profimat ST, es mĂŒĂŸte aber ohne große Anpassungen jeder andere Assembler verwendbar sein. Profimat-spezifisch sind vielleicht die Makros zur Realisierung der wichtigsten Betriebssystemfunktionen im Include-File D:\TOS.Q (TOS.Q steht bei mir beim Assemblieren auf einer RAM-Disk D:). Im Zweifelsfall kann man auf die Makros verzichten und jeden Betriebssystemfunktionsaufruf durch drei Zeilen ersetzen: Funktionsnummer auf den Stack legen, entsprechenden Trap aufrufen, Stack restaurieren.

Im Programm wird zunĂ€chst an das Programmende gesprungen zur Installation der neuen BIOS-BCONOUT- und der neuen GEMDOS-Routine. Ferner wird der Interrupt-Vektor fĂŒr I/O-Port 0 (Drucker-Busy) des MFP 68901 gesetzt. Daneben wird eine Startmeldung auf den Bildschirm ausgegeben. Der Installationsteil dient nach der Installation gleichzeitig als Beginn des als Ringpuffer organisierten Druckpuffers.

Bei der neuen GEMDOS-Routine muß man die feine Unterscheidung machen, ob GEMDOS im User- oder Supervisor-Modus aufgerufen wurde. Je nachdem sind die VerhĂ€ltnisse auf dem Stack unterschiedlich. Die neue GEMDOS-Routine realisiert die CPRNOUT-Funktion mit Hilfe der neuen BIOS-BCONOUT-Funktion. Die Details sind dem kommentierten Listing entnehmbar, ebenso wie die Einzelheiten der Druckausgabe auf die parallele Schnittstelle (Routine Ausgabe), der neuen BIOS-BCONOUT-Funktion (Routine BconOut) sowie der Busy-IRQ-Routine (Routine Irq).

Literatur:

[1] H. Emmerl: Volldampf fĂŒr Centronics, ST-Computer 5/92, S. 79ff.

[2] M. Rogge: Von der Spule, c 't 6/90, S. 212 ff.

[3] H.-D. Jankowski, J. F. Reschke, D. Rabich: ATARI ST Profibuch, SYBEX-Verlag GmbH, DĂŒsseldorf

[4] C. Brod, A. Stepper: Scheibenkleister II, Maxon Computer GmbH

; *********************************************** ; * * ; * S P O O L x x x * ; * relozierbar assemblieren! * ; * (c) 1992 MAXON Computer * ; * * ; *********************************************** include d:\tos.q mfp equ $fffffa00 gi equ $ffff8800 hz200 equ $4ba timeout equ 2000 ; Timeout: 10 Sek. top bra install fname2 dc.b '\AUTO\' fname dc.b 'SPOOL*.PRG',0 buflen dc.l $8000 ; PuffergrĂ¶ĂŸe ; (Default: 32 KB) last dc.l 0 ; Zeitpunkt des letzten ; Non-Busy-Irq bzw. warm ; bei einem Druckvorgang ; das 1. Zeichen ausgege- ; ben wird. lobuf ds.l 1 ; Anf.adresse buffer hibuf ds.l 1 ; Adresse hinter buffer outmark ds.l 1 ; Adresse mit nĂ€chstem ; auszugebendem Zeichen inmark ds.l 1 ; nĂ€chste freie Adresse ; im buffer ; inmark = outmark : ; buffer leer ; inmark + 1 = outmark : ; buffer voll Irq movem.l d0/d1/a0,-(sp) move.l last(pc),d1 ; letzter NonBusy- move.l hz200,d0 ; Irq Ă€lter als move.l d0,last ; (aktuelle Zeit sub.l d1,d0 ; nach last ; bringen) cmp.l #timeout,d0 ; timeout-Wert ? bcs.s Irq0 ; Nein: Irq0 move.l inmark(pc),outmark ; Puffer löschen Irq0 move.l outmark(pc),a0 ; Puffer leer ? cmp.l inmark(pc),a0 ; beq.s Irq2 ; Ja: Irq2 move.b (a0)+,d0 ; NĂ€chstes Zeichen cmp.l hibuf(pc),a0 ; bne.s Irq1 ; move.l lobuf(pc),a0 ; Irq1 move.l a0,outmark ; nach d0 bsr.s Ausgabe Irq2 bclr #0,mfp+$11 ; Busy-Interrupt ; wieder freigeben ; (I/O-Port 0 des Interrupt-In- ; Service-Register ; B des MFP 68901) movem.l (sp)+,d0/d1/a0 rte Ausgabe lea gi,a0 ; Reg. 7 des move.b #7,(a0) ; Soundchip wĂ€hlen move.b (a0),d1 ; Reg. 7 nach d1 bset #7,d1 ; Port B des ; Soundchip (Druk- ; kerdaten) auf move.b d1,2(a0) ; Ausgabe stellen move.b #15,(a0) ; Reg. 15 (Port B) ; auswĂ€hlen move.b d0,2(a0) ; d0 auf Port B ; ausgeben move.b #14,(a0) ; Reg. 14 (Port A) ; auswĂ€hlen move.b (a0),d1 ; Bit 5 des Port A bclr #5,d1 ; (Strobe) move.b d1,2(a0) ; Low setzen bset #5,d1 ; Strobe High move.b d1,2(a0) ; setzen ; Strobe Low- und ; High-Setzen er- ; gibt, einen ; Strobe-Impuls rts dc.b 'XBRA' ; XBRA- dc.b 'SPLX' ; Kennung OldBconOut: ; alter dc.l 1 ; BConOut-Vektor BconOut: move.l last(pc),d1 ; letzter NonBusy- move.l hz200,d0 ; Irq Ă€lter als sub.l d1,d0 ; cmp.l #timeout,d0 ; timeout-Wert ? bes.s BconOut1 ; Nein: BconOut1 move.l inmark(pc),a0 ; Puffer leer ? cmp.l outmark(pc),a0 ; bne.s BconOut0 ; Nein: BconOut0 move.l $55e,a0 ; Bcostat jsr (a0) ; fĂŒr PRT tst.l d0 ; Drucker nicht ; bereit ? beg.s BconOut0 ; Ja: BconOut0 move.l hz200,last ; last mit aktuel- bra.s BconOut2a ; ler Zeit versor- ; gen BconOut0: move.l inmark(pc),outmark ; Puffer löschen moveq #0,d0 ; Fehler zurĂŒck- rts ; melden BconOut1: move.l inmark(pc),d0 ; liegt inmark addq.l #1,d0 ; nur 1 Stelle cmp.l hibuf(pc),d0 ; hinter outmark, bne.s BconOut2 ; d.h. ist der move.l lobuf(pc),d0 ; Puffer BconOut2: sub.l outmark(pc),d0 ; voll ? beq.s BconOut0 ; Ja: BconOut0 BconOut2a: move.w 6(sp),d0 ; auszugebendes ; Zeichen move.w sr,d2 ; Statusregister ; nach d2 retten or.w #$700,sr ; Interrupts ; ausmaskieren move.l inmark(pc),a1 ; Puffer leer ? cmp.l outmark(pc),a1 ; bne.s BconOut3 ; Nein: BconOut3 lea mfp,a0 btst #0,1(a0) ; Drucker busy ? bne.s BconOut3 ; Ja: BconOut3 btst #0,$d(a0) ; Ist ein ; Busy-Interrupt ; pending ? bne.s BconOut3 ; Ja: BconOut3 bsr Ausgabe bra.s BconOut5 BconOut3: move.b d0,(a1)+ ; Zeichen in den cmp.l hibuf(pc),a1 bne.s BconOut4 move.l lobuf(pc),a1 ; BconOut4: move.l a1,inmark ; Puffer schreiben BconOut 5: move.w d2,sr ; Statusregister ; restaurieren, ; insbes. Inter- ; rupts wieder ; zulassen moveq #-1,d0 ; OK zurĂŒckgeben rts dc.b 'XBRA' ; XBRA- dc.b 'SPLX' ; Kennung OldGemDos: ; alter dc.l 1 ; GemDos-Vektor NewGemDos: move.l usp,a0 move.w (sp),d0 ; Trap-Aufruf im btst #13,d0 ; Supervisor-Mode? beq.s NewGemDos1 ; Nein: NewGemDos1 lea 6(sp),a0 NewGemDos1: cmpi.w #5,(a0)+ ; Funktions-Nr. 5 ; (Cprnout) ? bne.s ExitNewGemDos ; Nein: ; ExitNewGemDos move.w (a0),-(sp) ; Zeichen auf ; Stack legen clr.w -(sp) ; GerĂ€t 0 fĂŒr ; BIOS-Bconout move.l $57e,a0 ; Aufruf jsr (a0) ; bconout fĂŒr PRN addq.l #4,sp ; Stack-Korrektur rte ExitNewGemDos: jmp $11111111 buffer Install movea.l 4(sp),a5 ; a5=Basepageadr. clr.l -(ap) Super move.l d0,-(sp) ; alten ssp auf ; Stack retten bsr.s GetBufSize pea logo(pc) Cconws lea buffer(pc),a0 move.l a0,lobuf move.l a0,inmark move.l a0,outmark adda.l buflen(pc),a0 move.l a0,hibuf bsr Setvec Super pea Irq ; Setzt Interrupt- ; vektor fĂŒr I/O- clr.w -(sp) ; Port 0 (Drucker- ; Busy des Mfpint ; MFP 68901 clr.w -(sp) move.l #$100+Install-top,d0 add.l buflen(pc),d0 move.l d0,-(sp) Ptermres GetBufSize: clr.w -(sp) ; Prg-Datei suchen pea fname(pc) ; ->'SPOOL*.PRG’ move.l 36(a5),a5 ; PD-Adresse der ; Eltern move.l 36(a5),a5 ; PD-Adresse der ; Großeltern tst.l 36(a5) ; Urgroßeltern ; vorhandenn ? bne.s GetBufSize1 ; Ja: GetBufSize1 move.l #fname2,(sp) ; Nein: Programm ; stammt aus \AUTO GetBufSize1: Fsfirst tst.w d0 ; Datei gefunden? bne.s GetBufSize2 ; Nein : ; GetBufSize2 Fgetdta addi.l #30+5,d0 ; Zeiger auf Da- ; teiname (nach movea.l d0,a0 ; 'SPOOL') in a0 clr.w d0 ; PuffergrĂ¶ĂŸe bsr.s Digit ; nĂ€chste Stelle bne.s GetBufSize2 ; fehlerhafte An- ; gabe: GetBufSize2 bsr.s Digit ; nĂ€chste Stelle bne.s GetBufSize2 ; fehlerhafte An- ; gabe:GetBufSize2 bsr.s Digit ; nĂ€chste Stelle bne.s GetBufSize2 ; fehlerhafte An- ; gabe:GetBufSize2 mulu #1024,d0 move.l d0,buflen lea size(pc),a1 ; size im logo move.b -(a0),-(a1) ; move.b -(a0),-(a1) cmpi.b #'0',-(a0) beq.s GetBufSize2 ; mit PuffergrĂ¶ĂŸe move.b (a0),-(a1) ; laden GetBufSize2: rts Digit move.b (a0)+,d1 cmpi.b #'0',d1 bcs.s Digit1 ; < '0' cmpi.b #'9',d1 bhi.s Digit1 ; > '9' andi.b #$0f,d1 mulu #10,d0 add.w d1,d0 moveq #0,d1 rts Digit1 moveq #1,d1 rts Setvec move.l $57e,OldBconOut ; BIOS-Bconout move.l #BconOut,$57e ; umleiten pea NewGemDos(pc) ; GEMDOS move.w #33,-(sp) Setexc ; umleiten move.l d0,ExitNewGemDos+2 move.l d0,OldGemDos lea mfp,a0 bclr #0,3(a0) ; Interrupt, wenn ; Busy auf 0 geht! bclr #0,5(a0) ; I/O-Port Busy- ; Eingang auf ; Eingabe ! rts logo dc.b $1b,'E',$1b,'p',' Spooler ' dc.b '(C) 26.6.1992 Horst ' dc.b 'Albrecht, Brahmsstr. 25, 4047 ' dc.b 'Dormagen 5 ',$1b,'q',$0d,$0a dc.b ' - Puffergroße: 32' size dc.b ' KB',$0d,$0a dc.b ' - nutzt die Geschwindigkeit des ' dc.b 'Druckers optimal‘,$0d,$0a dc.b ' - Ausschalten des Druckers fĂŒr 10 ' dc.b 'Sek. beendet jeden Druckvorgang' dc.b $0d,$0a dc.b ' - am Beginn eines Drucks erscheint' dc.b ' bei ausgeschaltetem Drucker nach’ dc.b $0d,$0a dc.b ' spĂ€testens 10 Sek. eine Fehler' dc.b 'meldung unabhĂ€ngig von der Art der' dc.b $0d,$0a dc.b ' Programmierung der Anwendung' dc.b $0d,$0a dc.b ' - muß vor jedem anderen Programm ' dc.b 'gestartet werden, welches ebenfalls' dc.b $0d,$0a dc.b ' die BIOS-PRN-bconout-Routine ' dc.b 'verĂ€ndert',$0d,$0a dc.b ' - Durch Umbenennung des Programms ' dc.b 'in SPOOLxxx.PRG lĂ€ĂŸt sich der ' dc.b 'Spooler',$0d,$0a dc.b ' mit frei wĂ€hlbarer PuffergrĂ¶ĂŸe ' dc.b 'verwenden.’,$0d,$0a dc.b ' xxx ist die PuffergrĂ¶ĂŸe in KB ' dc.b 'und muß mit 3 Stellen angegeben ' dc.b 'werden.',$0d,$0a dc.b ' Bei einer fehlerhaften Angabe ' dc.b 'wird der Puffer mit 32 KB ' dc.b 'eingerichtet.',0 END ; ************************************************ ; * * ; * T O S . Q * ; * * ; * Makros fĂŒr die wichtigsten BIOS-, XBIOS- * ; * und GEMDOS-Funktionen * ; * (c) 1992 MAXON Computer * ; ************************************************ DOTRAP macro %\trap, %\fct ; %\trap: Nr. des Trap ; %\fct: <2-stellig; Fkt> ; <2-stellig: #Bytes fĂŒr ; Stack-Korrektur nach trap> move.w #(\fct/$100),-(sp) trap #\trap stk@ =\fct-$100*(\fct/$100) IFNE 0,stk@ IFHI 8,stk@ lea stk@(sp),sp ELSE addq.l #stk@,sp ENDIF ENDIF endm ERR_BRA macro $\label tst.w d0 blt \label endm TSTL_ERR_BRA macro $\label tst.l d0 blt \label endm Rwabs equ DOTRAP 13,$040e Setexc equ DOTRAP 13,$0508 Getbpb equ DOTRAP 13,$0704 Bcostat equ DOTRAP 13,$0804 Mediach equ DOTRAP 13,$0904 Kbshift equ DOTRAP 13,$0b04 Floprd equ DOTRAP 14,$0814 Flopwr equ DOTRAP 14,$0914 Flopfmt equ DOTRAP 14,$0a1a Mfpint equ DOTRAP 14,$0d08 Protobt equ DOTRAP 14,$120e Flopver equ DOTRAP 14,$1314 Cursconf equ DOTRAP 14,$1506 Settime equ DOTRAP 14,$1606 Gettime equ DOTRAP 14,$1702 Kbdvbase equ DOTRAP 14,$2202 Supexec equ DOTRAP 14,$2606 Floprate equ DOTRAP 14,$2906 Pterm0 equ DOTRAP 1,$0000 Cconin equ DOTRAP 1,$0102 Cconout equ DOTRAP 1,$0204 Cprnout equ DOTRAP 1,$0504 Crawcin equ DOTRAP 1,$0702 Cconws equ DOTRAP 1,$0906 Cconrs equ DOTRAP 1,$0a06 Cprnos equ DOTRAP 1,$1102 Dgetdrv equ DOTRAP 1,$1902 Fsetdta equ DOTRAP 1,$1a06 Super equ DOTRAP 1,$2006 Tgetdate equ DOTRAP 1,$2a02 Tsetdate equ DOTRAP 1,$2b04 Tgettime equ DOTRAP 1,$2c02 Tsettime equ DOTRAP 1,$2d04 Fgetdta equ DOTRAP 1,$2f02 Ptermres equ DOTRAP 1,$3100 Dereate equ DOTRAP 1,$3906 Ddelete equ DOTRAP 1,$3a06 Dsetpath equ DOTRAP 1,$3b06 Fcreate equ DOTRAP 1,$3c08 Fopen equ DOTRAP 1,$3d08 Fclose equ DOTRAP 1,$3e04 Fread equ DOTRAP 1,$3f0c Fwrite equ DOTRAP 1,$400c Fdelete equ DOTRAP 1,$4106 Fseek equ DOTRAP 1,$420a Fattrib equ DOTRAP 1,$430a Fdup equ DOTRAP 1,$4504 Fforce equ DOTRAP 1,$4606 Dgetpath equ DOTRAP 1,$4708 Malloc equ DOTRAP 1,$4806 Mfree equ DOTRAP 1,$4906 Mshrink equ DOTRAP 1,$4a0c Pexec equ DOTRAP 1,$4b10 Pterm equ DOTRAP 1,$4c00 Fsfirst equ DOTRAP 1,$4e08 Fsnext equ DOTRAP 1,$4f02 Frename equ DOTRAP 1,$560c Fdatime equ DOTRAP 1,$570a
Horst Albrecht