Volldampf für Centronics: Beschleuniger für die parallele Schnittstelle

So mancher, der vor Jahren die Wahl hatte zwischen einem 68000er- Rechner von ATARI und einem Konkurrenzmodell der Sorte, wo die Prozessoren auf ‘86’ enden, entschied sich für den ATARI wegen der größeren Geschwindigkeit und Leistungsfähigkeit. Heute wirft wohl jeder ATARIaner hin und wieder einen neidvollen Blick auf das Lager der ‘Kompatiblen’. Dort hat sich in punkto Geschwindigkeit zugegebenermaßen doch einiges getan.

Nicht jeder kann nun gleich das nötige Kleingeld für ein TT- Modell aufbringen. Also wird der ATARI ST mit allen Tricks ‘hochgepeppelt’. Beschleunigerkarten mit verschiedenstem Komfort und sogenannte ‘Software-Beschleuniger’ gehen bei den Anbietern weg wie warme Semmeln. Gerade die zuletzt genannten ‘Aufputschmittel’ haben bewiesen, daß das TOS des ATARI die Hardware des Rechners eben manchmal doch mehr bremst, als eigentlich nötig wäre. Durch konsequente Umprogrammierung von Teilen des Betriebssystems in Assembler wurden hier erhebliche Geschwindigkeitssteigerungen bei der Bildschirmausgabe erzielt.

Richtig! Bei der Bildschirmausgabe! Aber wie steht es eigentlich mit der Schnittstelle, an der nach dem Bildschirm wohl die zweitgrößte Datenmenge ausgegeben wird, nämlich dem parallelen Drucker-Port? (Einmal abgesehen von DMA-Port und Floppy!) Wo bleiben die Beschleuniger-Routinen für die Centronics-Ausgabe etwa bei NVDI oder KAOS-TOS? Man sucht sie bis heute vergebens.

Die parallele Schnittstelle wurde bis dato meist als Stiefkind behandelt. Welche Druckermechanik konnte denn schon so schnell drucken, wie der ATARI die Daten abschickte? So schwärmen die Verfasser des bekannten ‘Profibuchs’ noch von der Geschwindigkeit der Druckerausgabe am Parallel-Port: „Die Datentransferrate liegt bei ca. 4000 Bytes/Sek. War’ das toll, wenn der Drucker da mitkäme ..." [3]

In der Praxis ist es aber so, daß viele der heutigen Druckermodelle schon von Haus aus mit Zwischenspeichern (Buffern) in der Größe von 64 KByte bis manchmal sogar 1 MB ausgestattet sind, die für eine extrem rasche Datenabnahme sorgen. Preiswertere Drucker ohne Daten-Buffer oder mit nur wenigen Kilobyte als Zwischenspeicher lassen sich durch externe Hardware- Buffer ‘aufwerten’. Ein aus der Sicht des Verfassers sehr empfehlenswerter Drucker-Buffer für den Selbstbau, aufrüstbar bis 4MB, ist in [2] beschrieben. Und nun stellt sich die Situation in der Praxis tatsächlich so dar: Nicht der ATARI-Rechner wartet auf den Drucker, bis dieser fertig ist, sondern umgekehrt!

Schuld daran ist, wie so oft, das ‘schaumgebremste’ TOS des ATARI. Die Ausgabe von Zeichen an die Centronics-Schnittstelle erfolgt im Betriebssystem durch die GEMDOS-Funktionen #5 (Printer Output) und #64 (Write) und die BIOS-Funktion #3 (Bconout). Durch Probieren findet man heraus, daß die GEMDOS-Funktion #64 intern eigenartigerweise die BIOS-Funktion #3 benutzt. Nur die GEMDOS- Funktion #5 geht ihre eigenen Wege.

Worin besteht die Aufgabe dieser Funktionen? Da das Prinzip der Centronics-Ausgabe schon mehrfach beschrieben wurde, unter anderem auch in [4], soll der Vorgang hier nur kurz Umrissen werden. Wird ein Byte an den Drucker abgeschickt, legt das TOS dieses Datum zunächst an den B-Port des Soundchips an, der direkt mit den acht Datenleitungen der Druckerschnittstelle verbunden ist. Durch einen kurzen LOW-Impuls an Bit 5 des A-Ports im Soundchip wird dem Drucker über die STROBE-Leitung signalisiert, daß nun ‘Arbeit’ auf ihn zukommt. Der Drucker quittiert diese Aufforderung sofort mit einem HIGH-Pegel an der BUSY-Leitung, welche ihrerseits mit den Eingängen 10 und TAI des MFP-Bausteins verbunden ist. Wenn der Drucker das Zeichen verarbeitet hat, legt er diese Leitung wieder auf LOW- Pegel, und der ATARI weiß, daß er ihm das nächste Zeichen senden darf. Bild 1 zeigt, wie die Parallelschnittstelle intern mit der Hardware des ATARI verbunden ist.

Bei all den beschriebenen Vorgängen läßt sich das TOS recht viel Zeit. Das mag einmal daran liegen, daß große Teile des Betriebssystems nicht in Assembler programmiert sind, zum anderen daran, daß man bei ATARI nicht damit gerechnet hat, daß es eines Tages datenhungrige Drucker mit sehr kurzen Verarbeitungszeiten geben sollte (sprich Buffer-Riesen). Wer sich in [5] das disassemblierte Listing der Centronics-Ausgabe des TOS 1.4 angesehen hat, wird zustimmen, daß auch C-Compiler umständlichen Spaghetti-Code erzeugen können. Die erwähnten Aus- und Eingänge der beteiligten Hardware-Bausteine lassen sich jedoch auch recht einfach in Assembler ansprechen. Listing 1 zeigt ein Programm, das die Druckerausgabe der ATARI-Rechners um einiges beschleunigt.

Wir brauchen uns, wie schon erwähnt, nur um die GEMDOS-Funktion #5 und die BIOS-Funktion #3 zu kümmern. Zunächst leiten wir mit der BIOS-Funktion #5 (Setexec) den Exception-Vektor für Trap #13 (eben BIOS) auf unsere ‘Volldampfroutine’ newbios um. Wir erhalten im Register d0 den Wert des ursprünglichen Vektors, den wir uns natürlich merken. Wir wollen ja nicht das ganze BIOS neu programmieren, sondern nur die Funktion #3. Alle anderen BIOS- Aufrufe sollen mit den Original-TOS-Routinen weiterlaufen. Das gleiche machen wir mit dem GEMDOS-Exception-Vektor. Ihn lenken wir auf unsere Beschleunigerroutine newgem um. Den ursprünglichen Wert merken wir uns wieder. Anschließend sorgen wir dafür, daß uns das Programm im Speicher erhalten (resident) bleibt.

Jedesmal, wenn nun ein GEMDOS- oder BIOS-Aufruf erfolgt, muß der Rechner zuerst unsere Routinen newbios oder newgem anspringen. In beiden Fällen prüfen wir erst einmal, ob eine Druckerausgabe verlangt wird. Im Falle des GEMDOS erkennen wir dies daran, daß auf dem Stack die Funktionsnummer 5 abgelegt wurde. Im Fall des BIOS müssen auf dem Stack die Funktionsnummer 3 und die Gerätenummer (device) 0 zu finden sein. Wenn dies nicht der Fall ist, springt der Rechner zu seinen ‘eingebauten’ Systemroutinen und macht ‘normal’ weiter. Den Wert des Stackpointers haben wir ins Register a0 übernommen, damit wir ihn gefahrlos manipulieren können.

Unsere wichtigste Routine beginnt beim Label newprout. Sie gibt ein Zeichen an der Centronics-Schnittstelle aus, wobei die Hardware-Register direkt angesprochen werden. Das auszugebende Byte erhalten wir wieder vom Stack. Wegen der vorausgegangenen Wortzugriffe.müssen wir dabei den Zeiger in a0 um 1 Byte versetzen, damit wir an das als Byte vorliegende Datum herankommen. Weitere Erklärungen findet man im Kommentartext des Listings.

Was nun, wenn kein Drucker angeschlossen oder er aus irgendwelchen Gründen nicht bereit ist (off line, Papier aus, Buffer voll usw.)? Das Original-TOS wartet in diesem Fall bekanntermaßen ca. 30 Sekunden, bis es von selbst abbricht. Auch unser Programm enthält ein (bewußt einfach) programmiertes ‘Timeout’. Eine Langwortzelle wird einfach mit dem Wert 2000000 geladen. Von diesem Wert aus wird solange heruntergezählt, bis die Speicherzelle den Wert 0 enthält. Meldet sich der Drucker innerhalb dieses Zeitraums durch einen LOW-Pegel an der BUSY- Leitung, senden wir das nächste Zeichen. Läuft die Zeit ab, ohne daß der Drucker sich meldet, bricht das Programm ab und übergibt in TOS-Manier den Wert 0 als Fehler-Flag. Für einen Rechner mit 16-MHz-Takt und Cache ergibt sich mit dem angegebenen Wert ein Timeout von etwa 12 Sekunden. Für das Timeout wurde absichtlich kein Timer benutzt, seine Programmierung und Abfrage würde mehr Rechenzeit in Anspruch nehmen als die vorgeschlagene einfache Lösung. Die Benutzung eines Prozessorregisters für das Timeout hätten einige Programme, die davon ausgehen, daß Betriebssystemaufrufe gewisse Register nicht verändern, übelgenommen. Ein ebenfalls zeitaufwendiges Retten und Zurückholen der Register konnte dadurch ebenso umgangen werden. Mit der vorgeschlagenen Lösung kommen sogar TEMPUS und EDISON und das Hardcopy-Programm in [1] zurecht. Noch einen Vorteil hat die Sache: Das Timeout funktioniert auch beim BIOS-Aufruf #3 (Bconout), was im TOS nicht der Fall war. Hier konnte man bei einem Druckerfehler nur Reset drücken oder warten, warten, warten ...

Am Schluß unserer Routine steht nicht etwa ein ‘rts’, sondern ein ‘rte’ denn wir befanden uns ja in einer Exception-Routine. Da unser Programm Vektoren verbiegt, wäre eigentlich ein XBRA- oder Cookie-Jar-Protokoll vonnöten. Die peniblen ATARIaner mögen dem Verfasser seine Faulheit in dieser Hinsicht verzeihen. So wie es abgedruckt ist, sollte man das Programm als letztes vektorverbiegendes Programm (z.B. im Auto-Ordner) starten, also z.B. auch nach NVDI, OVERSCAN und anderen ‘Vektorverbiegern’. Nochmals erwähnt sei, daß das Programm nur dann einen Sinn hat, wenn Drucker mit entsprechend großen und schnellen Buffern Verwendung finden. Profitieren werden alle Programme, welche die Druckerausgabe über die erwähnten Systemroutinen abwickeln. Einige neuere Grafik-, DTP- und Textprogramme beseitigen das Manko der Centronics-Ausgabe des TOS bereits durch eigene Direktausgaben. Bei diesen beschleunigt unser NEWPRN.PRG natürlich nichts!

Was ist nun der Lohn für das das Abtippen und Assemblieren des Programms? In den Tabellen 1 bis 3 sind einige Werte aus der Praxis zusammengestellt. Angegeben ist neben der Art desTests immer die Zeitdauer ohne unser Beschleuniger-Programm, dann die Zeit mit dem Programm und schließlich der erzielte Beschleunigungsfaktor.

Im Falle des GFA-BASIC (Tabelle 1) sind die Werte für den Interpreter und die für den Compiler angegeben. Im Interpreter entfällt nämlich mehrZeit auf die Abarbeitung der Schleife als auf den eigentlichen Druckvorgang. Das Benchmarkprogramm steht in Listing 2. Wer das Programm auf seinen Drucker loslassen möchte, sollte zweierlei beachten: Einmal muß das Programm NEWPRN.PRG auf der gleichen Directory-Ebene liegen wie das Benchmark-Programm, und es darf vorher noch nicht gestartet worden sein. Zum anderen macht es nur Sinn, wenn der verwendete Drucker einen genügend großen Buffer (mindestens 192 KByte) hat. Bei Druckern mit weniger Buffer muß man die getestete Datenmenge (32000 Byte) entsprechend vermindern.

Der Benchmark-Test Hardcopy (Tabelle 2) wurde mit der in [1] beschriebenen Hardcopy-Routine für 24-Nadler durchgeführt. Die Hardcopy-Routine des Original-TOS hat nämlich unfairerweise einen Haken: Sie benutzt offensichtlich die TOS-eigene (natürlich auch entsprechend langsame) Ausgaberoutine und verweigert hartnäckig den Zugriff auf unsere neuen GEMDOS- und BIOS-Schleifen. Mit anderen Worten: Die Original-Hardcopy ist mit und ohne NEWPRN.PRG gleich schnell (oder langsam)! Leider trifft dies auch für das KAOS-TOS zu. Hier müßte man direkt ins ROM patchen, was angesichts der vielen verschiedenen TOS-Versionen nicht immer ratsam ist.

In Tabelle 3 findet man die Ergebnisse eines recht interessanten Tests. Dort wurde nämlich eine DIN-A4-Seite (Querformat) in der Größe von 1440 x 2140 Pixel im Rechner virtuell aufgebaut und über das NVDI-GDOS an den Drucker ausgegeben (Druckertreiber: NB15.SYS). Gemessen wurde die Zeit, die der VDI-Aufruf Update Workstation (VDI 4) in Anspruch nimmt. Berücksichtigt man, daß dabei viel Rechenzeit vom GDOS-selbst benötigt wird, ergibt sich in der Praxis dennoch immer noch eine deutliche Beschleunigung der Druckerausgabe.

Zusammenfassend betrachtet ermöglicht unser NEWPRN.PRG in der Praxis im ungünstigsten Falle (GDOS-VDI-Ausgabe) eine Beschleunigung um fast den Faktor 2 und im günstigsten eine um ca. den Faktor 10 (GEMDOS-#5-Ausgabe). Nicht verschwiegen werden darf, daß durch die Umlenkung zweier Betriebssystemroutinen BIOS und GEMDOS im Rechner um einige wenige Prozent langsamer ablaufen, was Utilities wie GEMTEST oder Q_INDEX auch prompt bestätigen.

Literaturhinweis:

[1] Hardcopy, ST-Computer 2/92, S. 94ff
[2] D-RAM-Druckerbuffer, ELEKTOR 11/91, S. 9ff
[3] Jankowski/Jeschke/Rabich: ATARI ST Proßbuch, Düsseldorf 1988, S. 741
[4] Der ST wird handgreiflich, ST-Computer 2/92, S. 126ff
[5] Somewhere over the Rainbow, ST-Computer 11/89, S. 160


;NEWPRN.PRG
;Beschleunigung der Druckerausgabe an der Parallelschnittstelle 
;über die Funktion BIOS #3, Gerät 0 (Printer) und GEMDOS #5
;Programm zum einmaligen Start (letztes Programm im AUTO-Ordner)
;by Heinrich Emmerl (c) 1992 MAXON Computer 
xbios   equ 14
bios    equ 13
gemdos  equ 1
psg_r   equ $ff8800     ;PSG-Read-Register
psg_w   equ $ff8802     ;PSG-Write-Register
gpip    equ $fffa01     ;MFP-Data-Register (Bit 0 = 'BUSY')
;
;***** Exception-Vektoren 'verbiegen'
;
start:  lea.l   newbios(pc),a1 ;neue BIOS-Einsprungadresse 
        pea     (a1)
        move.w #45,-(sp) ;Vektor-Nummer
        move.w #5,-(sp) ;setexec
        trap #bios
        addq.l #8,sp
        move.l d0,oldbv ;alten BIOS-Vektor merken
;
        lea.l newgem(pc),a1

;neue GEMDOS-Einsprungadresse 
pea (a1)
move.w #33,-(sp) ;Vektor-Nummer
move.w #5,-(sp) ;setexec
trap #bios
addq.l #8,sp
move.l d0,oldgv
;
move.l #ende-start+256,-(sp)
move.w #$31,-(sp) ;keep process
trap #gemdos ;Programm resident halten

;
;***** Hier beginnt die neue GEMDOS-5-Routine
;
newgem:     move.w  (sp),d0
            move.l  usp,a0
            btst.l  #13,d0
            beq.s   newgem1
            lea.l   6(sp),a0
newgem1:    cmpi.w  #5,(a0)+ ;ist es unsere Funktions-Nr. 5?
            beq.s   newprout
            movea.l oldgv,a0 ;weiter im Original-GEMDOS
            jmp     (a0)
;
;***** Hier beginnt die neue BIOS-3-Routine
;
newbios:    move.w  (sp),d0
            move.l  usp,a0
            btst.l  #13,d0
            beq.s   newbios1
            lea.l   6(sp),a0
newbios1:   cmpi.w  #3,(a0)+ ;ist es unsere Funktions-Nr. 3?
            bne.s   nein
            cmpi.w  #0,(a0)+ ;ist es Gerät 0 (Drucker)?
            beq.s   newprout
nein:       movea.l oldbv,a0 ;weiter im Original-BIOS
            jmp     (a0)
;
;***** Hier beginnt die neue Zeichenausgabe an Centronics
;
newprout:   move.l  #2000000,t_out ;Zähler für timeout
            move.l  #-1,d0      ;falls kein Fehler 
timeout:    btst.b  #0,gpip     ;BUSY schon LOW?
            beq.s   out         ;ja!
            subq.l  #1,t_out    ;nein! weiter warten
            bne.s   timeout
            clr.l   d0          ;Fehlerflag!
            rte
out:        move.b  #15,psg_r    ;Register IOB (Soundchip) ansprechen 
            move.b  1(a0),psg._w ;Datum an Centronics anlegen 
            move.b  #14,psg_r    ;Register IOA ansprechen 
            bclr.b  #5,psg_w     ;Strobe-Impuls ausgeben... 
            bset.b  #5,psg_w     ;... und gleich wieder wegnehmen 
            rte                  ;zurück aus der Exception
;
            .EVEN
oldgv:      .DS.l 1     ;Platz zum Merken des alten GEMDOS-Vektors 
oldbv:      .DS.l 1     ;Platz zum Merken des alten BIOS-Vektors 
t_out:      .DS.l 1     ;Zähler für Drucker-Timeout
ende:       .EVEN

Listing 2

' Programm in GFA-BASIC zum Testen der 
' Ausgabe an der Parallel-Schnittstelle 
' Das Programm lädt während des Testa 
' den Centronics-Ausgabe-Beschleuniger 
' NEWPRN.PRG nach.
' by Heinrich Emmerl (c) 1992 MAXON Computer 
PRINT "AUSGABETEST: 32000 BYTE AN CENTRONICS" 
PRINT
PRINT "Ohne NEWPRN.PRG:" 
teste
VOID EXEC(0,"NEWPRN.PRG","","")
PRINT
PRINT "Mit NEWPRN.PRG:"
teste
PRINT
PRINT "Bitte Maustaste drucken"
REPEAT 
UNTIL MOUSEK 
PROCEDURE teste 
    t=TIMER
    FOR i%=1 TO 32000 
        VOID GEMDOS(5,64)
    NEXT i%
    X=TIMER-t
    PRINT "Zeit für GEMDOS 5-Ausgabe: ";x/200;" s" 
    t=TIMER
    FOR i%=1 TO 32000 
        VOID BIOS(3,0, 64)
    NEXT i% 
    x=TIMER-t
    PRINT "Zeit für BIOS 3-Ausgabe: ";x/200;" s" 
    t=TIMER
    LPRINT STRING$(32000,CHR$(64)) 
    x=TIMER-t
    PRINT "Zeit für GEMDOS 64-Ausgabe: ";x/200;" s" 
RETURN

Heinrich Emmerl
Aus: ST-Computer 05 / 1992, Seite 79

Links

Copyright-Bestimmungen: siehe Über diese Seite