Ja, nach ein paar Dutzend Assemblerbefehlen, mit denen die Stackpointer, und der IP gesetzt werden, geht das in den Inner-Interpreter, der anfängt, den Faden abzuwickeln. Er rennt nur in einer (praktisch Endlos)Schleife und hangelt sich an den gefädelten Addressen weiter und findet dort wieder gefädelte Listen von gefädelten Listen, die gefädelte Listen usw. sind. Das geht solange, wie kein aktuelle Listeneintrag mehr auf andere Listen verweist, sondern etwas nützliches tun kann (ein sog. primitiv) zB. auf einem DROP landet.
DROP verwirft das oberste Element des Parameter Stack, also bei einem PC wären das die Befehle INC SP und JP [next] (oder so ähnlich). Next hangelt sich nun wieder anhand des Returnstack zurück und weiter geht's mit dem nächsten Listeneintrag.
Alle HLL definitionen sind im Prinzip nur Listen und zucken nur mit der Schulter (wie in einer grossen Firma/Behörde) und verweisen auf eine andere Liste usw. bis der Inner-Interpreter an jemanden gerät, der die Arbeit nicht mehr abwimmeln kann und sie erledigen muss.
Das hört sich jetzt furchtbar umständlich an, geht aber in Wirklichkeit für jedes NEST und NEXT (so heissen die HLL Routinen), ziemlich schnell, teilweise 1-3 Maschinenbefehle plus einem indirekten JP
Man hätte auch jede Liste aus ellenlangen Folgen von CALL addr ausführen können (wird je nach Forth Compiler auch gemacht), dann sparste zwar etwas overhead, ist auch etwas schneller, dafür braucht es 50% mehr Speicherplatz. Aber fürs Veständnis etwas leichter. Der Inner- Interpreter simuliert also praktisch einen CALL and die addr in der Liste. So ganz allgemein ausgedrückt.
BTW Forth auf einem hp2100 muss eigentlich genial schnell gewesen sein, denn der hatte in seinen 16bit das oberste bit als indirekt bit benutzt und automatisch mehrere Level indirekt in einem Befehl abgehandelt, bis er auf eine addr. mit bit2^15=0 kam und dann dort die Aktion des Befehls ausgeführt. Habs leider nie gesehen oder benutzt.
Die Zeit, die der IP verballert, bis er auf ein Wort stößt, was die Arbeit erledigt wird i.a. locker dadurch wettgemacht, dass die Daten nicht zwischen Stack in Variablen samt Rückgabewerten geschaufelt werden müssen.
Und die Worte in niedrigen Ebenen stossen halt sehr schnell auf ein primitive. So aus dem Gefühl raus, würde ich bei meinen Applikationen von höchsten
10-20 Ebenen ausgehen. Ich hab selten mehr als 8-10 Einträge auf dem Returnstack gesehen. Aber das ist nicht wirklich interessant.Das Feueralarm Beispiel (anderer Thread) definiert vielleicht 2-3 zusätzliche Verschachtelungs Ebenen oberhalb der Kernel Funktionen.
Aber wie gesagt, das soll nur ein Gefühl geben, was da so abläuft und was der ICE und der LA sieht und was nicht. Die primitives wissen halt, was der Stack gerade enthält, der LA/ICE haben keinen Schimmer.
Das DROP interessiert sich garnicht und wirft was weg. Das DUP verdoppelt den TOS, also ein PUSH TOS, wobei es ihm auch egal ist.
Ein @ (fetch) tauscht den TOS mit dem Inhalt der addr die auf dem TOS liegt. Es weis auch nicht, was es ist. Braucht es auch nicht. Beim reengineering muss Du aber sehr genau wissen, was das primitive macht.
Das ! (store) schreibt den Wert von NOS nach der Stelle, auf die TOS zeigt. Das sollte eine Variable oder ein Buffer oder array sein, könnte aber während dem boot eine dynamisch angelegte Liste sein, die erst später interpretiert wird. Typisch bei Open-Boot und ähnliche Loadern. Viele Programme kompilieren beim Start irgendwas dynamisch zusammen, was in irgendwelchen 'USERconfig' Dateien steht. Wo der user (dann ohne es typischerweise zu wissen) mit Forth Befehlen irgendwas ändert.
Da steht evtl. "5 kADCgain !" was die Verstärkung des ADC auf 5 setzt.
Oder ein patch
: newMAXtemp 5 ADC@ 5 17 */ nTEMPmax ! ; \ das wird kompiliert
...
newMAXtemp \ und so ausgeführt
...
Bei einem @ *kann* der Datentyp wechseln, ist dem Forth egal, also Addresse gegen Inhalt = Addr gegen Daten vertauscht. Du musst es aber genau wissen, sonst stehste beim nächsten primitive schon auf dem Schlauch.
So das sollte erst mal reichen um zu verdeutlichen, das reverse eng. mit Forth ohne Quellen ein Alptraum ist. Und das ist gut so (Wowereit).
Mit den Quellen ist es recht einfach und wenn die ordentlich dokumentiert sind und der Programmierer einen ordentlichen Stil hatte, macht es Spass.
Saludos Wolfgang