Threaded Code
Eine verkettete Liste von Vektoren (Adressen) in einem Programm heißt Threaded Code. In Forth verweist jeder solcher Vektoren auf das Forth-Wort das ausgeführt werden soll.
Es gibt vier verschiedene Arten von Threaded Code:
- Direct Threaded Code (DTC)
- Indirect Threaded Code (ITC)
- Subroutine Threaded Code (STC)
- Token Threaded Code (TTC)
Der Threaded Code wird interpretiert von einem Maschinenprogramm, dem Inner Interpreter. NEXT ist der traditionelle Name dieses Inner Interpreter, weil darin auf die nächste Instruktion weiter geschaltet wird (next instruction).
Das Assembler-Fragment NEXT liegt meist an zentraler Stelle im Forthsystem. Änderungen daran sind dann einfach durch zu führen. Es kann aber auch dezentral in den Code eingeflochten werden. Dies erfolgt meist um die Ausführungsgeschwindigkeit zu erhöhen.
Anmerkung: Diese Erklärungen um den threaded code herum gehen zurück auf das erste wikiforth aus dem Jahre 2001, das Ulrich Hoffmann damals auf dem Wiki Server auf www.forth-ev.de versucht hat anzulegen. Er benutzte dazu eine Sammlung von Perl-Skripts die unter dem Namen DolphinWikiWeb zusammengefassst waren. Es zeigte sich dann aber bald das wirklich jeder in dieses wiki schreiben konnte, und es fiel dem Internet-Vandalismus zum Opfer, mußte stillgelegt werden. Aus den geretteten Dateien wurde nun das eine oder andere wieder hervor geholt. Möge es nun klappen, und wir sehen das forth wiki wachsen und gedeihen. Ich danke Ulrich Hoffmann für die unermüdliche Hilfe. mka
Ergänzung
(Unter Verwendung von Texten von Brad Rodriguez, Rafael Deliano; de.comp.lang.forth 18.06.2006)
Eine ausführliche englischsprachige Beschreibung mit Bezug auf reale 8 Bit CPUs stammt von Brad Rodriguez. (Der Suchstring für google war „Brad Rodriguez threading“ - die Quelle ist eventuell auch anderswo zu finden.)
Originaler „threaded code“ ist wohl auch nur „indirect threaded“ und „direct thread“ gewesen.
Abgesehen von der Portabilität geht es dabei um die Frage wie man den Speicherverbrauch gegen die Geschwindigkeit auslegt. Mit Blick auf 8 Bit CPUs bedeutet das:
- „native code“ bei dem alles als Opcodes der CPU hintereinander steht wäre am schnellsten, aber verbraucht zuviel Speicher. Wieviel Byte pro Befehl so mehr anfallen gegenüber threaded code ist variabel, aber im Schnitt sicherlich 10 - 15 oder noch schlechter. Dieser weg ist also nur gangbar, wenn die CPU mikrocodiert bzw. selbst ein Stackprozessor ist und die Opcodes damit nahezu FORTH-Befehlen entsprechen.
- „JSR-theaded“ ist vom Speicherverbrauch günstiger, greift über Unterprogrammaufruf auf einen „nucleus“ von Code-Routinen zu die den FORTH-Primitives entsprechen. Der Umfang so eines Nucleus ist ca. 4k Byte. Ein Unterprogrammaufruf besteht aus 3 Byte: 16 Bit Adresse und 8 Bit JSR,-Opcode der CPU.
- die historischen „threaded“-Varianten (die auf mikrocodierten 16 Bit CPUs a la PDP11 entstanden) werfen den Opcode der CPU ganz raus und speichern nur die Adresse, also nur noch 2 Byte. Die Funktion des JSR,-Opcodes wird in kleinem NEXT-Programm nachgebildet.
- „token-threaded“ wäre heute auf wohl „bytecode“. Man speichert pro Befehl nur noch 1 Byte. Die Adressen dieser bis zu 256 Befehlen des Nucleus können dann in einer 512 Byte umfassenden Adressentabelle abgelegt werden und das NEXT-Programm kann daraus relativ problemlos die Sprünge zum ausführbaren Code holen. Bei Harvard-Maschine setzt so ein Vorgehen natürlich voraus, daß der Tabellenzugriff in der CPU gut implementiert sind.
Man beachte, daß die Angabe, wieviele Byte ein Befehl braucht vereinfacht ist. JSR-threaded Code hat z.B. Sprungbefehle und oft Literals typischerweise auch inline compiliert. Das sind keine Unterprogramme, diese Sequenzen verbrauchen damit mehr Speicher. Gleiches gilt für die anderen Varianten.
Eine bebilderte Darstellung findet sich auch in VD 1/1997, Seite 35.