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
Links

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