============== Aufbau der ULM ============== Die ULM besteht aus einem Speicher, einem Ein- und Ausgabegerät, die mit einem Bus an eine CPU angeschlossen ist. Der Aufbau im Groben kann wie folgt skizziert werden: ---- TIKZ ---------------------------------------------------------------------- \begin{tikzpicture}[x=1.3cm,y=0.9cm,name=main, font=\sffamily ] %% Bottom \newcommand\XBot{\XTop} % X offset middle \newcommand\YBot{0} % Y offset middle \newcommand\HBot{4.4} % Height middle boxes \newcommand\PXBot{0.4} % padding x at bottom \newcommand\PYBot{0.5} % padding y at bottom % X offset and width of boxes \newcommand\XCpu{\XBot} % CPU \newcommand\WCpu{\WBus} \newcommand\XCu{\XBot+\PXBot} % control unit \newcommand\YCu{\YBot+\PYBot} \newcommand\WCu{8} \newcommand\HCu{2.4} \newcommand\XAlu{\XCu+\WCu+\PXBot} % control unit \newcommand\YAlu{\YBot+\PYBot} \newcommand\WAlu{\WCpu-\WCu-3*\PXBot} \newcommand\HAlu{2.4} %% Middle \newcommand\XMid{\XTop} % X offset middle \newcommand\YMid{\YBot+\HBot+2} % Y offset middle \newcommand\HMid{0.1} % Height middle boxes % X offset and width of boxes \newcommand\XBus{\XMid} % bus \newcommand\WBus{\XOut+\WOut} \newcommand\XBusText{\XBus+0.4} % busText %% Top \newcommand\XTop{0} % X offset top \newcommand\YTop{\YMid+2} % Y offset top \newcommand\HTop{2} % Height top boxes \newcommand\PXTop{0.4} % padding x at top % X offset and width of boxes \newcommand\XMem{\XTop} % memory \newcommand\WMem{10} \newcommand\XIn{\XTop + \WMem + \PXTop} % input \newcommand\WIn{3.6} \newcommand\XOut{\XIn + \WIn + \PXTop} % output \newcommand\WOut{5} %% ----------------- %% At Top % RAM \draw [fill=gray!30] (\XMem,\YTop) rectangle (\XMem+\WMem,\YTop+\HTop); \draw (\XMem + \WMem/2,\YTop + \HTop/2) node {\strut Speicher}; % Input \draw [fill=gray!30] (\XIn,\YTop) rectangle (\XIn+\WIn,\YTop+\HTop); \draw (\XIn + \WIn/2,\YTop + \HTop/2) node {\strut Eingabegerät}; % Output \draw [fill=gray!30] (\XOut,\YTop) rectangle (\XOut+\WOut,\YTop+\HTop); \draw (\XOut + \WOut/2,\YTop + \HTop/2) node {\strut Ausgabegerät}; %% At Middle % BUS \fill (\XBus,\YMid) rectangle (\XBus+\WBus,\YMid+\HMid); % annotate Bus \draw (\XBusText,\YMid) node[above] {Bus}; % connect Mem with BUS \draw [<->] (\XMem + \WMem/2, \YTop) -- (\XMem + \WMem/2 , \YMid+\HMid); % connect In with BUS \draw [->] (\XIn + \WIn/2, \YTop) -- (\XIn + \WIn/2 , \YMid+\HMid); % connect Out with BUS \draw [<-] (\XOut + \WOut/2, \YTop) -- (\XOut + \WOut/2 , \YMid+\HMid); %% At Bottom \draw [fill=gray!30] (\XCpu,\YBot) rectangle (\XCpu+\WCpu,\YBot+\HBot); \draw ({\XCpu+(\WCpu)/2},\YBot + \HBot) node[below] {CPU (central processing unit)}; % connect CPU with Bus \draw [<->] ({\XCpu+(\WCpu)/2}, \YBot+\HBot) -- ({\XCpu+(\WCpu)/2}, \YMid); % control unit \draw [fill=white] (\XCu,\YCu) rectangle (\XCu+\WCu,\YCu+\HCu); \draw (\XCu + \WCu/2,\YCu + \HCu) node[below] {Steuerwerk}; \newcommand\XIp{\XCu+0.33} \newcommand\WIp{3.5} \newcommand\XIr{\XIp+\WIp+0.33} \newcommand\WIr{3.5} \draw [fill=white] (\XIp,\YCu+0.2) rectangle (\XIp + \WIp,\YCu+0.8); \draw ({\XIp+\WIp/2},\YCu+0.5) node {Befehlszeiger \%IP}; \draw [fill=white] (\XIr,\YCu+0.2) rectangle (\XIr + \WIr,\YCu+0.8); \draw ({\XIr+\WIr/2},\YCu+0.5) node {Befehlsregister \%IR}; % ALU \draw [fill=white] (\XAlu,\YAlu) rectangle (\XAlu+\WAlu,\YAlu+\HAlu); \draw ({\XAlu + (\WAlu)/2},\YAlu + \HAlu) node[below] {\strut ALU (arithmetical logical unit)}; \newcommand\XRzero{\XAlu+0.33} \newcommand\WR{2.4} \newcommand\XRdots{{(\XRzero+\WR+\XRone)/2}} \newcommand\XRone{\XRlast-\WR-0.33} \newcommand\XRlast{\XAlu+\WAlu-\WR-0.33} \draw [fill=white] (\XRzero,\YAlu+0.2) rectangle (\XRzero + \WR,\YAlu+0.8); \draw ({\XRzero+\WR/2},\YAlu+0.5) node {Register \%0x00}; \draw [fill=white] (\XRone,\YAlu+0.2) rectangle (\XRone + \WR,\YAlu+0.8); \draw ({\XRone+\WR/2},\YAlu+0.5) node {Register \%0xFF}; \draw (\XRdots,\YAlu+0.5) node {$\dots$}; \draw [fill=white] (\XRlast,\YAlu+0.2) rectangle (\XRlast + \WR,\YAlu+0.8); \draw ({\XRlast+\WR/2},\YAlu+0.5) node {Statusregister}; \end{tikzpicture} -------------------------------------------------------------------------------- Speicher ======== Der Speicher besteht aus einer Anordnung (Array) von Speicherzellen, die beginnend bei 0, fortlaufend indiziert sind. Der Index einer Speicherzelle wird als deren _Adresse_ bezeichnet. Beispielsweise kann man sich den Speicherbereich mit Adressen von 0 bis 16 wie folgt vorstellen (wobei die Adressen hier in der Hexadezimaldarstellung angegeben sind): ---- TIKZ ---------------------------------------------------------------------- \begin{tikzpicture} \input{memory.tex} \renewcommand\MemCellWidth {1.3} \DrawMemArrayOpenRight{0}{15} \DrawMemAddress{0}{0x00} \DrawMemAddress{1}{0x01} \DrawMemAddress{2}{0x02} \DrawMemAddress{3}{0x03} \DrawMemAddress{4}{0x04} \DrawMemAddress{5}{0x05} \DrawMemAddress{6}{0x06} \DrawMemAddress{7}{0x07} \DrawMemAddress{8}{0x08} \DrawMemAddress{9}{0x09} \DrawMemAddress{10}{0x0A} \DrawMemAddress{11}{0x0B} \DrawMemAddress{12}{0x0C} \DrawMemAddress{13}{0x0D} \DrawMemAddress{14}{0x0E} \DrawMemAddress{15}{0x0F} \DrawAnnotateMemCellAbove{10}{Speicherzelle mit Adresse $u(\text{0x0A}) = 10$} \end{tikzpicture} -------------------------------------------------------------------------------- In jeder Speicherzelle kann ein Byte gespeichert werden, was bei der ULM (und heutzutage allen gängigen Computer Architekturen), als eine Datenmenge von 8 Bits festgelegt ist. Der Inhalt einer Zelle kann deshalb mit zwei Hexadezimalziffern dargestellt werden, wobei auf das Präfix "0x" verzichtet werden kann, da aus dem Kontext die Zahlendarstellung klar ist: ---- TIKZ ---------------------------------------------------------------------- \begin{tikzpicture} \input{memory.tex} \renewcommand\MemCellWidth {1.3} \DrawMemArrayOpenRight{0}{15} \DrawMemAddress{0}{0x00} \DrawMemAddress{1}{0x01} \DrawMemAddress{2}{0x02} \DrawMemAddress{3}{0x03} \DrawMemAddress{4}{0x04} \DrawMemAddress{5}{0x05} \DrawMemAddress{6}{0x06} \DrawMemAddress{7}{0x07} \DrawMemAddress{8}{0x08} \DrawMemAddress{9}{0x09} \DrawMemAddress{10}{0x0A} \DrawMemAddress{11}{0x0B} \DrawMemAddress{12}{0x0C} \DrawMemAddress{13}{0x0D} \DrawMemAddress{14}{0x0E} \DrawMemAddress{15}{0x0F} \DrawMemAddress{16}{0x10} \DrawMemCellContent{0}{56} \DrawMemCellContent{1}{00} \DrawMemCellContent{2}{05} \DrawMemCellContent{3}{01} \DrawMemCellContent{4}{56} \DrawMemCellContent{5}{00} \DrawMemCellContent{6}{08} \DrawMemCellContent{7}{02} \DrawMemCellContent{8}{30} \DrawMemCellContent{9}{01} \DrawMemCellContent{10}{02} \DrawMemCellContent{11}{03} \DrawMemCellContent{12}{01} \DrawMemCellContent{13}{03} \DrawMemCellContent{14}{00} \DrawMemCellContent{15}{00} \DrawAnnotateMemCellAbove{10}{Speicherzelle mit Adresse $u(\text{0x0A})$ hat den Inhalt 0x02} \end{tikzpicture} -------------------------------------------------------------------------------- Unrealistisch bei der ULM ist die Größe des Speichers, der aus $2^{64}$ Speicherzellen besteht. Dabei werden allerdings nur technische Details unter den Teppich gekehrt. In der Realität ist das die Größe des __virtuellen Speichers__ bei einem 64-bit System, und vereinfacht ausgedrückt die Größe des Speichers aus Sicht der Programmierung. Ein- und Ausgabegeräte ======================= Das Eingabegerät kann man sich als eine Speicherzelle vorstellen deren Inhalt man nur auslesen kann (_read only_) und das Ausgabegerät als eine Speicherzelle in die man nur schreiben kann (_write only_). Gibt man auf der Tastatur ein Zeichen ein, dann kann anschließend aus dem Eingabegerät dessen __ASCII__ Wert gelesen werden. Beispielsweise beim Zeichen 'A' der Wert 65 bzw. 0x41. Schreibt man ein Byte in das Ausgabegerät, dann erscheint das zugehörige Zeichen (bei 0x41 also ein 'A'). ALU und Statusregister ====================== Die ALU (Arithmetical Logical Unit) der ULM besitzt 256 64-Bit-Register, die mit `%0x00`, `%0x01`, ..., `%0xFF` bezeichnet werden: - Die Register `%0x01`, ..., `%0xFF` sind Arbeitsregister (_general purpose register_). - Das Register `%0x00` ist ein Spezialregister (_special purpose register_), das unveränderlich ein Null-Bitmuster enthält (Schreiben ins Register wird ignoriert, Lesen liefert immer das Null-Bitmuster). Die Register der ALU sind mit einem Addierer/Subtrahierer verbunden, der nach jeder Rechenoperation die Flags ZF (Zero Flag), CF (Carry Flag), OF (Overflow Flag) und SF (Sign Flag) des Statusregisters setzt. Bus === Mit dem Bus können Daten zwischen dem Speicher und der CPU übertragen werden: - Vom Speicher kann der Inhalt von 1, 2, 4 oder 8 Speicherzellen in ein Register geladen werden. Dies wird dann als _Fetch-Operation_ bezeichnet. Da die Register der ALU 64 Bits (also 8 Bytes) speichern können muss das Bitmuster bei einer Fetch-Operation eventuell erweietert werden (dann wenn weniger als 8 Bytes geladen werden). Dafür stehen zwei Befehlsvarianten zur Verfügung, damit bei der Erweiterung der Wert einer Unsigned-Integer (hier wird mit Nullen erweitert) oder Signed-Integer (hier wird abhängig vom Vorzeichen mit Nullen oder Einsen erweitert) erhalten bleibt. - Umgekehrt können aus einem Register 1, 2, 4 oder 8 Bytes in den Speicher geschrieben werden. Dies wird als Store-Operation bezeichnet. Die Bytes, die aus dem Register kopiert werden sind dabei die Least-Significant-Bytes (LSB), also "die Bytes rechts". Da das Eingabe- und Ausgabegerät jeweils wie eine Speicherzelle betrachtet werden kann, handelt sind Ein- und Ausgabeoperationen eigentlich nur spezielle Fetch- und Storeoperationen. Steuerwerk ========== Programme bestehen aus einer Folge von Bytes und werden vom Steuerwerk ausgeführt. Voraussetzung dafür ist allerdings, dass das Programm zuvor in den Speicher geladen wurde. Zuständig ist dafür eine Komponente der ULM, die in der Abbildung oben nicht dargestellt wurde, der __Lader__. Bei eurem eigenen Computer entspricht das dem __BIOS__ oder der __Firmware__. Wird der Rechner gestartet wird von der Komponente von der Festplatte das Betriebssystem (das erste Programme das auch dem Rechner gestartet wird) geladen. Danach beginnt das Steuerwerk der CPU mit dessen Ausführung. Bereits weiter oben wurde ein Bereich des Speichers gezeigt, der ein geladenes Programm enthält: ---- TIKZ ---------------------------------------------------------------------- \begin{tikzpicture} \input{memory.tex} \renewcommand\MemCellWidth {1.3} \DrawMemArrayOpenRight{0}{15} \DrawMemAddress{0}{0x00} \DrawMemAddress{1}{0x01} \DrawMemAddress{2}{0x02} \DrawMemAddress{3}{0x03} \DrawMemAddress{4}{0x04} \DrawMemAddress{5}{0x05} \DrawMemAddress{6}{0x06} \DrawMemAddress{7}{0x07} \DrawMemAddress{8}{0x08} \DrawMemAddress{9}{0x09} \DrawMemAddress{10}{0x0A} \DrawMemAddress{11}{0x0B} \DrawMemAddress{12}{0x0C} \DrawMemAddress{13}{0x0D} \DrawMemAddress{14}{0x0E} \DrawMemAddress{15}{0x0F} \DrawMemAddress{16}{0x10} \DrawMemCellContent{0}{56} \DrawMemCellContent{1}{00} \DrawMemCellContent{2}{05} \DrawMemCellContent{3}{01} \DrawMemCellContent{4}{56} \DrawMemCellContent{5}{00} \DrawMemCellContent{6}{08} \DrawMemCellContent{7}{02} \DrawMemCellContent{8}{30} \DrawMemCellContent{9}{01} \DrawMemCellContent{10}{02} \DrawMemCellContent{11}{03} \DrawMemCellContent{12}{01} \DrawMemCellContent{13}{03} \DrawMemCellContent{14}{00} \DrawMemCellContent{15}{00} \end{tikzpicture} -------------------------------------------------------------------------------- Um das Programm "zu sehen" muss man zunächst das Befehlsformat und (zumindest teilweise) den Befehlssatz kennen. Befehlsformat ------------- Befehle für die ULM bestehen aus 32 Bits, haben aber im Prinzip das gleiche Format wie die Befehle der Mini-ALU: ---- TIKZ ---------------------------- \begin{tikzpicture} \input{memory.tex} \renewcommand\MemCellWidth {1} \DrawMemArray{0}{3} \DrawMemCellContent{0}{Op} \DrawMemCellContent{1}{X} \DrawMemCellContent{2}{Y} \DrawMemCellContent{3}{Z} \end{tikzpicture} -------------------------------------- Die Felder Op, X, Y, Y bestehen aus jeweils 8 Bits also einem Byte. Wie bei der Mini-ALU bezeichnet Op den Opcode, also den Befehlstyp und X, Y und Z werden als Operanden benutzt. Im oben abgebildeten Speicher liegen 4 Befehle: - Bei Addresse 0x00 liegt der Befehl ---- TIKZ ---------------------------- \begin{tikzpicture} \input{memory.tex} \renewcommand\MemCellWidth {1} \DrawMemArray{0}{3} \DrawMemCellContent{0}{56} \DrawMemCellContent{1}{00} \DrawMemCellContent{2}{05} \DrawMemCellContent{3}{01} \end{tikzpicture} -------------------------------------- mit Op = 0x56, X = 0x00, Y = 0x05 und Z = 0x01. Im Befehlssatz wird der Befehl mit Opcode 0x56 wie folgt beschrieben: ---- LATEX ----------------------------------------------------------- \bigl(u(\text{%00}) + u(\text{XY})\bigr) \bmod 2^{64} \to u(\text{%}Z) ---------------------------------------------------------------------- In diesem Fall bedeutet dies ---- LATEX ------------------------------------------------------------------- \bigl(u(\text{%00}) + u(\text{0x0005})\bigr) \bmod 2^{64} \to u(\text{%}0x01). ------------------------------------------------------------------------------ Da `%0x00` das spezielle Null-Register ist, gilt stets $u(\text{%00})=0$. Folglich wird mit diesem Befehl einfach der Wert 5 in das Register `%0x01` geschrieben. - Bei Addresse 0x04 liegt der Befehl ---- TIKZ ---------------------------- \begin{tikzpicture} \input{memory.tex} \renewcommand\MemCellWidth {1} \DrawMemArray{0}{3} \DrawMemCellContent{0}{56} \DrawMemCellContent{1}{00} \DrawMemCellContent{2}{08} \DrawMemCellContent{3}{02} \end{tikzpicture} -------------------------------------- mit Op = 0x56, X = 0x00, Y = 0x08 und Z = 0x02. Da der Befehl den gleichen Opcode besitzt wie der vorige Befehl leitet man leicht ab, dass damit der Wert 8 in das Register `%0x02` geschrieben wird. - Bei Addresse 0x08 der Befehl ---- TIKZ ---------------------------- \begin{tikzpicture} \input{memory.tex} \renewcommand\MemCellWidth {1} \DrawMemArray{0}{3} \DrawMemCellContent{0}{30} \DrawMemCellContent{1}{01} \DrawMemCellContent{2}{02} \DrawMemCellContent{3}{03} \end{tikzpicture} -------------------------------------- mit Op = 0x30, X = 0x01, Y = 0x02 und Z = 0x03. Im Befehlssatzt ist dieser mit ---- LATEX ------------------------------------------------------------------- \bigl(u(\text{%}Y) + u(\text{%}X)\bigr) \bmod 2^{64} \to u(\text{%}Z) ------------------------------------------------------------------------------ festgelegt. In diesem Fall als die Operation ---- LATEX ------------------------------------------------------------------- \bigl(u(\text{%}0x02) +u(\text{%}0x01)\bigr) \bmod 2^{64} \to u(\text{%}0x03). ------------------------------------------------------------------------------ Damit werden also die Register `%0x01` und `%0x02` addiert und das Ergbnis in `%0x03` geschrieben. Zusätzlich werden noch vom Addierer die Statusflags gesetzt. - Bei Addresse 0x0C der Befehl ---- TIKZ ---------------------------- \begin{tikzpicture} \input{memory.tex} \renewcommand\MemCellWidth {1} \DrawMemArray{0}{3} \DrawMemCellContent{0}{01} \DrawMemCellContent{1}{03} \DrawMemCellContent{2}{00} \DrawMemCellContent{3}{00} \end{tikzpicture} -------------------------------------- mit Op = 0x01, X = 0x03, Y = 0x00 und Z = 0x00. Der Befehl mit Obcode `0x01` ist ein _halt_ Befehl. Damit wird die Programmausführung beendet. Zusätzlich wird das LSB aus Register Z (hier Register `%0x03) als __Exit-Code__ verwendet. Damit wird nachempfunden, dass in der Praxis jedes Program mit solch einem Rückgabewert siganlisieren kann, ob die Ausführung erfolgreich war oder nicht. Bei diesem beispiel werden wird sehen, dass der Exit-Code gerade das Ergebnis einer Rechnoperation ist. Wir ersparen uns damit zu Beginn die etwas kompiziertere Ausgaben von Werten auf dem Ausgabegerät (der Zahlenwert müsste dazu zuerst in eine Folge von Ziffern konvertiert werden). Etwas heimtückisch ist, dass der Rückgabe immer ein Wert aus dem Bereich von 0 bis 255 ist (da nur das LSB des Regsiters verwendet wird). Dies ist der Tatsache geschuldet, dass die ULM nachbildet was in der Praxis gängig ist. Aus technischer Sicht späche nichts dagegen, dass alle 64 Bit für den Exit-Code verwendet werden. Nachdem geklärt wurde aus welchen Befehlen das (Mini-)Programm besteht, und was diese bei der Ausführung tun, muss noch geklärt werden in welcher Reichenfolge diese ausgeführt werden. Sequentielle Programmausführung -------------------------------- Technisch wird bei der ULM die Ausführung eines Programmes mit Hilfe von zwei Registern im Steuerwerk realisiert: - Das Register `%IP` (für _Instruction Pointer_) dient als Befehlszeiger, d.h. der Inhalt ist die Adresse an dem der Befehl steht, der als nächstes ausgeführt wird. Da es $2^{64}$ möglich Adressen gibt ist dies ein 64-Bit-Register. Nach dem Laden eines Programmes wird dieses Register mit Null initialisiert. - Das Register `%IR` (für _Instruction Register_) ist das Befehlsregister in den ein Befehl aus dem Speicher zunächst geladen wird, dann dekodiert und schliesslich ausgeführt wird. Da Befehle aus 32-Bit bestehen ist dies ein 32-Bit Register. Die sequentielle Ausführung eines Programmes kann mit folgendem Flußdiagramm beschrieben werden, das einen sogenannten __Von-Neumann-Zyklus__ beschreibt: ---- TIKZ ---------------------------------------------------------------------- \begin{tikzpicture} \input{flowchart.tex} \renewcommand\BoxWidth{17} \SetMargin{1}{0}{0}{0} \PutStatement[align=left]{0}{\;Stufe A: Lade von Adresse \%IP vier Bytes in \%IR.} \PutStatement[align=left]{1}{\;Stufe B: Dekodiere den Inhalt von \%IR.} \PutStatement[align=left]{2}{\;Stufe C: Führe den dekodierten Befehl aus.} \PutStatement[align=left]{3}{\;Stufe D: Falls der Befehl den Inhalt von \%IP nicht verändert hat: Erhöhe \%IP um 4.} \AddPath{0}{1} \AddPath{1}{2} \AddPath{2}{3} \AddJumpPath{3}{0} \end{tikzpicture} -------------------------------------------------------------------------------- In einem Durchlauf dieses Zykluses wird genau ein Befehl (stufenweise) ausgeführt. Beendet wird ein Programm, wenn in der Ausführungsstufe (Stufe C) ein Halt-Befehl ausgeführt wird. Allerdings kann die ULM auch "abstürzen", wenn ein Befehl einen nicht unterstützen Opcode enthält. Beispielsweise ist 0x00 solch ein illegaler Opcode, weshalb dieses Program mit Hurra bei der Ausführung des vierten Befehls die arme ULM zum Absturz bringt: ---- TIKZ ---------------------------------------------------------------------- \begin{tikzpicture} \input{memory.tex} \renewcommand\MemCellWidth {1.3} \DrawMemArrayOpenRight{0}{15} \DrawMemAddress{0}{0x00} \DrawMemAddress{1}{0x01} \DrawMemAddress{2}{0x02} \DrawMemAddress{3}{0x03} \DrawMemAddress{4}{0x04} \DrawMemAddress{5}{0x05} \DrawMemAddress{6}{0x06} \DrawMemAddress{7}{0x07} \DrawMemAddress{8}{0x08} \DrawMemAddress{9}{0x09} \DrawMemAddress{10}{0x0A} \DrawMemAddress{11}{0x0B} \DrawMemAddress{12}{0x0C} \DrawMemAddress{13}{0x0D} \DrawMemAddress{14}{0x0E} \DrawMemAddress{15}{0x0F} \DrawMemAddress{16}{0x10} \DrawMemCellContent{0}{56} \DrawMemCellContent{1}{00} \DrawMemCellContent{2}{05} \DrawMemCellContent{3}{01} \DrawMemCellContent{4}{56} \DrawMemCellContent{5}{00} \DrawMemCellContent{6}{08} \DrawMemCellContent{7}{02} \DrawMemCellContent{8}{30} \DrawMemCellContent{9}{01} \DrawMemCellContent{10}{02} \DrawMemCellContent{11}{03} \DrawMemCellContent{12}{00} \DrawMemCellContent{13}{03} \DrawMemCellContent{14}{00} \DrawMemCellContent{15}{00} \end{tikzpicture} -------------------------------------------------------------------------------- Mit diesem Beispiel soll deutlcih werden, dass dem Computer keineswegs bewusst ist, ob der Speicherinhalt tatsächlich einen Befehl darstellt. Der Rechner führt Programme stupide nach obigem Muster aus, er denkt nicht mit (alles andere würde sich in der Praxis kaum effizient in Hardware umsetzen lassen). Beim Programmieren muss deshalb festgelegt werden, dass immer nur als Befehle vorgesehene Speicherinhalt von der ULM bei der Ausführung geladen werden. Aufgabe für die Mini-ULM ======================== Testet beide Mini-Programme auf der __Mini-ULM__: - Das erste Programm besteht aus der Byte-Folge ---- CODE (file=session08/prog01) ------------- 56 00 05 01 56 00 08 02 30 01 02 03 01 03 00 00 ----------------------------------------------- Ihr könnt diese direkt Byte für Byte in den Speicher schreiben (also die Aufgabe des BIOS übernehmen): ---- IMAGE ------------- session08/mini-ulm02.png ------------------------ Oder zunächt im Eingabefeld ganz unten auf der Seite eingeben (bzw. obigen Code mit Copy&Paste übernehmen). Mit dem Button _Step_ könnt ihr das Programm Befehl für Befehl ausführen. Alternativ könnt ihr aber auch einen Befehl stufenweise ausführen lassen. Dann könnt ihr beobachten wie in Stufe A der Befehl geladen, in Stufe B dekodiert wird, etc. ---- IMAGE ------------- session08/mini-ulm03.png ------------------------ Nach dem vierten Befehl sollte oben dann der Exit-Code das Ergebnis der Addition anzeigen. ---- IMAGE ------------- session08/mini-ulm04.png ------------------------ - Das zweite Programm besteht aus der Byte-Folge ---- CODE (file=session08/prog02) ------------- 56 00 05 01 56 00 08 02 30 01 02 03 00 03 00 00 ----------------------------------------------- Hier solltet ihr sehen, dass die ULM beim letzten "Befehl" abbricht. Aufgabe, um zu Testen, ob ihr auf die richtige ULM zugreifen könnt ==================================================================== Die Mini-ULM wurde aus der Not heraus geboren und unterstüzt deshlab aktuell nur eine Teilmenge des Befehlssatzes. Die "richtige" ULM existiert auf den Uni-Rechnern an denen wir normalerweise gearbeitet hätten, der `theon`. Und hier wird es etwas kompliziert, deshalb testen und berichten ob es klappt oder melden, wenn es Probleme gibt. Für Windows-Benutzer: Cygwin installieren und `ssh` verwenden ------------------------------------------------------------- Um sich auf der `theon` von einem Windows_rechner einloggen zu können, empfiehlt es sich __Cygwin__ zu installieren. Wie das geht und was es dabei zu beachten gibt zeigt euch meine Kollegin Laura Burr: ---- VIDEO(type=html5) -------------------------------------------------------- http://www.mathematik.uni-ulm.de/numerik/hpc/ss20/hpc0/HPC0_cygwin_german.mp4 -------------------------------------------------------------------------------- Für MacOS-Benutzer ------------------- Bis auf einen X11-Server (der in dieser Session nicht benütigt wird) sollte eigentlich alles bereits installiert sein Für Linux-Benutzer ------------------- Hier sollte eigentlich alles bereits installiert sein. Einloggen auf `theon` und testen, ob alles klappt ------------------------------------------------- ---- VIDEO --------------------------------------------------------------------- https://www.youtube.com/embed/ILhtiL5-I5E -------------------------------------------------------------------------------- Loggt euch mit `ssh` auf `theon` ein. Gebt dazu im Terminal ---- CODE (type=txt) ----------------------------------------------------------- ssh login@theon.mathematik.uni-ulm.de -------------------------------------------------------------------------------- ein, wobei `login` mit dem Login des SLC-Accounts zu ersetzen ist. Mit einem Texteditor schreibt ihr dann das erste Programm in eine Datei `prog01`. Ihr könnt dazu einfach im Terminal ---- CODE (type=txt) ----------------------------------------------------------- nano prog01 -------------------------------------------------------------------------------- eintippen. Dann tippt ihr das Programm ---- CODE (file=session08/prog01) ------------- 56 00 05 01 56 00 08 02 30 01 02 03 01 03 00 00 ----------------------------------------------- ein. Mit Control-O kann die Datei gespeichert werden und mit Control-X der Editor verlassen werden. Anschliessend könnt ihr das Programm auf der ULM ausführen und euch den Exit-Code anzeigen lassen: ---- SHELL (path=session08) ---------------------------------------------------- ulm prog01 echo $? -------------------------------------------------------------------------------- Mit `ulm prog01` wird das Programm ohne graphischen Schnickschnack geladen und ausgeführt (im Normalfall will man ja nicht sehen wie das Programm ausgeführt wird, sondern was es tut). Da das Programm aber keine Ausgabe hat seht ihr zunächst gar nichts. Mit dem zweiten Befehl `echo $?` wird aber der Exit-Code des letzten Programmes angezeigt (das funktioniert bei Unix allgemein so). Und damit seht ihr, dass mit der ULM tatsächlich gerechnet wurde. Auf unserem Server `theon` ist die richtige ULM als Kommandozeilen-Programm installiert. Auf dem Linux-Rechner `heim` :links: virtuellen Speichers -> https://de.wikipedia.org/wiki/Virtuelle_Speicherverwaltung ASCII -> https://de.wikipedia.org/wiki/American_Standard_Code_for_Information_Interchange Lader -> https://de.wikipedia.org/wiki/Lader_(Programmierung) BIOS -> https://de.wikipedia.org/wiki/BIOS Firmware -> https://de.wikipedia.org/wiki/Firmware Exit-Code -> https://de.wikipedia.org/wiki/Return_Code Von-Neumann-Zyklus -> https://de.wikipedia.org/wiki/Von-Neumann-Zyklus Mini-ULM -> http://www.mathematik.uni-ulm.de/numerik/hpc/ss20/mathsoft/mini-ulm.html Cygwin -> https://de.wikipedia.org/wiki/Cygwin