Ausgabeumlenkung via BIOS

Wem geht es nicht ab und an so: Man hat ein schönes Programm, das ebenso schöne Grafiken im Vektorformat erzeugt und daher ewig langsam im Ausdruck ist?
Oder man programmiert für seine serielle Schnittstelle eine neue, extrem schnelle Übertragungsroutine, die aber nicht ganz richtig funktioniert. Leider braucht man zum Testen einen zweiten Rechner, und der Freund ist ganz schön sauer ob der für ihn nicht nutzbaren Zeit.

Wie schön wäre es, könnte man den Drucker oder die serielle Schnittstelle durch die Tastatur oder eine Datei oder ... ersetzen. Der erfahrene ST-COMPUTER-Leser wird sagen : Dafür gibts doch die GEM-DOS-Funktion $4b (Fforce). Stimmt ja auch, aber...

Fforce

An dieser Funktion störten mich schon lange drei Mängel:

  1. Es können nur GEMDOS-Funktionen umgelenkt werden.
  2. Es wird nicht nach Ein-und Ausgabefunktionen unterschieden (außer CON:).
  3. Es können auch nicht alle verfügbaren Geräte umgelenkt werden (z.B. MIDI).

Was lag näher, als sich den Assembler zu nehmen und sich der Sache anzunehmen? Aber zunächst einmal zu den bekannten Sachen, sprich Fforce.

Diese GEMDOS-Funktion bewirkt wie bekannt das Umlenken von Geräten in andere Geräte oder Dateien. Ein kurzes Beispiel in C:

#define CON_IN  0 
#define CON_OUT 1 
#define AUX	    2
#define PRN	    3
/* womit wir auch schon alle behandelbaren Geräte erwähnt hätten */

main()
{
    int han;
    han=Fcreate("test.dat",0); 
    Fforce(PRN,han);
    Cprnout('a');
    Fclose(han); 
    return 0;
}

Es passiert im Programm nichts weiter, als daß die Datei TEST.DAT geöffnet und der Drucker dorthin umgelenkt wird. Wenn nun ein Zeichen auf den Drucker ausgegeben wird, landet es nicht auf diesem, sondern in unserer Datei, und man kann in der Datei nach Beenden des Programmes den Buchstaben ‘a’ bewundern. So weit, so gut. Jetzt weiß ich also, wie ich in meinem eigenen Programm den Drucker „verbiege“. Das hilft mir aber beim Grafikprogramm nicht weiter. Schließlich habe ich das nicht selbst geschrieben und der Hersteller wird wohl auch kaum den Quelltext verschicken. Die Hilfe ist einfach: Diese „Geräteverbiegung“ kann weitervererbt werden. Ersetzt man also das

Cprnout('a')

im Beispiel durch z.B.

Pexec("graphik.prg", command, "",0)

so wird GRAPHIK.PRG gestartet und die Grafik, die sonst auf dem Drucker gelandet wäre, liegt dann in unserer Datei, wo sie ja gut aufgehoben ist. Nachdem unser Programm beendet wurde, ist wieder alles in bester Ordnung, das Betriebssystem sorgt selbst dafür, daß der Drucker druckt und nicht mehr „dateit“. Und wie drucke ich dann das ganze aus? Ganz einfach: vom Desktop aus die Datei zweimal anklicken und die Frage ansehen drucken Abbruch mit „drucken“ beantworten. Dann geht die Post ab, und der Drucker wirft die schönste Grafik aufs Papier. Damit hätten wir also die einfache Lösung kurz nochmal abgeschnitten. Aber wie gesagt, das funktioniert sofort nicht mehr, wenn unser Grafikprogramm die Grafik mittels BIOS ausgibt. Dann müssen wir nach wie vor eine halbe Stunde Rechenzeit in Kauf nehmen. Und für andere Anwendungen bestehen immer noch die oben genannten Mängel. Und hier setzt mein BIOS.PRG an.

Jetzt komme ich

Was eigentlich das Programm bewirken soll, ist ja nun klar. Und weil ich Fforce nicht für so schlecht halte, sollte die Wirkung auch ähnlich sein. Ich habe also extra für dieses kleine Programm eine neue BIOS-Funktion implementiert, die bei mir Bforce heißt und die Dateiumlenkung auf BIOS-Ebene bewirkt und im Gegensatz zu Fforce auch noch zwischen Ein- und Ausgabe unterscheidet. Folglich gibt es drei Parameter:

  1. der umzulenkende Kanal
  2. wohin der Kanal umzulenken ist
  3. ob Ein- oder Ausgabe gemeint ist

Um das Kapitel der Benutzung gleich ganz abzuhaken, nun noch das Schema des Aufrufs der Funktion in C und Assembler:

#define Bforce(a,b,c) 
bios(132,a,b,c)
Bforce(device,newdevice,in_out);

move.w in_out,-(sp) 
move.w newdev,-(sp) 
move.w device,-(sp) 
move.w #132,-(sp) 
trap   #13 
addq.l #8,sp

Die Parameter haben dabei folgende Bedeutung:

in_out: 0 - Eingabe umlenken 1 - Ausgabe umlenken
newdev: BIOS- bzw. Datei-Handle des Umlenkungszieles
device: BIOS-Handle des umzuleitenden Kanales. Ist newdev größer oder gleich sechs, handelt es sich um ein Datei-Handle, sonst um eines der folgenden BIOS-Handles.

0 Drucker
1 serielle Schnittstelle
2 Konsole
3 MIDI
4 IKBD
5 RAW-Konsole

Datei-Handles sind automatisch größer als sechs, darüber braucht man sich also keine Sorgen zu machen. Wen jetzt die technischen Feinheiten nicht interessieren (sog. Nutzer), der kann aufhören zu lesen und anfangen, das Listing abzutippen. Allerdings sollte er sich die Warnungen am Ende des Textes noch durchlesen.

Programmierung

Dem geehrten Leser stehen natürlich noch alle anderen Wege offen. Aber so funktioniert es in etwa: Mit einer Funktion namens install klinke ich mich in den BIOS-Vektor (XBRA) und fange erstmal alle BIOS-Aufrufe ab. Danach werden alle mich interessierenden Funktionen ausgefiltert, als da wären

Name Nummer
1. Bconstat 0
2. Bconin 2
3. Bconout 3
4. Bcostat 8
5. Bforce 132

Das sind alle BIOS-Funktionen, die mit Ein-und Ausgabe auf Geräte zu tun haben. 1. und 4. sind Erkundigungsfunktionen, die den Status von Geräten feststellen, 2. und 3. sind die Ein- und Ausgabefunktionen, und 5. schließlich ist hier gerade im Entstehen. Zuerst beschäftigen wir uns mit 5.

Wird diese Funktion auf gerufen, wird anhand von device in die Ein- oder Ausgabetabelle der Wert von newdev eingetragen. device wird dabei als Offset in der Tabelle verwendet.

Wird nun allerdings 1., 2., 3. oder 4. erkannt, liegt als Funktionsparameter auch immer die device-Nummer auf dem Stack. Diese wird vom neuen BIOS-Handler als Offset in die Ein- oder Ausgabetabelle verwendet und die dort stehende Nummer anstelle des alten Wertes auf den Stack gelegt. Kurz gesagt, wird die Funktionsnummer ausgetauscht. Danach wird der BIOS-Aufruf weitergeleitet an den ursprünglichen BIOS-Handler, der dann ganz unbekümmert mit dem untergemogelten Gerät weitermacht, als wäre nichts passiert. Das Programm, das den Aufruf tätigt, bekommt auch nichts von der kleinen Manipulation mit.

So wie bis hier dargestellt, funktioniert das ganze aber nur für Umleitungen innerhalb der sechs BIOS-Kanäle. In Dateien kann man damit aber noch nichts schreiben. Da aber Datei-Handies gut zu erkennen sind (>=6), wird in diesem Fall wie folgt vorgegangen:

Umleitung in Dateien

Für Ein- oder Ausgabefunktionen wird die in der Tabelle gefundene neue Gerätenummer als Datei-Handle erkannt und zur weiteren Bearbeitung das GEMDOS genutzt. Der zur Ausgabe vorgesehene Wert wird also in einen Ein-Byte-Puffer übertragen und mittels Fwrite in die ausgewählte Datei ausgegeben. Bei der Eingabe wird der einzulesende Wert mittels Fread in den Puffer eingelesen und danach in D0 übertragen, wo das BIOS alle Funktionswerte zurückgibt. Die Erkundigungsfunktionen sind in diesem Fall nur Dummy-Funktionen, da sie immer wahr zurückgeben. Es gibt also hier einen „Rückschritt“ vom BIOS zum GEMDOS, der allerdings nicht ganz problemlos funktioniert, da das GEMDOS auf das BIOS zurückgreift. Wenn nämlich ein BIOS-Aufruf des GEMDOS erfolgt, und dieses wiederum das GEMDOS konsultiert, werden die internen Speicher des GEMDOS gnadenlos überschrieben, und man kriegt das große Grübeln, wo plötzlich die ganzen Bomben herkommen. Aber man kann sich ja helfen. Die Lösung ist zwar nicht so ganz elegant, funktioniert aber. Es wird nämlich auch noch der GEMDOS-Vektor überwacht, und sobald eine Funktion (außer Pexec) angesprungen wird, ignoriert unsere BIOS Routine alle Umleitungen in Dateien, so daß das GEMDOS ungestört arbeiten kann. Dadurch ist zwar die Umleitung nicht mehr ganz so mächtig, aber dafür sicherer.

Das war’s im Prinzip auch schon. Als Zugabe ist noch ein kleines Programm dabei, das die BIOS-Routine resident im Speicher ablegt. Und zum Schluß nun noch die versprochenen Warnungen.

Achtung!

Als wichtigstes und allgemeinstes: am BIOS führt (fast) kein Weg vorbei. Eine Umlenkung von Kanälen wirkt sich also überall aus, selbst bei solchen Sachen wie der Fileselectbox! Also Vorsicht bei der Anwendung, insbesondere bei Umlenkung der Standardeingabe. Des weiteren ist zu beachten, daß nicht alle BIOS-Funktionen für alle Geräte implementiert sind. Die hier vorgestellte Routine kümmert sich um solche Feinheiten nicht, aber die Original-BIOS-Fehlermeldungen kommen zurück. Das gilt nicht bei der Umlenkung in Dateien. Man sollte also immer sehen, daß die Dateiarbeit reibungsfrei ablaufen kann. Des weiteren macht das Betriebssystem die Veränderung der Kanäle nicht selbständig wieder rückgängig, sondern der Nutzer muß das selbst machen [mit Bforce(device,device,in out)]. Sonst bleibt alles bestehen bis zum nächsten Reset. Die Funktionen install und exstall, sollte sie jemand in einem eigenen Programm nutzen wollen, müssen im Supervisormodus aufgerufen werden. Ich hoffe, ich habe nun alle Gefahren ertappt, die auf den Nutzer warten, wenn ich natürlich dafür auch keine Garantie übernehmen kann. Getestet wurden die Routinen übrigens auf einem STE mit 1 MByte.


        GLOBAL install,exstall

bios    EQU     $B4             ;adr des bios-
;                                vektors
gemdos  EQU     $84             ;adr des gemdos-
;                                vektors

anfang: bra     schluss
        DC.B    "XBRABFOR" 
oldbios:DC.L    1 
mybios:
        movem.l D0-D1/A0-A1,-(SP) 
        lea     22(SP),A0       ;funktionsnum-
;                                mer auf superstack
        move.w  -6(A0),D0       ;Statusregister
        btst    #13,D0          ;alter Status
;                                == superviser?
        bne.s   aktiv           ;nein->zu aktiv
        move    USP,A0          ;sonst usp
;                                laden
aktiv:
        move.w  (A0),D0         ;funktionsnum-
;                                mer laden
        cmpi.w  #132,D0         ;meine umlenk-
;                                funktion?
        beq     newnumb         ;ja->
;                                neuumlenkung
        cmpi.w  #1,D0           ;nein, Bconin?
        beq.s   input           ;ja ->
;                                inputfunktion
        cmp.w   #2,D0           ;Bconstat?
        beq.s   input           ;ja
        cmpi.w  #3,D0           ;Bconout?
        beq.s   outputs         ;ja
        cmpi.w  #8,D0           ;Bcostat?
        beq.s   outputs         ;ja
back:       ;eigene arbeit erledigt
        movem.l (SP)+,D0-D1/A0-A1 ;weiter im 
;                                  bios
        move.l  oldbios(PC),-(SP) 
        rts

input:
        move.w  2(A0),D0        ;device in dO
        add.w   D0,D0           ;2* wegen word
        lea     itab(PC),A1     ;inputtab in a1
        cmpi.w  #6,0(A1,D0.w)   ;wert in tab<6? 
        bcs.s   biosin

;                                ja->
;                                umlenkung bleibt
;                                im bios

; ab hier umlenkung in gemdos
        tst.b   isdos           ;aufruf aus
;                                gemdos?
        bne.s   back            ;ja, dann weiter
        cmpi.w  #1,(A0)         ;Bconstat?
        beq.s   info
;                                ja, dann wie
;                                Bcostat

dosin:  move.w  0(A1,D0.w),D0
;                                nein, gemdos
;                                lesen:
;                                device in d0
        pea     buf(PC)         ;alles klar-
;                                machen für
;                                Fread
        move.l  #1,-(SP)
        move.w  D0,-(SP)
        move.w  #$3F,-(SP)
        trap    #1
        lea     12(SP),SP
inend:  movem.l (SP)+,D0-D1/A0-A1
        moveq   #0,D0           ;wegen long-
;                                rückgabe
;                                löschen
        move.b  buf(PC),D0      ;ergebnis in d0
;                                bringen
        rte

;ende der gemdosarbeit

biosin: move.w  0(A1,D0.w),2(A0) ;funktions-
;                                 nummer aus
;                                 tabelle
;                                 übernehmen
        bra.s   back            ;weiter
;                                im bios


outputs:
        move.w  2(A0),D0        ;device in d0
        add.w   D0,D0           ;2* wegen word
        lea     otab(PC),A1     ;inputtab in a1
        cmpi.w  #6,0(A1,D0.w)   ;wert in tab<6?
        bcs.s   biosout         ;ja-umlenkung
;                                bleibt im bios
        tst.b   isdos           ;aufruf aus
;                                gemdos?
        bne.s   back            ;ja, dann weiter
        cmpi.w  #8,(A0)         ;Bcostat?
        bne.s   dosout          ;nein, dann
;                                raus damit
info:   movem.l (SP)+,D0-D1/A0-A1 ;register
;                                  zurück
        move.l  #-1,D0          ;wert
;                                zurückgeben
        rte

dosout: move.w  0(A1,D0.w),D0   ;gemdos:
;                                newdev in d0
        move.w  4(A0),D1        ;zeichen-
        move.b  D1,buf          ;argument in
;                                buffer bringen
        pea     buf(PC)         ;alles klar-
        move.l  #1,-(SP)        ;machen für
        move.w  D0,-(SP)        ;Fwrite
        move.w  #$40,-(SP)
        trap    #1
        lea     12(SP),SP
        bra.s   exit

biosout:move.w  0(A1,D0.w),2(A0) ;funktions-
;                                 nummer aus
;                                 tabelle
;                                 übernehmen
        bra     back            ;weiter im bios

newnumb:move.w  2(A0),D0        ;device
        add.w   D0,D0           ;2* wegen word
        move.w  4(A0),D1        ;neue device-
;                                nummer
        lea     itab(PC),A1     ;tabelle für
;                                input
        tst.w   6(A0)           ;input
;                                gewünscht?
        beq.s   umleit          ;ja->dann los
        lea     otab(PC),A1     ;sonst Output-
;                                tabelle
umleit:
        move.w  D1,0(A1,D0.w)   ;neue num-
;                                mer eintragen
exit:   movem.l (SP)+,D0-D1/A0-A1
        rte

install::
        move.l  A0,-(SP)
        movea.l bios.w,A0
        cmpi.l  #"BFOR",-8(A0)  ;schon
;                                installiert?
        beq.s   fast_ende
        move.l  A0,oldbios      ;alten bios
;                                retten
        move.l  #mybios,bios.w  ;neuen ein-
;                                setzen
fast_ende:          ;nochmal das ganze mit gemdos
        movea.l gemdos.w,A0
        cmpi.l  #"BFOR",-8(A0)
        beq.s   ende
        move.l  A0,dosvek
        move.l  #mydos,gemdos.w 
ende:   movea.l (SP)+,A0        ;und
        rts                     ;zurück



;hier kann man alles wieder rückgängig machen

exstall::cmpi.l #mybios,bios.w 
        bne.s   fastbye
        move.l  oldbios(PC),bios.w 
fast_bye:
        cmpi.l  #mydos,gemdos.w 
        bne.s   bye
        move.l  dosvek(PC),gemdos.w 
bye:    rts


;einklinken in GEMDOS-Vektor

        DC.B "XBRABFOR" 
dosvek: DC.L 1 
mydos:
        movem.l D0/A0,-(SP)
        move.w  14(SP),D0       ;funktionsnummer
        btst    #5,8(SP)        ;Statusregister
        bne.s   ok              ;supervisor
        move    USP,A0          ;sonst user
        move.w  (A0),D0         ;funktionsnummer
ok:
        cmp.w   #$4B,D0         ;pexec?
        beq.s   dosend          ;ja, dann ok
        move.l  10(SP),saveptr  ;sonst
        move.l  #dosret,10(SP)  ;einklinken 
        st      isdos           ;bin in gemdos
dosend: movem.l (SP)+,D0/A0     ;weiter im
        move.l  dosvek(PC),-(SP) ;gemdos 
        rts
dosret: clr.b   isdos           ;zurück aus
        move.l  saveptr(PC),-(SP) ;gemdos
        rts

saveptr:DC.L    1
itab:                           ;tabelle der 
        DC.W 0                  ;eingabe-
        DC.W 1                  ;leitung
        DC.W 2 
        DC.W 3 
        DC.W 4 
        DC.W 5

otab:   DC.W 0                  ;und der
        DC.W 1                  ;Ausgabe-
        DC.W 2                  ;umleitung
        DC.W 3 
        DC.W 4 
        DC.W 5

buf :
        DS.B 1                  ;puffer für
;                                Fread
isdos:  DS.B 1                  ;semaphore für
;                                gemdos-erkennung
        EVEN

;ein kleines installationsprogramm 
schluss:pea     install(PC)     ;Supexec
        move.w  #38,-(SP) 
        trap    #14
        addq.l  #6,SP
        clr.w   -(SP)           ;Ptermres
        move.l  #(256+schluss-anfang),-(SP)
        move.w  #$31,-(SP) 
        trap    #1

        END

Jan Starzynski
Aus: ST-Computer 07 / 1991, Seite 78

Links

Copyright-Bestimmungen: siehe Über diese Seite