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)<br/> Bit 1-3 Kennummer des zu benutzenden Papiereinzugs (-)<br/> Bit 4 Automatische Selektierung des Papiereinzugs: (-) Falls der aktuelle Papiereinzug leer, wird einer mit gleichem Papierformat gewählt.<br/> Bit 5 Papier einziehen für sofortigen Druckstart (-)<br/> Bit 6 Drucker ist ein write-black device (nein)<br/> 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


Links

Copyright-Bestimmungen: siehe Über diese Seite
Classic Computer Magazines
[ Join Now | Ring Hub | Random | << Prev | Next >> ]