package ckelling.baukasten;

import java.applet.*;
import java.awt.*;
import java.lang.*;
import java.util.*;
/* import java.io.*; */
import java.net.*;


// Versionsgeschichte
// 0.7.7  21.02.97
// 0.8.0  23.06.97  So viele Variablen und Methoden "static", wie nur moeglich
// 0.8.1  03.07.97

/**
 *	Rechner.java
 *
 *	"Rechner", die aus den Komponenten des Rechnerbaukastens
 *	zusammengestellt werden, müssen diese Klasse erweitern
 *	(statt Applet).
 *
 *	@author		Carsten Kelling
 *	@version	0.8.1, 03.07.97
 */
public abstract class Rechner extends Applet
{
	///// Konstanten Anfang /////

    /**
     * Standardbreite für Rechner; wird durch initialize() verändert.
     */
    protected int   WIDTH   = 300;

    /**
     * Standardhöhe für Rechner; wird durch initialize() verändert.
     */
    protected int   HEIGHT  = 300;


    /**
     * Breite von Bussen in Pixeln
     */
    protected int       LINEWIDTH   = 5;

    /**
     * Abstand der laufenden Punkte eines aktivierten Busses in Pixeln,
     * von Punktbeginn zu Punktbeginn.
     * Die Punkte selbst messen alle LINEWIDTH mal LINEWIDTH.
     */
    protected int       STRIPEWIDTH = 10;


    /**
     * Nach Ablauf dieser Zeit wird scrollAll() aufgerufen, so daá
     * aktivierte Busse ihre Punkte ein Pixel weiterbewegen.
     * Diese Zeit kann auf langsamen Computern überschritten werden.
     */
    public static int SCROLLTIME  = 100; // ms between scrolls

    public static int BLINKTIME   = 333;

    /**
     * Empfohlene Zeit, nach der ein TimerThread o.ä. aufwachen
     * und moveDot() bei allen Bussen aufrufen sollte, die einen
     * einzelnen Punkt, als Symbol der relevanten Taktflanke,
     * wandern lassen.
     */
    public static int DOTMOVETIME = 150;


    /**
     * Anzahl der gespeicherten Simulationsschritte
     */
    public final static int SIM_BUFFER_SIZE = 15;


    /**
     * Standardbitbreite für Komponenten
     */
    public final static int BITWIDTH = 16;

    /**
     * Standardspeichergröße für EditableMemory
     */
    public final static int MEMORYSIZE = 256;

    /**
     * Wenn eine Komponente es nicht anders angibt, wird davon
     * ausgegangen, daß sie 2 hoch BITWIDTH unterschiedliche Werte
     * speichern kann (nur wichtig für Erweiterung von Zahlen auf n Stellen)
     */
    public final static long DEFAULT_MAXVALUE = (long) Math.pow(2, BITWIDTH);


    /**
     * Opcode eines unbekannten Kommandos
     */
    public final static int UNKNOWN_COMMAND = 0x9999;


    /**
     * Standardschrifthöhe in Pixeln
     */
    public final static int DEFAULTFONTHEIGHT   = 18; // in Pixeln


    /**
     * Zustände des Automaten im Leitwerk des Von-Neumann-Rechners
     */
	public final static int FETCH			= 0x101;
	public final static int DECODE			= 0x102;
	public final static int FETCH_DECODE    = 0x103;

    /**
     * Dieser Opcode des VNR muß der niedrigste von allen sein!
     */
	public final static int	NOP 			= 0x10;

	public final static int	LDA_MEM 		= 0x11;
	public final static int	LDA_MEM_INDIR	= 0x12;
	public final static int	LDA_ABSOL		= 0x18;

	public final static int	STA_MEM 		= 0x21;
	public final static int	STA_ABSOL		= 0x28;

	public final static int	ADD_MEM 		= 0x30;
	public final static int	SUB_MEM 		= 0x31;
	public final static int	MUL_MEM 		= 0x32;
	public final static int	DIV_MEM 		= 0x33;
	public final static int	AND_MEM			= 0x34;
	public final static int	OR_MEM			= 0x35;
	public final static int	NOT				= 0x36;
	public final static int	XOR_MEM			= 0x37;
	public final static int	INC 			= 0x38;
	public final static int	DEC 			= 0x39;
	public final static int	SHL 			= 0x3C;
	public final static int	SHR 			= 0x3D;

	public final static int	JMP_MEM 		= 0x41;
	//public final static int	JMP_MEM_INDIR	= 0x42;
	public final static int	JMP_ABSOL		= 0x48;
	public final static int	JZE_MEM 		= 0x51;
	public final static int	JZE_ABSOL		= 0x58;
	public final static int	JNZ_MEM 		= 0x52;
	public final static int	JNZ_ABSOL		= 0x59;
	public final static int	JLE_MEM 		= 0x53;
	public final static int	JLE_ABSOL		= 0x5a;
	public final static int	JZE_NOT_TAKEN	= 0x55;
	public final static int	JNZ_NOT_TAKEN	= 0x56;
	public final static int	JLE_NOT_TAKEN	= 0x57;

	public final static int	IN_MEM			= 0x61;

    /**
     * Dieser Opcode des VNR muß der höchste von allen sein!
     */
	public final static int	OUT_MEM 		= 0x71;


    /**
     * Adressierungsarten des MC 68000
     */
	public final static int	ABSOLUT			= 0x10;
	public final static int	UNMITTELBAR		= 0x20;
	public final static int	REG_DIREKT		= 0x30;
	public final static int	REG_INDIR		= 0x40;
	public final static int	REG_INDIR_PI	= 0x41;
	public final static int	REG_INDIR_PD	= 0x42;
	public final static int	REG_INDIR_O		= 0x43;
	public final static int	REG_INDIR_OI	= 0x44;
	public final static int	MEM_INDIR		= 0x50; // nicht MC 68000


    /**
     * Mögliche Werte für c_state aus Klasse "Komponenten_Register"
     */
	public final static int	NORMAL	= 0x201;
	public final static int	RESET	= 0x202;


    /**
     * Mögliche Werte für c_state aus Klasse "Komponenten_RAM"
     */
	public final static int	READ	= 0x301;
	public final static int	WRITE	= 0x302;
	//					RESET	= s.o.


    /**
     * Mögliche Werte für lastResult aus Klasse "TagMemory",
     * benutzt in Klasse "Speicherhierarchie"
     */
    public final static int READ_HIT                    = 0x410;
    public final static int READ_MISS_CACHED            = 0x420;
    public final static int READ_MISS_NON_CACHED        = 0x421;
    public final static int READ_MISS_SYNC		        = 0x422;
    public final static int WRITE_BACK_HIT              = 0x430;
    public final static int WRITE_BACK_MISS_CLEAN       = 0x431;
    public final static int WRITE_BACK_MISS_DIRTY       = 0x432;
    public final static int WRITE_BACK_AROUND           = 0x438;
    public final static int WRITE_THROUGH               = 0x440;
    public final static int	WRITE_THROUGH_AROUND		= 0x448;

    /**
     * Mögliche Schlüssel für die statistischen Informationen
     */
    public final static int	CLOCKS_TOTAL				= 0x500;
    public final static int	COMMANDS_TOTAL				= 0x510;
    public final static int	LOADS_TOTAL					= 0x520;
    public final static int	STORES_TOTAL				= 0x530;

    /**
     * Benötigte Taktzyklen für Teile der Befehlsausführung
     */
    public final static int	CLK_READ_HIT				= 2;
    public final static int	CLK_READ_HIT_DIRECT_MAPPED	= 1;
    public final static int	CLK_READ_MISS_LEADIN		= 10;
    public final static int	CLK_READ_MISS_OTHER			= 3;
    public final static int	CLK_DECODE  				= 1;
    public final static int	CLK_WRITE_THROUGH			= 10;
    public final static int	CLK_WRITE_BACK_HIT_OR_CLEAN	= 2;
    //public final static int	CLK_WRITE_BACK_DIRTY		= 12;
    //Ist Summe der beiden darueber.
    public final static int	CLK_JUMP_TAKEN				= 1;
    public final static int	CLK_JUMP_NOT_TAKEN			= 1;
    public final static int	CLK_ADD     				= 1;
    public final static int	CLK_INC     				= 1;
    public final static int	CLK_SHL     				= 1;
    public final static int	CLK_MUL     				= 16;
    public final static int	CLK_DIV        				= 48;
    public final static int	CLK_NOP        				= 1;


    /**
     * Einstellungen MIPS-Architektur (Kapitel über Pipelining)
     */
    public final static int	MIPS_NUMBER_OF_REGISTERS	= 8;
    public final static int	MIPS_INSTRUCTION_MEM_SIZE	= 32;
    public final static int	MIPS_DATA_MEM_SIZE			= 32;


    /**
     * Opcode der MIPS-Architektur (Kapitel über Pipelining):
     * load word
     */
    public final static int	MIPS_LW		= 35;

    /**
     * Opcode der MIPS-Architektur (Kapitel über Pipelining):
     * store word
     */
    public final static int MIPS_SW     = 43;

    /**
     * Opcode der MIPS-Architektur (Kapitel über Pipelining):
     * R-Format: Befehle ADD, SUB, SLT und JR
     */
    public final static int MIPS_R      = 0;

    /**
     * Opcode der MIPS-Architektur (Kapitel über Pipelining):
     * Befehl, der "still" die Pipeline durchläuft
     */
    public final static int MIPS_UNIMPORTANT = -1;

    /**
     * Konstanten für Connections
     */
    public final static int IN = 1;
    public final static int OUT = 2;
    public final static int INOUT = 3;
    public final static int ADDRESS_IN = 4;


//// Farben; Wertzuweisung in Methode chooseColors() ////

    /**
     * Hintergrundfarbe, mit der Komponenten ihren Bereich löschen (helles Grau)
     */
	public Color	    BACKGROUND;

    /**
     * Farbe für nicht-aktivierte Busse mit gesetztem Flag "BLACK_ROOT" (Schwarz)
     */
    public Color        BUS_COLOR;

    //public Color        BUS_COLOR_ACTIVATED; // not needed

    /**
     * Farbe für nicht-aktivierte Abzweigungen von Bussen und
     * nicht-aktivierte Busse mit nicht gesetztem Flag "BLACK_ROOT" (Mittelgrau)
     */
    public Color        EDGE_COLOR;

    //public Color        EDGE_COLOR_ACTIVATED; // not needed

    /**
     * Farbe für Tri-State-Gatter an Bus-Abzweigungen (Schwarz)
     */
    public Color        GATE_COLOR;

    /**
     * Farbe für Lötpunkte an Bus-Abzweigungen (Schwarz)
     */
    public Color        SOLDER_COLOR;

    /**
     * Farbe für nicht-aktivierte Register (Blau); auch verwendet von:
     * EditableLabel, Misc, Mux, PipelineRegister, SimpleLabel
     */
    public Color        REG_COLOR;

    /**
     * Farbe für aktivierte Register (Rot); auch verwendet von:
     * EditableLabel, Misc, Mux, SimpleLabel
     */
    public Color        REG_COLOR_ACTIVATED;

    /**
     * Farbe für einzeln aktivierte Bytes eines Register16Split (Rot)
     */
	public Color	    REG_COLOR_BYTE_ACTIVATED;

    /**
     * Hintergrundfarbe für EditableLabel, sofern gerade editierbar (Weiß)
     */
	public Color	    LABEL_COLOR_EDITABLE;

    /**
     * Farbe für nicht-aktivierte Zellen eines EditableMemory oder TagMemory (Schwarz)
     */
	public Color	    MEM_COLOR;

    /**
     * Farbe für aktivierte Zellen eines EditableMemory oder TagMemory (Rot)
     */
	public Color	    MEM_COLOR_ACTIVATED;

    /**
     * Falls ein EditableMemory oder TagMemory eine linesize > 1 hat,
     * werden in dieser Farbe die Zellen der line, in der sich die
     * adressierte Zelle befindet, gezeichnet (Orange)
     */
	public Color		MEM_COLOR_ACTIVATED_LINE;

    /**
     * Farbe für Zellen eines TagMemory, die gerade mit der anliegenden Adresse verglichen werden (Hellblau)
     */
	public Color        MEM_COLOR_COMPARED;

    /**
     * Farbe für Zellen eines TagMemory, die valid sind (Grün)
     */
    public Color        MEM_COLOR_VALID;

    /**
     * Farbe für Zellen eines TagMemory, die dirty sind (Gelb)
     */
    public Color        MEM_COLOR_DIRTY;

    /**
     * Hintergrundfarbe für Zellen eines EditableMemory, in denen ein Breakpoint gesetzt wurde (helles Rot)
     */
    public Color        MEM_COLOR_BREAKPOINT;

    /**
     * Hintergrundfarbe bei Eingabe eines Wertes in eine Zelle eines TagMemory,
     * die valid ist (Dunkelgrn)
     */
    public Color        INPUT_COLOR_VALID;

    /**
     * Hintergrundfarbe bei Eingabe eines Wertes in eine Zelle eines TagMemory,
     * die dirty ist (Dunkelgelb)
     */
    public Color        INPUT_COLOR_DIRTY;

    /**
     * Hintergrundfarbe bei Eingabe eines Wertes in eine Zelle eines EditableMemory,
     * in der ein Breakpoint gesetzt wurde (Dunkelrot)
     */
    public Color        INPUT_COLOR_BREAKPOINT;

    /**
     * Farbe für Symbol und ggf. angezeigte Werte einer nicht-aktivierten ALU (Blau)
     */
    public Color        ALU_COLOR;

    /**
     * Farbe für Symbol und ggf. angezeigte Werte einer aktivierten ALU (Rot)
     */
    public Color        ALU_COLOR_ACTIVATED;

    /**
     * Farbe für Hintergrund des bewegten Punktes bei SimpleBus.moveDot()
     */
    public Color        MOVEDOT_COLOR_BACKGROUND;

    /**
     *  Farbe für bewegten Punkt bei SimpleBus.moveDot()
     */
	public Color	    MOVEDOT_COLOR_FOREGROUND;

    /**
     * Farbe des "Lineals" am oberen und linken Rand von EditableMemory/TagMemory
     */
    public Color        RULER_COLOR;

    /**
     * Farbe der oberen Textzeile und der Linien eines PipelineStageLabel,
     * Farbe der gestrichelten senkrechten Linien bei PipelineRegister (Schwarz)
     */
    public Color        PIPELINE_STAGE_COLOR;

	/**
	 * Farbe der unteren Textzeile eines PipelineStageLabel;
	 * im Pipelining-Aufbau verwendete Farbe:
	 * Orange wie in Hennessy/Patterson für
	 * REG_COLOR_ACTIVATED und ALU_COLOR_ACTIVATED
	 */
	public Color		MIPS_COLOR;

	/**
	 * Farbe für aktiviertes PipelineRegister (Orange)
	 */
	public Color	    MIPS_COLOR_LIGHT;

	/**
	 * 50% Weiß
	 */
	public final static Color	MEDIUM_GREY = new Color(127, 127, 127);

	/**
	 * 87,5% Weiß
	 */
	public final static Color	LIGHT_GREY	= new Color(223, 223, 223);


//// Schriftarten etc.; Wertzuweisung in Methode chooseFonts() ////

	public final static int    NORMALFONTSIZE	= 12;  // in Punkten
	public static int          SMALLFONTSIZE   = NORMALFONTSIZE;
	public final static int    DIALOGFONTSIZE  = 10;  // in Punkten
   	public final static Font   NORMALFONT      = new Font("Helvetica", Font.PLAIN, NORMALFONTSIZE);
   	public static Font         SMALLFONT;
    public final static Font   DIALOGFONT      = new Font("Dialog", Font.BOLD, 10);
    public final static Font   SMALLDIALOGFONT = new Font("Dialog", Font.PLAIN, 10);
  	public static FontMetrics  NORMALFONTMETRICS;
   	public static FontMetrics  SMALLFONTMETRICS;
   	public static FontMetrics  DIALOGFONTMETRICS;
   	public static FontMetrics  SMALLDIALOGFONTMETRICS;
   	public static int          NORMALFONTHEIGHT;
   	public static int          SMALLFONTHEIGHT;
   	public static int          DIALOGFONTHEIGHT;
   	public static int          SMALLDIALOGFONTHEIGHT;

   	/**
   	 * <EM>Angenommene</EM> (nicht berechnete) Breite eines Buchstabens
   	 * von DIALOGFONT - korrekt unter Win95; benutzt in getCellSize().
   	 */
   	public static int          DIALOGFONTCHARWIDTH = 7;

   	/**
   	 * <EM>Angenommene</EM> (nicht berechnete) Breite eines Buchstabens
   	 * von SMALLDIALOGFONT - korrekt unter Win95; benutzt in getCellSize().
   	 */
   	public static int          SMALLDIALOGFONTCHARWIDTH = 6;


	/**
	 * Standardzahlenformat: hexadezimal
	 */
	public static int			NUMBERBASE = 16;

	/**
	 * Anzahl ms, die der Mauszeiger unbewegt bleiben muß,
	 * bis ein Infotip erscheint.
	 */
	public static int			INFOTIP_DELAY = 1000;

	/**
	 * Anzahl der Fehlermeldungen, die per out() angezeigt werden; sind
	 * mehr als MAX_ERROR_MESSAGES zu sehen, erscheint ein Fenster, das
	 * auf diesen Umstand hinweist und weitere Meldungen werden unterdrückt.
	 */
	 public static int			MAX_ERROR_MESSAGES = 10;


	///// Konstanten Ende /////


	/**
	 * Für die Simulation eines endlichen Automaten: current state, next state.
	 * Mit jedem Zustandswechsel von c_state sollte ein Schritt simuliert.
	 * @see #demonstrationStep
	 * @see #demonstrationReady
	 */
	public			int 			c_state, n_state;

	/**
	 * Unterschritte zu c_state: Mit jeder Erhöhung von demonstrationStep sollte
	 * weiter am Bildschirm erläutert werden, was während dieses Zustandes (dieses Wertes
	 * von c_state) passiert.
	 * @see #c_state
	 * @see #demonstrationStep
	 */
	protected		int 			demonstrationStep;

	/**
	 * Sollte true gesetzt werden, wenn demonstrationStep seinen Maximalwert
	 * (für das aktuelle c_state) erreicht hat - c_state kann dann
	 * einen Zustandswechsel durchführen.
	 * @see #c_state
	 * @see #demonstrationStep
	 */
	protected		boolean 		demonstrationReady;

	protected		Vector[]		simBuffer;
	protected		int 			simBufferTop;
	protected		int 			simBufferBottom;


	/**
	 * java.awt.Graphics für das VORDERGRUND-Bild
	 */
	public      	Graphics        onScreenGC;

	/**
	 * Das Hintergrundbild für double-buffering
	 */
   	public			Image			offScreenImage;

   	/**
   	 * java.awt.Graphics für das HINTERGRUND-Bild
   	 */
	public			Graphics		offScreenGC;

	public			String			PROGRAM;
	public			boolean 		blackAndWhite;
	public			boolean 		manualRedraw;
	public static	boolean 		expandNumbers = true;
	public			boolean			programChoice;
	public			boolean			showOpcodes;
	public			boolean 		showDecodeCycle;
	public			boolean			buttonOpcodes;
	public			boolean			buttonDecode;
	public			boolean			buttonFast;
	public			boolean			showInfoTips;
	public			int				editableFlags;

	public			TimerThread		scrollThread;
    public          TimerThread     blinkThread;
	public 			Timer			infoTipTimer;
	protected   	boolean         blinker;
	private 		int				lastMouseMoveX;
	private 		int				lastMouseMoveY;

	public			boolean 		firstTime;
	private			boolean			isWin32;
	private 		boolean			fileDialogSupported;
	public			boolean			activationLocked;
	public			Dimension		screenSize;

	protected		int				versionStringX;
	protected		int				versionStringY;

    /**
     * Soll durch einen Aufruf von out() eine Fehlermeldung erscheinen (Standard: true)?
     */
	public static	boolean			errorMessagesEnabled = true;

	/**
	 * Wird durch die Klasse ErrorMessage ausgewertet und gesetzt.
	 */
	public static	int				numberOfErrorMessagesShowing = 0;

	public static	Panel			infoTipPanel;
	public static	Label			infoTipLabel;
	public static	ConsoleWindow	traceWindow;



	/**
	 * Gibt Informationen über unterstützte applet-Parameter aus;
	 * wird z.B. von Suns "appletviewer" benutzt.
	 *
	 * @see java.applet.Applet#getAppletInfo
	 * @see #getAuthorString
	 * @see #getVersionString
	 */
	public String[][] getParameterInfo()
	{
		String[][] info =
		{
			{"program",   			"String",  		"program to be loaded (default: fill RAM with 0xff)"},
			// {"blackandwhite",   	"boolean",  	"use only two 'colors' (default: false)"},
			{"manualredraw",    	"boolean",  	"show button to manually redraw screen (default: false)"},
			{"expandnumbers",   	"boolean",  	"show '00ff' instead of 'ff' (default: true)"},
			{"numberbase",      	"int (decimal)",    "radix for numbers - display as hex, decimal etc. (default: 16)"},
			{"fontsize",        	"int (decimal)",    "size (point) of fonts (default: 12 for big screen, 10 for small screen)"},
			{"programchoice",		"boolean",		"allow choosing different programs (default: false)"},
			{"showopcodes",			"boolean",		"memory-components show opcodes instead of numbers (default: false)"},
			{"showdecode",			"boolean",		"show DECODE-cycle (default: true)"},
			{"buttonopcodes",		"boolean",		"show button 'show opcodes' (default: true)"},
			{"buttondecode",		"boolean",		"show button 'show DECODE-cycle' (default: true)"},
			{"buttonfast",			"boolean",		"show buttons '<<', '>>', and '>>|' (default: true)"},
			{"showinfotips",		"boolean",		"show infotips (default: true)"},
			{"editableflags",		"int (binary)",		"Should RAM, PC, IReg, JReg, Akku be editable? (default: '11111')"},
		};
		return info;
	}

	/**
	 * Liefert Angaben über Autor und Erstellungsdatum des applets.
	 *
	 * @see java.applet.Applet#getAppletInfo
	 * @see #getParameterInfo
	 * @see #getVersionString
	 */
	public String getAuthorString()
	{
		return "(c) 1997  Carsten Kelling";
	}

	public void initialize(Dimension preferredSize, Dimension minimumSize)
	{
		System.out.println("Rechner-Initialisierung - Anfang");


	///// Bildschirmgroesse und Betriebssystem ermitteln
		getScreenSize();
		Toolkit tk = getToolkit();
		String fl[] = tk.getFontList();
		if (fl.length >= 1)
		{
			System.out.print("Bekannte Schriftarten: ");
			for (int i = 0; i < fl.length - 1; i++)
				System.out.print(fl[i] + ", ");
			System.out.println(fl[fl.length - 1]);
		}
		else
			System.out.println("Keine Schriftarten bekannt!");

		if (System.getProperty("os.name", "?").equals("Windows 95"))
		{
			System.out.println("Dieser Rechner läuft unter Windows 95");
			isWin32 = true;
		}
		else
		{
			System.out.println("Dieser Rechner läuft unter: " + System.getProperty("os.name", "?"));
			isWin32 = false;
		}
		
		String vendor = System.getProperty("java.vendor");
		if ( (vendor.substring(0, 8).equalsIgnoreCase("netscape")) ||
		     (vendor.substring(0, 3).equalsIgnoreCase("sun")) )
			fileDialogSupported = false;
		else
			fileDialogSupported = true;


	///// mitgegebene Parameter auswerten
		String param;

		PROGRAM = new String("");
		param = getParameter("program");
		if ((param != null))
			PROGRAM = new String(param);

		blackAndWhite = false;
		param = getParameter("blackandwhite");
		if ((param != null) && (param.equalsIgnoreCase("true")))
			blackAndWhite = true;

		manualRedraw = false;
		param = getParameter("manualredraw");
		if ((param != null) && (param.equalsIgnoreCase("true")))
			manualRedraw = true;

		programChoice = false;
		param = getParameter("programchoice");
		if ((param != null) && (param.equalsIgnoreCase("true")))
			programChoice = true;

		showOpcodes = false;
		param = getParameter("showopcodes");
		if ((param != null) && (param.equalsIgnoreCase("true")))
			showOpcodes = true;

		showDecodeCycle = true;
		param = getParameter("showdecodecycle");
		if ((param != null) && (param.equalsIgnoreCase("false")))
			showDecodeCycle = false;
		param = getParameter("showdecode");
		if ((param != null) && (param.equalsIgnoreCase("false")))
			showDecodeCycle = false;

		buttonOpcodes = true;
		param = getParameter("buttonopcode");
		if ((param != null) && (param.equalsIgnoreCase("false")))
			buttonOpcodes = false;
		param = getParameter("buttonopcodes");
		if ((param != null) && (param.equalsIgnoreCase("false")))
			buttonOpcodes = false;

		buttonDecode = true;
		param = getParameter("buttondecode");
		if ((param != null) && (param.equalsIgnoreCase("false")))
			buttonDecode = false;

		buttonFast = true;
		param = getParameter("buttonfast");
		if ((param != null) && (param.equalsIgnoreCase("false")))
			buttonFast = false;

		showInfoTips = true;
		param = getParameter("showinfotips");
		if ((param != null) && (param.equalsIgnoreCase("false")))
			showInfoTips = false;

		editableFlags = (int)(Math.pow(2, 31)) - 1;  // bis zu 31 editierbare Komponenten
		param = getParameter("editableflags");
		if (param != null)
			editableFlags = Integer.valueOf(param, 2).intValue();

		expandNumbers = true;
		param = getParameter("expandnumbers");
		if ((param != null) && (param.equalsIgnoreCase("false")))
			expandNumbers = false;

		param = getParameter("numberbase");
		if (param != null)
			NUMBERBASE = (Integer.valueOf(param)).intValue();
			// NUMBERBASE defaults to 16

		param = getParameter("fontsize");
		if (param != null)
			SMALLFONTSIZE = (Integer.valueOf(param)).intValue();
			// SMALLFONTSIZE defaults to 12
		else
		{
			if ((screenSize.width < 800) || (screenSize.height < 600))
			{
				SMALLFONTSIZE = 10;
				LINEWIDTH = 3;
				STRIPEWIDTH = 9;
			}


			// Abfrage der Bildschirmgroesse ueber eine Instanz von
			// ParameterSender.
			// Diese versucht eine Datei zu lesen (nur bei lokaler
			// Ausfuehrung moeglich!). Ist diese nicht vorhanden
			// oder enthaelt noch keine Angaben, wird die
			// Schirmgroesse vom Benutzer erfragt.
			// Umstaendlich und nicht kompatibel.
			/**
			System.out.println("Rechner: Bildschirmgröße abfragen");
			ParameterSender question = new ParameterSender();
			long ct = System.currentTimeMillis();
			while (! question.buttonPressed())
			{
				if (System.currentTimeMillis() - ct > 1000)
				{
					ct = System.currentTimeMillis();
					System.out.println(".");
				}
			}
			if (question.getLowRes() == true)
				SMALLFONTSIZE = 10;
			question = null;
			*/
		}

		if (SMALLFONTSIZE <= 10)
		// das applet platzsparend darstellen
		{
			System.out.println("Bildschirmgröße: " + screenSize.width + "x" + screenSize.height + " - platzsparende Darstellung wird benutzt");
			WIDTH = minimumSize.width;
			HEIGHT = minimumSize.height;
		}
		else
		// das applet normalgross darstellen
		{
			System.out.println("Bildschirmgröße: " + screenSize.width + "x" + screenSize.height + " - normale Darstellung wird benutzt");
			WIDTH = preferredSize.width;
			HEIGHT = preferredSize.height;
		}
		resize(WIDTH, HEIGHT);


	///// Grafikkontext
		onScreenGC = getGraphics();


	///// Konstanten setzen
		chooseColors();
		chooseFonts();
		//info System.out.println("Eine \"0\" von DIALOGFONT hat angeblich die Breite " + stringWidth(DIALOGFONT, "0") + "; benutzt (DIALOGFONTCHARWIDTH): " + DIALOGFONTCHARWIDTH);
		//info System.out.println("Eine \"1\" von DIALOGFONT hat angeblich die Breite " + stringWidth(DIALOGFONT, "1") + "; benutzt (DIALOGFONTCHARWIDTH): " + DIALOGFONTCHARWIDTH);
		//info System.out.println("Eine \"4\" von DIALOGFONT hat angeblich die Breite " + stringWidth(DIALOGFONT, "4") + "; benutzt (DIALOGFONTCHARWIDTH): " + DIALOGFONTCHARWIDTH);
		//info System.out.println("Eine \"0\" von SMALLDIALOGFONT hat angeblich die Breite " + stringWidth(SMALLDIALOGFONT, "0") + "; benutzt (SMALLDIALOGFONTCHARWIDTH): " + SMALLDIALOGFONTCHARWIDTH);
		//info System.out.println("DIALOGFONT und SMALLDIALOGFONT haben die Höhen " + DIALOGFONTHEIGHT + " und " + SMALLDIALOGFONTHEIGHT);


	///// zweites Image fuer double-buffering
		offScreenImage = createImage(WIDTH, HEIGHT);
		offScreenGC = offScreenImage.getGraphics();
		offScreenGC.setFont(DIALOGFONT);


	///// Initialisierungen
		c_state = n_state = FETCH;
		demonstrationReady = true;
		demonstrationStep = 1;

		simBuffer = new Vector[SIM_BUFFER_SIZE];
		simBufferTop = 0;
		simBufferBottom = 0;

		firstTime = true;
		blinker = true;
		activationLocked = false;

		infoTipPanel = symantec.itools.awt.InfoTipManager.getInfoTipPanel(); // initialisiert das InfoTipPanel
		add(infoTipPanel);
		infoTipPanel.setLayout(null);
		infoTipLabel = new Label("", Label.CENTER);
		infoTipLabel.setFont(SMALLFONT);
		infoTipPanel.add(infoTipLabel);
		infoTipPanel.hide();

		lastMouseMoveX = lastMouseMoveY = -1;

		traceWindow = new ConsoleWindow();

		setBackground(BACKGROUND);

		//Demo-ErrorMessage anzeigen:
		//String darth = "What <I>I</I> see, Admiral Zaarin, is just another one of your endless tests. I will <B>not</B> be satisfied until I have, under my command, the full power to <B>crush</B> the Rebel Alliance!";
		//(new ErrorMessage("Star Wars", "Darth Vader:", darth + " " + darth + " " + darth + " " + darth)).show();

		//(new ErrorMessage("Zu Ihrer Information", "Vier Properties lauten:", "java.version: " + System.getProperty("java.version") + "\nawt.toolkit: " + System.getProperty("awt.toolkit") + "\nuser.name: " + System.getProperty("user.name") + "\nuser.dir: " + System.getProperty("user.dir"))).show();
		// Davon einzig im appletviewer erlaubt: java.version

		System.out.println("Rechner-Initialisierung - Ende");

	} /* end initialize *


	/**
		Override the layout method with the one that does nothing. We do not
		need layout for the applet panel since all the positioning is
		being done manually.
	*/

	public synchronized void layout ()
	{

	} /* end layout */


	public void stop()
	// Alle Threads anhalten und beseitigen
	{
		if ((scrollThread != null) && (scrollThread.isAlive()))
			scrollThread.stop();
		scrollThread = null;

        if ((blinkThread != null) && (blinkThread.isAlive()))
            blinkThread.stop();
        blinkThread = null;

		infoTipTimer = null;
	}

	public void start()
	// Alle Threads neu erzeugen und starten
	{
		if ((scrollThread != null) && (scrollThread.isAlive()))
			scrollThread.stop();
		scrollThread = new TimerThread(SCROLLTIME, "scrolltimer", (Rechner) this);
		scrollThread.start();

		String param = getParameter("blackandwhite");

		blackAndWhite = false;
		blinkThread = null;
        //noBlink if ((blinkThread != null) && (blinkThread.isAlive()))
        //noBlink     blinkThread.stop();
        if ((param != null) && (param.equalsIgnoreCase("true")))
		{
			blackAndWhite = true;
            //noBlink blinkThread = new TimerThread(BLINKTIME, "blinktimer", (Rechner) this);
            //noBlink blinkThread.start();
		}

		infoTipTimer = new Timer(this, INFOTIP_DELAY, true, Event.ACTION_EVENT);
		infoTipTimer.setName("infoTipTimer");
	}


	public static String expandToString (int intValue, long maxValue, int numberBase, boolean expandNumbers1)
	{
		boolean oldExpandNumbers = expandNumbers;
		expandNumbers = expandNumbers1;
		String result = expandToString(intValue, maxValue, numberBase);
		expandNumbers = oldExpandNumbers;
		return result;
	}

	public static String expandToString (int intValue, long maxValue, int numberBase)
	{
		boolean isNegative = false;
		if (intValue < 0)
		{
			intValue = Math.abs(intValue);
			isNegative = true;
		}

		StringBuffer value = new StringBuffer(Integer.toString(intValue, numberBase));

		if (expandNumbers == true)
		{
			int expandTo;
			if (numberBase == 16)
			{
				if (maxValue == 65536)     // Standardbreite von RAM
					expandTo = 4;
				else if (maxValue <= 16)
					expandTo = 1;
				else if (maxValue <= 256)
					expandTo = 2;
				else if (maxValue == Math.pow(2, 32))
					expandTo = 8;
				else
				{
					expandTo = 1;
					while (maxValue > Math.pow(numberBase, (double) expandTo))
						expandTo++;
				}
			}
			else if (numberBase == 10)
			{
				if (maxValue == 65536)     // Standardbreite von RAM
					expandTo = 5;
				else if (maxValue <= 10)
					expandTo = 1;
				else if (maxValue <= 100)
					expandTo = 2;
				else if (maxValue <= 1000)
					expandTo = 3;
				else if (maxValue == Math.pow(2, 32))
					expandTo = 10;
				else
				{
					expandTo = 1;
					while (maxValue > Math.pow(numberBase, (double) expandTo))
						expandTo++;
				}
			}
			else
			// numberBase ist weder 16 noch 10
			{
				expandTo = 1;
				while (maxValue > Math.pow(numberBase, (double) expandTo))
					expandTo++;
			}

			if (isNegative && (expandTo > 1))
				expandTo--;

			while (value.length() < expandTo)
				value.insert(0, "0");
		}

		if (isNegative == true)
			value.insert(0, "-");

		return value.toString();

	} /* end expandToString */

	public static String expandToString (int intValue, long maxValue)
	{
		return expandToString(intValue, maxValue, NUMBERBASE);
	} /* end expandToString */


	public void chooseColors()
	{
		if (blackAndWhite == true)
		// nur Schwarz und Weiss fuer Sparc LC etc.
		{
			BACKGROUND					= Color.white;
			BUS_COLOR					= Color.black;
            //BUS_COLOR_ACTIVATED         = Color.black; // not needed
			EDGE_COLOR					= Color.black;
            //EDGE_COLOR_ACTIVATED        = Color.black; // not needed
			GATE_COLOR					= Color.white;
			SOLDER_COLOR				= Color.black;
			REG_COLOR					= Color.black;
			REG_COLOR_ACTIVATED 		= Color.black;
			REG_COLOR_BYTE_ACTIVATED	= Color.black;
			LABEL_COLOR_EDITABLE		= Color.white;
			MEM_COLOR					= Color.black;
			MEM_COLOR_ACTIVATED 		= Color.white;
			MEM_COLOR_ACTIVATED_LINE	= Color.white;
			MEM_COLOR_COMPARED          = Color.white;
			MEM_COLOR_VALID             = Color.black;
			MEM_COLOR_DIRTY             = Color.black;
			MEM_COLOR_BREAKPOINT		= Color.black;
			INPUT_COLOR_VALID			= Color.white;
			INPUT_COLOR_DIRTY			= Color.white;
			INPUT_COLOR_BREAKPOINT		= Color.white;
			ALU_COLOR					= Color.black;
			ALU_COLOR_ACTIVATED 		= Color.black;
			MOVEDOT_COLOR_BACKGROUND	= Color.black;
			MOVEDOT_COLOR_FOREGROUND	= Color.white;
			RULER_COLOR                 = Color.white;
			PIPELINE_STAGE_COLOR		= Color.black;
			MIPS_COLOR					= Color.black;
			MIPS_COLOR_LIGHT			= Color.black;
		}
		else
		// Standardfarben
		{
            BACKGROUND                  = new Color(192, 192, 192);
            BUS_COLOR					= Color.black;
            //BUS_COLOR_ACTIVATED         = new Color(255, 200,   0); // not needed
            EDGE_COLOR                  = new Color(128, 128, 128);
            //EDGE_COLOR_ACTIVATED        = new Color(255, 255,   0); // not needed
			GATE_COLOR					= Color.black;
			SOLDER_COLOR				= Color.black;
			REG_COLOR					= Color.blue;
			REG_COLOR_ACTIVATED 		= Color.red;
			//REG_COLOR_BYTE_ACTIVATED	= new Color(255,  80,   0);
			REG_COLOR_BYTE_ACTIVATED	= REG_COLOR_ACTIVATED;
			LABEL_COLOR_EDITABLE		= Color.white;
			MEM_COLOR					= Color.black;
			MEM_COLOR_ACTIVATED 		= Color.red;
			MEM_COLOR_ACTIVATED_LINE	= new Color(255, 100,   0);
            MEM_COLOR_COMPARED          = new Color(  0, 128, 255);
			MEM_COLOR_VALID             = Color.green;
			MEM_COLOR_DIRTY             = Color.yellow;
			MEM_COLOR_BREAKPOINT		= new Color(180,   0,   0);
            INPUT_COLOR_VALID           = new Color(  0, 128,   0);
            INPUT_COLOR_DIRTY           = new Color(128, 128,   0);
			INPUT_COLOR_BREAKPOINT		= new Color(100,   0,   0);
			ALU_COLOR					= Color.blue;
			ALU_COLOR_ACTIVATED 		= Color.red;
			MOVEDOT_COLOR_BACKGROUND	= new Color(160, 160, 160);
			MOVEDOT_COLOR_FOREGROUND	= Color.black;
			RULER_COLOR                 = new Color(220, 220, 220);
			PIPELINE_STAGE_COLOR		= Color.black;
			MIPS_COLOR					= new Color(255, 89, 0);
			MIPS_COLOR_LIGHT			= new Color(255, 168, 121);
		}
	}


    public void chooseFonts()
    {
        SMALLFONT = new Font("Helvetica", Font.PLAIN, SMALLFONTSIZE);

        NORMALFONTMETRICS = onScreenGC.getFontMetrics(NORMALFONT);
        SMALLFONTMETRICS  = onScreenGC.getFontMetrics(SMALLFONT);
        DIALOGFONTMETRICS = onScreenGC.getFontMetrics(DIALOGFONT);
        SMALLDIALOGFONTMETRICS = onScreenGC.getFontMetrics(SMALLDIALOGFONT);

        NORMALFONTHEIGHT = NORMALFONTMETRICS.getHeight();
        SMALLFONTHEIGHT  = SMALLFONTMETRICS.getHeight();
        DIALOGFONTHEIGHT = DIALOGFONTMETRICS.getHeight();
        SMALLDIALOGFONTHEIGHT = SMALLDIALOGFONTMETRICS.getHeight();

        onScreenGC.setFont(DIALOGFONT);
    }


	public static int getHeight(Font f)
	{
	    if (f.equals(SMALLFONT))
	        return SMALLFONTHEIGHT;
	    else if (f.equals(NORMALFONT))
	        return NORMALFONTHEIGHT;
	    else if (f.equals(SMALLDIALOGFONT))
	    	return SMALLDIALOGFONTHEIGHT;
	    else
	        return DIALOGFONTHEIGHT;
	}


	public static int stringWidth(Font f, String str)
	{
	    if (f.equals(SMALLFONT))
	        return SMALLFONTMETRICS.stringWidth(str);
	    else if (f.equals(NORMALFONT))
	        return NORMALFONTMETRICS.stringWidth(str);
	    else if (f.equals(SMALLDIALOGFONT))
	    	return SMALLDIALOGFONTMETRICS.stringWidth(str);
	    else
	        return DIALOGFONTMETRICS.stringWidth(str);
	}

	public static int log(int a, int b)
	{
		return (int) ( Math.log((double) a) / Math.log((double) b));
	}

	public Dimension getScreenSize()
	{
		if (screenSize == null)
		{
			Toolkit tk = getToolkit();
			screenSize = tk.getScreenSize();
		}

		return screenSize;
	}

	public boolean isWin32()
	{
		return isWin32;
	}
	
	public boolean fileDialogIsSupported()
	{
		return fileDialogSupported;
	}

	public void flushSimbuffer()
	{
		simBufferTop = 0;
		simBufferBottom = 0;
	}

	public static void out(String message)
	{
		if (errorMessagesEnabled)
		{
			if (numberOfErrorMessagesShowing <= MAX_ERROR_MESSAGES)
			    (new ErrorMessage(message)).show();
			else if (numberOfErrorMessagesShowing == MAX_ERROR_MESSAGES + 1)
				(new ErrorMessage("Bitte beachten", "Maximale Fehlerzahl erreicht", "Es sind " + MAX_ERROR_MESSAGES + " Fenster mit Fehlermeldungen geöffnet und ein weiteres sollte erscheinen. Alle weiteren Fehlermeldungen werden momentan unterdrückt. Sie können einige Fenster schließen, um zukünftige Meldungen wieder zu sehen. Wenn Sie der Autor des die Fehler hervorrufenden <i>applets</i> sind, empfiehlt es sich aber, erst die Ursachen der bereits aufgetretenen Fehler zu beheben.")).show();
		}
	} /* end out */

	public static void outToTrace(String message)
	{
		traceWindow.append(message);
	}

	public static void outToTraceln(String message)
	{
		traceWindow.append(message + "\n");
	}

	public static void outToTraceS(String message)
	{
		traceWindow.appendIfVerbose(message);
	}

	public static void outToTracelnS(String message)
	{
		traceWindow.appendIfVerbose(message + "\n");
	}

	public void outToTrace(String always, String supplement)
	{
		if (! traceWindow.isVerbose())
			traceWindow.append(always + "\n");
		else
			traceWindow.appendIfVerbose(always + supplement);
	}


	public void explainAddress()
	{
		out("FEHLER: Falsches explainAddress() wird benutzt!");
	}

	public boolean action(Event e, Object arg)
	{
		if ( (e.target == infoTipTimer) && showInfoTips && (lastMouseMoveX != -1) )
		{
			//debug System.out.println("infoTipTimer aktiviert bei (" + lastMouseMoveX + ", " + lastMouseMoveY + ")!");
			showInfoTip(new Point(lastMouseMoveX, lastMouseMoveY));
			return true;
		}
		else
			return super.action(e, arg);
	}

	public boolean mouseMove(Event e, int x, int y)
	{
		if (infoTipTimer != null)
			if ( (x != lastMouseMoveX) || (y != lastMouseMoveY) )
			{
				infoTipPanel.hide();
				infoTipTimer.start();
			}

		lastMouseMoveX = x; lastMouseMoveY = y;

		return super.mouseMove(e, x, y);
	}

	public boolean mouseExit(Event e, int x, int y)
	{
		lastMouseMoveX = -1; lastMouseMoveY = -1;
		return super.mouseExit(e, x, y);
	}

	/**
	 * Wandelt einen array von Strings in einen einzigen String um;
	 * die Elemente des arrays werden durch ein Leerzeichen getrennt,
	 * Elemente die leer sind werden übersprungen.
	 */
	public static String arrayToString(String[] array)
	{
		return arrayToString(array, " ");
	}

	/**
	 * Wandelt einen array von Strings in einen einzigen String um;
	 * die Elemente des arrays werden durch den String separator getrennt,
	 * Elemente die leer sind werden übersprungen.
	 */
	public static String arrayToString(String[] array, String separator)
	{
		if (array == null)
			return new String("");
		else
		{
			String str = array[0];
			for (int i = 1; i < array.length; i++)
				if (array[i] != "")
					str += separator + array[i];

			return str;
		}
	}


	public static Point getCellSize(int bitWidthOfCell)
	{
        double bitsPerDecimal = Math.log(10.0) / Math.log(2.0);
        int cw = (int) Math.ceil( (double) bitWidthOfCell / bitsPerDecimal);
        // cellWidth: Wieviele Zeichen werden in Dezimaldarstellung maximal benoetigt?
        // Wird weiter unten in die benoetigten Pixel umgerechnet.

        int ch;  // cellHeight in Pixeln
        if (SMALLFONTSIZE <= 10)
        // fuer Notebooks
        {
	        cw = 8 + SMALLDIALOGFONTCHARWIDTH*cw;  // cellWidth in Pixeln
        	ch = SMALLDIALOGFONTHEIGHT + 4;
        }
        else
        // Standard
        {
	        cw = 9 + DIALOGFONTCHARWIDTH*cw;  // cellWidth in Pixeln
        	ch = DIALOGFONTHEIGHT + 4;
        }

        return new Point(cw, ch);
    } /* end getCellWidth */


	public synchronized void timerWokeUp(String title)
	{
		if (title.equalsIgnoreCase("scrolltimer"))
		{
			scrollAll();
		}
	} /* end timerWokeUp */


	/**
	 * Wird von Klasse ScrollControl aufgerufen.
	 */
	public void initRam(String program) {}

	abstract void showInfoTip(Point p);

	abstract synchronized void paintActivated(Graphics onScreenGC);
	abstract void deactivateAll();
	abstract void scrollAll();
	abstract void updateAll();
	abstract synchronized void invalidateAllImageCaches();
	abstract void showAllOpcodes(boolean b);

	abstract void stopSimulation();

	abstract void goUntilBreakpoint();
	public void singleInstruction()
	// ">>"
	{
		singleInstruction(true);
	}
	abstract void singleInstruction(boolean doRepaint);

	public void singleInstructionBack()
	// "<<"
	{
		singleInstructionBack(true);
	}
	abstract void singleInstructionBack(boolean doRepaint);

	abstract void demonstrate();
	abstract void demonstrate(boolean doRepaint);
	abstract void demonstrateBack();
	abstract synchronized void bufferComputer();
	abstract synchronized void setComputer();
	abstract void initCacheComponents(int cSize, int tSize, int assoc);
	abstract void removeCacheRessources();
	abstract void showWindow(String name);
	abstract void hideWindow(String name);

	/**
	 * Liefert eine Versionsangabe des applets.
	 *
	 * @see java.applet.Applet#getAppletInfo
	 * @see #getAuthorString
	 * @see #getVersionString
	 */
	abstract String getVersionString();


} /* end Rechner */

