TOS-Accessory: Multi-Accessory im Quelltext, Teil 5

Nachdem nun das »TOS-Accessory« über diverse kleinere Tools verfügt, die so ziemlich in jedem Multi-Accessory in ähnlicher Form zum Einsatz kommen, starten wir jetzt mit einem Drucker-Spooler, der Seinesgleichen sucht, einen Generalangriff gegen die großen Vertreter dieser Klasse.

Nach Beseitigung einiger kleiner Kinderkrankheiten aus der RAM-Disk von der letzten TOS-Ausgabe [1], richten wir in diesem Artikel unser Hauptaugenmerk auf den Drucker-Spooler. Dazu sei erst einmal erklärt, was ein Spooler ist und wie er überhaupt funktioniert. Da Textverarbeitungen die Daten für Druckausgaben in aller Regel schneller erzeugen als der angeschlossene Drucker sie zu Papier bringt, warten die Programme die meiste Zeit darauf, daß der Drucker wieder für neue Daten aufnahmebereit ist. In der Praxis äußert sich das so, daß der Rechner während des Druckens so tut, als sei er vollauf beschäftigt, obwohl dies eigentlich nur der Drucker ist. Diejenigen, die einen Drucker mit zuverlässigem Papiertransport besitzen und für den ganzen Ausdruck genügend Papier eingelegt haben, suchen sofort das Weite, um entweder dem Gekreische der Nadler oder dem Ozon der Laser zu entfliehen. Der viel größere Rest, wie Tintenstrahldrucker-Besitzer oder einfach unter Zeitdruck stehende Anwender, verwenden in Zukunft den TOS-Accessory Drucker-Spooler. Dieser hängt sich in die Betriebssystemfunktion ein, die die Zeichen an den Drucker schickt. Ist der Drucker für weitere Ausgaben bereit, füttert die neue Funktion ihn mit den Daten. Somit produziert die Textverarbeitung oder das DTP-Programm in einem Bruchteil der eigentlichen Ausgabe die Druckdaten. Der Clou besteht nun darin, daß der Spooler die Daten im Hintergrund an den Drucker schickt, wodurch sich mit dem Rechner ganz normal weiterarbeiten läßt. Selbst die im Druck befindliche Datei steht für neue Arbeiten bereit, obwohl sie noch nicht einmal komplett auf Papier steht.

Zielsetzung

Soviel zum allgemeinem Vorgehen eines Drucker-Spoolers. Kommen wir jetzt aber zum TOS-Accessory Spooler. Wer die letzten Ausgaben zum Thema TOS-Accessory verfolgt hat, weiß inzwischen, daß Flexibilität und große Anwendungsvielfalt zu unseren Prinzipien gehören. Aus diesem Grund schicken wir die Druckzeichen nicht direkt durch Hardware-Programmierung an die Cetronics-Schnittstelle, weil alle Atari-Laser-Besitzer auf der Strecke bleiben würden. Dort sind nämlich die Daten via DMA zu schicken. Außerdem gibt es immer noch Drucker mit serieller Schnittstelle, die wir mit dem Spooler auch erreichen wollen. So bleibt als einzige Alternative die Verwendung der Betriebssystem-Ausgabefunktion, Diese kümmert sich dann hoffentlich um die Versendung der Daten an den richtigen Port.

Außerdem ist im Konzept zu berücksichtigen, daß der Spooler - und somit sein benötigter Speicherplatz -brach liegt. Da wir mit der RAM-Disk schon so einen Speicherfresser haben, bietet es sich an, diesen Speicher gemeinsam zu verwalten. Doch was nutzt die beste Speicherverwaltung, wenn das RAM für den kompletten Druckauftrag nicht ausreicht? Wie sich jeder leicht ausrechnen kann, würde in diesem Fall der Spooler gar nichts bringen, da das System doch wieder blockiert wäre. Dazu kommt noch, daß die meisten Druckausgaben auf dem Atari im Grafikmodus ablaufen, pro Seite also etwa ein halbes Megabyte an Druckdaten entstehen. Ein Ausweichen auf Diskette oder besser Festplatte ist somit unumgänglich. Besonders letztere Fähigkeit dürfte alle Anwender, die sich bereitsauf einen anderen Spooler eingeschworen haben, ins Grübeln bringen. Soweit uns bekannt ist, gibt es derzeit keinen Spooler, der bei Speicherplatzmangel auch auf die Festplatte zurückgreift.

Hintergrundarbeit

Wie versorgen wir nun die Bios-Funktion »Bconout« mit Druckdaten, ohne das Hauptprogramm bei der Arbeit zu stören? Die einzige Möglichkeit, im Hauptprogramm mal kurz ein paar Zeichen an den Drucker zu schicken, läuft über einen Prozessor-Interrupt. Bei der Suche nach einem passenden Interrupt fiel die Wahl auf den 200-Hz-Timer. Zum einen ist er vom Interrupt-Level her relativ niedrig, das heißt Interrupts höherer Level arbeiten während der Druckausgabe ungestört weiter, und zum anderen steht er bei jeder Rechnerkonfiguration zur Verfügung. An der Adresse »$14« steht ein Zeiger auf die Routine, die diesen MFP-Interrupt bedient. Durch Eintragen der neuen Funktion an Position »$14« leiten wir diesen Interrupt um. Vor dem Neusetzen rettet man noch den alten Zeiger, denn die sich dahinter verbergende Funktion hatte mit Sicherheit auch ihre Daseinsberechtigung.

Da unsere Routine solange Zeichen an den Drucker schickt, bis dieser weitere Eingaben verweigert, dauert der Vorgang relativ lange. Im Zeitverständnis des Prozessors eine halbe Ewigkeit. Routinen, die zu lange einen Interrupt sperren, führen zu allen nur erdenklichen Absurditäten, wie Bildschirmflackern, Maushüpfen oder gar Systemabsturz. Der Interrupt darf nie zu lange gesperrt bleiben.

Also müssen wir aus dem 200-Hz-lnterrupt irgendwie aussteigen, ohne jedoch ins Hauptprogramm zurückzuspringen. Der Assemblerbefehl »rte« würde zwar den Interrupt im Statusregister ausmaskieren, nur befindet sich der Prozessor dann nicht mehr innerhalb unserer Routine.

Da alle Interrupt-Routinen mit einem »rte« schließen, dieser Befehl beim Zurückspringen aber auch den Interrupt-Level zurücksetzt, haben wir leichtes Spiel. Zuerst merken wir uns die Rücksprungadresse. Diese steht im Falle von »rte« mit zwei bzw. vier Bytes Abstand vom Stackpointer. Jetzt tragen wir an gleicher Stelle die Adresse der Funktion ein, die die Zeichen an den Drucker schickt. Mit einem Sprung zur alten 200-Hz-Funktion erledigt sich der Rest von selbst. Die Funktion zur Zeichenausgabe darf dann nur nicht mit »rts« abschließen, sondern springt direkt an die zuvor gesicherte Adresse. Um bei der Bearbeitung ein Chaos durch verschachtelte Interrupts zu vermeiden, signalisieren wir mit einem Schalter, daß die Interrupts bis auf weiteres direkt an die Originalroutine weiterzuleiten sind.

Mit dieser Methode läßt sich nun immer noch nicht nach Gusto schalten und walten. Alle von unserer Ausgabefunktion aufzurufenden Betriebssystemfunktionen dürfen nämlich nicht durch unseren Interrupt unterbrochen worden sein. Hierzu stelle man sich folgendes vor: An einer bestimmten Adresse sichert eine Systemfunktion einen Wert, um später darauf zurückzugreifen. Unser Interrupt würgt hier ab und ruft die gleiche Funktion nochmals auf. Nach Rückkehr steht an dieser Stelle jetzt der durch unseren Aufruf eingetragene Wert, der längst nicht identisch mit dem ersten sein muß.

In unserem Fall sind davon die GEMDOS-Funktionen zum Laden und Speichern von Dateien betroffen. Die BIOS-Funktion zur Ausgabe an den Drucker leiten wir ja ohnehin um, sie ist also garantiert nicht aktiv. Ist ein Atari-Laser im Einsatz, bei dem die »Bconout-Funktion« die Zeichen über den DMA-Port schickt, müssen wir uns vergewissern, daß das Hauptprogramm nicht gerade selbst am DMA-Port rumfummelt. Durch Setzen der System-Variablen »flock« (Ox43E) lassen sich DMA-Zugriffe vorübergehend sperren. Natürlich dürfen wir diese Variable nicht einfach wild setzen, ohne es vorher zu testen. Schließlich ist noch der DMA-Port auf Freiheit zu prüfen und gegebenenfalls der Prozessor anderweitig bei Laune zu halten. In dem so zusammengebastelten Interrupt lassen wir nur Zeichen, die schon im Speicher sind, an den Drucker schicken. Zeitlich gesehen reicht das eine ganze Weile. Stellt die Interrupt-Routine fest, daß ihr so langsam die Zeichen im Speicher ausgehen, signalisiert sie es durch das Setzen eines Flags. Daraufhin kümmert sich an anderer Stelle eine zweite Funktion um das Nachfüllen der Daten von Platte oder Diskette. Diese Funktion hängt im GEMDOS- und GEM-Trap. Ist der Hauptprozeß ein GEM-Programm, erreicht man spätestens nach einer Sekunde einen GEM-Aufruf, nämlich den vom MU__TIMER-Ereignis des TOS-Accessories. Hier kann die zweite Funktion ohne Bedenken die Druckdaten vom festen Medium in den Speicher kopieren. Bei einer Nicht-GEM-Anwendung bleibt nur zu hoffen, daß sie hin und wieder einen Systemaufruf vornimmt. Wenn nicht, ist das nur insofern tragisch, als der Drucker dann eine Pause einlegt; auf keinen Fall gehen aber Daten verloren. Spätestens nach Rückkehr zum Desktop geht's weiter. Eine wesentlich elegantere Art für die Abwicklung eines zweiten Prozesses möchten wir hier aber nicht verschweigen: Nämlich durch das Einbinden kleiner Routinen in sämtlichen Traps (BIOS, XBIOS, GEMDOS, GEM und evtl. LINEA), die bei Eintritt in die entsprechenden Funktionen ein Flag setzen und es beim Verlassen der Funktion wieder löschen. Bei den BIOS-Funktionen muß man die Einsprungtiefe mitzählen, da diese reentrant sind, sich also selbst aufrufen können. Eine weitere Sonderbehandlung verlangen die GEMDOS-Funktionen »Pterm« und »Pexec«. Weil sie nicht gleich zum »parent process« zurückspringen, sondern den aktuellen Prozeß beenden bzw. einen neuen starten, wartet man einfach, bis der alte oder neue Prozeß eine andere Systemfunktion aufruft.

Die bereits vorgestellte Interrupt-Routine führt den zweiten Prozeß nur durch, wenn dieses Flag gelöscht ist. Damit ließe sich der Hauptprozeß bei nahezu jeder Bedingung einmal unterbrechen, um den zweiten Prozeß zu starten. Leider macht uns hier der Desktop einen Strich durch die Rechnung. Er ruft die System-Funktionen nicht über die Traps auf, sondern springt direkt zu den Funktionen. Denkbar wäre eine Kombination aus beiden Verfahren. Die Interrupt-Routine prüft, ob der Hauptprozeß gerade der Desktop ist. Wenn ja, findet die erste Methode Einsatz, sonst die zweite.

Wie schon weiter oben beschrieben, sollte noch der freie RAM-Disk-Speicher im Spooler Verwendung finden. Vor der Auflösung dieses Rätsels ein paar Worte zur Organisation der RAM-Disk. Der Dateimanager des GEMDOS teilt die ganze RAM-Disk in sogenannte Cluster auf, wobei in unserem Fall ein Cluster immer aus zwei aufeinanderfolgenden Sektoren besteht. Jede Datei belegt mindestens ein Cluster. In der »FAT«, das sind spezielle Sektoren am Anfang der Disk, steht ein 2-Byte-Eintrag für jeweils ein Cluster. Diese Cluster-Einträge zeigen bei größeren Dateien auf das jeweils nächste Cluster dieser Dateien. Für uns ist aber nur die Tatsache wichtig, daß die belegten Cluster-Einträge einen Wert größer als Null haben. Nur die freien Cluster haben in der FAT einen Cluster-Eintrag mit dem Wert Null. Also stellt es jetzt kein Problem mehr dar, diese freien Cluster aufzuspüren und für den Spooler zu gebrauchen. Die so abgespeckte RAM-Disk stört sich nicht einmal an Lesezugriffen des Hauptprozesses. Bei neuen Schreibversuchen seitens des Hauptprozesses hilft bei akutem Speicherplatzmangel nur noch die Rückgabe eines Fehlercodes. (ah)

Literaturhinweise: [1] Jürgen Lietzow, »Individuell, Teil 4«, TOS 3/92, Seite 94

Nach diesem Teil des TOS-Accessories legen wir eine kreative Pause ein. Falls Sie Ideen für weitere Tools haben, schreiben Sie uns einfach:

ICP-Verlag
Redaktion TOS
Kennwort: TOS-Accessory Wendelsteinstraße 3 8011 Vaterstetten

Aufbau der Interrupt-Routine

Prüfen, ob die Interrupt-Routine schon aktiv ist. Dies wird anhand eines Flags ermittelt. Wenn ja, zu 6 springen.

  1. Prüfen, ob Zeichen zu senden sind. Wenn nicht, zu 6 springen.
  2. Das Aktiv-Flag setzen
  3. Rücksprungadresse eintragen
  4. Neue Rücksprungadresse setzen
  5. Adresse der alten 200-Hz-Routine auf den Stack laden
  6. Alte 200-Hz-Routine (mit »rts«) aufrufen
  7. Prüfen, ob »flock« eingeschaltet ist. Wenn ja, zu 12 springen.
  8. »flock« sperren
  9. Zeichen über BIOS-Funktion an Drucker senden
  10. »flock« wieder auf Null setzen
  11. Gesicherte Rücksprungadresse auf den Stack laden
  12. Mit »rts« zurück zum Hauptprozeß

Jürgen Lietzow
Aus: TOS 04 / 1992, Seite 102

Links

Copyright-Bestimmungen: siehe Über diese Seite