// Todo:
//	- Dokumentieren
//	-	 "", was zu aendern waere, falls neue Komponenten hinzu kaemen
//
//

package ckelling.baukasten;

import java.applet.*;
import java.awt.*;
import java.awt.image.*;
import java.lang.*;
import java.util.*;
/* import java.io.*; */
import java.net.*;


// Versionsgeschichte
// 1.1.0  erste Version ohne singleStep()
// 1.1.1  21.02.97
// 1.1.2  23.06.97
// 1.1.4  28.06.97

/**
 *	Demonstration eines
 *	Von-Neumann-Rechners
 *	mit animierten Komponenten
 *
 *	@author		Carsten Kelling
 *	@version	1.1.5, 01.08.97
 */
public class Von_Neumann_Rechner extends Rechner
{
	///// Konstanten Anfang /////

    public final static String VERSIONSTRING = "Von-Neumann-Rechner 1.1.5";

	// Zustaende des Automaten im Leitwerk
	// sind definiert in Klasse "Rechner"

	///// Konstanten Ende /////


	protected SimpleBus		    dbus;
	protected SimpleBus		    abus;
	protected SimpleBus         aluLeftInputBus;
	protected SimpleBus         aluOutputBus;

	protected Register16        jreg;
	protected Register16	    akku;
	protected Register8	    	pc;

	protected Register16Split   ireg;

	protected EditableMemory    ram;

	protected ALU 			    alu;

	//AC_aus protected ActivationControl	activationControl;
	protected ScrollControl		    scrollControl;
	protected StatInfo				statInfo;
	protected DescriptionLibrary	descriptionLibrary;
	protected RTFTextArea		    helpText;

	protected SystemMessage		wm;  // waitMessage

	// nur temporaer benoetigte Variablen
	protected int 					aluResult = 0;
	protected String 				aluOpcode = "";


	public String getAppletInfo()
	{
		return "Von-Neumann-Rechner aus Komponenten des Rechner-Baukastens von Carsten Kelling";
	}

	public String getVersionString()
	{
		return VERSIONSTRING;
	}


	public void init()
	{
		wm = new SystemMessage("Bitte warten, die Klassen für das applet\n'" + VERSIONSTRING + "'\nwerden geladen.", SystemMessage.WAITLENGTH, (Rechner) this);
		// Der SystemMessage wird mitgeteilt, daß die Summe der n aller Aufrufe an ihr inc(n) genau WAITLENGTH betragen wird,
		// also die Anzahl der Striche, die in ihre Warteanzeige passen.
		// Dadurch ist gewaehrleistet, daß bei inc(1) auch wirklich 1 Strich erscheint
		// (und nicht beispielsweise 0.8 = 0 Striche).

		System.out.println(VERSIONSTRING);

		System.out.println("VNR: Initialisierung - Anfang");

		initialize(new Dimension(510, 490), new Dimension(510, 380)); // defined in class Rechner
	    wm.inc(9);

		if (PROGRAM.equals(""))
			PROGRAM = "bubblesort";  // kein Parameter in HTML-Datei

		initComponents();

		//AC_aus activationControl = new ActivationControl((Rechner) this);
		//AC_aus activationControl.setTitle("Activate a connection");
		//AC_aus activationControl.setComponents(ram, dbus, abus, aluLeftInputBus, aluOutputBus, alu, akku, pc, ireg, jreg);
		//AC_aus add (activationControl);
		//AC_aus activationControl.pack();

		scrollControl = new ScrollControl((Rechner) this);
	    wm.inc(47);
		scrollControl.setTitle("Rechner steuern");
		//scrollControl.pack();

		//SI_aus statInfo = new StatInfo((Rechner) this);
		wm.inc(1);

		/*
		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)
		{
			out("FEHLER bei dem Laden von Legende_Farben.gif: java.lang.InterruptedException");
		}
		if (tracker.isErrorAny())
			out("FEHLER: Legende_Farben.gif konnte nicht geladen werden!");
		*/

		wm.inc(4);

		//AC_aus Rectangle bounds = bounds();
		//AC_aus Rectangle abounds;

		//AC_aus coord = abus.getCoordinates("end");
		//AC_aus abounds = activationControl.bounds();
		//AC_aus activationControl.move (coord.x + 5, bounds.y);

		//Rectangle bbounds = scrollControl.bounds();
		//scrollControl.resize (bbounds.width, bbounds.height + 30);
	    wm.inc(1);
		//scrollControl.move (bounds.x + bounds.height, bounds.y);
		//AC_aus scrollControl.move (coord.x + 5,
		//AC_aus 						bounds.y +  abounds.height);


		//System.out.println("FontMetrics: " + onScreenGC.getFontMetrics().toString());
		//System.out.println("Fonthöhe: " + new Integer(onScreenGC.getFontMetrics().getHeight()).toString());
		//System.out.println("Fontbreite '1': " + new Integer(onScreenGC.getFontMetrics().stringWidth("1")).toString());
		//System.out.println("Fontbreite '0': " + new Integer(onScreenGC.getFontMetrics().stringWidth("0")).toString());

		show();
		scrollControl.show();

	    wm.inc(1);

        //Point coord = dbus.getCoordinates("end");
		versionStringX = WIDTH - stringWidth(DIALOGFONT, VERSIONSTRING) - 5;
		//versionStringY = coord.y;
		versionStringY = DIALOGFONTHEIGHT;
	    wm.inc(1);

		wm.hide();
		wm.dispose();

		System.out.println("VNR: Initialisierung - Ende");

		paint(onScreenGC);
		//{{INIT_CONTROLS
		//}}
} /* end init */


    protected void initComponents()
    {
		//ram = new SimpleMemory("Programm- und Datenspeicher", 55, 15, MEMORYSIZE, BITWIDTH, (Rechner) this);
		// Format: (<Bezeichner>, x, y, Speichergroesse, Bitbreite, Verweis auf Von_Neumann_Rechner)
		//ram.initRam(PROGRAM);

		int cellY = 8;
		int ramX = 2;
		if (SMALLFONTSIZE < 12)
		{
			cellY = 4;
			ramX = 12;
		}
		ram = new EditableMemory("Programm- und Datenspeicher", ramX, 36, 8, cellY, MEMORYSIZE, BITWIDTH, (Rechner) this);
	    wm.inc(8);
		// Format:              (Titel,                         x,    y,  Zellen x, Speichergroesse,      Verweis auf dieses Objekt)
		//                                                                   Zellen y,          Speicherbreite,
		ram.initRam(PROGRAM);
		ram.setEditable((editableFlags & 16) == 16);
		ram.showOpcodes(showOpcodes);
		ram.allowBreakpoints(true);

		Point coord = ram.getCoordinates("bottom");
		int dbusStartX = coord.x;
		int dbusStartY = coord.y;

	    wm.inc(3);


	//// DBUS ////
		dbus = new SimpleBus("Datenbus", dbusStartX, dbusStartY, 0, 185, (Rechner) this);
	    wm.inc(1);
		/**
		   Format: (<Bezeichner>, x1, y1, width, height, Referenz auf dieses Objekt um Farb-Konstanten benutzen zu können)
		   Hauptstrang des Busses verläuft von
		   (x1, y1) nach (x1 + width, y1 + height).
		*/
		dbus.setLabelPosition("left", "start");

		dbus.addEdge("pc",              0,  45,     50,     0);
		dbus.addEdge("ireg",            0,  95,     50,     0);
		dbus.addEdge("jreg",            0,  145,    50,     0);
		dbus.addEdge("aluRightInput",   0,  50,     -50,    0,  0,  10);
		dbus.addEdge("inOut",           0,  180,    50,   0);
	    wm.inc(1);

		/**
			Format: (<Bezeichner>, x3, y3, x4, y4)
			Eine Abzweigung vom Bus beginnt bei (x1+x3, y1+y3)
			und verläuft x4 Pixel nach rechts sowie y4 nach unten.
		*/

		coord = dbus.getCoordinates("pc", "end");
		pc = new Register8("Programmzähler", coord.x, coord.y, "left", (Rechner) this);
		pc.setEditable((editableFlags & 8) == 8);
		add(pc); pc.show();
	    wm.inc(1);

		coord = dbus.getCoordinates("ireg", "end");
		ireg = new Register16Split("Befehlsregister", coord.x, coord.y, "left", (Rechner) this);
		ireg.setEditable((editableFlags & 4) == 4);
		add(ireg); ireg.show();
	    wm.inc(1);

		coord = dbus.getCoordinates("jreg", "end");
		jreg = new Register16("Hilfsregister", coord.x, coord.y, "left", (Rechner) this);
		jreg.setEditable((editableFlags & 2) == 2);
		add(jreg); jreg.show();
	    wm.inc(1);


	//// ALU ////
		coord = dbus.getCoordinates("aluRightInput", "end");
		alu = new ALU("ALU", coord.x, coord.y, "right", (Rechner) this);
	    wm.inc(1);


	//// ABUS ////
		coord = ram.getCoordinates("right");
		int abusStartX = coord.x;
		int abusStartY = coord.y;
		abus = new SimpleBus("Adreßbus", abusStartX, abusStartY, 65, 0, (Rechner) this);
	    wm.inc(1);

		coord = pc.getCoordinates("right");
		coord.translate(-abusStartX, -abusStartY);
		abus.addEdge("pc", 20, 0, 0, coord.y, coord.x - 20, 0);

		coord = ireg.getCoordinates("right");
		coord.translate(-abusStartX, -abusStartY);
		abus.addEdge("ireg", 40, 0, 0, coord.y, coord.x - 40, 0);

		coord = jreg.getCoordinates("right");
		coord.translate(-abusStartX, -abusStartY);
		abus.addEdge("jreg", 60, 0, 0, coord.y, coord.x - 60, 0);
	    wm.inc(1);


	//// Bus at output of ALU ////
		coord = alu.getCoordinates("output");
		aluOutputBus = new SimpleBus("", coord.x, coord.y, 0, 25, (Rechner) this);
	    wm.inc(1);

		coord = aluOutputBus.getCoordinates("end");
		coord.translate(-dbusStartX, -dbusStartY);
		dbus.addEdge("aluOutput", 0, coord.y, coord.x, 0);

		aluOutputBus.addEdge("akku", 0, 25, -20, 0);


	//// Akku ////
		coord = aluOutputBus.getCoordinates("akku", "end");
		akku = new Register16("Akku", coord.x, coord.y, "right", (Rechner) this);
		akku.setEditable((editableFlags & 1) == 1);
		add(akku); akku.show();
	    wm.inc(1);


	//// Bus at left input of ALU ////
		coord = alu.getCoordinates("leftInput");
		int aluLeftInputBusStartX = coord.x;
		int aluLeftInputBusStartY = coord.y - 10 - LINEWIDTH;

		aluLeftInputBus = new SimpleBus("", aluLeftInputBusStartX, aluLeftInputBusStartY, 0, 10 + LINEWIDTH, SimpleBus.NONE, (Rechner) this);
	    wm.inc(1);
		// SimpleBus.NONE (statt Standard-Wert SimpleBus.BLACK_ROOT):
		// Hauptstrang des Busses soll farblich
		// nicht von seinen Aesten zu unterscheiden sein

		coord = akku.getCoordinates("left");
		coord.translate(-aluLeftInputBusStartX, -aluLeftInputBusStartY);
		aluLeftInputBus.addEdge("akku", 0, 0, coord.x - 10 - LINEWIDTH, 0, 0, coord.y, 10 + LINEWIDTH, 0);
		aluLeftInputBus.setFlag("akku", SimpleBus.NONE);  // kein Tri-State-Gatter zum Akku hin einfuegen


	//// Connections ////
		// Den Bussen wird mitgeteilt, welche Komponenten an welchen Kanten angeschlossen sind;
		// bei einem bus.activate() kann der Bus dann den passenden Wert annehmen
		// und im InfoTip zeigen.

		abus.setConnection("start", ram, OUT);
		abus.setConnection("pc", pc, IN);
		abus.setConnection("ireg", ireg, IN);
		abus.setConnection("jreg", jreg, IN);

		dbus.setConnection("start", ram, INOUT);
		dbus.setConnection("pc", pc, OUT);
		dbus.setConnection("ireg", ireg, INOUT);
		dbus.setConnection("jreg", jreg, OUT);
		dbus.setConnection("aluRightInput", alu, OUT);
		dbus.setConnection("aluOutput", aluOutputBus, INOUT);

		aluOutputBus.setConnection("start", alu, IN);
		aluOutputBus.setConnection("akku", akku, INOUT);

		aluLeftInputBus.setConnection("end", alu, OUT);
		aluLeftInputBus.setConnection("akku", akku, IN);

		if ( (abus.getNumberOfConnections() != abus.getNumberOfEdges() + 1) ||  // "start" angeschlossen
		     (dbus.getNumberOfConnections() != dbus.getNumberOfEdges()) ||  // "start" ja, "inOut" nicht angeschlossen
		     (aluOutputBus.getNumberOfConnections() != aluOutputBus.getNumberOfEdges() + 1) ||  // "start" angeschlossen
		     (aluLeftInputBus.getNumberOfConnections() != aluLeftInputBus.getNumberOfEdges() + 1) )  // "end" angeschlossen
			out("Sie haben eine Kante eines Busses nicht mit setConnection() angeschlossen!");


		coord = dbus.getCoordinates("end");
		helpText = new RTFTextArea(SMALLFONT, 4, 70);
		add(helpText);

		//ColorModel cm = helpText.getColorModel();
		//System.out.println("ColorModel: " + cm.toString());
		//DirectColorModel dcm = new DirectColorModel(8, 0, 0, 0);

		helpText.move(0, coord.y + 10);
		helpText.resize(Math.min(WIDTH, screenSize.width), HEIGHT - coord.y - 10);
		//if (SMALLFONTSIZE <= 10)
		//	helpText.resize(Math.min(WIDTH, screenSize.width), HEIGHT - coord.y + 11);
		//else
		//	helpText.resize(Math.min(WIDTH, screenSize.width), HEIGHT - coord.y - 10);
	    wm.inc(2);

		descriptionLibrary = new DescriptionLibrary();
	    wm.inc(1);

		helpText.setText("Hier werden Hilfetexte erscheinen.");
	    wm.inc(1);

	} /* end initComponents */
	
	public void initRam(String program)
	{
		PROGRAM = program;
		ram.initRam(program);
	}


	/**
		this method is called when repaint of the panel is requested
	*/

	public synchronized void paint (Graphics onScreenGC)
	{
		paint(onScreenGC, false);
	}
	public synchronized void paint (Graphics onScreenGC, boolean calledFromExtendingClass)
	{
		//noSpeedTest long startTime, midTime, endTime;
		//noSpeedTest startTime = System.currentTimeMillis();

		Rectangle size = bounds();

		// Flaeche des Applets loeschen
		offScreenGC.setColor (BACKGROUND);
		offScreenGC.fillRect(0, 0, size.width, size.height);

		// Komponenten neu zeichnen
		// Diese Komponenten werden "schnell" gezeichnet und
		// benutzen keine AWT-Komponenten

		abus.	paint(offScreenGC);
		dbus.	paint(offScreenGC);
		aluLeftInputBus.paint(offScreenGC);
		aluOutputBus.paint(offScreenGC);

		akku.	paint(offScreenGC);
		pc. 	paint(offScreenGC);
		ireg.	paint(offScreenGC);
		jreg.	paint(offScreenGC);

		//noSpeedTest midTime = System.currentTimeMillis();

		alu.	paint(offScreenGC);

		offScreenGC.setColor(Color.black);
		offScreenGC.setFont(DIALOGFONT);

		Point coord = dbus.getCoordinates("inOut", "end");
		offScreenGC.drawString("Ein-/Ausgabe", coord.x + 5, coord.y + 5);

		offScreenGC.drawString(VERSIONSTRING, versionStringX, versionStringY);

		if (! calledFromExtendingClass)
		{
			//if (scrollControl != null)
			//	scrollControl.show();
			onScreenGC.drawImage(offScreenImage, 0, 0, this);
		}

	    // Komponenten, die "langsam" gezeichnet werden und
      	// AWT-Komponenten benutzen
		helpText.repaint();
		ram.	paint(onScreenGC);

		//noSpeedTest endTime = System.currentTimeMillis();
		//noSpeedTest System.out.println("--- VNR.p(): " + (midTime - startTime) + "/" + (endTime - startTime) + "  |  ");

	} /* end paint */


	public synchronized void paintActivated (Graphics onScreenGC)
	{
		paintActivated(onScreenGC, false);
	}
	public synchronized void paintActivated (Graphics onScreenGC, boolean calledFromExtendingClass)
	{
		//noSpeedTest long startTime, midTime, endTime;
		//noSpeedTest startTime = System.currentTimeMillis();

		abus.	paintActivated(offScreenGC);
		dbus.	paintActivated(offScreenGC);
		aluLeftInputBus.paintActivated(offScreenGC);
		aluOutputBus.paintActivated(offScreenGC);

		akku.	paintActivated(offScreenGC);
		pc. 	paintActivated(offScreenGC);
		ireg.	paintActivated(offScreenGC);
		jreg.	paintActivated(offScreenGC);

		alu.	paintActivated(offScreenGC);

		//noSpeedTest midTime = System.currentTimeMillis();

		ram.	paintActivated(offScreenGC);

		if (! calledFromExtendingClass)
			onScreenGC.drawImage(offScreenImage, 0, 0, this);

		//noSpeedTest endTime = System.currentTimeMillis();
		//noSpeedTest System.out.print("--- VNR.pA(): " + (midTime - startTime) + "/" + (endTime - startTime) + "  |  ");

	} /* end paintActivated */


	public void stop()
	{
		super.stop();  // Alle Threads anhalten und beseitigen

		//AC_aus activationControl.hide();
		if (scrollControl != null)
			scrollControl.hide();
		//SI_aus statInfo.hide();
	}

	public void start()
	{
		super.start();  // Alle Threads erzeugen und starten

		Rectangle bounds = bounds();
		if (scrollControl != null)
			scrollControl.show();  // ruft move() auf

		//SI_aus statInfo.move (bounds.x, bounds.y + bounds.height);

		//AC_aus activationControl.show();
		//SI_aus statInfo.show();
	}

	public void showInfoTip(Point p)
	{
		if (infoTipLabel == null)
			return;

		String itt = "";
		//Color bg = MEDIUM_GREY;
		Color bg = Color.yellow;

		if (abus.intersectsWith(p))
			itt = "Adreßbus: " + abus.getInfoTipText(p);
		else if (dbus.intersectsWith(p))
			itt = "Datenbus: " + dbus.getInfoTipText(p);
		else if (aluLeftInputBus.intersectsWith(p))
			itt = "Linker ALU-Eingang: " + aluLeftInputBus.getInfoTipText(p);
		else if (aluOutputBus.intersectsWith(p))
			itt = "ALU-Ausgang: " + aluOutputBus.getInfoTipText(p);
		else if (pc.intersectsWith(p))
			itt = pc.getLabel() + ": " + pc.getInfoTipText(p);
		else if (ireg.intersectsWith(p))
			//itt = ireg.getLabel() + ": " + ireg.getInfoTipText(p, ! ram.opcodesAreShowing());
			itt = ireg.getLabel() + ": " + ireg.getInfoTipText(p, true);  // immer Opcode anzeigen
		else if (jreg.intersectsWith(p))
			itt = jreg.getLabel() + ": " + jreg.getInfoTipText(p);
		else if (akku.intersectsWith(p))
			itt = akku.getLabel() + ": " + akku.getInfoTipText(p);
		else if (alu.intersectsWith(p))
			itt = "ALU: " + alu.getInfoTipText(p);
		else if (ram.intersectsWith(p))
		{
			String ritt = ram.getInfoTipText(p);
			if (! ritt.equals(""))
				itt = "Programm- und Datenspeicher: " + ritt;
		}

		//if (itt.equals(""))
		//	itt = "Zeigen Sie auf eine Komponente";
		//else
		//	bg = Color.yellow;

		int stringWidth = stringWidth(SMALLFONT, itt);
		int gap = 16;
		if (p.x + gap + stringWidth > bounds().width)
			p = new Point(p.x - stringWidth - (3*gap)/2, p.y);
		if (p.y + SMALLFONTHEIGHT > bounds().height)
			p = new Point(p.x, p.y - SMALLFONTHEIGHT);

		symantec.itools.awt.InfoTipManager.draw(p.x + gap, p.y, itt, SMALLFONTMETRICS, bg, Color.black);
		Rectangle b = infoTipPanel.bounds();
		infoTipLabel.setText(itt);
		infoTipLabel.reshape(0, 0, b.width, b.height);
		infoTipLabel.setBackground(bg);

	} /* end showInfoTip */

	public void deactivateAll()
	{
		if (! activationLocked)
		{
			dbus.deactivate();
			abus.deactivate();
			aluLeftInputBus.deactivate();
			aluOutputBus.deactivate();

			akku.deactivate();
			pc.deactivate();
			ireg.deactivate();
			jreg.deactivate();

			ram.deactivate();

			alu.deactivate();
		}
	} /* end deactivateAll */


	public synchronized void scrollAll()
	{
		if (! activationLocked)
		{
			dbus.scroll();
			abus.scroll();
			aluLeftInputBus.scroll();
			aluOutputBus.scroll();

			abus.			paintActivated(offScreenGC);
			dbus.			paintActivated(offScreenGC);
			aluLeftInputBus.paintActivated(offScreenGC);
			aluOutputBus.	paintActivated(offScreenGC);

			onScreenGC.drawImage(offScreenImage, 0, 0, this);
		}
	} /* end scrollAll */


	public void blinkAll()
	{
		blinker = !blinker;

		ireg.blink(blinker);
		jreg.blink(blinker);
		pc.blink(blinker);
		akku.blink(blinker);

		ram.blink(blinker);

		alu.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()
	{
		akku.update();
		pc.update();
		ireg.update();
		jreg.update();

		ram.updateAll();

		alu.update();
	} /* end updateAll */


	public void lockAll()
	// Keine Komponente reagiert mehr auf activate(),
	// activateCompared() oder deactivate().
	{
        if (! activationLocked)
        {
            activationLocked = true;
        
            traceWindow.lock();

            abus.   lock();
            dbus.   lock();
            aluLeftInputBus.lock();
            aluOutputBus.   lock();

            akku.   lock();
            pc.     lock();
            ireg.   lock();
            jreg.   lock();

            ram.    lock();

            alu.    lock();
        }
	} /* end lockAll */


	public void unlockAll()
	// Alle Komponenten reagieren wieder auf activate(),
	// activateCompared() und deactivate().
	{
        if (activationLocked)
        {
            activationLocked = false;

            traceWindow.unlock();

            abus.   unlock();
            dbus.   unlock();
            aluLeftInputBus.unlock();
            aluOutputBus.   unlock();

            akku.   unlock();
            pc.     unlock();
            ireg.   unlock();
            jreg.   unlock();

            ram.    unlock();

            alu.    unlock();
        }
	} /* end unlockAll */


	public synchronized void invalidateAllImageCaches()
	{
		ram. 			invalidateImageCache();

		dbus.			invalidateImageCache();
		abus.			invalidateImageCache();
		aluLeftInputBus.invalidateImageCache();
		aluOutputBus.	invalidateImageCache();

	} /* end invalidateAllImageCaches */


	public void showAllOpcodes(boolean b)
	{
		showOpcodes = b;
		ram.	showOpcodes(b);

		paint(onScreenGC);
	}


	protected void setHelpText(int cState, int demStep)
    // Format:                 c_state,
    //                                     demonstrationStep
    {
		helpText.setText(descriptionLibrary.helpText_Von_Neumann_Rechner(cState, demStep));
    } /* end setHelpText */


	public synchronized void timerWokeUp(String title)
	{
		if (title.equalsIgnoreCase("scrolltimer"))
		{
			scrollAll();
		}
		else if (title.equalsIgnoreCase("blinktimer"))
		{
            blinkAll();
		    onScreenGC.drawImage(offScreenImage, 0, 0, this);
		}

	} /* end timerWokeUp */


	public void stopSimulation()
	{
		demonstrationReady = true;
		c_state = n_state = FETCH;

		pc.setValue(0);
		akku.setValue(0);
		ireg.setValue(0);
		jreg.setValue(0);
		alu.calculate(ADD_MEM, 0, 0, true);
		ram.initRam(PROGRAM);

		//helpText_speedup helpText.setText(descriptionLibrary.helpText_Von_Neumann_Rechner(c_state, 1) );
		setHelpText(c_state, 1);
	} /* end stopSimulation */


	public void goUntilBreakpoint()
	// ">> BP"
	{
		if (ram.hasBreakpoint())
		{
			int stopper = 0;
			int pcValue;
			boolean traceShowing = traceWindow.isShowing();
			if (traceShowing)
				traceWindow.hide();  // speedup ohne Ende!
			do
			// mindestens einen Befehl abarbeiten
			{
				singleInstruction(false);
				stopper++;
				if (stopper > 600)
				{
					out("BREAKPOINT: Nach " + --stopper + " Schritten zwangsgestoppt!");
					stopper = -1;
					break;
				}
				pcValue = pc.getValue();
				if (c_state == FETCH_DECODE)
					pcValue--;
				//speedup System.out.println("PC: " + pcValue);
			} while ( !ram.isBreakpoint(pcValue) );
			if (stopper >= 0)
				outToTraceln(stopper + " Befehle bis zum Breakpoint ausgeführt.");

			singleInstructionBack();
			singleInstruction(true);

			if (traceShowing)
				traceWindow.show();
		}
		else
			out("Kein Breakpoint vorhanden! Setzen Sie einen Breakpoint, indem Sie eine Zelle des RAMs editieren und dann 'p' drücken.");
	} /* end goUntilBreakpoint */


	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 == 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_Von_Neumann_Rechner(i) + ")");
			else
				helpText.appendText("\n" + descriptionLibrary.helpText_Von_Neumann_Rechner(UNKNOWN_COMMAND, 1));

			paint(onScreenGC);
		}
		//speedup System.out.println("SingleInstruction beendet");
	} /* end singleInstruction */

	public void singleInstructionBack(boolean doRepaint)
	// "<<"
	{
		if (simBufferTop == simBufferBottom)
			out("'<<': 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_Von_Neumann_Rechner(i) + ")");
				else
					//helpText_speedup helpText.setText(descriptionLibrary.helpText_Von_Neumann_Rechner(UNKNOWN_COMMAND, 1));
					setHelpText(UNKNOWN_COMMAND, 1);

				paint(onScreenGC);
			}
		}
	} /* end singleInstructionBack */

	public synchronized void bufferComputer()
	{
		Vector v = new Vector(7);

		v.addElement(ram.getAllValues());

        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()));

		//SI_aus 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));

            alu.setAllValues((String[]) v.elementAt(1));

			akku.	setValue( ((Integer) v.elementAt(2)).intValue() );
			pc.		setValue( ((Integer) v.elementAt(3)).intValue() );
			ireg.	setValue( ((Integer) v.elementAt(4)).intValue() );
			jreg.	setValue( ((Integer) v.elementAt(5)).intValue() );

			//SI_aus statInfo.setStats((Hashtable) v.elementAt(6));

			deactivateAll();
			c_state = n_state = FETCH;
			demonstrationStep = 1;
			demonstrationReady = false;
	} /* end setComputer */


	public void demonstrate()
	{
		demonstrate(true);
	}

	public void demonstrate(boolean doRepaint)
	{
		if (demonstrationReady == true)
		{
			if (doRepaint == true)
			{
				deactivateAll();
				//helpText_speedup helpText.setText(descriptionLibrary.helpText_Von_Neumann_Rechner(c_state, 99) );
				setHelpText(c_state, 99);
			}

			if (c_state == FETCH_DECODE)
			{
			    pc.activate();
			    pc.deactivate();
			}

			if (c_state == FETCH_DECODE)
			{
				outToTracelnS(")\n  --Decode (1)");
			}
			else
				outToTracelnS(")");

			c_state = n_state;

			demonstrationReady = false;
			demonstrationStep = 1;
		}
		else
		{
			switch (c_state)
			{
				case FETCH:
				case FETCH_DECODE:
					switch (demonstrationStep)
					{
						case 1:
								outToTraceS("  --Fetch (");
								bufferComputer();
							ram.deactivate();
							abus.activate("pc", "start");
							break;
						case 2:
								ram.setAddress(pc.getValue());
							ram.activate();
							break;
						case 3:
							dbus.activate("start", "ireg");
							break;
						case 4:
								ireg.setValue(ram.getValue());
								if (showDecodeCycle == true)
									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)
					{
						case 1:
								outToTraceS("  --Decode, ");
								outToTraceS("naechster Befehl: '" + SimpleMemory.toOpcode(ireg.getValue(), ireg.ZWEI_HOCH_BITWIDTH) + " (");
								pc.setValue(pc.getValue() + 1);
								n_state = ireg.getValue() / 256;
							pc.activate();
							demonstrationReady = true;
					}
					break;
				case LDA_MEM:
					switch (demonstrationStep)
					{
						case 1:
								outToTrace("--LDA " + Integer.toString(ireg.getValue() & 255, 16), " (");
								ram.setAddress(ireg.getValue() & 255);
							ireg.activate(1);
							abus.activate("ireg", "start");
							break;
						case 2:
							ram.activate();
							break;
						case 3:
							dbus.activate("start", "aluOutput");
							aluOutputBus.activate("end", "akku");
							break;
						case 4:
								akku.setValue(ram.getValue());
								n_state = FETCH;
							akku.activate();
							demonstrationReady = true;
					}
					break;
				case LDA_MEM_INDIR:
					switch (demonstrationStep)
					{
						case 1:
								outToTrace("--LDA (" + Integer.toString(ireg.getValue() & 255, 16) + ")", "Adresse2: " + Integer.toString(jreg.getValue() & 255, 16) + " (");
								ram.setAddress(ireg.getValue() & 255);
							ireg.activate(1);
							abus.activate("ireg", "start");
							break;
						case 2:
							ram.activate();
							break;
						case 3:
							dbus.activate("start", "jreg");
							break;
						case 4:
								jreg.setValue(ram.getValue());
							jreg.activate();
							break;
						case 5:
								ram.setAddress(jreg.getValue() & 255);
							deactivateAll();
							//jreg.activate();
							abus.activate("jreg", "start");
							break;
						case 6:
							ram.activate();
							break;
						case 7:
							dbus.activate("start", "aluOutput");
							aluOutputBus.activate("end", "akku");
							break;
						case 8:
								akku.setValue(ram.getValue());
								n_state = FETCH;
							akku.activate();
							demonstrationReady = true;
					}
					break;
				case LDA_ABSOL:
					switch (demonstrationStep)
					{
						case 1:
								outToTrace("--LDA #" + Integer.toString(ireg.getValue() & 255, 16), " (");
							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)
					{
						case 1:
								outToTrace("--STA #" + Integer.toString(ireg.getValue() & 255, 16), " (");
								ram.setAddress(ireg.getValue() & 255);
							ireg.activate(1);
							abus.activate("ireg", "start");
							break;
						case 2:
							aluOutputBus.activate("akku", "end");
							dbus.activate("aluOutput", "start");
							break;
						case 3:
								ram.setValue(akku.getValue());
								n_state = FETCH;
							ram.activate();
							demonstrationReady = true;
					}
					break;
				case STA_MEM:
					switch (demonstrationStep)
					{
						case 1:
								outToTrace("--STA " + Integer.toString(ireg.getValue() & 255, 16), " (");
								ram.setAddress(ireg.getValue() & 255);
							ireg.activate(1);
							abus.activate("ireg", "start");
							break;
						case 2:
							ram.activate();
							break;
						case 3:
							dbus.activate("start", "jreg");
							break;
						case 4:
								jreg.setValue(ram.getValue());
							jreg.activate();
							break;
						case 5:
								ram.setAddress(jreg.getValue() & 255);
							deactivateAll();
							//jreg.activate();
							abus.activate("jreg", "start");
							break;
						case 6:
							aluOutputBus.activate("akku", "end");
							dbus.activate("aluOutput", "start");
							break;
						case 7:
								ram.setValue(akku.getValue());
								n_state = FETCH;
							ram.activate();
							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)
					{
						case 1:
								if (c_state == ADD_MEM)
									aluOpcode = "--ADD ";
								else if (c_state == SUB_MEM)
									aluOpcode = "--SUB ";
								else if (c_state == MUL_MEM)
									aluOpcode = "--MUL ";
								else if (c_state == DIV_MEM)
									aluOpcode = "--DIV ";
								else if (c_state == AND_MEM)
									aluOpcode = "--AND ";
								else if (c_state == OR_MEM)
									aluOpcode = "--OR ";
								else if (c_state == XOR_MEM)
									aluOpcode = "--XOR ";
								outToTrace(aluOpcode + Integer.toString(ireg.getValue() & 255), " (");
								ram.setAddress(ireg.getValue() & 255);
							ireg.activate(1);
							abus.activate("ireg", "start");
							break;
						case 2:
							ram.activate();
							break;
						case 3:
							dbus.activate("start", "aluRightInput");
							aluLeftInputBus.activate("akku", "end");
							break;
						case 4:
								aluResult = alu.calculate(c_state, akku.getValue(), ram.getValue());
							alu.activate();
							break;
						case 5:
							aluOutputBus.activate("start", "akku");
							break;
						case 6:
								akku.setValue(aluResult);
								n_state = FETCH;
							aluLeftInputBus.deactivate();
							akku.activate();
							demonstrationReady = true;
					}
					break;
				case INC:
				case DEC:
					switch(demonstrationStep)
					{
						case 1:
								if (c_state == INC)
									outToTrace("--INC", " (");
								else if (c_state == DEC)
									outToTrace("--DEC", " (");
								else
									outToTrace("--NOT", " (");
							aluLeftInputBus.activate("akku", "end");
							break;
						case 2:
								aluResult = alu.calculate(c_state, akku.getValue());
							alu.activate();
							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)
					{
						case 1:
								if (c_state == SHL)
									outToTrace("--SHL #" + Integer.toString(ireg.getValue() & 255), " (");
								else
									outToTrace("--SHR #" + Integer.toString(ireg.getValue() & 255), " (");
							ireg.activate(1);
							dbus.activate("ireg", "aluRightInput");
							aluLeftInputBus.activate("akku", "end");
							break;
						case 2:
							alu.activate();
							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;

					if (demonstrationStep == 1)
						switch(c_state)
						{
							case JMP_ABSOL:
								outToTrace("--JMP #" + Integer.toString(ireg.getValue() & 255), " (");
								break;
							case JZE_ABSOL:
								if (alu.getFlag("zero") == true)
									outToTrace("--JZE #" + Integer.toString(ireg.getValue() & 255), " (");
								else
								{
									outToTrace("--nicht ausgeführter bedingter Sprung: JZE #" + Integer.toString(ireg.getValue() & 255), " (");
									c_state = JZE_NOT_TAKEN;
									demonstrationReady = true;
								}
								break;
							case JNZ_ABSOL:
								if (alu.getFlag("zero") == false)
									outToTrace("--JNZ #" + Integer.toString(ireg.getValue() & 255), " (");
								else
								{
									outToTrace("--nicht ausgeführter bedingter Sprung: JNZ #" + Integer.toString(ireg.getValue() & 255), " (");
									c_state = JNZ_NOT_TAKEN;
									demonstrationReady = true;
								}
								break;
							case JLE_ABSOL:
								if ((alu.getFlag("zero") == true) || (alu.getFlag("less") == true))
									outToTrace("--JLE #" + Integer.toString(ireg.getValue() & 255), " (");
								else
								{
									outToTrace("--nicht ausgeführter bedingter Sprung: JLE #" + Integer.toString(ireg.getValue() & 255), " (");
									c_state = JLE_NOT_TAKEN;
									demonstrationReady = true;
								}
								break;
						}

					if (demonstrationReady == false)  // Sprung wird durchgeführt
						switch(demonstrationStep)
						{
							case 1:
								ireg.activate(1);
								dbus.activate("ireg", "pc");
								break;
							case 2:
									pc.setValue(ireg.getValue() & 255);
								pc.activate();
								demonstrationReady = true;
						}
					break;
				case JMP_MEM:
				case JZE_MEM:
				case JNZ_MEM:
				case JLE_MEM:
					n_state = FETCH;

					if (demonstrationStep == 1)
						switch(c_state)
						{
							case JMP_MEM:
								outToTrace("--JMP " + Integer.toString(ireg.getValue() & 255), " (");
								break;
							case JZE_MEM:
								if (alu.getFlag("zero") == true)
									outToTrace("--JZE " + Integer.toString(ireg.getValue() & 255), " (");
								else
								{
									outToTrace("--nicht ausgeführter bedingter Sprung: JZE " + Integer.toString(ireg.getValue() & 255), " (");
									c_state = JZE_NOT_TAKEN;
									demonstrationReady = true;
								}
								break;
							case JNZ_MEM:
								if (alu.getFlag("zero") == false)
									outToTrace("--JNZ " + Integer.toString(ireg.getValue() & 255), " (");
								else
								{
									outToTrace("--nicht ausgeführter bedingter Sprung: JNZ " + Integer.toString(ireg.getValue() & 255), " (");
									c_state = JNZ_NOT_TAKEN;
									demonstrationReady = true;
								}
								break;
							case JLE_MEM:
								if ((alu.getFlag("zero") == true) || (alu.getFlag("less") == true))
									outToTrace("--JLE " + Integer.toString(ireg.getValue() & 255), " (");
								else
								{
									outToTrace("--nicht ausgeführter bedingter Sprung: JLE " + Integer.toString(ireg.getValue() & 255), " (");
									c_state = JLE_NOT_TAKEN;
									demonstrationReady = true;
								}
								break;
						}

					if (demonstrationReady == false)  // Sprung wird durchgeführt
						switch(demonstrationStep)
						{
							case 1:
								ireg.activate(1);
								abus.activate("ireg", "start");
								break;
							case 2:
								ram.activate();
								break;
							case 3:
								dbus.activate("start", "pc");
								break;
							case 4:
									ram.setAddress(ireg.getValue() & 255);
									pc.setValue(ram.getValue());
								pc.activate();
								demonstrationReady = true;
						}
					break;
				case IN_MEM:
					switch(demonstrationStep)
					{
						case 1:
								outToTrace("--IN " + Integer.toString(ireg.getValue() & 255), " (");
								ram.setAddress(ireg.getValue() & 255);
							ireg.activate(1);
							abus.activate("ireg", "start");
							break;
						case 2:
							dbus.activate("inOut", "start");
							break;
						case 3:
								ram.setValue((int) (Math.random() * Math.pow(2, (double) BITWIDTH)) );
								n_state = FETCH;
							ram.activate();
							demonstrationReady = true;
					}
					break;
				case OUT_MEM:
					switch(demonstrationStep)
					{
						case 1:
								outToTrace("--OUT " + Integer.toString(ireg.getValue() & 255), " (");
								ram.setAddress(ireg.getValue() & 255);
							ireg.activate(1);
							abus.activate("ireg", "start");
							break;
						case 2:
							ram.activate();
							break;
						case 3:
								outToTracelnS("OUT-Port <= 0x" + Integer.toString(ram.getValue(), 16));
								n_state = FETCH;
							dbus.activate("start", "inOut");
							demonstrationReady = true;
					}
					break;
				case NOP:
						outToTrace("--NOP", " (");
				case JZE_NOT_TAKEN:
				case JNZ_NOT_TAKEN:
				case JLE_NOT_TAKEN:
						n_state = FETCH;
					demonstrationReady = true;
					break;
				case UNKNOWN_COMMAND:
				default:
						out("--- Unbekannter Opcode! ---");
						c_state = UNKNOWN_COMMAND;
						n_state = FETCH;
					demonstrationReady = true;
					break;
			}
			if (doRepaint == true)
			{
				//helpText_speedup helpText.setText(descriptionLibrary.helpText_Von_Neumann_Rechner(c_state, demonstrationStep) );
				setHelpText(c_state, demonstrationStep);
			}
			if (demonstrationStep == 1)
				outToTraceS(Integer.toString(demonstrationStep));
			else
				outToTraceS(", " + demonstrationStep);

			demonstrationStep++;
		}
	} /* end demonstrate */

	public void demonstrateBack()
	// "<"
	{
		outToTracelnS("");
		if (demonstrationStep <= 2)  // Ist bereits inkrementiert worden, bevor dies aufgerufen wird!
		// 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 4, zu
		// LDA mem indir., Schritt 8 von 8).
		{
			if (c_state == FETCH)
			// Zum vorherigen Befehl zurueckgehen
			{
				if (demonstrationStep == 2)
				// Bei FETCH, Unterschritt 1 wird setComputer() aufgerufen.
					singleInstructionBack(false);
				singleInstructionBack(false);

				while (! (demonstrationReady && (n_state == FETCH)))
					demonstrate(false);

				updateAll();
				paint(onScreenGC);

				demonstrationStep--;
				//helpText_speedup helpText.setText(descriptionLibrary.helpText_Von_Neumann_Rechner(c_state, demonstrationStep) );
				setHelpText(c_state, demonstrationStep);
				demonstrationStep++;
			}
			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--;
				//helpText_speedup helpText.setText(descriptionLibrary.helpText_Von_Neumann_Rechner(c_state, demonstrationStep) );
				setHelpText(c_state, demonstrationStep);
				demonstrationStep++;
			}
		}
		else  // (demonstrationStep >= 3)
		// 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 - 1;

			singleInstructionBack(false);

			while (old_c_state != c_state)
				demonstrate(demonstrationReady);

			while (demonstrationStep < 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 showWindow(String name)
	{
		//SI_AUS if (name.equalsIgnoreCase("statinfo"))
		//SI_aus 	statInfo.show();
	}

	public void hideWindow(String name)
	{
		//SI_aus if (name.equalsIgnoreCase("statinfo"))
		//SI_aus 	statInfo.hide();
	}


	public void initCacheComponents(int cSize, int tSize, int assoc) {}
	public void removeCacheRessources() {}

	//{{DECLARE_CONTROLS
	//}}
} /* end Von_Neumann_Rechner */



