% Doku-Section Registerbelegung bp 31.7.90

\section{Die Register}
\label{refRegs}
Der Belegung und Verwaltung der Prozessorregister kommt in dieser
Version (ab V4.0) von \mtwoamiga{} eine weit gr"ossere Bedeutung zu
als in fr"uheren. {\em Wichtig\/} sind die nachfolgenden Informationen
beispielsweise dann, wenn
\begin{itemize}
\item
  der eingebaute Assembler benutzt wird
\item
  Programmteile von anderen Programmiersprachen (Assembler, "`C"')
  aufgerufen werden
\item
  \mtwo-Prozeduren von Codeteilen in einer fremden Programmiersprache
   benutzt werden
\item
  einzelne Prozeduren dem Betriebssystem zur Verf"ugung gestellt
  werden, z.B. Inputhandler, Traphandler, Exceptionhandler,
  "`PutCH-Pro\-ze\-du\-ren"' f"ur {\tt ExecL.RawDoFmt},
  Kollisionsprozeduren, usw.
\item
  oder Tasks und Prozesse gestartet werden sollen
\end{itemize}

Es gibt sogenannte {\bf freie\/} Register, die vom Compiler f"ur vielerlei
Zwecke eingesetzt werden und nicht f"ur spezielle Funktionen reserviert
sind. Zu den freien Registern z"ahlen

\begin{quote}
D2\ldots D7, A0\ldots A3\ und FP0\ldots FP7.
\end{quote}

Alle anderen Register dienen in \mtwoamiga\ speziellen Zwecken, die
im Anschluss noch genauer erl"autert werden:
\begin{description}
\item[D0 und D1] dienen als R"uckgabe-Register der Funktionsprozeduren.
\item[A4] zeigt auf die globalen \near-Variablen
\item[A5] ist das LINK-Register, die Basis der lokalen Variablen
und Parameter von Prozeduren.
\item[A6] wird nur f"ur den Aufruf von Library-Prozeduren benutzt
\item[A7] ist wie immer der Stackpointer
\end{description}

\subsection{Registerkonventionen}
Der Compiler befolgt strengstens die Konventionen, die von Commodore
in \cite{RKMLib} aufgestellt wurden. Dies bedeutet, dass keine
Prozedur die Register D2\ldots D7 oder A2\ldots A6 ver"andern darf.
Dar"uberhinaus wird von diesem Compiler der Standard auch auf
die Floating-Point Register ausgedehnt, d.h.\ FP2\ldots FP7 werden
gerettet.

\mtwoamiga\ rettet nicht nur die benutzten Register, sondern verl"asst sich
auch darauf, dass sich {\em alle\/} aufgerufenen Prozeduren {\em ebenfalls\/}
an diese Konvention halten! Dies wird dann wichtig, wenn etwa
eine externe Assembler- oder interne {\tt ASSEMBLE}-Prozedur ohne
Ein- und Ausgangscode aufgerufen wird.

\begin{note}
   Beim Umstellen vorhandener Moduln auf die Version 4.0 des Compilers ist
   in diesem Punkt besondere Vorsicht geboten. Alte {\tt INLINE}-Prozeduren
   d"urfen nicht einfach eins zu eins umgesetzt, sondern m"ussen entsprechend
   angepasst werden. Bitte lesen Sie hierzu den Abschnitt "uber den
   Assembler (\ref{refAsm} ab Seite \pageref{refAsm}), insbesondere
   Abschnitt \ref{refINLASS}!
\end{note}

\subsection{Das Register A4}
\label{refA4}
A4 ist das "`Heiligtum"' von \mtwoamiga. Das Register zeigt auf die globalen
\near-Variablen des Programms, dazu z"ahlen auch alle Zeiger auf die
ge"offneten Libraries (siehe\ Abschnitt \ref{refNear},
Seite \pageref{refNear}).
Im Gegensatz zu den fr"uheren Versionen von M2Amiga wird A4 {\em nicht\/}
in jeder Prozedur neu geladen, sondern nur {\em ein einziges Mal\/}
innerhalb des gesamten Programms (es sei denn,
die Compiler-Option "`{\tt LoadA4:=TRUE}"' wird benutzt oder in Assembler
wird eine Referenz auf das Symbol "`\_LinkerDB"' erzeugt).

Sollte A4 zerst"ort sein, ist so gut wie keine Operation mehr
 m"oglich. Dies kommt beispielsweise vor, wenn die Prozedur vom
Betriebssystem aufgerufen wird (siehe oben,
InputHandler, Exceptionhandler, usw.).
In diesem Fall muss die richtige Addresse wieder nach A4 geladen werden.
Durch die Compiler-Option {\bf "`LoadA4:=TRUE"'} vor Beginn der Prozedur wird
erreicht, dass A4 auf den Stack gerettet (siehe oben,
Registerkonventionen) und mit
dem speziellen Linkersymbol "`\_LinkerDB"' geladen wird, welches eben
diesen Zeiger darstellt.

In einer {\em residenten\/} Version des Programms ist dies leider
{\em nicht\/} m"oglich, da der Variablen-Speicher f"ur jeden Prozess
neu alloziert wird. Gl"ucklicherweise bekommen die meisten der eben
angesprochenen, vom Betriebssystem aufgerufenen Prozeduren von Exec
einen Zeiger auf ihre Daten im Register  A0 oder A1 mit auf den Weg.
 Diese Daten sollte man als {\tt RECORD} definieren,
hierin w"ahrend der Initialisierungsphase das Register A4 ablegen und
dann sp"ater innerhalb der Prozedur wieder daraus laden. Dazu muss vor
dem {\tt BEGIN} der Prozedur die Option {\bf "`SaveA4:=TRUE"'} angegeben werden,
damit beim
Eintritt und Ausgang A4 auf den Stack gerettet bzw.\ wiederhergestellt
wird. Diese Option verh"alt sich also "ahnlich wie \optH{}, nur dass
hier A4 nicht automatisch geladen wird.

\begin{example}
Am Beispiel eines Inputhandlers wollen wir nun versuchen, dies alles
etwas verst"andlicher zu machen:

\begin{verbatim}
IMPORT R; (* Register-Konstanten! *)
...
TYPE
  MyDataPtr = POINTER TO MyData;
  MyData = RECORD
    qualifier: QualifierSet;
    code:CARDINAL;
    oldA4:ADDRESS;
  END;
VAR
  intr: ExecD.Interrupt;
  handlerData: MyData;
  ...
   handlerData.oldA4:=SYSTEM.REG(R.A4);
   intr.code:=ADR(Handler);
   intr.data:=ADR(handlerData);
  ...
PROCEDURE Handler(events{R.A0}:EventPtr;
                  data{R.A1}:MyDataPtr):EventPtr;
(*$ SaveA4:=TRUE *)
BEGIN
  ASSEMBLE(
    MOVE.L MyData.oldA4(A1),A4
  END);
  (* oder: ... SETREG(R.A4,data^.oldA4) *)
  ...
  RETURN ...;
END Handler;
 \end{verbatim}
Nun k"onnte man nat"urlich meinen, ein InputHandler ben"otige keinen Zugriff
auf die globalen Daten, aber wie gesagt: die Libraries werden {\em auch\/}
via A4 addressiert; und ein solcher Handler wird mit ziemlicher Sicherheit
die eine oder andere Exec-Funktion aufrufen!
\end{example}

\subsection{Das Register A5}
\label{refA5}
A5 ist das "`Lokal"'-Register. Es zeigt innerhalb einer Prozedur auf
die lokalen Variablen, die Parameter und unter Umstanden auf den "`static
link"' zu weiter aussen liegenden Prozeduren. Dieses Register ist
im Gegensatz zu A4 v"ollig problemlos, weil es in jeder Prozedur
neu geladen wird. Im Assembler muss man nur beachten, dieses Register
nicht zu ver"andern!

\subsection{Das Register A6}
\label{refA6}
Wie es \cite{RKMLib} vorschreibt, wird A6 als Basisregister f"ur
Library-Aufrufe verwendet; es dient keinem anderen Zweck.

\subsection{Die Register D0 und D1}
\label{refD0}
Diese beiden Register werden sehr h"aufig ben"otigt und sind deshalb
etwas problembehaftet.
Sie werden als Parameter-Register f"ur sehr viele Betriebssystem-Prozeduren
und auch als Resultat-Register von Funktionsprozeduren benutzt.

Ein (noch) ungel"ostes Problem tritt genau dann auf, wenn sich in D0 ein
Registerparameter befindet und eine Funktionsprozedur aufgerufen
werden soll. Da diese Prozedur ihr Resultat in D0 liefert, muss
der Parameter zwangsl"aufig zerst"ort werden, da er fest an D0 gebunden
ist und somit weder in ein anderes Register noch in einer Variablen
zwischengespeichert werden kann! Andere Register als D0 und D1 werden
in einem solchen Fall auf dem Stack zwischengespeichert und nach dem
Prozeduraufruf wieder geladen, aber in diesem Fall ist es nicht m"oglich,
da das Zur"uckladen von D0 das Funktionsresultat zerst"oren w"urde.
Der Compiler meldet bei einem derartigen "`Konflikt"' einen Fehler;
die gew"unschte Operation ist grund\-s"atz\-lich nicht durchf"uhrbar. In diesem
Fall muss der Parameter an ein anderes Register "`gebunden"' werden.
Das Gleiche gilt genauso f"ur D1, obwohl eine korrekte Verarbeitung
manchmal vielleicht doch m"oglich w"are (D0 und D1 sind compilerintern
fest verbunden).
Hier hilft also nur die Vermeidung von D0 und D1 als Registerparameter.

\label{refZ}
Nur f"ur die beiden Register D0 und D1 existiert die Compiler-Option \optZ{},
die nach dem Setzen auf {\tt FALSE}
(Kommandozeile Option "`{\tt-z}"') folgendes bewirkt:
soll eine Prozedur aufgerufen werden, die Parameter in D0 und/oder D1
erwartet, so werden die Werte nicht zwischenzeitlich auf den Stack,
sondern gleich in die entsprechenden Register
geschrieben. Dadurch wird Code gespart und das Programm wird schneller
laufen. Bestehen diese oder weitere Parameter jedoch aus
Ausdr"ucken, die zus"atzliche Funktionsaufrufe implizieren, m"ussen
deren Resultate ja in D0 und/oder D1 ankommen, w"urden also die
bereits errechneten Werte "uberschreiben! In diesem Fall meldet der Compiler
den Fehler "`{\tt Register D0 oder D1 belegt}"'.
Dieser Prozeduraufruf muss dann
also mit der Option "`StackParms:=TRUE"' compiliert werden, womit eine
tempor"are Ablage der Parameter auf dem Stack erreicht wird und eine
Zerst"orung unm"oglich ist.

\begin{example}
Ein Beispiel, bei dem der Compiler ohne die angegebene Option
einen Fehler melden wird:
\begin{verbatim}
  PROCEDURE Val():LONGINT;
  END Val;
  PROCEDURE Reg0(val1{R.D0},val2{R.D6}:LONGINT);
  END Reg0;
  ...
  (*$ StackParms:=TRUE *)
  Reg0(Val(),Val());
  (*$ POP StackParms *)
  ...
\end{verbatim}
\end{example}

\subsection{Register-Parameter}
\label{refRegPars}
Prozedur-Parameter k"onnen, statt wie "ublich auf dem Stack, auch direkt
in den Registern "ubergeben werden. Dazu wird im Kopf der
Prozedur hinter dem Namen des Parameters in geschweiften Klammern die
Nummer des zu benutzenden Registers angegeben. Bei Prozedur-Typen
wird das Register hinter dem Typenbezeichner angegeben.
\begin{example}
\begin{verbatim}
   TYPE writeProc = PROCEDURE(CHAR{0});
   PROCEDURE Min(a{7},b{6}: INTEGER):INTEGER;
\end{verbatim}
\end{example}

 Eine Mischung aus Register- und Stack-Parametern ist ohne
weiteres m"oglich, ebenso m"ussen die angegebenen Register nicht in
einer bestimmten Reihenfolge auftreten.

Jedes Register besitzt eine interne Nummer, die sich aus
Tabelle \ref{refRegNrs} ergibt. Der Lesbarkeit halber schreibt
man besser oktal, z.B. A3 als \verb|{13B}| oder FP6 als
\verb|{26B}|. Es existiert ein Modul "`R"', in dem die Konstanten
aller Register definiert sind. Man sollte dieses Modul verwenden
und dann sehr eindeutig und gut lesbar \verb|{R.A3}| oder
\verb|{R.FP6}| schreiben.

\begin{table}[hbt]
\begin{center}
\begin{tabular}{|c|rrrrrrrr|} \hline
Register & D0 & D1 & D2 & D3 & D4 & D5 & D6 & D7 \\
Nummer & 0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 \\ \hline
Register & A0 & A1 & A2 & A3 & A4 & A5 & A6 & A7 \\
Nummer & 8 & 9 & 10 & 11 & 12 & 13 & 14 & 15 \\ \hline
Register & FP0 & FP1 & FP2 & FP3 & FP4 & FP5 & FP6 & FP7 \\
Nummer & 16 & 17 & 18 & 19 & 20 & 21 & 22 & 23 \\ \hline
\end{tabular}\\
\end{center}
\caption{Interne Registernummern\label{refRegNrs}}
\end{table}

Ein Registerparameter wird wie eine {\em echte\/} Variable behandelt,
d.h.\ das zugeh"orige Register ist sicher vor Zerst"orung gesch"utzt
(mit Ausnahme von D0 und D1, siehe \ref{refD0}). Nur explizite Zuweisungen
k"onnen ihren Wert ver"andern.

\begin{itemize}
\item
In den Registern D0\ldots D7 k"onnen {\tt VAR}-Parameter beliebigen Typs
(mit Ausnahme offener Felder) oder
unstrukturierte Werteparameter, deren Gr"osse maximal ein Langwort ist,
"ubergeben werden. Eine Ausnahme stellt der Typ {\tt LONGREAL} dar. F"ur
LONGREALs wird sowohl das angegebene als auch das n"achst\-h"o\-he\-re
Register belegt, das angegebene Register sollte jedoch immer "`gerade"'
sein (D0, D2, D4 oder D6), damit der Compiler f"ur die Berechnung von
Ausdr"ucken gen"ugend freie Registerpaare zur Verf"ugung hat.
\item
In den Addressregistern A0\ldots A3 und A6
k"onnen {\tt VAR}-Parameter sowie
unstrukturierte Werteparameter der Gr"ossen Wort und Langwort "ubergeben
werden. Werte in Bytegr"osse sind hier also verboten, ebenso wie die
Register A4, A5 und A7!
\item
Parameter in den FPU-Registern FP0\ldots FP7 d"urfen nur vom Typ
{\tt LONGREAL} sein. Sie sind aber nicht in Definitions-Moduln anzugeben
und in anderen auch nur dann deklarierbar, wenn die Compileroption
f"ur die FPU 68881 oder CPU 68040 eingeschaltet ist.
\item
Records oder Arrays sind nur als VAR-Parameter erlaubt,
offene Felder sind g"anzlich verboten!
\end{itemize}

Wie man sieht, ist es also durchaus m"oglich, VAR-Parameter oder Zeiger
in Datenregistern oder Werte in Adressregistern zu "ubergeben. Hiervon
ist jedoch abzuraten, da der Compiler zus"atzlichen Code
generieren muss, der die Geschwindigkeit des Programmes verringert.
Um beispielsweise einen Zeiger in einem Datenregister zu
{\em dereferenzieren},
muss dieser vorher in ein Adressregister kopiert werden, andererseits
sind die meisten {\em Berechnungen\/} nur in Datenregistern m"oglich!

Jedes so belegte Register geht dem Compiler als "`freies"' Register
f"ur interne Operationen und lokale Prozeduren verloren!
Dies kann soweit gehen, dass
Berechnungen oder Adressierungen nicht mehr ausgef"uhrt werden
k"onnen, was dann bei der Compilierung zu einer Fehlermeldung f"uhrt.
Insbesondere bei Berechnungen von Ausdr"ucken des Typs {\tt LONGREAL}
ohne 68881-FPU werden sehr viele Datenregister
ben"otigt. Jede {\tt WITH}-Anweisung ben"otigt eines der sehr knappen
Adressregister.

Man sollte sich also davor h"uten, einer Prozedur allzuviele
Registerparameter zu geben! Falls eine Prozedur mit vielen
Registerparametern jedoch {\em komplett\/} in Assembler implementiert
wird, treten Probleme meist nicht so auf, da der Programmierer die
momentan gerade nicht ben"otigten Register beliebig abspeichern oder
auf dem Stack ablegen kann.

\begin{warning}
Ein anderes "`Problem"' stellen lokale Prozeduren dar. Sie haben
zwar korrekten Zugriff auf die Registerparameter der weiter aussen liegenden
Prozeduren, doch weil sich {\em jede\/} Prozedur an den Register-Standard
halten muss, bleiben Zuweisungen an {\em zu rettende\/} Register ausserhalb
dieser lokalen Prozedur wirkungslos, weil beim Verlassen
derselben die urspr"unglichen Werte wiederhergestellt werden m"ussen!
\end{warning}

\begin{example}
 Ein Beispiel zu den Register-Parametern:
\begin{verbatim}
PROCEDURE Demo(b{2}: BOOLEAN; li{3}: LONGINT;
               adr{10B}: ADDRESS;
               lr1{4}, lr2{20B}: LONGEAL)
\end{verbatim}
\parbox[t]{3.5cm}{\sloppy Die Register sind folgendermassen belegt:}
\hfill
\begin{tabular}{lll}
Variable & Register & Typ \\ \hline
b & D2 & {\tt BOOLEAN}\\
li & D3 & {\tt LONGINT} \\
lr1 & D4, D5 & {\tt LONGREAL} \\
adr & A0 & {\tt ADDRESS} \\
lr2 & FP0 & {\tt LONGREAL} \\
\end{tabular}
\end{example}

\subsection{Register-Variablen}
\label{refRegVars}
Register-Variablen d"urfen nur als {\em lokal\/} zu einer
Prozedur deklariert werden. Sie verhalten sich exakt so wie
die Register-Parameter, die ja im Grunde auch lokale Variablen sind,
unterliegen somit auch den gleichen Beschr"ankungen.

\begin{example}
Durch die Deklaration
"`\verb|VAR i{2}:INTEGER;|"' wird eine Re\-gis\-ter-Va\-ria\-ble im Register
D2 vom Typ {\tt INTEGER} angelegt.
\end{example}

Die in der Deklaration angegebenen Register werden blockiert
und d"ur\-fen nicht mit einem Parameter-Register kollidieren. Der Compiler
meldet "Uberschneidungen als Fehler.


\subsection{Register-Erhaltung} % doofes Wort/bp!
Der Compiler erzeugt Code, der sich m"oglichst viele Werte in
Registern merkt und entsprechend nutzt, um ef"|fizienten Code
(Geschwindigkeit und L"ange) zu generieren.
Hierzu z"ahlen Konstanten,
"`Level"', Library-Basen und auf Wunsch auch Zeiger.

"`Level"' bed"urfen einer weiteren Erkl"arung: Falls eine lokale Prozedur auf
Variablen oder Parameter einer weiter aussen liegenden Zugriff ben"otigt,
muss sie sich zun"achst am "`static link"' (siehe
 \ref{refStatLink}) "`entlanghangeln"'. Dazu wird Code
 erzeugt, der bei einem sp"ateren Zugriff auf denselben Level unterbleiben
 kann, wenn der angelegte Zeiger noch in einem Adressregister
 vorhanden ist.

\label{refVolatile}
Durch die Option "`{\tt Volatile:=FALSE}"'
(Kommandozeile: {\tt -h}) versucht der
Compiler Zeiger folgender Art in Adressregistern zu halten:
\begin{itemize}
\item Globale POINTER (nur \near!)
\item Lokale POINTER
\item POINTER-Parameter
\item Addressen von VAR-Parametern
\end{itemize}
BPOINTER (siehe \ref{refBPTR}) sind hiervon ebenso ausgenommen wie
 \far-Variablen!

Die Wirkung l"asst sich wohl am ehesten als "`vollautomatisches WITH"'
(ohne Schreiberleichterung) umschreiben. Dies spart sehr viel Code,
unter Umst"anden mehr als ein
{\em echtes\/} {\tt WITH}. Ein echtes WITH kann nat"urlich mehr
({\tt \verb|WITH x^.y^|} oder {\tt \verb|WITH x^[y]|}),
hat aber den Nachteil, dass ein Adressregister blockiert wird,
wohingegen bei diesem Verfahren der Register-Erhaltung bei
"`Registermangel"' einfach der Inhalt vergessen und bei Bedarf neu
geladen werden kann.

Der Compiler geht sehr vorsichtig vor, indem er nach
einem Prozeduraufruf immer von dem "`schlimmsten Fall"' ausgeht,
dass alle Objekte, auf die die Prozedur Zugriff hat,
zerst"ort sind (globale Prozeduren haben z.B. {\em keinen\/} Zugriff auf die
lokalen Variablen und Werte-Parameter). Ebenfalls als zerst"ort
gelten Zeiger selbstverst"andlich nach direkten Zuweisungen und
nach der Benutzung als VAR-Parameter eines Prozedur-Aufrufs.

Dennoch kann die Strategie leicht zu Fall gebracht werden,
 indem {\em irgendein\/}
Zeiger auf eine der oben genannten Variablen zeigt und deren Wert ver"andert.
Damit hat dann die Variable im Speicher einen anderen Wert als das
Register. Dies kann der Compiler nicht feststellen!
Aus diesem Grunde wird diese Optimierung auch nur auf Wunsch vorgenommen.

\begin{example}
Ein Beispiel, bei dem die Option "`Volatile:=FALSE"' zu fehlerhaftem
Programmverhalten f"uhrt:
\begin{verbatim}
VAR
 scr:IntuitionD.ScreenPtr;
 win:IntuitionD.WindowPtr;
 height:INTEGER;
 ...
 win^.screen:=scr;
 ...
 size:=scr^.height; (* scr wird in Register gemerkt *)
 CloseScreen(win^.screen);  (* win^.screen = scr *)
 win^.screen:=NIL;          (* aber Register unver"andert *)
 IF scr#NIL THEN            (* Alter Wert im Register wird *)
  CloseScreen(scr);         (* getestet => Fehler *)
 END;
\end{verbatim}
Die Zuweisung \verb|win^.screen:=NIL| ver"andert indirekt (f"ur den Compiler
unbemerkt) die Variable {\tt scr}!
Da die Variable {\tt scr} bereits benutzt wurde, ist sie
in einem Register und der neue Inhalt wird in der
{\tt IF}-Anweisung nicht aus dem
Speicher geladen, somit wird {\tt CloseScreen} nochmals ausgef"uhrt,
was zu einem --- schwierig zu findenden ---
Programmfehler f"uhrt.
\end{example}

Als Faustregel kann man sagen, dass Prozeduren und Moduln, die nicht
mit Zeigern oder Listen arbeiten, immer mit der Option "`Volatile:=FALSE"'
kompiliert werden k"onnen. Der Vorteil liegt dabei im Merken der
Adressen von VAR-Parametern.

Sobald Zeiger auf globale oder lokale Variablen zeigen, insbesondere
bei Listenverarbeitung sollte die Option immer auf TRUE gesetzt
sein! Wenn dennoch unbedingt Code eingespart werden soll, kann
die (unsch"one) Methode des Ausprobierens helfen. Im Compiler selbst
musste nur eine einzige Prozedur mit "`Volatile:=TRUE"' kompiliert werden.

