Absender: Uwe Berger
Datum: Do, 02.12.2010 22:03:54
In-reply-to:
<BDD47587-412D-4D50-AC42-4B488A95575E@xxxxxxxxxxxxxxx>
References:
<1ac236efdb2ae353dba0652e0009d0d7.squirrel@xxxxxxxxxxxxxxxxxxx> <BDD47587-412D-4D50-AC42-4B488A95575E@xxxxxxxxxxxxxxx>
MoinMoin, Timo Sandmann schrieb:
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.
>naja, auch 4kB bzw.16 kB SRAM sind nicht unendlich viel. Selbst ein kurzes Basic-Programm könnte leicht diesen RAM dynamisch "zumüllen", je nach der Komplexität eines Ausdrucks (gerade hier wird intensiv mit Rekursionen gearbeitet). ... und es gibt bestimmt auch Dinge die ihr programmieren wollt und SRAM verbrauchen.
> 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.
>10ms sind fast eine Ewigkeit, aber das Anliegen ist mir schon verständlich. Wenn der Basic-Interpreter vernünftig, und so wie gedacht eingebaut wird, wird nach jeder Programmzeile die Kontrolle wieder an das "Hauptprogramm" übergeben.
Frage, gerade diese wichtigen Funktionen, wie Sensorzustandwechsel, sind nicht in Interrupt-Routinen verpackt?
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.
>sagen wir es mal so, gerade sinnvoll implementierte Routinen zum Schreiben/Lesen von SD-Karten sollten intern einen Bufferspeicher verwenden, auf den in der Regel ein Block (meist 512 Byte bei FATxx) von der Karte eingelesen wird und sämtliche Zugriffsroutinen (Lesen, Schreiben, Positionieren) arbeiten auf diesem Buffer. Warum sollte dann also noch ein weiterer Buffer vorhanden sein, wir arbeiten doch schon im RAM... Ich gehe davon aus, dass es auch einen solchen internen Bufferbereich in euren SD-Karten-Routinen gibt. Auch Filezugriffsroutinen aus der libc arbeiten auch mit solchen internen Buffern, sonst würden die Schreib-/Leseköpfe einer Festplatte nicht zur Ruhe kommen.
Mit der Bemerkung zu atoi und strcmp hast du natürlich recht. Aus diesem Grund ist ersteres ganz aus dem Code verschwunden (Zahlen werden jetzt von Hand und sind wahrscheinlich Maschinencode-sparender umgesetzt). strcmp existiert natürlich noch, da eine eine eigene Implementierung nicht so effektiv wäre, und da gibt es noch einen kleinen Buffer (;-)), der so groß, wie die maximale Befehlswortlänge ist (tokenizer.c; Routine get_next_token()).
Achso, mit dem Zeilenbuffer wird auch wieder eine künstliche Beschränkung geschaffen, die max. Basic-Zeilenlänge ist begrenzt (ich gehe davon aus, dass kein malloc verwendet wird)...
> gespeichert werden können, keine libc mit fread() usw.). Ich habe die Version des Interpreters, die ich von Frank bekommen hatteDreh- 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
> (da gab es nur Variante 1), wie folgt angepasst:
bei den 3 Varianten handelt es sich, wie auch einleitend bemerkt, um BEISPIELE, die ich zum Testen verwendet habe. Sie sollen nur aufzeigen, wie diese Defines in tokenizer_access.* gemeint sind! Aber gerade das 3.Beispiel zeigt, dass man diese Defines auch mit eigenen Funktionen bestückn kann. fread, fseek sind durch die Routinen zu ersetzen, die die gleiche bzw. ähnliche Funktionalität für eure SD-Karten-Zugriffe haben. Mir ist schon klar, dass es keine libc für den Bot gibt!
(Nebenbei, da ich es leid war, ständig meinen mega16 zu flashen, gibt es die Linux-Versionen des Interpreters und ist auch dann letztendlich der Antrieb dafür, dass er plattformunabhängig ist und bleibt. Wahrscheinlich analog eurem Simulator....)
> die einen RAM-Puffer mit der nächsten Zeile des Programms, das von der SD-Karte eingelesen wird, füllt.- 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),
>wahrscheinlich könnte man dies auch in die Defines in tokenizer_access.* verpacken, es muss ja nicht unbedingt p++, *p, fread, fseek o.ä. drinstehen. Es zählt nur das Ergebnis des jeweiligen Defines, dahinter könnte sich auch eine ausgeklügelte Speicherverwaltung verbergen, solange die "Schnittstellen-Defintionen" eingehalten werden.
- Die Keyword-, Call- und CVar-Tabellen habe ich ins Flash verschoben, um RAM zu sparen.
>hatte ich mir bis jetzt geschenkt, da es sich nicht lohnte (Speicherverbrauch klein) und Stichwort Plattformunabhängigkeit, ich hätte auch dort einen Define-Konstrukt einbauen müssen..., naja, kommt vielleicht doch noch.
- 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.
aha, schaue ich mir mal an und werde ich u.U. in meinen Code übernehmen.
bis auf DIM (Felder), DATA (konstante Daten) und INPUT entspricht der zur Verfügung stehende Basic-Syntax dem damals sehr weit verbreiteten TinyBasic. Zum Testen habe ich ein paar alte Programme im Netz gesucht/gefunden und erfolgreich durch meinen Interpreter gejagt. Wobei ich aber gerade darüber nachdenke das angesprochene Fehlende auch noch einzubauen...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.
siehe weiter oben meine Anmerkung zu dem Thema...
richtig, dafür gibt es ja das Makro, Frank wollte es am Anfang nur nicht einsehen...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.
Grüße Uwe