SUPEXEC: Mit Parametern und Rückgabewert

Warum kann man einer Supervisor-Modus-Routine keine Parameter mit auf den Weg geben? - Keine Ahnung, warum das so sein soll, aber es ist gar nicht so!

Programmierer und andere Interessierte, die ab und zu mal einen Zugriff auf die heiligen Systemvariablen brauchen, kennen die bombigen Ergebnisse, wenn sie mal rasch mit printf("%1x", *(long *) 0x4ba1); sehen wollen, welche 200 Hertz es geschlagen hat.

Manch einer behilft sich mit der Modus-Umschaltung durch GEMDOS 32 (Super) und hat sich über seltsame Ereignisse für den Parameter 0 gewundert. Klarer und zuverlässiger arbeitet die Routine XBIOS 38 (Supexec). Leider sieht die Dokumentation hierfür keine Parameter und keinen Rückgabewert vor. Aber ähnlich wie bei Socken: Wenn der richtige Knoten gelöst ist. ribbelt sich das ganze Ding von selbst auf.

Böse Falle

Sieht man sich den (X)BIOS-Trap-Handler durch die scharfe Brille eines Disassemblers (oderz. B. einfach in [1]. Seite 49) an. so sieht man. daß hier sehr sorgfältig alles Nötige erledigt wird:

Bedauerlicherweise gehen hier die Register A0,A1 und D0 baden, aber erfreulicherweise bleiben nach der Rückkehr aus den Wirren der (X)BIOS-Routine die Register A0 und D0 unangetastet. Das ist auch sinnvoll, denn die eine oder andere (X)BIOS-Routine gibt schließlich etwas zurück.

Wenn wir uns jetzt den ROM-Code von Supexec (im ROMTOS 1.4 an der Stelle $fc0950) zu Gemüte führen, stellen wir erstaunt fest, daß dieser genau sechs Bytes lang ist:

movea.l $04(A7),A0
jmp     (a0)

Bei $04(A7) hatten wir beim Aufruf von Superexec(funktion) gerade die Adresse der im Super-Modus auszuführenden Routine_funktion_ abgelegt.

Bei Eintauchen in die (eigene) Routine_funktion_ befindet sich der Prozessor also im Super-Modus, A7 zeigt auf die Rücksprungadresse (in den Trap-Handler) auf dem Parameter-Stack (USP oder SSP je nachdem, ob Superexec aus dem User- oder Supervisor-Modus aufgerufen wurde), bei $04(A7) liegt die Adresse von Funktion , und bei $08(A7) läge der letzte vor der Adresse von funktion abgelegte Parameter.

Erfreulich wäre es, wenn Atari beim ROM-Code von Supexec nicht so sparsam mit dem Speicher gewesen wäre und statt der o. a. 6 Bytes so etwas programmiert hätte wie im Listing SUPEXEC.ROM. Dann könnte die Supervisor-Modus-Routine wie jede x-beliebige Standard-C-Routine mit Parametern auf dem Stack programmiert werden. (Aber bitte wirklich auf dem Stack und nicht in Registern, wie bei Turbo-C. wenn das Zauberwort cdecl vergessen wurde!)

Für Trapper

Solange Atari dieses Feature nicht einbaut, scheint dieser Weg aber nicht glücklich, weil zu aufwendig! Man müßte sich einen eigenen Trap-Handler basteln, für den man die (selbstverständlich nicht dokumentierten) Routinen-Tabellen des Betriebssystems verwenden muß. Oder man filtert alle Aufrufe von Supexec vor dem Einsprung in den originalen Handler heraus, mit den bekannten Konsequenzen: Verlangsamung des Systems, und wenn das jeder machte, und wo kämen wir denn da hin?

Aber auch ohne ROM-Patches gehts: Assembler-Programmierer könnten Parameter in den o.a. geretteten Registern übergeben und den Rückgabewert in D0 (oder A0 für Adressen) ablegen.

Übersichtlicher legen Sie die Parameter einfach auf den Stack, schieben die Adresse der funktion nach, packen #38 als Opcode für Supexec drauf und jumpen in den XBIOS-Trap. In funktion liegen dann alle Parameter genau ein Langwort höher auf dem Stack, als bei direktem Unterprogrammaufruf von funktion. (Im Listing SUPERPAR.S kann man bei peek_word sehen, wie's gemeint ist.)

In Hochsprachen jedoch (insbesondere Turbo-C) haben die Götter vor den Erfolg einen gordischen Knoten gesetzt, nämlich das Binding von Supexec. Nur die Deklaration von Supexec in TOS.H zu ändern. hat keinen Sinn, weil die Bibliotheksfunktion von Turbo-C nur den Inhalt von AO auf den Stack packt - und natürlich die Nummer von Supexec (38).

Es scheint also sinnvoll, das einer eigenen Routine superpar zu übertragen. Allerdings müssen wir die Parameter einmal umspeichern, weil die Rückkehradresse aus superpar die übergebenen Parameter sonst noch einmal um ein Langwort verschiebt. Das erledigt die Routine superpar (siehe Listing SUPERPAR.S). die auf die Übergabe von zwei long-Parametern ausgelegt ist. Auf diese Weise muß man bei funktion nur berücksichtigen, daß die eigene Adresse zusätzlich übergeben wird.

Variable Parameteranzahl für superpar bedeutet hier wesentlich höheren Aufwand, z. B. durch Übergabe der Parameterzahl als letzten Parameter; von den Bibliotheksfunktionen va_... ganz zu schweigen. Aber mit zwei longs kann man auskommen, wenn man ggf. Struktur-Adressen übergibt.

Über den Rückgabewert einer eigenen C-Funktion brauchen wir uns keine Gedanken zu machen. der landet automatisch und C-konform im Register D0 (oder A0).

Als Beispiel habe ich im Listing SUPERPAM.C mal aufgeschrieben, wie man mit diesem Verfahren prüfen kann, ob in einem der Vektoren 5 bis 15 (rein willkürlich) eine XBRA-Kette hängt. Natürlich ist das Listing nicht wasserdicht, weil Zugriffe auf nichtexistierende Speicherstellen nicht abgefangen werden, aber immerhin...

Bleibt nur noch zu bemerken, daß w'ir ein nicht dokumentiertes Feature benutzen, für dessen zukünftiges Funktionieren keine Garantie übernommen werden kann. Allerdings sind die (X)BIOS-Trap-Handler zumindest im Uralt-TOS von 1985 und im TOS 1.4 gleich, wenn auch um ein paar Bytes verschoben, so daß gute Chancen bestehen, daß die o. a. Vorgehensweise unkritisch ist.

[1] Jankowski, Rabich, Reschke: ATARI ST Profibuch, SYBEX-Verlag GmbH, Düsseldorf, 2. Auflage 1989

/* SUPERPAM.C
   Routinen für Supervisormodus mit Parametern 
   aufrufen. Für Turbo-C!
   V 2 - 04.05.90, 09.10.90 
   Martin Godejohann 
   Dernburgstr. 51 
   1000 Berlin 19
   (c) MAXON Computer GmbH 1990 */

#include <stdio.h> /* mit printf..              */
#define DUMMY 0L

typedef struct { long xb_magic;
                 long xb_id; 
                 long xb_oldvec;
                 } XBRA;

/* Parameterübergabe über Stack erzwingen!
   'superpar.o' dazulinken! Prototyp: */ 
extern long cdecl superpar(
                    long cdecl (*funktion)(), 
                    long par1, long par2);


long cdecl peek(void *selfpointer, long par1, long dummy)
{   /* in selfpointer steht die nach dem Aufruf
    durch 'superpar' die Adresse von peek */

    return ( *(long *)par1);
}

long cdecl xbra_test(void *selfpointer,
                     XBRA *startadr, long dummy) 
{   /* eigentlich wird (XBRA *) zurückgegeben,
    das kollidiert aber mit der Deklaration von 
    (long superpar()) */

    if ( (startadr - 1)->xb_magic != 0x58425241L) 
        return 0L; /* falsches xb_magic! */
    else
        return (long)(startadr - 1);
            /* Auch dieses Casting nur wegen der 
            Deklaration von (long superpar()) */
}

void main()
{
    XBRA *ret_wert, *adresse; 
    long dummy = DUMMY; 
    int  zlr; 
    char kennung[5];

    kennung[4] = '\0';      /* String-Ende-Byte */

    for ( zlr = 5; zlr < 15; zlr++)
    {                      /* z. B. ab Vektor 5 */
        adresse = (XBRA *) superpar( peek, (long)(4 * zlr), DUMMY);
                /* Aus der Vektornummer die erste 
                   Routinenadresse berechnen */ 
        printf("\n Vektor %4d :",zlr);

        do
        {
            ret_wert = (XBRA *)superpar( xbra_test, (long) adresse, DUMMY); 
            if( ret_wert == 0)
            {
                printf("\n%81x zeigt nicht auf XBRA", (long) adresse);
            }
            else
            {
                *(long *)kennung = ret_wert->xb_id; 
                /* long zu string konvertiert */

                printf("\nvor %81x liegt Kennung%s", (long)adresse, kennung);

                adresse = (XBRA *)adresse->xb_oldvec;
            }
        } while (ret_wert != 0);
    } /* Ende for-Schleife */ 
    printf("\n Bitte Return drücken"); 
    getchar();
}

; superpar.s - Aufruf einer Routine im Supervi-
; sor-Modus mit Parametern aus dem Usermodus 
; für MAS-68k in Verbindung mit Turbo-C 
; Martin Godejohann 
; Dernburgstr. 51 
; 1000 Berlin 19 
; V 2 - 04.05.90, 09.10.90

    .GLOBL superpar         ; für Export

superpar:
    ; long cdecl superpar(long (*funktion) (),
    ;                     long par1, long par2);
    ; Parameterübergabe über Stack erzwingen! 
    move.l  12(sp),-(sp)    ; par1 unter Return-
    move.l  12(sp),-(sp)    ; Adresse holen
    move.l  12(sp),-(sp)    ; funktion
    move.w  #38,-(sp)       ; Supexec
    trap    #14             ; Xbios
                            ; Rückgabewert in d0 
    lea     14(sp),sp       ; Stackorrektur
    rts

; DEMO-Programmsegment, für Supexec mit Parame-
; tern aus Assembler aufgerufen
; nicht lauffähig, nur zur Veranschaulichung!!!
;   move.l      #$440,-(sp)     Systemvar.seek_rate
;   pea         peek_word       diese Routine soll
;                               mit Super laufen
;   move.w      #38,-(sp)       Opcode für Supexec
;                               auf Stack
;   trap        #14             XBIOS
;   lea         10(sp),sp       Stackkorrektur
;    .                          Inhalt v. seek_rate
;    .                          ist jetzt in d0
;    .
;-------
; peek_word:            dies ist besagte Routine
;   move.l      8(sp),a0        Adresse $440 vom
;                               Stack holen
;   move.w      (a0),d0         und Inhalt nach d0
;   rts                         und zurück


Martin Godejohann
Aus: ST-Computer 12 / 1990, Seite 79

Links

Copyright-Bestimmungen: siehe Über diese Seite