Fast jeder Programmierer wird früher oder später sein Prunkstück von Programm mit Druckertreibern ausstatten. Wenn dabei Ataris Laserdrucker »SLM 804« oder SLM 605 angesteuert werden sollen, gibt es für die Grafikausgabe nur den »legalen« Weg über GDOS oder über einen der mitgelieferten Druckeremulatoren. Wer sich nicht auf die Erzeugnisse der Atari-Software-Klempnerei verlassen will oder kann, dem bleibt nur der direkte Sprung ins heiße Herz der feinen Laserstrahler Unser kommentiertes Assemblerlisting hilft Ihnen und dem Atari-Laser auf die Sprünge und zeigt außerdem, wie Sie die größere Auflösung 600 x 300 dpi des Marvin-Laserkit nutzen können.
Wie man mit wenig Hirn und großer Masse erfolgreich durchs Leben kommt, haben die urzeitlichen Dinosaurier bewiesen. Zumindest über eine lange Zeit, denn irgendwann sind sie ja bekanntlich aus noch nicht völlig geklärten Gründen in der Versenkung der Erdgeschichte verschwunden.
Wie man ohne Hirn und mit viel Power einen erfolgreichen Laserdrucker auf dem Markt etabliert, hat die vereinigte »Brainpower« von Ataris Entwicklungs- und Marketingabteilung bewiesen. Das Konzept des hirnlosen Laserpixlers mit schlummernder Dinosaurierkraft in puncto Druckgeschwindigkeit und Pixelauflösung hat sich im Bereich der Atari-Computer durchgesetzt.
Die beiden Laserdrucker-Dinosaurier SLM804 und SLM605 aus dem Hause Atari arbeiten ohne eigenen Mikroprozessor und verlassen sich bekanntlich voll und ganz auf die Macht des sie steuernden Computerhirns ST und TT. Zur vollendeten Umsetzung der Idee ist allerdings einiges an »Gehirnschmalz« notwendig, das von den Programmieren in die Verbindung einzubringen ist.
Zum Lieferumfang der Atari-Laserdrucker gehören zwei Druckeremulatoren (bezeichnenderweise trägt ein Emulator den Namen »Laserbrain«) und der GDOS-Treiber. Diese »Zwischenhirne« schaffen eine Verständigungsbrücke zwischen Programm und Laserdrucker, indem sie per Software die Steuersignale bestimmter Druckerstandards an die Bedürfnisse des unintelligenten Laserdruckwerks anpassen. Wer sich als Programmierer mit den Leistungen dieser Emulatoren nicht zufriedengeben will, muß sein Programm mit einem eigenen Assemblerhirn für den Laserdrucker ausstatten.
Leider existiert nur wenig Literatur über die Laserdruckeransteuerung, und häufig hagelt es auch Bomben, wenn man beispielsweise die GDOS-Version wechselt. Möchte man zu allem Überfluß die auf 600 x 300 dpi erhöhte Auflösung des 600-dpi-Kit der »Marvin AG« nutzen (Bezug über »Richter Distributor« in Gevelsberg), lohnt es, sich mit der direkten Programmierung des SLM804 auseinanderzusetzen.
Der Atari-Laserdrucker ist über den DMA-Bus mit dem »ST« oder »TT« verbunden. Mit diesem Bus betreibt man bekanntlich bis zu acht Geräte gleichzeitig. Jeder »Teilnehmer« wird über seine »Telefon«-Nummer angewählt.
Der Atari-Laserdrucker wird meist mit der Gerätenummer 5 oder 7 ausgeliefert. Hat man dann endlich herausgefunden, ob und unter welcher Nummer der Laser zu erreichen ist, beginnt man einen informativen Dialog, fragt nach seinem Befinden (Blattgröße ... ) und teilt ihm unsere sehnlichsten Wünsche mit (300 oder 600 dpi, ...). Worauf der Laser es kaum noch erwarten kann, unsere Bitmap in sich »hineinzuschlürfen«.
Bis dahin ist es jedoch ein weiter Assembler-Weg:
Die Adressen für die Hardware-Register sind hoffentlich selbsterklärend:
DMADATA EQU $FFFF8604
DMAMODE EQU $FFFF8606
DMAHIGH EQU $FFFF8609
DMAMID EQU $FFFF860B
DMALOW EQU $FFFF860D
SOUND EQU $FFFF8800
GPIP EQU SFFFFFAO1
ACIACTRL EQU $FFFFFC02
; Systemvariablen
Hz200 EQU $04BA
flock EQU S043E
; Befehle an den Laser-Controller
PRINT EQU $OA
INQUIRY EQU $12
M_SELECT EQU $15
M_SENSE EQU $1A
Die folgende Programmzeile dient lediglich Testzwecken. Sie verzweigt in ein kleines Assembler-Programm am Ende des Listings, das unser Hausprogrammierer Michael Bernards für uns entworfen hat. Das Assembler-Programm erzeugt eine Bitmap (senkrechte Streifen) und gibt sie wahlweise mit 300 dpi oder 600 x 300 dpi auf dem SLM804 aus:
jmp slm_test
Zunächst jedoch weiter in den Ansteuerungsroutinen. Zu Beginn müssen wir einige grundlegende Routinen bereitstellen, die auch für Festplattenstationen verwendet werden können. Eine genaue Dokumentation kann man z.B. im »Atari ST Profibuch« oder dem »Scheibenkleister« nachlesen.
Da wäre zunächst einmal eine Routine, die ein Byte über den ACSI-Bus jagt. In D0 stehen sowohl das Byte selbst (Highword) als auch der Status für das Mode-Select-Register (Lowword). Nach dem Abschicken wird auf die Quittung vom Device gewartet. Hat in unserem Fall der Laser das Byte erhalten, liefert er den Wert 0 zurück, andernfalls -1:
wcbyte:move.l D0, DMADATA.w ; Schreibe in CAC und DMA-Mode
move.w #$64, D1
wwait: dbra D1, wwait ; Moment warten
move.l #$0A,D1 ; Zähler für Timeout setzen
add.l HZ200.w, D1 ; Timeout nach 10 * 5 = 50 msec
moveq #0, D0 ; default return = TRUE
ww_1: btst #5, GPIP.w ; INT aufgetreten?
beq.b wend ; Ja -> alles in Ordnung
cmp.l HZ200.w, D1 ; Timeout?
bpl.b ww_1
moveq #-1, D0 ; return Timeout
wend: rts
rsbyte ist das Gegenstück zu wcbyte! Diese Routine wartet auf ein Byte vom Laser. Bei Timeout wird -1 zurückgeliefert. Ein positiver Rückgabewert repräsentiert das gelesene Byte:
rsbyte:
move.w #$64, D0 ; Moment warten
rbyte:
dbra D0, rbyte
move.l #$0A, D0 ; Timeout
add.l HZ200.w, D0
rwbyte:
btst #5, GPIP.w ; Interrupt?
beq.b lrbyte
cmp.l HZ200.w, D0 ; Timeout?
bpl.b rwbyte ; weiter warten
moveq #-1, D0 ; return FALSE
rts
lrbyte:
moveq #0, D0 ; Byte einlesen
move.w DMADATA.w, D0
andi #$FF, D0 ; und Byte maskieren
rts
Gehen wir das erste Problem an: Woher weiß ich, welche Geräteadresse das winzige »Mäuseklavier« in dem grauen Kistchen zwischen Rechner und Laser für unseren Drucker festlegt? Zu diesem Zweck können wir natürlich das Interface aufschrauben, die Schalterstellung ablesen (sofern unser eigenes digitallogisches Verständnis zur krausen Atari-Logik dieser DIP-Schalter kompatibel ist) und die Steuerroutinen auf eine feste ACSII-Adresse abstimmen.
Fähige Programmierer lösen die Aufgabe jedoch intelligenter. Dazu muß man wissen, daß alle ACSII-Geräte zur Auskunft über ihre Geräteadresse verpflichtet sind. Das Zauberwort, das solche Geräte verstehen müssen, heißt INQUIRE. Nach diesem Befehl muß das Gerät mindestens 5 Byte zurückliefern, in denen der Typ (Platte, Drucker usw.) verschlüsselt ist. Anschließend kann es seinen tatsächlichen Namen als ASCII-Text kundtun. Wir schicken nun sinvollerweise von Device 7 abwärts allen Geräten ein Inquire und fragen ihre Druckfähigkeit ab:
find_dev:
move SR, D0 ; Altes Statusregister merken
ori #$0700, SR ; Keine Störung bitte -> IRQ aus!
move.b #$0E, SOUND.w ; Floppy selekt
move.b SOUND.w, D1
move D0, SR ; Interupt wieder ein
andi.w #6, D1
cmp.w #6, D1 ; Laufwerk A noch selektiert?
bne.b find_dev ; wenn ja, warten
move.l #$14, D1
add.l Hz200.w, D1
find1:
cmp.l Hz200.w, D1 ; Warten
bpl.b find1
move.w #$2710, D0 ; Warten
find2:
dbra D0, find2
moveq #8, D7 ; D7 = aktuelles Device
nextdev:
subq.w #1, D7 ; Wir fangen mit Device 7 an
bmi.b fail ; Bei 0 hören wir auf
move.l D7, D0 ; In D0 basteln wir das Kommando
lsl.b #5, D0 ; Device in Bit 5..7
ori.b #INQUIRY, D0
swap D0
move.l #$8A, D2 ; DMA-Mode
moveq #4, D3 ; 4+1 = 5 Byte senden
move.w #$88, DMAMODE.w
devnextb:
move.w D2, D0
bsr wcbyte ; Byte schreiben
dbmi D3, devnextb
bmi.b nextdev ; Bei Timeout, mit nächstem Device
move.l #$80008A, DMADATA.w ; Das letzte Byte muß $80 sein
lea id_list, A0
moveq #5, D2 ; 1 Status + 5 ID-Byte lesen
idnextb:
bsr rsbyte
bmi.b nextdev ; Bei Fehler natürlich abbrechen
move.b D0,(A0)+ ; Byte in Liste schreiben
dbra D2, idnextb ; nächstes Byte
move.w D0, D2 ; Letztes Byte gibt Länge des
subq.w #1, D2 ; ID-Strings an:
strnextb:
bsr rsbyte ; Lesen wir auch dieses ein
bmi.b nextdev
move.b D0, (A0)+
dbra D2, strnextb
clr.b (A0) ; String terminieren
cmpi.b #2, slm_id ; 1.Byte in Liste ist Status!!!
bne.b nextdev ; Kein Drucker, Pech gehabt
move.w D7, device ; Sonst ist er unser Laser, toll!
moveq #0, D0 ; Eine positive Antwort
rts
fail:
moveq #-1, D0 ; ... sonst eine traurige Nachricht
rts
DATA
EVEN
device:
dc.w 7
dc.b 0
idlist:
dc.b 0
slm-id:
dc.l 0,0,0,0,0.0,0.0,0,0,0,0,0,0,0,0,0
TEXT
Nachdem wir nun wissen, daß ein Laserdrucker vorhanden ist, fragen wir nach der Größe der Bitmap. Der Laserdrucker kann verschiedene Papierformate ausdrucken:
Brief 216 x 279 mm oder 2400x3180 Pixel
Standard 216 x 356 mm oder 2400x4080 Pixel
DIN A4 210 x 297 mm oder 2336x3386 Pixel
DIN B5 182 x 257 mm oder 2016x2914 Pixel
Die Einzelpapierzufuhr erfolgt immer im Briefformat.
Da man das Papierformat durch verschiedene Papierkassetten einfach wechseln kann, muß man unbedingt vor dem Drucken die aktuelle Einstellung auslesen und seine Bitmap entsprechend anpassen. Dazu liest man eine Parameterliste mittels REQUEST SENSE vom Laser ein, die folgendermaßen aufgebaut ist:
Byte 0: Länge der Liste
Byte l+2: Blockhöhe
Byte 3+4: Blockbreite
Byte 5+6: Oberer Rand
Byte 7+8: Linker Rand
Byte 9: Bit 0: Einzelblatt
Byte 10+11: Vertikale Auflösung
Byte 12+13: Horizontale Auflösung
Byte 14: Timeout
Byte 15+16: Scantime
Byte 17+18: Anzahl gedruckter Seiten
Byte 19+20: Eingangskapazität
Byte 21+22: Ausgangskapazität
Wir haben die Liste bis zum bitteren Ende aufgeführt, um Sie ein wenig zu erschrecken. Wollen Sie nämlich später auf Einzelblatt (600 dpi beim Laserkit) schalten, müssen Sie dem Laser die komplette Liste schicken, was ganz schön lästig ist. Wir werden später jedoch trickreich mit REQUEST SENSE eine solche Liste anfordern, die entsprechende Stelle ändern und den Laser mit einer korrekten Parameterliste überraschen.
Man kann diesem Parameterblock entnehmen, wie breit (Byte 3 + 4) und wie hoch (Byte 1 + 2) die zu sendende Bitmap sein soll. Der Rest ist zu diesem Zeitpunkt noch uninteressant. Später kann man - schaltet man auf 600 dpi (Einzelblatt gesetzt) - noch den oberen Rand vergrößern, um (Achtung aufs Briefformat) die zu druckende Bitmap auf dem Blatt zu zentrieren.
Da Atari nicht an 600 dpi gedacht hat, liefert der Controller »fälschlicherweise« auch bei 600 dpi eine 300er Breite. Wir müssen also per Hand die Breite verdoppeln:
mode_sense:
move.w #$2710, D0 ; warten
mode_s1:
dbra D0, mode_s1
clr.l D0 ; Kommando basteln
move.w device, D0
lsl.b #5, D0 ; Device an Bit 5..7
ori.b #M_SENSE, D0 ; Befehl
swap D0
move.l #$8A, D2 ; DMA-Mode
moveq #5, D3 ; 6-Byte
move.w #$88, DMAMODE.w
mnextb:
move.w D2, D0
bsr wcbyte ; Byte schreiben
dbmi D3, mnextb
bmi return
lea prstat, A0 ; Liste bei prstat ablegen
moveq #1, D2 ; 1 Status + 1 Parm-Länge
mplen:
bsr rsbyte
bmi return
move.b D0, (A0)+ ; an prstat schreiben
dbra D2, mplen
move.w D0, D2 ; Länge der Liste in d0
subq.w #1, D2 ; eins abziehen wegen dbf
mparm:
bsr rsbyte ; Parameterliste einlesen
bmi return
move.b D0, (A0)+ ; und an prstat schreiben
dbra D2, mparm
moveq #0' D0 ; D0 löschen
move.b prstat, D0 ; prstat[0] gibt Status zurück
rts
DATA
EVEN
prstat:
dc.b 0 ; Status-Byte
parmIst:
dc.b 0 ; Länge der Parameterliste
pr_height:
dc.w 0 ; Bitmap Höhe
pr_width:
dc.w 0 ; Bitmap Breite
top_mar:
dc.w 0
left_mar:
dc.w 0
parm9:
dc.b 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
dc.b 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
dc.b 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
dc.b 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
TEXT
Ist die fertige Parameterliste erst einmal via mode_sense eingelesen, kann man daran Änderungen vornehmen (z. B. auf 600 dpi schalten = Einzelblatt-Bit setzen) und das Ganze mit mode-select dem Laser mitteilen.
Hier noch eine Warnung: Es wäre töricht, mit Mode-Sense eine Auflösung von z. B. DIN A4 zu lesen und dann zu versuchen, auf Einzelblatt DIN A4 auszudrucken. Der Atari-Laserdrucker SLM 804 druckt nun einmal Einzelblätter grundsätzlich (weil hardwarefixiert) im amerikanischen Briefformat! Wenn man sich nicht sicher ist, sollte man das Einzelblatt-Bit setzen und erneut mit mode_sense die Auflösung erfragen. Dann sorgt der Drucker für die richtige Ausgabegröße: Der Laser wird das Bildformat entsprechend klippen.
mode_select:
move.w #$2710, D0
mode_s2:
dbra D0, mode_s2 ; warten
clr.l d0 ; Kommando basteln
move.w device, d0
lsl.b #5, d0 ; Device Bit 5..7
ori.b #M_SELECT, d0
swap d0
move.l #$8A, d2 ; DMA-Mode
move.w D2, d0
move.q #4, d3 ; 5 Byte
mowe.w #$88, DMAMODE.w
mode_s3:
bsr wcbyte ; Byte schreiben
move.l d2, d0
dbmi d3, mode_s3
bmi.b return
tst.w def_reset ; Reset aller Parameter?
beq.b no_reset ; normale Funktion
move.l #$80008A, d0
bra.b pa_reset
no_reset:
move.l #$8A, d0
pa_reset:
bsr wcbyte ; letztes Byte des Kommandoblocks
bmi.b return
tst.w def_reset
bne.b nolist ; bei Reset keine Liste
lea parmlst, a0 ; Parameterliste von mode_sense
moveq #0, d3
move.b (a0), d3 ; 1. Byte gibt Länge an
mode_s4:
move.b (a0)+, d0 ; Byte aus Liste nehmen
swap d0
move.w d2, d0
bsr wcbyte ; ... und senden
bmi.b return
dbra d3, mode_s4
no_list:
bsr rsbyte ; Status zurücklesen
return:
rts
DATA
EVEN
def_reset:
dc.w 0 ; gibt an, ob Reset oder Set
TEXT
Die Ausgabe der Bitmap ist nach dieser Vorarbeit nur noch eine Kleinigkeit. Schnell auf DMA-Turbo super schalten und warten, bis alles vorbei ist. Der Laser holt sich nämlich die Daten selbst aus dem Speicher: So gefällt es uns!
Leider sieht die Sache insofern knifflig aus, als die Blocklänge nicht beliebig lang programmiert werden kann. Der ST »schaufelt« maximal 256 Blöcke zu 512 Byte, also 128 KByte am Stück. Man muß also zwischendurch (wenn keine Daten abgeholt werden) den Sektorzähler neu setzen. Damit sich der DMA-Chip nicht verzählt, wollen wir die Angelegenheit diskret zwischen den Zeilen erledigen. Den rechten Zeitpunkt finden Sie, indem das Programm auf das Lowbyte des DMA-BaseRegisters schaut. Ändert sich dieses einige Zeit nicht, haben Sie die Austastlücke erwischt. Die Adresse im Speicher steht übrigens im Register A0.
dma_out:
move.w #$01D0, DMAMODE.w ; DMA-FIFO löschen und
move.w #$D0, DMAMODE.w ; auf Schreiben stellen
move.w #$01D0, DMAMODE.w
move.l (a0), -(sp) ; Basisadresse setzen
move.b 3(sp), DMALOW.w
move.b 2(sp), DMAMID.w
move.b 1(sp), DMAHIGH.w
addq.w #4, sp
move.w #$2710, d0
out1:
dbra d0, out1 ; warten
move.w #$88, DMAMODE.w
move.w #$0188, DMAMODE.w
move.w #$88, DMAMODE.w
moveq #0' d0
move.w device, d0
lsl.b #5, D0
ori.b #PRINT, D0 ; Start Print (Na endlich!)
swap D0
move.l #$8A, D2 ; DMA-Mode
moveq #4, D3 ; 5 Byte
out2:
move.w D2, D0
bsr wcbyte ; Byte schicken
bmi.b return
dbra D3, out2
move.l #$0116, DMADATA.w ; DMA starten
move.w #$3F, DMADATA.w ; Sector Count auf 63
move SR, D7
ori #$0700, SR ; IRQ aus
move.w #$0116, DMAMODE.w
move.b DMALOW.w, D2 ; momentanen Zählerstand
merken
bra.b wr_loop
set_count:
move.w #$3F, DMADATA.w ; Sector Count auf 63
wr_loop:
move.b D2, D3 ; alten Zählerstand in d3 kopieren
move.b DMALOW.w, D2 ; neuen Stand in D2
cmp.b D3, D2 ; alten Stand mit neuem vergleichen
bne.b set_count ; gleich geblieben -> Austast-
btst #5, GPIP.w ; ende erreicht?
bne.b wr_loop ; sonst warten
move.w #$01CA, DMAMODE.w ; DMA-Transfer ausschalten
moveq #0, D0
move.w DMADATA.w, D1 ; Status abholen
move.b D1, D0
move D7, SR ; IRQ's wieder zulassen
addi.l #1200, HZ200.w
out3:
moveq #5, D1
add.l HZ200.w, D1
out4:
cmp.l HZ200.w, D1 ; warten
bpl.b out4
btst #5, GPIP.w ; Auf INT testen
beq.b out3 ; weiter warten
move.l #$14, D1
add.l HZ200.w, D1
out5:
cmp.l HZ200.w, D1 ; warten
bpl.b out5
btst #5, GPIP.w ; Auf INT testen
beq.b out3 ; weiter warten
neg.l D0
rts
Bei den vorangegangenen Routinen können einige Fehler zurückgemeldet werden. Die meisten kommen direkt vom Controller des Lasers:
Rückmeldung / Fehlermeldungen:
-------------------------
-1: TIMEOUT
0: Alles OK
1: DRUCKER IST NICHT EINGESCHALTET ODER NICHT
VORHANDEN!
2: ALLGEMEINER HARDWARE-FEHLER!
3: DER TONER IST LEER!
4: NOCH NICHT FERTIG AUFGEHEIZT!
5: DER PAPIERVORRAT IST LEER!
6: DIE WALZE MUSS AUSGETAUSCHT WERDEN!
7: PAPIERSTAU IN DER ZUFÜHRUNG!
8: PAPIERSTAU IM INNEREN!
9: PAPIERSTAU IN DER ABLAGE!
10: BITTE GEHÄUSE SCHLIESSEN!
11: FEHLER IN DER FIXIER-EINHEIT!
12: FEHLER IN DER BELICHTUNGSEINHEITI
13: MOTORFEHLFUNKTION!
14: VIDEO-FEHLFUNKTION1
16: SYSTEM TIMEOUT
18: FALSCHER OP-CODE
19: FALSCHES DEVICE
20: FALSCHE PARAMETERLIST
Zum guten Schluß schreiben wir die Rahmenroutinen. Angenommen, wir hätten einen Aufruf zur Abfrage der Auflösung in C:
int cdeel slm_init (int mode, int &breite, int &hoehe);
mode soll für 600 dpi oder Einzelblatt 1, ansonsten 0 sein. Für Breite und Höhe der Bitmap übergeben wir Zeiger auf unsere Variablen, damit die Routine die aktuellen Werte hineinschreiben kann. Als Rückgabewert werden die oben aufgeführten Fehlermeldungen geliefert:
slm_init:
link A6, #0
movem.l D1-D7/A1-A5, -(A7) ; Register sichern
clr.l -(A7)
move #$20, -(A7) ; GEMDOS: SUPER
trap #1
addq.l #6, A7
move.l D0, savesp ; Gut merken!
st flock.w ; VBL fernhalten
move.b #$13, ACIACTRL.w ; Tastatur aus
bsr.b init
movea.l 10(A6), A0 ; Pointer auf Breite
move.w pr_width, D1
tst.w 8(A6) ; 600 dpi?
beq.b dpi300
lsl.w #1, D1 ; Bei 600 dpi Breite mal 2
dp1300:
move.w D1, (A0)
movea.l 14(A6), A0 ; Pointer auf Höhe
move.w pr_height, (A0)
pr_exit:
sf flock.w ; ich bin fertig mit DMA
move.b #$11, ACIACTRL.w ; Tastatur ein
move.l savesp, -(A7)
move.l D0, savesp ; D0 vor GEMDOS sichern
move.w #$20, -(A7) ; GEMDOS: SUPER
trap #1
addq.l #6, A7
move.l savesp, D0 ; D0 ist Rückgabewert
movem.l (A7)-, D1-D7/A1-A5
unlk a6
rts
_init:
bsr find_dev ; Device des Lasers suchen
bmi return ; nicht gefunden ?
bsr mode_sense ; Parameterblock laden
cmp.w #-1, D0 ; Kein Parameterblock ?
beq return ; Tut uns leid -> Ende
st def_reset
move.b #1, parm9
bsr mode_select ; Parameterliste auf Default
sf def_reset
bsr mode_sense ; und neu einlesen
move.w 8(A6), D0 ; 300 oder 600 dpi?
move.b DO, parm9
bsr mode_select ; Auswählen
bsr mode_sense ; und Parameter lesen
rts
BSS
EVEN
savesp:
ds.l 1 ; von GEMDOS Super
TEXT
Drucken wollen wir wie folgt:
int cdecl slm_print (int mode,char *bild);
Mode sollte den gleichen Wert wie beim zugehörenden slm_init haben, »bild« ist ein Zeiger auf die zu druckende Bitmap. Als Rückgabewert erwarten wir die üblichen Fehlercodes:
slm_print:
link a6, #0
movem.l D1-D7/A1-A5, -(A7) ; Register sichern
clr.l -(A7)
move.w #$20, -(A7) ; GEMDOS: Super
trap #1
addq.l #6, A7
move.l D0, savesp
st flock.w ; VBL fernhalten
move.b #$13, ACIACTRL.w ; Tastatur aus
bsr.b _init ; INIT: Sicher ist sicher
bne pr_exit
movea.l 10(A6), A0 ; Pointer auf Bitmap
bsr dma_out ; Bild drucken
bmi pr_exit
bsr mode_sense ; Status holen. Stellt sicher,
bra pr_exit ; daß der Druckvorgang beendet ist
Damit Sie die Routinensammlung ohne weitere Klimmzüge testen können, hat Programmierer Bernards ein simples Testprogramm in Assembler entworfen, das eine Bitmap mit senkrechtem Streifenmuster erzeugt und über slm_init und slm_print auf dem SLM804 ausgibt. Die im Programm enthaltene Abfrage nach der Druckerauflösung hat natürlich nur dann einen Sinn, wenn Ihr Laserdrucker mit dem 600-dpiLaserkit aufgerüstet ist:
; nicht benötigten Speicher freigeben
slm_test:
move.l 4(a7), a5
move.l 12(a5), d0
add.l 20(a5), d0
add.l 28(a5), d0
add.l #256, d0
move.l d0, -(a7)
move.l a5, -(a7)
move.w #0, -(a7)
move.w #74, -(a7)
trap #1
lea 10(a7), a7
; Meldung ausgeben
pea msg
move.w #9, -(a7)
trap #1
addq.l #6, a7
; Eingabe
move.w #7, -(a7)
trap #1
addq.l #2, a7
; Eingabe auswerten
andi.w #1, d0
move.w d0, modus
; Drucker-Initialisierung
pea hoehe
pea breite
move.w modus, -(a7)
jsr slm_init
lea 10(a7), a7
; Druckseiten-Aufbereitung
move.w breite, d0
lsl.w #3, d0
mulu hoehe, d0
move.l d0, size
move.l d0, -(a7)
move.w #72, -(a7)
trap #1
addq.l #6, a7
move.l d0' buffer
bmi error
beq error
move.l d0, a0
move.l size, d0
lsr.l #2, d0
subq.l #1, d0
move.l #$c0000003, d1
loopx:
move.l d1, (a0)+
subq.l #1, d0
bpl.s loopx
; Seite drucken
move.l buffer, -(a7)
move.w modus, -(a7)
jsr slm_print
addq.l #6, a7
; Programm beenden
error: clr.w -(a7)
trap #1
DATA
buffer: dc.l 0
size: dc.l 0
hoehe: dc.w 0
breite: dc.w 0
modus: dc.w 0
msg: dc.b 'Möchten Sie 300 dpi (0) oder 600 dpi (1)?',0
even
END
Hat Ihr Assembler alles richtig assembliert, steht der »Punkteproduktion« per Laser ohne Emulatorumwege nichts mehr entgegen. Weisen Sie Ihren Programmen künftig den direkten Weg zum Laser!
Markus Breuer/W. Fastenrath/uw)