Laservisionen: Programmierung des ATARI Laserdruckers

Zusammen mit dem ATARI Laserdrucker wird eine Diskette mit Software und ein Handbuch mitgeliefert. Doch bei intensiver Benutzung stellt man schnell einige Mängel bei der Software und auch bei der Dokumentation fest.
Mit 1st Word klappt auf einmal kein einwandfreier Blocksatz mehr und irgendwann hat man auch das Bedürfnis mehr als nur Hardcopies ausdrucken zu können, und falls beim Drucken ein Fehler auftritt, hängt das System...

So kommt es dann, daß man auf die naive Idee kommt, einen neuen Treiber für den Drucker zu schreiben. Dies scheint auch gar nicht schwierig, da der Drucker mit der normalen DMA-Schnittstelle ähnlich wie eine Harddisk zu programmieren ist. Auf knappen acht Seiten findet man im Handbuch die Informationen darüber, welche Befehle verstanden werden. Doch beim Überlesen der wenigen Informationen ahnt man schon, daß hier nicht alles verraten wird. Und es verwunderte mich auch nicht sehr, als der erste Treiber zur Ausgabe einer Grafikseite nur wirre Flecken auf das Papier brachte. Immerhin!?! Kurzum entschließt man sich den Originaltreiber zu disassemblieren, um hier des Rätsels Lösung zu finden. Doch diese Informationsquelle scheint von ATARI nicht geplant worden sein. Beim ersten Anblick des Listings glaubt man nicht, daß hier überhaupt etwas sinnvolles programmiert wurde. Doch der selbstmodifizierende Code scheint zu laufen - nur gibt die Laserdruckeranleitung nicht viel Hilfestellung, warum er läuft.

An die Arbeit...

Da zumindest ATARI Deutschland zu diesem Zeitpunkt nicht weiterhelfen konnte, begann meine Programmierung nach dem ‘Trial and Error’-Verfahren (Ausprobieren und versuchen, aus den Fehlern zu lernen). Das Ergebnis dieser Versuche, die noch etwas mehr zu Tage förderten, möchte ich Ihnen hiermit vorstellen. Ein neuer Treiber für 1st_Word und ein universeller Grafiktreiber sind für diese Artikelserie herausgekommen. Der 1st_Word Treiber soll in der Lage sein einen EPSON-kompatiblen Drucker zu simulieren, und der Grafiktreiber kann in jedes beliebige Programm eingebunden werden. Doch zunächst zur Programmierung (Erst die Theorie).

Ein wenig DMA...

Wie allgemein bekannt sein sollte, wird der ATARI Laserdrucker über den ATARI DMA-Port angeschlossen. Über diese Schnittstelle - auch AHDI (für ‘ATARI Hard Disk Interface’) genannt - kann eine schnelle Datenübertragung vorgenommen werden. Diese Übertragung kann ohne Zutun des Prozessors durch den DMA-Chip erfolgen. DMA steht für ‘Direkt Memory Access’ und weist auf die Fähigkeit des Chips hin, direkt ohne die Hilfe anderer Chips auf den Speicher zuzugreifen und diesen mit Daten zu beschreiben oder daraus zu lesen.

Das externe Interface hat eine Ähnlichkeit mit dem SCSI (Small Computer System Interface). Jedoch fehlen dem AHDI einige Steuerleitungen, die sonst für das etwas kompliziertere Protokoll der SCSI-Schnitt-stelle benötigt werden. Jene Schnittstelle ermöglicht auch mehrere Busmaster (Geräte, die über die Busbenutzung bestimmen). Für ATARI-Nutzung reicht ein Busmaster, der ATARI, natürlich aus.

Die Programmierung der Steuer- und Datenleitungen erfolgt über zwei Register im DMA-Chip. Mit diesem Chip wird auch der Floppytransfer ermöglicht. Auf die Floppyfunktionen soll hier allerdings nicht detaillierter eingegangen werden. Das Interesse gilt hier besonders den zum DMA-Port geführten Leitungen.

Die Funktionen der Register unterscheiden sich je nachdem, ob auf das Register schreibend oder lesend zugegriffen wird. Das Datenregister liest oder schreibt Daten auf den Port bzw. in das Sektorzählerregister. Es stellt bei entsprechender Programmierung eine parallele Schnittstelle dar. Durch Schreiben auf das Steuerregister können die Steuerleitung und die Funktionsweise des DMA-Chips eingestellt werden. Hier wird ausgewählt, ob mit dem Datenregister der Port oder das Sektorzählerregister angesprochen wird. Lesezugriffe geben den Fehlerstatus des Bausteins wieder.

Bild 1 zeigt den Aufbau des DMA-Chips. Sofort fällt der große FIFO-Speicher (First In - First Out) auf. Dieser Speicher arbeitet wie eine Pipeline und puffert den externen und internen Datenaustausch. Er besteht aus 2 mal 16 Bytes - doch dazu später.

Bild 1: Aufbau des DMA-Controlers

...und einige Befehle

Genau wie für die Harddisk gibt’s auch für die Laserdruckmaschine ein paar Bitkombinationen, die das magische Gerät zur Arbeit bewegen. Diese Befehle lehnen sich wie bei der Festplatte an den CCS (Common Com-mand Set = Gemeinsame Befehlsmenge) an. Dies ist ein Standard, der für SCSI-Geräte vereinbart wurde. Beim ATARI-Drucker stehen die in Tabelle 1 genannten Befehle bereit. Zum Vergleich dazu wurden die Befehle der Harddisks SH204 und SH205 angegeben. Wie man erkennt, werden als Befehle beim AHDI immer 6 Byte große Befehlsblöcke gesandt. Beim ATARI ist nur der Aufbau der ersten Bytes für alle Geräte verbindlich. Die Bits 5-7 geben im Byte 0 die Nummer des Gerätes an, daß angesprochen werden soll. Diese Nummer wird von allen Controlern beachtet.

Das Programmieren beginnt...

Bevor das Drucken beginnen kann, muß dem Laserdrucker der entsprechende Befehl gesandt werden, doch wir erinnern uns: im ersten Byte ist die Gerätenummer enthalten. Welche Gerätenummer aber hat der Laserdrucker?

Die Nummer ist mit drei kleinen Schaltern vom Benutzer an der Interfacebox SLMC 804 frei einstellbar. Doch mit einem Trick kann man die Nummer erfahren. Vorausgesetzt der Benutzer hat ‘nur’ einen Laserdrucker, so kann dieser gefunden verden, indem an alle Geräte (Devices) am DMA-Port ein Befehl gesandt wird, der Daten über das angeschlossene Gerät abfragt. Ist kein Gerät mit einer bestimmten Nummer vorhanden, so kann dies mit einem Timeout abgefangen werden. Andere Geräte als der Laserdrucker können dadurch erkannt werden, daß sie andere Daten als dieser (bzw. eine Fehlermeldung) zurücksenden.

Der Befehl zum Überprüfen, ob ein Laserdrucker vorhanden ist, heißt ‘Inquiry’ und hat den Befehlskode $12. Jetzt müssen die 6 Bytes des Befehls ausgegeben werden. Dafür muß zunächst dem Betriebssystem mitgeteilt werden, daß man bei der Arbeit mit dem DMA-Chip nicht gestört werden will. Dies geschieht durch Setzen der Variable $43E (‘flock’ = Floppy Lock = Disk Schloß). Zuvor begibt man sich in den Supervisormode, denn die Speicherverwaltungseinheit (MMU) duldet keine Usermodezugriffe auf Systemvariablen.

Die Vertical-Blanc-Routine beachtet die Variable flock und stört daher nicht mehr durch Floppyzugriffe während der Beschäftigung mit dem Laserdrucker.

Danach muß der DMA-Port angewählt werden. Die Adressleitung A1 wird als Zeichen für einen neuen Befehl auf Low gelegt ($88 ins Steuerregister), nachdem zuvor der DMA-Status und die FIFO durch hin-und herschalten des Bits 8 im Steuerregister gelöscht wurden.

In das Datenregister läßt sich jetzt das erste Byte schreiben. Da der Controler des Laserdruckers rein softwaremäßig arbeitet, muß etwas gewartet werden. Bei der Harddisk ist dies nicht erforderlich. Für alle weiteren Bytes wird die Adressleitung auf High gelegt ($8A ins Steuerregister). Den ordnungsgemäßen Empfang eines jeden Bytes quittiert der Laserdrucker durch die IRQ-Leitung. Das dadurch gelöschte Bit im 68901 zeigt an, daß das DMA-Gerät bereit ist, ein weiteres Byte zu senden bzw. zu empfangen. Der Status dieser Leitung wird im 68901 abgefragt, an dessen Port sie geführt ist. Für bestimmte Anwendungen besteht hiermit auch die Möglichkeit, durch einen Impuls auf dieser Leitung einen Interrupt auszulösen. Mit dem IRQ ist es zu erklären, daß nach dem Senden des 6. Bytes einer Kommandosequenz keine Bestätigung durch das DMA-Gerät erfolgt, es sei denn, es erwartet eine Fortsetzung der Kommunikation.

Der DMA-Kommando-Block

Byte 0 | ooooooooo | Befehlsnummer Controlernummer  
Byte 1 | ooo------ | beim ATARI ungenutzt (sonst Befehlssatzerweiterungen)  
Bei Harddisks etc.: Devicenummer (Laufwerksnummer)  
Byte 2	|---------|	bei der Harddisk für die  
Byte 3	|---------|	Sektornummer  
Byte 4	|ooooooooo|	Operationslänge bzw. Blockanzahl etc.
Byte 5	|oo-------|	Zusatzinformationen (max. 8 Bit)

Harddiskbefehle

$00 Test Drive Ready, prüft auf vorhandene Festplatte
$03 Request Sense
Byte 4 muß 4 sein, sonst evtl. Plattenabsturz (Nur der Controler?!). Liefert den Status zurück
$04 Formatierbefehl
$08/$0a Lese- und Schreibbefehle
Byte 1-3: Blocknummer in Reihenfolge High, Mid, Low
Byte 4: Blockanzahl
$0b Seekbefehl zum Anfahren eines Sektors (Param. wie $08)
$15 Mode Select zur Einstellung diverser Parameter der Platte

Laserdruckerbefehle

$03 Request Sense
Keine Parameter, fragt den Status ab

$0A Print
Byte 4: Anzahl zu übertragender Seiten 0 = Eine Seite drucken n = n Seiten drucken 255 = unendlich drucken
Byte 5: Bit 6: Laser FIFO vor dem Druck der nächsten Seite nicht löschen
Bit 7: Bei jedem Strahlrücklauf einen IRQ generieren Nach jeder gedruckten Seite wird ein Statusbyte zurückgegeben.

$12 Inquiry
Byte 5 muß $80 sein, sonst keine Parameter Liefert eine Liste zurück:
Byte 0: $02 als Kennung für Drucker
Byte 1-3 unbenutzt. Byte 4: Stringlänge und Byte 6-n z.B.:
"PAGE PRINTER:SLMC804v2.1:ATARI "

$15 Mode Select Byte 5 Bit 7: Zurücksetzen auf Defaultwerte
Parameterliste wird dann nicht beachtet

$1A Mode Sense Byte 4: Anzahl der zu lesenden Bytes aus der Parameterliste
Byte 5 Bit 7: Maximalwerte ermitteln gelöscht: Aktuelle Werte auslesen

$1B Stop Print Stoppt einen Mehrseitendruckbefehl. Dieser Befehl muß nach dem Empfang des Statusbytes (wird nach jeder Seite gesandt) gegeben werden. Da in diesem Moment bereits eine neue Seite eingezogen wurde, muß der Befehl jeweils eine Seite früher gegeben werden.

Tabelle 1: Die Befehle des ATARI-Lasers im Vergleich zur Harddisk SH204/205

So, nun kann der Inquiry-Befehl für alle am DMA-Bus angeschlossenen Geräte gesandt werden. Hierfür beginnt man sinnvollerweise mit der Controlernummer 7, da der SLM 804 werkmäßig auf diese Nummer eingestellt wird. Falls ein Laserdrucker angeschlossen ist, so erhält man den Namen ‘PAGE PRINTER’, zurück. Der Vorgang während dessen der Name zurückgesandt wird, nennt sich Extended Status Phase. Er stellt eine Erweiterung gegenüber den Möglichkeiten einer Harddisk dar. Diese bietet nur eine Command Phase, die DMA-Übertragung und eine Status-Phase. Letztere kennt auch der Laserdrucker. Nach jedem Befehl, so auch nach dem Inquiry-Befehl und vor der Rückgabe des Druckernamens, wird ein Statusbyte zurückgegeben, das über Fehler im Befehlsformat oder Hardwarefehler des Druckers Auskunft gibt (siehe Tabelle). Bei dem abgelisteten Programm wird etwas getrickst: Sofern der Inquiry-Befehl zufällig ein anderes intelligentes DMA-Gerät trifft und dieses eine andere Kennung als der Laserdrucker zurücksendet, so wird die Kommunikation abgebrochen bevor alle Daten empfangen wurden. Dies geschieht dadurch, daß dem Controller durch setzen der A1-Leitung vorgegaukelt wird, einer neuer Befehl komme an. An dieser Stelle könnte tatsächlich ein anderer Befehl z.B. für die Festplatte folgen. In unserem Falle wird allerdings wieder der Laserdrucker angesprochen. Dadurch wird verhindert, daß andere DMA-Devices sich angesprochen fühlen. Der Effekt, daß der Laserdrucker nun den DMA-Bus freigibt, wird dadurch nicht verändert. Erst ein wiederholtes Senden eines Bytes mit der Laserdruckerkennung kann ihn wieder aufmerksam machen. Bei dem Test des Programmes wurde ein kleiner Fehler festgestellt, denn die Harddisk hält dieses Protokoll nicht ein. Die SH 205 reagiert nicht auf das Setzen der A1-Leitung, so daß bei selektierter Harddisk gleichzeitig der Laserdrucker angesprochen werden kann (Buskollision). Hierdurch wird ein kleiner Fehler im TOS ausgeglichen, denn dieses setzt die A1-Leitung für den Bootvorgang falsch.

…und geht weiter...

Bis jetzt haben wir es geschafft mit dem Laserinterface zu kommunizieren, doch das Ziel war (Sie erinnern sich??) Grafik auf den Drucker zu bringen. Für die Übergabe der Grafikdaten reicht die Geschwindigkeit des Prozessors nicht mehr ganz aus (mal abgesehen von dem Programmieraufwand). Also nutzen wir hier die Fähigkeiten des DMA-Chips. Kurz nachdem der Laserdrucker seinen Print-Befehl ($A) mit der richtigen Controllernummer erhalten hat, erwartet er Daten auf seinem AHD-Interface. Dies zeigt er durch ein Low auf der DRQ-Leitung (Data Request) an. Damit der Controller des Laserdruckers nicht einfach den Zustand seiners internen Speichers an das Druckwerk schickt, muß der DMA-Controller für diesen Fall gerüstet sein.

Seine DMA-Adressregister müssen die richtige Startadresse enthalten. Bei den Adressregistern ist zu beachten, daß der DMA-Chip nur wortweise und somit nur auf gerade Adressen zugreifen kann. Die Adresse wird nun nacheinander (niedriges, mittleres, hohes Adressbyte) gesetzt. Damit aber überhaupt DMA möglich ist, erwartet der DMA-Chip auch Informationen über die Anzahl der zu übertragenden Sektoren. Dies sind Blocks von 512 Byte, auch bei einem weniger sektororientierten, unformatierten (?!) Laserdrucker.

Das Sektorcountregister wird mit dem aufgerundeten Wert (oder mehr) geladen und das Steuerregister mit $12. Bit 8 muß bei Schreib- (bzw. Druck-) Vorgängen via DMA gesetzt sein, so daß effektiv $112 eingeschrieben werden muß. Wenn Sie bis hierhin keine Fehler gemacht haben und der Speicher eine Grafik enthält, so kann man schon hier mit den ersten Druckergebnissen rechnen.

Unter der Bedingung, daß ein deutscher SLM 804 verwandt wurde und insgesamt 255 Sektoren übertragen wurden, kann so ein 3,8 cm hoher Streifen bedruckt werden. Die Höhe ergibt sich aus der Auflösung von 300 Zeilen pro Zoll und dem DIN A4 Format mit 292*8 = 2336 Pixeln pro Druckzeile. Will man aber mehr als schlappe 4 cm drucken, so wird es schwierig. Um hier keine Pixels zu ‘verlieren’, muß man sich genaue Gedanken zu den internen Abläufen machen. Zunächst einmal wartet man durch wiederholtes Abfragen der DMA-Adressen die Beendigung des DMA-Vorgangs ab. Diese Adressen werden jeweils um 16 Bytes hochgezählt, und zwischen jedem Transfer von 16 Bytes bekommt der Prozessor das Zugriffsrecht auf den Datenbus. Durch Sperrung aller Interrupts in diesem zeitkritischen Teil wird garantiert, daß der Moment, in dem die erste DMA-Übertragung beendet wurde, nicht verfehlt wird. Aus der Sperrung der Interrupts beruht der Effekt, daß die Maus während des Druckvorgangs nicht bewegt werden kann. Da in dieser Phase auch keine Eingaben verarbeitet werden können, wird dies dem Tastaturprozessor vor und nach dem Druckvorgang mitgeteilt.

Was passiert beim Drucken?

Sobald der DMA-Chip alle Informationen über den zu übertragenden Speicherbereich erhalten hat, lädt er 16 Bytes bzw. 8 Worte aus dem Speicher in seinen FIFO. Danach schaltet er zwischen den beiden FIFO-Hälften um und lädt die anderen 8 Worte. Die ersten Daten stehen jetzt zur Ausgabe an den Drucker bereit. Der Drucker wiederum besitzt auch einen FIFO, die die Länge 2 Bytes bzw. 1 Wort hat und der Übertragungspufferung dient. Wenn nun das Ende der Übertragung erreicht wurde, muß eine neue DMA-Adresse geladen werden und die DMA neu gestartet werden, ohne daß durch den weiterlaufenden Druckvorgang Daten verloren gehen.

Wurde die Endadresse erreicht, so wurde gerade die zweite Hälfte des DMA-FIFO geladen und der Laserdrucker benutzt gerade die untere Hälfte oder seinen FIFO Speicher. Dieses “oder” hängt vom Format der zu druckenden Seite ab und wird in Bild 2 verdeutlicht.

Je nachdem, ob der Laser-FIFO bereits Daten enthält, müssen bei der neuen DMA-Adresse 2 Bytes addiert werden oder nicht. Die 32 Bytes des DMA-Chip-FIFOs gehen in jedem Fall verloren. Ob der FIFO im Laserdrucker noch Daten enthält, hängt von einigen Parametern ab. Der Laser im Drucker schreibt eine Zeile von links nach rechts, vergleichbar mit einem Elektronenstrahl im Monitor. Der Laser wird auch durch ähnliche Signal wie HSync etc. kontrolliert. Kommt der Laser an den rechten Rand der Selenwalze, so benötigt er eine gewisse Zeit für den Rücklauf nach links. In dieder Zeit wird auch das Papier weitertransportiert. Der Einfachheit halber wartet man im Programm nun auf ein solches Zeilenende und kann die übertragenen Bytes durch 16 teilen. Liegt die nächste 16 Byte Grenze auf dem Anfang der nächsten Druckzeile, so kann man davon ausgehen, daß der FIFO Daten enthält. Diese wurden vom Lasercontroler kurz vor dem Ende der Druckzeile angefordert. Der Inhalt des FIFOs wird am Anfang der nächsten Druckzeile ausgegeben. Dies muß beim Neusetzen der DMA beachtet werden, denn die neue DMA-Adresse ist die Abbruchadresse minus 32 Bytes DMA-Chip-FIFO plus bereits gedruckte 2 Bytes Laser-FIFO. Bei den folgenden DMA-Transfers (man braucht mindestens 8) muß dieses Problem immer neu bedacht werden. Es sei denn, man wählt als zu übertragenden Block einen mit einer Länge, die erstens durch die Anzahl der Bytes (bzw. Pixel) pro Zeile und durch 16 teilbar ist (also ein horizontaler Druckstreifen). In diesem Fall wird nur beim ersten gedruckten Streifen der Laser-FIFO-Inhalt genutzt. Bei allen weiteren Streifen kann muß der FIFO neu geladen werden.

Bild 2: Das FIFO-Problem

Bei Fehlern:

Bisher sind wir von einer Schleife ausgegangen, in der auf das Erreichen der Endadresse gewartet wird, um um das Senden der Daten für den nächsten Streifen vorzubereiten. Falls nun währenddessen ein Fehler auftritt, würde das Programm in einer Endlosschleife hängen. Dieses Problem wurde von den Laserdruckerkonstrukteuren dadurch umgangen, daß der Laserdrucker selbst den ordnungsgemäßen Druckvorgang überwacht. Tritt ein Fehler auf, so wird der Print-Befehl sofort beendet, d.h. es wird das abschließende Statusbyte mit der Fehlermeldung gesandt. Ein druckendes Programm braucht also während des Druckvorganges nur zu überprüfen, ob ein Statusbyte zur Verfügung steht (Sie erinnern sich? Die IRQ-Leitung!).

...fertig!

Das abgedruckte Listing wurde mit dem DRI-Assembler erstellt, und die Routinen sind so gestaltet, daß sie aus einem DRI-C-Programm, aus Pascal Plus (alle mit DRI-Objektcodeformat) und demnächst auch von Megamax aufrufbar sind. Die Aufrufe sind jeweils im Header einer Routine angegeben. ‘nr’ ist die Gerätenummer, ‘buffer’ ein zu übertragender Bereich mit Grafikdaten oder Steuerparametern. ‘offset’ ist der Adresswert, um den die Startadresse für DMA-Operationen inkrementiert wird.

Ein paar Worte zur Harddisk

Bevor hier einige weitere Geheimnisse des Laserdruckers gelüftet werden, möchte ich hier noch einmal auf die Harddisk eingehen. Ihr Ansteuerungsprotokoll ist ja bekanntlich dem Laserdrucker ähnlich. Im 6-Byte Kommandoblock geben die Bits 5-7 des 1. Bytes hier zusätzlich die Laufwerksnummer an. Jeder Controller kann bis zu acht Laufwerke verwalten. Die Suche nach funktionstüchtigen Laufwerken geschieht bei der Harddisk nicht durch den Inquiry-Befehl sondern durch den Test Drive Ready Befehl ($0). Dieser gibt ein Statusbyte zurück, das bei entsprechendem Wert die Bestätigung für ein funktionsfähiges Laufwerk ist. Neben den bekannten Mode Select und Request Sense Befehlen gibt es Lese-, Schreib- und Formatierbefehle.

Laserspielereien

Interessante Möglichkeiten ergeben sich mit den im ATARI-Manual nicht genau dokumentierten Einstellmöglichkeiten der Druckers. Wußten Sie, daß man den Druckbereich des Druckers seinen Wünschen anpassen kann? Oder wie man den manuellen Papiereinzug ermöglicht?

Den Schlüssel zu diesen Möglichkeiten bilden die beiden Befehle Mode Select und Mode Sense ($15/$1A). Mit diesen Befehlen kann eine Parameterliste in den Drucker gesandt oder ausgelesen werden. Beim Senden von Mode Sense kann außerdem angegeben werden, ob die augenblicklich eingestellten Werte oder die Maximalwerte abgefragt werden sollen. Die Maximalwerte geben Auskunft über die Möglichkeiten des Druckers. Hiermit kann die Auflösung in DPI, das maximal möglich Druckformat, abgefragt werden. Einige weitere Parameter lassen mit einem recht zukunftssicheren Protokoll rechnen. So gibt es beispielsweise Bits, die Auskunft über vorhandene Eingangs- und Ausgangssorter geben oder solche, die angeben, ob doppelseitiger Druck oder auch Farbdruck möglich ist. Ein gesetztes Bit gibt an, daß der Drucker dieses Feature unterstützt.

Nun genauer zu den Parametern: Dabei enthalten die ersten 2 Werte Höhe und Breite des zu bedruckenden Bereichs. Unabhängig von diesen Parametern kann die Breite und Höhe des linken bzw. oberen Randes auf dem Papier angegeben werden. Der Controler unterstützt entgegen seiner Angaben auch das Bedrucken längerer Formate, d. h. es kann auch eine Papierrolle bedruckt werden. Dies geht natürlich nur über den manuellen Papiereinzug, der bei einem Mode Select durch Setzen des Bit 0 in Byte 9 selektiert wird. In diesem Fall hat der Benutzer nach der Ausführung des Printbefehls Zeit, ein Blatt Papier anzulegen. Die maximale Zeit in Sekunden kann in Byte 14 angegeben werden. Dieses Timeout wird auch benutzt, wenn der Papiervorrat aufgebraucht ist und trotzdem gedruckt werden soll. Für saubere, wirklich zukunftssichere Programme ist die Beachtung der Mode Sense-Parameter zu empfehlen, denn bei ATARI denkt man bereits über neue, leistungsfähigere Drucker nach.

Nun zum Mode Select-Befehl, der die gleiche Parameterliste wie Mode Sense benutzt. Hiermit können Voreinstellungen gemacht werden. Mit modifiziertem Kommandoblock kann auch auf die Standardwerte zurückgeschaltet werden. Bei den deutschen Geräten sind dies die Maße des DIN A4-Formates.

In der nächsten Folge stelle ich Ihnen einen Laserdruckertreiber für 1 st_Word vor, der verschiedene Zeichensätze und Proportionalschrift erlaubt. In dem Zusammenhang wird auch erläutert, wie man die Druckgeschwindigkeit durch Voreinzug des Papiers auf 8 Seiten pro Minute steigern kann, und mit welchem Trick das Drucken selbst mit sehr wenig Speicher möglich ist. Doch nun viel Spaß beim Programmieren und Drucken. Möge der Toner niemals enden...

JW

Fehlermeldungen des Laserdruckers:

Statusbyte: |aaabbbbb| a: Gerätenummer b: Fehlernummer

$00 'OK - Kein Fehler'
$02 'Drucker nicht bereit'
$03 ‘Toner aufgebraucht’
$04 'Aufwärmphase'
$05 ‘Kein Papier mehr’
$06 ‘Keine Trommel' (Selentrommel nicht eingesetzt oder abgenutzt)
$07 ‘Papiereingangsstau'
$08 ‘Papierinnenstau'
$09 'Papierausgangsstau'
$0A 'Gehäuse offen'
$0B 'Fixiererfehler' (Die Einheit, die den Toner einbrennt)
$0C 'Bildaufbaufehler'
$0D ‘Motorfehler’
$0E 'Videofehler'
$10 'Zeitüberschreitung' (Entsprechend dem Timeoutparamametern von mode_select)
$12 'Befehlsfehler' (Fehlerhafter Kommandoblock)
$15 ‘Falsche Gerätenummer' (Der LD belegt eine ganzen Controler)
$1A 'Fehlerhafte Parameter' (bei Befehlen)

Tabelle 2

Parameterliste von Mode Select/Mode Sense:

(Maximal/Defaultwerte der deutschen SLM804-Version in Klammern)

Byteoffset Bedeutung
00 Länge der Parameterliste ohne dieses Byte (23)
01/02 Anzahl der zu druckenden Pixelzeilen (4080)
03/04 Druckbreite = Anzahl Pixel pro Zeile (2400)
05/06 Anzahl unbedruckter Zeilen vom Blattoberrand (0)
07/08 Anzahl unbedruckter Pixel vom linken Rand (0)
09 Bit 0 Manuellen Einzelblatteinzug benutzen (möglich)
Bit 1-3 Kennummer des zu benutzenden Papiereinzugs (-)
Bit 4 Automatische Selektierung des Papiereinzugs: (-) Falls der aktuelle Papiereinzug leer, wird einer mit gleichem Papierformat gewählt.
Bit 5 Papier einziehen für sofortigen Druckstart (-)
Bit 6 Drucker ist ein write-black device (nein)
0A/0B Vertikalauflösung in Pixel pro inch (300)
0C/0D Horizontalauflösung in Pixel pro inch (300)
0E Timeout für Einzelblatteinzug in Sekunden etc. (60)
0F/10 Zeit zum Belichten einer Zeile in 1/1000 s (1797)
11/12 Anzahl bedruckter Seiten seit Reset (???)
13/14 Kapazität des selektierten automatischen Papiereinzugs (250)
15/16 Kapazität des selektierten Papierausgabekorbs (50)
17 Bit 0 Papier stapelweise ausgeben, so daß es während des Druckvorgangs gewendet werden kann. (-)
Bit 1-3 Kennummer des Papierausgabekorbs im Sorter
Bit 4 Druckseiten beidseitig bedrucken (n. möglich)
Bit 5 Seiten im 4-Farbmodus drucken (n. möglich)
nn Reserviert

Tabelle 3

				.text

*******************************************************
*                                                     *
* ldsuchen                                            *
* --------                                            *
*                                                     *
* Sucht nach der Devicenummer des Atari Laserdruckers *
*                                                     *
*******************************************************

DMA_STATUS 		.equ	$FFFF8604 ; DMA-Statusadresse
MFP_GPIO 		.equ	$FFFFFA01 ; I/O Register des MFP
				.xdef	_ld_suche ; externer Zugriff
				.xdef	_ld_print ; möglich
				.xdef	_ld_mselect 
				.xdef	_ld_msense 
				.xdef	_ld_reqsense

*******************************************************
*                                                     *
* Druckt eine Rasterseite aus                         *
* Aufruf muH aus dem Supervisormode erfolgen          *
*                                                     *
* C-Syntax: er = ld_print(nr, buffer, offset, anz);   *
*                                                     *
*******************************************************

pr_dma_ben:
		moveq #-3,D0
		rts

_ld_print:
		illegal
		tst.b 	$43E 				; DMA in Benutzung?
* 									(z. B. Hintergrundprozess)
		bne.s 	pr_dma_ben 			; ja...

		movem.l D3-D4,-(A7) 		; Register retten

		st 		$43e 				; DMA für Eigenbedarf sperren

* 		pea 	aus(pc) 			; Tastaturcontroler ausschalten
* 		clr.w	-(a7)
* 		move.w	#$19,-(a7) 			; ikbdws
* 		trap	#$E 				; XBIOS
* 		addq.l	#8,a7

		lea DMA_STATUS.A0 			; Basisadresse

		move.w	#$88,2(a0) 			; Controlerregister selektieren 
		move.w	2*4+4(A7),d0 		; Devicenummer holen 
		lsl.w	#5,d0 				; in die richtige Position
		ori.H	#$A,d0 				; Print-Befehl
		bsr		schreiben 			; an den Controler 
		bne		prtrerror 			; Übertragungsfehler?

		moveq	#3,d2 				; 4
prbef:	moveq	#0,d0 				; Nullbytes
		bsr		schreiben 			; schreiben
		bne		prtrerror 			; Übertragungsfehler? 
		dbf		d2,prbef

		move.l	#$82,(a0)			; Druckvorgang starten (move.long!)

		move.l	2*4*6(A7),D0		; Grafikdatenbasis

		move.l	2*4+10(A7),D1		; 1 Streifen + 32 Byte DMA-Chip-FIFO 
		moveq	#30,D3 				; Laser-FIFO Hird benutzt
* 									(32-Byte DMA-Chip-FIFO - 2 Bytes Laser-FIFO)
		move.l	D0,-(A7) 			; DMA-Adressen
		bsr 	setdma 				; setzen
		addq.l	#4,A7 				; Stackkorrektur

		move.w	2*4*14(A7),D4		; Streifenanzahl-1 laden

		move.w	#$192,2(a0)			; Sektorzähler selektieren 
		move.l	#$7F0112,(a0)		; DMA-Count setzen
		move	sr,-(A7) 			; Status merken
		ori 	#$708,sr 			; Interrupts sperren

neustr:
		add.l D1,d0 				; + 58400*32
* 									(200 Zeilen *32 Byte FIFO)
strwart:
		btst.b	#5,MFP_GPIO 		; Fehler aufgetreten?
		beq 	dr_fehler

		movep.w	7(a0),d2			; Aktuelle Adresse mit der
		cmp.w	d2,d0 				; Zieladresse vergleichen
		bne		strwart 			; Weiter warten

		sub.l	03,00 				; FIFO-Offset abziehen

		move.l	D0,-(A7) 			; DMA-Adressen
		bsr		setdma 				; setzen
		addq.l	#4,A7				; Stackkorrektur

		move.w	#$92,2(a0) 			; DMA-Status löschen
		move.w	#$192,2(a0) 		; Sektorzähler selektieren 
		move.l	#$7F0112,(a0) 		; DMA-Count setzen

		moveq	#32,D3 				; ab jetzt: LO-FIFO nicht benutzen 
		dbf		d4,neustr 			; bis zum Seitenende

		move	(A7)+,sr 			; Interrupts freigeben
		move.l	#10*200,D1 			; 10 s Timeout
		bsr 	readywait
		bne 	prtrerror
dr_fertig:
		bsr		lesen 				; 5tatus auslesen
		bmi		prtrerror 			; Fehler?

* 		pea		ein(pc)				; Tastatur wieder einschalten 
* 		clr.w	-(a7) 				; Dummy
* 		move.w	#$19.-(a7) 			; ikbdws
* 		trap	#$E 				; XBIOS
* 		addq.l	#8,a7
* 		movea.l (a7)+,a0
* 		add1,l	#$2E,$4A2

prfertig:
		movem.l (A7)*,D3-D4 		; Register zurückholen
		move.w	#$80,2(A0) 			; DMA für Floppy freigeben
		sf 		$43e 				; DMA freigeben (VBlanc zulassen)
		rts

dr_fehler:
		move	(A7)+,sr 			; Interrupts freigeben
		bra		dr_fertig

prtrerror:
		moveq	#-1,D0 				; Übertragungsfehler aufgetreten
		bra 	prfertig
*
* DMA-Adressen setzen
*
setdma:
		move.b	7(A7),9(A0) 		; DMA-Adressen setzen
		move.b	6(A7),7(A0)
		move.b	5(A7),5(A0)
		rts

*******************************************************
*                                                     *
* Fragt den Status eines DMA-Gerätes ab               *
* Aufruf muß aus dem Supervisormode erfolgen          *
*                                                     *
* C-Syntax: er = ld_reqsense(nr);                     *
*                                                     *
*******************************************************

rq_dma_ben:
		moveq	#-3,C0
		rts

_ld_reqsense:
		illegal
		tst.b	$43E 				; DMA in Benutzung?
* 									(z.B. Hintergrundprozess)
		bne.s	rq_dma_ben 			; ja...

		st		$43e 				; DMA für Eigenbedarf sperren

		lea 	DMA_STATUS,A0 		; Basisadresse
		move.w	#$198,2(A0) 		; DMA selektieren
		move.w	#$88,2(A0) 			; Controlerregister
		move.w	9*4+4(A7),D0 		; Druckernummer
		asl.w	#5,D0 				; Nach Bit 5-7
		ori.w	#3,D0 				; Befehl Request Sense
		bsr 	schreiben 			; Byte 0
		bmi 	rqtrerror 			; Übertragungsfehler
		moveq	#4,D2				; 5
rqbef:	moveq	#0,D0 				; Nullbytes
		bsr 	schreiben 			; schreiben
		bmi 	rqtrerror 			; Timeout
		dbf 	D2,rqbef
		bsr 	lesen 				; Statusbyte lesen
		bmi 	rqtrerror 			; Übertragungsfehler

rqfertig:
		move.w	#$80,2(A8)			; DMA für Floppy freigeben
		sf 		$43e				; DMA freigeben (VBlanc zulassen)
		rts

rqtrerror;
		moveq	#-1,D0				; Übertragungsfehler aufgetreten
		bra 	rqfertig

*******************************************************
*                                                     *
* Sucht auf dem DMA-Bus nach dem Laserdrucker         *
* Aufruf muß aus dem Supervisormode erfolgen          *
*                                                     *
* C-Syntax: nr = ld.suche();                          *
*                                                     *
*******************************************************

su_dma_ben:
		moveq	#-3,D0
		rts

_ld_suche;
		illegal
		tst.b	$43E 				; DMA in Benutzung?
* 									(z. B. Hintergrundprozess)
		bne.s	su_dma_beu 			; ja,.,

		move.l	D3,-(A7) 			; Register retten

		st $43e 					; DMA für Eigenbedarf sperren

		moveq 	#7,D3				; Größte Wahrscheinlichkeit: 7 
		lea		DMA_STATUS,A0		; laden
gersu:	lea		ldtext,A1			; Meldung laden
		move.w	#$198,2(A0) 		; DMA selektieren
		move.w	#$88,2(A0) 			; Controlerregister
		move.w	D3,D0 				; kopieren
		asl.w 	#5,D0 				; nach Bit 5-7
		orl.w	#$12,D0 			; Befehl Inquiry
		bsr		schreiben 			; Byte 0
		bmi		keindev 			; Kein Gerät uorhanden (Timeout)

		moveq	#3,D2 				; 4
subef:	moveq	#0,D0 				; Nullbytes
		bsr		schreiben 			; schreiben
		bmi		sutrerror			; Timeout?
		dbf		D2,subef
		moveq 	#-128,D0 			; Bit 7 gesetzt
		bsr 		schreiben 			; auf DMA schreiben
		bmi		sutrerror 			; Timeout?

		bsr		lesen 				; Statusbyte lesen
		bmi		sutrerror			; Übertragungsfehler
		tst.b	D0 					; Rückgabe testen
		bne		keindev 			; Übertragungsfehler

		bsr 	lesen 				; Gerätetyp lesen
		bmi 	sutrerror 			; Übertragungsfehler
		cmpi.b 	#2,D0 				; Printer?
		bne 	dmaend 				; Übertragung beenden

		moveq	#3,D2 				; 4 Bytes
sules:	bsr		lesen				; lesen
		bmi		sutrerror 			; Timeout
		dbf		D2,sules

		subq.w	#1,D0 				; 1 von der Länge abziehen
		moveq	#0,D1 				; löschen
		move.b	D0,01 				; kopieren
		cmp.w	#21,D1 				; Mind. 21 Zeichen
		bcs.s	dmaend 				; Übertragung beenden

		moveq	#20-1,D2			; 28 Zeichen vergleichen

kenloop:
		bsr		lesen 					; Byte einlesen
		bmi		sutrerror 				; Übertragungsfehler
		cmp.b	(A1)+,D0 				; vergleichen
		bne		dmaend 					; Übertragung beenden
		dbf		D2,kenloop

		move.w	D3,D0					; Controlernummer
		move.w	#$88,2(A0)				; Neuer Befehl
		asl.w	#5,D3					; Nach Bit 5-7
		ori.w	#$12,D3					; Befehl Inquiry
		moveq	#80,D2
test:	bsr		warte40
		dbf		D2,fest
		move.w	D3,(A0)					; Schaltet Controler vom DMA-Bus

* (jeder saubere Controler sollte dies unterstützen!)
		bra		sufertig

dmaend:
		move.w	#$88,2(A0)				; Neuer Befehl
		move.w	D3,D0					; kopieren
		asl.w	#5,D0					; Nach Bit 5-7
		orl.w	#$12,D0 				; Befehl Inquiry
		bsr		warte40
		move.w	D0,(A0)					; Schaltet Controler vom DMA-Bus
		bsr		warte40					; Verzögerung

* (jeder saubere Controler sollte dies unterstützen!) 
keindev:
		dbf		D3,gersu				; Schon alle Devicenummern?

		moveq	#-2,D0					; Nicht vorhanden
sufertig:
		move.l	(A7)+,D3				; Register zurückholen
		move.w	#$80,2(A0)				; Für Floppy freigeben
		sf		$43e					; VBlanc-Routine zulassen
		rts

ldtext: DC.B "PAGE PRINTER:SLMC804"
										; Oruckerkennung

sutrerror:
		moveq	#-1,D0					; Übertragungsfehler
		bra		sufertig

*******************************************************
*                                                     *
* Mode Sense Abfrage uom Laserdrucker                 *
* Aufruf muß aus dem Supervisormode erfolgen          *
*                                                     *
* C-Syntax: nr = ld_msense(nr, anz, def, buffer);     *
*                                                     *
*******************************************************

ms_dma_ben:
		moveq	#-3,D0
		rts

_ld_msense:
		illegal
		tst.b	$43E					; DMA in Benutzung?
*						(z. B. Hintergrundprozess)
		bne.s	ms_dma_ben	; ja...

		st		$43e		; DMA für Eigenbedarf sperren

		lea		DMA_STATUS,A0 ; laden
		move.w	#$198,2(AB)	; DMA selektieren
		move.w	#$88,2(A0)	; Controlerregister
		move.w	4(A7),D0	; Devicenummer holen
		asl.w	#5,D0		; Nach Bit 5-7
		ori.w	#$1a,D0		; Befehl Mode Sense
		bsr		schreiben	; Byte 0
		bmi.s	mstrerror	; Übertragungsfehler

		moveq	#2,D2		; 3
ssbef:	moveq	#0,D0		; Nullbytes
		bsr		schreiben	; schreiben
		bmi		mstrerror	; Timeout?
		dbf		D2,ssbef

		move.w	6(A7),D0	; Anzahl der Werte
		bsr		schreiben
		bmi		mstrerror
		move.w	8(A7),D0	; Aktuelle Werte?
		bsr		schreiben	; schreiben
		bmi		mstrerror

		move.l	10(A7),A2	; Pufferadresse laden
		bsr		lesen		; Statusbyte lesen
		bmi		mstrerror	; Übertragungsfehler
		move.w	D0,A1		; Rückgabe testen

		bsr		lesen		; Listenlänge lesen
		bmi		mstrerror	; Übertragungsfehler
		move.b	D0,(A2)+	; merken
		moveq	#0,D2		; löschen
		move.b	D0,D2		; kopieren
		subq.w	#1,D2		; anpassen

listl:	bsr		lesen		; aus der Liste
		bml		mstrerror	; Übertragungsfehler
		move.b	D0,(A2)+	; Daten aus der Liste auslesen
		dbf		D2,listl

		move.w	A1,D0		; Rückgabewert
msfertig:
		move.w	#$80,2(A0)	; für Floppy freigeben
		sf		$43e		; VBlanc-Routine zulassen
		rts
mstrerror:
		moveq	#-1,D0		; Übertragungsfehler
		bra		msfertig

*******************************************************
*                                                     *
* Mode Select Abfrage vom Laserdrucker                *
* Aufruf muß aus dem Supervisormode erfolgen          *
*                                                     *
* C-Syntax: nr = ld_mselect(nr. anz, abs, buffer);    *
*                                                     *
*******************************************************

_ld_mselect:
		illegal
		tst.b	$43E		; DMA in Benutzung?
* (z. B. Hintergrundprozess)
		bne		ms_dma_ben	; ja...

		st		$43e		; DMA für Eigenbedarf sperren

		lea		DMA_STATUS,A0	; laden
		move.w	#$198,2(A0)		; DMA selektieren
		move.w	#$88,2(A0)		; Controlerregister
		move.w	4(A7),D0		; Devicenummer holen
		asl.w	#5,D0			; Nach Bit 5-7
		ori.w	#$15,D0			; Befehl Mode Sense
		bsr		schreiben		; Byte 0
		bmi		mstrerror		; Übertragungsfehler

		moveq	#3,D2			; 4
slbef:	moveq	#0,D0			; Nullbytes
		bsr		schreiben		; schreiben
		bmi		mstrerror		; Timeout?
		dbf		D2,slbef

		move.w	8(A7),D0		; Defaultwerte?
		bsr		schreiben		; schreiben
		bmi		mstrerror		; Übertragungsfehler

		move.l	10(A7),A2		; Pufferadresse laden

		tst.w	8(A7)			; Defaultwerte?
		bne		mselfertig		; ja...
		move.w	6(A7),D2		; Anzahl holen
		addq.l	#1,A2			; Listenlänge übergehen

		move.w	D2,D0
		bsr		schreiben		; Listenlänge schreiben
		bmi		mstrerror		; Übertragungsfehler
		subq.w	#1,D2			; Korrektur
slll:	move.b	(A2)+,D0		; weitere Bytes holen
		bsr		schreiben		; Listenlänge lesen
		bmi		mstrerror		; Übertragungsfehler
		dbf		D2,slll			; bis zum Ende
mselfertig:
		bsr		lesen			; Statusbyte lesen
		bmi		mstrerror		; Übertragungsfehler

		move.w	#$80,2(A0)		; für Floppy freigeben
		sf		$43e			; VBlanc-Routine zulassen
		rts

*******************************************************
*                                                     *
* Low-Level Funktionen für das Schreiben und Lesen    *
* der Controlerregister eines Gerätes am DMA-Bus      *
*                                                     *
*******************************************************

schreiben:
		swap	D0				; Worte vertauschen
		move.w	#$8a,D0			; Takt (A1 auf high)
		move.l	D0,(A0)			; schreiben
		bsr		warte40			; etwas verschnaufen...

irqwarten:
		moveq	#80,D1			; 400 ms
		bra		readywait		; warten auf Beendigung

lesen:
		bsr		irqwarten		; warten
		bne		fehler			; Fehler aufgetreten
		move.w	#$8a,2(A0)		; Nächstes Byte bitte
		move.w	(A0),D0			; schreiben
		bsr		warte40			; warten

fertig:	moveq	#0,D1			; alles OK
fehler:	rts

warte40:
		moveq	#$2c,D1			; 45 mal warten
w401:	dbf		D1,w401			; immer mit der	Ruhe
		rts

readywait:
		add.l	$4ba,D1			; addieren
rwl:	btst.b	#5,MFP_GPID		; Schon IRQ?
		beq		fertig			; ja
		cmp.l	$4ba,D1			; Schon Timeout?
		bge.s	rwl				; nein
		moveq	#-1,D1			; Fehler
		rts

				.end


Aus: ST-Computer 06 / 1988, Seite 56

Links

Copyright-Bestimmungen: siehe Über diese Seite