\section{Das Laufzeitsystem (Arts)}

Das Laufzeitsystem bildet die Basis f"ur jedes Programm, das mit \mtwoamiga{}
entwickelt wurde. Es steht dem Programm w"ahrend dessen Ausf"uhrung zur Seite
und "ubernimmt im Fehlerfall die Kontrolle. Das Laufzeitsystem ist in mehrere
Aufgabenbereiche unterteilbar. Diese Teilbereiche sind jedoch eng miteinander
verflochten und deshalb in einem Modul realisiert. Dieses Modul heisst
Arts (Amiga Runtime System, oder eben das Laufzeitsystem).

Die Aufgaben von Arts k"onnen in zwei Kategorien unterteilt werden. Die
erste umfasst alle Teilbereiche, die f"ur den Programmablauf
unerl"asslich sind. Dies sind der {\bf Ein- und Ausgangscode} sowie die
{\bf Compilerunterst"utzung}. Zur zweiten Kategorie geh"oren die Behandlung
der {\bf Laufzeitfehler} und die Unterst"utzung des Programmierers.


\subsection{Ein- und Ausgangscode}

Ein- und Ausgangscode bilden eine Klammer um das Anwenderprogramm und
verbinden es mit dem Betriebssystem. Diese beiden Teile bilden
die Prozedur {\tt Arts.Startup}.

Es gibt verschiedene Implementationen des Laufzeitsystems, die sich
lediglich durch unterschiedlichen Ein- und Ausgangscode unterscheiden.
Welches Laufzeitsystem in ein Programm eingebunden wird ist
durch Optionen des Linkers bestimmt.
\begin{ttscript}{MRArts:}
\item[Arts:] Nichtresidentf"ahige Vollversion
\item[RArts:] Residentf"ahige Vollversion
\item[MArts:] Nichtresidentf"ahige Minimalversion
\item[MRArts:] Residentf"ahige Minimalversion
\item[DArts:] Nichtresidentf"ahige Debugger-Version
\item[DRArts:] Residentf"ahige Debugger-Version%
  \footnote{Der Vollst"andigkeit halber sei das f"ur Libraries
  bestimmte Laufzeitsystem {\tt LArts} erw"ahnt, dessen Beschreibung
  sich im Abschnitt \ref{LArts} befindet. Die Einbindung
  von {\tt LArts} wird aber {\em nicht\/} durch ein Option des Linkers,
  sondern durch einen eigenen Linker ({\tt M2LibLink}), bestimmt.}
\end{ttscript}

Residentf"ahige Versionen handhaben die Initialisierung des Speichers
f"ur die globalen Variablen anders als nichtresidentf"ahige Versionen.
Vollversionen fangen Laufzeitfehler mit Hilfe von Requestern ab,
Minimalversionen erzeugen Requester nur bei {\em bestimmten\/} Laufzeitfehlern
oder auf Wunsch des Programmierers und f"uhren ansonsten zu "'Guru"'
Meldungen. Requester betreffen nur den aktuellen Prozess,
"'Gurus"' haben im allgemeinen den Neustart des gesamten Systems
zur Folge.

\subsubsection{Eingangscode}

Der Eingangscode vervollst"andigt die Laufzeitumgebung und initialisiert
die Programmumgebung. In dieser Phase werden die folgenden Schritte
ausgef"uhrt:

\begin{enumerate}
\item
  Initialisierung der globalen \near-Variablen. F"ur ein residentes
  Programm wird der Speicher vom Betriebssystem angefordert, f"ur die
  nichtresidenten Programme ist dieser Speicher
  bereits im Programmcode enthalten
  und wird mit Null initialisiert.
\item
  Initialisierung der Prozess-Struktur f"ur die Stapel"uberpr"ufung.
  Residente Programme vergleichen die Gr"osse des zur Verf"ugung stehenden
  Stapelspeichers mit einer beim Linkvorgang angegebenen Mindestgr"osse
  und allozieren bei Bedarf den Stapelspeicher selbst.
\item
  "Ubernahme der Programmargumente von der Betriebssystemumgebung.
  Bei dieser "Ubernahme m"ussen die Unterschiede des Aufrufs von
  CLI (Shell) und Workbench ber"ucksichtigt werden. Die "ubernommenen
  Argumente werden in den globalen Variablen von Arts abgelegt und
  stehen dort dem Programm zur Verf"ugung. Das Bibliotheksmodul
  {\tt Arguments} extrahiert aus diesen Variablen die angeforderten
  Werte.
\item Installation der Prozeduren zur Behandlung von Laufzeitfehlern.
  Damit Laufzeitfehler keine "'Guru Meditations"' ausl"osen, muss eine
  Prozedur zur Behandlung der Laufzeitfehler in der Prozess-Struktur
  installiert werden. Eine ausf"uhrliche Beschreibung dieser Prozedur
  erfolgt weiter unten.

  F"ur eine Minimalversion unterbleibt dieser Schritt. Dadurch wird
  das Programm k"urzer, kann aber im Fehlerfall die unliebsame
  Bekanntschaft mit dem "'Guru"' machen.
\end{enumerate}

Die soeben geschilderten Schritte bereiten die Programm-Umgebung so
vor, dass das Hauptmodul nun aufgerufen werden kann. Das Hauptmodul
ruft zun"achst alle Initialisierungsteile der importierten Moduln
auf, diese wiederum die von ihnen importierten und so fort. Dadurch ist
gew"ahrleistet, dass alle importierten Objekte initialisiert und benutzbar
sind.

\subsubsection{Amiga Libraries}

Im Zusammenhang des Eingangscodes ist wichtig zu wissen, dass auch
die Amiga-Bibliotheken (shared libraries) in dieser Phase {\em automatisch\/}
ge"offnet werden. In anderen Entwicklungsumgebungen auf dem Amiga ist
dies die Aufgabe des Programmierers. Im \mtwoamiga-System erzeugt der
Compiler f"ur die Libraries jedoch 
kleine Objekt-Dateien\footnote{Dies geschieht
ohne ein zugeh"origes Implementationsmodul},
welche die Bibliotheken selbst"andig "offnen und
schliessen und im Fehlerfall einen Laufzeitfehler erzeugen.

Dies gilt nat"urlich nicht f"ur optionale Libraries.
Diese sollen ja
dem Programmierer die M"oglichkeit geben, das Programm
auch {\em ohne\/} Verwendung der Library ablaufen zu lassen.

Ausserdem muss f"ur die Verwendung aller Prozeduren, die
erst mit der Betriebssystem-Version 2.0 eingef"uhrt wurden,
zun"achst die Version der ge"offneten Library "uberpr"uft werden
(siehe Kapitel \ref{interface})!

Das selbst"andige "Offnen bringt zwei Vorteile: Einerseits kann
weder das "Offnen noch der Test auf dessen Erfolg vergessen werden und
andererseits entspricht dies eher der \mtwo{} Philosophie, dass importierte
Objekte
auf jeden Fall benutzt werden k"onnen, ohne Initialisierungs-Routinen
aufrufen zu m"ussen.

Auch in diesem Punkt unterscheiden sich die Voll- und Minimalversionen
des Laufzeitsystems: Kann eine Bibliothek nicht ge"offnet werden,
erzeugen die Vollversionen einen Requester, in dem der Name der Libary
zu lesen ist. Die Minimalversionen hingegen beenden das Programm
stillschweigend und geben den Wert 20 ({\tt FAIL}) an die aufrufende Instanz
zur"uck.

\subsubsection{Ausgangscode}

 Es ist sichergestellt,
dass --- im Normalfall --- das Programm am Ende den Ausgangscode
ausf"uhren wird. Der Ausgangscode hat die Aufgabe, alle im Programm
vorhandenen Moduln zu de-initialisieren (Close-Phase, siehe \ref{refClose},
Seite \pageref{refClose}), die vom
Eingangscode vorgenommenen Schritte wieder r"uckg"angig zu machen
(insbesondere die Prozess-Struktur wiederherzustellen) und die
Kontrolle an die aufrufende Instanz (CLI oder Workbench) zur"uckzugeben.

Der R"uckgabewert ({\tt returnVal}) wird bei einem Aufruf vom CLI im Register
D0 --- wie von AmigaDOS erwartet --- zur"uckgegeben. Beim Start des Programms
von der Workbench hingegen, wird dieser Wert in der Komponente {\tt length}
der {\tt StartupMsg} zur"uckgeliefert. Dies ist {\em kein Standard}, sondern
eine Spezialit"at von \mtwoamiga!

\subsection{Compilerunterst"utzung}

Die Compilerunterst"utzung umfasst eine Reihe von Prozeduren und Funktionen,
die von Programmen verwendet werden, obwohl sie nicht ausdr"ucklich importiert
wurden. Diese Prozeduren sind zu umfangreich,
um sie direkt bei jeder Verwendung
in das Programm einzukodieren. Statt dessen wird ein Aufruf in das
Laufzeitsystem codiert.

In unserem Fall beschr"ankt sich die direkte Compilerunterst"utzung auf
Prozeduren zur Multiplikation und Division von 32-Bit Werten; dies ist
nicht n"otig bei
Prozessoren ab 68020, hier wird direkt kodiert. Ausserdem wird
die Stapel"uberlaufkontrolle und die Anzeige von Laufzeitfehlern
"ubernommen.

\noindent
\begin{example}
F"ur das folgende Programmst"uck wird demnach nicht direkt der Maschinencode
der Multiplikation, sondern nur ein Prozeduraufruf erzeugt:
\begin{verbatim}
  VAR
    x, y, z: LONGINT;
  BEGIN
    z:=x*y;
    (*
     * Dies entspricht exakt der Anweisung
     * z:=Arts.Muls32(x,y);
     *)
  END
\end{verbatim}
\end{example}

\subsection{Die Behandlung von Laufzeitfehlern}
\label{runtimeErr}
Viele Fehler in einem Quelltext k"onnen bereits zur Kompilationszeit eines
einzelnen Moduls ausfindig gemacht werden. Dabei sind nicht nur syntaktische
Fehler wie vergessene Strichpunkte oder unausgeglichene Klammerpaare
eingeschlossen. Dar"uber hinaus k"onnen etwa fehlende Deklarationen,
typen\-in\-kom\-pa\-tib\-le Vergleiche oder Zuweisungen, Namenskonflikte
und dergleichen mehr festgestellt werden. Selbst eine Division mit der
Konstanten 0 (Null) wird vom Compiler beanstandet.

Leider gibt es noch einige Fehlersituationen, die der Compiler nicht zur
Compilationszeit erkennen kann. Solche Fehler treten zur Laufzeit des
Programms auf, weshalb sie {\bf Laufzeitfehler} genannt werden.

Die Laufzeitfehler, die bei einem \mtwoamiga-Programm auftreten k"onnen,
werden durch einen Fehlerzustand des Programms erzeugt. Diese Fehlerzust"ande
werden entweder vom Prozessor oder vom Programm selbst erkannt.

Auf dem Amiga d"urfte wohl allen die Reaktion des Betriebssystems auf
Laufzeitfehler bekannt sein: die "'Guru Meditation"'. Diese erscheint
dann, wenn das Betriebssystem durch einen fehlerhaften Task (Prozess)
so empfindlich gest"ort wurde, dass als L"osung nur der Neustart des
Systems bleibt.

Eine Quelle f"ur den (so hartn"ackig) meditierenden Guru sind die
{\bf Traps} des
Prozessors. Sobald der Prozessor einen ung"ultigen Zustand entdeckt, wird
zur Fehlerbehandlung eine entsprechende Trap-Prozedur aufgerufen.
M"ogliche Gr"unde f"ur diese Ausnahmezust"ande sind unter anderem die Division
durch 0, ein Fehler bei der Adressierung einer Variablen, ein Bereichsfehler,
der durch die {\tt CHK}-Instruktion erkannt wurde oder eine explizite
{\tt TRAP}-Anweisung, wie sie der Compiler zum Erkennen der Laufzeitfehler
generiert. Zur Vermeidung der "'Guru Meditation"' muss die Behandlung
der Traps vom Laufzeitsystem "ubernommen werden.

Die Laufzeitfehler, die das Programm erkennt, werden durch Aufrufe
von Prozeduren des Laufzeitsystems angezeigt. Diese Aufrufe k"onnen einerseits
vom Compiler selbst"andig erzeugt oder vom Programmierer ins Programm
eingef"ugt werden. Alle M"oglichkeiten zur Erzeugung von Laufzeitfehlern
durch den Programmierer sind aus der \mtwo-Anweisung {\tt HALT} entstanden.
Diese Anweisung ist daf"ur gedacht, um im Fehlerfall ohne Ausweg das Programm
zu beenden.

Neben der Anweisung {\tt HALT} bietet das Laufzeitsystem dem Programmierer
die Prozeduren {\tt Assert}, {\tt BreakPoint}, {\tt Error} und
{\tt Requester}, sowie {\tt Exit} und {\tt Terminate}. Diese Prozeduren
sind beim Modul {\tt Arts} im Kapitel \ref{biblio},
Bibliotheksmoduln, genauer beschrieben.


\subsection{Fehlermeldungen des Laufzeitsystems}

Das Laufzeitsystem meldet verschiedene Ereignisse, die ein Weiterlaufen
des Programmes unm"oglich machen, mittels eines Requesters dem Benutzer.
Der Benutzer kann dadurch die Ursache des Fehlers erkennen und das
Programm entweder debuggen oder endg"ultig abbrechen.

\subsubsection{Fehler beim \protect"Offnen der {\sl xxx\/}.library}

Da das Laufzeitsystem alle Libraries selbst"andig "offnet,
ist es dem Benutzer nur beschr"ankt m"oglich zu testen, ob die Library
erfolgreich ge"offnet werden konnte. Ist dies nicht der Fall,
erscheint diese Fehlermeldung. Soll das Programm auch bei
einem nicht erfolgreichen "Offnen der Library ablaufen, m"ussen
optionale Libraries verwendet werden.

\subsubsection{Programmierter HALT}

Tritt auf, wenn die \mtwo-Anweisung "`{\tt HALT}"' abgearbeitet
wurde.

\subsubsection{Ung\protect"ultiger CASE-Index}

Diese Fehlermeldung erhalten Sie dann, wenn f"ur den Wert des getesteten
Ausdrucks 
kein entsprechendes {\tt CASE}-Label vorhanden ist.

\begin{example}
\begin{alltt}
i:=2;
CASE i OF (* getesteter Ausdruck: i *)
| 1: Write('1'); (* kleinstes Label: 1 *)
| 3: Write('3'); (* gr"osstes Label: 3 *)
END;
\end{alltt}
\end{example}

Selbstverst"andlich verunm"oglicht ein stetiges Verwenden von
{\tt ELSE}-Klau\-seln (innerhalb {\tt CASE}) diesen Laufzeitfehler.
Dies erschwert jedoch die Fehlersuche, da unvorhergesehene
(unm"ogliche?) F"alle nicht unmittelbar auf"|fallen.

\subsubsection{Funktion ohne {\tt RETURN} beendet}

Funktionsprozeduren m"ussen durch eine {\tt RETURN}-Anweisung
beendet werden; bei Prozeduren ist diese nicht zwingend. Ist
das Ende einer Funktionsprozedur ({\tt END}-Anweisung) erreicht,
ohne dass eine {\tt RETURN}-Anweisung ausgef"uhrt wurde, so
erscheint dieser Fehler. Erg"anzen Sie die Funktion mit
einer {\tt RETURN}-Anweisung, um den Fehler zu beheben.

\subsubsection{Stapel\protect"uberlauf}

Jeder Prozeduraufruf belegt einen gewissen Platz auf dem Stapel
(Stack). Dort sind R"ucksprungadresse, Verweis auf die aufrufende
und umschliessende Prozedur, Parameter, lokale Variablen
und gerettete Register
abgelegt. Jedem Task ist nur ein begrenzter Stapelbereich zugeordnet.
Ist der verbleibende Stapelbereich zu klein, dann kann keine weitere
Prozedur aufgerufen werden, was durch diesen Laufzeitfehler angezeigt
wird. Vergewissern Sie sich, dass das Programm fehlerfrei ist
(insbesondere bei Rekursion) und vergr"ossern Sie n"otigenfalls den
Stapelbereich.

Die Anfangsgr"osse des Stapelsbereichs kann im CLI mit dem {\tt STACK}-Befehl
\cite{AmigaDOS} eingestellt werden. In der Workbench ist die Anfangsgr"osse
f"ur jedes Programm (Tool) in dessen {\tt .info}-Datei abgelegt.
Diese Gr"osse kann innerhalb der Workbench
angezeigt und ge"andert werden mit der Funktion
{\tt Information...} des Menus {\tt Icons}.\footnote{Mit Workbench 1.3
ist es die Funktion {\tt Info} des Menus {\tt Workbench}.}

\subsubsection{Adress-Fehler}

Ein Adress-Fehler wird direkt durch den Prozessor dann ausgel"ost,
wenn dieser auf ein Wort, ein Langwort (32 Bit) oder ein Instruktion
an einer ungeraden Adresse (Byte-Adressen) zugreifen will. Dieser
Fehler bedeutet meist, dass eine nicht initialisierte Zeiger-
oder Prozedurvariable verwendet wurde. 

\subsubsection{Ung\protect"ultige Instruktion}

Als ung"ultige Instruktion wird jedes Wort-Bitmuster bezeichnet,
das nicht dem Bitmuster des ersten Worts eines g"ultigen
Prozessor-Befehls entspricht.
Meist bedeutet dies auch hier, dass irrt"umlich
eine nicht initialisierte Zeiger-
oder Prozedurvariable verwendet wurde.
\begin{note}
Es gibt eine Ausnahme, bei der eine ung"ultige Instruktion
nicht auf einen Fehler zur"uckzuf"uhren ist:
Das Wort {\tt 4AFCH} (bin"ar 0100101011111100) entspricht
dem "`g"ultigen"' Befehl "`{\tt ILLEGAL}"'. Es ist das einzige
Bitmuster, das auch nach zuk"unftigen Erweiterungen der
MC680{\sl x\/}0-Familie "`{\tt ILLEGAL}"' bleibt.
\end{note}

\subsubsection{Division durch Null}

Zahlen beliebigen Typs k"onnen laut Regeln der Mathematik nicht
durch Null (0) dividiert werden. Wird in einer Anweisung versucht,
durch 0 zu dividieren, kann das Programm nicht weiter ausgef"uhrt
werden. Gleich verh"alt es sich mit der Modulo-Operation,
deren Wert sich aus dem Rest der vorangehenden Division ergibt.

Beispiel:
\begin{example}
\begin{alltt}
VAR
 i,j:INTEGER;
 r,s:REAL;
{\rm \ldots}
i:=0; r:=0.0;
(*
 * Folgende Anweisungen f"uhren zum
 * beschriebenen Laufzeitfehler
 *)
j:=j DIV i;
j:=j / i;
j:=j MOD i;
j:=j REM i;
s:=s / r;
\end{alltt}
\end{example}

Dieses Beispiel gilt selbstverst"andlich auch f"ur die anderen numerischen
Typen.

\subsubsection{Privilegverletzung}

Die Prozessoren der 68000-Familie kennen zwei Modi: den {\bf User-Mode}
und den {\bf Supervisor-Mode}. Letzterer ist dabei f"ur
Aufgaben des Betriebssystem vorgesehen.
Gewisse Instruktionen d"urfen dabei nur im Supervisor-Mode
ausgef"uhrt werden,
man sagt auch,
sie seien {\bf privilegiert}.
Wird im User-Mode versucht, eine dieser 
privilegierten Instruktionen auszuf"uhren, meldet das Laufzeitsystem
diese Verletzung mit diesem Laufzeitfehler.

\subsubsection{Zeiger ist NIL (TRAP 13)}

Jede Zeigervariable enth"alt im Normalfall die Adresse
eines Objekts. Soll eine Zeigervariable ausdr"ucklich auf {\em kein\/}
Objekt zeigen, enth"alt diese den Wert {\tt NIL}. Da die
(ung"ultige) Dereferenzierung einer 
solchen Variable vom Betriebssystem nicht festgestellt wird,
generiert der Compiler daf"ur Pr"uf"-code. Vor der Dereferenzierung
wird der Zeigerinhalt auf {\tt NIL} gepr"uft und bei Gleichheit
die Instruktion {\tt TRAP \#13} ausgef"uhrt. 
Auf h"oheren Prozessoren erzeugt der Prozessor die Instruktion
{\tt TRAPEQ}, wie untenstehend bei der Erl"auterung des "Uberlaufs
beschrieben.

\subsubsection{Bereichsfehler}

Bei jeder Zuweisung an einen Unterbereich und bei jedem Zugriff auf ein
Feldelement ({\tt ARRAY}) 
findet eine Pr"ufung der unteren und der oberen
Schranke statt. Bei einer Zuweisung an einen Unterbereich wird gepr"uft,
ob der resultierende Wert in die Zielvariable gespeichert werden kann.
Bei einem Zugriff auf ein Feldelement wird gepr"uft, ob "uberhaupt
ein Element mit dem ausgewerteten Indexausdruck existiert (deklariert
ist). Bei einer {\tt CASE}-Anweisung wird der berechnete Ausdruck
als Index in eine Sprungtabelle verwendet, die f"ur jeden der
{\tt CASE}-Label die Adresse der zugeh"origen Anweisungen erh"alt.
Also muss wie bei einem Zugriff auf ein Feldelement "uberpr"uft werden,
ob der Index g"ultig ist.

Ist die untere Schranke Null (0) und die obere Schranke kleiner als
$2^{15}$ (32768), geschieht diese "Uberpr"ufung mit der MC68000-Instruktion
"`{\tt CHK}"'. Ist die obere Schranke zwischen $2^{15}$ und $2^{31}$
und ist der Zielprozessor {\em mindestens\/} eine MC68020-CPU steht
f"ur die "Uberpr"ufung die Instruktion "`{\tt CHK.L}"' zur Verf"ugung.
Dann erscheint "`{\tt Bereichsfehler (CHK)}"' als Fehlermeldung.
Andernfalls ist die Bereichspr"ufung
etwas komplexer: nach erfolgter Pr"u"-fung mit Hilfe von einer oder zwei
Vergleichsinstruktionen wird im Fehlerfall\footnote{Test des "'Condition
Code"'-Registers} auf MC68000 die Instruktion "`{\tt TRAP \#14}"'
ausgef"uhrt,
wie es auch in der Fehlermeldung "`{\tt Bereichsfehler (TRAP 14)}"'
vermerkt ist. F"ur den Anwender ist diese Unterscheidung allerdings
von untergeordneter Bedeutung. 

\begin{example}
Beispiel:
\begin{alltt}
VAR
 a:ARRAY [0..5] OF INTEGER;
 sub:[0..10];
 i:INTEGER;
{\rm \ldots}
i:=11;
sub:=i;         (* Bereichsfehler, da  11 > MAX(sub) *)
sub:=-i;        (* Bereichsfehler, da -11 < MIN(sub) *)
i:=a[i];        (* Bereichsfehler, da kein a[11] *)
\end{alltt}
\end{example}

\subsubsection{\protect"Uberlauf}

Ist das Resultat einer Rechenoperation gr"osser als es der
Maximalwert der beteiligten Typen erlaubt, so tritt "Uberlauf ein.
Die entsprechende Unterschreitung des Minimalwerts wird {\bf Unterlauf}
genannt. Auf Prozessorebene wird "Uberlauf und Unterlauf hingegen
nicht unterschieden, weshalb hier "Uberlauf auch Unterlauf
beinhalten soll.

\begin{example}
Beispiel:
\begin{alltt}
VAR
 i,j,k:INTEGER;
{\rm \ldots}
i:=30000;
j:=20000;
k:=-30000;
IF (i+j)<0 THEN {\rm \ldots} END; (* "Uberlauf *)
IF (k-j)>0 THEN {\rm \ldots} END; (* Unterlauf *)
\end{alltt}
\end{example}

"Ublicherweise wird mit der {\tt TRAPV}-Anweisung auf "Uberlauf
gepr"uft. Dies ist aber bei einer 16 Bit Multiplikation nicht
m"oglich. Die "Uberlaufspr"ufung erfolgt hier durch eine explizite
Pr"ufung. Im Fehlerfall erfolgt ein Aufruf der Instruktion
{\tt TRAP \#15}. Im Requester wird angegeben, ob {\tt TRAPV}
oder {\tt TRAP \#15} ausgef"uhrt wurde.

Auf den Prozessoren ab MC68020 steht eine Instruktion
{\tt TRAP\cmsltt cc\/}\footnote{Dabei steht {\cmsltt cc\/} f"ur
"'condition code"'} zur Verf"ugung, welche f"ur eine
Bedingung direkt eine TRAP-Instruktion aus"-f"uhrt. Dadurch
werden {\em keine\/} Instruktionen {\tt TRAP \#13},
{\tt TRAP \#14} und {\tt TRAP \#15} benutzt. Logischerweise
erscheinen die beschriebenen Requester dann auch nicht.
Stattdessen erscheint die Fehlermeldung des "Uberlaufs,
da {\tt TRAP\cmsltt cc} nur die allgemeinere Form der
{\tt TRAPV}-Instruktion ist. Dies bedeutet aber, dass ab
MC68020 dieser Fehler f"ur Zugriff auf {\tt NIL}-Zeiger,
Bereichsfehler, "Uberlauf und Unterlauf auftritt. Diesem Nachteil
des Informationsverlustes steht der Vorteil des k"urzeren und
schnelleren Codes gegen"uber.

\subsubsection{Prozessor Trap \#{\sl xxx\/}}

Diese Meldung erscheint bei allen Traps, die nicht durch eine
der oben beschriebenen Meldungen abgefangen werden.
Dabei stellt "`{\sl xxx\/}"'
eine Dezimalzahl dar, welche die Nummer der Exception angibt und den
Prozessor-Handb"uchern entnommen werden muss. Vorkommen kann diese
Meldung bei einer expliziten {\tt TRAP}-Anweisung im Assembler
(diese tragen die Nummern 32 bis 47 f"ur {\tt TRAP \#0} bis {\tt TRAP \#15})
oder bei Coprozessor-Exceptions, beispielsweise durch die 68881 FPU.

\section[Laufzeitumgebung]{Die Laufzeitumgebung von \mtwoamiga}

\subsection{Speicherbereiche}

Ein \mtwo-Programm besteht aus einem oder mehreren Moduln. Zu jedem
Modul werden zwei Speicherbereiche, ein {\bf Daten}- und ein {\bf Codebereich},
angelegt. Im Datenbereich werden die globalen Variablen des Moduls,
im Codebereich werden neben dem Code auch die Konstanten, die Speicher
ben"otigen (Zeichenketten), abgelegt.

Beim Linken des Programmes werden alle \near-Variablen zu einem
einzigen Block zusammengelegt. Das Register A4 (static base) zeigt
zur Laufzeit dann auf den Beginn dieses Blocks.  Ist dieser 
gr"osser als 32\,kByte, zeigt A4 auf einen Offset von 32\,kByte des Blocks.
Dieser Wert wird zu Beginn von der
Prozedur {\bf Arts.Startup} nur ein einziges Mal geladen.

\label{refA4Desc}

\subsection{Modul-Initialisierung}
\label{modInit}
F"ur jedes Modul wird automatisch eine globale INTEGER-Variable
alloziert, die als Z"ahler dient, wie oft dieses Modul bereits
initialisiert wurde. Nur beim ersten Aufruf der
Initialisierungs-Prozedur (Modulrumpf) wird der Code tats"achlich
durchlaufen. W"ahrend der Close-Phase (siehe \ref{refClose}) bei Programmende
wird dieser Z"ahler wieder heruntergez"ahlt,
bis beim Wert 0 der {\tt CLOSE}-Teil
des Modulrumpfes ausgef"uhrt wird. Es gibt {\em keine\/} M"oglichkeit,
diesen Z"ahler vom Programm aus anzusprechen, da er keine explizit
deklarierte Variable ist.


\subsection{Stack-Aufbau}

\label{refA5Desc}
\label{refStatLink}
Das Register A5 zeigt auf den {\bf Prozedurdeskriptor} (mark pointer).
Der Prozedurdeskriptor umfasst bei globalen Prozeduren 8 Byte,
bestehend aus der R"ucksprungadresse und einem Zeiger auf den
Prozedurdeskriptor der aufrufenden Prozedur (dynamic link).
Bei lokalen Prozeduren sind es 12 Byte; hier wird zus"atzlich der aktuelle
Prozedurdeskriptor der umschliessenden Prozedur auf dem
Stack abgelegt (static link). Die beiden Arten der Prozedurdeskriptoren
sind in Abbildung \ref{figLink} dargestellt.
\begin{figure}
\begin{center}
\def\sp{\hfill}
\def\A#1{\framebox{\space\space\space\space A#1\space\space\space\space}}
\begin{tabular}{l|c|cl|c|}
        &            &\sp &            &    \\
        & Parameter  &\sp &            & \\ \cline{2-2}
        & static link&\sp &          & Parameter\\ \cline{2-2} \cline{5-5}
        & return adr.&\sp &  & return adr. \\ \cline{2-2} \cline{5-5}
\A5$\longrightarrow$ & dynamic link &\sp & \A5$\longrightarrow$ & dynamic link \\ \cline{2-2}\cline{5-5}
        &            &\sp &             &  \\
        & lokale     &\sp &             & lokale    \\
        & Variablen  &\sp &             &  Variablen   \\ \cline{2-2} \cline{5-5}
        &            &\sp &             &      \\
        & gerettete  &\sp &        &  gerettete \\
\A7$\longrightarrow$ & Register &\sp & \A7$\longrightarrow$ & Register\\ \cline{2-2} \cline{5-5}
        &          &\sp &             &          \\
\multicolumn{5}{c}{\ } \\
\multicolumn{2}{c}{lokale Prozedur}&\sp &\multicolumn{2}{c}{globale Prozedur}\\
\end{tabular}
\end{center}
\caption{\label{figLink} Stackaufbau von Prozeduren.}
\end{figure}

Vom Prozedurdeskriptor aus in negativer Richtung werden die lokalen
Variablen angelegt. Mit positivem Offset zum A5-Register erreicht
man die Parameter der Prozedur. Die Anordnung von Variablen und
Parametern ist in Abschnitt \ref{alignLocVars} genau beschrieben.

\subsection{Adressierung der Parameter}

Parameter k"onnen in \mtwo{} als {\bf Wert}- oder als
{\bf Variablenparameter} an
Prozeduren "ubergeben werden. Bei Wertparametern wird immer von der aufrufenden
Prozedur der Wert
der Variablen auf den Stack kopiert. Man sollte daran denken, einen gen"ugend
grossen Stack bereitzustellen (mit Hilfe des DOS-Kommandos
"`{\tt STACK}"'). Falls eine Prozedur beispielsweise
ein grosses Feld als Wertparameter besitzt und zur Laufzeit nicht gen"ugend
Stapelspeicher zur Verf"ugung steht, wird es in jedem Fall unangenehme
Folgen haben. Ist die Option
"`{\tt StackChk}"' eingeschaltet, wird ein Laufzeitfehler
erzeugt (siehe \ref{refStackChk} auf
Seite \pageref{refStackChk}). Andernfalls kann der Speicher von anderen
Programmen oder gar vom Betriebssystem "uberschrieben werden; diese
Auswirkungen sind im allgemeinen nicht voraussehbar und f"uhren
fr"uher oder sp"ater --- aber immer fr"uher als gew"unscht ---
zum "'Guru"'.

Bei Variablenparametern
wird nur die Adresse der Variablen auf den Stack kopiert. Die Prozedur
benutzt die Adresse, um auf den Inhalt der Variablen zuzugreifen.

\subsection{Offene Feldparameter}
\label{refOpenArrays}

Eine Ausnahme von der oben beschriebenen Regel tritt bei offenen
 Feldparametern ein.
Da nicht von vornherein bekannt ist, wie gross das Feld beim Aufruf ist,
kann nicht der Inhalt der Variablen auf
den Stack kopiert werden, da die Prozedur sonst nicht w"usste, wie sie
die nachfolgenden Parameter adressieren sollte. Es wird in diesem
Fall ein 8 Byte grosser Deskriptor auf den Stack kopiert. Dieser
enth"alt den Wert von {\tt HIGH} und die Adresse der Variablen.
{\tt HIGH} liegt
als Langwort auf der h"oheren Adresse. Wenn es sich um einen
Werteparameter handelt, kopiert die Prozedur die Variable, an die
geretteten Register anschliessend, auf den Stack und passt den Deskriptor
entsprechend an, sodass die Adresse nunmehr auf die Kopie zeigt.
Die Kopie von offenen Feldern kann durch die Option
\begin{quote} \verb|(*$ CopyDyn:=FALSE *)| \end{quote}
vor der Prozedur f"ur diese einzelne Prozedur ausgeschaltet werden,
was bewirkt, dass der Werteparameter wie ein VAR-Parameter
behandelt wird, was Code, Stack und Laufzeit spart. Dies sollte man
aber nur tun, wenn der Parameter wirklich nicht ver"andert wird.

\subsection{Kontrollcode}

Der Quelltext eines \mtwo-Programmes wird vom Compiler bereits
auf gr"osstm"ogliche Fehlerfreiheit kontrolliert. Der Compiler pr"uft,
ob die Syntax des Programmes korrekt ist. Einige Fehler sind
jedoch erst zur Laufzeit des Programmes feststellbar.

F"ur diese Fehler wird vom Compiler (bei entsprechend eingeschalteten
Optionen!) Kontrollcode erzeugt, der bei Eintritt bestimmter Fehlerzust"ande
durchlaufen wird und zum Programmabbruch mit einem
Lauf\-zeit\-fehler-Re\-quester
f"uhrt. Dies bringt dem Programmierer zwei entscheidende Vorteile:
\begin{itemize}
\item Das Programm wird im Fehlerfall sauber abgebrochen; die anderen
Prozesse laufen ungest"ort weiter, programmfremde Speicherinhalte werden
nicht zerst"ort. Das hat zur Konsequenz, dass der
ungeliebte "`Guru"' nicht erscheint. Ausserdem kann die
Close-Phase noch durchlaufen werden um belegte Ressourcen
wieder freizugeben.
\item  Fehler in der Programmierung k"onnen genauer lokalisiert werden,
und die Fehlersuche wird k"urzer und effektiver.
\end{itemize}

Diesen Vorteilen stehen aber auch Nachteile gegen"uber:
\begin{itemize}
\item Der Kontrollcode vergr"ossert die Objektdatei.
\item Das Programm wird langsamer.
\end{itemize}

\begin{warning}
 Trotz des intensiven vom Compiler erzeugten Pr"ufcodes ist es
 m"oglich, dass ein Programm "`abst"urzt"' und dabei systemeigenen
 Speicher zerst"ort! H"aufigste Ursache f"ur derartige Fehler
 sind nicht-initialisierte Zeiger und Prozedurvariablen
 (z.B. lokale Variablen) oder
 der fehlerhafte Aufruf von Prozeduren des Betriebssystems.
\end{warning}

Die genauen Bedingungen f"ur Laufzeitfehler k"onnen Sie im Abschnitt
 \ref{runtimeErr}
ab Seite \pageref{runtimeErr} nachlesen, an dieser Stelle wollen wir
lediglich einige Beispiele f"ur den erzeugten Pr"ufcode und seine
Auswirkung auf die Geschwindigkeit geben. Der Pr"ufcode ist in den
Beispielen jeweils durch einen Pfeil gekennzeichnet.

Bei Prozessoren vom 68020 an aufw"arts wird f"ur RangeCheck und NilCheck
jeweils k"urzerer und schnellerer Pr"ufcode generiert, weil die auf
diesen Prozessoren verf"ugbaren Befehle "`{\tt TRAP\cmsltt cc\/}"'
und "`{\tt CHK.L}"'
benutzt werden und somit der kurze Sprung "`drumherum"' unn"otig ist.

\begin{example}
\begin{verbatim}
CONST
  Count = 1000000;

PROCEDURE RangeCheck;
VAR
  cnt: LONGINT;
  i: INTEGER;
  a: ARRAY[0..7] OF INTEGER;
BEGIN
  i:=5;
  FOR cnt:=0 TO Count DO
    (*$ RangeChk:=TRUE *)
    a[i]:=2;

        MOVE.W  -6(A5),D7
-->     CHK.W   #$0007,D7
        ASL.W   #1,D7
        LEA     -22(A5),A3
        MOVE.W  #$0002,0(A3,D7.W)

    (*$ POP RangeChk *)
  END;
END RangeCheck;

PROCEDURE RangeCheckLong;
VAR
  cnt,j: LONGINT;
  a: ARRAY[0..7] OF INTEGER;
BEGIN
  j:=5;
  FOR cnt:=1 TO Count DO
    (*$ RangeChk:=TRUE *)
    a[j]:=2;

        MOVE.L  -8(A5),D7
-->     CMPI.L  #$00000007,D7
-->     BLS.S   L000003
-->     TRAP    #14
  L000003:
        ASL.L   #1,D7
        LEA     -24(A5),A3
        MOVE.W  #$0002,0(A3,D7.L)

    (*$ POP RangeChk *)
  END;
END RangeCheckLong;

PROCEDURE OverflowCheck;
VAR
  i,cnt: LONGINT;
BEGIN
  i:=0;
  FOR cnt:=1 TO Count DO
    (*$ OverflowChk:=TRUE *)
    i:=i+1;

         MOVE.L   -4(A5),D7
         ADDQ.L   #1,D7
-->      TRAPV
         MOVE.L   D7,-4(A5)

    (*$ POP OverflowChk *)
  END;
END OverflowCheck;


(*$ StackChk:=TRUE *)
PROCEDURE StackCheck;
VAR
  a: ARRAY[0..99] OF LONGINT;
BEGIN

-->     MOVE.L   #$FFFFFE70,D0
-->     JSR      Arts_StkChk(PC)
        RTS

END StackCheck;
(*$ POP StackChk *)

PROCEDURE NilCheck;
VAR
  cnt,i: LONGINT;
  p: POINTER TO LONGINT;
BEGIN
  p:=ADR(i);
  FOR cnt:=1 TO Count DO
    (*$ NilChk:=TRUE *)
    p^:=3;

       MOVEA.L  -12(A5),A3
-->    MOVE.L   A3,D7
-->    BNE.S    L000010
-->    TRAP     #13
  L000010:
       MOVEQ    #3,D7
       MOVE.L   D7,(A3)

    (*$ POP NilChk *)
  END;
END NilCheck;
\end{verbatim}
\end{example}

Bereichs-, "Uberlaufs- und NIL-Pr"ufung ben"otigen wenig zus"atzlichen
Code. In diesen Prozeduren wurde nur jeweils eine bis maximal drei
Anweisungen eingef"ugt (2 bis 6 Byte).

Anders verh"alt es sich mit der Stapel"uberlauf-Pr"ufung. Beim Eingangscode
einer Prozedur m"ussen zwei Befehle eingef"ugt werden (jenachdem, ob der Wert
in ein Byte passt, ergibt dies 6 oder 10 Byte).
Der eingef"ugte Code ruft eine Prozedur
im Modul {\tt Arts} auf, die "uberpr"uft, ob die ben"otigte Anzahl Byte auf
dem Stapel noch verf"ugbar ist.

Alle "Uberpr"ufungen kosten nicht nur Speicherplatz, sondern auch
Aus"-f"uh"|rungs"|zeit:

\begin{table}[h]
\begin{center}
\begin{tabular}{lccc}
"Uberpr"ufung & ausgeschaltet & eingeschaltet & Zunahme \\ \hline
RangeCheck & 17.0s & 18.8s & 11\% \\
RangeCheckLong & 17.8s & 21.3s & 20\% \\
OverflowCheck & 16.0s & 16.6s & 4\% \\
StackCheck & 15.2s & 39.5s & 160\% \\
NilCheck & 14.9s & 17.1s & 15\% \\
\end{tabular}
\end{center}
\caption{Aus\protect"uhrungszeit mit \protect"Uberpr\protect"ufungscode}
\end{table}

Wie man sieht, ist also die Stapel"uberpr"ufung die gr"osste "`Bremse"'.
Sollte ein Programmteil nicht schnell genug laufen, empfiehlt es sich,
als erstes die Stapel"uberlaufpr"ufung auszuschalten, weil man damit das
Programm am ehesten beschleunigen kann.

