// To Do / (Aenderungen):
// - Wird die gesamte Cache-Line IMMER (STA, ADD; WB/WT) nachgeladen?
//
// - (Bei line size groesser Eins: Anzeigen der geaenderten line durch cache.activate().)
//
// - (Aufblitzen aktivierter Busse bei "<" (repaint() zuviel) - hasBeenActivated fuer Reg., ALU
//   So weit, so gut, aber bei "<" werden Speicherinhalte nicht zurueckgenommen!)
//
// - (Fenster lassen sich ueber ScrollControl verstecken - Menue "Fenster" darf bei VNR nicht vorhanden sein!)
//
// - (scrollThread/blinkThread public in Rechner definieren und so nutzen
//   -> in stop() alle Threads killen, in start() alle erneuern!)
//
// - Blinken?
//
// - (Busse aktiviert mit zwei Senken -> dotted lines in zwei Richtungen!)


package ckelling.baukasten;

import java.applet.*;
import java.awt.*;
import java.lang.*;
import java.util.*;
/* import java.io.*; */
import java.net.*;


// Versionsgeschichte
// 0.8.0  erste Version ohne singleStep()
// 0.8.1  16.02.97
// 0.8.2  23.06.97

/**
 *	Speicherhierarchie.java
 *
 *	Demonstration der Speicherhierarchie
 *	Register -> Cache -> Hauptspeicher
 *	am Beispiel eines
 *	Von-Neumann-Rechners
 *
 *      0.8.2  23.06.97
 *
 *	@author		Carsten Kelling
 *	@version	0.8.3, 28.06.97
 */
public class Speicherhierarchie extends Von_Neumann_Rechner
{
    public final static String VERSIONSTRING = new String("Speicherhierarchie 0.8.3");


	private int	LINESPACE = (LINEWIDTH * 5) / 2;
	private CacheControl					cacheControl;

	protected	EditableMemory		cache;
	protected	TagMemory			tag;
	private		AddressExplainer	addex;

	private   	int					tagResult;
	private		int					lastWriteMode = -1;
	private		int					operand = 0;

	private		DemonstrationStep	demonstrationStep;


	public String getAppletInfo()
	{
		return "Speicherhierarchie aus Komponenten des Rechner-Baukastens von Carsten Kelling";
	}

	public String getVersionString()
	{
		return VERSIONSTRING;
	}


	public void init()
	// wm.inc(42)
	{
	    //PROGRAM = new String("replacetest");
	    PROGRAM = new String("allcommands");

		wm = new SystemMessage("Bitte warten, die Klassen für das applet\n'" + VERSIONSTRING + "'\nwerden geladen.", SystemMessage.WAITLENGTH, (Rechner) this);
		// siehe Anmerkung in VonNeumannRechner.java

		System.out.println(VERSIONSTRING);

		System.out.println("Speicherhierarchie: Initialisierung - Anfang");

		long a = 1;
		//System.out.println("<< 30: " + (a << 30));
		//System.out.println("<< 50: " + (a << 50));

		initialize(new Dimension(775, 490), new Dimension(675, 380)); // defined in class Rechner
	    wm.inc();

		initComponents();
		// wm.inc() wird dort aufgerufen

		ram.initRam(PROGRAM);
		ram.setLabel("Hauptspeicher");
	    wm.inc();

		initCacheComponents(8, 1, 1);
	    wm.inc();
		// Format: Cache-Groesse, Bytes pro Cache-Line, Assoziativitaet
		// Beachte: Die Line-Groesse wird ermittelt, so dass gilt:
		//          Cache-Gr. = Tag-Gr. * Line-Gr.


	//// ABUS (neu definiert: beginnt am Tag-Speicher) ////
		// in Methode initAbus(), aufgerufen aus initCacheComponents()


    //// AddressExplainer ////
	    addex = new AddressExplainer((Rechner) this);
		wm.inc();
	    add(addex);
    	addex.show();
		Rectangle bounds = addex.bounds();
		addex.move(WIDTH - bounds.width - 40, helpText.bounds().y - DIALOGFONTHEIGHT - bounds.height - 10);
		addex.setParameters(ram.getMemorySize(), cache.getMemorySize(), tag.getLineSize(), tag.getAssociativity());



		scrollControl = (ScrollControl) (new ScrollControlSpeicherhierarchie((Rechner) this));
	    wm.inc(20);
		scrollControl.setTitle("Rechner steuern");
		//scrollControl.pack();

		MediaTracker tracker = new MediaTracker(this);
                Image img = getImage(getDocumentBase(), "Bilder/Legende_Farben.gif");
		tracker.addImage(img, 0);
		try
		{
			tracker.waitForAll();
		}
		catch (java.lang.InterruptedException e)
		{
			System.out.println("Eine java.lang.InterruptedException ist aufgetreten!");
		}
		if (tracker.isErrorAny())
			System.out.println("Farblegende konnte nicht geladen werden!");

		cacheControl = new CacheControl(this);
	    wm.inc();
		cacheControl.setTitle("Cache-Einstellungen");

		statInfo = new StatInfo((Rechner) this);

		versionStringX = Math.min(WIDTH, screenSize.width) - stringWidth(DIALOGFONT, VERSIONSTRING) - 35;
		versionStringY = helpText.bounds().y - 5;
	    wm.inc(1);

		tagResult = READ_MISS_NON_CACHED;

		demonstrationStep = new DemonstrationStep((Rechner) this);

		wm.hide();
		wm.dispose();

        setBackground(BACKGROUND);
		show();

		System.out.println("Speicherhierarchie: Initialisierung - Ende");

		paint(onScreenGC);
	} /* end init */


	public void initCacheComponents(int cacheSize, int lineSize, int associativity)
	// wm.inc(1);
	{
		int tagSize = cacheSize / lineSize;
		int cellY = 8;
		if (SMALLFONTSIZE < 12)
			cellY = 4;


	//// Linesize des RAM anpassen ////
		ram.setLineSize(lineSize);


    //// Datenspeicher des Cache ////
        Point coord = ram.getCoordinates("rightTop");

        cache = null;
		cache = new EditableMemory("Cache-Daten", coord.x + 20, coord.y, lineSize, cellY, cacheSize, BITWIDTH, (Rechner) this);
		// Format:                (Titel,         x,            y,       Zellen x,        Speichergroesse,     Verweis auf dieses Objekt)
		//                                                                         Zellen y,         Speicherbreite,
		cache.initRam("zero");
		cache.showOpcodes(showOpcodes);
		cache.setLineSize(lineSize);


    //// Tag-Speicher des Cache ////
        coord = cache.getCoordinates("rightTop");
        int tagWidth = log(ram.getMemorySize(), 2);
		tagWidth = tagWidth - log(cacheSize / associativity, 2);
		int writeMode = TagMemory.WRITE_THROUGH;
		int replaceMode = TagMemory.RANDOM;
		boolean cacheDisabled = false;
		if (tag != null)
		{
			writeMode = tag.getWriteMode();
			replaceMode = tag.getReplaceMode();
			cacheDisabled = tag.isCacheDisabled();
		}
		tag = null;
		tag = new TagMemory("Cache-Tags", coord.x + 20, coord.y, 1, cellY, tagSize, tagWidth, lineSize, associativity, writeMode, replaceMode, (Rechner) this, ram, cache);
		// Format:         (Titel,        x,            y,       Zellen x, Speichergroesse,   Line-Groesse,            WT/WB,                  Verweis auf dieses Objekt,)
		//                                                          Zellen y,       Speicherbreite,     Assoziativitaet,          LRU/random,                  Verweis auf Hauptspeicher-Objekt,
		//                                                                                                                                            Verweis auf Cache-Daten-Objekt
		tag.initRam("zero");
		tag.showOpcodes(showOpcodes);
		if (cacheDisabled)	
			tag.disableCache();

 
        initAbus();
        if (addex != null)
			explainAddress();


	//// DBUS (neu: Verbindung zum Cache) ////
		dbus.saveActivationState();
		if (dbus.existsEdge("cache"))
			dbus.removeEdge("cache");
	    coord = dbus.getCoordinates("start");
	    int dbusStartX = coord.x;
	    int dbusStartY = coord.y;
        coord = cache.getCoordinates("bottom");
        coord.translate(-dbusStartX, -dbusStartY);
        dbus.addEdge("cache", 0, 7, coord.x, 0, 0, coord.y - 7);
        dbus.restoreActivationState();
	    wm.inc(1);


	} /* end initCacheComponents */


	public void removeCacheRessources()
	{
		cache.remove();
		tag.remove();
	}

	public void initAbus()
	// wm.inc(9)
	{
		String oldState[];
		if (abus != null)
			oldState = abus.getActivationState();
		else
			oldState = null;

		Point coord = tag.getCoordinates("right");
		int abusStartX = coord.x;
		int abusStartY = coord.y;
		abus = new SimpleBus("Adreßbus", abusStartX, abusStartY, 85, 0, (Rechner) this);
        abus.setLabelPosition("start", "top");
		if (firstTime)
			wm.inc();

		coord = pc.getCoordinates("right");
		int offset = 30;
		coord.translate(-abusStartX, -abusStartY);
		abus.addEdge("pc", offset, 0, 0, coord.y, coord.x - offset, 0);
		if (firstTime)
			wm.inc(1);

		coord = ireg.getCoordinates("right");
		coord.translate(-abusStartX, -abusStartY);
		Point coord2 = pc.getCoordinates("right");
		coord2.translate(-abusStartX, -abusStartY);
		coord2.translate(-coord.x, -coord.y);
		offset = offset + LINESPACE;
		//abus.addEdge("ireg", offset, 0, 0, coord.y, coord.x - offset, 0);
		//abus.addEdge("ireg", offset, 0, 0, coord2.y + LINESPACE, coord.x + coord2.x + LINESPACE - offset, 0,
		//                     0, coord.y - LINESPACE, -coord2.x - LINESPACE, 0);
		abus.addEdge("ireg", offset, 0,
		                     0, coord.y + (LINESPACE + coord2.y),
		                     coord.x - offset + (coord2.x + LINESPACE) + 50, 0,
		                     0, -(LINESPACE + coord2.y),
		                     -(coord2.x + LINESPACE) - 50, 0);
		if (firstTime)
			wm.inc(1);

		coord = jreg.getCoordinates("right");
		coord.translate(-abusStartX, -abusStartY);
		Point coord3 = ireg.getCoordinates("right");
		coord3.translate(-abusStartX, -abusStartY);
		coord3.translate(-coord.x, -coord.y);
		offset = offset + LINESPACE;
		//abus.addEdge("jreg", offset, 0, 0, coord.y, coord.x - offset, 0);
		//abus.addEdge("jreg", offset, 0, 0, coord2.y + 2*LINESPACE, coord2.x + coord.x + 2*LINESPACE - offset, 0,
		//                     0, coord.y + coord3.y - 2*LINESPACE, coord3.x - coord2.x - 2*LINESPACE, 0);
		abus.addEdge("jreg", offset, 0,
		                     0, coord.y + (2*LINESPACE + coord2.y) + coord3.y,
		                     coord.x - offset + (coord2.x + 2*LINESPACE) + coord3.x + 50, 0,
		                     0, -(2*LINESPACE + coord2.y) - coord3.y,
		                     -(coord2.x + 2*LINESPACE) - coord3.x - 50, 0);
		if (firstTime)
			wm.inc(1);


		coord = ram.getCoordinates("top");
		coord.translate(-abusStartX, -abusStartY);
		coord2 = abus.getCoordinates("end");
		coord2.translate(-abusStartX - LINEWIDTH, -abusStartY);
		abus.addEdge("ram", coord2.x, 0, 0, coord.y - 20, coord.x - coord2.x + 80, 0, 0, 20);
		if (firstTime)
			wm.inc(1);

		if (oldState != null)
			abus.restoreActivationState(oldState);
	} /* end initAbus */



	/**
		this method is called when repaint of the panel is requested
	*/

	public synchronized void paint (Graphics onScreenGC)
	{
		//noSpeedTest long startTime, midTime, endTime;
		//noSpeedTest startTime = System.currentTimeMillis();

		super.paint(onScreenGC, false);

		//noSpeedTest midTime = System.currentTimeMillis();

		// Komponenten neu zeichnen
		//xbus.	paint(offScreenGC);

		//onScreenGC.drawImage(offScreenImage, 0, 0, this);

		cache.	paint(onScreenGC);
		tag.	paint(onScreenGC);
		addex.	paint(onScreenGC);

		//noSpeedTest endTime = System.currentTimeMillis();
		//noSpeedTest System.out.println("SPE.p(): " + (midTime - startTime) + "/" + (endTime - startTime));

	} /* end paint */


	public synchronized void paintActivated (Graphics onScreenGC)
	{
		//noSpeedTest long startTime, midTime, endTime;
		//noSpeedTest startTime = System.currentTimeMillis();

		super.paintActivated(onScreenGC);

		//noSpeedTest midTime = System.currentTimeMillis();

		//xbus. paintActivated(offScreenGC);

		cache.    paintActivated(onScreenGC);
		tag.      paintActivated(onScreenGC);

		//onScreenGC.drawImage(offScreenImage, 0, 0, this);

		//noSpeedTest endTime = System.currentTimeMillis();
		//noSpeedTest System.out.println("SPE.pA(): " + (midTime - startTime) + "/" + (endTime - startTime));

	} /* end paintActivated */


	public void stop()
	{
		super.stop();  // Alle Threads anhalten und beseitigen

		scrollControl.hide();
		cacheControl.hide();
		statInfo.hide();
}

	public void start()
	{
		super.start();  // Alle Threads erzeugen und starten

        if (SMALLFONTSIZE >= 12)
        {
    		Rectangle bounds = scrollControl.bounds();
	    	scrollControl.move (screenSize.width - bounds.width, 0);
	    	Rectangle bbounds = scrollControl.bounds();
	    	bounds = cacheControl.bounds();
    		cacheControl.move (screenSize.width - bounds.width, bbounds.y + bbounds.height);
    		bbounds = cacheControl.bounds();
    		bounds = statInfo.bounds();
    		statInfo.move (screenSize.width - bounds.width, bbounds.y + bbounds.height);

			scrollControl.show();
			cacheControl.show();
			statInfo.show();
    	}
    	else
    	{
    		move(0, 0);
   	        Rectangle bounds = scrollControl.bounds();
    	    Rectangle bbounds = helpText.bounds();
    	    scrollControl.move (640 - bounds.width, 480 - bounds.height - bbounds.height);
    	    bounds = cacheControl.bounds();
    	    cacheControl.move (0, 480 - bounds.height);
    	    bbounds = statInfo.bounds();
    	    statInfo.move (bounds.width, 480 - bbounds.height);

			scrollControl.show();
			cacheControl.hide();
			statInfo.hide();
    	}
	} /* end start */

	public void deactivateAll()
	{
		if (! activationLocked)
		{
		    super.deactivateAll();

			//xbus.deactivate();

			cache.  deactivate();
			tag.    deactivate();
			addex.	deactivate();
		}
	} /* end deactivateAll */


	public void blinkAll()
	{
			super.blinkAll();

			cache.  blink(blinker);
			tag.    blink(blinker);
    } /* end blinkAll */


	/**
	 *  Alle Komponenten, die zwischen ihrem eigentlichem Wert (value)
	 *  und dem gerade sichtbaren Wert (displayValue) unterscheiden,
	 *  führen ein "displayValue = value" durch.
	 */
	public void updateAll()
	{
	    super.updateAll();

		cache.  updateAll();
		tag.    updateAll();
	} /* end updateAll */


	public synchronized void lockAll()
	// Keine Komponente reagiert mehr auf activate(),
	// activateCompared() oder deactivate().
	{
		super.lockAll();

		cache.		lock();
		tag.		lock();

		addex.		lock();
	} /* end lockAll */


	public synchronized void unlockAll()
	// Alle Komponenten reagieren wieder auf activate(),
	// activateCompared() und deactivate().
	{
		super.unlockAll();

		cache.		unlock();
		tag.		unlock();

		addex.		unlock();
	} /* end unlockAll */


	public synchronized void invalidateAllImageCaches()
	{
	    super.invalidateAllImageCaches();

		cache.	invalidateImageCache();
		tag.    invalidateImageCache();

		//xbus.			invalidateImageCache();
	} /* end invalidateAllImageCaches */


	public void showAllOpcodes(boolean b)
	{
		showOpcodes = b;
		ram.	showOpcodes(b);
		cache.	showOpcodes(b);

		paint(onScreenGC);
	}


	protected void setHelpText(int cState, int demStep, int subStep, int tResult, int tAssoc, boolean tIsFullAssoc, int tLineSize)
    // Format:                 c_state,                 Unterschritt,             Assoziativitaet,                  line-Groesse
    //                                     dem.-step,                tagResult,               voll-assoziativ?
    {
		helpText.setText(descriptionLibrary.helpText_Speicherhierarchie(cState, demStep, subStep, tResult, tAssoc, tIsFullAssoc, tLineSize));
    } /* end setHelpText */


	public void explainAddress()
	{
		addex.setParameters(ram.getMemorySize(), cache.getMemorySize(), tag.getLineSize(), tag.getAssociativity());
		
		String str = abus.getSourceString();
		if (! str.equals(""))
		{
			if (str == "pc")
				if (c_state == FETCH_DECODE)
					addex.setAddress((pc.getValue() & 255) - 1);
				else
					addex.setAddress(pc.getValue() & 255);
			else if (str == "ireg")
				addex.setAddress(ireg.getValue() & 255);
			else if (str == "jreg")
				addex.setAddress(jreg.getValue() & 255);
			addex.activate();
		}
		else
			addex.deactivate();
	} /* end explainAddress() */


	public void showWindow(String name)
	{
		super.showWindow(name);

		if (name.equalsIgnoreCase("cachecontrol"))
			cacheControl.show();
		else if (name.equalsIgnoreCase("statinfo"))
			statInfo.show();
	}

	public void hideWindow(String name)
	{
		super.hideWindow(name);

		if (name.equalsIgnoreCase("cachecontrol"))
			cacheControl.hide();
		else if (name.equalsIgnoreCase("statinfo"))
			statInfo.hide();
	}


	public void stopSimulation()
	{
		super.stopSimulation();

		tag.	flushCache();
		cache.  initRam("zero");
		tag.    initRam("zero");
		statInfo.clearStats();
	} /* end stopSimulation */


	public void goUntilBreakpoint()
	// ">> BP"
	{
		statInfo.lock();  // speedup
		super.goUntilBreakpoint();
		statInfo.unlock();
	}

	
	public void singleInstruction(boolean doRepaint)
	// ">>"
	{
		deactivateAll();

		// Die folgenden Zeilen zwischen den Lock-Anweisungen simulieren
		// das Verhalten des Benutzers, bis zum Ende des Befehls
		// ">" zu druecken. Durch das "Lock" wird dabei jede Ausgabe
		// vermieden, sehr wohl aber wird die Statistik fortgefuehrt.
		lockAll();

		demonstrate(false);  // Mindestens einen Unterschritt machen

		boolean dodoRepaint;
		while ( ! ((c_state == FETCH) && (demonstrationStep.get() == 1)))
		// Der erste Unterschritt eines Befehls steht noch bevor.
		{
			dodoRepaint = doRepaint && demonstrationReady && (n_state == FETCH);
			demonstrate(dodoRepaint);
			// Nur der Hilfetext "Der Befehl Xxx ist beeendet." sollte erscheinen.
		}

		unlockAll();

		if (doRepaint)
		{
			updateAll();

			if (c_state == FETCH)
				ram.setAddress(pc.getValue());
			else  // FETCH_DECODE
				ram.setAddress(pc.getValue() - 1);

			// genau die Zelle im Hauptspeicher aktivieren, aus der der Befehl geholt werden wird.
			ram.activate(MEM_COLOR_ACTIVATED, MEM_COLOR);

			int i = ram.getValue() / 256;
			if ((i >= Rechner.NOP) && (i < Rechner.OUT_MEM))
				helpText.appendText("\n\nDer anstehende Befehl ist im RAM hervorgehoben.\n(" + descriptionLibrary.helpText_Speicherhierarchie(i) + ")");
			else
				helpText.appendText("\n" + descriptionLibrary.helpText_Speicherhierarchie(UNKNOWN_COMMAND, 1, demonstrationStep.getSub(), tagResult, tag.getAssociativity(), tag.isFullyAssociative(), tag.getLineSize()));

			paint(onScreenGC);
		}
		//speedup System.out.println("SingleInstruction beendet");
	} /* end singleInstruction */


	public void singleInstructionBack(boolean doRepaint)
	// "<<"
	{
		if (simBufferTop == simBufferBottom)
			outToTraceln("'<<': Keine weiteren Arbeitsschritte mehr gespeichert!");
		else
		{
			setComputer();

			if (doRepaint)
			{
				updateAll();

				ram.setAddress(pc.getValue());
				// genau die Zelle im Hauptspeicher aktivieren, aus der der Befehl geholt werden wird.
				ram.activate(MEM_COLOR_ACTIVATED, MEM_COLOR);

				int i = ram.getValue() / 256;
				if ((i >= Rechner.NOP) && (i < Rechner.OUT_MEM))
					helpText.setText("\nDer anstehende Befehl ist im RAM hervorgehoben.\n(" + descriptionLibrary.helpText_Speicherhierarchie(i) + ")");
				else
					helpText.setText(descriptionLibrary.helpText_Speicherhierarchie(UNKNOWN_COMMAND, 1, demonstrationStep.getSub(), tagResult, tag.getAssociativity(), tag.isFullyAssociative(), tag.getLineSize()));

				paint(onScreenGC);
			}
		}
	} /* end singleInstructionBack */


	public synchronized void bufferComputer()
	{
		Vector v = new Vector(10);

		v.addElement(ram.getAllValues());
		v.addElement(cache.getAllValues());
		v.addElement(tag.getAllValues());
		v.addElement(tag.getAllBits());

        v.addElement(alu.getAllValues());

		v.addElement(new Integer(akku.getValue()));
		v.addElement(new Integer(pc.getValue()));
		v.addElement(new Integer(ireg.getValue()));
		v.addElement(new Integer(jreg.getValue()));

		v.addElement(statInfo.getStats());

		simBuffer[simBufferTop] = v;

		simBufferTop = (simBufferTop+1) % SIM_BUFFER_SIZE;
		if (simBufferTop == simBufferBottom)
			simBufferBottom = (simBufferBottom+1) % SIM_BUFFER_SIZE;

	} /* end bufferComputer */

	public synchronized void setComputer()
	{
			simBufferTop--;
			if (simBufferTop == -1)
				simBufferTop = SIM_BUFFER_SIZE - 1;
			//debug System.out.println("'<<' - simBufferTop = " + simBufferTop);

			Vector v = simBuffer[simBufferTop];

			ram.setValue((int[]) v.elementAt(0));
			cache.setValue((int[]) v.elementAt(1));
			tag.setValue((int[]) v.elementAt(2));
			tag.setBits((Vector) v.elementAt(3));

            alu.setAllValues((String[]) v.elementAt(4));

			akku.setValue( ((Integer) v.elementAt(5)).intValue() );
			pc.setValue( ((Integer) v.elementAt(6)).intValue() );
			ireg.setValue( ((Integer) v.elementAt(7)).intValue() );
			jreg.setValue( ((Integer) v.elementAt(8)).intValue() );

			statInfo.setStats((Hashtable) v.elementAt(9));

			deactivateAll();
			c_state = n_state = FETCH;
			demonstrationStep.reset();
			demonstrationReady = false;
	} /* end setComputer */


	public synchronized void demonstrateBack()
	// "<"
	{
		if (demonstrationStep.stepsSinceReset() <= 1)  // Faengt bei 0 an zu zaehlen!
		// Es muss ueber die Grenze eines Ausfuehrungsschrittes zurueckgegangen werden
		// (z.B. von DECODE zum davorliegenden FETCH) oder gar
		// zum Befehl davor (z.B. von FETCH, Schritt 1 von 5, zu
		// LDA mem indir., Schritt 11 von 11).
		{
			if (c_state == FETCH)
			// Zum vorherigen Befehl zurueckgehen
			{
				if (demonstrationStep.stepsSinceReset() == 1)
				// Bei FETCH, Unterschritt 1 wird setComputer() aufgerufen.
					singleInstructionBack(false);
				singleInstructionBack(false);

				while (! (demonstrationReady && (n_state == FETCH)))
					demonstrate(false);

				updateAll();
				paint(onScreenGC);

				demonstrationStep.dec();
				setHelpText(c_state, demonstrationStep.get(), demonstrationStep.getSub(), tagResult, tag.getAssociativity(), tag.isFullyAssociative(), tag.getLineSize());
				demonstrationStep.inc();
				explainAddress();
			}
			else  // (c_state ist nicht FETCH)
			      // Zum vorherigen Ausfuehrungsschritt zurueckgehen
			{
				int old_c_state = c_state;

				singleInstructionBack(false);

				while (! (demonstrationReady && (old_c_state == n_state)))
					demonstrate(demonstrationReady);

				updateAll();
				paint(onScreenGC);

				demonstrationStep.dec();
				setHelpText(c_state, demonstrationStep.get(), demonstrationStep.getSub(), tagResult, tag.getAssociativity(), tag.isFullyAssociative(), tag.getLineSize());
				demonstrationStep.inc();
				explainAddress();
			}
		}
		else  // (demonstrationStep.stepsSinceReset() >= 2)
		// Der Ausfuehrungsschritt (z.B. FETCH) bleibt derselbe,
		// aber demonstrationStep wird dekrementiert.
		// Dazu wird mit singleInstructionBack() zum letzten FETCH gegangen,
		// dann mit demonstrate() zum richtigen Ausfuehrungsschritt
		// und richtigen demonstrationStep.
		{
			deactivateAll();

			int old_c_state = c_state;
			if (old_c_state == FETCH_DECODE)
				old_c_state = FETCH;
			int ds = demonstrationStep.stepsSinceReset() - 1;

			singleInstructionBack(false);

			while (old_c_state != c_state)
				demonstrate(demonstrationReady);

			while (demonstrationStep.stepsSinceReset() < ds-1)
				demonstrate(demonstrationReady);

			demonstrate(true);

			updateAll();
			paint(onScreenGC);

			/**
			if (ds > 0)
			{
			    demonstrate(true);
			}
			else
    			helpText.setText("\nBitte drücken Sie '&<' oder '>'.");
    		*/
		}
	} /* end demonstrateBack */

	public void demonstrate()
	{
		demonstrate(true);
	}

	public void demonstrate(boolean doRepaint)
	{
		//time long startTime, midTime1, midTime2, midTime3, endTime;
		//time startTime = System.currentTimeMillis();

		if (demonstrationReady == true)
		{
			if (doRepaint == true)
			{
				deactivateAll();
				setHelpText(c_state, 99, 0, 0, tag.getAssociativity(), tag.isFullyAssociative(), tag.getLineSize());
				//paintActivated(onScreenGC);
			}

			if (c_state == FETCH_DECODE)
			{
			    pc.activate();
			    pc.deactivate();
				statInfo.delay(CLK_DECODE);
				statInfo.showStatus("Dekodieren: " + CLK_DECODE + " Takt(e)");
			}

			statInfo.singleStep(c_state);

			c_state = n_state;

			demonstrationReady = false;
			demonstrationStep.reset();
			//speedup System.out.println("- ready");
		}
		else
		{
			switch (c_state)
			{
				case FETCH:
				case FETCH_DECODE:
					switch (demonstrationStep.get())
					{
						case 1:
								outToTracelnS("--Fetch");
								bufferComputer();
							ram.deactivate();
							abus.activate("pc", "start");
							break;
						case 2:
								tag.readRam(pc, ireg);
						case 3:
						case 4:
							demonstrateReadRam("pc", "ireg", demonstrationStep.get() - 1);
							break;
						case 5:
								if (showDecodeCycle)
									n_state = DECODE;
			    				else
			    				{
			    				    c_state = FETCH_DECODE;
									pc.setValue(pc.getValue() + 1);
									n_state = ireg.getValue() / 256;
								}
							ireg.activate();
							demonstrationReady = true;
					}
					break;
				case DECODE:
					switch(demonstrationStep.get())
					{
						case 1:
							outToTracelnS("--Decode, nächster Zustand: 0x" + Integer.toString(ireg.getValue() / 256, 16));
							pc.setValue(pc.getValue() + 1);
							n_state = ireg.getValue() / 256;
						pc.activate();
						demonstrationReady = true;
						statInfo.delay(CLK_DECODE);
						statInfo.showStatus("Dekodieren: " + CLK_DECODE + " Takt(e)");
					}
					break;
				case LDA_MEM:
					switch (demonstrationStep.get())
					{
						case 1:
								outToTraceln("--LDA mem, Adresse: 0x" + Integer.toString(ireg.getValue() & 255, 16) );
							ireg.activate(1);
							abus.activate("ireg", "start");
							break;
						case 2:
								tag.readRam(ireg, akku, 255);
						case 3:
						case 4:
							demonstrateReadRam("ireg", "akku", demonstrationStep.get() - 1);
							break;
						case 5:
								n_state = FETCH;
							akku.activate();
							demonstrationReady = true;
					}
					break;
				case LDA_MEM_INDIR:
					switch (demonstrationStep.get())
					{
						case 1:
								outToTrace("--LDA (mem)", ", Adresse1: 0x" + Integer.toString(ireg.getValue() & 255, 16) );
							ireg.activate(1);
							abus.activate("ireg", "start");
							break;
						case 2:
						    	tag.readRam(ireg, jreg, 255);
								outToTracelnS(", Adresse2: 0x" + Integer.toString(jreg.getValue() & 255, 16) );
						case 3:
						case 4:
							demonstrateReadRam("ireg", "jreg", demonstrationStep.get() - 1);
							break;
						case 5:
							jreg.activate();
							break;
						case 6:
							deactivateAll();
							//jreg.activate();
							abus.activate("jreg", "start");
							break;
						case 7:
						    	tag.readRam(jreg, akku, 255);
						case 8:
						case 9:
							demonstrateReadRam("jreg", "akku", demonstrationStep.get() - 6);
						    break;
						case 10:
								n_state = FETCH;
							akku.activate();
							demonstrationReady = true;
					}
					break;
				case LDA_ABSOL:
					switch (demonstrationStep.get())
					{
						case 1:
								outToTraceln("--LDA absol.");
							ireg.activate(1);
							dbus.activate("ireg", "aluOutput");
							aluOutputBus.activate("end", "akku");
							break;
						case 2:
								akku.setValue(ireg.getValue() & 255);
								n_state = FETCH;
							akku.activate();
							demonstrationReady = true;
					}
					break;
				case STA_ABSOL:
					switch (demonstrationStep.get())
					{
						case 1:
								outToTraceln("--STA absol.");
						case 2:
								tag.writeRam(ireg, akku, 255);
						case 3:
						case 4:
							demonstrateWriteRam("ireg", "akku", demonstrationStep.get());
							if ( (demonstrationStep.get() == 4) &&
							     (!demonstrationStep.subStepEnabled() || (demonstrationStep.getSub() >= 3)) )
							{
									n_state = FETCH;
								demonstrationReady = true;
							}
					}
					break;
				case STA_MEM:
					switch (demonstrationStep.get())
					{
						case 1:
								outToTraceln("--STA mem");
							ireg.activate(1);
							abus.activate("ireg", "start");
							break;
						case 2:
								tag.readRam(ireg, jreg, 255);
						case 3:
						case 4:
							demonstrateReadRam("ireg", "jreg", demonstrationStep.get() - 1);
							break;
						case 5:
							jreg.activate();
							break;
						case 6:
							deactivateAll();
						    	tag.writeRam(jreg, akku, 255);
						case 7:
						case 8:
						case 9:
							demonstrateWriteRam("jreg", "akku", demonstrationStep.get() - 5);
							if ( (demonstrationStep.get() == 9) &&
							     (!demonstrationStep.subStepEnabled() || (demonstrationStep.getSub() >= 3)) )
							{
									n_state = FETCH;
								demonstrationReady = true;
							}
					}
					break;
				case ADD_MEM:
				case SUB_MEM:
				case MUL_MEM:
				case DIV_MEM:
				case AND_MEM:
				case OR_MEM:
				case XOR_MEM:
					switch (demonstrationStep.get())
					{
						case 1:
								outToTraceln("--ALU-Befehl (2 Op.): 0x" + Integer.toString(c_state, 16));
							ireg.activate(1);
							abus.activate("ireg", "start");
							break;
						case 2:
								operand = tag.readRam(ireg, 255);
						case 3:
						case 4:
							demonstrateReadRam("ireg", "aluRightInput", demonstrationStep.get() - 1);
							break;
						case 5:
								aluResult = alu.calculate(c_state, akku.getValue(), operand);
							alu.activate();
							switch (c_state)
							{
								case ADD_MEM:
									statInfo.delay(CLK_ADD);
									statInfo.showStatus("Addieren: " + CLK_ADD + " Takt(e)");
									break;
								case SUB_MEM:
									statInfo.delay(CLK_ADD);
									statInfo.showStatus("Subtrahieren: " + CLK_ADD + " Takt(e)");
									break;
								case MUL_MEM:
									statInfo.delay(CLK_MUL);
									statInfo.showStatus("Multiplizieren: " + CLK_MUL + " Takt(e)");
									break;
								case DIV_MEM:
									statInfo.delay(CLK_DIV);
									statInfo.showStatus("Dividieren: " + CLK_DIV + " Takt(e)");
									break;
								case AND_MEM:
									statInfo.delay(CLK_ADD);
									statInfo.showStatus("UND: " + CLK_ADD + " Takt(e)");
									break;
								case OR_MEM:
									statInfo.delay(CLK_ADD);
									statInfo.showStatus("ODER: " + CLK_ADD + " Takt(e)");
									break;
								case XOR_MEM:
									statInfo.delay(CLK_ADD);
									statInfo.showStatus("Exklusiv-ODER: " + CLK_ADD + " Takt(e)");
									break;
							}
							break;
						case 6:
							aluOutputBus.activate("start", "akku");
							break;
						case 7:
								akku.setValue(aluResult);
								n_state = FETCH;
							aluLeftInputBus.deactivate();
							akku.activate();
							demonstrationReady = true;
					}
					break;
				case INC:
				case DEC:
				case NOT:
					switch (demonstrationStep.get())
					{
						case 1:
								if (c_state == INC)
									outToTraceln("--INC");
								else if (c_state == DEC)
									outToTraceln("--DEC");
								else
									outToTraceln("--NOT");
							aluLeftInputBus.activate("akku", "end");
							break;
						case 2:
								aluResult = alu.calculate(c_state, akku.getValue());
							alu.activate();
							statInfo.delay(CLK_INC);
							if (c_state == INC)
								statInfo.showStatus("Inkrementieren: " + CLK_INC + " Takt(e)");
							else if (c_state == DEC)
								statInfo.showStatus("Dekrementieren: " + CLK_INC + " Takt(e)");
							else
								statInfo.showStatus("NICHT: " + CLK_INC + " Takt(e)");
							break;
						case 3:
							aluOutputBus.activate("start", "akku");
							break;
						case 4:
								akku.setValue(aluResult);
								n_state = FETCH;
							aluLeftInputBus.deactivate();
							akku.activate();
							demonstrationReady = true;
					}
					break;
				case SHL:
				case SHR:
					switch (demonstrationStep.get())
					{
						case 1:
								if (c_state == SHL)
									outToTraceln("--SHL absol.");
								else
									outToTraceln("--SHR absol.");
							ireg.activate(1);
							dbus.activate("ireg", "aluRightInput");
							aluLeftInputBus.activate("akku", "end");
							break;
						case 2:
							alu.activate();
							statInfo.delay(CLK_SHL);
							statInfo.showStatus("Schieben: " + CLK_SHL + " Takt(e)");
							break;
						case 3:
							aluOutputBus.activate("start", "akku");
							break;
						case 4:
								akku.setValue(alu.calculate(c_state, akku.getValue(), ireg.getValue() & 255));
								n_state = FETCH;
							aluLeftInputBus.deactivate();
							akku.activate();
							demonstrationReady = true;
					}
					break;
				case JMP_ABSOL:
				case JZE_ABSOL:
				case JNZ_ABSOL:
				case JLE_ABSOL:
					n_state = FETCH;

					switch(c_state)
					{
						case JMP_ABSOL:
							outToTraceln("--JMP absol.");
							break;
						case JZE_ABSOL:
							if (alu.getFlag("zero") == true)
								outToTraceln("--JZE absol.");
							else
							{
								outToTraceln("--nicht ausgeführter bedingter Sprung: JZE");
								c_state = JZE_NOT_TAKEN;
								demonstrationReady = true;
							}
							break;
						case JNZ_ABSOL:
							if (alu.getFlag("zero") == false)
								outToTraceln("--JNZ absol.");
							else
							{
								outToTraceln("--nicht ausgeführter bedingter Sprung: JNZ");
								c_state = JNZ_NOT_TAKEN;
								demonstrationReady = true;
							}
							break;
						case JLE_ABSOL:
							if ((alu.getFlag("zero") == true) || (alu.getFlag("less") == true))
								outToTraceln("--JLE absol.");
							else
							{
								outToTraceln("--nicht ausgeführter bedingter Sprung: JLE");
								c_state = JLE_NOT_TAKEN;
								demonstrationReady = true;
							}
							break;
					}

					if (demonstrationReady == false)
						switch (demonstrationStep.get())
						{
							case 1:
								ireg.activate(1);
								dbus.activate("ireg", "pc");
								break;
							case 2:
									pc.setValue(ireg.getValue() & 255);
								pc.activate();
								demonstrationReady = true;
								statInfo.delay(CLK_JUMP_TAKEN);
								statInfo.showStatus("Ausgef. Sprung: " + CLK_JUMP_TAKEN + " Takt(e)");
						}
					else
					{
						statInfo.delay(CLK_JUMP_NOT_TAKEN);
						statInfo.showStatus("Nicht ausgef. Sprung: " + CLK_JUMP_NOT_TAKEN + " Takt(e)");
					}

					break;
				case JMP_MEM:
				case JZE_MEM:
				case JNZ_MEM:
				case JLE_MEM:
					n_state = FETCH;

					switch(c_state)
					{
						case JMP_MEM:
							outToTraceln("--JMP mem");
							break;
						case JZE_MEM:
							if (alu.getFlag("zero") == true)
								outToTraceln("--JZE mem");
							else
							{
								outToTraceln("--nicht ausgeführter bedingter Sprung: JZE");
								c_state = JZE_NOT_TAKEN;
								demonstrationReady = true;
							}
							break;
						case JNZ_MEM:
							if (alu.getFlag("zero") == false)
								outToTraceln("--JNZ mem");
							else
							{
								outToTraceln("--nicht ausgeführter bedingter Sprung: JNZ");
								c_state = JNZ_NOT_TAKEN;
								demonstrationReady = true;
							}
							break;
						case JLE_MEM:
							if ((alu.getFlag("zero") == true) || (alu.getFlag("less") == true))
								outToTraceln("--JLE mem");
							else
							{
								outToTraceln("--nicht ausgeführter bedingter Sprung: JLE");
								c_state = JLE_NOT_TAKEN;
								demonstrationReady = true;
							}
							break;
					}

					if (demonstrationReady == false)
						switch (demonstrationStep.get())
						{
							case 1:
								ireg.activate(1);
								abus.activate("ireg", "start");
								break;
							case 2:
								    tag.readRam(ireg, pc, 255);
							case 3:
							case 4:
								demonstrateReadRam("ireg", "pc", demonstrationStep.get() - 1);
								break;
							case 5:
								pc.activate();
								demonstrationReady = true;
								statInfo.delay(CLK_JUMP_TAKEN);
								statInfo.showStatus("Ausgef. Sprung: " + CLK_JUMP_TAKEN + " Takt(e)");
						}
					else
					{
						statInfo.delay(CLK_JUMP_NOT_TAKEN);
						statInfo.showStatus("Nicht ausgef. Sprung: " + CLK_JUMP_NOT_TAKEN + " Takt(e)");
					}
					break;
				case IN_MEM:
					switch (demonstrationStep.get())
					{
						case 1:
								outToTraceln("--IN mem");
						case 2:
								tag.writeRam(ireg, (int) (Math.random() * Math.pow(2, (double) BITWIDTH)), 255);
						case 3:
						case 4:
							demonstrateWriteRam("ireg", "inOut", demonstrationStep.get() - 1);
							if ( (demonstrationStep.get() == 4) &&
							     (!demonstrationStep.subStepEnabled() || (demonstrationStep.getSub() >= 3)) )
							{
									n_state = FETCH;
								demonstrationReady = true;
							}
					}
					break;
				case OUT_MEM:
					switch (demonstrationStep.get())
					{
						case 1:
								outToTraceln("--OUT mem");
							ireg.activate(1);
							abus.activate("ireg", "start");
							break;
						case 2:
								int data = tag.readRam(ireg, 255);
								outToTraceln("  OUT-Port <= 0x" + Integer.toString(data, 16));
						case 3:
						case 4:
							demonstrateReadRam("ireg", "inOut", demonstrationStep.get() - 1);
							if ( (demonstrationStep.get() == 4) &&
							     (!demonstrationStep.subStepEnabled() || (demonstrationStep.getSub() >= 3)) )
							{
									n_state = FETCH;
								demonstrationReady = true;
							}
					}
					break;
				case NOP:
						outToTraceln("--NOP");
						n_state = FETCH;
					demonstrationReady = true;
					statInfo.delay(CLK_NOP);
					statInfo.showStatus("Nichtstun (NOP): " + CLK_NOP + " Takt(e)");
					break;
				case JZE_NOT_TAKEN:
				case JNZ_NOT_TAKEN:
				case JLE_NOT_TAKEN:
						n_state = FETCH;
					demonstrationReady = true;
					break;
				case UNKNOWN_COMMAND:
				default:
						outToTraceln("     --- Unbekannter Opcode! ---");
						c_state = UNKNOWN_COMMAND;
						n_state = FETCH;
					demonstrationReady = true;
					break;
			}

			//time midTime1 = System.currentTimeMillis();
			//time midTime2 = midTime3 = midTime1;

			if (doRepaint == true)
			{
				//helpText.setText(descriptionLibrary.helpText_Speicherhierarchie(c_state, demonstrationStep.get(), demonstrationStep.getSub(), tagResult, tag.getAssociativity(), tag.isFullyAssociative(), tag.getLineSize()) );
				setHelpText(c_state, demonstrationStep.get(), demonstrationStep.getSub(), tagResult, tag.getAssociativity(), tag.isFullyAssociative(), tag.getLineSize());
				//time midTime2 = System.currentTimeMillis();
				//helpText_no_repaint helpText.repaint();
				//time midTime3 = System.currentTimeMillis();
				explainAddress();
			}

			demonstrationStep.inc();

			//time endTime = System.currentTimeMillis();
			//time System.out.print("SPE.demonstrate(): " + (midTime1 - startTime) + "+"  + (midTime2 - midTime1) + "+" + (midTime3 - midTime2) + "+" + (endTime - midTime3) + "  |  ");
		}
	} /* end demonstrate */


	private void demonstrateReadRam(String adr, String dest, int demStep)
	{
		switch(demStep)
		{
			case 1:
			    tag.activateCompared();
			    break;
			case 2:
			    tagResult = tag.getLastResult();
			    if (tagResult == READ_HIT)
			    {
			    	ram.activateCompared();
			        cache.activate(MEM_COLOR);
			        tag.activate();

			        if (tag.getAssociativity() == TagMemory.DIRECT_MAPPED)
			        {
			        	statInfo.delay(CLK_READ_HIT_DIRECT_MAPPED);
						statInfo.showStatus("Lesen aus Cache (direct-mapped): " + CLK_READ_HIT_DIRECT_MAPPED + " Takt(e)");
					}
			        else
			        {
				        statInfo.delay(CLK_READ_HIT);
						statInfo.showStatus("Lesen aus Cache: " + CLK_READ_HIT + " Takt(e)");
					}
			    }
			    else if ( (tagResult == READ_MISS_CACHED) || (tagResult == READ_MISS_NON_CACHED))
			    {
			    	abus.activate(adr, "start", "ram");
			        ram.activate();

			    	statInfo.delay(CLK_READ_MISS_LEADIN + CLK_READ_MISS_OTHER*(tag.getLineSize() - 1));
			    	String status = new String("Lesen aus Hauptspeicher: " + CLK_READ_MISS_LEADIN);
			    	for (int i = 0; i < tag.getLineSize() - 1; i++)
			    		status = status + "+" + CLK_READ_MISS_OTHER;
					statInfo.showStatus(status + " Takt(e)");
			    }
			    else if (tagResult == READ_MISS_SYNC)
			    {
			    	if (! demonstrationStep.subStepEnabled())
			    		demonstrationStep.startSubStepping(3);

			    	switch (demonstrationStep.getSub())
			    	{
			    		case 1:
			    			cache.saveLine();
			    			cache.saveAddress();
			    			int flushLine[] = tag.getFlushLine();
			    			int cacheAddress = cache.getAddress() & (cache.getMemorySize() - tag.getLineSize());
			    			for (int i = 0; i < flushLine.length; i++)
			    			{
			    				cache.setAddress(cacheAddress + i);
			    				cache.setValue(flushLine[i]);
			    			}
					    	cache.activate(MEM_COLOR_ACTIVATED);
					    	cache.restoreLine();
					    	abus.activate(adr, "start", "ram");
				    		dbus.activate("cache", "start");
			    			break;
			    		case 2:
			    			ram.saveAddress();  // !!!
			    			for (int i = 0; i < tag.getLineSize(); i++)
			    			{
						    	ram.setAddress(tag.getFlushAddress() + i);  // !!!
						    }
					    	ram.activate(MEM_COLOR_ACTIVATED);
			    			break;
			    		case 3:
			    			ram.deactivate();
			    			ram.paint(onScreenGC);
			    			ram.restoreAddress();  // !!!
					        ram.activate();
					        cache.deactivate();
					        cache.paint(onScreenGC);
					        cache.restoreAddress();
					        dbus.deactivate();

			    			statInfo.delay(CLK_READ_MISS_LEADIN + CLK_READ_MISS_OTHER*(tag.getLineSize() - 1));
					    	String status = new String("Lesen aus Hauptspeicher: " + CLK_READ_MISS_LEADIN);
					    	for (int i = 0; i < tag.getLineSize() - 1; i++)
					    		status = status + "+" + CLK_READ_MISS_OTHER;
							statInfo.showStatus(status + " Takt(e)");
			    			break;
			    		default:
			    			out("FEHLER in Speicherhierarchie.demonstrateReadRam: subStep > 3");
			    	}
			    }
			    else
			        out("FEHLER in Speicherhierarchie.demonstrateReadRam: " + Integer.toString(c_state, 16) + ", " + Integer.toString(tagResult, 16));
			    break;
			case 3:
			    tag.deactivate();
			    if (tagResult == READ_HIT)
			    {
			    	ram.deactivate();
			    	if (dest.equalsIgnoreCase("akku"))
			    	{
						dbus.activate("cache", "aluOutput");
						aluOutputBus.activate("end", "akku");
					}
					else
				        dbus.activate("cache", dest);
			    }
				else if ((tagResult == READ_MISS_CACHED) ||
				         (tagResult == READ_MISS_SYNC))
				{
				    cache.activate();
				    tag.activate();
			    	if (dest.equalsIgnoreCase("akku"))
			    	{
						//dbus.activate("start", "aluOutput", "cache");
						dbus.activate("start", "cache", "aluOutput");
						aluOutputBus.activate("end", "akku");
					}
					else
					    dbus.activate("start", dest, "cache");
				}
			    else if (tagResult == READ_MISS_NON_CACHED)
			    {
			    	if (dest.equalsIgnoreCase("akku"))
			    	{
						dbus.activate("start", "aluOutput");
						aluOutputBus.activate("end", "akku");
					}
					else
						dbus.activate("start", dest);
			    }

			    if (dest.equalsIgnoreCase("aluRightInput"))
					aluLeftInputBus.activate("akku", "end");
		}
	} /* end demonstrateReadRam */

	private void demonstrateWriteRam(String adr, String src, int demStep)
	{
		switch(demStep)
		{
			case 1:
				if (adr.equalsIgnoreCase("ireg"))
					ireg.activate(1);
				else if (adr.equalsIgnoreCase("jreg"))
				{
					//	jreg.activate();
					//System.out.println("JReg absichtlich nicht aktiviert");
				}
				else
					out("FEHLER in Speicherhierarchie.demonstrateWriteRam: Unbekanntes Register für Adresse!");

				abus.activate(adr, "start");
				break;
			case 2:
			    tag.activateCompared();
			    break;
			case 3:
			    tagResult = tag.getLastResult();
			    if (tagResult == WRITE_THROUGH)
			    {
			    	abus.activate(adr, "start", "ram");
			    	if (src.equalsIgnoreCase("akku"))
			    	{
						aluOutputBus.activate("akku", "end");
						dbus.activate("aluOutput", "start", "cache");
					}
					else
						dbus.activate(src, "start", "cache");
				}
			    else if (tagResult == WRITE_THROUGH_AROUND)
			    {
			    	abus.activate(adr, "start", "ram");
			    	if (src.equalsIgnoreCase("akku"))
			    	{
						aluOutputBus.activate("akku", "end");
						dbus.activate("aluOutput", "start");
					}
					else
						dbus.activate(src, "start");
				}
			    else if ( (tagResult == WRITE_BACK_HIT) || (tagResult == WRITE_BACK_MISS_CLEAN) )
			    {
			    	if (src.equalsIgnoreCase("akku"))
			    	{
						aluOutputBus.activate("akku", "end");
						dbus.activate("aluOutput", "cache");
					}
					else
						dbus.activate(src, "cache");
				}
			    else if (tagResult == WRITE_BACK_AROUND)
			    {
			    	abus.activate(adr, "start", "ram");
			    	if (src.equalsIgnoreCase("akku"))
			    	{
						aluOutputBus.activate("akku", "end");
						dbus.activate("aluOutput", "start");
					}
					else
						dbus.activate(src, "start");
				}
			    else if (tagResult == WRITE_BACK_MISS_DIRTY)
			    {
			        cache.activate(MEM_COLOR_ACTIVATED);
			        abus.activate("start", "ram");
					dbus.activate("cache", "start");
				}
			    else
			        out("FEHLER in Speicherhierarchie.demonstrateWriteRam: " + Integer.toString(c_state, 16) + ", " + Integer.toString(tagResult, 16));
			    break;
			case 4:
			    tag.deactivate();
			    if (tagResult == WRITE_THROUGH)
			    {
					ram.activate();
					cache.activate();
					tag.activate();
					statInfo.delay(CLK_WRITE_THROUGH);
					statInfo.showStatus("Schreiben (WT: RAM bestimmt): " + CLK_WRITE_THROUGH + " Takt(e)");
				}
			    else if (tagResult == WRITE_THROUGH_AROUND)
			    {
					ram.activate(MEM_COLOR);
					statInfo.delay(CLK_WRITE_THROUGH);
					statInfo.showStatus("Schreiben (write around: nur RAM): " + CLK_WRITE_THROUGH + " Takt(e)");
				}
			    else if ( (tagResult == WRITE_BACK_HIT) || (tagResult == WRITE_BACK_MISS_CLEAN) )
			    {
			    	ram.activateCompared();
					cache.activate(MEM_COLOR);
					tag.activate();
					statInfo.delay(CLK_WRITE_BACK_HIT_OR_CLEAN);
					if (tagResult == WRITE_BACK_HIT)
						statInfo.showStatus("Schreiben (WB, hit: nur Cache): " + CLK_WRITE_BACK_HIT_OR_CLEAN + " Takt(e)");
					else
						statInfo.showStatus("Schreiben (WB, miss, clean: nur Cache): " + CLK_WRITE_BACK_HIT_OR_CLEAN + " Takt(e)");
				}
			    else if (tagResult == WRITE_BACK_AROUND)
			    {
					ram.activate(MEM_COLOR);
					statInfo.delay(CLK_WRITE_THROUGH);
					statInfo.showStatus("Schreiben (write around: nur RAM): " + CLK_WRITE_THROUGH + " Takt(e)");
				}
			    else if (tagResult == WRITE_BACK_MISS_DIRTY)
			    {
			    	if (! demonstrationStep.subStepEnabled())
			    		demonstrationStep.startSubStepping(3);

			    	switch (demonstrationStep.getSub())
			    	{
			    		case 1:
							ram.activate(MEM_COLOR_ACTIVATED);
							statInfo.delay(CLK_WRITE_THROUGH);
							statInfo.showStatus("Schreiben in den Hauptsp.: " + CLK_WRITE_THROUGH + " Takt(e)");
			    			break;
			    		case 2:
						    deactivateAll();

							if (adr.equalsIgnoreCase("ireg"))
								ireg.activate(1);
							//else if (adr.equalsIgnoreCase("jreg"))
							//	jreg.activate();
							abus.activate(adr, "start");

					    	if (src.equalsIgnoreCase("akku"))
					    	{
								aluOutputBus.activate("akku", "end");
								dbus.activate("aluOutput", "cache");
							}
							else
								dbus.activate(src, "cache");
			    			break;
			    		case 3:
						    cache.activate();
						    tag.activate();
						    statInfo.delay(CLK_WRITE_BACK_HIT_OR_CLEAN);
							statInfo.showStatus("Schreiben (WB, miss, clean: nur Cache): " + CLK_WRITE_BACK_HIT_OR_CLEAN + " Takt(e)");
							demonstrationReady = true;
			    			break;
			    		default:
			    			out("FEHLER in Speicherhierarchie.demonstrateWriteRam: subStep > 3");
			    	}
				}

				if (! demonstrationStep.subStepEnabled())
				// Wenn nicht WRITE_BACK_MISS_DIRTY, ist hier SOFORT Schluss,
				// sonst erst bei (demonstrationStep.getSub() == 3)
			    	demonstrationReady = true;
		}
	} /* end demonstrateWriteRam */


} /* end Speicherhierarchie */


