Von: Michael Seyfried @ STA (So, 23.09.90 19:49)

>Reg-bergabe wie TC: (Auszge aus TC Bunutzerhandbuch Seite 155ff)

[Zitat on]
[..]
Normalerweise werden die Parameter nach einer gemischten Register/Stack-
Konvention bergeben. Das bedeudet, da solange wie mglich die schnelle
Registerbergabe versucht wird und erst dann der Stack verwendet wird. Daten-
typen wie char, int oder long werden in den Datenregistern D0-D2 bergeben.
Adreparameter (Zeiger) kommen in die Register A0-A1. Fliekommavariablen,
wie float, double und long double werden immer ber den Stack bergeben.
Im Falle von Funktionen mit variabler Parameterzahl erfolgt die bergabe
in jedem Fall ber den Stack. Dasselbe trifft fr Funktionen zu, die
explizit mit den Schlsselworten pascal und cdecl deklariert wurden.

Das bergabeformat von C

[..]
Die Register D0-D2 und A0-A1 dienen als Parameterregister. Diese Register
mssen naturgem nicht gesichert werden. Sie werden zum Teil auch fr die
Rckgabe von Ergebnissen verwendet. Skalare Parameter mit einer 8-, 16- oder
32-Bit Darstellung (z.B: char, int, long, *(Zeiger)) werden in Registern ber-
geben, soweit ausreichend Parameterregister zur Verfgung stehen. Dabei erfolgt
die Zuteilung von Parametern auf Register getrennt nach Adre- und Daten-
parametern entsprechend folgender Konvention (1):

1. Der erste Adreparameter wird in A0 bergeben, der zweite in A1, alle
   anderen auf dem Stack.

2. Anschlieend wird der erste skalare Parameter in D0 bergeben, der zweite
   in D1, der dritte in D2, alle anderen auf dem Stack.

[..]

Diese Konvention gilt fr alle Standardfunktionen. Funktionen mit variabler
Parameterzahl arbeiten jedoch nur mit Stackbergabe. Das bedeutet, da fr
die bergabe keine Register verwendet werden (2). [..]
Bei der reinen Stackbergabe, die sich brigens mit der expliziten Verwen-
dung des Schlsselworts cdecl erzwingen lt, werden die Parameter in umge-
kehrter Reihenfolge auf den Stack gelegt. Wenn das Schlsselwort pascal ver-
wendet wird, werden die Parameter in der Reihenfolge der Argumente auf den
Stack gelegt. [..]

Wer ist wann fr den Stack verantwortlich?

Bei allen normal und mit cdecl deklarierten Funktionen mu die aufrufende
Funktion nach dem Aufruf die Parameter selbst vom Stack holen (z.B: mit
ADDQ.W #8, A7). Dies erbrigt sich natrlich, wenn alle Parameter in Registern
bergeben wurden.
Eine als pascal deklarierte Funktion rumt alle ihre Parameter vor der Rck-
kehr selbst vom Stack ab. Nur die Funktionsresultate bleiben auf dem Stack.

[..]
Funktionsresultate
[..]

Fr die Rckgabe von Funktionswerten gelten allgemein folgende Regeln:

- Die Datentypen char, int, enum, und long werden im Register D0 zu-
  rckgeliefert. Zeigerresultate werden ber A0 zurckgegeben.
- Strukturen und Verbnde (struct und union) werden per Adresse in A0
  zurckgegeben. Die Adresse der temponren Speicherstelle wird als letzter
  Parameter in den Adreregistern beziehungsweise am Stack abgelegt (3).

Erlaubete und unerlaubte Verwendung von Registern

Die Register D3-D7 und A2-A6 mssen von allen Routinen gerettet werden, falls
sie verwendet werden. Diese Register werden vom Compiler intern verwendet und
stehen aus diesem Grunde nicht zur freien Verfgung.
[Zitat off]

Anmerkungen:

ad 1:   In dieser Regel wird leider wenig ber die Reihenfolge der Parameter,
        die nicht in Register passen, auf dem Stack gesagt. Das wird folgen-
        dermaen gehandhabt:
        a) Alle Parameter, die in Register passen werden gem 1. und 2. in
           Registern bergeben.
        b) Alle brigen Parameter werden in der Reihenfolge von rechts nach
           links auf dem Stack bergeben. Siehe dazu Beispiel weiter unten.
ad 2:   Da gar keine Register verwendet werden stimmt nicht. Die festen
        Parameter (z.B der Formatstring bei 'printf') werden nach der
        Standardkonvention (d.h. in Registern/Stack) bergeben. Alle variablen
        Parameter (z.B bei 'printf' alle Parameter nach dem Formatstring)
        werden in Reihenfolge von rechts nach links auf dem Stack bergeben.
        Siehe dazu ebenfalls Beispiel weiter unten.
        Da es aber in Modula-2 keine Prozeduren mit variabler Parameteranzahl
        gibt, mssen solche Prozeduren gesondert behandelt werden. Ich knnte
        vorerst ohne direkte Untersttzung dieser speziellen Parameter-
        bergabeart leben.
ad 3:   Diese Regel gilt offensichtlich fr alle Datenstrukturen >32-Bit,
        also insbesondere auch fr float, double , und long double.


Beispielprogramm:

int printf(const char *format, ... ); /* Prototyp */

double p1(int i1,
          int *p1,
          int i2,
          int *p2,
          double d1,
          double *p3,
          int i3,
          int *p4,
          int i4,
          int *p5)
{
  *p1 = i1;
  *p2 = i2;
  *p3 = d1;
  *p4 = i3;
  *p5 = i4;
  return i1 + i2 + i3 + i4 + d1;
}

int i1, i2, i3, i4;
double d1, d;

void main( void)
{
   d = p1( 1, &i1, 2, &i2, 0.123456789, &d1, 3, &i3, 4, &i4);
   printf( "d: %10lf  i1: %10d\n", d, i1);
}

Was der Compiler daraus macht (siehe Kommentar ab 'main'):

DISPOBJ  Object file browser                Borland Intl. Scotts Valley
Copyright (C) 1990                          All Rights reserved
Version 1.00

* Object File "tcparam1"


          .TEXT

          .MODULE GLOBAL

p1:
T000000:   MOVE.W    D6,-(A7)
T000002:   LEA.L     -$000A(A7),A7
T000006:   MOVE.W    $0026(A7),D6
T00000A:   MOVE.W    D0,(A0)
T00000C:   MOVE.W    D1,(A1)
T00000E:   MOVEA.L   $001E(A7),A0
T000012:   LEA.L     $0014(A7),A1
T000016:   MOVE.L    (A1)+,(A0)+
T000018:   MOVE.L    (A1)+,(A0)+
T00001A:   MOVE.W    (A1)+,(A0)+
T00001C:   MOVEA.L   $0022(A7),A0
T000020:   MOVE.W    D2,(A0)
T000022:   MOVEA.L   $0028(A7),A1
T000026:   MOVE.W    D6,(A1)
T000028:   ADD.W     D1,D0
T00002A:   ADD.W     D2,D0
T00002C:   ADD.W     D6,D0
T00002E:   LEA.L     (A7),A0
T000030:   JSR       _wxcnv(PC)
T000034:   LEA.L     (A7),A0
T000036:   LEA.L     $0014(A7),A1
T00003A:   JSR       _xxadd(PC)
T00003E:   MOVEA.L   $0010(A7),A0
T000042:   LEA.L     (A7),A1
T000044:   MOVE.L    (A1)+,(A0)+
T000046:   MOVE.L    (A1)+,(A0)+
T000048:   MOVE.W    (A1)+,(A0)+
T00004A:   LEA.L     $000A(A7),A7
T00004E:   MOVE.W    (A7)+,D6
T000050:   RTS

          .MODULE GLOBAL

main:
T000000:   MOVE.L    A2,-(A7)       ; A2 wird gerettet
T000002:   LEA.L     -$000A(A7),A7  ; 10 Bytes auf Stack fr Returnwert
T000006:   LEA.L     i1,A2          ; Basisadresse der Var's nach A2
T00000C:   PEA.L     i4             ; Adresse von i4 auf Stack
T000012:   MOVEQ.L   #$04,D0
T000014:   MOVE.W    D0,-(A7)       ; Wert 4 auf Stack
T000016:   PEA.L     i3             ; Adresse von i3 auf Stack
T00001C:   PEA.L     d1             ; Adresse von d1 auf Stack
T000022:   LEA.L     +$0000001E,A0  ; 0.123456789 auf Stack
T000028:   MOVE.L    -(A0),-(A7)
T00002A:   MOVE.L    -(A0),-(A7)
T00002C:   MOVE.W    -(A0),-(A7)
T00002E:   MOVEQ.L   #$03,D2        ; Wert 3 in Parameterregister D2
T000030:   LEA.L     $0002(A2),A1   ; Adresse von i2 in Parameterregister A1
T000034:   MOVEQ.L   #$02,D1        ; Wert 2 in Parameterregister D1
T000036:   MOVEA.L   A2,A0          ; Adresse von i1 in Parameterregister A0
T000038:   MOVEQ.L   #$01,D0        ; Wert 1 in Parameterregister D0
T00003A:   PEA.L     $0018(A7)      ; Adresse von Returnwert auf den Stack
T00003E:   JSR       p1(PC)         ; Aufruf von 'p1'
T000042:   LEA.L     $001C(A7),A7   ; Stackkorrektur und Ergebnis nach d:
T000046:   LEA.L     (A7),A0
T000048:   LEA.L     $0012(A2),A1
T00004C:   MOVE.L    (A0)+,(A1)+
T00004E:   MOVE.L    (A0)+,(A1)+
T000050:   MOVE.W    (A0)+,(A1)+
T000052:   MOVE.W    (A2),-(A7)     ; i1 auf Stack
T000054:   LEA.L     $001C(A2),A0   ; d  auf Stack
T000058:   MOVE.L    -(A0),-(A7)
T00005A:   MOVE.L    -(A0),-(A7)
T00005C:   MOVE.W    -(A0),-(A7)
T00005E:   LEA.L     ,A0            ; Adresse von Formatstring nach A0
T000064:   JSR       printf(PC)     ; Aufruf von 'printf'
T000068:   LEA.L     $000C(A7),A7   ; Stackkorrektur und Prozedurende:
T00006C:   LEA.L     $000A(A7),A7
T000070:   MOVEA.L   (A7)+,A2
T000072:   RTS


          .DATA

          .MODULE LOCAL

D000000:   .DC.W   $643A ,$2025 ,$3130 ,$6C66 ,$2020 ,$6931 ,$3A20 ,$2531
D000010:   .DC.W   $3064 ,$0A00 ,$3FFB ,$FCD6 ,$E9B9 ,$CB1A ,$F989


          .BSS

          .MODULE LOCAL

i1::
B000000:   .DS.B   2
i2::
B000002:   .DS.B   2
i3::
B000004:   .DS.B   2
i4::
B000006:   .DS.B   2
d1::
B000008:   .DS.B   10
d::
B000012:   .DS.B   10


          .END

Stackframe beim Call von p1:

 Datenregister | Stack        | Adreregister
---------------|--------------|----------------
D0:  1         | ADR( i4)     |  A0:  ADR( i1)
D1:  2         | 4            |  A1:  ADR( i2)
D2:  3         | ADR( i3)     |
               | ADR( d1)     |
               | 0.123456789  |
               | ADR( return) |

Dabei ist ADR( return) die lokale Adresse, an der der Returnwert gespeicht
wird. Dieser ist ja vom Typ 'double' und wird daher nicht in D0/A0 bergeben.

Stackframe beim Call von 'printf'

 Datenregister | Stack        | Adreregister
---------------------------------------------------------
D0:  -         | i1           |  A0:  ADR( Formatstring)
D1:  -         | d            |  A1:  -
D2:  -         |              |

Der Formatstring ist ein fester Parameter und wird deshalb nach Standard-
konvention (hier: Register A0 fr ersten Adreparameter) bergeben.

Das ganze ist wohl nicht ganz einfach zu implementieren. Ich glaube aber,
da sich der Aufwand lohnt. Man htte dann echtes Multilanguage Programming
auf dem ST/TT.

END

