c't

c't-Projekte - Mailinglisten


[Voriger (Datum)] [Nächster (Datum)] [Voriger (Thread)] [Nächster (Thread)]
[Nach Datum][Nach Thread]

Re: [ct-bot] Basic-Interpreter (für c't - Bot)

Absender: Timo Sandmann
Datum: Do, 02.12.2010 19:23:17
In-reply-to: <1ac236efdb2ae353dba0652e0009d0d7.squirrel@xxxxxxxxxxxxxxxxxxx>
References: <1ac236efdb2ae353dba0652e0009d0d7.squirrel@xxxxxxxxxxxxxxxxxxx>


Hallo Uwe,

eines vorweg, ich habe den Basic-Interpreter als Patch von Frank bekommen und inzwischen in das ct-Bot-Framework eingebaut (funktioniert auch sehr gut). Mit dem Code des Interpreters habe ich mich aber nur an den wenigen Stellen, die ich leicht angepasst habe, auseinandergesetzt. Zum Sprachumfang und ähnlichen Dingen, kann ich also ziemlich wenig sagen, ich habe auch keine weiteren Basic-Programme ausprobiert, als die beiden Beispiele von Frank.

Am 02.12.2010 um 16:17 schrieb Uwe Berger:
> <snip>
> In der neuen Version ist es vollkommen egal, von welchem "Medium" das
> Basic-Programm eingelesen wird. Vor allem wird nicht mehr ein Puffer im
> RAM benötigt, das Einlesen erfolgt Byte-weise vom jeweiligen
> Speichermedium (ja, ist mir wirklich gelungen, dazu war nur ein kleiner
> Trick in tokenizer.c notwendig...!).

Das byte-weise Einlesen ist sicherlich für Mikrocontroller mit wenig RAM sehr vorteilhaft. 
Beim ct-Bot ist zwar standardmäßig auch nur ein ATmega32 vorhanden, da der Codeumfang aber inzwischen deutlich angewachsen ist, braucht man im Prinzip mindestens einen ATmega644(P) (oder ATmega1284P). Von daher müssen wir nicht ganz so knauserig mit dem RAM sein, insbesondere, wenn man dadurch Vorteile bekommt. Viel wichtiger ist beim ct-Bot, dass die Hauptschleife alle 10 ms durchlaufen wird, damit z.B. die Abgrundsensoren noch rechtzeitig ausgewertet werden und den Bot _vor_ der Tischkante stoppen können. Je mehr von den 10 ms eines Zyklus noch übrig bleiben, desto besser, weil dann Hintergrund-Threads länger laufen können, um u.a. eine Karte der Umgebung auf die SD-Karte zu schreiben. Das ist natürlich sehr ct-Bot spezifisch, spielt für "uns" aber eine wichtige Rolle, weil neue Features bereits Vorhandene nicht unbrauchbar machen sollen. Wir stellen also sozusagen gewisse Performanz-Anforderungen an die Bot-Verhalten.
Aus diesem Grund ist es auf dem ct-Bot besser einen RAM-Puffer für eine ganze Basic-Zeile zu opfern und dafür die Kosten eines oder mehrerer Funktionsaufrufe pro Zeichen des Basic-Programms einzusparen. Hinzu kommt, dass man spätestens für einen Aufruf von atoi(), strcmp() usw. Teile der Daten sowieso wieder puffern muss. 
Wie gesagt, dass soll nicht bedeuten, dass ein entsprechendes Vorgehen allgemein für den Basic-Interpreter nicht sinnvoll sein könnte.

> Dreh- und Angelpunkt ist dabei eine Schnittstelle die als Defines
> realisiert ist und in den Dateien tokenizer_access.* zu finden ist. Die
> dortigen Defines sind von der Sache her einfach nur umzudefinieren, je
> nach seinem eigenen internen Routinen für die Mediumzugriffe. Ich denke
> der Quelltext dort ist ausreichend kommentiert in der Doku im Archiv
> (Kapitel 5) gibt es eine ausführlichere Beschreibung. Desweiteren sind 3
> Beispiele für unterschiedliche Speichermedien ausgeführt:
> * Zugriff wie in den bisherigen Versionen über einen Speicherbereich im RAM
> * Basic-Programm im PROGMEM des AVR
> * Basic-Programm in einer Datei und Zugriffe über fread(), fseek()...,
> allerdings libc und für eine Linux-Plattform.
> 
> Zum Aufzeigen der Funktionsweise sollte es aber reichen, gerade das
> 3.Beispiel dürfte am interessantesten sein...

Variante 2) und 3) sind auf dem ct-Bot nicht sinnvoll (Programme sollen zur Laufzeit zum Bot übertragen und dort auf SD-Karte gespeichert werden können, keine libc mit fread() usw.). Ich habe die Version des Interpreters, die ich von Frank bekommen hatte (da gab es nur Variante 1), wie folgt angepasst:

- Alle Änderungen, die ich am Interpreter-Code gemacht habe, sind mit einem Kommentar "ct-Bot Anpassung" versehen.

- Man kann eine Funktion registrieren, die (falls registriert) aufgerufen wird, bevor der Basic-Interpreter eine neue Zeile einliest:
 http://www.heise.de/ct/projekte/machmit/ctbot/browser/devel/ct-Bot/bot-logic/ubasic_tokenizer.c#L246
- Das Basic-Verhalten des Bots registriert eine Funktion (der Code befindet sich somit nicht innerhalb des Basic-Interpreter-Codes), die einen RAM-Puffer mit der nächsten Zeile des Programms, das von der SD-Karte eingelesen wird, füllt. 

- Die Keyword-, Call- und CVar-Tabellen habe ich ins Flash verschoben, um RAM zu sparen.
- Die Suche in der Call- und CVar-Tabelle hatte einen Bug: Es wurde erst strcmp() aufgerufen und anschließend der Parameter auf ungleich NULL geprüft. Da strcmp() gemäß C-Standard selbst nicht auf NULL prüfen muss (und das je nach Plattform auch nicht tut), kam es hier zum Programm-Absturz. Ich habe den Check auf NULL vor den strcmp()-Aufruf verschoben. 

> In der neuesten Version gibt es auch noch ein paar kleine Verbesserungen
> beim Basic-Syntax. Ich verweise mal auf das EBNF ganz hinten in der Doku,
> welches den vollständigen realisierten Syntax widerspiegelt.

Dazu kann ich leider nichts sagen, da ich weder die EBNF noch den kompletten Umfang der alten Version kenne.

> Für Fragen stehe ich gern zur Verfügung, sage aber gleich, dass ich keine
> spezielle Anpassungen für den c't-Bot einbauen werde (es sei denn, die
> sind auch für andere Plattformen interessant).

Klar, es wäre sicher auch nicht sinnvoll, ct-Bot-spezifischen Code in das allgemeine Projekt zu integrieren. 
Ich habe die Anpassung für das zeilenweise Einlesen des Programms so allgemein wie möglich gehalten, um die Änderungen am Basic-Code zu minimieren. Grundsätzlich würde ich sagen, dass diese Erweiterung auch für andere Plattformen interessant ist. Meiner Meinung nach ist eine Zeile die kleinste, sinnvolle Einheit (entsprechende RAM-Größe vorausgesetzt). 
Wenn Du das in den Basic-Interpreter einbauen möchtest, würden wir vom ct-Bot-Projekt neue Versionen leichter integrieren können, wenn nicht, ist das aber auch nicht weiter tragisch - die paar Codezeilen können wir auch leicht wieder in eine neue Version einbauen. 

> Speziell meine ich dabei
> z.B. die PRINT-Anweisung, da gab es schon heftige Diskussionen mit
> Frank...:-).

Also da würde ich das PRINTF-Makro einfach auf "vsnprintf(mein_puffer, ...); ++last_print_line;" definieren und dann kann das Basic-Verhalten im Bot-Code mit der Ausgabe anstellen, was es möchte, sobald sich last_print_line geändert hat. Ich denke, da braucht man keine besondere Anpassung im Basic-Interpreter. Weiter habe ich mich mit dem Thema aber nicht beschäftigt.


Soweit erstmal meine Anmerkungen aus ct-Bot-Sicht, ich hoffe, sie helfen etwas weiter. 

Grüße,
Timo