Carsten Kelling 1997 ([EMail senden]); letzte Änderung: 17. September 1997


4.5 Wertzuweisung und Berechnung

Die meisten Klassen des Rechner-Baukastens simulieren Rechnerkomponenten, welche eine speichernde Funktion haben. Diesen Klassen läßt sich per setValue() mindestens ein Wert zuweisen und per getValue() später wieder abfragen. Komponenten, die mehr als einen Wert speichern, müssen vorher entsprechend adressiert werden: EditableMemory und TagMemory bieten hierzu die Methode setAddress(), bei TagMemory jedoch empfiehlt der Autor die Verwendung der Methoden readRam() und writeRam() (siehe 4.5.1.4 Simulation eines Cache mit TagMemory). Register16Split übernimmt den Parameter von setValue() zwar stets in seine beiden Bytes, gibt aber, sollte es nur teilweise aktiviert sein (siehe hierzu 4.6 Visualisierung), bei getValue() auch nur den Wert des entsprechenden Bytes aus.

4.5.1 Berechnungen mit ALU und Addierer

Die verarbeitenden Komponenten ALU und Adder bieten verschiedenen Varianten von calculate(), um neue Werte aus bestehenden zu berechnen:

  1. calculate() - wiederholt die letzte Berechnung = getValue()
  2. calculate(int calculatingMode, int operand) - führt eine Berechnung mit einem Operanden durch und aktualisiert die flags.
  3. calculate(int calculatingMode, int operand, boolean updateFlags) - führt eine Berechnung mit einem Operanden durch und aktualisiert auf Wunsch die flags.
  4. calculate(int calculatingMode, int operand1, int operand2) - führt eine Berechnung mit zwei Operanden durch und aktualisiert die flags.
  5. calculate(int calculatingMode, int operand1, int operand2, boolean updateFlags) - führt eine Berechnung mit zwei Operanden durch und aktualisiert auf Wunsch die flags.

Der Addierer kennt, wie der Name schon sagt, als Operation nur die Addition, calculatingMode muß Rechner.ADD_MEM sein; als Abkürzung und um beliebig viele Operanden zuzulassen, existiert deswegen die Methode add() in einigen Varianten:

  1. add(int operand1, int operand2) - addiert die zwei Operanden und aktualisiert die flags.
  2. add(int operand1, int operand2, boolean updateFlags) - addiert die zwei Operanden und aktualisiert auf Wunsch die flags.
  3. add(int[] operands, boolean updateFlags) - addiert beliebig viele Operanden und aktualisiert auf Wunsch die flags.

Für die ALU kommen als Werte für calculatingMode in Betracht:

Konstante aus Rechner

Operation mit zwei Operanden

ADD_MEM, SUB_MEM, MUL_MEM, DIV_MEM Addition, Subtraktion, Multiplikation, Division
SHL, SHR Schieben von erstem Operand um <zweiter Operand> Stellen nach links/rechts mit Verlust der "hinausgeschobenen" Bits.
AND_MEM, OR_MEM, XOR_MEM logisches UND, ODER, exklusiv-ODER
Tabelle 12: Von ALU unterstützte Rechenarten mit zwei Operanden

Konstante aus Rechner

Operation mit einem Operand

INC, DEC Erhöhen/Vermindern des Operanden um 1
NOT logisches NICHT
Tabelle 13: Von ALU unterstützte Rechenarten mit einem Operanden

Das Ergebnis der Berechnung wird von calculate() als Rückgabewert geliefert, kann aber auch später über getValue() abgefragt werden.

Mit den Methoden getValue(), setValue() und calculate() sowie einigen zusätzlichen Berechnungen, die der Rolle des Steuerwerks entsprechen, lassen sich bereits alle Vorgänge in einem Rechner simulieren:

4.5.1.1 Lesen aus einem RAM:

Beispiel: RAM

	ram.setAddress(address.getValue());
	register.setValue(ram.getValue());

Eine Variante mit etwas Arbeit für das Steuerwerk:

	int address = instructionRegister.getValue();
	address = (address >> 5) & 255; // Rolle des Steuerwerks: Adresse liegt in
	                                // den Bits 5 bis 12 des Befehlswortes
	ram.setAddress(address);
	targetRegister.setValue(ram.getValue());

4.5.1.2 Schreiben in ein RAM:

Beispiel: RAM

	ram.setAddress(address.getValue());
	ram.setValue(register.getValue());

4.5.1.3 Durchführen einer Berechnung mittels einer ALU:

	alu.calculate(Rechner.ADD_MEM, register1.getValue(), register2.getValue());
	targetRegister.setValue(alu.getValue());

4.5.1.4 Simulation eines Cache mit TagMemory

Eine Instanz von TagMemory bekommt bei der Initialisierung Verweise auf zwei Instanzen von EditableMemory übergeben; mit der einen zusammen bildet es einen kompletten Cache (Tag- und Datenspeicher) für die andere. Außerdem kennt TagMemory Einstellmöglichkeiten für alle relevanten Parameter eines Cache (Größe, Assoziativität, linesize, Schreibverhalten, Ersetzungsstrategie). Damit sind sehr mächtige Methoden readRam() und writeRam() möglich - readRam() überprüft, ob der angeforderte Wert sich im Cache befindet, holt ihn dann aus Cache oder RAM, legt den Wert gegebenenfalls im Cache ab und aktualisiert die Verwaltungsinformationen entsprechend der eingestellten Cache-Strategie; writeRam() verfährt analog bei Schreibzugriffen. Somit läßt sich Simulationscode einfach an einen Cache anpassen - aus

	ram.setAddress(address.getValue());
	register.setValue(ram.getValue());

wird die sogar kürzere Anweisung

	tag.readRam(address, register);

die den richtigen Wert in register ablegt und sich um die Cache-Verwaltung kümmert; etwas ähnlicher dem Vorgehen ohne Cache kann man auch formulieren:

	register.setValue(tag.readRam(address));

Nota bene: Die gezeigten Codebeispiele gehen offenbar davon aus, daß zwischen den einzelnen Komponenten dort, wo es notwendig ist, Verbindungen durch Busse bestehen, und daß die (durch den Rechner-Baukasten simulierten) Busse Binärwerte auch korrekt weitergeben. Wenn man bereit ist, dieses zuzugestehen, erspart man sich auf dieser Stufe der Simulation einiges an Arbeit bei dem Codieren.

Caveat: Weder getValue()/setValue() noch calculate() verändern das Erscheinungsbild der Komponenten am Bildschirm, was sich vorteilhaft auf die Simulationsgeschwindigkeit auswirkt, aber natürlich zusätzliche Methoden bedingt, um dem Betrachter die Abläufe klarzumachen. Diese Methoden beschreibt der nächste Abschnitt.

4.6 Visualisierung

Die Methoden getValue()/setValue(), calculate() und Verwandte ermöglichen die Simulation eines Rechners ohne optische Darstellung der Vorgänge; um das Geschehene deutlich zu machen, bedarf es Methoden zur Visualisierung. Für die Zwecke dieser Anleitung ist es wichtig, sich den Unterschied zwischen Simulation und Demonstration bewußt zu machen.

Wichtigste Methode zur Visualisierung/Demonstration ist activate() (s. 4.6.1), mit dem Gegenstück deactivate() (s. 4.6.2). Durch activate() und einen anschließenden Aufruf an paint() (s. 4.6.4.1), oder besser paintActivated() (s. 4.6.4.2), wird die betreffende Komponente optisch hervorgehoben, mit der Bedeutung "bitte hierher sehen, hier geschieht gerade etwas". Die nachstehende Tabelle zeigt die unterstützten

4.6.1 Varianten von activate()

Aufruf

unterstützt von

Wirkung

activate() Adder
ALU
EditableLabel
Misc
Mux
Register16
Register16Split
Register8
Der Rahmen der Komponente und angezeigte Werte (nicht die Überschrift, diese erscheint immer schwarz) werden statt beispielsweise in Rechner.ALU_COLOR in Rechner.ALU_COLOR_ACTIVATED dargestellt (siehe Klasse Rechner für weitere Farben). Die Klasse EditableLabel zeigt keinen Rahmen an.
Ein eventuell zwischenzeitlich zugewiesener neuer Wert wird sichtbar (update() wird aufgerufen), bei ALU und Adder bedeutet dieses, daß das Ergebnis der letzten Berechnung und die dadurch angefallenen flags angezeigt werden.
activate() EditableMemory Der Wert in der zuletzt durch setAdress() bezeichneten Speicherzelle erscheint statt in Rechner.MEM_COLOR in MEM_COLOR_ACTIVATED und diese Zelle erscheint im sichtbaren Speicherbereich. Sollte der Speicher eine linesize (Größe des kleinsten adressierbaren Elements in Zellen) größer 1 besitzen, erscheinen die übrigen Werte der line in MEM_COLOR_ACTIVATED_LINE.
Eventuell zwischenzeitlich den Zellen der aktuellen line zugewiesene neue Werte werden sichtbar (update() wird aufgerufen).
Durch sukzessives Aufrufen von setAddress() und activate() ist es möglich, mehr als eine line einzufärben.
activate() PipelineRegister Abkürzung für activate(PipelineRegister.BOTH).
activate() PipelineStageLabel Ein PipelineStageLabel erscheint nur, falls es aktiviert ist (löscht aber auf jeden Fall den von ihm belegten Platz). Oberhalb seiner Mittellinie erscheint der bei der Initialisierung oder dem letzten activate(String) übergebene Text.
activate() TagMemory Siehe zunächst 4.5.1.4 Simulation eines Cache mit TagMemory. Hervorgehoben wird die Zelle, die bei dem letzten Aufruf von readRam()/writeRam() einen neuen Wert aufgenommen hat (eventuell also keine).
Der neue Wert dieser Zelle wird sichtbar.
activate(int act) PipelineRegister Mögliche Werte für act: PipelineRegister.BOTH, WRITE, READ; dieser Wert wird mit dem vorherigen Aktivierungszustand per ODER verknüpft.
Bei BOTH als Ergebnis erscheint der gesamte Rahmen in REG_COLOR_ACTIVATED, dessen Inneres in MIPS_COLOR_LIGHT; bei WRITE/READ wird nur die linke/rechte Hälfte von Rahmen und Innerem eingefärbt.
activate(int act) Register16Split Mögliche Werte für act: Register16Split.BOTH, HIGH_BYTE, LOW_BYTE.
Bei letzteren beiden wird nur das linke/rechte angezeigte Byte (nicht der Rahmen) in REG_COLOR_BYTE_ACTIVATED eingefärbt; nur bei diesem Byte wird ein eventuell vorher zugewiesener neuer Wert sichtbar.
activate() SimpleBus Abkürzung für activate("start", "end")
activate(Color restColor)
activate(Color selectColor, Color restColor)
EditableMemory
TagMemory
Wie activate(), nur wird statt MEM_COLOR_ACTIVATED_LINE restColor und ggf. statt MEM_COLOR_ACTIVATED selectColor verwendet.
activateCompared() EditableMemory
TagMemory
Bei EditableMemory: Abkürzung für activate(MEM_COLOR_COMPARED, <nicht benötigt>), wobei die linesize als 1 angenommen wird.
Bei TagMemory: Alle die Zellen werden in MEM_COLOR_COMPARED hervorgehoben, die der Tag-Speicher untersuchen muß, um festzustellen, ob sich ein Wert bereits im Cache befindet - bei einem Cache mit Assoziativität n also n Zellen.
activate(String str) PipelineStageLabel Ein PipelineStageLabel erscheint nur, falls es aktiviert ist (löscht aber auf jeden Fall den von ihm belegten Platz). Oberhalb seiner Mittellinie erscheint der Text str.
activate(String src, String dst)

activate(String src, String dst1, String dst2)
SimpleBus Auf dem Bus laufen von der durch src bezeichneten Stelle Punkte zu dst bzw. dst1 und dst2. Diese Zeichenketten können entweder "start", "end" oder der Bezeichner einer Abzweigung des Busses sein.
Wurde setMoveDotMode(true) (ein Punkt als Symbol der relevanten Taktflanke soll sich bewegen) aufgerufen, wird der Hauptstrang des Busses zunächst nur in Rechner.MOVEDOT_COLOR_BACKGROUND eingefärbt, Abzweigungen werden wie üblich aktiviert (siehe moveDot).
Sofern dem Bus durch setConnection() mitgeteilt wurde, welche Komponente an der durch src bezeichneten Stelle angeschlossen ist, wird deren Wert übernommen.
activateTo(String dst)

activateFrom(String dst)
SimpleBus Nur auf der durch dst bezeichneten Abzweigung des Busses laufen Punkte zu dem Hauptstrang hin (activateTo()) oder von ihm weg (activateFrom()); activateTo() kann zusammen mit activate() benutzt werden, um Punkte zu mehr als zwei Datensenken laufen zu lassen.
boolean moveDot() SimpleBus Wurde setMoveDotMode(true) zuvor aufgerufen, bewegt sich ein Punkt als Symbol der relevanten Taktflanke bei jedem Aufruf um 2 Pixel weiter.
Dieser Punkt kann nur auf dem Hauptstrang des Busses bewegt werden und wird in Rechner.MOVEDOT_COLOR_FOREGROUND gezeichnet.
Liefert true zurück, nachdem die "Taktflanke" am Zielpunkt verschwunden ist.
Tabelle 14: Varianten von activate() in den Rechnerkomponenten

Caveat: Auch activate() zeichnet keine Komponente neu; um diese mit verändertem Aussehen zu bewundern, ist immer noch ein Aufruf an deren paint()- oder besser paintActivated()-Methode notwendig.

4.6.2 deactivate()

Alle Komponenten unterstützen von deactivate() nur diese eine Variante ohne Parameter. Alle Hervorhebungen an der Komponente werden dadurch beseitigt, sichtbare Werte nicht verändert.

Weitere Auswirkungen:

Da deactivate() das Aussehen der Komponente verändert, wird diese dafür vorgesehen, bei dem nächsten Aufruf von paintActivated() noch einmal neu gezeichnet zu werden.

4.6.3 Varianten von update()

activate() hebt eine Komponente nicht nur optisch hervor, sondern ersetzt in vielen Fällen auch angezeigte Werte durch neue, intern bereits enthaltene. Diese Aktualisierung kann bei einigen Komponenten auch losgelöst von activate() durch die Methode update() erfolgen, deren Varianten Tabelle 15 nennt. Man beachte, daß das hier gemeinte update() nichts mit java.awt.Component.update() zu tun hat.

Die Kenntnis der Methoden zur Simulation und Demonstration reicht bereits zur Verständnis des nachfolgenden Codeausschnitts aus dem Von-Neumann-Rechner-Applet. Gezeigt ist der (leicht modifizierte) Code, der einen Maschinenbefehl LDA absol. (load accumulator absolutely) durchführt und am Bildschirm darstellt; dieser Befehl überträgt das niederwertige (rechte) Byte des Befehlsregisters (Variable ireg) über den Datenbus (dbus) in den Akkumulator (akku). Die weiter nach rechts eingerückten Zeilen führen die Simulation durch, die anderen die Demonstration. Letztere erfolgt in zwei Schritten durch Hochzählen von demonstrationStep; wenn im Zuge eines Demonstrationsschrittes demonstrationReady auf true gesetzt wird, kann zum nächsten Simulationsschritt fortgeschritten werden (in diesem Fall FETCH, um den nächsten Maschinenbefehl aus dem Hauptspeicher zu laden).

Aufruf

unterstützt von

Wirkung

update() Register16
Register16Split
Register8
Der angezeigte Wert des Registers wird auf den Stand des intern gespeicherten Wertes gebracht.
update() EditableMemory
TagMemory
Ruft erst updateText(), dann updateBackground() auf.
update(int adr) EditableMemory
TagMemory
Ändert die aktuelle Adresse auf adr, ruft danach erst updateText() und dann updateBackground() auf und stellt die alte Adresse wieder her.
updateText()
updateBackground()
updateForeground()
EditableMemory
TagMemory
Gilt nur für die durch den letzten Aufruf von setAddress() bezeichnete Speicherzelle und ggf. die Zellen in derselben line: Der Inhalt (Wert), die Hintergrund- bzw. die Vordergrundfarbe werden auf den intern gespeicherten Stand gebracht.
update() ALU Aktualisiert Operanden, Operator, Ergebnis und flags.
Tabelle 15: Varianten von update()

case LDA_ABSOL:
  switch (demonstrationStep)
  // LDA absol. wird in zwei Schritten visualisiert.
  {
    case 1:
      ireg.activate(1);  // zu ladender Wert ist das niederwertige Byte des
                         // Befehlswortes
      dbus.activate("ireg", "akku");
      break;
    case 2:
        akku.setValue(ireg.getValue);  // getValue() liefert nach einem
                                       // activate(1) nur das gewünschte Byte
        n_state = FETCH;
      akku.activate();
      demonstrationReady = true;
  }
  break;

Abschnitt 4.8 Der Simulationsansatz widmet sich den Fragen, wie man das gegebene Verhalten einer Hardware am besten mit dem Rechner-Baukasten simuliert und ob man Simulation und Demonstration, anders als im obigen Beispiel, in getrennten Methoden durchführen soll.

4.6.4 Neuzeichnen der Komponenten

4.6.4.1 paint()

Alle Komponenten kennen eine paint()-Methode, die das Bild der jeweiligen Komponente unbedingt, vollständig und sofort zeichnet. Um Flackern während des Neuzeichnens zu vermeiden, ist es auf jeden Fall ratsam, double buffering zu verwenden, was von dem Rechner-Baukasten durch einige vordefinierte und -initialisierte Variablen unterstützt wird. Die paint()-Methode eines von Rechner abgeleiteten Applets, die den gesamten Rechneraufbau neu zeichnet, sollte damit strukturell so aussehen:

  1. Die Deklaration der Methode laute "public synchronized void paint(Graphics onScreenGC)" ("GC" steht für graphics context).
  2. Ein Aufruf der Art "komponente.paint(offScreenGC);" für alle Komponenten sorgt dafür, daß alle Komponenten ihr Bild aktualisieren, aber nicht auf dem Bildschirm, sondern in dem nicht sichtbaren Bild offScreenImage (eine Instanz von java.awt.Image).
  3. Die Zeile "onScreenGC.drawImage(offScreenImage, 0, 0, this);" überträgt auf einen Schlag alle Änderungen auf den sichtbaren Schirm.

Die paint()-Methode des Rechner-Objekts wird vom Browser automatisch immer dann aufgerufen, wenn Teile der Fläche des Applets des Neuzeichnens bedürfen, weil sie z.B. durch ein Fenster verdeckt waren. Es sollte kaum notwendig sein, paint(), abgesehen vom Start des Applets, selbst aufzurufen.

Um die Änderungen durch Demonstrationsmethoden sichtbar zu machen, gibt es die Methode

4.6.4.2 paintActivated()

Ebenfalls alle Komponenten besitzen die Methode paintActivated(), die das Bild der Komponente nur dann neu aufbaut, wenn sich dieses durch einen Aufruf von activate() oder deactivate() verändert hat. Für die meisten Komponenten bedeutet dieses, daß nur der erste Aufruf von paintActivated() nach activate() und deactivate() eine Wirkung hat; die von Instanzen der Klasse SimpleBus dargestellten Busse jedoch zeichnen sich, sofern aktiviert, bei jedem Aufruf von paintActivated() neu. Durch dieses Verhalten sind folgende Vorgehensweisen möglich:

  1. Nach jedem Demonstrationsschritt wird einfach nur paintActivated() des Rechner-Applets aufgerufen. Diese Methode sollte strukturell wie paint() aufgebaut sein, aber eben paintActivated() aller Komponenten des jeweiligen Rechners aufrufen. Komponenten, die in dem Demonstrationsschritt ihren Aktivierungsstatus nicht verändert haben, vergeuden auch keine Rechenzeit durch ihr Neuzeichnen; diejenigen jedoch, die frisch aktiviert oder deaktiviert wurden, also ihr Aussehen geändert haben, werden neugezeichnet.
  2. Alle n Millisekunden sollen bei allen aktivierten Bussen die Punkte auf dem Bildschirm einen Bildpunkt weiterrücken. Wie in 4.3 Die Wahl der Basisklasse des Applets beschrieben, wird durch Rechner eine Instanz von TimerThread eingerichtet und gestartet, die jeweils nach der in Rechner.SCROLLTIME festgelegten Anzahl Millisekunden die Methode timerWokeUp() aufruft, die daraufhin die Methode scrollAll() abarbeitet. Auch in scrollAll() aber kann beruhigt erst scroll() und dann paintActivated() aller Busse aufgerufen werden, denn nur bei den wirklich aktivierten passiert daraufhin optisch etwas.

4.6.5 Vergrößern/Verkleinern und Verschieben von Komponenten

4.6.5.1 reshape()

Wie in 4.4.4 Die zwei Arten von Komponenten erklärt, gibt es Rechnerkomponenten, die sich von java.awt.Panel ableiten, und solche, die das nicht tun. Erstere stellen sowieso schon die Methode

reshape(int x, int y, int width, int height)

bereit, letztere bekamen eine solche geschrieben. Diese Methode ist bei Panel die Verbindung eines move() und eines resize(), und genauso funktioniert sie auch in dem Rechner-Baukasten: Die jeweilige Komponente, d.h. die linke obere Ecke des kleinsten umfassenden Rechtecks, wird an den Punkt (xy) verschoben und Breite und Höhe dieses Rechtecks werden angepaßt.

Da die Rechnerkomponenten aber statt eines move() das mächtigere setCoordinates() kennen, gibt es auch eine entsprechende Variante von reshape():

reshape(int x, int y, int width, int height, String grabMode)

Mögliche Werte für grabMode sind natürlich die in 4.4.2.1 getCoordinates() genannten.


4.7 Zusätzliche Funktionalität

Die beiden folgenden Abschnitte erläutern die Anwendung von unterstützenden Funktionen des Rechner-Baukastens.

4.7.1 Einstellen der Überschrift

Alle Komponenten des Rechner-Baukastens erlauben es, erläuternden Text anzuzeigen. Bei den meisten erscheint dieser als "echte" Überschrift oberhalb der graphischen Repräsentation der Komponente, bei einigen jedoch innerhalb oder gar beides. Dieser Text kann eine oder auch mehrere Zeilen umfassen.

Alle von RechnerKomponentePanel abgeleiteten Klassen kennen nur eine einzeilige Überschrift. Um keine nicht vorhandenen Fähigkeiten vorzutäuschen, kann diese lediglich mit dem Methodenpaar setLabel(String)/String getLabel() eingestellt und überprüft werden. RechnerKomponente jedoch fordert die Implementation von setLabel(String[]) und String[] getLabel(), die also mit einer Reihung (array) von Zeichenketten arbeiten; nicht alle hiervon abgeleiteten Klassen aber benutzen mehr als die erste Zeile des arrays. Als Abkürzung, um nur eine Zeile Text zuzuweisen, kennt auch RechnerKomponente ein setLabel(String), welches einfach auf setLabel(String[]) umgesetzt wird (eine naheliegende analoge Implementation von String getLabel() ist nicht möglich, da eine Methode in Java nur einen Rückgabetyp haben darf).

Es ergibt sich also folgendes Bild:

Unterstützte Zeilen

Klasse

Zugriffsmethoden

1 (Überschrift) EditableLabel
PipelineRegister
Register16
Register16Split
Register8
void setLabel(String)

String getLabel()
1 (Überschrift) Adder
ALU
EditableMemory
TagMemory
void setLabel(String)
void setLabel(String[])
String[] getLabel()
2 (Opcode des in der Pipelinestufe bearbeiteten Befehls und Name der Stufe selbst) PipelineStageLabel void setLabel(String) (nur Opcode ändern)
void setLabel(String[])
String[] getLabel()
3 (Überschrift, Bezeichnung des oberen und unteren Einganges) Mux void setLabel(String) (nur Überschrift ändern)
void setLabel(String[])
String[] getLabel()
beliebig viele (Überschrift) SimpleBus void setLabel(String)
beliebig viele (zentrierter Text innerhalb der Ellipse) Misc void setLabel(String[])
String[] getLabel()
Tabelle 16: Zugriff auf die Überschriften der Komponenten

Man beachte, daß sich die Position der Überschrift eines Busses mit Hilfe von setLabelPosition() verändern läßt:

verticalBus1.setLabelPosition("left", "start");      // links oben
verticalBus2.setLabelPosition("right", "end");       // rechts unten
// Standard: "right", "start"

horizontalBus1.setLabelPosition("start", "bottom");  // links unten
horizontalBus2.setLabelPosition("end", "top");       // rechts oben
//Standard: "start", "top"

4.7.2 Hilfetexte am Mauszeiger

Der Rechner-Baukasten unterstützt kontextabhängige kurze Hilfetexte, die neben dem Mauszeiger erscheinen, falls dieser eine bestimmte Zeit lang nicht bewegt wurde (sogenannte info tips). Um ein Applet mit info tips zu versehen, genügt es, die Methode showInfoTip(Point p) zu implementieren - dazu ist man bei Ableitung des Applets von ckelling.baukasten.Rechner auch gezwungen.

Eine beispielhafte Implementation könnte so aussehen:

public void showInfoTip(Point p)  // Mauszeiger steht am Punkt p
{
  if (infoTipLabel == null)  // initialize() noch nicht aufgerufen, abbrechen
    return;

  String itt = "";          // info tip text
  Color bg = Color.yellow;  // Hintergrundfarbe für info tip

  if (abus.intersectsWith(p))	1.
    itt = "Adreßbus: " + abus.getInfoTipText(p);	2.
  else if (dbus.intersectsWith(p))
    itt = "Datenbus: " + dbus.getInfoTipText(p);
  else if (pc.intersectsWith(p))
    itt = pc.getLabel() + ": " + pc.getInfoTipText(p);
  else if (ireg.intersectsWith(p))
    itt = ireg.getLabel() + ": " + ireg.getInfoTipText(p, true);	3.

  if (itt.equals(""))
  {
    bg = Color.white;
    itt = "Zeigen Sie auf eine Komponente";
  }
  // Kopieren Sie das folgende bis zum Schluß in ihr showInfoTip()	4.
  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 */

Zu den Anmerkungen:

  1. Alle Komponenten des Rechner-Baukastens unterstützen diese Methode, um anzuzeigen, ob sich der Mauszeiger innerhalb ihres Umrisses befindet.
  2. Ebenso kennen alle Komponenten diese Methode; sie liefert üblicherweise den Zahlenwert einer Komponente zu der Basis, die gerade nicht zur Darstellung benutzt wird (also dezimal, wenn ansonsten nur hexadezimale Zahlen zu sehen sind, und umgekehrt). Man beachte, daß man es selbst in der Hand hat, ob man diese Ausgabe noch um Text ergänzt, wie oben.
  3. Register16Split kennt diese Form von getInfoTipText(); der boolesche Parameter bestimmt, ob die Ausgabe ein Opcode statt einer Zahl sein soll, falls das Register, wie in diesem Beispiel, das Instruktionsregister o.ä. eines Aufbaus darstellt. Der Parameter wird, falls fortgelassen, als true angenommen.
  4. Den folgenden Code können Sie unverändert übernehmen, damit der Hilfetext in einem kleinen Rechteck in der Nähe des Mauszeigers erscheint, mit auf kleinen Bildschirmen kleinerer Schrift, einstellbarer Hintergrundfarbe, nur so groß wie nötig und garantiert immer vollständig zu sehen. Sie können aber auch eigenen Code entwickeln und den Text des info tip beispielsweise in einer Statuszeile erscheinen lassen.

Einige Informationen benötigt man noch, um info tips mit dem Rechner-Baukasten kontrolliert einzusetzen:


Carsten Kelling 1997 ([EMail senden]); letzte Änderung: 17. September 1997