von Christoph Bach Hinweis: Aus technischen Gründen mußten einige Symbole durch Markierungen ersetzt werden: - (SH8) steht für den Klammeraffen - (SEMIKOLON) steht für ein Semikolon 00 / FRACTAL BERECHNUNG 2/2 01 02 : ITER 0 COLOR 03 REAL0 (SH8) REAL ! IMAG0 (SH8) IMAG ! 04 ITMAX 1 DO 4F 05 REAL (SH8) DUP F */ DUP REALQ ! 06 IMAG (SH8) DUP F */ DUP IMAGQ ! 07 + U< IF I COLOR LEAVE THEN 08 IMAG (SH8) REAL (SH8) F/2 */ IMAG0 (SH8) + 09 IMAG ! 0A REALQ (SH8) IMAGQ (SH8) - REAL0 (SH8) + 0B REAL ! 0C LOOP (SEMIKOLON) Im letzten Forth Kurs habe ich gezeigt, wie man Basic Programme in Forth über- tragen kann. Als Beispiel habe ich ein Programm zur Berechnung der Mandel- brotmenge gewählt. Heute beschäftigen wir uns mit dem Kern des Programms, dem Wort ITER, das die Hauptarbeit, die Iteration, durchführt. Durch leichtes Umstellen erhält man: 00 / FRACTAL BERECHNUNG 2/2 01 02 : ITER 0 COLOR 03 REAL0 (SH8) REAL ! IMAG0 (SH8) IMAG ! 04 ITMAX 1 DO 05 IMAG (SH8) DUP F */ IMAGQ ! 06 REAL (SH8) DUP F */ REALQ ! 07 IMAG (SH8) REAL (SH8) F/2 */ IMAG0 (SH8) + 08 IMAG ! 09 REALQ (SH8) IMAGQ (SH8) - REAL0 (SH8) + 0A REAL ! 0B 4F REALQ (SH8) IMAGQ (SH8) + U< 0C IF I COLOR LEAVE THEN 0D LOOP (SEMIKOLON) Im Hauptprogramm (FRACT) tauchen die Variablen IMAGQ und REALQ nicht wieder auf. Man benötigt sie nur hier, um Ergebnisse kurzfristig zwischen- zuspeichern. Da so etwas häufiger vorkommt, gibt es in Forth dafür eine spezielle Lösung: man schiebt die Werte vom "normalen" Stack auf den RETURNSTACK. Der Returnstack ist, der Name sagt es schon, der Bereich, wo alle Rücksprung- addressen gespeichert sind. Außerdem findet man dort auch die Schleifen- parameter (DO - LOOP). Wenn man einen Wert auf dem Returnstack zwischen- speichern will, so muß man ihn von dort wieder geholt haben, wenn man an das Schleifenende oder Wortende kommt. Andernfalls kommen die Rücksprung- addressen durcheinander, und der Computer stürzt ab. Der Returnstack ist also ein etwas heikler Bereich. Mit >R schiebt man die Zahl vom TOS (Top Of Stack) auf den Returnstack. Mit R> holt man sie wieder zurück. Mit R wird sie vom Returnstack auf den Stack kopiert. R entfernt die Zahl NICHT vom Returnstack, das schaft nur R>. Die Variablen REALQ und IMAGQ werden nun nicht mehr benötigt, da wir die Werte auf dem Returnstack zwischenspeichern. Damit man nicht alles neu tippen muß, aber zwischen den verschiedenen Varianten vergleichen kann, empfiehlt es sich die Screens zu kopieren. Mit VON NACH COPY wird der Inhalt von Screen Nr. VON nach Screen Nr. NACH kopiert. Das IMAGQ ! und das REALQ ! wird jeweils durch ein >R ersetzt. Damit werden die Werte auf den Returnstack geschoben. Um sie später voneinander subtrahieren zu können, werden sie mit R> R OVER >R auf den Stack kopiert. Normalerweise geht das eleganter mit R J, wobei J das NOS des Returnstacks auf den Stack kopiert. Zuletzt wird zur Addition beider Werte der Stack mit R> R> geräumt. Auf diese Weise haben wir uns zwei Variablen gespart, und das Programm auch einen Hauch beschleunigt. 02 : ITER 0 COLOR 03 REAL0 (SH8) REAL ! IMAG0 (SH8) IMAG ! 04 ITMAX 1 DO 05 IMAG (SH8) DUP F */ >R 06 REAL (SH8) DUP F */ >R 07 IMAG (SH8) REAL (SH8) F/2 */ IMAG0 (SH8) + 08 IMAG ! 09 R> R OVER >R - REAL0 (SH8) + 0A REAL ! 0B 4F R> R> + U< 0C IF I COLOR LEAVE THEN 0D LOOP (SEMIKOLON) Bei einer weiteren Analyse fällt uns auf, daß die Variablen REAL und IMAG ebenfalls nur in dem Wort ITER ver- wendet werden. Auch sie kann man einsparen wenn man ihre Werte auf dem Stack läßt. Man muß nur dafür sorgen, daß der Stack nach dem Ende des Wortes den gleichen Zustand hat wie zu Beginn. In Zeile 03 sparen wir uns das REAL ! und IMAG !. Die Werte liegen jetzt also auf dem Stack. Statt dem Auslesen der Variablenwerte am Schleifenbeginn schreiben wir OVER OVER, was uns beide Werte in der gleichen Reihenfolge nochmals auf den Stack legt. Sie können jetzt bei der Quadratbildung verbraten werden. In Zeile 07 braucht man beide Werte das letzte Mal, weshalb wir die eh auf dem Stack liegenden dafür hernehmen. Das Ergebnis der Zeile 07 ist der neue Wert für IMAG, den wir gleich auf dem Stack liegen lassen. Anschließend wird der neue Wert für REAL berechnet, der jetzt ebenfalls auf dem Stack liegt. Da beide Werte in der falschen Reihenfolge auf dem Stack liegen, müssen wir sie mit einem SWAP vertauschen. In der Schleife ist damit alles fertig. Wenn die Schleife ver- lassen wird (und damit sind wir auch schon am Wortende), liegen immer noch REAL und IMAG auf dem Stack (am Beginn des Wortes lagen sie nicht dort). Die beiden Werte werden mit einem zwei- fachen DROP entfernt. 02 : ITER 0 COLOR 03 REAL0 (SH8) IMAG0 (SH8) 04 ITMAX 1 DO 05 OVER OVER DUP F */ >R 06 DUP F */ >R 07 F/2 */ IMAG0 (SH8) + 08 R> R OVER >R - REAL0 (SH8) + 09 SWAP 0A 4F R> R> + U< 0B IF I COLOR LEAVE THEN 0C LOOP DROP DROP (SEMIKOLON) Dadurch, daß bei dieser Version weniger Worte gebraucht werden als in der ersten Definition von ITER, ist die Ausführungszeit auch etwas kürzer. Da es die innerste Schleife ist, macht es sich, vor allem bei hohem ITMAX, bemerkbar. Eine wesentliche Steigerung der Geschwindigkeit würde eine Neudefinition des Wortes */ bringen. Mit ZZ */ wird es decompiliert. Wenn ein Wort in Assembler geschrieben ist, wird es beim decompilieren als "Primitive" angezeigt. Das Wort */ schleppt eine Menge Ballast mit sich herum, wie man beim weiteren decom- pilieren feststellen kann. Wem die Erklärungen über die Stack- und Returnstackmanipulationen etwas zuviel waren, dem sei gesagt, daß selbst Forth-Profis nicht ohne ein Hilfsmittel auskommen: Das Stackdiagramm Es enthält in der Regel drei Spalten: OPERATION, STACK und RETURNSTACK. Aus Platzmangel (40 Zeichen) werde ich jede Spalte in eine extra Zeile schreiben. Als Beispiel wähle ich das Wort ITER nach der letzten Definition. Das Zeichen R REAL,IMAG,REAL,R REAL,IMAG, IMAG,REAL0R IMAG,REAL0,IMAG0, REAL,IMAG,4F,REAL0, REAL,IMAG,4F,REAL0,IMAG0,