package ckelling.baukasten;

import java.lang.*;


/**
 *	DescriptionLibrary.java
 *
 *	Haelt Beschreibungen fuer die Arbeitsschritte des
 *	Von-Neumann-Rechners und anderer applets bereit.
 *
 *	@author		Carsten Kelling
 *	@version	1.2.4, 21.12.96
 */
public class DescriptionLibrary
{
    public final static String SPEICHERZELLE	= new String("Speicherzelle");
    public final static String LINE			= new String("'line'");
	String	cell;

    public DescriptionLibrary()
    {
        System.out.println("Init DescriptionLibrary");
    }

    private String explainAssociativity(int assoc, boolean fully)
    {
        String a;

        if (fully)
            a = new String("Dieser Cache ist voll-assoziativ, also müssen alle " + assoc + " Einträge des Tag-Speichers");
        else
        {
            if (assoc == 1)
                a = new String("Dieser Cache arbeitet 'direct-mapped', d.h. nur ein Eintrag des Tag-Speichers muß");
            else
                a = new String("Dieser Cache ist " + assoc + "fach-assoziativ, es müssen also " + assoc + " Einträge des Tag-Speichers");
        }

        return new String(a + " mit dem 'Tag'-Teil der Adresse verglichen werden (siehe Aufgliederung der Adresse unter 'Wert des Adreßbusses').");
    } /* end explainAssociativity */

    private String helpText_readRam(int tagResult, int readStep, int subStep, int assoc, boolean fully, int lineSize)
    {
        String s = new String("");

        switch (readStep)
        {
            case 1:
                s = "Das Speichersystem soll einen Wert aus dem Speicher liefern. Dazu muß es 'wissen', ob dieser sich schon im Cache befindet, oder erst aus dem Hauptspeicher geholt werden muß.\n";
                s = new String(s + explainAssociativity(assoc, fully));
                break;
            case 2:
                switch (tagResult)
                {
                    case Rechner.READ_HIT:
                        s = "<b>'read hit'</b>: Der angeforderte Wert befindet sich bereits im Datenspeicher des Cache.\nDem Cache wird nun über Steuersignale mitgeteilt, daß er den Wert ausgeben soll.";
                        break;
                    case Rechner.READ_MISS_CACHED:
                        s = "<b>'read miss'</b>";
                    case Rechner.READ_MISS_NON_CACHED:
                        if (s.equals(""))
                            s = "<b>'read miss' ohne Speichern im Cache</b>";
                        s = new String(s + ": Der angeforderte Wert befindet sich noch nicht im Datenspeicher des Cache.\nDer Hauptspeicher wird nun direkt mit der Adresse verbunden und über Steuersignale angewiesen, daß er ");
                        if (lineSize == 1)
                            s = new String(s + "den Wert der durch die Adresse bezeichneten Speicherzelle ausgeben soll.");
                        else
                            s = new String(s + "die " + lineSize + " Werte der 'line', in der die Speicherzelle liegt, nacheinander ausgeben soll.");
                        break;
                    case Rechner.READ_MISS_SYNC:
                        s = "<b>'read miss sync'</b>: ";
                        switch(subStep)
                        {
                            case 1:
                                s = new String(s + "Der angeforderte Wert befindet sich noch nicht im Datenspeicher des Cache.\nKeine für das Speichern in Frage kommende " + cell + " des Caches ist mehr unbelegt, es muß also eine verdrängt werden. Die Verdrängung wird besonders aufwendig, weil dieser Cache mit 'write back' arbeitet und eine " + cell + "ausgewählt wurde, die 'dirty' ist, d.h. sie enthält ");
if (lineSize == 1)
    s = new String(s + "einen gültigen Wert, die entsprechende Zelle im Hauptspeicher aber enthält noch einen alten, ungültigen Wert.");
else
    s = new String(s + "gültige Werte, die entsprechenden Zellen im Hauptspeicher aber enthalten noch alte, ungültige Werte.");
s = new String(s + "\nDiese " + cell + " muß also erst im Hauptspeicher gesichert werden, bevor sie für neue Werte genutzt werden kann (Cache und Hauptspeicher werden 'synchronisiert'). Dazu ");
if (lineSize == 1)
    s = new String(s + "wird zunächst die Adresse, zu der der Wert gehört, generiert und auf den Adreßbus geschaltet. Der Wert der Speicherzelle wird auf den Datenbus geschaltet und der Hauptspeicher wird durch Steuersignale angewiesen, den Wert abzuspeichern.");
else
    s = new String(s + "werden nacheinander die " + lineSize + " Adressen, zu denen die Werte der 'line' gehören, generiert und auf den Adreßbus geschaltet. Gleichzeitig wird jeweils der entsprechende Wert auf den Datenbus geschaltet und der Hauptspeicher über Steuersignale angewiesen, den Wert abzuspeichern.");
break;
                            case 2:
                                s = new String(s + "Die " + cell + " wird in den Hauptspeicher geschrieben. Der Datenspeicher des Cache kann nun neue Werte aufnehmen.");
                                break;
                            case 3:
                                s = new String(s + "Erst jetzt kann der gewünschte Wert geladen werden.\n Dazu wird der Hauptspeicher nun direkt mit der Adresse verbunden und über Steuersignale angewiesen, daß er ");
                        if (lineSize == 1)
                            s = new String(s + "den Wert der durch die Adresse bezeichneten Speicherzelle ausgeben soll.");
                        else
                            s = new String(s + "die " + lineSize + " Werte der 'line', in der die durch die Adresse bezeichnete Speicherzelle liegt, nacheinander ausgeben soll.");
                                break;
                        }
                }
                break;
            case 3:
                switch (tagResult)
                {
                    case Rechner.READ_HIT:
                        s = "<b>'read hit'</b>: Der Datenspeicher des Cache treibt den Datenbus mit dem angeforderten Wert.";
                        break;
                    case Rechner.READ_MISS_CACHED:
                    case Rechner.READ_MISS_SYNC:
                        s = "<b>'read miss'</b>: Der Hauptspeicher treibt den Datenbus ";
if (lineSize == 1)
    s = new String(s + "mit dem angeforderten Wert.\nDer Datenspeicher des Cache übernimmt diesen Wert in eine passende Speicherzelle. Im Tag-Speicher des Cache wird vermerkt, daß diese Speicherzelle nun einen gültigen Wert enthält und zu welcher Adresse dieser gehört (gespeichert wird der Tag-Anteil der Adresse, siehe Aufgliederung unter 'Wert des Adreßbusses').");
else
    s = new String(s + "nacheinander mit den Werten aus " + lineSize + " aufeinanderfolgenden Speicherzellen.\nDer Datenspeicher des Cache übernimmt diese Werte in eine 'line'. Im Tag-Speicher des Cache wird vermerkt, daß diese 'line' nun gültige Werte enthält und zu welchen (aufeinanderfolgenden) Adressen diese gehören (gespeichert wird der Tag-Anteil der Adresse, siehe Aufgliederung unter 'Wert des Adreßbusses').");
                        break;
                    case Rechner.READ_MISS_NON_CACHED:
s = "<b>'read miss' ohne Speichern im Cache</b>: Der Hauptspeicher treibt den Datenbus ";
if (lineSize == 1)
    s = new String(s + "mit dem angeforderten Wert.\nDieser Wert soll nicht im Cache abgelegt werden, weil er in einer 'non-cacheable area' liegt. Solche Adressenbereiche stellen die Konsistenz der Werte im Cache sicher, wenn auch andere Geräte in diese Bereiche schreiben können (z.B. ein unabhängig von der CPU arbeitender Festplatten-Controller).");
else
    s = new String(s + "nacheinander mit den Werten aus " + lineSize + " aufeinanderfolgenden Speicherzellen.\nDiese Werte sollen nicht im Cache abgelegt werden, weil mindestens einer von ihnen in einer 'non-cacheable area' liegt. Solche Adressenbereiche stellen die Konsistenz der Werte im Cache sicher, wenn auch andere Geräte in diese Bereiche schreiben können (z.B. ein unabhängig von der CPU arbeitender Festplatten-Controller).");
break;
}
                break;
        }

        return s;
    } /* end helpText_readRam */


    private String helpText_writeRam(int tagResult, int writeStep, int subStep, int assoc, boolean fully, int lineSize)
    {
        String s = new String("");

        switch (writeStep)
        {
            case 1:
                s = "Das Speichersystem soll einen Wert an einer bestimmten Adresse im Speicher ablegen.";
                s = new String(s + explainAssociativity(assoc, fully) );
                break;
            case 2:
                switch (tagResult)
                {
                    case Rechner.WRITE_THROUGH:
                        s = "'<b>write through'</b>: Dieser Cache schreibt Werte, egal ob schon im Cache, egal ob geändert oder nicht, immer in den Hauptspeicher. Weil dieser langsamer als der Datenspeicher des Cache arbeitet, kann letzterer ohne ebenfalls den neuen Wert aufnehmen, ohne daß der gesamte Speichervorgang länger dauert.\nSowohl der Hauptspeicher, als auch der Datenspeicher des Cache werden also über den Datenbus mit dem abzuspeichernden Wert und über den Adreßbus mit der zugehörigen Adresse verbunden.";
                        break;
                    case Rechner.WRITE_THROUGH_AROUND:
                        s = "'<b>write through' ohne Speichern im Cache</b>: Dieser Cache arbeitet 'write through', d.h. daß neue Werte immer in den Hauptspeicher geschrieben werden; außerdem werden immer die Werte von " + lineSize + " aufeinanderfolgenden Speicherzellen in einer 'line' gespeichert.\nEs soll der Wert zu einer bestimmten Adresse im Cache abgelegt werden. Der alte Wert zu dieser Adresse befindet sich noch nicht im Cache, folglich befinden sich auch die Werte, die mit ihm in eine 'line' gehören, nicht im Cache. Um den Wert im Datenspeicher des Cache zu speichern, müßten zusätzlich also die Werte aus seiner 'line' gelesen werden. Dieser zusätzliche Aufwand wird vermieden, indem der Cache beim Schreiben umgangen wird ('write around').\nNur der Hauptspeicher wird über den Datenbus mit dem abzuspeichernden Wert und über den Adreßbus mit der zugehörigen Adresse verbunden.";
                        break;
                    case Rechner.WRITE_BACK_HIT:
                        s = "'<b>write back - hit'</b>: Der Cache enthält bereits einen alten Wert, der zu der Adresse gehört ('hit'). Dieser Wert kann einfach überschrieben werden; im Hauptspeicher wird der Wert nicht überschrieben - der Cache arbeitet 'write back'.\nNur der Datenspeicher des Cache wird über den Datenbus mit dem abzuspeichernden Wert und über den Adreßbus mit der zugehörigen Adresse verbunden.";
                        break;
                    case Rechner.WRITE_BACK_MISS_CLEAN:
                        s = "'<b>write back - miss, clean'</b>: Der Cache enthält noch keinen alten Wert, der zu der Adresse gehört ('miss'). Also muß eine bereits anderweitig belegte " + cell + " benutzt werden. Hierzu wurde eine " + cell + " ausgewählt, die 'clean' ist, d.h. ihr Inhalt ist identisch mit ";
if (lineSize == 1)
    s = new String(s + "dem der entsprechenden Speicherzelle im Hauptspeicher. Sie kann");
else
    s = new String(s + "denen der entsprechenden Speicherzellen im Hauptspeicher. Die Speicherzellen dieser 'line' können");
s = new String(s + " also ohne vorherigen Hauptspeicherzugriff überschrieben werden.\nNur der Datenspeicher des Cache wird über den Datenbus mit dem abzuspeichernden Wert und über den Adreßbus mit der zugehörigen Adresse verbunden.");
                        break;
                    case Rechner.WRITE_BACK_AROUND:
                        s = "'<b>write back' ohne Speichern im Cache</b>: Dieser Cache speichert immer die Werte von " + lineSize + " aufeinanderfolgenden Speicherzellen in einer 'line'.\nEs soll der Wert zu einer bestimmten Adresse im Cache abgelegt werden. Der alte Wert zu dieser Adresse befindet sich noch nicht im Cache, folglich befinden sich auch die Werte, die mit ihm in eine 'line' gehören, nicht im Cache. Um den Wert im Datenspeicher des Cache zu speichern, müßten zusätzlich also die Werte aus seiner 'line' gelesen werden. Dieser zusätzliche Aufwand wird vermieden, indem der Cache beim Schreiben umgangen wird ('write around').\nDiese Designentscheidung ist für einen 'write back'-Cache ungewöhnlich, üblich ist die Wahl von 'write sync', wobei tatsächlich erst die gesamte neue 'line' aus dem Hauptspeicher gelesen wird und natürlich vorher eine alte 'line' gesichert werden muß, wenn sie 'dirty' ist.\nNur der Hauptspeicher wird über den Datenbus mit dem abzuspeichernden Wert und über den Adreßbus mit der zugehörigen Adresse verbunden";
                        break;
                    case Rechner.WRITE_BACK_MISS_DIRTY:
                        s = "'<b>write back - miss, dirty'</b>: Dies ist der ungünstigste Fall für einen 'write back'-Cache: Der Cache enthält noch keinen alten Wert, der zu der Adresse gehört ('miss'). Also muß eine bereits anderweitig belegte " + cell + " benutzt werden. Hierzu wurde eine " + cell + " ausgewählt, die 'dirty' ist, d.h. ihr Inhalt ist aktueller als ";
if (lineSize == 1)
    s = new String(s + "der der entsprechenden Speicherzelle im Hauptspeicher. Sie muß");
else
    s = new String(s + "der der entsprechenden Speicherzellen im Hauptspeicher. Die Speicherzellen dieser 'line' müssen");
s = new String(s + " erst in den Hauptspeicher geschrieben werden, bevor die " + cell + " erneut genutzt werden kann.\nZunächst treibt also der Datenspeicher des Cache den Datenbus und der Tag-Speicher des Cache den Adreßbus.");
                        break;
                }
                break;
            case 3:
                switch (tagResult)
                {
                    case Rechner.WRITE_THROUGH:
                        s = "'<b>write through'</b>: Der Hauptspeicher und der Datenspeicher des Cache übernehmen diesen Wert in eine passende Speicherzelle. Im Tag-Speicher des Cache wird vermerkt, daß diese Speicherzelle nun einen gültigen Wert enthält und zu welcher Adresse dieser gehört (gespeichert wird der Tag-Anteil der Adresse, siehe Aufgliederung unter 'Wert des Adreßbusses').";
                        break;
                    case Rechner.WRITE_THROUGH_AROUND:
                        s = "'<b>write through' ohne Speichern im Cache</b>: Der Hauptspeicher übernimmt den neuen Wert. Es gibt keine Änderungen im Cache.";
                        break;
                    case Rechner.WRITE_BACK_HIT:
                        s = "'<b>write back - hit'</b>: Der Datenspeicher des Cache übernimmt den neuen Wert in die gleiche Speicherzelle, in der der alte schon gespeichert war. Im Tag-Speicher des Cache wird vermerkt, daß der Wert dieser Speicherzelle 'dirty' ist, weil er sich von dem entsprechenden Wert im Hauptspeicher unterscheidet; der Hauptspeicher enthält einen veralteten und somit ungültigen Wert.";
                        break;
                    case Rechner.WRITE_BACK_MISS_CLEAN:
                        s = "'<b>write back - miss, clean'</b>: Der Datenspeicher des Cache übernimmt diesen Wert in eine passende Speicherzelle. Im Tag-Speicher des Cache wird vermerkt, daß diese Speicherzelle nun einen gültigen Wert enthält und zu welcher Adresse dieser gehört (gespeichert wird der Tag-Anteil der Adresse, siehe Aufgliederung unter 'Wert des Adreßbusses'); außerdem wird das 'dirty'-Bit gesetzt, weil der Wert der entsprechenden Speicherzelle im Hauptspeicher veraltet und somit ungültig ist.";
                        break;
                    case Rechner.WRITE_BACK_AROUND:
                        s = "'<b>write back' ohne Speichern im Cache</b>: Der Hauptspeicher übernimmt den neuen Wert. Es gibt keine Änderungen im Cache.";
                        break;
                    case Rechner.WRITE_BACK_MISS_DIRTY:
                        s = "'<b>write back - miss, dirty'</b>: ";
switch(subStep)
{
    case 1:
        s = new String(s + "Der Hauptspeicher übernimmt den Wert der zu 'rettenden' Speicherzelle des Datenspeichers des Cache; sie kann nun für einen neuen Wert benutzt werden.");
        break;
    case 2:
        s = new String(s + "Der Adreßbus wird wieder von der ursprünglichen Quelle getrieben. Erst jetzt kann der Datenbus mit dem eigentlich zu speichernden Wert getrieben werden.");
        break;
    case 3:
        s = new String(s + "Der Datenspeicher des Cache übernimmt diesen Wert in eine passende Speicherzelle. Im Tag-Speicher des Cache wird vermerkt, daß diese Speicherzelle nun einen gültigen Wert enthält und zu welcher Adresse dieser gehört (gespeichert wird der Tag-Anteil der Adresse, siehe Aufgliederung unter 'Wert des Adreßbusses'); außerdem wird das 'dirty'-Bit gesetzt, weil der Wert der entsprechenden Speicherzelle im Hauptspeicher veraltet und somit ungültig ist.");
}
                        break;
                }
}

        return s;
    } /* end helpText_writeRam */


    public String helpText_Speicherhierarchie(int c_state)
    // liefert nur "LDA absol. (load accumulator...)" etc.
    {
    	return helpText_Speicherhierarchie(c_state, -2, 0, 0, 1, true, 1);
    }

    public String helpText_Speicherhierarchie(int c_state, int demonstrationStep, int subStep, int tagResult, int assoc, boolean fully, int lineSize)
    {
        String t = new String("");
        String s = new String("Leider ist zu diesem Schritt noch keine Hilfe verfügbar.");
        int maxStep = -1;

        if (lineSize == 1)
            cell = SPEICHERZELLE;
        else
            cell = LINE;

        switch (c_state)
        {
            case Rechner.FETCH:
            case Rechner.FETCH_DECODE:
                t = "FETCH - Holen eines neuen Befehlswortes";
                maxStep = 5;
                switch (demonstrationStep)
                {
                    case 1:
                        s = "Das Speichersystem wird mit dem Wert des Programmzählers adressiert.";
                        break;
                    case 2:
                    case 3:
                    case 4:
                        s = helpText_readRam(tagResult, demonstrationStep - 1, subStep, assoc, fully, lineSize);
                        break;
                    case 5:
                        s = "Der Wert des Datenbusses, also das neue Befehlswort, wird ";
                        if (lineSize > 1)
                        	s = new String(s + "im richtigen Moment ");
                        s = new String(s + "im Befehlsregister gepuffert. Der Datenbus wird dadurch frei für andere Transaktionen während der kommenden Befehlsausführung.");
                        break;
                    case 99:
                        s = "\nDas Holen des neuen Befehlswortes aus dem Speicher ist abgeschlossen.";
                        if (c_state == Rechner.FETCH_DECODE)
                            s = new String(s + "\n\nDie DECODE-Phase wird nicht angezeigt.");
                }
                break;
            case Rechner.LDA_MEM:
                t = "LDA mem ('load accumulator from memory')";
                maxStep = 5;
                switch (demonstrationStep)
                {
                    case 1:
                        s = "Das Allzweckregister Akkumulator soll mit dem Wert aus der angegebenen Speicherzelle beschrieben ('geladen') werden. Dazu wird zunächst das Speichersystem mit der rechten Hälfte des Befehlswortes addressiert.";
                        break;
                    case 2:
                    case 3:
                    case 4:
                        s = helpText_readRam(tagResult, demonstrationStep - 1, subStep, assoc, fully, lineSize);
                        break;
                    case 5:
                        s = "Der Akkumulator übernimmt ";
                        if (lineSize > 1)
                        	s = new String(s + "im richtigen Moment ");
                        s = new String(s + "den Wert des Datenbusses, also der geforderten Speicherzelle. Dieser Wert kann nun für eine Berechnung verwendet und anschließend in einer anderen Speicherzelle abgelegt werden; fällt der Rechenschritt weg, wird der Wert im Speicher kopiert.");
                        break;
                    case 99:
                        s = "\nDer Ladebefehl LDA mem ist abgeschlossen.";
                }
                break;
            case Rechner.LDA_MEM_INDIR:
                t = "LDA (mem) ('load accumulator from memory indirectly')";
                maxStep = 10;
                switch (demonstrationStep)
                {
                    case 1:
                        s = "Das Allzweckregister Akkumulator soll mit einem Wert aus dem Speicher beschrieben werden. Dieser ergibt sich so: Der rechte Teil des Befehlswortes verweist (wie bei LDA mem) auf eine Speicherzelle. Deren Wert wird geholt und im Hilfsregister abgelegt. Dieser Wert ist aber noch nicht der gesuchte, sondern verweist auf eine zweite Speicherzelle. Erst deren Wert wird in den Akkumulator geladen. Zunächst wird also das Speichersystem mit der rechten Hälfte des Befehlsregisters adressiert.";
                        break;
                    case 2:
                    case 3:
                    case 4:
                        s = helpText_readRam(tagResult, demonstrationStep - 1, subStep, assoc, fully, lineSize);
                        break;
                    case 5:
                        s = "Der Wert auf dem Datenbus ist noch nicht der endgültige Wert, sondern wird im Hilfsregister abgelegt.";
                        break;
                    case 6:
                        s = "Das Speichersystem wird ein zweites Mal adressiert, nun mit dem Wert des Hilfsregisters.";
                        break;
                    case 7:
                    case 8:
                    case 9:
                        s = helpText_readRam(tagResult, demonstrationStep - 6, subStep, assoc, fully, lineSize);
                        break;
                    case 10:
                        s = "Erst dieser Wert wird im Akkumulator gespeichert. \nBefehle mit indirekter Adressierung wie LDA (mem) erlauben die Arbeit mit Zeigern in höheren Programmiersprachen.";
                        break;
                    case 99:
                        s = "\nDer Ladebefehl LDA (mem) ist abgeschlossen.";
                }
                break;
            case Rechner.STA_ABSOL:
                t = "STA absol. ('store accumulator in memory absolutely')";
                maxStep = 4;
                switch (demonstrationStep)
                {
                    case 1:
                        s = "Das Allzweckregister Akkumulator wird mit diesem Befehl in den Speicher geschrieben. Die Adresse, an der dieses geschehen soll, steht schon in der rechten Hälfte des Befehlswortes, mit welcher also zunächst das Speichersystem adressiert wird.";
                        break;
                    case 2:
                    case 3:
                    case 4:
                        s = helpText_writeRam(tagResult, demonstrationStep - 1, subStep, assoc, fully, lineSize);
                        break;
                    case 99:
                        s = "\nDer Speicherbefehl STA absol. ist abgeschlossen.";
                }
                break;
            case Rechner.STA_MEM:
                t = "STA mem ('store accumulator in memory')";
                maxStep = 11;
                switch (demonstrationStep)
                {
                    case 1:
                        s = "Der Akkumulator soll in einer Speicherzelle abgelegt werden. Deren Adresse (A) ist aber nicht im Befehlswort enthalten, sondern in einer Speicherzelle abgelegt. Erst die Adresse (B) dieser zweiten Speicherzelle ist im Befehlswort enthalten.\n";
                        s = new String(s + "Zuerst muß also das Speichersystem mit (B) aus dem Befehlswort adressiert werden.");
                        break;
                    case 2:
                    case 3:
                    case 4:
                        s = helpText_readRam(tagResult, demonstrationStep - 1, subStep, assoc, fully, lineSize);
                        break;
                    case 5:
                        s = "Das Speichersystem liefert den zur Adresse (B) gehörigen Wert; dieses ist die Adresse (A).\nSie wird im Hilfsregister abgelegt.";
                        break;
                    case 6:
                        s = "Nun kann das Speichersystem mit Adresse (A) adressiert werden. Sobald das Speichersystem dann bereit zum Schreiben ist, kann er den Wert des Akkumulators aufnehmen.";
                        break;
                    case 7:
                    case 8:
                    case 9:
s = helpText_writeRam(tagResult, demonstrationStep - 6, subStep, assoc, fully, lineSize);
                        break;
                    case 99:
                        s = "\nDer Speicherbefehl STA mem ist abgeschlossen.";
                }
                break;
            case Rechner.ADD_MEM:
                t = "ADD mem - 'Akku := Akku + &<Wert aus Speicher>'";
            case Rechner.SUB_MEM:
                if (t.equals(""))
                    t = "SUB mem - 'Akku := Akku - &<Wert aus Speicher>'";
            case Rechner.MUL_MEM:
                if (t.equals(""))
                    t = "MUL mem - 'Akku := Akku * &<Wert aus Speicher>'";
            case Rechner.DIV_MEM:
                if (t.equals(""))
                    t = "DIV mem - 'Akku := Akku / &<Wert aus Speicher>'";
                maxStep = 7;
                switch (demonstrationStep)
                {
                    case 1:
        s = "Ein Rechenbefehl mit zwei Operanden soll ausgeführt werden. Das Ergebnis wird immer im Allzweckregister Akkumulator gespeichert; einer der Operanden ist ebenfalls immer der Akkumulator. Der zweite Operand wird über den Datenbus vom Speichersystem geliefert, es soll also ein Wert aus dem Speicher gelesen werden.\nDazu wird zunächst der Adreßbus mit der rechten Hälfte des Befehlsregisters getrieben.";
        break;
                    case 2:
                    case 3:
                    case 4:
                        s = helpText_readRam(tagResult, demonstrationStep - 1, subStep, assoc, fully, lineSize);
        if (demonstrationStep == 4)
            s = new String(s + "\nZusätzlich wird jetzt der Wert des Akkumulators auf den linken Eingang der ALU geschaltet.");
                        break;
                    case 5:
        s = "Die ALU führt die eigentliche Berechnung durch; diese dauert je nach Befehl unterschiedlich lange.";
                        break;
        case 6:
            s = "Der berechnete Wert wird auf den Bus am Ausgang der ALU geschaltet.";
                        break;
                    case 7:
s = "Der Akkumulator übernimmt den berechneten Wert.";
                        break;
                    case 99:
                        s = "\nDer Rechenbefehl mit zwei Operanden ist abgeschlossen.";
                }
                break;
            case Rechner.JMP_MEM:
                t = "JMP mem ('jump memory') - unbedingter Sprung";
                maxStep = 5;
                switch (demonstrationStep)
                {
                    case 1:
s = "Das Programm, welches der Rechner ausführt, soll zu einer bestimmten Adresse (A) verzweigen, statt den nächsten Befehl immer von der nächsthöheren Adresse zu holen. Dazu muß der Programmzähler mit dem Wert (A) aus dem Speicher beschrieben werden. Die Adresse, unter der (A) zu finden ist, steht direkt in der rechten Hälfte des Befehlswortes, mit der also zunächst der Adreßbus getrieben wird.";
                        break;
                    case 2:
                    case 3:
                    case 4:
                        s = helpText_readRam(tagResult, demonstrationStep - 1, subStep, assoc, fully, lineSize);
                        break;
                    case 5:
s = "Der Programmzähler übernimmt den neuen Wert; der nächste Befehl wird von dieser Adresse geholt werden.";
                        break;
                    case 99:
                        s = "\nDer unbedingte Sprungbefehl JMP mem ist abgeschlossen.";
                }
                break;
            case Rechner.JZE_MEM:
                t = "JZE mem</b> ('<b>J</b>ump if <b>ZE</b>ro')<b> - bedingter Sprung";
                maxStep = 5;
                switch (demonstrationStep)
                {
                    case 1:
s = "Die Sprungbedingung dieses bedingten Sprungs ist erfüllt, weil die letzte Berechnung der ALU 0 ergeben hat.\nDas Programm, welches der Rechner ausführt, soll also zu einer bestimmten Adresse (A) verzweigen, statt den nächsten Befehl von der nächsthöheren Adresse zu holen. Dazu muß der Programmzähler mit dem Wert (A) aus dem Speicher beschrieben werden. Die Adresse, unter der (A) zu finden ist, steht direkt in der rechten Hälfte des Befehlswortes, mit der also zunächst der Adreßbus getrieben wird.";
                        break;
                    case 2:
                    case 3:
                    case 4:
                        s = helpText_readRam(tagResult, demonstrationStep - 1, subStep, assoc, fully, lineSize);
                        break;
                    case 5:
s = "Der Programmzähler übernimmt den neuen Wert; der nächste Befehl wird von dieser Adresse geholt werden.";
                        break;
                    case 99:
                        s = "\nDer bedingte Sprungbefehl JZE mem ist abgeschlossen.";
                }
                break;
            case Rechner.JNZ_MEM:
                t = "JNZ mem</b> ('<b>J</b>ump if <b>N</b>ot <b>Z</b>ero')<b> - bedingter Sprung";
                maxStep = 5;
                switch (demonstrationStep)
                {
                    case 1:
s = "Die Sprungbedingung dieses bedingten Sprungs ist erfüllt, weil die letzte Berechnung der ALU nicht 0 ergeben hat.\nDas Programm, welches der Rechner ausführt, soll also zu einer bestimmten Adresse (A) verzweigen, statt den nächsten Befehl von der nächsthöheren Adresse zu holen. Dazu muß der Programmzähler mit dem Wert (A) aus dem Speicher beschrieben werden. Die Adresse, unter der (A) zu finden ist, steht direkt in der rechten Hälfte des Befehlswortes, mit der also zunächst der Adreßbus getrieben wird.";
                        break;
                    case 2:
                    case 3:
                    case 4:
                        s = helpText_readRam(tagResult, demonstrationStep - 1, subStep, assoc, fully, lineSize);
                        break;
                    case 5:
s = "Der Programmzähler übernimmt den neuen Wert; der nächste Befehl wird von dieser Adresse geholt werden.";
                        break;
                    case 99:
                        s = "\nDer bedingte Sprungbefehl JNZ mem ist abgeschlossen.";
                }
                break;
            case Rechner.JLE_MEM:
                t = "JLE mem</b> ('<b>J</b>ump if <b>L</b>ess or <b>E</b>qual')<b> - bedingter Sprung";
                maxStep = 5;
                switch (demonstrationStep)
                {
                    case 1:
s = "Die Sprungbedingung dieses bedingten Sprungs ist erfüllt, weil die letzte Berechnung der ALU einen Wert kleiner oder gleich 0 ergeben hat.\nDas Programm, welches der Rechner ausführt, soll also zu einer bestimmten Adresse (A) verzweigen, statt den nächsten Befehl von der nächsthöheren Adresse zu holen. Dazu muß der Programmzähler mit dem Wert (A) aus dem Speicher beschrieben werden. Die Adresse, unter der (A) zu finden ist, steht direkt in der rechten Hälfte des Befehlswortes, mit der also zunächst der Adreßbus getrieben wird.";
                        break;
                    case 2:
                    case 3:
                    case 4:
                        s = helpText_readRam(tagResult, demonstrationStep - 1, subStep, assoc, fully, lineSize);
                        break;
                    case 5:
s = "Der Programmzähler übernimmt den neuen Wert; der nächste Befehl wird von dieser Adresse geholt werden.";
                        break;
                    case 99:
                        s = "\nDer bedingte Sprungbefehl JLE mem ist abgeschlossen.";
                }
                break;
            case Rechner.IN_MEM:
                t = "IN mem - Daten von Peripheriegerät empfangen";
                maxStep = 6;
                switch (demonstrationStep)
                {
                    case 1:
s = "Dieser Befehl holt einen (zufällig erzeugten) Wert von einem Gerät außerhalb des Rechners ab und speichert ihn in einer Speicherzelle. Welche Speicherzelle dies sein soll, steht in der rechten Hälfte des Befehlsregisters.\nMit dieser wird zunächst der Adreßbus getrieben; anschließend wird in den Speicher geschrieben werden, mit dem Peripheriegerät als Datenquelle.";
                        break;
                    case 2:
                    case 3:
                    case 4:
                        s = helpText_writeRam(tagResult, demonstrationStep - 1, subStep, assoc, fully, lineSize);
break;
                    case 99:
                        s = "\nDer Ein-/Ausgabebefehl IN mem ist abgeschlossen.";
                }
                break;
            case Rechner.OUT_MEM:
                t = "OUT mem - Daten an Peripheriegerät senden";
                maxStep = 4;
                switch (demonstrationStep)
                {
                    case 1:
s = "Dieser Befehl sendet einen Wert aus dem Speicher an ein Gerät außerhalb des Rechners. Es soll also aus dem Speicher gelesen werden, mit dem Peripheriegerät als Datensenke. Die Adresse, von der gelesen werden soll, steht direkt in der rechten Hälfte des Befehlsregisters und wird nun zunächst auf den Adreßbus geschaltet.";
                        break;
                    case 2:
                    case 3:
                    case 4:
                        s = helpText_readRam(tagResult, demonstrationStep - 1, subStep, assoc, fully, lineSize);
if (demonstrationStep == 4)
    s = new String(s + "\nDas Peripheriegerät kann den Wert nun übernehmen.");
                        break;
                    case 99:
                        s = "\nDer Ein-/Ausgabebefehl OUT mem ist abgeschlossen.";
                }
                break;
            default:
                s = helpText_Von_Neumann_Rechner(c_state, demonstrationStep);
                demonstrationStep = 99;
        }

        if (demonstrationStep == 99)
        	return s;
        else if (demonstrationStep == -2)
        	return t;
        else
        {
            String n = new String(Integer.toString(demonstrationStep, 10));
            if (subStep == 1)
                n = new String(n + "a");
            else if (subStep == 2)
                n = new String(n + "b");
            else if (subStep == 3)
                n = new String(n + "c");
            t = new String("<b>" + t + " </b>(Schritt " + n + " von " + Integer.toString(maxStep, 10) + ")\n");
            return new String(t + s);
        }
    } /* end helpText_Speicherhierarchie */


    public String helpText_Von_Neumann_Rechner(int c_state)
    // liefert nur "LDA absol. (load accumulator...)" etc.
    {
    	return helpText_Von_Neumann_Rechner(c_state, -2);
    }

    public String helpText_Von_Neumann_Rechner(int c_state, int demonstrationStep)
    {
        String t = new String("");
        String s = new String("Leider ist zu diesem Schritt noch keine Hilfe verfügbar.");
        int    maxStep = -1;

        switch (c_state)
        {
            case Rechner.FETCH:
            case Rechner.FETCH_DECODE:
                t = "FETCH - Holen eines neuen Befehlswortes";
                maxStep = 4;
                switch (demonstrationStep)
                {
                    case 1:
                        s =  "Der Speicher wird mit dem Wert des Programmzählers adressiert.";
                        break;
                    case 2:
                        s = "Dem Speicher wird über Steuersignale mitgeteilt, daß er den Wert der adressierten Speicherzelle ausgeben soll.";
                        break;
                    case 3:
                        s = "Der Speicher treibt den Datenbus mit dem verlangten Wert.";
                        break;
                    case 4:
                        s = "Der Wert des Datenbusses, also das neue Befehlswort, wird im Befehlsregister gepuffert. Der Datenbus wird dadurch frei für andere Transaktionen während der kommenden Befehlsausführung.";
                        break;
                    case 99:
                        s = "\nDas Holen des neuen Befehlswortes aus dem Speicher ist abgeschlossen.";
                        if (c_state == Rechner.FETCH_DECODE)
                            s = new String(s + "\n\nDie DECODE-Phase wird nicht angezeigt.");
                }
                break;
            case Rechner.DECODE:
                t = "DECODE - Dekodieren des neuen Befehlswortes";
                maxStep = 1;
                switch (demonstrationStep)
                {
                    case 1:
                        s = "Im Steuerwerk wird 'entschieden', in welche Folge von 'Mikrobefehlen' der neue Befehl zu zerlegen ist.\nParallel dazu wird jetzt schon der Programmzähler um Eins erhöht. ";
                        s = new String(s + "In diesem Beispiel kann sich der Programmzähler selbständig inkrementieren; für das Erhöhen um Eins könnte aber auch die ALU benutzt werden, die in dieser Phase ansonsten nicht benötigt wird (spart Logik, erfordert eine zusätzliche Leitung).");
                        break;
                    case 99:
                        s = "\nDas Dekodieren des neuen Befehlswortes ist abgeschlossen.";
                }
                break;
            case Rechner.LDA_MEM:
                t = "LDA mem ('load accumulator from memory')";
                maxStep = 4;
                switch (demonstrationStep)
                {
                    case 1:
                        s = "Das Allzweckregister Akkumulator soll mit dem Wert aus der angegebenen Speicherzelle beschrieben ('geladen') werden. Dazu wird zunächst der Speicher mit der rechten Hälfte des Befehlswortes addressiert.";
                        break;
                    case 2:
                        s = "Dem Speicher wird über Steuersignale mitgeteilt, daß er den Wert der adressierten Speicherzelle ausgeben soll.";
                        break;
                    case 3:
                        s = "Der Speicher treibt den Datenbus mit dem verlangten Wert.";
                        break;
                    case 4:
                        s = "Der Akkumulator übernimmt den Wert des Datenbusses, also der geforderten Speicherzelle. Dieser Wert kann nun für eine Berechnung verwendet und anschließend in einer anderen Speicherzelle abgelegt werden; fällt der Rechenschritt weg, wird der Wert im Speicher kopiert.";
                        break;
                    case 99:
                        s = "\nDer Ladebefehl LDA mem ist abgeschlossen.";
                }
                break;
            case Rechner.LDA_MEM_INDIR:
                t = "LDA (mem) ('load accumulator from memory indirectly')";
                maxStep = 8;
                switch (demonstrationStep)
                {
                    case 1:
                        s = "Das Allzweckregister Akkumulator soll mit einem Wert aus dem Speicher beschrieben werden. Dieser ergibt sich so: Der rechte Teil des Befehlswortes verweist (wie bei LDA mem) auf eine Speicherzelle. Deren Wert wird geholt und im Hilfsregister abgelegt. Dieser Wert ist aber noch nicht der gesuchte, sondern verweist auf eine zweite Speicherzelle. Erst deren Wert wird in den Akkumulator geladen. Zunächst wird also der Speicher mit der rechten Hälfte des Befehlsregisters adressiert.";
                        break;
                    case 2:
                        s = "Dem Speicher wird über Steuersignale mitgeteilt, daß er den Wert der adressierten Speicherzelle ausgeben soll.";
                        break;
                    case 3:
                        s = "Der Speicher treibt den Datenbus mit dem verlangten Wert.";
                        break;
                    case 4:
                        s = "Der Wert auf dem Datenbus ist noch nicht der endgültige Wert, sondern wird im Hilfsregister abgelegt.";
                        break;
                    case 5:
                        s = "Der Speicher wird ein zweites Mal adressiert, nun mit dem Wert des Hilfsregisters.";
                        break;
                    case 6:
                        s = "Wieder wird dem Speicher über Steuersignale mitgeteilt, daß er den Wert der adressierten Speicherzelle ausgeben soll.";
                        break;
                    case 7:
                        s = "Der Speicher liefert ein zweites Mal einen Wert auf den Datenbus.";
                        break;
                    case 8:
                        s = "Erst dieser Wert wird im Akkumulator gespeichert. \nBefehle mit indirekter Adressierung wie LDA (mem) erlauben die Arbeit mit Zeigern in höheren Programmiersprachen.";
                        break;
                    case 99:
                        s = "\nDer Ladebefehl LDA (mem) ist abgeschlossen.";
                }
                break;
            case Rechner.LDA_ABSOL:
                t = "LDA absol. ('load accumulator absolutely')";
                maxStep = 2;
                switch (demonstrationStep)
                {
                    case 1:
                        s = "Das Allzweckregister Akkumulator wird mit diesem Ladebefehl ohne weiteren Speicherzugriff auf einen Wert gesetzt. Mit der rechten Hälfte des Befehlswortes wird dazu sofort der Datenbus getrieben.";
                        break;
                    case 2:
                        s = "Dieser Wert kann 'absolut' in den Akkumulator übernommen werden.";
                        break;
                    case 99:
                        s = "\nDer Ladebefehl LDA absol. ist abgeschlossen.";
                }
                break;
            case Rechner.STA_ABSOL:
                t = "STA absol. ('store accumulator in memory absolutely')";
                maxStep = 3;
                switch (demonstrationStep)
                {
                    case 1:
                        s = "Das Allzweckregister Akkumulator wird mit diesem Befehl in den Speicher geschrieben. Die Adresse, an der dieses geschehen soll, steht schon in der rechten Hälfte des Befehlswortes, mit welcher also zunächst der Speicher adressiert wird.";
                        break;
                    case 2:
                        s = "Um in den Speicher zu schreiben, muß der Datenbus nun mit dem entsprechenden Wert, dem Akkumulator, getrieben werden.";
                        break;
                    case 3:
                        s = "Durch Steuersignale wird der Speicher in den Schreibmodus versetzt und übernimmt den Wert des Akkumulators an die gewünschte Adresse.";
                        break;
                    case 99:
                        s = "\nDer Speicherbefehl STA absol. ist abgeschlossen.";
                }
                break;
            case Rechner.STA_MEM:
                t = "STA mem ('store accumulator in memory')";
                maxStep = 7;
                switch (demonstrationStep)
                {
                    case 1:
                        s = "Der Akkumulator soll in einer Speicherzelle abgelegt werden. Deren Adresse (A) ist aber nicht im Befehlswort enthalten, sondern in einer Speicherzelle abgelegt. Erst die Adresse (B) dieser zweiten Speicherzelle ist im Befehlswort enthalten.\n";
                        s = new String(s + "Zuerst muß also der Speicher mit (B) aus dem Befehlswort adressiert werden.");
                        break;
case 2:
                        s = "Dem Speicher wird über Steuersignale mitgeteilt, daß er den Wert der adressierten Speicherzelle ausgeben soll.";
                        break;
                    case 3:
                        s = "Der Speicher treibt den Datenbus mit dem verlangten Wert.";
                        break;
                    case 4:
                        s = "Das Hilfsregister übernimmt den Wert des Datenbusses.";
case 5:
                        break;
                    case 6:
s = "Erst jetzt kann in den Speicher geschrieben werden. Das Hilfsregister treibt dazu den Adreßbus und der Akkumulator, dessen Wert ja gespeichert werden soll, den Datenbus.";
                        break;
                    case 7:
s = "Durch Steuersignale wird der Speicher in den Schreibmodus versetzt und übernimmt den Wert des Akkumulators an die gewünschte Adresse.";
                        break;
                    case 99:
                        s = "\nDer Speicherbefehl STA mem ist abgeschlossen.";
                }
                break;
            case Rechner.ADD_MEM:
                t = "ADD mem - 'Akku := Akku + &<Wert aus Speicher>'";
            case Rechner.SUB_MEM:
                if (t.equals(""))
                    t = "SUB mem - 'Akku := Akku - &<Wert aus Speicher>'";
            case Rechner.MUL_MEM:
                if (t.equals(""))
                    t = "MUL mem - 'Akku := Akku * &<Wert aus Speicher>'";
            case Rechner.DIV_MEM:
                if (t.equals(""))
                    t = "DIV mem - 'Akku := Akku / &<Wert aus Speicher>'";
                maxStep = 6;
                switch (demonstrationStep)
                {
                    case 1:
s = "Ein Rechenbefehl mit zwei Operanden soll ausgeführt werden. Das Ergebnis wird immer im Allzweckregister Akkumulator gespeichert; einer der Operanden ist ebenfalls immer der Akkumulator. Der zweite Operand wird über den Datenbus vom Speichersystem geliefert, es soll also ein Wert aus dem Speicher gelesen werden.\nDazu wird zunächst der Adreßbus mit der rechten Hälfte des Befehlsregisters getrieben.";
                        break;
                    case 2:
s = "Dem Speicher wird über Steuersignale mitgeteilt, daß er den Wert der adressierten Speicherzelle ausgeben soll.";
                        break;
                    case 3:
s = "Der Speicher treibt den Datenbus mit dem verlangten Wert; der rechte Eingang der ALU wird an den Datenbus geschaltet. Zusätzlich wird der linke Eingang der ALU mit dem Wert des Akkumulators getrieben.";
                        break;
                    case 4:
s = "Die ALU führt die eigentliche Berechnung durch; diese dauert je nach Befehl unterschiedlich lange.";
                        break;
                    case 5:
s = "Der berechnete Wert wird auf den Bus am Ausgang der ALU geschaltet.";
                        break;
                    case 6:
s = "Der Akkumulator übernimmt den berechneten Wert.";
                        break;
                    case 99:
                        s = "\nDer Rechenbefehl mit zwei Operanden ist abgeschlossen.";
                }
                break;
            case Rechner.INC:
                t = "INC ('increase accumulator') - Akku := Akku + 1";
            case Rechner.DEC:
                if (t.equals(""))
                    t = "DEC ('decrease accumulator') - Akku := Akku - 1";
                maxStep = 4;
                switch (demonstrationStep)
                {
                    case 1:
s = "Ein Rechenbefehl mit einem Operanden soll ausgeführt werden. Das Ergebnis wird immer im Allzweckregister Akkumulator gespeichert; einziger Operand ist ebenfalls immer der Akkumulator. Dessen Wert wird nun auf den linken Eingang der ALU geschaltet. Der rechte Eingang wird für die Berechnung ignoriert.";
                        break;
                    case 2:
s = "Die ALU führt die eigentliche Berechnung durch.";
                        break;
                    case 3:
s = "Der berechnete Wert wird auf den Bus am Ausgang der ALU geschaltet.";
                        break;
                    case 4:
s = "Der Akkumulator übernimmt den berechneten Wert.";
                        break;
                    case 99:
                        s = "\nDer Rechenbefehl mit einem Operand ist abgeschlossen.";
                }
                break;
            case Rechner.SHL:
                t = "SHL ('shift left')";
            case Rechner.SHR:
                if (t.equals(""))
                    t = "SHR ('shift right')";
                maxStep = 4;
                switch (demonstrationStep)
                {
                    case 1:
s = "Ein Rechenbefehl (Schiebebefehl) mit zwei Operanden soll ausgeführt werden. Das Ergebnis wird immer im Allzweckregister Akkumulator gespeichert; der zu schiebende Wert ist ebenfalls immer der Akkumulator. Die Anzahl der Binärstellen, die geschoben werden soll, steht in der rechten Hälfte des Befehlswortes.\nDer linke Eingang der ALU wird nun mit dem Wert des Akkumulators getrieben, der rechte über den Datenbus mit der rechten Hälfte des Befehlswortes.";
                        break;
                    case 2:
s = "Die ALU führt die Schiebeoperation durch.";
                        break;
                    case 3:
s = "Der berechnete Wert wird auf den Bus am Ausgang der ALU geschaltet.";
                        break;
                    case 4:
s = "Der Akkumulator übernimmt den berechneten Wert.";
                        break;
                    case 99:
                        s = "\nDer Rechenbefehl (Schiebebefehl) " + t + " ist abgeschlossen.";
                }
                break;
            case Rechner.JMP_ABSOL:
                t = "JMP absol. ('jump absolutely') - unbedingter Sprung";
                maxStep = 2;
                switch (demonstrationStep)
                {
                    case 1:
s = "Das Programm, welches der Rechner ausführt, soll zu einer bestimmten Adresse (A) verzweigen, statt den nächsten Befehl immer von der nächsthöheren Adresse zu holen. Diese Adresse (A) steht direkt in der rechten Hälfte des Befehlswortes und wird nun auf den Datenbus geschaltet.";
                        break;
                    case 2:
s = "Der Programmzähler übernimmt den neuen Wert; der nächste Befehl wird von dieser Adresse geholt werden.";
                        break;
                    case 99:
                        s = "\nDer unbedingte Sprungbefehl JMP absol. ist abgeschlossen.";
                }
                break;
            case Rechner.JMP_MEM:
                t = "JMP mem ('jump memory') - unbedingter Sprung";
                maxStep = 4;
                switch (demonstrationStep)
                {
                    case 1:
s = "Das Programm, welches der Rechner ausführt, soll zu einer bestimmten Adresse (A) verzweigen, statt den nächsten Befehl immer von der nächsthöheren Adresse zu holen. Dazu muß der Programmzähler mit dem Wert (A) aus dem Speicher beschrieben werden. Die Adresse, unter der (A) zu finden ist, steht direkt in der rechten Hälfte des Befehlswortes, mit der also zunächst der Adreßbus getrieben wird.";
break;
                    case 2:
s = "Dem Speicher wird über Steuersignale mitgeteilt, daß er den Wert der adressierten Speicherzelle ausgeben soll.";
                        break;
                    case 3:
s = "Der Speicher treibt den Datenbus mit dem verlangten Wert.";
                        break;
                    case 4:
s = "Der Programmzähler übernimmt den neuen Wert; der nächste Befehl wird von dieser Adresse geholt werden.";
                        break;
                    case 99:
                        s = "\nDer unbedingte Sprungbefehl JMP mem ist abgeschlossen.";
                }
                break;
            case Rechner.JZE_ABSOL:
                t = "JZE absol.</b> ('<b>J</b>ump absolutely if <b>ZE</b>ro')<b> - bedingter Sprung</b>";
                maxStep = 2;
                switch (demonstrationStep)
                {
                    case 1:
s = "Die Sprungbedingung dieses bedingten Sprungs ist erfüllt, weil die letzte Berechnung der ALU 0 ergeben hat.\nDas Programm, welches der Rechner ausführt, soll also zu einer bestimmten Adresse (A) verzweigen, statt den nächsten Befehl von der nächsthöheren Adresse zu holen. Diese Adresse (A) steht direkt in der rechten Hälfte des Befehlswortes und wird nun auf den Datenbus geschaltet.";
                        break;
                    case 2:
s = "Der Programmzähler übernimmt den neuen Wert; der nächste Befehl wird von dieser Adresse geholt werden.";
                        break;
                    case 99:
                        s = "\nDer bedingte Sprungbefehl JZE absol. ist abgeschlossen.";
                }
                break;
            case Rechner.JZE_MEM:
                t = "JZE mem</b> ('<b>J</b>ump if <b>ZE</b>ro')<b> - bedingter Sprung</b>";
                maxStep = 4;
                switch (demonstrationStep)
                {
                    case 1:
s = "Die Sprungbedingung dieses bedingten Sprungs ist erfüllt, weil die letzte Berechnung der ALU 0 ergeben hat.\nDas Programm, welches der Rechner ausführt, soll also zu einer bestimmten Adresse (A) verzweigen, statt den nächsten Befehl von der nächsthöheren Adresse zu holen. Dazu muß der Programmzähler mit dem Wert (A) aus dem Speicher beschrieben werden. Die Adresse, unter der (A) zu finden ist, steht direkt in der rechten Hälfte des Befehlswortes, mit der also zunächst der Adreßbus getrieben wird.";
                        break;
                    case 2:
s = "Dem Speicher wird über Steuersignale mitgeteilt, daß er den Wert der adressierten Speicherzelle ausgeben soll.";
                        break;
                    case 3:
s = "Der Speicher treibt den Datenbus mit dem verlangten Wert.";
break;
                    case 4:
s = "Der Programmzähler übernimmt den neuen Wert; der nächste Befehl wird von dieser Adresse geholt werden.";
                        break;
                    case 99:
                        s = "\nDer bedingte Sprungbefehl JZE mem ist abgeschlossen.";
                }
                break;
            case Rechner.JNZ_ABSOL:
                t = "JNZ absol.</b> ('<b>J</b>ump absolutely if <b>N</b>ot <b>Z</b>ero')<b> - bedingter Sprung</b>";
                maxStep = 2;
                switch (demonstrationStep)
                {
                    case 1:
s = "Die Sprungbedingung dieses bedingten Sprungs ist erfüllt, weil die letzte Berechnung der ALU nicht 0 ergeben hat.\nDas Programm, welches der Rechner ausführt, soll also zu einer bestimmten Adresse (A) verzweigen, statt den nächsten Befehl von der nächsthöheren Adresse zu holen. Diese Adresse (A) steht direkt in der rechten Hälfte des Befehlswortes und wird nun auf den Datenbus geschaltet.";
break;
                    case 2:
s = "Der Programmzähler übernimmt den neuen Wert; der nächste Befehl wird von dieser Adresse geholt werden.";
                        break;
                    case 99:
                        s = "\nDer bedingte Sprungbefehl JNZ absol. ist abgeschlossen.";
                }
                break;
            case Rechner.JNZ_MEM:
                t = "JNZ mem</b> ('<b>J</b>ump if <b>N</b>ot <b>Z</b>ero')<b> - bedingter Sprung</b>";
                maxStep = 4;
                switch (demonstrationStep)
                {
                    case 1:
s = "Die Sprungbedingung dieses bedingten Sprungs ist erfüllt, weil die letzte Berechnung der ALU nicht 0 ergeben hat.\nDas Programm, welches der Rechner ausführt, soll also zu einer bestimmten Adresse (A) verzweigen, statt den nächsten Befehl von der nächsthöheren Adresse zu holen. Dazu muß der Programmzähler mit dem Wert (A) aus dem Speicher beschrieben werden. Die Adresse, unter der (A) zu finden ist, steht direkt in der rechten Hälfte des Befehlswortes, mit der also zunächst der Adreßbus getrieben wird.";
                        break;
                    case 2:
s = "Dem Speicher wird über Steuersignale mitgeteilt, daß er den Wert der adressierten Speicherzelle ausgeben soll.";
                        break;
                    case 3:
s = "Der Speicher treibt den Datenbus mit dem verlangten Wert.";
break;
                    case 4:
s = "Der Programmzähler übernimmt den neuen Wert; der nächste Befehl wird von dieser Adresse geholt werden.";
                        break;
                    case 99:
                        s = "\nDer bedingte Sprungbefehl JNZ mem ist abgeschlossen.";
                }
                break;
            case Rechner.JLE_ABSOL:
                t = "JLE absol.</b> ('<b>J</b>ump absolutely if <b>L</b>ess or <b>E</b>qual')<b> - bedingter Sprung</b>";
                maxStep = 2;
                switch (demonstrationStep)
                {
                    case 1:
s = "Die Sprungbedingung dieses bedingten Sprungs ist erfüllt, weil die letzte Berechnung der ALU einen Wert kleiner oder gleich 0 ergeben hat.\nDas Programm, welches der Rechner ausführt, soll also zu einer bestimmten Adresse (A) verzweigen, statt den nächsten Befehl von der nächsthöheren Adresse zu holen. Diese Adresse (A) steht direkt in der rechten Hälfte des Befehlswortes und wird nun auf den Datenbus geschaltet.";
break;
                    case 2:
s = "Der Programmzähler übernimmt den neuen Wert; der nächste Befehl wird von dieser Adresse geholt werden.";
                        break;
                    case 99:
                        s = "\nDer bedingte Sprungbefehl JLE absol. ist abgeschlossen.";
                }
                break;
            case Rechner.JLE_MEM:
                t = "JLE mem</b> ('<b>J</b>ump if <b>L</b>ess or <b>E</b>qual')<b> - bedingter Sprung</b>";
                maxStep = 4;
                switch (demonstrationStep)
                {
                    case 1:
s = "Die Sprungbedingung dieses bedingten Sprungs ist erfüllt, weil die letzte Berechnung der ALU einen Wert kleiner oder gleich 0 ergeben hat.\nDas Programm, welches der Rechner ausführt, soll also zu einer bestimmten Adresse (A) verzweigen, statt den nächsten Befehl von der nächsthöheren Adresse zu holen. Dazu muß der Programmzähler mit dem Wert (A) aus dem Speicher beschrieben werden. Die Adresse, unter der (A) zu finden ist, steht direkt in der rechten Hälfte des Befehlswortes, mit der also zunächst der Adreßbus getrieben wird.";
                        break;
                    case 2:
s = "Dem Speicher wird über Steuersignale mitgeteilt, daß er den Wert der adressierten Speicherzelle ausgeben soll.";
                        break;
                    case 3:
s = "Der Speicher treibt den Datenbus mit dem verlangten Wert.";
break;
                    case 4:
s = "Der Programmzähler übernimmt den neuen Wert; der nächste Befehl wird von dieser Adresse geholt werden.";
                        break;
                    case 99:
                        s = "\nDer bedingte Sprungbefehl JLE mem ist abgeschlossen.";
                }
                break;
            case Rechner.IN_MEM:
                t = "IN mem - Daten von Peripheriegerät empfangen";
                maxStep = 3;
                switch (demonstrationStep)
                {
                    case 1:
s = "Dieser Befehl holt einen (zufällig erzeugten) Wert von einem Gerät außerhalb des Rechners ab und speichert ihn in einer Speicherzelle. Welche Speicherzelle dies sein soll, steht in der rechten Hälfte des Befehlsregisters.\nMit dieser wird zunächst der Adreßbus getrieben; anschließend wird in den Speicher geschrieben werden, mit dem Peripheriegerät als Datenquelle.";
                        break;
                    case 2:
s = "Der Datenbus wird mit dem Wert vom Peripheriegerät getrieben.";
                        break;
                    case 3:
s = "Durch Steuersignale wird der Speicher in den Schreibmodus versetzt und übernimmt den Wert des Datenbusses, also die Daten vom Peripheriegerät, an die gewünschte Adresse.";
                        break;
                    case 99:
                        s = "\nDer Ein-/Ausgabebefehl IN mem ist abgeschlossen.";
                }
                break;
            case Rechner.OUT_MEM:
                t = "OUT mem - Daten an Peripheriegerät senden";
                maxStep = 3;
                switch (demonstrationStep)
                {
                    case 1:
s = "Dieser Befehl sendet einen Wert aus dem Speicher an ein Gerät außerhalb des Rechners. Es soll also aus dem Speicher gelesen werden, mit dem Peripheriegerät als Datensenke. Die Adresse, von der gelesen werden soll, steht direkt in der rechten Hälfte des Befehlsregisters und wird nun zunächst auf den Adreßbus geschaltet.";
                        break;
                    case 2:
s = "Dem Speicher wird über Steuersignale mitgeteilt, daß er den Wert der adressierten Speicherzelle ausgeben soll.";
                        break;
                    case 3:
s = "Der Speicher treibt den Datenbus mit dem verlangten Wert; das Peripheriegerät kann den Wert nun übernehmen.";
                        break;
                    case 99:
                        s = "\nDer Ein-/Ausgabebefehl OUT mem ist abgeschlossen.";
                }
                break;
            case Rechner.NOP:
                t = "NOP (No OPeration)";
                maxStep = 1;
                switch (demonstrationStep)
                {
                    case 1:
                        s = "Dieser Befehl tut tatsächlich nichts und wird gerade deswegen in primitiven Warteschleifen eingesetzt.";
                        break;
                    case 99:
                        s = "\nDer Befehl ohne Wirkung NOP ist abgeschlossen.";
                }
                break;
            case Rechner.JZE_NOT_TAKEN:
                t = "JZE</b> ('<b>J</b>ump if <b>ZE</b>ro')<b> - bedingter Sprung</b>";
                maxStep = 1;
                switch (demonstrationStep)
                {
                	case 1:
		                s = "Dieser bedingte Sprung wurde nicht ausgeführt.\nDer Programmzähler bleibt erhalten und das Programm fährt wie nach einem NOP fort.";
		                break;
		            case 99:
		            	s = "\nDer bedingte Sprungbefehl JZE ist abgeschlossen.";
		        }
                break;
            case Rechner.JNZ_NOT_TAKEN:
                t = "JNZ</b> ('<b>J</b>ump if <b>N</b>ot <b>Z</b>ero')<b> - bedingter Sprung</b>";
                maxStep = 1;
                switch (demonstrationStep)
                {
                	case 1:
		                s = "Dieser bedingte Sprung wurde nicht ausgeführt.\nDer Programmzähler bleibt erhalten und das Programm fährt wie nach einem NOP fort.";
		                break;
		            case 99:
		            	s = "\nDer bedingte Sprungbefehl JNZ ist abgeschlossen.";
		        }
                break;
            case Rechner.JLE_NOT_TAKEN:
                t = "JLE</b> ('<b>J</b>ump if <b>L</b>ess or <b>E</b>qual')<b> - bedingter Sprung</b>";
                maxStep = 1;
                switch (demonstrationStep)
                {
                	case 1:
		                s = "Dieser bedingte Sprung wurde nicht ausgeführt.\nDer Programmzähler bleibt erhalten und das Programm fährt wie nach einem NOP fort.";
		                break;
		            case 99:
		            	s = "\nDer bedingte Sprungbefehl JLE ist abgeschlossen.";
		        }
                break;
            case Rechner.UNKNOWN_COMMAND:
                demonstrationStep = 99;
                s = "Unbekannter Befehlscode!\n\nBitte brechen Sie hier die Simulation ab.";
                break;
        }

        if (demonstrationStep == 99)
        	return s;
        else if (demonstrationStep == -2)  // durch Aufruf von helpText_Von_Neumann_Rechner(c_state)
        	return t;
        else
        {
            t = new String("<b>" + t + " </b>(Schritt " + Integer.toString(demonstrationStep, 10) + " von " + Integer.toString(maxStep, 10) + ")\n");
            return new String(t + s);
        }
    } /* end helpText_Von_Neumann_Rechner */


    public String helpText_Komponenten_Register(int c_state, int demonstrationStep)
    {
        String as = Integer.toString(c_state, 16);
        String bs = Integer.toString(demonstrationStep, 16);
        String s = new String("Noch keine Hilfe verfügbar: Befehl $" + as + ", Demonstrationsschritt $" + bs);

        switch (c_state)
        {
            case Rechner.NORMAL:
                switch (demonstrationStep)
                {
                    case 1:
                        s = "<b>Übernehmen eines neuen Wertes in das Register </b>(Schritt 1 von 4)\n\nZuerst wird selbstverständlich in einem anderen Teil des Rechners der Wert gebildet, den man im Register speichern möchte.";
                        //s = "a1 a2 a3 a4 a5 a6 a7 a8 a9 a0 b1 b2 b3 b4 b5 b6 b7 b8 b9 b0 c1 c2 c3 c4 c5 c6 c7 c8 c9 c0 d1 d2 d3 d4 d5 d6 d7 d8 d9 d0 e1 e2 e3 e4 e5 e6 e7 e8 e9 e0 f1 f2 f3 f4 f5 f6 f7 f8 f9 f0 g1 g2 g3 g4 g5 g6 g7 g8 g9 g0 h1 h2 h3 h4 h5 h6 h7 h8 h9 h0 i1 i2 i3 i4 i5 i6 i7 i8 i9 i0 j1 j2 j3 j4 j5 j6 j7 j8 j9 j0 k1 k2 k3 k4 k5 k6 k7 k8 k9 k0 l1 l2 l3 l4 l5 l6 l7 l8 l9 l0 ";
                        //s = "<b>a1 a2 a3 a4 a5 a6 a7 a8 a9 a0 b1 b2 b3 b4 b5 b6 b7 b8 b9 b0 c1 c2 c3 c4 c5 c6 c7 c8 c9 c0</b> d1 d2 d3 d4 d5 d6 d7 d8 d9 d0 <b>e1</b> <b>e2</b> <b>e3</b> <b>e4</b> <b>e5</b> <b>e6</b> <b>e7</b> <b>e8</b> <b>e9</b> <b>e0</b> f1 f2 f3 f4 f5 f6 f7 f8 f9 f0 g1 g2 g3 g4 g5 g6 g7 g8 g9 g0 <b>h1 </b><b>h2 </b><b>h3 </b><b>h4 </b><b>h5 </b><b>h6 </b><b>h7 </b><b>h8 </b><b>h9 </b><b>h0 </b>i1 i2 i3 i4 i5 i6 i7 i8 i9 i0 j1 j2 j3 j4 j5 j6 j7 j8 j9 j0 k1 k2 k3 k4 k5 k6 k7 k8 k9 k0 l1 l2 l3 l4 l5 l6 l7 l8 l9 l0 ";
                        //s = "<b>a1 a2 a3 a4 a5 a6 a7 a8 a9 a0 b1 b2 b3 b4 b5 b6 b7 b8 b9 b0 c1 c2 c3 c4 c5 c6 c7 c8 c9 c0</b> d1 d2 d3 d4 d5 d6 d7 d8 d9 d0 <b>e1 e2 e3 e4 e5 e6 e7 e8 e9 e0</b> f1 f2 f3 f4 f5 f6 f7 f8 f9 f0 g1 g2 g3 g4 g5 g6 g7 g8 g9 g0 <b>h1 h2 h3 h4 h5 h6 h7 h8 h9 h0 </b>i1 i2 i3 i4 i5 i6 i7 i8 i9 i0 j1 j2 j3 j4 j5 j6 j7 j8 j9 j0 k1 k2 k3 k4 k5 k6 k7 k8 k9 k0 l1 l2 l3 l4 l5 l6 l7 l8 l9 l0 ";
                        break;
                    case 2:
                        s = "<b>Übernehmen eines neuen Wertes in das Register </b>(Schritt 2 von 4)\n\nDer zu übernehmende Wert wird auf den Dateneingang des Registers geschaltet. Dazu müssen auf dem Weg liegende Schalter, Multiplexer, Busse etc. entsprechend gesteuert werden. ";
                        s = new String(s + "Dies kostet ebenso Zeit wie die eigentliche Bewegung des Signals auf den Leitungen.\nDer vorgesehene neue Wert muß nun für eine bestimmte Zeitdauer, die sogenannte Voreinstellzeit ('setup time') konstant bleiben, bevor das Register ihn im nächsten Schritt übernehmen kann.");
                        break;
                    case 3:
                        s = "<b>Übernehmen eines neuen Wertes in das Register </b>(Schritt 3 von 4)\n\nDas Steuerwerk generiert ein Taktsignal; nach dessen Empfang beginnt das Register damit, den Wert des Dateneinganges zu übernehmen. Der Datenwert muß jetzt für die sogenannte Haltezeit ('hold time') unverändert bleiben.";
                        break;
                    case 4:
                        s = "<b>Übernehmen eines neuen Wertes in das Register </b>(Schritt 4 von 4)\n\nDas Register enthält jetzt einen neuen Wert. Dieser ist nur dann garantiert mit dem Wert am Dateneingang identisch, wenn Voreinstell- und Haltezeit eingehalten wurden. Für das Ändern seines Wertes hat das Register eine für es typische Schaltzeit ('propagation delay') gebraucht.\n";
                        s = new String(s + "Ein Register sendet immer seinen Wert aus; sobald das Register also einen neuen Wert enthält, bewegt sich dieser vom Datenausgang in den übrigen Rechner. Nur durch die Signalverzögerung der Leitungen gebremst, steht er dann an anderer Stelle zur Verfügung.");
                        break;
                    /**
                    case 5:
                        s = "<b>Übernehmen eines neuen Wertes in das Register </b>(Schritt 5 von 6)\n\nEin Register sendet immer seinen Wert aus.\nSobald das Register also einen neuen Wert enthält, bewegt dieser sich vom Datenausgang in den übrigen Rechner.";
                        break;
                    case 6:
                        s = "<b>Übernehmen eines neuen Wertes in das Register </b>(Schritt 6 von 6)\n\nEine Datensenke empfängt den neuen Registerinhalt zur weiteren Verarbeitung";
                        break;
                    */
                    case 99:
                        s = "\n\nDas Übernehmen eines neuen Wertes in das Register ist abgeschlossen.";
                }
                break;
            case Rechner.RESET:
                switch (demonstrationStep)
                {
                    case 1:
                        s = "<b>Zurücksetzen ('Reset') des Registers </b>(Schritt 1 von 2)\n\n";
                        s = new String(s + "Mit dem Rücksetzsignal ('reset') kann das Register auf einen in der Hardware festgelegten Standardwert (hier: 0000) zurückgesetzt werden. Ein möglicherweise gerade laufender Speichervorgang kann dadurch abgebrochen werden; Daten- und Takteingang werden solange ignoriert, wie das Reset-Signal aktiv ist.");
                        break;
                    case 2:
                        s = "<b>Zurücksetzen ('Reset') des Registers </b>(Schritt 2 von 2)\n\n";
                        s = new String(s + "Nach einer gewissen Zeit geht das Register zurück auf seinen Standardwert. Bis dahin ist sein Wert undefiniert.\n");
                        s = new String(s + "Ein Register sendet immer seinen Wert aus; sobald das Register also einen neuen Wert enthält, bewegt sich dieser vom Datenausgang in den übrigen Rechner. Nur durch die Signalverzögerung der Leitungen gebremst, steht er dann an anderer Stelle zur Verfügung.");
                        break;
                    /**
                    case 3:
                        s = "<b>Zurücksetzen ('Reset') des Registers </b>(Schritt 3 von 4)\n\nEin Register sendet immer seinen Wert aus.\nSobald das Register also einen neuen Wert enthält, bewegt dieser sich vom Datenausgang in den übrigen Rechner.";
                        break;
                    case 4:
                        s = "<b>Zurücksetzen ('Reset') des Registers </b>(Schritt 4 von 4)\n\nEine Datensenke empfängt den neuen Registerinhalt zur weiteren Verarbeitung";
                        break;
                    */
                    case 99:
                        s = "\n\nDas Zurücksetzen des Registers auf den durch die Hardware\nfestgelegten Standardwert ist abgeschlossen.";
                }
                break;
            case Rechner.UNKNOWN_COMMAND:
                s = "<b>Unbekannte Vorgehensweise!</b>\n\nBitte brechen Sie hier die Simulation ab.";
                break;
        }

        return s;
    } /* end helpText_Komponenten_Register */


    public String helpText_Komponenten_RAM(int c_state, int demonstrationStep)
    {
        String as = Integer.toString(c_state, 16);
        String bs = Integer.toString(demonstrationStep, 16);
        String s = new String("Noch keine Hilfe verfügbar: Befehl $" + as + ", Demonstrationsschritt $" + bs);

        switch (c_state)
        {
            case Rechner.READ:
                switch (demonstrationStep)
                {
                    case 1:
                        s = "<b>Lesen aus dem Speicher </b>(Schritt 1 von 5)\n\n";
                        s = new String(s + "Zuerst wird in einem anderen Teil des Rechners eine Adresse generiert. Diese wird später genau eine Zelle im Speicher bezeichnen.");
                        break;
                    case 2:
                        s = "<b>Lesen aus dem Speicher </b>(Schritt 2 von 5)\n\n";
                        s = new String(s + "Die neue Adresse wird auf den Adreßeingang des Speichers geschaltet. Dazu müssen auf dem Weg liegende Schalter, Multiplexer, Busse etc. entsprechend gesteuert werden. ");
                        s = new String(s + "Dies kostet ebenso Zeit wie die eigentliche Bewegung des Signals auf den Leitungen. Auch die Adresse muß für eine Voreinstellzeit ('setup time') konstant bleiben, bevor der Speicher im nächsten Schritt auf 'Lesen' eingestellt wird.");
                        break;
                    case 3:
                        s = "<b>Lesen aus dem Speicher </b>(Schritt 3 von 5)\n\n";
                        /**
                        s = new String(s + "Der <b>Speicher </b>verfügt <b>über </b>mindestens <b>drei </b>Eingänge <b>für </b>Steuersignale <b>aus </b>dem <b>Steuerwerk; </b>diese <b>bewirken:\n");
                        s = new String(s + "- <b>Aktivierung </b>des <b>Speichers </b>('chip <b>enable')\n");
                        s = new String(s + "- <b>Auswahl </b>Lesen <b>oder </b>Schreiben\n");
                        s = new String(s + "- 'Treiben' <b>des </b>Datenbusses <b>('output </b>enable')\n");
                        s = new String(s + "<b>Sobald</b> der Speicher im Lesemodus aktiviert wird, beginnt er damit, den Wert der <b>Speicherzelle </b>herauszusuchen, die durch die Adresse bezeichnet wird. ");
                        s = new String(s + "Spätestens <b>wenn </b>dieser <b>Wert </b>verfügbar <b>ist, </b>wird <b>er </b>durch <b>Setzen </b>von 'output enable' auf den <b>Datenbus </b>gebracht; <b>keine </b>andere <b>Datenquelle </b>darf <b>dann </b>noch diesen Bus treiben. ");
                        */
                        s = new String(s + "Der Speicher verfügt über mindestens drei Eingänge für Steuersignale aus dem Steuerwerk; diese bewirken:\n");
                        s = new String(s + "- Aktivierung des Speichers ('chip enable')\n");
                        s = new String(s + "- Auswahl Lesen oder Schreiben\n");
                        s = new String(s + "- 'Treiben' des Datenbusses ('output enable')\n");
                        s = new String(s + "Sobald der Speicher im Lesemodus aktiviert wird, beginnt er damit, den Wert der Speicherzelle herauszusuchen, die durch die Adresse bezeichnet wird. ");
                        s = new String(s + "Spätestens wenn dieser Wert verfügbar ist, wird er durch Setzen von 'output enable' auf den Datenbus gebracht; keine andere Datenquelle darf dann noch diesen Bus treiben.\n");
                        s = new String(s + "Zwischen den Änderungen der Steuersignale sind, je nach Speichertyp, Wartezeiten einzuhalten.\n");
                        s = new String(s + "Das 'Suchen' des richtigen Wertes kostet ebenfalls Zeit.");
                        //s = new String(s + "\nmm1 mm2 mm3 mm4 mm5 mm6 mm7 mm8 mm9 mm0 mm1 mm2 mm3 mm4 mm5 mm6 mm7 mm8 mm9 mm0");
                        //s = new String(s + "\n<b>mm1 mm2 mm3 mm4 mm5 mm6 mm7 mm8 mm9 mm0 mm1 mm2 mm3 mm4 mm5 mm6 mm7 mm8 mm9 mm0</b>");
                        break;
                    case 4:
                        s = "<b>Lesen aus dem Speicher </b>(Schritt 4 von 5)\n\n";
                        s = new String(s + "Der Datenbus wird mit dem gesuchten Wert getrieben. Dieser strahlt nun über den Bus in den Rechner aus.");
                        break;
                    case 5:
                        s = "<b>Lesen aus dem Speicher </b>(Schritt 5 von 5)\n\n";
                        s = new String(s + "Ein Register wird über Steuersignale aus dem Steuerwerk dazu gebracht, den Wert zu übernehmen.");
                        break;
                    case 99:
                        s = "\n\nDas Lesen aus dem Speicher ist abgeschlossen.";
                }
                break;
            case Rechner.WRITE:
                switch (demonstrationStep)
                {
                    case 1:
                        s = "<b>Schreiben in den Speicher </b>(Schritt 1 von 5)\n\n";
                        s = new String(s + "Zuerst wird in einem anderen Teil des Rechners eine Adresse generiert. Diese wird später genau eine Zelle im Speicher bezeichnen.");
                        break;
                    case 2:
                        s = "<b>Schreiben in den Speicher </b>(Schritt 2 von 5)\n\n";
                        s = new String(s + "Die neue Adresse wird auf den Adreßeingang des Speichers geschaltet. Dazu müssen auf dem Weg liegende Schalter, Multiplexer, Busse etc. entsprechend gesteuert werden.");
                        s = new String(s + "Dies kostet ebenso Zeit wie die eigentliche Bewegung des Signals auf den Leitungen. Auch die Adresse muß für eine Voreinstellzeit ('setup time') konstant bleiben, bevor der Speicher im nächsten Schritt auf 'Schreiben' eingestellt wird.");
                        break;
                    case 3:
                        s = "<b>Schreiben in den Speicher </b>(Schritt 3 von 5)\n\n";
                        s = new String(s + "Beim Schreiben muß selbstverständlich auch ein Wert gebildet werden, der im Speicher abgelegt werden soll.");
                        break;
                    case 4:
                        s = "<b>Schreiben in den Speicher </b>(Schritt 4 von 5)\n\n";
                        s = new String(s + "Dieser Wert erreicht auf dem Datenbus den Speicher.");
                        break;
                        case 5:
                        s = "<b>Schreiben in den Speicher </b>(Schritt 5 von 5)\n\n";
                        s = new String(s + "Der Speicher verfügt über mindestens drei Eingänge für Steuersignale aus dem Steuerwerk; diese bewirken:\n");
                        s = new String(s + "- Aktivierung des Speichers ('chip enable')\n");
                        s = new String(s + "- Auswahl Lesen oder Schreiben\n");
                        s = new String(s + "- 'Treiben' des Datenbusses ('output enable')\n");
                        s = new String(s + "Sobald der Speicher im Schreibmodus aktiviert wird, beginnt er damit, den Wert, der auf dem Datenbus liegt, in der Speicherzelle zu speichern, die durch die Adresse bezeichnet wird.");
                        s = new String(s + "Dabei darf der Datenbus auf gar keinen Fall getrieben werden ('output enable' ist deaktiviert). ");
                        s = new String(s + "Zwischen den Änderungen der Steuersignale sind, je nach Speichertyp, Wartezeiten einzuhalten.\n");
                        s = new String(s + "Das eigentliche Speichern des Wertes kostet ebenfalls Zeit.");
                        break;
                    case 99:
                        s = "\n\nDas Schreiben in den Speicher ist abgeschlossen.";
                }
                break;
            case Rechner.RESET:
                switch (demonstrationStep)
                {
                    case 1:
                        s = "<b>Zurücksetzen ('Reset') des Speichers </b>(Schritt 1 von 2)\n\n";
                        s = new String(s + "Mit dem Rücksetzsignal ('reset') können die Inhalte aller Speicherzellen auf in der Hardware festgelegte Standardwerte (hier: Wert = 3*Adresse) zurückgesetzt werden. Ein möglicherweise gerade laufender Lese- oder Schreibvorgang kann dadurch abgebrochen werden; alle Eingänge werden  solange ignoriert, wie das Reset-Signal aktiv ist.");
                        break;
                    case 2:
                        s = "<b>Zurücksetzen ('Reset') des Speichers </b>(Schritt 2 von 2)\n\n";
                        s = new String(s + "Nach einer gewissen Zeit gehen alle Speicherzellen zurück auf ihre Standardwerte. Bis dahin sind ihre Werte undefiniert.\n");
                        s = new String(s + "Der geänderte Inhalt des Speichers strahlt, anders als bei einem Register, nicht von alleine in den Rechner aus.");
                        break;
                    case 99:
                        s = "\n\nDas Zurücksetzen ('Reset') des Speichers ist abgeschlossen.";
                }
                break;
            case Rechner.UNKNOWN_COMMAND:
                s = "<b>Unbekannte Vorgehensweise!</b>\n\nBitte brechen Sie hier die Simulation ab.";
                break;
        }

        return s;
    } /* end helpText_Komponenten_RAM */


    public String helpText_Adressierungsarten(int c_state, int demonstrationStep)
    {
        String as = Integer.toString(c_state, 16);
        String bs = Integer.toString(demonstrationStep, 16);
        String s = new String("Noch keine Hilfe verfügbar: Befehl $" + as + ", Demonstrationsschritt $" + bs);

        switch (c_state)
        {
            case Rechner.ABSOLUT:
                switch (demonstrationStep)
                {
                    case 1:
                        s = "<b>Absolute Adressierung </b>(Schritt 1 von 3)\n\n";
                        s = new String(s + "Dies ist die einfachste Art, ein Register mit einem Wert zu laden, denn eine Adresse wird nicht benötigt.\nDas Befehlswort enthält bereits den neuen Wert für das Zielregister.");
                        break;
                    case 2:
                        s = "<b>Absolute Adressierung </b>(Schritt 2 von 3)\n\n";
                        s = new String(s + "Der neue Wert für das Zielregister wird auf den Datenbus geschaltet.");
                        break;
                    case 3:
                        s = "<b>Absolute Adressierung </b>(Schritt 3 von 3)\n\n";
                        s = new String(s + "Das Zielregister wird über Steuersignale angewiesen, den Wert des Datenbusses zu übernehmen.");
                        break;
                    case 99:
                        s = "\n\nDie absolute Adressierung ist abgeschlossen.";
                }
                break;
            case Rechner.UNMITTELBAR:
                switch (demonstrationStep)
                {
                    case 1:
                        s = "<b>Unmittelbare Adressierung </b>(Schritt 1 von 5)\n\n";
                        s = new String(s + "Bei dieser Adressierungsart soll dem Zielregister ein Wert aus dem Speicher zugewiesen werden. Die Adresse, unter der dieser Wert im Speicher zu finden ist, ist bereits im Befehlswort enthalten.\nDeshalb nennt man das folgende Vorgehen auch 'Speicher direkt'.");
                        break;
                    case 2:
                        s = "<b>Unmittelbare Adressierung </b>(Schritt 2 von 5)\n\n";
                        s = new String(s + "Die Adresse aus dem Befehlswort wird auf den Adreßbus geschaltet.");
                        break;
                    case 3:
                        s = "<b>Unmittelbare Adressierung </b>(Schritt 3 von 5)\n\n";
                        s = new String(s + "Nachdem die Adresse ausreichend lange unverändert am Adreßeingang des Speichers angelegen hat, kann der Speicher den Inhalt der zu dieser Adresse gehörenden Speicherzelle heraussuchen.");
                        break;
                    case 4:
                        s = "<b>Unmittelbare Adressierung </b>(Schritt 4 von 5)\n\n";
                        s = new String(s + "Der Speicher treibt den Datenbus mit dem durch die Adresse bezeichneten Wert.");
                        break;
                    case 5:
                        s = "<b>Unmittelbare Adressierung </b>(Schritt 5 von 5)\n\n";
                        s = new String(s + "Das Zielregister wird über Steuersignale angewiesen, den Wert des Datenbusses zu übernehmen.");
                        break;
                    case 99:
                        s = "\n\nDie unmittelbare Adressierung ist abgeschlossen.";
                }
                break;
            case Rechner.REG_DIREKT:
                switch (demonstrationStep)
                {
                    case 1:
                        s = "<b>Adressierung 'Register direkt' </b>(Schritt 1 von 4)\n\n";
                        s = new String(s + "Bei dieser Adressierungsart soll dem Zielregister ein Wert aus einem der Allzweckregister zugewiesen werden. Das Befehlswort enthält keine 'echte' Adresse, sondern verweist auf eines der Allzweckregister.");
                        break;
                    case 2:
                        s = "<b>Adressierung 'Register direkt' </b>(Schritt 2 von 4)\n\n";
                        s = new String(s + "Das Steuerwerk hat die untere (rechte) Hälfte des Befehlswortes ausgelesen; diese bezeichnet eines der Allzweckregister.");
                        break;
                    case 3:
                        s = "<b>Adressierung 'Register direkt' </b>(Schritt 3 von 4)\n\n";
                        s = new String(s + "Das bezeichnete Register wird vom Steuerwerk auf den Datenbus geschaltet und treibt diesen mit seinem Wert.");
                        break;
                    case 4:
                        s = "<b>Adressierung 'Register direkt' </b>(Schritt 4 von 4)\n\n";
                        s = new String(s + "Das Zielregister wird über Steuersignale angewiesen, den Wert des Datenbusses zu übernehmen.");
                        break;
                    case 99:
                        s = "\n\nDie Adressierung 'Register direkt' ist abgeschlossen.";
                }
                break;
            case Rechner.REG_INDIR:
                switch (demonstrationStep)
                {
                    case 1:
                        s = "<b>Adressierung 'Register indirekt' </b>(Schritt 1 von 6)\n\n";
                        s = new String(s + "Bei dieser Adressierungsart soll dem Zielregister ein Wert aus dem Speicher zugewiesen werden; die Adresse, unter der dieser Wert zu finden ist, steht in einem der Allzweckregister.");
                        break;
                    case 2:
                        s = "<b>Adressierung 'Register indirekt' </b>(Schritt 2 von 6)\n\n";
                        s = new String(s + "Das Steuerwerk hat die untere (rechte) Hälfte des Befehlswortes ausgelesen; diese bezeichnet eines der Allzweckregister.");
                        break;
                    case 3:
                        s = "<b>Adressierung 'Register indirekt' </b>(Schritt 3 von 6)\n\n";
                        s = new String(s + "Das bezeichnete Register wird vom Steuerwerk auf den Datenbus geschaltet und treibt diesen mit seinem Wert.");
                        break;
                    case 4:
                        s = "<b>Adressierung 'Register indirekt' </b>(Schritt 4 von 6)\n\n";
                        s = new String(s + "Nachdem die Adresse ausreichend lange unverändert am Adreßeingang des Speichers angelegen hat, kann der Speicher den Inhalt der zu dieser Adresse gehörenden Speicherzelle heraussuchen.");
                        break;
                    case 5:
                        s = "<b>Adressierung 'Register indirekt' </b>(Schritt 5 von 6)\n\n";
                        s = new String(s + "Der Speicher treibt den Datenbus mit dem durch die Adresse bezeichneten Wert.");
                        break;
                    case 6:
                        s = "<b>Adressierung 'Register indirekt' </b>(Schritt 6 von 6)\n\n";
                        s = new String(s + "Das Zielregister wird über Steuersignale angewiesen, den Wert des Datenbusses zu übernehmen.");
                        break;
                    case 99:
                        s = "\n\nDie Adressierung 'Register indirekt' ist abgeschlossen.";
                }
                break;
            case Rechner.REG_INDIR_PI:
                switch (demonstrationStep)
                {
                    case 1:
                        s = "<b>Adressierung 'Register indirekt' mit Post-Inkrement </b>(Schritt 1 von 7)\n\n";
                        s = new String(s + "Bei dieser Adressierungsart soll dem Zielregister ein Wert aus dem Speicher zugewiesen werden; die Adresse, unter der dieser Wert zu finden ist, steht in einem der Allzweckregister.\n");
                        s = new String(s + "Nach der Ausführung des eigentlichen Datentransferbefehls soll die Adresse (also der Wert des benutzten Allzweckregisters) um Eins erhöht werden.");
                        break;
                    case 2:
                        s = "<b>Adressierung 'Register indirekt' mit Post-Inkrement </b>(Schritt 2 von 7)\n\n";
                        s = new String(s + "Das Steuerwerk hat die untere (rechte) Hälfte des Befehlswortes ausgelesen; diese bezeichnet eines der Allzweckregister.");
                        break;
                    case 3:
                        s = "<b>Adressierung 'Register indirekt' mit Post-Inkrement </b>(Schritt 3 von 7)\n\n";
                        s = new String(s + "Das bezeichnete Register wird vom Steuerwerk auf den Datenbus geschaltet und treibt diesen mit seinem Wert.");
                        break;
                    case 4:
                        s = "<b>Adressierung 'Register indirekt' mit Post-Inkrement </b>(Schritt 4 von 7)\n\n";
                        s = new String(s + "Nachdem die Adresse ausreichend lange unverändert am Adreßeingang des Speichers angelegen hat, kann der Speicher den Inhalt der zu dieser Adresse gehörenden Speicherzelle heraussuchen.");
                        break;
                    case 5:
                        s = "<b>Adressierung 'Register indirekt' mit Post-Inkrement </b>(Schritt 5 von 7)\n\n";
                        s = new String(s + "Der Speicher treibt den Datenbus mit dem durch die Adresse bezeichneten Wert.");
                        break;
                    case 6:
                        s = "<b>Adressierung 'Register indirekt' mit Post-Inkrement </b>(Schritt 6 von 7)\n\n";
                        s = new String(s + "Das Zielregister wird über Steuersignale angewiesen, den Wert des Datenbusses zu übernehmen.");
                        break;
                    case 7:
                        s = "<b>Adressierung 'Register indirekt' mit Post-Inkrement </b>(Schritt 7 von 7)\n\n";
                        s = new String(s + "Der Wert des Allzweckregisters, welches die Adresse enthielt, wird um Eins erhöht.");
                        break;
                    case 99:
                        s = "\n\nDie Adressierung 'Register indirekt' mit Post-Inkrement ist abgeschlossen.";
                }
                break;
            case Rechner.REG_INDIR_PD:
                switch (demonstrationStep)
                {
                    case 1:
                        s = "<b>Adressierung 'Register indirekt' mit Prä-Dekrement </b>(Schritt 1 von 6)\n\n";
                        s = new String(s + "Bei dieser Adressierungsart soll dem Zielregister ein Wert aus dem Speicher zugewiesen werden; die Adresse, unter der dieser Wert zu finden ist, steht in einem der Allzweckregister.\n");
                        s = new String(s + "Bevor die Adresse aber benutzt wird, soll von ihr (also dem Wert eines der Allzweckregister) Eins abgezogen werden.");
                        break;
                    case 2:
                        s = "<b>Adressierung 'Register indirekt' mit Prä-Dekrement </b>(Schritt 2 von 6)\n\n";
                        s = new String(s + "Das Steuerwerk hat die untere (rechte) Hälfte des Befehlswortes ausgelesen; diese bezeichnet eines der Allzweckregister.\nBevor nun dessen Wert benutzt wird, wird er um Eins vermindert.");
                        break;
                    case 3:
                        s = "<b>Adressierung 'Register indirekt' mit Prä-Dekrement </b>(Schritt 3 von 6)\n\n";
                        s = new String(s + "Das bezeichnete Register wird vom Steuerwerk auf den Datenbus geschaltet und treibt diesen mit seinem Wert.");
                        break;
                    case 4:
                        s = "<b>Adressierung 'Register indirekt' mit Prä-Dekrement </b>(Schritt 4 von 6)\n\n";
                        s = new String(s + "Nachdem die Adresse ausreichend lange unverändert am Adreßeingang des Speichers angelegen hat, kann der Speicher den Inhalt der zu dieser Adresse gehörenden Speicherzelle heraussuchen.");
                        break;
                    case 5:
                        s = "<b>Adressierung 'Register indirekt' mit Prä-Dekrement </b>(Schritt 5 von 6)\n\n";
                        s = new String(s + "Der Speicher treibt den Datenbus mit dem durch die Adresse bezeichneten Wert.");
                        break;
                    case 6:
                        s = "<b>Adressierung 'Register indirekt' mit Prä-Dekrement </b>(Schritt 6 von 6)\n\n";
                        s = new String(s + "Das Zielregister wird über Steuersignale angewiesen, den Wert des Datenbusses zu übernehmen.");
                        break;
                    case 99:
                        s = "\n\nDie Adressierung 'Register indirekt' mit Prä-Dekrement ist abgeschlossen.";
                }
                break;
            case Rechner.REG_INDIR_O:
                switch (demonstrationStep)
                {
                    case 1:
                        s = "<b>Adressierung 'Register indirekt' mit Offset </b>(Schritt 1 von 6)\n\n";
                        s = new String(s + "Bei dieser Adressierungsart soll dem Zielregister ein Wert aus dem Speicher zugewiesen werden; die Adresse, unter der dieser Wert im Speicher zu finden ist, ergibt sich als:\n");
                        s = new String(s + "[Wert eines Allzweckregisters] + [sogenannter Offset]\nWeil nur ein Teil der unteren (rechten) Hälfte des Befehlswortes benötigt wird, um ein Allzweckregister auszuwählen, bleibt hier noch Platz für den Offset (zweite Ziffer von rechts in Hexadezimaldarstellung).");
                        break;
                    case 2:
                        s = "<b>Adressierung 'Register indirekt' mit Offset </b>(Schritt 2 von 6)\n\n";
                        s = new String(s + "Das Steuerwerk hat die untere (rechte) Hälfte des Befehlswortes ausgelesen; diese bezeichnet eines der Allzweckregister.");
                        break;
                    case 3:
                        s = "<b>Adressierung 'Register indirekt' mit Offset </b>(Schritt 3 von 6)\n\n";
                        s = new String(s + "Um die Adresse zu berechnen, wird ein Addierer benutzt. Dieser bekommt als Summanden den Wert des aktivierten Allzweckregisters und den Offset aus dem Befehlswort.");
                        break;
                    case 4:
                        s = "<b>Adressierung 'Register indirekt' mit Offset </b>(Schritt 4 von 6)\n\n";
                        s = new String(s + "Nachdem die berechnete Adresse ausreichend lange unverändert am Adreßeingang des Speichers angelegen hat, kann der Speicher den Inhalt der zu dieser Adresse gehörenden Speicherzelle heraussuchen.");
                        break;
                    case 5:
                        s = "<b>Adressierung 'Register indirekt' mit Offset </b>(Schritt 5 von 6)\n\n";
                        s = new String(s + "Der Speicher treibt den Datenbus mit dem durch die Adresse bezeichneten Wert.");
                        break;
                    case 6:
                        s = "<b>Adressierung 'Register indirekt' mit Offset </b>(Schritt 6 von 6)\n\n";
                        s = new String(s + "Das Zielregister wird über Steuersignale angewiesen, den Wert des Datenbusses zu übernehmen.");
                        break;
                    case 99:
                        s = "\n\nDie Adressierung 'Register indirekt' mit Offset ist abgeschlossen.";
                }
                break;
            case Rechner.REG_INDIR_OI:
                switch (demonstrationStep)
                {
                    case 1:
                        s = "<b>Adressierung 'Register indirekt' mit Offset und Index </b>(Schritt 1 von 6)\n\n";
                        s = new String(s + "Bei dieser Adressierungsart soll dem Zielregister ein Wert aus dem Speicher zugewiesen werden; die Adresse, unter der dieser Wert im Speicher zu finden ist, ergibt sich als:\n");
                        s = new String(s + "[Wert eines Allzweckregisters] + [sogenannter Offset] + [Wert des Indexregisters]\nWeil nur ein Teil der unteren (rechten) Hälfte des Befehlswortes benötigt wird, um ein Allzweckregister auszuwählen, bleibt hier noch Platz für den Offset (zweite Ziffer von rechts in Hexadezimaldarstellung).");
                        break;
                    case 2:
                        s = "<b>Adressierung 'Register indirekt' mit Offset und Index </b>(Schritt 2 von 6)\n\n";
                        s = new String(s + "Das Steuerwerk hat die untere (rechte) Hälfte des Befehlswortes ausgelesen; diese bezeichnet eines der Allzweckregister.");
                        break;
                    case 3:
                        s = "<b>Adressierung 'Register indirekt' mit Offset und Index </b>(Schritt 3 von 6)\n\n";
                        s = new String(s + "Um die Adresse zu berechnen, wird ein Addierer benutzt. Dieser bekommt als Summanden den Wert des aktivierten Allzweckregisters, den Offset aus dem Befehlswort und den Wert des Indexregisters.");
                        break;
                    case 4:
                        s = "<b>Adressierung 'Register indirekt' mit Offset und Index </b>(Schritt 4 von 6)\n\n";
                        s = new String(s + "Nachdem die berechnete Adresse ausreichend lange unverändert am Adreßeingang des Speichers angelegen hat, kann der Speicher den Inhalt der zu dieser Adresse gehörenden Speicherzelle heraussuchen.");
                        break;
                    case 5:
                        s = "<b>Adressierung 'Register indirekt' mit Offset und Index </b>(Schritt 5 von 6)\n\n";
                        s = new String(s + "Der Speicher treibt den Datenbus mit dem durch die Adresse bezeichneten Wert.");
                        break;
                    case 6:
                        s = "<b>Adressierung 'Register indirekt' mit Offset und Index </b>(Schritt 6 von 6)\n\n";
                        s = new String(s + "Das Zielregister wird über Steuersignale angewiesen, den Wert des Datenbusses zu übernehmen.");
                        break;
                    case 99:
                        s = "\n\nDie Adressierung 'Register indirekt' mit Offset und Index ist abgeschlossen.";
                }
                break;
            case Rechner.MEM_INDIR:
                switch (demonstrationStep)
                {
                    case 1:
                        s = "<b>Adressierung 'Speicher indirekt' </b>(Schritt 1 von 9)\n\n";
                        s = new String(s + "Bei dieser Adressierungsart soll dem Zielregister ein Wert aus dem Speicher zugewiesen werden; die Adresse (A), unter der dieser Wert im Speicher zu finden ist, ist aber in keinem der Register abgelegt (oder über mehrere verteilt), sondern befindet sich wiederum in einer Speicherzelle. Deren Adresse (B) ist bekannt und im Befehlswort enthalten.");
                        break;
                    case 2:
                        s = "<b>Adressierung 'Speicher indirekt' </b>(Schritt 2 von 9)\n\n";
                        s = new String(s + "Die Adresse (B) wird aus dem Befehlswort auf den Adreßbus geschaltet.");
                        break;
                    case 3:
                        s = "<b>Adressierung 'Speicher indirekt' </b>(Schritt 3 von 9)\n\n";
                        s = new String(s + "Nachdem die Adresse (B) ausreichend lange unverändert am Adreßeingang des Speichers angelegen hat, kann der Speicher den Inhalt der zu ihr gehörenden Speicherzelle heraussuchen.\nDieses ist die Adresse (A).");
                        break;
                    case 4:
                        s = "<b>Adressierung 'Speicher indirekt' </b>(Schritt 4 von 9)\n\n";
                        s = new String(s + "Der Speicher treibt den Datenbus mit der Adresse (A).");
                        break;
                    case 5:
                        s = "<b>Adressierung 'Speicher indirekt' </b>(Schritt 5 von 9)\n\n";
                        s = new String(s + "Um unter der Adresse (A) im Speicher 'nachzuschlagen', muß diese in einem der Register gepuffert werden; hierzu wird beispielsweise das Indexregister verwendet (was dem Programmierer natürlich bekannt sein muß, damit er dessen Wert vorher sichern kann).");
                        break;
                    case 6:
                        s = "<b>Adressierung 'Speicher indirekt' </b>(Schritt 6 von 9)\n\n";
                        s = new String(s + "Wieder wird der Adreßbus getrieben, diesmal aber mit Adresse (A) aus dem Indexregister.");
                        break;
                    case 7:
                        s = "<b>Adressierung 'Speicher indirekt' </b>(Schritt 7 von 9)\n\n";
                        s = new String(s + "Wieder wird der Speicher adressiert. Diesmal enthält die ausgewählte Speicherzelle aber den neuen Wert für das Zielregister.\n");
                        s = new String(s + "Beachte: In diesem Beispiel werden die Adressen modulo 64 (dezimal) genommen (also auf den Bereich zwischen 0 und 63 abgebildet).");
                        break;
                    case 8:
                        s = "<b>Adressierung 'Speicher indirekt' </b>(Schritt 8 von 9)\n\n";
                        s = new String(s + "Der Speicher treibt den Datenbus mit dem durch Adresse (A) bezeichneten Wert.");
                        break;
                    case 9:
                        s = "<b>Adressierung 'Speicher indirekt' </b>(Schritt 9 von 9)\n\n";
                        s = new String(s + "Das Zielregister wird über Steuersignale angewiesen, den Wert des Datenbusses zu übernehmen.");
                        break;
                    case 99:
                        s = "\n\nDie Adressierung 'Speicher indirekt' ist abgeschlossen.";
                }
                break;
            case Rechner.UNKNOWN_COMMAND:
                s = "<b>Unbekannte Vorgehensweise!</b>\n\nBitte brechen Sie hier die Simulation ab.";
                break;
        }

        return s;
    } /* end helpText_Adressierungsarten */

} /* end DescriptionLibrary */

