\documentclass[DIV15,a4wide]{scrartcl}
\usepackage{multicol}
\usepackage[latin1]{inputenc}
\usepackage{german}
%\usepackage{longtable}
%\parindent0pt
\parskip0.17ex
\sloppy
\pagestyle{empty}
\begin{document}

\textit{Erg"anzungen zur Vorlesung und zu den "Ubungen. Version vom
18. Dezember 2000. Korrekturen und Kommentare bitte an
Chris Hodges $<$hodges@in.tum.de$>$}. 

\section{Adressierungsarten der MI}

Im folgenden werden die Adressierungsarten der MI erkl"art.
Verwendete Abk"urzungen:
\begin{description}
\setlength{\itemsep}{-1eX}
\item[\texttt{EA}:] Effektive Adresse
\item[\texttt{LOP}:] Breite des Operanden in Bytes (entsprechend B, H, W).
\item[\texttt{S[EA].LOP}:] Speicherinhalt ab Adresse EA mit der Breite LOP
\end{description}

Allen Beispielen liegt jeweils folgende Speicher- und Registerbelegung
zugrunde:
{\small\begin{verbatim}
Speicherbelegung ab H'1000':      Register: R2 = H'00000042'
1000: 00 00 47 11 12 34 00 00               R3 = H'00001004'
1008: 10 04 10 08 00 00 00 04               R4 = 2   
\end{verbatim}}

Nur bei Adressierungsarten, die eine Effektive Adresse berechnen, kann diese
auch f"ur den \texttt{MOVEA} Befehl verwendet werden. Adressen haben immer
Wortbreite (32 Bit), daher gilt bei \texttt{MOVEA} LOP=W.

\subsection{Direkter (immediater) Operand}

Es wird die mit einem 'I' vorangestellten Konstante direkt verwendet und
in den Zielbereich geladen.

Schreibweise: \texttt{I <konstante>} $\Rightarrow$ wert = konstante

Beispiele:
{\small\begin{verbatim}
        MOVE W  I 1234,R0       -> R0 = H'000004D2'
\end{verbatim}}

\subsection{Registeradressierung (direkt)}

Es wird der Inhalt des Registers verwendet.

Schreibweise: \texttt{Rx} $\Rightarrow$ wert = Inhalt von Register x

Beispiele:
{\small\begin{verbatim}
        MOVE H  R2,R0           -> R0 = H'xxxx0042'
\end{verbatim}}

\subsection{Absolute Adresse}

Hierbei wird die Adresse direkt im Operandenfeld spezifiziert (bzw. der
Operand wird als Speicheradresse gewertet, dessen Inhalt mit Operandenbreite
ausgelesen wird). Oft wird das 'I' bei der Immediate-Adressierung vergessen und
f"uhrt dann f"alschlicherweise zur absoluten Adressierung.

Schreibweise: \texttt{<speicheradresse>} $\Rightarrow$ EA = speicheradresse; wert = S[EA].LOP

Beispiele:
{\small\begin{verbatim}
    MOVE H  H'1004',R0      -> R0 = H'xxxx1234'
    MOVE W  H'1000',R0      -> R0 = H'00004711'
    MOVEA   H'1000',R0      -> R0 = H'00001000'
\end{verbatim}}

\subsection{Relative Adressierung (herkömmlich: indirekte Adressierung)}

Es wird eine Effektive Adresse berechnet, deren Speicherzelleninhalt
ausgelesen bzw. "uberschrieben wird. Als Basis dient immer ein Register,
dessen Inhalt folglich als Adresse interpretiert wird. Dazu kommt optional
noch ein Displacement (Offset) und/oder
ein optionaler nach Operandenbreite skalierter Index (per Register), das
noch bei der Berechnung der EA hinzuaddiert wird.
Anwendung: Strukturen, Arrays.

\pagebreak
Schreibweisen:
\begin{description}
\setlength{\itemsep}{-1eX}
\item[Register indirekt:] \texttt{!Rx}\\$\Rightarrow$ EA = Inhalt von Register x; wert = S[EA].LOP
\item[Register indirekt mit Displacement:]\texttt{<offset> + !Rx}\\$\Rightarrow$ EA = Summe von Register x und offset; wert = S[EA].LOP
\item[Register indirekt mit Index:] \texttt{!Rx/Ry/}\\$\Rightarrow$ EA = Summe von Register x und LOP*Register y; wert = S[EA].LOP
\item[Register indirekt mit Index und Displacement:] \texttt{<offset> + !Rx/Ry/}\\$\Rightarrow$ EA = Summe von Register x, offset und LOP*Register y; wert = S[EA].LOP
\end{description}

Beispiele:
{\small\begin{verbatim}
    MOVE H  !R3,R0          -> EA = H'00001004'; R0 = S[EA].H    = H'xxxx1234'
    MOVE W  -2+!R3,R0       -> EA = H'00001002'; R0 = S[EA].W    = H'47111234'
    MOVE H  !R3/R4/,R0      -> EA = R3+LOP*R4  = H'00001008'; R0 = H'xxxx1004'
    MOVE W  -10+!R3/R4/,R0  -> EA = -10+R3+4*2 = H'00001002'; R0 = H'47111234'
    MOVEA   -10+!R3/R4/,R0  -> EA = -10+R3+4*2 = H'00001002'; R0 = H'00001002'
\end{verbatim}}

\subsection{Postinkrement/pr"adekrement Adressierungen (Stackaddressierung)}

Diese Adressierungsart entspricht der Register indirekt-Adressierung, nur da"s
\emph{nach} der Operation das Register um LOP erh"oht (postinkrement) oder aber
\emph{vor} der Operation das Register um LOP dekrementiert wird. Mit diesen
Adressierungsarten lassen sich leicht \texttt{PUSH} und \texttt{POP} Operationen
definieren.

Schreibweisen:
\begin{description}
\setlength{\itemsep}{-1eX}
\item[Postinkrement:] \texttt{!Rx+} $\Rightarrow$ EA = Inhalt von Register x; wert = S[EA].LOP; Register x += LOP
\item[Pr"adekrement:] \texttt{-!Rx} $\Rightarrow$ Register x -= LOP; EA = Inhalt von Register x; wert = S[EA].LOP
\end{description}

Beispiele:
{\small\begin{verbatim}
    MOVE H  !R3+,R0         -> EA = H'00001004'; R0 = H'xxxx1234'; R3 = H'1006'
    MOVE W  -!R3,R0         -> R3 = H'1000'; EA = H'00001000'; R0 = H'00004711'
\end{verbatim}}

\subsection{indirekte Adressierung (herkömmlich: doppelt indirekte Adressierung)}

Es wird wie bei der indirekten Adressierung eine Adresse berechnet, deren
Speicherzelleninhalt ausgelesen wird. Dieser Wert bestimmt dann die
Effektive Adresse (d.h. diese wird selbst nochmals auswertet). Zu beachten
ist, da"s der optionale operandenbreitenskalierte Index sich auf den "au"seren
Teil bezieht, sprich erst nach dem Auswerten der inneren EA addiert wird.
Anwendung: z.B. zweidimensionale Arrays (Zeigerarrays von Zeigern).

Schreibweisen:
\begin{description}
\setlength{\itemsep}{-1eX}
\item[doppelt Register indirekt:] \texttt{!!Rx}\\$\Rightarrow$ EA = S[Inhalt von Register x].W; wert = S[EA].LOP
\item[indirekte relative Adressierung mit Displacement:] \texttt{!(<offset> + !Rx)}\\$\Rightarrow$ EA = S[Inhalt von Register x + offset].W; wert = S[EA].LOP
\item[indizierte relative Adressierung:] \texttt{!!Rx/Ry/}\\$\Rightarrow$ EA = S[Inhalt von Register x].W + LOP*Register y; wert = S[EA].LOP
\item[indizierte relative Adresierung mit Displacement:] \texttt{!(<offset> + !Rx)/Ry/}\\$\Rightarrow$ EA = S[Inhalt von Register x + offset].W + LOP*Register y; wert = S[EA].LOP
\end{description}

Beispiele:
{\small\begin{verbatim}
    MOVE H  !(2+!R3),R0     -> EA = S[H'00001006'] = H'00001004'; R0 = H'xxxx1234'
    MOVEA   !(2+!R3)/R4/,R0 -> EA = S[H'00001006'] = H'1004'+4*2; R0 = H'0000100C'
    MOVE W  !(2+!R3)/R4/,R0 -> EA = S[H'00001006'] = H'0000100C'; R0 = H'00000004'
\end{verbatim}}

\section{Der Stack (Keller)}

Hier noch ein paar Worte zum Stack. Der Stackpointer SP muß in der MI auf
einen gültigen Wert initialisiert werden. Da der Stack in der MI im
Speicher von oben (höherwertigen Adressen) nach unten (niederwertigen
Adressen) wächst, ist dies üblicherweise ein großer Wert (z.B.
\texttt{H'10000'}).

Achtung! Leider gibt es oft ein paar sprachliche Mißverständnisse: Was auf
dem Stack \emph{oben} liegt, liegt demnach im Speicher von der Adresse her
\emph{unten} (höherwertige)! Wenn man allerdings, wie üblich, den Speicher
schematisch aufzeichnet mit Anfangsadresse 0 oben und dem Stackbereich
unten (da höherwertige Adresse), so wächst der Stack \emph{graphisch
betrachtet} von unten nach oben.

Der Stackpointer zeigt \emph{immer auf das oberste (zuletzt auf den Stack
geschriebene) Element}. Alle Speicherzellen über letzten Element sind
undefiniert (enthalten Speichermüll). Relative Adressierungen dürfen daher
nur mit positivem Displacement angegeben werden (siehe oben).
Adressierungen der Form -X+!SP sind zwar nicht syntaktisch illegal, es wird
jedoch ausdrücklich davor gewarnt, diese in der Praxis einzusetzen!

Um Werte auf den Stack zu schreiben (entspr. Push-Operation) wird die
prädekrement Adressierung eingesetzt (\texttt{-!SP}), um das oberste
Element vom Stack zu holen und zu entfernen (Pop-Operation) die
postinkrement Adressierung (\texttt{!SP+}).
\vspace{-2ex}
\section{Unterprogrammtechniken}

Im diesem Abschnitt werden die wesentlichen Programmiertechniken für
Unterprogramme erläutert.

Für alle Unterprogramme gilt: Aufruf geschieht mit der \texttt{CALL}
Anweisung, Terminierung des Unterprogramms mit \texttt{RET}. \texttt{CALL}
speichert die Rücksprungadresse (Adresse des Befehls nach dem
\texttt{CALL}) auf dem Stack und springt dann zum/zur angegeben
Label/Adresse. \texttt{RET} nimmt die Rücksprungadresse vom Stack und
schreibt sie in das Register PC, was effektiv zur Fortsetzung des Programms
an der Stelle nach der \texttt{CALL} Anweisung führt.

\vspace{-2ex}
\subsection{Leichtgewichtige Unterprogramme}

Leichtgewichtige Unterprogramme haben weder Parameter noch Ergebnis oder falls doch,
werden diese über Register übergeben. Das Unterprogramm muß darauf achten,
daß es nicht Register überschreibt/verändert, die vom Hauptprogramm genutzt
werden (dazu können einzelne Register auf den Stack geladen werden
(\texttt{MOVE W R0,-!SP})). Ansonsten kann nur auf globale/statische
Variablen zugegriffen werden. Rekursionen und lokale Variablen sind
normalerweise nicht möglich (solange keine Register auf dem Stack gesichert
werden). Durch den kurzen Prolog und Epilog ist der Unterprogrammaufruf
effizienter als bei schwergewichtigen Unterprogrammen.

Beispiele:
{\footnotesize\begin{verbatim}
        MOVEA   H'10000', SP    -- Stackpointer initialisieren
        CALL    init            -- Initialisierungsroutine
        MOVE  W I 23, R0
        MOVE  W I 12, R1
        CALL    min             -- R0/R1 Parameter, R0 Ergebnis
        MOVE  W R0, minimum
        MOVE  W I 5, R0
        CALL    fakit           -- R0 Parameter, R0 Ergebnis
        MOVE  W I 7, R0
        CALL    fakrek          -- R0 Parameter, R0 Ergebnis
        HALT

init:   MOVE  W I 42, antwort   -- Ohne Parameter
        MOVE  W I 4711, koeln
        RET

min:    CMP   W R0, R1          -- R0=Min(R0,R1)
        JLT     ismin
        MOVE  W R1, R0
ismin:  RET

fakit:  MOVE  W R1,-!SP         -- R0=Fak(R0), iterativ
        MOVE  W R0,R1
floop:  SUB   W I 1,R1
        JLE     fend
        MULT  W R1,R0
        JUMP    floop
fend:   MOVE  W !SP+,R1         -- R1 restaurieren
        RET

fakrek: MOVE  W R1,-!SP         -- R0=Fak(R0), rekursiv
        SUB   W I 1, R0, R1
        JLE     fend2
        MOVE  W R0,-!SP         -- R0 retten
        MOVE  W R1, R0
        CALL    fakrek
        MULT  W !SP+, R0        -- Ergebnis ausrechnen
fend2:  MOVE  W !SP+, R1        -- R1 restaurieren
        RET
\end{verbatim}}
\vspace{-4ex}
\subsection{Schwergewichtige Unterprogramme}

Bei schwergewichtigen Unterprogrammen werden die Parameter und die
Ergebnisse auf dem Stack übergeben. Das Unterprogramm rettet im Prolog alle
Register, wodurch diese dann problemlos verwendet werden können. R12 wird
als Zeiger auf den ersten (bzw. letzten, je nach Reihenfolge) Parameter
geladen. R13 dient als Zeiger auf die lokalen Variablen (Adressierung über
negatives Displacement), sowie als Sicherheitskopie der Stackposition
(meiner Meinung nach schlechter Programmierstil, da das Stacknivau am Ende
eines Unterprogramms immer stimmen muß).

Ob die Parameter und Rückgabewerte auf dem Stack in auf- oder absteigender
Reihenfolge abgelegt werden, ist theoretisch egal, sie sollte nur durch
alle Programme hinweg konsistent sein.

Um lokale Variablen zu verwenden, wird der Stack nach dem Kopieren des
Stackpointers nach R13 nochmal um den Platzverbrauch der lokalen Variablen
dekrementiert (alternativ dazu kann man auch die Variablen durch
Push-Operationen auch gleichzeitig initialisieren).

Programmgerüst für den Aufrufer mit $n$ Parametern und $m$ Rückgabewerten:
{\footnotesize\begin{verbatim}
        CLEAR W -!SP            -- Platz für den Rueckgabewert m
        ...
        CLEAR W -!SP            -- Platz für den Rueckgabewert 1; Anstelle der m CLEAR
                                -- Anweisungen kann auch SUB W I m*4,SP verwendet werden
        MOVE  W <x>, -!SP       -- Parameter n auf den Stack
        ...
        MOVE  W <x>, -!SP       -- Parameter 1 auf den Stack
        CALL  <label>
        ADD   W I n*4, SP       -- Alle Parameter vom Stack nehmen
        MOVE  W !SP+, <y>       -- Ergebnis 1 vom Stack nehmen
        ...
        MOVE  W !SP+, <y>       -- Ergebnis m vom Stack nehmen
\end{verbatim}}

Programmgerüst für das Unterprogramm selbst:
{\footnotesize\begin{verbatim}
        PUSHR                   -- Retten aller Register
        MOVEA   64+!SP, R12     -- Zeiger auf Parameterstack
        MOVE  W SP, R13         -- Zeiger auf lokale Variablen
        MOVE  W <x>, -!SP       -- Initialisierung fuer lokale Variable l
        ...
        MOVE  W <x>, -!SP       -- Initialisierung fuer lokale Variable 1
        [...]                   -- Unterprogrammcode
        ADD   W I l*4, SP       -- Stackpegel wiederherstellen
        POPR                    -- Register wiederherstellen
        RET                     -- Ruecksprung
\end{verbatim}}

Beispiel (ohne Rekursion und lokale Variablen):
{\footnotesize\begin{verbatim}
        MOVEA   H'10000', SP    -- Stackpointer initialisieren
        SUB   W I 4, SP         -- Platz fuer Ergebnis
        MOVEA   string, -!SP    -- Stringpointer auf den Stack
        CALL    strlen          -- Unterprogramm aufrufen
        ADD   W I 4, SP         -- Parameter ueberspringen
        MOVE  W !SP+, laenge    -- Stringlaenge speichern
        HALT

strlen: PUSHR                   -- Alle Register retten
        MOVEA   64+!SP, R12     -- Zeiger auf Parameterstack
        MOVE  W !R12, R1        -- Parameter Stringpointer in R1
        MOVEN W I 1, R0         -- Stringlaenge auf -1
sloop:  ADD   W I 1, R0         -- Stringlaenge erhoehen?
        CMP   B I 0, !R1+       -- Nullbyte gefunden?
        JNE     sloop           -- Schleife
        MOVE  W R0, 4+!R12      -- Rueckgabewert ueberschreiben
        POPR                    -- Register wiederherstellen
        RET                     -- Ruecksprung
\end{verbatim}}

Beispiel Ackermannfunktion (mit Rekursion, nur Unterprogramm):
{\footnotesize\begin{verbatim}
ack:    PUSHR                   -- v = ack(m,n)
        MOVEA   64+!SP, R12     -- Zeiger auf Parameterstack
        MOVE  W SP, R13         -- Zeiger auf lokale Variablen
        SUB   W I 4, SP         -- Platz fuer eine lokale Variable
        MOVE  W 4+!R12, R2      -- n
        MOVE  W !R12, R1        -- m
        JNE     nnzero          -- m ? 0
        ADD   W I 1, R2, R0     -- ack(0,n) = n+1
        JUMP    ackend          -- fertig
nnzero: CMP   W I 0, R2
        JNE     mnzero          -- n ? 0
        CLEAR W -!SP            -- Platz fuer Ergebnis
        MOVE  W I 1,-!SP        -- 2. Parameter auf Stack (1)
        SUB   W I 1, R1, -!SP   -- 1. Parameter auf Stack (m-1)
        CALL    ack             -- ack(m-1,1) berechnen
        ADD   W I 8,SP          -- Parameter ueberspringen
        MOVE  W !SP+, R0        -- Ergebnis holen
        JUMP    ackend          -- fertig
mnzero: CLEAR W -!SP            -- Platz fuer Zwischenergebnis
        SUB   W I 1, R2, -!SP   -- 2. Parameter (n)
        MOVE  W R1, -!SP        -- 1. Parameter (m)
        CALL    ack             -- ack(m,n-1) berechnen
        ADD   W I 8, SP         -- Parameter ueberspringen
        MOVE  W !SP+, -4+!R13   -- Zwischenergebnis in lokale Variable
        CLEAR W -!SP            -- Platz fuer Ergebnis
        MOVE  W -4+!R13, -!SP   -- 2. Parameter (ack(m,n-1))
        SUB   W I 1, R1, -!SP   -- 1. Parameter (m-1)
        CALL    ack             -- ack(m-1, ack(m,n-1)) berechnen
        ADD   W I 8, SP         -- Parameter ueberspringen
        MOVE  W !SP+, R0        -- Endergebnis holen
ackend: MOVE  W R0, 8+!R12      -- Rueckgabewert ueberschreiben
        ADD   W I 4, SP         -- Lokale Variable tilgen
        POPR                    -- Register wiederherstellen
        RET                     -- Ruecksprung
\end{verbatim}}

\end{document}
