MoinMoin, auf der Webseite: http://bralug.de/wiki/Basic-Interpreter_f%C3%BCr_AVR_(uBasic-avr) habe ich angefangen einen Basic-Interpreter für AVRs zu dokumentieren. Grundlage der Geschichte ist ubasic (http://www.sics.se/~adam/ubasic/), welches ich um AVR-spezifische Dinge aufgebohrt habe (bzw. noch dabei bin...). Der entstandene Interpreter ist in C geschrieben, ist relativ ressourcenschonend und kann leicht in eigene AVR-Programme integriert werden. Es handelt sich also nicht um eine "Standalone-Lösung", wie z.B. J.Ws. AVR-ChipBasic-Lösungen. Grüße Uwe PS.: Kommentare, Ideen etc. sind ausdrücklich erwünscht...
Uwe Berger schrieb: > auf der Webseite: > http://bralug.de/wiki/Basic-Interpreter_f%C3%BCr_AVR_(uBasic-avr) <<<< > habe ich angefangen einen Basic-Interpreter für AVRs zu dokumentieren. ähmm, die abschliessende Klammer bei der URL gehört mit zum Link... also so: bralug.de/wiki/Basic-Interpreter_für_AVR_(uBasic-avr) Uwe
Gast
#1703747
Schaue dir mal den BasicBeetle an: www.DieProjektseite.de
Donald schrieb: > Schaue dir mal den BasicBeetle an: www.DieProjektseite.de > ja, hatte ich schon im Vorfeld gemacht. Was meinst du damit? Grüße Uwe
... vielleicht nochmal zum Verständnis meines Interpreters: Es geht nicht darum ein Fertiggerät zu haben, was Basic-Programme interpretieren kann, wie es z.B. AVR-ChipBasic oder BasicBeetle ist! Mein Ansatz ist ein anderer: bei mir handelt es sich mehr um eine Art Library, die in eigene Mikrocontroller-Programme mit eingebunden werden kann, um z.B. auf bestehender Firmware Programmfragmente nachladen und ausführen zu können. Im derzeit vorliegenden Quelltext ist mindestens ungeklärt, wie der Basic-Quelltext in den Mikrocontroller kommt. Es werden nur die entsprechenden Schnittstellen zur Verfügung gestellt. Das Testprogramm zeigt nur eine Möglichkeit über die serielle Schnittstelle auf. Klar könnte man aus dieser Library auch solche Komplettgeräte, wie ganz oben erwähnt programmieren, aber wie gesagt, dass will ich ja gar nicht... Grüße Uwe
@uwe Schöne Adaption des uBasics. Hatte mir den Code mal vor 2-3 Jahren angeschaut, und war schon recht beeindruckt von der Größe. Und es ist eine gute Grundlage zum Verständnis eines Interpreters. Für mein geplantes Basic hat mir der Code vom Dunklen Adam ;-P auf jedenfall geholfen. Bei meinem Basic (ich werds bei dem geplanten Funktionsumfang wohl nicht uBasic oder TinyBasic oder so nennen können :))) wird es keine Zeilennummern geben, es soll Prozeduren/Funktionen geben und wenn ich es hinbekomme wirds auch die Möglichkeit geben Programmteile "nachzuladen" (sprich zur Laufzeit ausführen). Mal schauen wie weit ich komme.
Gast
#1707087
Cool wäre es, ein "Program" von einer externen Quelle (I²C EEPROM, SDCARD oder UART) Nachzuladen. Ich habe soetwas schon lange vor, aber mit einer AWL-Angehauchten Syntax, damit auch "Elektricker" damit was anfangen können ;-)
@tüddel Im Prinzip wäre der Parser den ich hier in der Code-Sammlung vorgestellt habe geeignet für soetwas. Einfach die Befehle für die AWL-Sprache definieren, die Funktionen dazu implementieren (also den C Code für die einzelnen AWL Befehle), SD-Karten/EEP-Unterstützung hinzufügen (das man Zeile für Zeile auslesen kann, und die aktuelle Lese-Position setzen/holen kann für Sprung-Befehle), und schon hat man ein brauchbares Grundgerüst. Nur mal so als Vorschlag.
MoinMoin, tüddel schrieb: > Cool wäre es, ein "Program" von einer externen Quelle (I²C EEPROM, > SDCARD oder UART) Nachzuladen. > ist im Prinzip möglich, in meiner Doku (obiger Link) habe ich ja geschrieben, dass der Quelltext der Funktion ubasic_init() übergeben werden muss. Um das Parameter zu füllen, dann man seinen eigenen Weg gehen. Desweiteren könnte man auch gleich mit einem anderen Medium arbeiten (z.B. SD-Card), muss dann aber den Quelltext an den entsprechenden Stellen abändern. Mein Plan ist es auch, das basic-Programm von SD-Card zu lesen... Das die ganze Geschichte ein Basic darstellt, ist Zufall, weil ich halt uBasic gefunden hatte. Aber man könnte auch einen anderen Sprach-Dialekt auf der Grundlage aufsetzen. Ich finde uBasic zeigt sehr schön, wie man vorgehen könnte.... Grüße Uwe
MoinMoin, neben einiger Bug-Fixes und kleinerer Erweiterungen des Sprachumfanges, ist in der neuen Version die Implementierung des call-Befehles hervorzuheben. Mit diesem Mechanismus (siehe Quellcode und dort enthaltene Kurz-Referenzen) ist es möglich relativ einfach vorhandene C-Funktionen in den Basic-Interpreter einzubinden, um sie im Basic-Programm nach dem Muster:
1 | |
2 | |
3 | |
oder auch
1 | |
2 | |
3 | |
aufzurufen. Der Quellcode ist auf oben benannter Webseite zu finden. Grüße Uwe
MoinMoin, die neueste, auf obiger Webseite abgelegte, Version ermöglicht einen Durchgriff auf interne C-Variablen via Basic-Befehl. Desweiteren habe ich mir mal die Mühe gemacht eine Doku zu schreiben, die ebenfalls im Quelltextarchiv zu finden ist. Grüße Uwe
MoinMoin, ich habe mal die URL der entsprechenden Projektseite so geändert, dass sie besser lesbar und angebbar ist. Sie lautet jetzt: http://bralug.de/wiki/UBasic-avr (Der alte Link funktioniert ebenfalls.) Desweiteren sind die Basic-Befehle vpeek und vpoke hinzu gekommen, mit denen man sich einen Zugriff auf interne C-Variablen verschaffen kann. Im Quelltext-Archiv ist nunmehr eine kleine Dokumentation zu finden, welche zum einen als Kurzreferenz für die Basic-Befehle zu verstehen ist. Zum anderen wird dort versucht zu erläutern, wie man uBasic in eigene Programme einbinden kann. Grüße Uwe
Gast
#1752148
Uwe,
ich habe den interpreter noch ein bisschen erweitert.. falls es dich
interessiert.. um xor und die shift ops.. nuetzlich wenn man mit bit's
arbeitet ..
Dann kann man so was tun :
print 1+1, 15 ^ 1 , 1 shl 2
was ergibt :
2 14 4
lg roman
hier meine aenderungen :
tokenizer.h :
..
TOKENIZER_AND,
TOKENIZER_OR,
TOKENIZER_XOR,
TOKENIZER_LSH,
TOKENIZER_RSH,
TOKENIZER_ASTR,
TOKENIZER_SLASH,
TOKENIZER_MOD,
...
tokenizer.c
...
{"shl", TOKENIZER_LSH},
{"shr", TOKENIZER_RSH},
{NULL, TOKENIZER_ERROR}
...
} else if(*ptr == '%') {
return TOKENIZER_MOD;
} else if(*ptr == '^') {
return TOKENIZER_XOR;
} else if(*ptr == '(') {
return TOKENIZER_LEFTPAREN;
} else if(*ptr == ')') {
..
dann noch ubasic.c:
/*----------------------------------------------------------------------
-----*/
static int
expr(void)
{
int t1, t2;
int op;
t1 = term();
op = tokenizer_token();
DEBUG_PRINTF("expr0: token %d\n", op);
while(op == TOKENIZER_PLUS ||
op == TOKENIZER_MINUS ||
op == TOKENIZER_AND ||
op == TOKENIZER_OR ||
op == TOKENIZER_XOR ||
op == TOKENIZER_LSH ||
op == TOKENIZER_RSH )
{
tokenizer_next();
t2 = term();
DEBUG_PRINTF("expr1: %d %d %d\n", t1, op, t2);
switch(op) {
case TOKENIZER_PLUS:
t1 = t1 + t2;
break;
case TOKENIZER_MINUS:
t1 = t1 - t2;
break;
case TOKENIZER_AND:
t1 = t1 & t2;
break;
case TOKENIZER_OR:
t1 = t1 | t2;
break;
case TOKENIZER_XOR:
t1 = t1 ^ t2;
break;
case TOKENIZER_LSH:
t1 = t1 << t2;
break;
case TOKENIZER_RSH:
t1 = t1 >> t2;
break;
}
op = tokenizer_token();
}
..
MoinMoin, roman65536 schrieb: > ich habe den interpreter noch ein bisschen erweitert.. falls es dich > interessiert.. um xor und die shift ops.. nuetzlich wenn man mit bit's > arbeitet .. > stimmt, die Funktionen hören sich interessant an, ich werde sie in meiner Version aufnehmen... Nebenbei, ich bin gerade dabei den Code etwas speicherplatzmäßig zu optimieren. Es geht dabei vorallem darum den SRAM des AVR freizuschaufeln und konstante Felder (z.B. die Tabelle keywords des Tokenizers) optional in den Flash verlagern zu können. Das wird ebenfalls in der nächsten öffentlichen Version enthalten sein. Grüße & Danke Uwe
MoinMoin, auf der Projektseite http://bralug.de/wiki/UBasic-avr ist eine neue Version verfügbar. Folgende Erweiterungen: * eine interne statische Tabelle ist auf den PROGMEM auslagerbar; es wird dabei AVR-SRAM gespart (ca. 200 Byte) * neue Operatoren: * shl * shr * xor (nicht ^ wie vorgeschlagen, da ^ in Basic für Potenz steht...) * >= * <= * <> * alternative Operatoren * für | geht auch or * für & geht auch and * für % geht auch mod Die, im Quellcode-Archiv vorhandene Doku wurde entsprechend angepasst. Grüße Uwe
Gast
#1762865
Uwe, eine kleine anmerkung... bei den vpeek,vpoke und auch bei den externen functions aufruffen... Da gibt es eine moeglichkeit, einfacher die variablem wie auch funktionen zu definieren. und zwar macht man dies auch im Linux kernel so.. Dort werden "export" zugaengliche variablem und funktionen mit EXPORT_SYMBOL deklariert. Durch die macros von macros und macros, wird eine symbol tabelle automatisch erstellt. Diese ist zwar in einem reservierten segment, aber dies koennte man anderst umgehen. was denkst du ?? (hoffe du verstehst was ich meine :) siehe dir http://lxr.linux.no/#linux+v2.6.34/include/linux/module.h#L199 mal an. lg roman
Hallo Roman, ohne die Sache genau zu hinterblicken, denke ich aber dass es für die kleine Geschichte, wie der Basic-Interpreter sie ist, die Sache mit EXPORT_SYMBOL noch komplizierter ist. Und noch eine 2.Sache kommt zum tragen, die ich ich vor Wochen mal in einem parallel Thread über die call-Funktion ausdiskutiert hatte --> ich müsste dazu die Quellen der zu exportierenden Funktionen und Variablen anpassen. Das genau möchte ich aber nicht... Grüße & Danke Uwe PS.: wobei die Sache aber schon interessant ist und vielleicht mal woanders zu gebrauchen ist (nur halt nicht auf Mikrocontrollern).
Gast
#1763394
@Roman0xffff und Uwe Evtl. lässt sich mit dem Präprozessor die vpeek&vpoke fkt reinfach erweitern. Ich habe in einem anderen Thread etwas zu der einfachen Erweiterbarkeit/Wartung mittels Präprozessor schon was geschrieben. Also wenns nur zur Compilierzeit sein muß und nicht zur laufzeit dann lässt sich das ganze recht einfach erweitern. Ich mache mal ein Beispiel dazu fertig. Vllt kann Uwe das ja bei sich mit aufnehmen.
TheMason schrieb: > Also wenns nur zur Compilierzeit sein muß und nicht zur laufzeit dann > lässt sich das ganze recht einfach erweitern. Ich mache mal ein Beispiel > dazu fertig. Vllt kann Uwe das ja bei sich mit aufnehmen. > ok, zeige mal her, wenn du soweit bist... Bzw. welches war der andere Thread? Grüße Uwe
Gast
#1763467
Uwe die sache ist gar nicht so schlimm...
kucke dir das wirklich mal an, hier ein "sampler" ;)
278 ssize_t vfs_read(struct file *file, char __user *buf, size_t count,
loff_t *pos)
279{
280 ssize_t ret;
281
282 if (!(file->f_mode & FMODE_READ))
283 return -EBADF;
..
..
..
303 return ret;
304}
305
306 EXPORT_SYMBOL(vfs_read);
307
Die Function wird ganz normal deklariert, so wie es der interpreter
braucht,
anschliessend wird per Macro die function in die funktions tabelle
aufgenommen. das zu kompilier zeit .. so ala TheMason..
In dem Linux Kernel funzt das ganz relativ kompliziert ab. Den alle
Symbole werden in eine Symbol section reingeschrieben. Diese kann man
ganz normal erreichen, da sie in dem ld script definiert wird siehe:
__start___ksymtab = .;
*(__ksymtab)
__stop___ksymtab = .;
Das ganze geht dann in die data section des kernels. Vor 2.4 tat man
dies anderst, mit verlinkten listen... jedoch musste man alle funktionen
die man exportieren moechte irgendwie dem kernel bekannt manche. Meist
per init funktion vom modul oder device driver. mit dem macros ist es
nun einfacher geworden..
Ich probiere was .. aber zb. die .fini9 bis .fini0 sectionen koennte man
dafuer verwuergen :p
lg roman
Gast
#1764340
So. Habe mal etwas gebastelt. Eine auf der Original-Version von Adam Dunkels basierende Erweiterung. Ist noch nicht ganz fertig, aber vorab schonmal eine Zwischenversion. Lässt sich aber für AVR compilieren und (scheint) im Simulator auch zu laufen. Allerdings eben ohne Hardware-Ansteuerung. Dafür aber schon mit (hoffentlich korrekten) Flash-Routinen (also das Schlüsselwörter nicht mehr zusätzlich RAM belegen). Was kann die Erweiterung ? - Schlüsselworte (und fkt) in einer Datei (basic_cfg.h) - eigene Benutzerfunktionen (mit is zu 4 Parametern) "einhängbar" - eigene Operatoren "einhängbar" (allerdings nur 1-Zeichen lang) - Zugriff auf Variablen in C - Zugriff auf Arrays in C - Variablen, Arrays und Benutzerfunktionen lassen sich in Ausdrücken verwenden - einfache Konfiguration in basic_cfg.h Beispiel Funktionen stehen in der use-basic.c Bei Fragen einfach melden. Es ist wie gesagt nur eine Zwischenversion. Daher auch der Original-Datei-Name (uBasic-0.1.zip). Und noch nicht am lebenden AVR getestet.
Gast
#1764455
Nachtrag : Die an die Benutzerfunktion übergebenen Datentypen können Zahlen bzw Ausdrücke oder Strings sein.
Gast
#1764702
Hier eine erneute Version. Sollte auf AVR wie auf PC lauffähig sein. Ich habe den Unterbau von Uwe verwendet (also die Eingabeaufforderung für die Befehle load, load x, run und list), und für die PC-Version Makros angelegt sodass die Funktionen usart.c auf dem PC verwendet werden können. Viel Spaß beim ausprobieren.
Gast
#1764775
<zynismus on> Man koennte doch den interpreter in ein Rom schiessen und anstatt bios im PC reinstecken :D damit waeren wir wieder ganz am Anfang. Back to the future.. <zynismus off> @Mason koennstest du "^" veraendern und "xor" verwenden ?? Das ^ wird meistens im Basic fuer "hoch" verwendet, 2^2=4 5^2=25.. gruesse roman(2^16)-1 ;)
Gast
#1764794
eine frage ?? Mason.. du verwenderst ja so in etwa dein parser... jetzt.. ein hypotetischer beispiel ok ?? ich moechte die expression erweiter um die ">=".. wie muss ich das anstellen ?? lg roman
Gast
#1764825
@roman (16^4)-1 : Ich habe vor das man die Operatoren frei definieren kann. Im moment können Oepratoren nur 1 zeichen lang sein. Daher funktioniert auch ein <>, >=, <= nicht als Operator. Dasselbe gilt für and,or und xor. Ich Bau das noch ein. Ist kein Ding. Hatte es nur noch nicht ausprogrammiert. Das Problem liegt darin das nur in den Tokens TOKEN_CHAR im Abschnitt BASIC_TOKENS die Operatoren definiert werden können. Die Zuordnung muß aber in TOKEN_TERM/EXPR/REL im Abschnitt TOKEN_OPS gemacht werden. Ich denke das werd ich wohl heute abend oder so einstellen können. Dann sind auch Operatoren wie mod, shl, shr, usw drin. Wie meinst du "So in etwa verwendest du deinen Parser ?" Mein Parser ist unabhängig vom uBasic-Strang (selbst wenn ich mir durch diesen Quellcode einiges aneignen konnte und schon einige Dinge dahingehend umgesetzt habe mir ein eigenes Basic zu schreiben)
Roman65536 schrieb: > <zynismus on> > Man koennte doch den interpreter in ein Rom schiessen und anstatt bios > im PC reinstecken :D damit waeren wir wieder ganz am Anfang. Back to the > future.. > <zynismus off> naja, soweit davon ist ja das Basic auf einem Mikrocontroller nicht entfernt, eigentlich fehlt doch nur noch eine vernünftige Eingabemöglichkeit für die Basic-Programme, deren dauerhafte Abspeicherung auf einem beschreibbaren Medium, z.B. SD-Card, und dann hast du doch fast einen Homecomputer, der vor 25 Jahren der Renner war... OK, eine schicke Ausgabe auf einem Bildschirm oder Fernseher wäre auch noch schick, aber machbar... beweisen ähnliche Projekte... Uwe
TheMason schrieb: > Hier eine erneute Version. Sollte auf AVR wie auf PC lauffähig sein. > Ich habe den Unterbau von Uwe verwendet (also die Eingabeaufforderung > für die Befehle load, load x, run und list), und für die PC-Version > Makros angelegt sodass die Funktionen usart.c auf dem PC verwendet > werden können. > Viel Spaß beim ausprobieren. schwere Kost ;-), die man sich mal in Ruhe anschauen und ausprobieren muss (ich habe mir noch nie deine Parser angesehen, nur davon gehört, dass sie reichlich tricky sind...). Es ist nicht mehr viel von der Ursprungsversion übrig geblieben. Zwei Dinge, die mir sofort aufgefallen sind und die ich nicht so gut finde: Bei den Userfunktionen/-variablen gibt es jetzt keine klare syntaktische Trennung zwischen "Standard-Basic" und eben diesen. Deshalb hatte ich die Konstrukte mit call, vpeek und vpoke gewählt, um damit zum Ausdruck zu bringen, dass es externe Dinge sind, die eigentlich nicht Basic-Bestandteil sind. Ich habe es noch nicht für einen AVR übersetzt und ausprobiert, muss ich mal in Ruhe machen, aber gefühlsmäßig könnte jetzt zuviel Overhead da sein, der mehr Resourcen des AVR okupiert... Aber zweifellos eine interessante Sache, die es ermöglicht einfacher neue Befehle zu kreieren und einige schöne Ansätze aufzeigen. Wie gesagt ich muss mal in Ruhe drüberschauen, vollständig verstehen und sehen, was ich adaptiere... Grüße & Danke Uwe
Gast
#1764899
@uwe In dem Thread AVR CP/M wird eine kleine schnuckelige Platform vorgestellt. Da werkelt ein AVR (MegaXX8) drauf und es ist noch ein 128KB DRAM, eine SD-Card sowie ein FT232 drauf. Ich denke mit dieser Platform lässt sich bezüglich des Basics sehr viel machen. Also für lange Programme, für einen Editor usw wäre damit genug Platz. Habe dazu auch schon ein paar Ideen. Ich denke ich werde mit dieser Platform mal was in Richtung des uBasics veranstalten :-)
achso, noch eine Anmerkung zu den "Einzeichen-Befehlen" und den
"Verwandten" ("<=", ">=", ...): ich habe in Abänderung vom Original in
meiner Version die Verarbeitung der Keywörter vor die Suche nach
Einzeichen-Befehlen gestellt, damit zuerst Keywörter (damit auch "<="
usw.) gefunden werden und dann erst Einzelbefehle ("<", ">", ...).
Uwe
TheMason schrieb: > In dem Thread AVR CP/M wird eine kleine schnuckelige Platform > vorgestellt. ... ja kenne ich, war auch nur eine Anmerkung zu dem "Zynismus" von Roman... ;-) Uwe
Gast
#1765025
Die verwendung von den includes..ist zwar fuer den preprocessor schoen und nach dem gcc -E sieht es auch super aus. doch man(n) muss zugeben, die lesbarkeit leidet.. dh. moechte man den parser zb. erweitern, um eine binaere suche (den mit strcmp schnell was suchen, ist so eine sache), ist der code jetzt schwerer zu lesen. zum beispiel.. die vielen if's in singelchar, koennte man doch per map resp. tabelle und der *ptr waere index, schneller loessen als sich durch die vielen if's durch zu arbeiten. bei der keywords... keywords sortieren und ueber eine hilfs tabelle fuer den anfangs buchstaben der keywords, an den effektiven keyword zu gelangen. Erst dann den strcmp durchfuehren. Die hilftabelle koennte ja schon hinweise beinhalten, das zb. kein keyword vorhanden ist der mit dem buchstaben "Z" anfaengt. ala.. key_tab['P']= &keywords[print]; Was mir auch vorschwebt im kopf.. zb. ein goto/gosub cache einzubauen.. den so wie die routinen jetzt arbeitet, sucht die jump_linenr von anfang an, bis die entsprechende linenr gefunden worden ist. :) lg roman(4^8)-2
Gast
#1765060
@roman 256^2 Es stimmt schon das es nicht soo schön lesbar ist, obwohl mit der richtigen Formatierung gehts einigermaßen. Viel wichtiger finde ich eig. das man bei komplexeren Datengebilden mit Querverweisen diese automatisch Eintragen lassen kann, und damit die Daten immer konsistent sind. Ich denke da kann man sich eine ganze Menge Arbeit mit ersparen, vor allem eine ganze Menge Fehlersuche in verrutschen Datentupeln oder Enums die einfach in der Zeile verrutscht sind. Binäre Suche wäre sicherlich deutlich schneller, aber ich denke weder mit Präprozessor, noch mit einfachen mitteln lässt sich das "mal eben" machen. Die Tabellenstrukturen sind da ja schon aufwendiger. Und ich denke dann wäre die lesbarkeit vollends im A...... Das ein plattes strcmp nicht das optimalste ist ist klar :-) Die Idee mit dem GOTO/GOSUB Cache habe ich auch, wobei ich denke das es evtl vllt sogar noch sinniger ist dem Basic-Interpreter die Tokens direkt zum Fressen vorzuwerfen. Also das ein Text bereits übersetzt ist (mit den notwendigen Informationen) und eben keine strcmp's und Suche nach Zeilennummern gemacht werden muß. Und so wie ich die Struktur des Basic-Interpreters verstanden hab müsste es sehr einfach sein da Umschalten zu können, da alle notwendigen Fkt in der tokenizer.c gekapselt sind. Diese müsste man eig. "einfach nur" austauschen und dann dürfte das ganze viiiieeeeel schneller gehen. Den Ansatz werde ich mal weiter verfolgen. Evtl das dieser "Compiler" sogar auf einem AVR lauffähig ist (die Daten können ja auf SD-Karte geschrieben werden, und das übersetzte Programm von der Karte aus ausgeführt werden). Als Krönung wäre dann noch das man zu den Token-Informationen noch Informationen zum Quellcode selbst mit ablegt (z.b. Zeichenposition Token <-> Text). Dann hat man "relativ" leicht die Möglichkeit umfangreiche Debug-Möglichkeiten (mit Ausgabe des Zeileninhaltes usw) zu schaffen. Aber das ist noch alles etwas Träumerei ;-)
Gast
#1765087
@theMason Also so in etwa wie es anno domini der 8052 oder auch der C64'er basic glaube ich) tat ?? Der 8052er basic hat, die eingegebene zeile gleich zu tokens verarbeitet und nur so abgeschpeichert. Dafuer hat man die upper 128 von den 8 bits verwendet. Der "list" musste das dann interpretieren, was ja nicht soo schwer ist. jedoch fuer die goto/gosub, so wie es du schreibst brauchte man eine eine 2 pass loessung. den vorwaerts gerichtete goto/gosub's kennt man nicht von anfang an. Was das andere angeht, mit preprocessor geht das evtl. gar nicht... jedoch .. die shell scripts verwanden laeute schreiben ja gern kleine scripts .. :) btw. deine version funzt jetzt auch mit Linux 8) roman(2^(32-16))-1
Gast
#1765119
@roman ((485*9)+4)*3*5 der C64 hat nur die Schlüsselwörter in Tokens übersetzt, der rest (Zahlen und Zeichen) wurden meine ich so Interpretiert. Man kommt um 2 Pass nicht drum herum, aber ich denke der Geschwindigkeitsvorteil dürfte enorm sein (die Suche nach Schlüsselwörtern, das Parsen von Zahlen, das suchen nach Zeilennummern usw).
Gast
#1765189
der 8052er basic tat es aenlich.. jedoch die zeilen nummern waren binaer .. dem kann ich nur zustimmen... der cray unter den basic's :) hihi... btw.. habe gerade den goto cache ausgetestet (quick and very very dirty) .. geht super..
Uwe Berger schrieb: > Ich habe es noch nicht für einen AVR übersetzt und ausprobiert, muss ich > mal in Ruhe machen, aber gefühlsmäßig könnte jetzt zuviel Overhead da > sein, der mehr Resourcen des AVR okupiert... hmm, diese Vermutung scheint sich zu bewahrheiten: Ich habe mal die Version von TheMason für meine Test-AVR-Umgebung übersetzt und geflasht. Das Ergebnis ist nicht gut (unused mem): --> nach Reset: 311 Byte --> nach load 0: 311 Byte --> nach run: 48 Byte!!!! Das ist Ergebnis ist auch bei den anderen Programmen ähnlich. Bei meiner letzten Version sieht es etwas anders aus: --> nach reset: 395 Byte --> nach load 0: 391 Byte (ein ungefähr vergleichbares Basic-Programm) --> nach run: 379 Byte!!!! ... da wird zur Laufzeit des Interpreters zuviel SRAM verbraucht...! Grüße Uwe
Gast
#1765778
Mmh, der SRAM-Verbrauch rührt evtl (oder eher sehr wahrscheinlich) daher das der durch den Präprozessor generierte Typ recht viel RAM verbraucht. Bei anderen Einstellungen in der BASIC-CONFIG (Parameter MAX_USER_KEYWORD_LEN und MAX_USER_PARAMS) spielen bei dem "Verbrauch" des User-Datentyps, der in jeder Rekursion von "varfactor" lokal angelegt wird, eine enorme Rolle. Da aber der Typ String als Übergabe-Parameter oftmals sinnvoll ist (z.b. bei LCD-Ausgaben) und Strings naturgemäß recht viel SRAM verbrauchen ist der Parameter MAX_USER_KEYWORD_LEN MAX_USER_PARAMS (Rekursionen der Ausdrücke) ein grobes Maß des SRAM Verbrauchs plus dem Overhead der durch die Erweiterung allgemein entsteht. Würde man auf Strings verzichten, so wäre MAX_USER_KEYWORD_LEN = 2 und es wäre ein "moderater" Anstieg des Ressourcenverbrauchs. Aber in Anbetracht der Features ist der erhöhte Ressourcenverbrauch denke ich (und hoffe ich :-)) gerechtfertigt. Dazu müsste man aber weitere "Messungen" vornehmen. Das Ganze ist im Moment eher "auf die Schnelle" gemacht und nicht gerade 100% ressourcenoptimiert. Ursprünglich war auch nur die einfache Erweiterung der call-Schlüsselwörter bzw Schlüsselwörter generell "geplant", aber irgendwie hab ich mich in dem Interpreter "verbissen". Da gibts einfach zu viele Möglichkeiten ;-)
TheMason schrieb: > Aber in Anbetracht der Features ist der erhöhte Ressourcenverbrauch > denke ich (und hoffe ich :-)) gerechtfertigt. eigentlich nicht, da es auf einem AVR, für den ich den Interpreter vorgesehen habe, kronischen Platzmangel im SRAM gibt. Der Interpreter soll ja eigentlich nur Beiwerk zu anderen Firmwarebestandteilen sein... Für einen Basic-Interpreter, der nicht auf einer Plattform mit beschränkten Resourcen laufen soll, hat deine Lösung natürlich schon einen gewissen Charme. Aber wie gesagt, dass ist eigentlich nicht mein Anliegen. Mal zur Diskussion über einen möglichen goto/gosub-Cache: Für For-Next-Schleifen gilt nebenbei ähnliches. Ich befürchte solch ein Cache frisst weitere Resourcen im SRAM. Zumindestens für gosub und for-next bietet sich eine andere Lösung an! Statt der Basic-Zeilennummer, könnte man auch den entsprechenden Pointer auf den Basic-Quelltext abspeichern und dorthin bei return bzw. next (zurück)springen. Für goto-Anweisungen hat man sowieso ein Problem, weil man nicht weis, ob es ein Sprung nach vorn oder zurück ist. Man müsste im Vorfeld das ganze Basic-Programm analysieren und entsprechenden Einsprungstellen abspeichern, was wieder auf Kosten der Performance und der Resourcen gehen würde... Grüße Uwe
Gast
#1766259
@Uwe Ich geb dir Recht das der SRAM Verbauch immer etwas kniffelig ist, bzw die Gratwanderung zwischen Features und Performance. Aber in einem kleinen AVR (512-1024Byte SRAM) ist der Interpreter ohnehin schon etwas "übertrieben", da dann nur noch wenig SRAM für die eigentlichen Firmwarefunktionen übrigbleibt. Und dadurch das der Basic-Code nur aus dem SRAM ausgeführt werden kann ist bei kleinen AVR's ohnehin schnell Schicht im Schacht, was die Programmlänge angeht. Ich denke mein Ansatz bei dem Basic ist eher für Größere AVR's (ab Mega16/32 mit 1-16kB RAM) wirklich sinnvoll einsetzbar, oder aber für ARMs usw. @All Ich habe mal mit dem "Compiler"-Ansatz etwas ausprobiert. Also das der Interpreter auch in der Lage ist Basic-Code zu "compilieren". Dabei werden in einem ersten Schritt die Goto/Gosub-Adressen ermittelt, und im zweiten Schritt die Tokens in ein Byte-Array gepackt (und bei Goto/Gosub die Stelle im Byte-Code angegeben). Weiterhin lässt sich der Byte-Code auch mit (nahezu) den gleichen Routinen ausführen. Zeitmessungen und SRAM-Verbräuche habe ich noch nicht ermittelt. Aber ich denke das man mit dem compilierten Byte-Code schon recht gute ergebnisse erzielen kann was SRAM-Verbrauch angeht. Von der Geschwindigkeit ganz zu schweigen.
TheMason schrieb: > Und dadurch das der Basic-Code nur aus dem SRAM ausgeführt werden kann > ist bei kleinen AVR's ohnehin schnell Schicht im Schacht, was die > Programmlänge angeht. > Ich denke mein Ansatz bei dem Basic ist eher für Größere AVR's (ab > Mega16/32 mit 1-16kB RAM) wirklich sinnvoll einsetzbar, oder aber für > ARMs usw. meine Testumgebung ist ein Mega16, immerhin sind da noch ca. 300 Byte Platz. Damit kann man noch eine Menge anfangen... Mit dem Basic-Code im SRAM hast du natürlich recht, aber einer der nächsten geplanten Schritte ist, das Basic-Programm von einer SD-Card (zeichenweise) einzulesen --> in tokenizer.c müssten dazu die Zugriffe auf die beiden Pointer (ptr und nextptr) entsprechend angepasst werden. Die ganze Geschichte mache ich eigentlich auch nur, weil ich in der Firmware zu meinem NET-I/O mit Add-On-Board eine Art Jobstarter (ala Cron) schreiben will, bei dem die auszuführenden Jobs halt Scripte auf der SD-Card sein sollen. Grüße Uwe
Gast
#1766338
Ehhh... Wenn es aber um limitierten resourcen geht, dann ist evtl. dieser basic interpreter so oder so "zu gross". Es gab einmal vor lange lange lange zeit, einen interpreter.. und der source code dazu ist 1536 bytes gross ... er kann aber so ziemlich alles was Dunkels interpreter kann.. ok Ihr werdet lachen wo man so was findet, obfuscated C aber schickt man den code durch den preprocessor und ein bisschen formartierung, sieht der code ganz ok aus und ziemlich schlau.. http://www0.us.ioccc.org/1990/dds.hint lg roman0x1000-1
Gast
#1766406
@Uwe >das Basic-Programm von einer SD-Card (zeichenweise) einzulesen Das ist ebenfalls eine Erweiterung die ich noch im Kopf habe. Zudem soll es dann auch möglich sein den Compilierten Byte-Code von SD-Karte auszuführen. Dann kann man (abgesehen von den 512 Byte für den SD-Karten Buffer) auch wieder etwas RAM freischaufeln. @Roman 0x10000-1 >ist evtl. dieser basic interpreter so oder so "zu gross" Daher auch der Ansatz möglichst viele Funktionen die sich "relativ" einfach nachrüsten lassen unterzubringen. Es braucht dann zwar mehr Ressourcen, aber kann dann auch entsprechend mehr. Ich werde bei meiner bestehenden Version noch einen Compiler-Schalter einbauen, mit dem man die zusätzliche Funktionalität (C-Routinen, C-Variablen, C-Arrays) abschalten kann um so das ganze flexibel anpassen zu können.
Gast
#1766451
Hier noch mein goto/gosub/for/next quick an very dirty cache.. ohne es
zu wissen, es cached auch die for/next :)
Bitte nicht erschiessen wegen dem 256 array.. war ja nur ein test auf
linux.
ubasic.c :
...
int ended;
extern char *ptr, *nextptr;
char *goto_cache[256];
char *goto_cachen[256];
int expr(void);
void line_statement(void);
void statement(void);
int get_parameters(T_USER_FUNC_DATAS *params, int numparams, int next);
int get_arrayindex (void);
void accept(int token);
/*----------------------------------------------------------------------
-----*/
void ubasic_init(const char *program)
{
int a;
for(a=0;a<256;a++) goto_cache[a]=goto_cachen[a]=(char *)0; //init
cache..
program_ptr = program;
for_stack_ptr = gosub_stack_ptr = 0;
tokenizer_init(program);
...
/*----------------------------------------------------------------------
-----*/
void jump_linenum(int linenum)
{
if(goto_cache[linenum % 255] != 0) {
ptr=goto_cache[linenum % 255];
nextptr=goto_cachen[linenum% 255];
printf("cache hit for %d %x %x\n",linenum,ptr,nextptr);
tokenizer_init(ptr);
return;
}
tokenizer_init(program_ptr);
while(tokenizer_num() != linenum) {
do {
do {
tokenizer_next();
if (tokenizer_token() == TOKENIZER_ERROR)
{
FATAL_ABORT();
}
} while(tokenizer_token() != TOKENIZER_CR &&
tokenizer_token() != TOKENIZER_ENDOFINPUT);
if(tokenizer_token() == TOKENIZER_CR) {
tokenizer_next();
}
} while(tokenizer_token() != TOKENIZER_NUMBER);
DEBUG_PRINTF("jump_linenum: Found line %d\n", tokenizer_num());
}
if(goto_cache[linenum % 255] == 0) {
goto_cache[linenum %255]=ptr;
goto_cachen[linenum%255]=nextptr;
printf("cache fill %d %x %x\n",linenum,ptr,nextptr);
}
}
hier noch der output (auf linux) :
AVR-uBasic-Interpreter; Uwe Berger, 2010
Compiliert am Jun 30 2010 um 14:55:28
>load 2
load 2
Programm 2 geladen.
>run
run
1
cache fill 20 804b192 804b194
2
cache hit for 20 804b192 804b194
3
cache hit for 20 804b192 804b194
4
cache hit for 20 804b192 804b194
5
cache hit for 20 804b192 804b194
6
cache hit for 20 804b192 804b194
7
cache hit for 20 804b192 804b194
8
cache fill 50 804b1bf 804b1c1
cache hit for 50 804b1bf 804b1c1
cache hit for 50 804b1bf 804b1c1
cache hit for 50 804b1bf 804b1c1
cache hit for 50 804b1bf 804b1c1
cache hit for 50 804b1bf 804b1c1
cache hit for 50 804b1bf 804b1c1
30
cache fill 80 804b1ef 804b1f1
31
cache hit for 80 804b1ef 804b1f1
32
cache hit for 80 804b1ef 804b1f1
33
cache hit for 80 804b1ef 804b1f1
34
cache hit for 80 804b1ef 804b1f1
35
cache hit for 80 804b1ef 804b1f1
36
cache hit for 80 804b1ef 804b1f1
37
>list
list
10 for i = 0 to 7
20 print adc [i]
30 next i
40 for i = 0 to 7
50 adc [i] = 30 + i
60 next i
70 for i = 0 to 7
80 print adc [i]
90 next i
99 end
>
lg roman
Gast
#1766494
@Roman 0b1111111111111111 Ich glaube den "goto_cachen" kannst du dir glaube ich sparen indem du nach dem setzen des zeigers ein "tokenize_next()" aufrufst. Zumindest hab ich das so verstanden. Das setzen des Zeigers holt noch nicht das aktuelle Token ab, und so kommt der Interpreter durcheinander, denn bei jedem accept muß das aktuelle Token vorhanden sein (also der ptr-zeiger schon auf das nächste Token stehen).
Gast
#1766531
@Mason wie gesagt quick and dirty.. aber habe mich auch schon gefragt.. muss den code noch durcharbeiten fuer was der nextptr ist.. evtl. hast du recht. waere cool .. mit dem array resp. hash bin ich auch nicht wirklich gluecklich.. lg roman0177777
Gast
#1766547
@Mason
nicht einmal das.. der tokenizer_init macht das schon :D
hier ohne goton
/*----------------------------------------------------------------------
-----*/
void jump_linenum(int linenum)
{
if(goto_cache[linenum % 255] != 0) {
ptr=goto_cache[linenum % 255];
tokenizer_init(ptr);
printf("cache hit for %d %x %x\n",linenum,ptr,nextptr);
return;
}
tokenizer_init(program_ptr);
while(tokenizer_num() != linenum) {
do {
do {
tokenizer_next();
if (tokenizer_token() == TOKENIZER_ERROR)
{
FATAL_ABORT();
}
} while(tokenizer_token() != TOKENIZER_CR &&
tokenizer_token() != TOKENIZER_ENDOFINPUT);
if(tokenizer_token() == TOKENIZER_CR) {
tokenizer_next();
}
} while(tokenizer_token() != TOKENIZER_NUMBER);
DEBUG_PRINTF("jump_linenum: Found line %d\n", tokenizer_num());
}
if(goto_cache[linenum % 255] == 0) {
goto_cache[linenum %255]=ptr;
printf("cache fill %d %x %x\n",linenum,ptr,nextptr);
}
}
>load 3
load 3
Programm 3 geladen.
>load 2
load 2
Programm 2 geladen.
>run
run
1
cache fill 20 804b192 804b194
2
cache hit for 20 804b192 804b194
3
cache hit for 20 804b192 804b194
4
cache hit for 20 804b192 804b194
5
cache hit for 20 804b192 804b194
6
cache hit for 20 804b192 804b194
7
cache hit for 20 804b192 804b194
8
cache fill 50 804b1bf 804b1c1
cache hit for 50 804b1bf 804b1c1
cache hit for 50 804b1bf 804b1c1
cache hit for 50 804b1bf 804b1c1
cache hit for 50 804b1bf 804b1c1
cache hit for 50 804b1bf 804b1c1
cache hit for 50 804b1bf 804b1c1
30
cache fill 80 804b1ef 804b1f1
31
cache hit for 80 804b1ef 804b1f1
32
cache hit for 80 804b1ef 804b1f1
33
cache hit for 80 804b1ef 804b1f1
34
cache hit for 80 804b1ef 804b1f1
35
cache hit for 80 804b1ef 804b1f1
36
cache hit for 80 804b1ef 804b1f1
37
der nextptr wird schon von tokenizer_init() richtig gesetzt :)
jupii
Gast
#1766559
Achso ja ... Sehs gerad. Der tokenizer_init ruft ja tokenizer_next auf. Von daher funktionierts so.
Gast
#1767932
Wie angedroht mal eine neue Version. Ich hoffe das es für dich ok geht Uwe, wenn ich den Thread "entführe". Wenn nicht mache ich einen neuen Thread dazu auf. Also ich habe mal ein bissl investigative Recherche an meinem Quellcode betrieben und so einige Parametrierbar gemacht, was den Speicherverbrauch (Flash/SRAM/Heap) angeht. Der reine Interpreter (ohne die Erweiterungen der C-Fkt, C-Variablen, C-Arrays) benötigt (mit printf, welches man auch noch wegparametrieren kann) - 4768 Bytes Flash - 132 Byte RAM (da lässt sich einiges per Gosub/For-Stack Tiefe konfigurieren) Mit Erweiterungen (ohne die Funktionen selbst, bzw mit nur einer Dummy-Fkt) benötigt der Interpreter : - 5844 Bytes Flash - 154 Byte RAM (hier wird allerdings der größte Teil für die Parameter-Übergabe auf dem Stack benötigt, das macht den allergrößten Teil aus, daher ist bei entsprechender Konfiguration auch noch einiges ein Einsparpotential drin). Der Interpreter ohne Erweiterungen, aber mit Compiler : - 6182 Byte - 154 Byte RAM (der Buffer für den Compiler ist hier noch nicht mit eingerechnet) Der Interpreter mit Erweiterungen und Compiler : - 7340 Byte - 180 Byte RAM Der Compiler lässt sich folgendermaßen benutzen : Erst das Programm laden mit load (x), dann "compile" eingeben, dabei wird der Byte-Code angezeigt, und mit "runc" lässt sich der Compilierte Code ausführen. Im moment muß wenn man ein anderes Programm Compilieren will, dieses erst mit run ganz normal ausgeführt werden (ist noch ein kleiner Bug, da der runmode beim erneuten compilieren noch auf "RM_COMPILE" steht, und nur im Text-Modus Compiliert werden kann. Es ist noch nicht alles getestet. So funktioniert das Beispielprogramm Nr 2 als Kompilat noch nicht. Aber für einen ersten Eindruck denke ich ist es ausreichend. Ich denke mit dem Ansatz lässt sich einiges anstellen. Zumal man theoretisch ein komplettes Entwicklungssystem (wenn auch recht eingeschränkt, dadurch das das SRAM eben sehr knapp ist für größere Programme) damit realisieren kann. Die nächste Erweiterung ist die von Uwe schon angesprochene, und zwar das das Programm (Text oder Byte-Code) nicht unbedingt im RAM stehen muß. Dazu wird es eine Funktion (bzw mehrere Funktionen) geben mit dem man das aktuelle Zeichen/Byte auslesen kann sowie die aktuelle Position lesen und setzen kann. Damit sollten die Grundvoraussetzungen für flexibles Speicherhandling geschaffen werden. Allerdings wird der Interpreter im Text-Modus dadurch nochmal erheblich langsamer (für jedes Zeichen muß eine Fkt aufgerufen werden, die je nach Quelle z.b. erst noch einen neuen Sektor von der SD-Karte lesen muß, usw). Viel Spaß beim testen. Und ich hoffe Uwe ist nicht böse das ich den Thread etwas entführt hab. Aber das Thema ist ja auch mal sehr interessant.
MoinMoin, TheMason schrieb: > Ich hoffe das es für dich ok geht Uwe, wenn ich den Thread "entführe". > Wenn nicht mache ich einen neuen Thread dazu auf. ich denke es ist sinnvoll, wenn wir hier den Thread aufsplitten, da deine Version in Richtung "Compiler" geht und (fast) nichts mehr mit meiner Ursprungsversion zu tun hat. Trotzdem finde ich deine Geschichte/Ansätze schon interessant und werde einige deiner Ideen überdenken und vielleicht in meiner Interpreter-Version übernehmen. Aber wie schon oben angedeutet, mein Ziel ist es eine resourcenschonende Version (vorallem SRAM) eines Interpreters zu implementieren, um diesen in bestehende Firmware integrieren zu können. Dabei steht z.B. das Laufzeitverhalten für mich nicht an erster Stelle (obwohl in der kommenden Version zwei Verbesserungen dazu eingebaut sind...). Grüße & Danke Uwe
MoinMoin, ... und wie gerade angedroht, auf meiner entsprechenden Projektseite (http://bralug.de/wiki/UBasic-avr) ist eine neue Version des Basic-Interpreters verfügbar. Die Änderungen betreffen hauptsächlich interne Dinge: * print-Befehl: man kann jetzt Ausdrücke auch klammern * print-Befehl abwählbar * gosub jetzt auch in if-then-else im then-Zweig möglich (war ein Bug) * for-next, gosub: Rücksprung nicht via Zeilennummer, sondern Textpointer * zur Laufzeitmessung von Basic-Programmen wurde ein entsprechender Befehl in main.c eingebaut Grüße Uwe
Gast
#1768470
@Uwe >Trotzdem finde ich deine Geschichte/Ansätze schon interessant und werde >einige deiner Ideen überdenken und vielleicht in meiner >Interpreter-Version übernehmen. Mach das. Für Fragen stehe ich gerne zur Verfügung. >Aber wie schon oben angedeutet, mein Ziel ist es eine resourcenschonende >Version (vorallem SRAM) Daher habe ich ja noch nachträglich Compiler-Schalter eingebaut. Um eben ressourcenfressende Routinen wie die Benutzer-Erweiterungen ausschalten zu können. Die Erweiterbarkeit ähnlich wie mit deinem Call-Befehl lässt sich bei mir ja auch implementieren, und somit ressourcensparende Erweiterungen dennoch nutzen. Von daher ist mein Code denke ich im ressourcenverbrauch deinem recht ähnlich wenn man bei mir die USER_EXTENSIONS und den USES_COMPILER Schalter weglässt. >ich denke es ist sinnvoll, wenn wir hier den Thread aufsplitten, Diesem Wunsch werde ich nachkommen :-) Aber ich denke es ist sinnvoll sich über die beiden Threads auszutauschen. Und wenns ok ist kann man sich die Features ja gegenseitig implementieren. Sprich wenn du noch eine Verbesserung oder Bugfixing hast das ich diese in meinem übernehme. Oder eben umgekehrt. Kleiner Tipp noch am Rande zum Thema ressourcensparen ... Ich hab noch nicht in deine aktuelle Version reingeschaut, aber in meiner aktuellen Version hab ich die Tokens als char behandelt (über das define TOKEN) statt als int. Dadurch lassen sich auch nochmal ein paar zig Bytes Flash und ein paar Bytes auf dem Stack einsparen. Zudem wird der Interpreter noch ein kleines Itzken schneller.
Ich habe mal einen Parallel-Thread aufgemacht. Den Link kann ich noch nicht posten da ich den Thread im falschen Unterforum erstellt habe. Ich habe die Admins bereits gebeten den Thread hierhin zu schubsen. Einen Link werde ich (wenns genehm ist) ebenfalls noch posten, da die beiden Threads ja schon eine gewisse Ähnlichkeit haben (zumindest die gemeinsame Basis des Quellcodes von Adam Dunkels Interpreter) und ich denke das sich beide Projekte ergänzen können.
Rene Böllhoff schrieb: > Ich hab noch nicht in deine aktuelle Version reingeschaut, aber in > meiner aktuellen Version hab ich die Tokens als char behandelt (über das > define TOKEN) statt als int. Dadurch lassen sich auch nochmal ein paar > zig Bytes Flash und ein paar Bytes auf dem Stack einsparen. Zudem wird > der Interpreter noch ein kleines Itzken schneller. ... habe ich gerade mal ausprobiert: * weniger Speicher ja, aber nur ein paar Byte * schneller: erstaunlicherweise wird er langsamer... Grüße Uwe
Gast
#1770655
@Uwe, Kannst du bitte ein beispiel mit den for-next, gosub als textpointer hinposten ?? Und noch eine Frage.. wo ist der kleine tcl/tk script ?? hat es nicht in den tar-ball geschaft ?? lg roman
Roman65536 schrieb: > Kannst du bitte ein beispiel mit den for-next, gosub als textpointer > hinposten ?? es handelt sich nur um eine interne Verbesserung: bei next (for-Schleife) bzw. return (gosub) wird nicht mehr das Programm von vorn neu geparst bis die entsprechende Zeilennummer ansteht, sondern es wird sich der entsprechende Zeiger auf die Basic-Programmstelle gemerkt und dann direkt dortin gesprungen. Roman65536 schrieb: > Und noch eine Frage.. wo ist der kleine tcl/tk script ?? hat es nicht in > den tar-ball geschaft ?? in meinem privaten CVS gibt es noch das Script ;-)... Es funktioniert noch nicht, wie es soll und eigentlich habe ich auch keine Lust mehr daran Zeit zu verschwenden. Deshalb habe ich es aus dem Archiv rausgenommen. Es war eigentlich auch nur für komfortableres Testen vorgesehen, bis ich dann auf die Idee kam, meine "Testsuite" via ubasic_tests.h in den FLASH zu brennen... Warum hast du Interesse an dem Tcl/Tk-Ding? Grüße Uwe
Gast
#1771402
@uwe hmm.. interessant.. dh. es koennte auch evtl. ganz ohne zeilen # gehen ?? Wie sieht es aus mit den goto's die waeren doch auch ein Kandidat um sich diese waerend dem parsen zu merken. Ach ja.. in meine Version.. aendere ich jeweils das variablen array auf short, ich bin es mir gewohnt von dem mini basic von Z80, M68k und auch von dem Z8, das man variablen bis 2^15 benuetzen kann :) Warum den tcl/tk ?? hmm.. mit gefaellt dir Idee.. daraus koennte man noch mehr machen. Den zb. ich moechte eines tages ein basic computer bauen so ala ehh.. wie hiess das DDR ding schon wieder (sollte nicht abwertend sein) mit dem meine kleine auch etwas tun kann. zb. einfache Grafiken oder so was wie eine Schildkröte ansteuern oder files laden/saven auf dem host system. Dh. tcl/tk dazu benuetzen um die Daten zu multiplexen, resp. packetizieren da man ja nur eine serielle Schnittstelle hat. lg roman
Gast
#1772054
@uwe,
wenn ich so jetzt den interpreter so anschaue.. so was wie dein
for_stack, resp. gosub stack koennte man auch fuer den goto einbauen ??
das es nicht viel speicher braucht, (ich meine, mein goto cache geht
ganz gut fuer linux, aber fuer mcu.. neee..) wie waere es mit eine goto
liste ?? ala for_stack oder gosub_stack ?? schon mit 8 entries kommt man
ganz schnell weit :)
ubasic.c :
...
...
static struct for_state for_stack[MAX_FOR_STACK_DEPTH];
static int for_stack_ptr;
struct goto_list {
const char *next_line_ptr;
int line;
};
static struct goto_list goto_lists[MAX_GOTO_LIST_DEPTH];
static int goto_list_ptr;
static int variables[MAX_VARNUM];
..
...
..
/*----------------------------------------------------------------------
-----*/
static void
jump_linenum(int linenum)
{
int tmp;
for(tmp=0;tmp < goto_list_ptr;tmp++)
{
if(goto_lists[tmp].line==linenum) {
jump_to_prog_text_pointer(goto_lists[tmp].next_line_ptr);
return;
}
}
tokenizer_init(program_ptr);
while(tokenizer_num() != linenum) {
do {
do {
tokenizer_next();
} while(tokenizer_token() != TOKENIZER_CR &&
tokenizer_token() != TOKENIZER_ENDOFINPUT);
if(tokenizer_token() == TOKENIZER_CR) tokenizer_next();
} while(tokenizer_token() != TOKENIZER_NUMBER);
DEBUG_PRINTF("jump_linenum: Found line %i\r\n", tokenizer_num());
}
if(goto_list_ptr < MAX_GOTO_LIST_DEPTH)
{
goto_lists[goto_list_ptr].next_line_ptr=get_prog_text_pointer();
goto_lists[goto_list_ptr].line=linenum;
goto_list_ptr++;
}
}
lg roman
Hallo Roman, > hmm.. interessant.. dh. es koennte auch evtl. ganz ohne zeilen # gehen > ?? > naja, spätestens beim gosub müsste man sich etwas einfallen lassen! Man müsste dann Prozedur-Namen o.ä. einführen... Und bei goto irgendwelche Namen für Sprung-Marken. Zusätzlich wäre es dann sinnvoll soetwas wie {} für Funktionsblöcke einzuführen... Das würde dann also nach VB aussehen. Ich bin eigentlich ganz zufrieden, wie es jetzt ist. > wenn ich so jetzt den interpreter so anschaue.. so was wie dein > for_stack, resp. gosub stack koennte man auch fuer den goto einbauen ?? > theoretisch hast du recht, hatte ich auch schon überlegt. Aber irgendwie ist der Aufwand dafür zu hoch, denn die Wahrscheinlichkeit, dass in (meinen geplanten) Basic-Programmen sehr viele Loops zu immer der selben Goto-Einsprungzeile gemacht werden, dürfte realtiv gering sein. Da bringt das "Vorübersetzen", wie es TheMason in seiner Version macht, schon mehr. Dabei könnte man gleich den Cache aufbauen und dann nur noch mit diesen Pointern direkt arbeiten. > wie hiess das DDR ding schon wieder > ach, da gab es einige (die ich auch selbst mal hatte...) KC85/x, KC87, Z1013, ... > Dh. tcl/tk dazu benuetzen um die Daten zu multiplexen, resp. > packetizieren da man ja nur eine serielle Schnittstelle hat. > was meinst du damit? Ich hatte das Tcl-Script eigentlich nur dafür vorgesehen, mehrere Basic-Programme auf dem PC zu verwalten und via serieller Schnittstelle zum MC zu senden und die Ausgaben wieder zurück zu bekommen. Eigentlich ist das Script fast fertig, irgendwie scheitere ich allerdings derzeit an der seriellen Übertragung selbst, weil ich wahrscheinlich die Schnittstelle beidseitig auf eine vernünftige Flusskontrolle umschalten müsste. Die derzeitige Version verschluckt u.a. ab und zu ein paar Zeichen.... Kannst du in Tcl programmieren? Grüße Uwe
Gast
#1772481
@uwe hast du die ergaenzungen mit dem goto buffer angeschaut ?? ist nicht viel code (resp. konfigurierbar) ein #if mehr spielt doch keine rolle und bringt schon viel.. Nun ja wenn deine version deinen vorstellungen entspricht ist es ok. Ich dachte eher, dass es eher in die richtung general purpose Basic geht, wo man leider nicht in voraus weiss, wie viele goto's daher kommen. Ich dachte da an ehh.. Papi Computer fuer meine kids. kleine tastatur fuer kleine haende und ein zwei oder vier linien LCD display. Aber wichtig selber gemacht.. mein 8 jaehrige hat schon am geschmack von Loetzinn gerochen :) tcl.. na ja .. wenn der syntax nicht so verwirrend waere .. es gibt bessere hirschen als mich. wie du gesehen hast, ich kann es buchstabieren :D lg roman PS: tja ich komme zwar auch aus der robotron ecke.. oder wie die auch alle geheisen haben.. magyartron, polskitron, baerentron, skodatron ;) dann schon lieber Tron 8-D
MoinMoin, Roman65536 schrieb: > hast du die ergaenzungen mit dem goto buffer angeschaut ?? > ist nicht viel code (resp. konfigurierbar) ein #if mehr spielt doch > keine rolle und bringt schon viel.. > naja, Frage ist ja immer, wieviel goto-Befehle treten in dem Basic-Programm auf und wieviel unterschiedliche Zeilennummern gibt es als Sprungziele.... Ich bin noch am überlegen, ob ich vielleicht doch etwas einbaue und spätestes beim zweiten Ansprung der gleichen Zeilennummer via besagten Textpointer geht, was man via Config zuschalten kann, mal sehen... Nebenbei, es gibt noch einen weiteren Performance-Fresser: an einigen Stellen in ubasic.c wird zum einen das Zeilenende oder, noch extremer eine bestimmte Zeilennummer gesucht. Dabei wird immer tokenizer_next() verwendet, also jedes einzelne Elemente einzeln geparst (ohne eine Aktion auszuführen). Da werde ich demnächst auch nochmal ansetzen... Grüße Uwe
Gast
#1773015
@Uwe >Dabei wird immer tokenizer_next() verwendet, also jedes einzelne Elemente >einzeln geparst (ohne eine Aktion auszuführen). Das ist mir auch schon aufgefallen. Ich denke da ist der größte Ressourcenfresser (was die Zeit angeht). Im Prinzip muß man ja nur nach \n suchen und danach muß dann eine Zahl kommen. Das tokenizer_next () ist wahrscheinlich nur eine bequemlichkeit von Adam gewesen, weils am einfachsten zu implementieren war/ist.
Gast
#1773111
Jungs,
ich habe da was getested, mit dem profiling..
ein einfaches prg:
10 for i = 1 to 100\n\
15 for n = 1 to 100\n\
20 gosub 100\n\
30 next n\n\
30 next i\n\
40 end\n\
100 print n,i \n\
105 return\n\
gprof main
Flat profile:
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls ns/call ns/call name
100.00 0.01 0.01 181242 55.17 55.17 get_next_token
0.00 0.01 0.00 392176 0.00 0.00 tokenizer_token
0.00 0.01 0.00 231645 0.00 0.00 tokenizer_finished
0.00 0.01 0.00 151242 0.00 55.17 tokenizer_next
0.00 0.01 0.00 130827 0.00 0.00 singlechar
0.00 0.01 0.00 121110 0.00 55.17 accept
0.00 0.01 0.00 50411 0.00 0.00 tokenizer_num
0.00 0.01 0.00 40301 0.00 0.00
ubasic_get_variable
0.00 0.01 0.00 40202 0.00 0.00 Check_for_data
0.00 0.01 0.00 40202 0.00 248.74 line_statement
0.00 0.01 0.00 40202 0.00 193.57 statement
0.00 0.01 0.00 40202 0.00 0.00 ubasic_finished
0.00 0.01 0.00 40202 0.00 248.74 ubasic_run
0.00 0.01 0.00 40202 0.00 0.00 usart_is_receive
0.00 0.01 0.00 30201 0.00 0.00
tokenizer_variable_num
0.00 0.01 0.00 30000 0.00 55.17 tokenizer_init
0.00 0.01 0.00 29998 0.00 55.17
jump_to_prog_text_pointer
0.00 0.01 0.00 20202 0.00 55.17 expr
0.00 0.01 0.00 20202 0.00 55.17 factor
0.00 0.01 0.00 20202 0.00 55.17 term
0.00 0.01 0.00 20000 0.00 55.17 varfactor
0.00 0.01 0.00 10201 0.00 0.00
ubasic_set_variable
0.00 0.01 0.00 10102 0.00 0.00
get_prog_text_pointer
0.00 0.01 0.00 10100 0.00 165.52 next_statement
0.00 0.01 0.00 10000 0.00 220.87 gosub_statement
0.00 0.01 0.00 10000 0.00 55.35 jump_linenum
0.00 0.01 0.00 10000 0.00 275.87 print_statement
0.00 0.01 0.00 10000 0.00 110.35 return_statement
0.00 0.01 0.00 101 0.00 386.22 for_statement
0.00 0.01 0.00 18 0.00 0.00 usart_receive_char
0.00 0.01 0.00 3 0.00 0.00 usart_read_line
0.00 0.01 0.00 1 0.00 55.17 end_statement
0.00 0.01 0.00 1 0.00 0.00 load_from_flash
0.00 0.01 0.00 1 0.00 55.17 ubasic_init
% the percentage of the total running time of the
time program used by this function.
cumulative a running sum of the number of seconds accounted
seconds for by this function and those listed above it.
self the number of seconds accounted for by this
seconds function alone. This is the major sort for this
listing.
calls the number of times this function was invoked, if
this function is profiled, else blank.
self the average number of milliseconds spent in this
ms/call function per call, if this function is profiled,
else blank.
total the average number of milliseconds spent in this
ms/call function and its descendents per call, if this
function is profiled, else blank.
name the name of the function. This is the minor sort
for this listing. The index shows the location of
the function in the gprof listing. If the index is
in parenthesis it shows where it would appear in
the gprof listing if it were to be printed.
Call graph (explanation follows)
granularity: each sample hit covers 4 byte(s) for 100.00% of 0.01
seconds
index % time self children called name
<spontaneous>
[1] 100.0 0.00 0.01 main [1]
0.00 0.01 40202/40202 ubasic_run [4]
0.00 0.00 1/1 ubasic_init [20]
0.00 0.00 40202/40202 usart_is_receive [29]
0.00 0.00 40202/40202 ubasic_finished [28]
0.00 0.00 3/3 usart_read_line [34]
0.00 0.00 2/18 usart_receive_char [33]
0.00 0.00 1/1 load_from_flash [35]
-----------------------------------------------
0.00 0.00 30000/181242 tokenizer_init [11]
0.01 0.00 151242/181242 tokenizer_next [5]
[2] 100.0 0.01 0.00 181242 get_next_token [2]
0.00 0.00 130827/130827 singlechar [24]
-----------------------------------------------
0.00 0.01 40202/40202 ubasic_run [4]
[3] 100.0 0.00 0.01 40202 line_statement [3]
0.00 0.01 40202/40202 statement [6]
0.00 0.00 40202/121110 accept [7]
0.00 0.00 40202/50411 tokenizer_num [25]
-----------------------------------------------
0.00 0.01 40202/40202 main [1]
[4] 100.0 0.00 0.01 40202 ubasic_run [4]
0.00 0.01 40202/40202 line_statement [3]
0.00 0.00 40202/231645 tokenizer_finished [23]
-----------------------------------------------
0.00 0.00 31/151242 jump_linenum [18]
0.00 0.00 101/151242 for_statement [19]
0.00 0.00 10000/151242 gosub_statement [9]
0.00 0.00 20000/151242 print_statement [8]
0.00 0.01 121110/151242 accept [7]
[5] 83.4 0.00 0.01 151242 tokenizer_next [5]
0.01 0.00 151242/181242 get_next_token [2]
0.00 0.00 151242/231645 tokenizer_finished [23]
-----------------------------------------------
0.00 0.01 40202/40202 line_statement [3]
[6] 77.8 0.00 0.01 40202 statement [6]
0.00 0.00 10000/10000 print_statement [8]
0.00 0.00 10000/10000 gosub_statement [9]
0.00 0.00 10100/10100 next_statement [10]
0.00 0.00 10000/10000 return_statement [16]
0.00 0.00 101/101 for_statement [19]
0.00 0.00 1/1 end_statement [21]
0.00 0.00 40202/392176 tokenizer_token [22]
-----------------------------------------------
0.00 0.00 1/121110 end_statement [21]
0.00 0.00 202/121110 factor [14]
0.00 0.00 404/121110 for_statement [19]
0.00 0.00 10000/121110 print_statement [8]
0.00 0.00 10000/121110 return_statement [16]
0.00 0.00 20000/121110 varfactor [17]
0.00 0.00 20000/121110 gosub_statement [9]
0.00 0.00 20301/121110 next_statement [10]
0.00 0.00 40202/121110 line_statement [3]
[7] 66.8 0.00 0.01 121110 accept [7]
0.00 0.01 121110/151242 tokenizer_next [5]
0.00 0.00 121110/392176 tokenizer_token [22]
-----------------------------------------------
0.00 0.00 10000/10000 statement [6]
[8] 27.6 0.00 0.00 10000 print_statement [8]
0.00 0.00 20000/151242 tokenizer_next [5]
0.00 0.00 20000/20202 expr [13]
0.00 0.00 10000/121110 accept [7]
0.00 0.00 150000/392176 tokenizer_token [22]
-----------------------------------------------
0.00 0.00 10000/10000 statement [6]
[9] 22.1 0.00 0.00 10000 gosub_statement [9]
0.00 0.00 20000/121110 accept [7]
0.00 0.00 10000/10000 jump_linenum [18]
0.00 0.00 10000/151242 tokenizer_next [5]
0.00 0.00 20000/392176 tokenizer_token [22]
0.00 0.00 10000/50411 tokenizer_num [25]
0.00 0.00 10000/10102 get_prog_text_pointer
[32]
-----------------------------------------------
0.00 0.00 10100/10100 statement [6]
[10] 16.7 0.00 0.00 10100 next_statement [10]
0.00 0.00 20301/121110 accept [7]
0.00 0.00 9999/29998
jump_to_prog_text_pointer [12]
0.00 0.00 20301/40301 ubasic_get_variable
[26]
0.00 0.00 10100/30201 tokenizer_variable_num
[30]
0.00 0.00 10100/10201 ubasic_set_variable
[31]
-----------------------------------------------
0.00 0.00 1/30000 ubasic_init [20]
0.00 0.00 1/30000 jump_linenum [18]
0.00 0.00 29998/30000
jump_to_prog_text_pointer [12]
[11] 16.6 0.00 0.00 30000 tokenizer_init [11]
0.00 0.00 30000/181242 get_next_token [2]
-----------------------------------------------
0.00 0.00 9999/29998 jump_linenum [18]
0.00 0.00 9999/29998 next_statement [10]
0.00 0.00 10000/29998 return_statement [16]
[12] 16.6 0.00 0.00 29998 jump_to_prog_text_pointer
[12]
0.00 0.00 29998/30000 tokenizer_init [11]
-----------------------------------------------
0.00 0.00 202/20202 for_statement [19]
0.00 0.00 20000/20202 print_statement [8]
[13] 11.1 0.00 0.00 20202 expr [13]
0.00 0.00 20202/20202 term [15]
0.00 0.00 20202/392176 tokenizer_token [22]
-----------------------------------------------
0.00 0.00 20202/20202 term [15]
[14] 11.1 0.00 0.00 20202 factor [14]
0.00 0.00 20000/20000 varfactor [17]
0.00 0.00 202/121110 accept [7]
0.00 0.00 20202/392176 tokenizer_token [22]
0.00 0.00 202/50411 tokenizer_num [25]
-----------------------------------------------
0.00 0.00 20202/20202 expr [13]
[15] 11.1 0.00 0.00 20202 term [15]
0.00 0.00 20202/20202 factor [14]
0.00 0.00 20202/392176 tokenizer_token [22]
-----------------------------------------------
0.00 0.00 10000/10000 statement [6]
[16] 11.0 0.00 0.00 10000 return_statement [16]
0.00 0.00 10000/29998
jump_to_prog_text_pointer [12]
0.00 0.00 10000/121110 accept [7]
-----------------------------------------------
0.00 0.00 20000/20000 factor [14]
[17] 11.0 0.00 0.00 20000 varfactor [17]
0.00 0.00 20000/121110 accept [7]
0.00 0.00 20000/30201 tokenizer_variable_num
[30]
0.00 0.00 20000/40301 ubasic_get_variable
[26]
-----------------------------------------------
0.00 0.00 10000/10000 gosub_statement [9]
[18] 5.5 0.00 0.00 10000 jump_linenum [18]
0.00 0.00 9999/29998
jump_to_prog_text_pointer [12]
0.00 0.00 31/151242 tokenizer_next [5]
0.00 0.00 1/30000 tokenizer_init [11]
0.00 0.00 56/392176 tokenizer_token [22]
0.00 0.00 7/50411 tokenizer_num [25]
0.00 0.00 1/10102 get_prog_text_pointer
[32]
-----------------------------------------------
0.00 0.00 101/101 statement [6]
[19] 0.4 0.00 0.00 101 for_statement [19]
0.00 0.00 404/121110 accept [7]
0.00 0.00 202/20202 expr [13]
0.00 0.00 101/151242 tokenizer_next [5]
0.00 0.00 202/392176 tokenizer_token [22]
0.00 0.00 101/30201 tokenizer_variable_num
[30]
0.00 0.00 101/10201 ubasic_set_variable
[31]
0.00 0.00 101/10102 get_prog_text_pointer
[32]
-----------------------------------------------
0.00 0.00 1/1 main [1]
[20] 0.0 0.00 0.00 1 ubasic_init [20]
0.00 0.00 1/30000 tokenizer_init [11]
-----------------------------------------------
0.00 0.00 1/1 statement [6]
[21] 0.0 0.00 0.00 1 end_statement [21]
0.00 0.00 1/121110 accept [7]
-----------------------------------------------
0.00 0.00 56/392176 jump_linenum [18]
0.00 0.00 202/392176 for_statement [19]
0.00 0.00 20000/392176 gosub_statement [9]
0.00 0.00 20202/392176 factor [14]
0.00 0.00 20202/392176 term [15]
0.00 0.00 20202/392176 expr [13]
0.00 0.00 40202/392176 statement [6]
0.00 0.00 121110/392176 accept [7]
0.00 0.00 150000/392176 print_statement [8]
[22] 0.0 0.00 0.00 392176 tokenizer_token [22]
-----------------------------------------------
0.00 0.00 40201/231645 ubasic_finished [28]
0.00 0.00 40202/231645 ubasic_run [4]
0.00 0.00 151242/231645 tokenizer_next [5]
[23] 0.0 0.00 0.00 231645 tokenizer_finished [23]
-----------------------------------------------
0.00 0.00 130827/130827 get_next_token [2]
[24] 0.0 0.00 0.00 130827 singlechar [24]
-----------------------------------------------
0.00 0.00 7/50411 jump_linenum [18]
0.00 0.00 202/50411 factor [14]
0.00 0.00 10000/50411 gosub_statement [9]
0.00 0.00 40202/50411 line_statement [3]
[25] 0.0 0.00 0.00 50411 tokenizer_num [25]
-----------------------------------------------
0.00 0.00 20000/40301 varfactor [17]
0.00 0.00 20301/40301 next_statement [10]
[26] 0.0 0.00 0.00 40301 ubasic_get_variable [26]
-----------------------------------------------
0.00 0.00 40202/40202 usart_is_receive [29]
[27] 0.0 0.00 0.00 40202 Check_for_data [27]
-----------------------------------------------
0.00 0.00 40202/40202 main [1]
[28] 0.0 0.00 0.00 40202 ubasic_finished [28]
0.00 0.00 40201/231645 tokenizer_finished [23]
-----------------------------------------------
0.00 0.00 40202/40202 main [1]
[29] 0.0 0.00 0.00 40202 usart_is_receive [29]
0.00 0.00 40202/40202 Check_for_data [27]
-----------------------------------------------
0.00 0.00 101/30201 for_statement [19]
0.00 0.00 10100/30201 next_statement [10]
0.00 0.00 20000/30201 varfactor [17]
[30] 0.0 0.00 0.00 30201 tokenizer_variable_num [30]
-----------------------------------------------
0.00 0.00 101/10201 for_statement [19]
0.00 0.00 10100/10201 next_statement [10]
[31] 0.0 0.00 0.00 10201 ubasic_set_variable [31]
-----------------------------------------------
0.00 0.00 1/10102 jump_linenum [18]
0.00 0.00 101/10102 for_statement [19]
0.00 0.00 10000/10102 gosub_statement [9]
[32] 0.0 0.00 0.00 10102 get_prog_text_pointer [32]
-----------------------------------------------
0.00 0.00 2/18 main [1]
0.00 0.00 16/18 usart_read_line [34]
[33] 0.0 0.00 0.00 18 usart_receive_char [33]
-----------------------------------------------
0.00 0.00 3/3 main [1]
[34] 0.0 0.00 0.00 3 usart_read_line [34]
0.00 0.00 16/18 usart_receive_char [33]
-----------------------------------------------
0.00 0.00 1/1 main [1]
[35] 0.0 0.00 0.00 1 load_from_flash [35]
-----------------------------------------------
This table describes the call tree of the program, and was sorted by
the total amount of time spent in each function and its children.
Each entry in this table consists of several lines. The line with the
index number at the left hand margin lists the current function.
The lines above it list the functions that called this function,
and the lines below it list the functions this one called.
This line lists:
index A unique number given to each element of the table.
Index numbers are sorted numerically.
The index number is printed next to every function name so
it is easier to look up where the function in the table.
% time This is the percentage of the `total' time that was spent
in this function and its children. Note that due to
different viewpoints, functions excluded by options, etc,
these numbers will NOT add up to 100%.
self This is the total amount of time spent in this function.
children This is the total amount of time propagated into this
function by its children.
called This is the number of times the function was called.
If the function called itself recursively, the number
only includes non-recursive calls, and is followed by
a `+' and the number of recursive calls.
name The name of the current function. The index number is
printed after it. If the function is a member of a
cycle, the cycle number is printed between the
function's name and the index number.
For the function's parents, the fields have the following meanings:
self This is the amount of time that was propagated directly
from the function into this parent.
children This is the amount of time that was propagated from
the function's children into this parent.
called This is the number of times this parent called the
function `/' the total number of times the function
was called. Recursive calls to the function are not
included in the number after the `/'.
name This is the name of the parent. The parent's index
number is printed after it. If the parent is a
member of a cycle, the cycle number is printed between
the name and the index number.
If the parents of the function cannot be determined, the word
`<spontaneous>' is printed in the `name' field, and all the other
fields are blank.
For the function's children, the fields have the following meanings:
self This is the amount of time that was propagated directly
from the child into the function.
children This is the amount of time that was propagated from the
child's children to the function.
called This is the number of times the function called
this child `/' the total number of times the child
was called. Recursive calls by the child are not
listed in the number after the `/'.
name This is the name of the child. The child's index
number is printed after it. If the child is a
member of a cycle, the cycle number is printed
between the name and the index number.
If there are any cycles (circles) in the call graph, there is an
entry for the cycle-as-a-whole. This entry shows who called the
cycle (as parents) and the members of the cycle (as children.)
The `+' recursive calls entry shows the number of function calls that
were internal to the cycle, and the calls entry for each member shows,
for that member, how many times it was called from other members of
the cycle.
Index by function name
[27] Check_for_data [35] load_from_flash [30]
tokenizer_variable_num
[7] accept [10] next_statement (ubasic.c) [28]
ubasic_finished
[21] end_statement (ubasic.c) [8] print_statement (ubasic.c) [26]
ubasic_get_variable
[13] expr [16] return_statement (ubasic.c) [20]
ubasic_init
[14] factor (ubasic.c) [24] singlechar (tokenizer.c) [4]
ubasic_run
[19] for_statement (ubasic.c) [6] statement (ubasic.c) [31]
ubasic_set_variable
[2] get_next_token (tokenizer.c) [15] term (ubasic.c) [29]
usart_is_receive
[32] get_prog_text_pointer [23] tokenizer_finished [34]
usart_read_line
[9] gosub_statement (ubasic.c) [11] tokenizer_init [33]
usart_receive_char
[18] jump_linenum (ubasic.c) [5] tokenizer_next [17] varfactor
(ubasic.c)
[12] jump_to_prog_text_pointer [25] tokenizer_num
[3] line_statement (ubasic.c) [22] tokenizer_token
ups.. sorry fuer das lange posting..
aber warum tokenizer_next bei dem print 150000 aufgeruffen wird .. ist
mir nicht ganz klar...
lg roman( der mit der zahl :)
Gast
#1773117
@roman Mal ne Frage zum Profiler. Welchen verwendest du, und wie funktioniert das. Gibt es das Tool kostenlos (wahrscheinlich nicht, oder ?)
Gast
#1773266
@theMason GNU 8D gcc -p ... umklammert jede funktion so, das man anschliessend runtime verhalten analyzieren kann. Die statistiken werden in einem gmon.out file gespeichert fuer analyze. mit "gprof" kommt die statistik wie oben als output, mit weitern optionen kann man noch vieles mehr :) so zum beispiel output generieren fuer "dot" der das auch noch grafisch anzeigt . viel Spass beim profilieren 8D roman
Gast
#1773361
@roman Danke erstmal für den hinweis :-). Ich nutze zwar eher Microsaft oder WinAVR, aber werds mal im Hinterkopf behalten.
Gast
#1773689
Bei den seattle softboys sollte es eigentlich (vor allem fuer die win32/64 compiler) auch so was geben. den das runtime profiling hat praktisch jeder compiler. zynisch gesagt, vielleicht sollten die mehr davon gebrauch machen :) oder haben sie angst was dabei raus kommt ?? zb. fuer linux kernel gibt es sowas aehnliches.. beim solaris kann man ganz anderst loessen, dort kann man kernel und app gleichzeitig und ohne grosse compiler unterstuetzung loessen (dtrace), so was gibt es glaube ich auch fuer linux.. Das ganze ist jedoch sehr hilfreich bei performance problemen ... lg roman
MoinMoin, auf meiner bekannten Projektseite ist eine aktuelle Version mit folgenden Änderungen verfügbar: * einige interne Variablen wurden auf sinnvollere Datentypen angepasst (Integer ist nicht immer das Allheilmittel...) * die weiter oben erwähnte Geschichte mit dem sinnlosen Parsen von Basic-Quelltexten bei der Suche einer bestimmten Zeilennummer bzw. neuen Zeile wurde verbessert (in tokenizer.c: jump_to_next_linenum() plus darauf aufbauende diverse Änderungen im Rest der Module...) Letzte Änderung hat einige Verbesserungen bei der Abarbeitungsgeschwindigkeit gebracht. In meiner AVR-Testumgebung (siehe Projektseite) verringerte sich z.B. die Laufzeit des Prog0 (siehe ubasic_tests.h) um ca. 4ms. Für das eine Gosub, bei dem die Änderung zum tragen kommt, schon ein enormer Unterschied (15ms --> 11ms). Nebenbei (und für mich auch bedeutend) wurden auch ein paar Byte Programmspeicherplatz eingespart.... Grüße Uwe PS.: bei der Sache mit den zwischengemerkten goto-Sprungzielen muss ich noch ein wenig überdenken, wie man das am sinnvollsten macht....
Gast
#1774016
@uwe PS.: bei der Sache mit den zwischengemerkten goto-Sprungzielen muss ich noch ein wenig überdenken, wie man das am sinnvollsten macht.... Warum ?? Dachte du machst da nichts.. in die richtung ?? lg roman
MoinMoin, Roman65536 schrieb: > PS.: bei der Sache mit den zwischengemerkten goto-Sprungzielen muss ich > noch ein wenig überdenken, wie man das am sinnvollsten macht.... > Warum ?? Dachte du machst da nichts.. in die richtung ?? > och, naja, sind ja derzeit nur Überlegungen, die zum Schluß geführt haben, dass die Sache vielleicht doch ganz interessant sein könnte, wenn es auf Laufzeiten ankommt... Und so aufwändig ist die Sache nun auch wieder nicht, wenn man so etwas ähnliches macht, wie du oben vorgeschlagen hast. Auf jeden Fall werde ich die Sache aber abschaltbar machen. Grüße Uwe
MoinMoin, zuletzt diskutierte Zeilennummern-Cache ist jetzt eingebaut, die entsprechende Version ist auf bekannter Webseite verfügbar. Berauschend sind die Laufzeitverbesserungen nicht unbedingt, wobei das aber im Zusammenhang mit meine gestrigen Änderung zu sehen ist, die an der gleichen Stelle ansetzte. 10 for i = 1 to 100 20 print i 30 gosub 100 40 next i 50 print a 60 end 100 a=a+1 110 return Laufzeiten (auf meiner Testumgebung...) *ohne Cache: 383ms *mit Cache: 371ms Grüße Uwe
Wenn auch "ausser Konkurrenz": Ich habe das mal mit der aktuellen ChipBasic Version nachgestellt (allerdings 10000 Durchläufe) und komme bei abgeschalteter Videoausgabe (16,17 MHz effektiver Takt) auf 87ms. Allerdings halt in ASM und die internen Strukturen sind auf Geschwindigkeit optimiert. Gruß Jörg
Hallo Jörg, > ...und komme > bei abgeschalteter Videoausgabe (16,17 MHz effektiver Takt) auf 87ms. :-), hätte ich jetzt auch nicht anders erwartet, da dein ChipBasic halt in ASM implementiert ist (Hut ab davor! Meine Kenntnisse/Nerven reichen leider dazu nicht mehr. Vor 20 Jahren sah es noch anders aus...). Eine Frage zu deinem Test: hattest du das print in der Schleife drin gelassen und wenn ja, via serieller Schnittstelle? Grüße Uwe
Nein, ich habe das auf den Bildschirm ausgegeben (der allerdings dabei auch scrollen muß), jeden Wert in eine neue Zeile. Seriell könnte ich auch probieren, mit der System-Schnittstelle (2400Bps) braucht allerdings die Ausgabe schon 417ms. Aber ich kann über den zweiten USART des Mega644P ausgeben. Welche USART-Geschwindigkeit benutzt Du? Gruß Jörg
...derzeit 57600 Baud (8n1). Grüße Uwe PS.: einen grossen Vorteil hat mein Interpreter ;-) --> er ist, wenn man das ganze AVR-spezifische-Zeugs ausschaltet, portierbar. Die ersten Versuche/Anpassungen mit dem Original-Codes habe ich unter Linux gemacht bevor ich dann auf einen AVR gegangen bin...
Gast
#1775773
@uwe, die gemessenen werte scheinen mir doch zu hoch!! da scheint etwas nicht mit rechten dingen zu gehen... Probiere es doch ohne den Print und mit -O3 und nicht mit -Os. Wenn es um speed gehen soll dann sicherlich mit allen optimierungen und nicht der size. lg roman
Gast
#1775886
Da wir hier schon in der Leistungsschlacht sind. Hier ein die Daten vom BasicBeetle (www.DieProjektseite.de):
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
Ergibt 210 mS bei einer Baudrate der Ausgabe von 19200.
MoinMoin, Dagobert schrieb: > Da wir hier schon in der Leistungsschlacht sind. Hier ein die Daten vom > BasicBeetle (www.DieProjektseite.de): > da es ja (leider) keinen Quellcode zum Beetle gibt, wenigstens eine Antwort auf die Frage: ASM oder C oder etwas ganz anderes? Grüße Uwe
Gast
#1775918
Der BasicBeetle ist komplett in Assembler geschrieben.
... na dann ist er ja ganz schön langsam... ;-) ...Spaß beseite! Ich denke mal es geht hier nicht um eine Leistungsschlacht der verschiedenen Projekte, zumal jeder eine etwas andere Zielsetzung hat. Ich zumindestens bin mit meinen Ergebnis eigentlich ganz zufrieden. Die Laufzeitmessung habe ich letztens auch nur reingebaut, um zu sehen, welche Tuningmaßnahmen sinnvoll sind und was sie bringen. Interessanter ist fast noch das Profiling, welches letztens Roman hier gepostet hatte. Da sieht man noch viel deutlicher, wo man bei Optimierungen ansetzen sollte. (Wenn ich meinen Interpreter auf einer schnelleren Plattform laufen lassen würde, was ja bei mir geht, würde ich wahrscheinlich supertolle Laufzeiten haben. Aber wie gesagt, eigentlich nicht so bedeutsam!) Grüße Uwe
Gast
#1775968
Ja. Der BasicBeetle ist für größere Projekte vorgesehen. Dies zeigt sich schon im Befehlsumfang und im unterstützten RAM. Für eine kleine Alarmanlage oder eine kleine Lichtsteuerung ist dieser 'oversized'. Da wäre deiner oder der ChipBasic wohl besser geeignet. Aber beim Steuern einer kompletten Modellbahnanlage oder umfangreiche Mess- und Steuerungsaufgaben, da geratet ihr wohl an die Grenzen. Da wird der BB erst langsam Munter ;) Und die Entwicklung bleibt ja nicht stehen. Es sind weitere Versionen geplant, welche z.B. Multiprozessorsteuerungen ermöglichen oder es ermöglichen, eigene Befehle zu erstellen um z.B. sehr zeitkritische Programmsegmente in eigenen Code einzubinden.
Gast
#1778344
Da mein Name irgendwo gefallen ist, moechte ich zu der ganzen Performance sache auch etwas sagen. Man muss den Dunkle interpretter ansehen als ein general Purpose interpreter und wie es Dunkle selbst sagt, just for fun. oder besser gesagt "mal kucken ob es funktioniert?" und im diesem sinne hat es funktioniert. Das konzept des interpretters ist der klassische lexer/parser ansatz. Ich bin ehrlich gesagt nicht sicher ob der lexer und parser wirklich der beste ist. ich denke jeder flex/yacc wuerde wahrscheinlich schnelleren code generieren. Wenn es wirklich um geschwindigkeit geht, musste man den ansatz von Mason verfolgen. Den lexer und parser umbauen. gemaess profiling ist der interpretter in der get_next_token(), das wird sich nach einem umbau nicht aendern, jedoch schneller machen kann man es auf jeden fall. Aus meiner erfahrung, steckt man ein paar stunden in die sache rein, kann man gewaltige sachen aendern. zb. schaut man sich nur die singlechar() routine an .. if else if else etc... ist im programm ein "=" muss der lexer alle if durchlaufen bis er den "=" findet. muss das so sein ?? eingentlich nicht. ok speicher ist immer ein problem. spendet man jedoch eine 128byte grosse tabelle, kann man alle singlechar() abdecken mit einem read. schaut man sich die keyword an ist es das selbe.. eine typische anweissung 10 a=10 wird bei dem lexer als letztes analyzier, dh. alle bedinungen mussen negativ sein, bis er dies als TOKENIZER_VARIABLE erkennt. die Keywords table so wie sie jetzt ist, ist eine zeitverschwendung. einfach zu programmieren (ich wuerde es ja auch so machen) aber sehr langsam. wuerde man zb. die keyword laenge in die tabelle reinnehmen und mit dem vorgegeben keyword vergleichen, wuerde es schon schneller gehen oder ein einsprung fuer die anfangs buchstaben, so das zb. beim print man let,goto,gosub,for gar nicht erst vergleicht. in diesem sinne ... cycle jagt eroeffnet :) roman
Gast
#1778376
@Roman Genau aus diesem Performance-"Flaschenhals", sprich das zuerst erfolgende erkennen der Tokens (was ja je nach token schon ewig viele Takte braucht, bei den ganzen strcmp's, und sonstigen Prüffunktionen und späterem Abarbeiten des erkannten Tokens, ist hier sehr schlecht für die Performance. Bei dem Byte-Code Ansatz wird ja eben genau dieser Teil "vorgekaut" und einfach nur die tokenizer_xxx Funktionen nicht auf Strings, sondern auf den Byte-Code gehetzt. Ich habe zwar noch keine Laufzeitmessungen gemacht, aber es dürfte sehr viel schneller sein. Den eigebauten "Compiler" kann man im Prinzip weglassen, es sei denn man möchte sich mit dem AVR eine Basic-Entwicklungsumgebung schaffen (Compilieren von Basic-Quellcode von der SD-Karte und Speichern des Kompilats auf SD-Karte/Flash/EEProm und Ausführen desselbigen). Nimmt man nur den Byte-Code lässt sich der Interpreter sicherlich auch mit sehr wenig auffwand in Assembler schreiben, womit man dann den ultimativen Geschwindigkeits-Kick bekommt. Ok, es ginge noch ne Stufe darüber, indem man PC-Seitig aus dem Basic-Programm direkt ASM-Code für den AVR erzeugt, aber das wäre ja am Ansatz vorbei. Ich denke das mein Ansatz da eine Grundlage für verbesserte Performance bietet, sofern man aus dem mittlerweile entstandenen ifdef/define-Spaghetti-Monster die Essenz bzw den Kern herausschnippelt. Würde mich jedenfalls freuen wenn jemand an meinem Ansatz mitwerkelt, da ích momentan noch kein Projekt habe in dem ich Basic-Code (egal ob Text oder Byte-Code) ausführen muß. Aber kommt vllt ja noch :-)
Gast
#1778402
ja klar .. nur das eigentlich problem wird ueber umweg weg programmiert.. ich meine verstehe mich nicht falsch ist auch ein ansatz. driftet allerdings schon in richtung JIT compilation ab und geht weg von einem reinem interpreter ansatz. das sind halt zwei verschiedene dinge. Ich verstehe dein ansatzt ist ja auch cool. jedoch Uwe verfollgt einen anderen ansatzt und warum nicht auch den verfolgen ?? Im schlimmsten falle lernen wir nur etwas davon :) Bei deinem ansatz frage ich mich .. warum den nicht gleich byte code von java verwenden und auf der nanovm lauffen lassen ?? aka Beitrag "Java auf AVR" lg roman
Gast
#1778417
War ja auch nicht bös gemeint. Ich denke halt das der reine
Interpreter-Ansatz eben sehr zeitintensiv ist. Es kommt ja auch immer
auf den Anwendungsfall ab. Wenn beispielsweise eine komplexe
Zeitschaltuhr damit programmiert werden soll kommt es sicherlich nicht
auf ms-genaues Timing an. Überhaupt sind zeitkritische(re) Sachen (egal
ob Text oder Byte-Code) sicherlich nicht geeignet um sich mit einem
Basic-Dialekt lösen zu lassen.
Ich werde sicherlich auch hier noch weiter mitlesen und das ein oder
andere in meinem Ansatz übernehmen.
>Im schlimmsten falle lernen wir nur etwas davon :)
Meinte ich weiter oben ja auch :-)
Wollte auch nur nochmal was Werbung für meinen Ansatz machen :-)
Und hoffe das bei mir auch bald eine gewisse Beteiligung einsetzt.
Gast
#1778445
Hey mason, nicht falsch verstehen.. ich denke... ganz persoenlich.. man koennte doch beide mergen... wir koennten doch den von uwe verbessern und neben bei wurde ja auch kompilation bei deinem verbessert werden..
Gast
#1778469
@roman Ich wär der letzte der da Nein sagen würde. Das können wir gerne machen. Ich denke ja ebenfalls das 3 Leute mehr Ideen haben/Fehler entdecken als einer. Von daher, immer gerne.
Gast
#1778557
Also gleiches programm mit Mason's Jit machine..
dh.
10 for i = 1 to 100\n\
15 for n = 1 to 100\n\
20 gosub 100\n\
30 next n\n\
30 next i\n\
40 end\n\
100 print n,i \n\
105 return\n\
Ach ja.. ignoriert die Check_for_data.. ist meine routine fuer Linux.
aber warum so viel mal die tokenizer_variable_num() ??
gprof main
Flat profile:
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls us/call us/call name
33.33 0.01 0.01 40202 0.25 0.25 Check_for_data
33.33 0.02 0.01 30207 0.33 0.33
tokenizer_variable_num
16.67 0.03 0.01 372019 0.01 0.01 tokenizer_token
16.67 0.03 0.01 171292 0.03 0.03 tokenizer_next
0.00 0.03 0.00 251695 0.00 0.00 tokenizer_finished
0.00 0.03 0.00 171293 0.00 0.00 get_next_token
0.00 0.03 0.00 171211 0.00 0.00
get_next_token_from_memory
0.00 0.03 0.00 121211 0.00 0.04 accept
0.00 0.03 0.00 40202 0.00 0.44 execbasicfunc
0.00 0.03 0.00 40202 0.00 0.50 line_statement
0.00 0.03 0.00 40202 0.00 0.45 statement
0.00 0.03 0.00 40202 0.00 0.00 ubasic_finished
0.00 0.03 0.00 40202 0.00 0.50 ubasic_run
0.00 0.03 0.00 40202 0.00 0.25 usart_is_receive
0.00 0.03 0.00 40200 0.00 0.00
ubasic_get_variable
0.00 0.03 0.00 29999 0.00 0.03 jump_linenum
0.00 0.03 0.00 29999 0.00 0.03
tokenizer_jump_to_pos
0.00 0.03 0.00 20202 0.00 0.41 expr
0.00 0.03 0.00 20202 0.00 0.38 factor
0.00 0.03 0.00 20202 0.00 0.40 term
0.00 0.03 0.00 20000 0.00 0.37 varfactor
0.00 0.03 0.00 10201 0.00 0.00
ubasic_set_variable
0.00 0.03 0.00 10100 0.00 0.45 next_statement
0.00 0.03 0.00 10000 0.00 0.11 gosub_statement
0.00 0.03 0.00 10000 0.00 1.12 print_statement
0.00 0.03 0.00 10000 0.00 0.07 return_statement
0.00 0.03 0.00 229 0.00 0.00 tokenizer_num
0.00 0.03 0.00 101 0.00 1.37 for_statement
0.00 0.03 0.00 82 0.00 0.00
get_next_token_from_text
0.00 0.03 0.00 73 0.00 0.00
drop_translated_token
0.00 0.03 0.00 73 0.00 0.00
tokenizer_drop_byte
0.00 0.03 0.00 56 0.00 0.00 singlechar
0.00 0.03 0.00 40 0.00 0.00
tokenizer_get_token_size
0.00 0.03 0.00 28 0.00 0.00 usart_receive_char
0.00 0.03 0.00 13 0.00 0.00
tokenizer_drop_word
0.00 0.03 0.00 4 0.00 0.00 usart_read_line
0.00 0.03 0.00 1 0.00 0.04 end_statement
0.00 0.03 0.00 1 0.00 0.00 load_from_flash
0.00 0.03 0.00 1 0.00 0.00 set_token_pointer
0.00 0.03 0.00 1 0.00 0.00 tokenizer_init
0.00 0.03 0.00 1 0.00 4.38
tokenizer_translate
0.00 0.03 0.00 1 0.00 0.00 ubasic_init
% the percentage of the total running time of the
time program used by this function.
cumulative a running sum of the number of seconds accounted
seconds for by this function and those listed above it.
self the number of seconds accounted for by this
seconds function alone. This is the major sort for this
listing.
calls the number of times this function was invoked, if
this function is profiled, else blank.
self the average number of milliseconds spent in this
ms/call function per call, if this function is profiled,
else blank.
total the average number of milliseconds spent in this
ms/call function and its descendents per call, if this
function is profiled, else blank.
name the name of the function. This is the minor sort
for this listing. The index shows the location of
the function in the gprof listing. If the index is
in parenthesis it shows where it would appear in
the gprof listing if it were to be printed.
Call graph (explanation follows)
granularity: each sample hit covers 4 byte(s) for 33.33% of 0.03 seconds
index % time self children called name
<spontaneous>
[1] 100.0 0.00 0.03 main [1]
0.00 0.02 40202/40202 ubasic_run [3]
0.00 0.01 40202/40202 usart_is_receive [8]
0.00 0.00 1/1 tokenizer_translate
[23]
0.00 0.00 40202/40202 ubasic_finished [28]
0.00 0.00 4/4 usart_read_line [39]
0.00 0.00 3/28 usart_receive_char [37]
0.00 0.00 1/1 load_from_flash [40]
0.00 0.00 1/1 ubasic_init [43]
-----------------------------------------------
0.00 0.02 40202/40202 ubasic_run [3]
[2] 66.7 0.00 0.02 40202 line_statement [2]
0.00 0.02 40202/40202 statement [4]
0.00 0.00 40202/121211 accept [14]
-----------------------------------------------
0.00 0.02 40202/40202 main [1]
[3] 66.7 0.00 0.02 40202 ubasic_run [3]
0.00 0.02 40202/40202 line_statement [2]
0.00 0.00 40202/251695 tokenizer_finished [25]
-----------------------------------------------
0.00 0.02 40202/40202 line_statement [2]
[4] 60.9 0.00 0.02 40202 statement [4]
0.00 0.02 40202/40202 execbasicfunc [5]
0.00 0.00 40202/372019 tokenizer_token [15]
-----------------------------------------------
0.00 0.02 40202/40202 statement [4]
[5] 59.1 0.00 0.02 40202 execbasicfunc [5]
0.00 0.01 10000/10000 print_statement [6]
0.00 0.00 10100/10100 next_statement [17]
0.00 0.00 10000/10000 gosub_statement [18]
0.00 0.00 10000/10000 return_statement [21]
0.00 0.00 101/101 for_statement [22]
0.00 0.00 1/1 end_statement [24]
-----------------------------------------------
0.00 0.01 10000/10000 execbasicfunc [5]
[6] 37.5 0.00 0.01 10000 print_statement [6]
0.00 0.01 20000/20202 expr [10]
0.00 0.00 150000/372019 tokenizer_token [15]
0.00 0.00 20000/171292 tokenizer_next [16]
0.00 0.00 10000/121211 accept [14]
-----------------------------------------------
0.01 0.00 40202/40202 usart_is_receive [8]
[7] 33.3 0.01 0.00 40202 Check_for_data [7]
-----------------------------------------------
0.00 0.01 40202/40202 main [1]
[8] 33.3 0.00 0.01 40202 usart_is_receive [8]
0.01 0.00 40202/40202 Check_for_data [7]
-----------------------------------------------
0.00 0.00 6/30207 tokenizer_translate
[23]
0.00 0.00 101/30207 for_statement [22]
0.00 0.00 10100/30207 next_statement [17]
0.01 0.00 20000/30207 varfactor [13]
[9] 33.3 0.01 0.00 30207 tokenizer_variable_num [9]
-----------------------------------------------
0.00 0.00 202/20202 for_statement [22]
0.00 0.01 20000/20202 print_statement [6]
[10] 27.7 0.00 0.01 20202 expr [10]
0.00 0.01 20202/20202 term [11]
0.00 0.00 20202/372019 tokenizer_token [15]
-----------------------------------------------
0.00 0.01 20202/20202 expr [10]
[11] 26.8 0.00 0.01 20202 term [11]
0.00 0.01 20202/20202 factor [12]
0.00 0.00 20202/372019 tokenizer_token [15]
-----------------------------------------------
0.00 0.01 20202/20202 term [11]
[12] 25.8 0.00 0.01 20202 factor [12]
0.00 0.01 20000/20000 varfactor [13]
0.00 0.00 20202/372019 tokenizer_token [15]
0.00 0.00 202/121211 accept [14]
0.00 0.00 202/229 tokenizer_num [31]
-----------------------------------------------
0.00 0.01 20000/20000 factor [12]
[13] 24.9 0.00 0.01 20000 varfactor [13]
0.01 0.00 20000/30207 tokenizer_variable_num
[9]
0.00 0.00 20000/121211 accept [14]
0.00 0.00 20000/40200 ubasic_get_variable
[29]
-----------------------------------------------
0.00 0.00 1/121211 end_statement [24]
0.00 0.00 202/121211 factor [12]
0.00 0.00 505/121211 for_statement [22]
0.00 0.00 10000/121211 print_statement [6]
0.00 0.00 10000/121211 return_statement [21]
0.00 0.00 20000/121211 varfactor [13]
0.00 0.00 20000/121211 gosub_statement [18]
0.00 0.00 20301/121211 next_statement [17]
0.00 0.00 40202/121211 line_statement [2]
[14] 17.2 0.00 0.01 121211 accept [14]
0.00 0.00 121211/171292 tokenizer_next [16]
0.00 0.00 121211/372019 tokenizer_token [15]
-----------------------------------------------
0.00 0.00 20202/372019 factor [12]
0.00 0.00 20202/372019 term [11]
0.00 0.00 20202/372019 expr [10]
0.00 0.00 40202/372019 statement [4]
0.00 0.00 121211/372019 accept [14]
0.00 0.00 150000/372019 print_statement [6]
[15] 16.7 0.01 0.00 372019 tokenizer_token [15]
-----------------------------------------------
0.00 0.00 82/171292 tokenizer_translate
[23]
0.00 0.00 20000/171292 print_statement [6]
0.00 0.00 29999/171292 tokenizer_jump_to_pos
[20]
0.00 0.00 121211/171292 accept [14]
[16] 16.7 0.01 0.00 171292 tokenizer_next [16]
0.00 0.00 171292/251695 tokenizer_finished [25]
0.00 0.00 171290/171293 get_next_token [26]
-----------------------------------------------
0.00 0.00 10100/10100 execbasicfunc [5]
[17] 15.0 0.00 0.00 10100 next_statement [17]
0.00 0.00 10100/30207 tokenizer_variable_num
[9]
0.00 0.00 20301/121211 accept [14]
0.00 0.00 9999/29999 jump_linenum [19]
0.00 0.00 20200/40200 ubasic_get_variable
[29]
0.00 0.00 10100/10201 ubasic_set_variable
[30]
-----------------------------------------------
0.00 0.00 10000/10000 execbasicfunc [5]
[18] 3.8 0.00 0.00 10000 gosub_statement [18]
0.00 0.00 20000/121211 accept [14]
0.00 0.00 10000/29999 jump_linenum [19]
-----------------------------------------------
0.00 0.00 9999/29999 next_statement [17]
0.00 0.00 10000/29999 gosub_statement [18]
0.00 0.00 10000/29999 return_statement [21]
[19] 2.9 0.00 0.00 29999 jump_linenum [19]
0.00 0.00 29999/29999 tokenizer_jump_to_pos
[20]
-----------------------------------------------
0.00 0.00 29999/29999 jump_linenum [19]
[20] 2.9 0.00 0.00 29999 tokenizer_jump_to_pos [20]
0.00 0.00 29999/171292 tokenizer_next [16]
-----------------------------------------------
0.00 0.00 10000/10000 execbasicfunc [5]
[21] 2.4 0.00 0.00 10000 return_statement [21]
0.00 0.00 10000/121211 accept [14]
0.00 0.00 10000/29999 jump_linenum [19]
-----------------------------------------------
0.00 0.00 101/101 execbasicfunc [5]
[22] 0.5 0.00 0.00 101 for_statement [22]
0.00 0.00 202/20202 expr [10]
0.00 0.00 101/30207 tokenizer_variable_num
[9]
0.00 0.00 505/121211 accept [14]
0.00 0.00 101/10201 ubasic_set_variable
[30]
-----------------------------------------------
0.00 0.00 1/1 main [1]
[23] 0.0 0.00 0.00 1 tokenizer_translate [23]
0.00 0.00 82/171292 tokenizer_next [16]
0.00 0.00 6/30207 tokenizer_variable_num
[9]
0.00 0.00 47/73 tokenizer_drop_byte
[34]
0.00 0.00 40/40
tokenizer_get_token_size [36]
0.00 0.00 27/229 tokenizer_num [31]
0.00 0.00 13/13 tokenizer_drop_word
[38]
0.00 0.00 2/171293 get_next_token [26]
-----------------------------------------------
0.00 0.00 1/1 execbasicfunc [5]
[24] 0.0 0.00 0.00 1 end_statement [24]
0.00 0.00 1/121211 accept [14]
-----------------------------------------------
0.00 0.00 40201/251695 ubasic_finished [28]
0.00 0.00 40202/251695 ubasic_run [3]
0.00 0.00 171292/251695 tokenizer_next [16]
[25] 0.0 0.00 0.00 251695 tokenizer_finished [25]
-----------------------------------------------
0.00 0.00 1/171293 tokenizer_init [42]
0.00 0.00 2/171293 tokenizer_translate
[23]
0.00 0.00 171290/171293 tokenizer_next [16]
[26] 0.0 0.00 0.00 171293 get_next_token [26]
0.00 0.00 171211/171211
get_next_token_from_memory [27]
0.00 0.00 82/82
get_next_token_from_text [32]
-----------------------------------------------
0.00 0.00 171211/171211 get_next_token [26]
[27] 0.0 0.00 0.00 171211 get_next_token_from_memory
[27]
-----------------------------------------------
0.00 0.00 40202/40202 main [1]
[28] 0.0 0.00 0.00 40202 ubasic_finished [28]
0.00 0.00 40201/251695 tokenizer_finished [25]
-----------------------------------------------
0.00 0.00 20000/40200 varfactor [13]
0.00 0.00 20200/40200 next_statement [17]
[29] 0.0 0.00 0.00 40200 ubasic_get_variable [29]
-----------------------------------------------
0.00 0.00 101/10201 for_statement [22]
0.00 0.00 10100/10201 next_statement [17]
[30] 0.0 0.00 0.00 10201 ubasic_set_variable [30]
-----------------------------------------------
0.00 0.00 27/229 tokenizer_translate
[23]
0.00 0.00 202/229 factor [12]
[31] 0.0 0.00 0.00 229 tokenizer_num [31]
-----------------------------------------------
0.00 0.00 82/82 get_next_token [26]
[32] 0.0 0.00 0.00 82 get_next_token_from_text
[32]
0.00 0.00 56/56 singlechar [35]
-----------------------------------------------
0.00 0.00 73/73 tokenizer_drop_byte
[34]
[33] 0.0 0.00 0.00 73 drop_translated_token [33]
-----------------------------------------------
0.00 0.00 26/73 tokenizer_drop_word
[38]
0.00 0.00 47/73 tokenizer_translate
[23]
[34] 0.0 0.00 0.00 73 tokenizer_drop_byte [34]
0.00 0.00 73/73 drop_translated_token
[33]
-----------------------------------------------
0.00 0.00 56/56
get_next_token_from_text [32]
[35] 0.0 0.00 0.00 56 singlechar [35]
-----------------------------------------------
0.00 0.00 40/40 tokenizer_translate
[23]
[36] 0.0 0.00 0.00 40 tokenizer_get_token_size
[36]
-----------------------------------------------
0.00 0.00 3/28 main [1]
0.00 0.00 25/28 usart_read_line [39]
[37] 0.0 0.00 0.00 28 usart_receive_char [37]
-----------------------------------------------
0.00 0.00 13/13 tokenizer_translate
[23]
[38] 0.0 0.00 0.00 13 tokenizer_drop_word [38]
0.00 0.00 26/73 tokenizer_drop_byte
[34]
-----------------------------------------------
0.00 0.00 4/4 main [1]
[39] 0.0 0.00 0.00 4 usart_read_line [39]
0.00 0.00 25/28 usart_receive_char [37]
-----------------------------------------------
0.00 0.00 1/1 main [1]
[40] 0.0 0.00 0.00 1 load_from_flash [40]
-----------------------------------------------
0.00 0.00 1/1 tokenizer_init [42]
[41] 0.0 0.00 0.00 1 set_token_pointer [41]
-----------------------------------------------
0.00 0.00 1/1 ubasic_init [43]
[42] 0.0 0.00 0.00 1 tokenizer_init [42]
0.00 0.00 1/1 set_token_pointer [41]
0.00 0.00 1/171293 get_next_token [26]
-----------------------------------------------
0.00 0.00 1/1 main [1]
[43] 0.0 0.00 0.00 1 ubasic_init [43]
0.00 0.00 1/1 tokenizer_init [42]
-----------------------------------------------
This table describes the call tree of the program, and was sorted by
the total amount of time spent in each function and its children.
Each entry in this table consists of several lines. The line with the
index number at the left hand margin lists the current function.
The lines above it list the functions that called this function,
and the lines below it list the functions this one called.
This line lists:
index A unique number given to each element of the table.
Index numbers are sorted numerically.
The index number is printed next to every function name so
it is easier to look up where the function in the table.
% time This is the percentage of the `total' time that was spent
in this function and its children. Note that due to
different viewpoints, functions excluded by options, etc,
these numbers will NOT add up to 100%.
self This is the total amount of time spent in this function.
children This is the total amount of time propagated into this
function by its children.
called This is the number of times the function was called.
If the function called itself recursively, the number
only includes non-recursive calls, and is followed by
a `+' and the number of recursive calls.
name The name of the current function. The index number is
printed after it. If the function is a member of a
cycle, the cycle number is printed between the
function's name and the index number.
For the function's parents, the fields have the following meanings:
self This is the amount of time that was propagated directly
from the function into this parent.
children This is the amount of time that was propagated from
the function's children into this parent.
called This is the number of times this parent called the
function `/' the total number of times the function
was called. Recursive calls to the function are not
included in the number after the `/'.
name This is the name of the parent. The parent's index
number is printed after it. If the parent is a
member of a cycle, the cycle number is printed between
the name and the index number.
If the parents of the function cannot be determined, the word
`<spontaneous>' is printed in the `name' field, and all the other
fields are blank.
For the function's children, the fields have the following meanings:
self This is the amount of time that was propagated directly
from the child into the function.
children This is the amount of time that was propagated from the
child's children to the function.
called This is the number of times the function called
this child `/' the total number of times the child
was called. Recursive calls by the child are not
listed in the number after the `/'.
name This is the name of the child. The child's index
number is printed after it. If the child is a
member of a cycle, the cycle number is printed
between the name and the index number.
If there are any cycles (circles) in the call graph, there is an
entry for the cycle-as-a-whole. This entry shows who called the
cycle (as parents) and the members of the cycle (as children.)
The `+' recursive calls entry shows the number of function calls that
were internal to the cycle, and the calls entry for each member shows,
for that member, how many times it was called from other members of
the cycle.
Index by function name
[7] Check_for_data [40] load_from_flash [16]
tokenizer_next
[14] accept [17] next_statement [31]
tokenizer_num
[33] drop_translated_token [6] print_statement [15]
tokenizer_token
[24] end_statement [21] return_statement [23]
tokenizer_translate
[5] execbasicfunc [41] set_token_pointer [9]
tokenizer_variable_num
[10] expr [35] singlechar [28]
ubasic_finished
[12] factor [4] statement [29]
ubasic_get_variable
[22] for_statement [11] term [43]
ubasic_init
[26] get_next_token [34] tokenizer_drop_byte [3]
ubasic_run
[27] get_next_token_from_memory [38] tokenizer_drop_word [30]
ubasic_set_variable
[32] get_next_token_from_text [25] tokenizer_finished [8]
usart_is_receive
[18] gosub_statement [36] tokenizer_get_token_size [39]
usart_read_line
[19] jump_linenum [42] tokenizer_init [37]
usart_receive_char
[2] line_statement [20] tokenizer_jump_to_pos [13] varfactor
romanp@dhcp-ezrh04-51-123:~/Desktop/ubasic-0.2a$
lg roman
Gast
#1778567
man koennte doch die .. tokenizer_variable_num als makro einbinden.. den.. fuer die paar instruktionen lohnt sich doch ein jsr gar nicht... lg roman
Gast
#1778842
@an alle basic fans..
im anhang findet ihr ueberarbeitete version von dem tokenizer..
was ist anderst.. die structure keyword enthaelt nun keyword laenge..
struct keyword_token {
#if USE_PROGMEM
// um via strxxx_P zugreifen zu koennen, muss eine feste Laenge
vorgegeben
// werden
char keyword[MAX_KEYWORD_LEN+1];
#else
char *keyword;
#endif
char len;
int token;
};
dem entsprechend muss man die keyword ergaenzen :
{"let",3, TOKENIZER_LET},
nun.. die routine mystrlen() gibt die laenge an von dem ptr string, dh.
wie lange ist der naechst isaplha() string. wenn diese kleiner als 2
ist, kann man die keywords ueberspringen, den die sind alle 2+ .
sollte der string 2+ sein, geht man nicht keyword per keyword, sondern
vergleicht zu erst die laenge der strings (was fuer eine bloede
bezeichnung :) erst wenn diese gleich ist, werden die keywords zeichen
per zeichen verglichen.
als naechstes gehe die singlechar() an :)
lg roman
Gast
#1778911
so jetzt mit dem FAST singlechar()... auch noch.. beim compilieren -DFAST angeben.. bei den ersten test's sollten wir naeher den 80ms kommen :) lg roman
Gast
#1778948
@roman Hört sich mal sinnig an, selbst wenns sich im ersten Moment etwas Konfizius liest :) Aber gute lösung mit dem keyword Vergleich und der Single-Char Optimierung. Gefällt mir gut. Werde ich wohl bei mir so übernehmen. Evtl. kann ich auch noch was zur "Abstraktion" der "Zeichenquelle" beitragen. Habe bei mir mal eine generische Funktion (bzw 4 generische Funkionen) eingebaut mit denen der Text von einer beliebigen Quelle kommen kann. Dazu braucht man 4 Funktionen : Aktuelles Zeichen, Nächstes Zeichen, Zeichenposition holen, Zeichenposition setzen. Mit den 4 Funktionen lässt sich der tokenizer von überall aus füttern :-) Der Quellcode folgt noch.
Gast
#1779009
@mason, hmmm... finde ich gut, aber man muss bedenken, so lange das ganz im arbeitspeicher vorhanden ist, kann man was schnell tun. abstrahiert man dies. muss man fuer alles wieder eine routine aufruffen, kann mir nicht vorstellen das dies schnell sein kann. ausser man tut dies ala getdent von linux. man gibt dem system call entsprechenden speicher und der kernel fuellt dies bis zum rand. single char finde ich nicht sooo gut. Ehrlich gesagt verstehe ich auch nicht ganz die abstraktion .. warum den ?? das man es von quelle a oder quelle b lesen kann ?? so von eeprom oder sd karte ?? geht es um das ?? lg roman PS: danke fuer den lob, was das deutsch angeht .. na ja .. nicht meine staerke :) da waere schwizerduetsch scho besser.. hihihi..
@Roman >so lange das ganz im arbeitspeicher vorhanden ist, kann man was schnell tun Das ist richtig, aber der Hauptspeicher (SRAM) ist ja nunmal sehr begrenzt. >kann mir nicht vorstellen das dies schnell sein kann Schneller wirds dadurch leider nicht, aber ich denke je nach Größe des Basic-Programms kommt man schneller an die Grenzen des SRAMs als einem lieb ist. >Ehrlich gesagt verstehe ich auch nicht ganz die abstraktion .. warum den?? >das man es von quelle a oder quelle b lesen kann ?? so von eeprom oder >sd karte ?? geht es um das ?? Genau darum geht es. Das man beispielsweise ein Basic-Programm "direkt" d.h. über den Sektor-Puffer, den man ohnehin benötigt, ausführt. Wenn ein Basic-Programm beispielsweise 1kByte hat (1kByte ASCII bekommt man ja recht schnell zusammen) dann würde es schon nicht mehr in einen ATMega16 reinpassen. Man kann zwar mit Tricks und Kniffen vllt den "erweiterten" Speicher durch umkopieren "simulieren", aber selbst dazu muß man an der Stelle ansetzen an der es um die zeichenweise Verarbeitung geht (wenn man beispielsweise auf einer 512-Byte Grenze ist und den nächsten Sektor, die nächste Page oder wie auch immer holen muß) und dort dann umkopiert. Mit den Funktionszeigern dauert es zwar länger, aber dafür lässt sich dann die "befütterung" auf nur jede erdenkliche Art lösen, bzw es kann jedes Medium (egal ob SD-Karte/HDD/ext. SRAM/EEProm/Flash/DataFlash oder sonstwas) an den Basic-Interpreter angekorkt werden. Außerdem lässt sich die Quelle während des Betriebs umschalten. Somit könnte man beispielsweise Basic-Programme "nachladen" bzw als getrennte Einheit vom Programmfluß ausführen und nachher wieder an der alten Stelle zurrückkehren.
Gast
#1779110
@Mason, hast du dir schon mal die stdio von Unix/linux angeschaut ?? hier eine idee dazu.. blockweise wuerde es gehen.. man liest die 512 von der sd oder von wo auch immer. der rest passiert in einem buffer per macros. wo bei ich ein grosses problem bei einem reinem interpretter sehe (bei deiner version auch) was mach tun mit den spruengen ?? da kann man den ptr nicht mehr speichern so wie es Uwe tut. @ alle koennte jemand irgend ein basic programm probieren was die aenderungen ueberhaupt bringen ?? mit allen speed optionen ?? ich kommen an avr heute nicht ran, muss noch was erledigen. wuerde mich aber interessieren.. lg roman
>wo bei ich ein grosses problem bei einem reinem interpretter sehe (bei >deiner version auch) was mach tun mit den spruengen ?? da kann man den >ptr nicht mehr speichern so wie es Uwe tut. Genau dafür brauche ich GETPOS und SETPOS. Damit lässt sich innerhalb meines Streams (Programm) der Zeiger setzen und lesen wie ich will. Der Zeigertyp ist per define definierbar. Damit ist es egal ob man sich beispielsweise mit einem Index auf ein Array bezieht, einen Zeiger innerhalb des SRAMs, einen virtuellen 32-Bit Zeiger auf einen Speicherbereich in einem externen großen RAM, einen 32-Bit Zeiger innerhalb der SD-Karte, einen 16-Bit Index bezogen auf den Anfang einer Text-Datei auf der SD-Karte. Mit dieser Abstraktion (4 separate Funktionen plus einen "Zeigertyp") lässt sich die Quelle für das Basic-Programm an nahezu jedwede Umgebung anpassen. Das ganze ist auch für die Simulation sehr nützlich. Man ist damit wirklich sehr flexibel. Allerdings nimmt man dadurch auch Laufzeit einbußen in Kauf. Aber ich denke in Anbetracht der Möglichkeiten (und der ansonst vorhandenen begrenzung des Basic-Programms auf den Bruchteil des SRAMs) die sich ergeben kann man (bzw ich) das verschmerzen. Auch wenn es um den Byte-Code geht lässt sich diese Abstraktion (fast) direkt umsetzen bzw direkt verwenden, dadurch das der Tokenizer transparent umschaltbar ist und von der Quelle der Daten nichts weiß bis auf eben die 4 Funktionen die der Tokenizer nutzt.
Gast
#1779189
@mason, hmmm... irgendwie macht es schon sinn. mal schauen was der uwe sagt dazu. Aber eine anmerkung, kannst du compiler/interpreter umbauen, das die sachen von oben mit singlechar und keyword einfliessen ?? wuerde mich wundernehmen was die aenderungen bringen. ich denke zu erst sollte man das ding as fast as possible umbauen, ab dann ist es nicht mehr schwer den rest einbauen. Was man auf alle faelle vermeiden sollte, etwas "jetzt" einbauen und spaeter merken dass man sich dadurch performance eingebust hat.
Roman65536 schrieb: > mal schauen was der uwe sagt dazu. derzeit nicht viel, weil ich mich derzeit auf dem Sprung in den Urlaub bin und dazu noch ein paar Dinge erledigen muss... Die Geschichte mit dem wahlfreien Medium geistert derzeit auch in meinem Kopf rum, wobei meine Gedanken in die Richtung gehen: * eine ganze Textzeile einlesen und parsen, oder * immer soviel lesen, wie das längste Schlüsselwort sein kann usw. Set- und Getpos ist schon klar und war auch mein erster Gedanke, aber irgendwie muss ja mit den Schlüsselwörtern in Gänze verglichen werden... Grüße Uwe
@Roman Ich glaube da ist ein kleiner gedanklicher Fehler in deinem Source. Und zwar holst du mit mystrlen via isaplha die Länge des zu suchenenden Schlüsselwortes. Das Problem ist nur wenn du auf z.b. >=, >> <> usw stößt, da isalpha hier nicht funktioniert und die Längen-Prüfung des Schlüsselwortes fehlschlägt. Mit isalpha lassen sich wirklich nur Wörter eingrenzen. Bei Symbolen wie <, >, = usw müsste man singlechar aufbohren das diese Fkt einem die 2-Zeichen Tokens zurückliefert. Aber damit wär es eben kein singlechar mehr ;-) @Uwe >* eine ganze Textzeile einlesen und parsen, oder >* immer soviel lesen, wie das längste Schlüsselwort sein kann usw. Ich denke das man mit Funktionszeiger für einzelne Zeichen inkl Get/SetPos schon sehr weit kommt. Zumal man beim zeilenweisen/blockweisen Verarbeiten ohnehin eine gesonderte Lesefkt benötigt die zu gegebener Zeit (sprich beim lesen des CRs) angestoßen werden muß. Die Sache mit dem Vergleichen der Schlüsselwörter in ihrer Gänze wäre evtl noch die Möglichkeit eine weiter Funktion des "Zeichengerätes" zu implementieren. Egal ob diese im ersten Schritt nur zeichenweise vergleicht, oder aber über soviel eigenen Buffer verfügt diesen Vergleich im lokalen SRAM vollführen zu können (bei zeilenweiser Verarbeitung z.b.). Also das die Implementierung letztendendes Sache des Mediums ist. Aber so gesehen hätte man ja auch noch per Compilerschalter die Möglichkeit bestimmte Optimierungen zu setzen Erstmal schönen Urlaub Uwe
Gast
#1779724
Kann sein.. ich dachte isaplha kommt mit den sonderzeichen klar :( na ja .. wieder an die taste :) uwe schoenen urlaub 8-D lg roman
Gast
#1779809
Dann mach ich mal das 100. Posting voll :-) @roman Ist auch nur ne Vermutung das isaplha damit nicht zurechtkommt. War auch nur ein Bauchgefühl das beispielsweise ? oder ! keine Buchstaben sind :)) Kannst du mir mal deine EMail zukommen lassen ? @roman und uwe Also ich denke vllt sollten wir wirklich mal zu dritt an dem Basic arbeiten. Man kann ja eine Gemeinschaftsversion machen, und jeder für sich seine bevorzugte Version. Jedenfalls wäre ich dafür wenn wir ein SVN-Repository beantragen in dem die einzelnen Erkenntnisse und Ideen zusammenfließen. Vllt wäre ja noch ein Unterordner für jede "persönliche" Basic Version sinnig. Was haltet ihr von der Idee ?
Gast
#1779813
@mason, isalpha gilt nur fuer buchstaben, keine sonderzeichen. koennte evtl. sein das deine avr-libc ein bug hat ?? oder generell ein problem hat ?? Diese sollte nur bei buchstaben zurueck geben. lg roman
Gast
#1779849
@roman
>War auch nur ein Bauchgefühl das beispielsweise ? oder ! keine Buchstaben >sind
:))
Das war nur ein Scherz :-)
isalpha darf eben nur bei Buchstaben true zurückliefern.
Desalb hab ich ja auch den vermeintlichen Fehler in deine Source
gefunden.
Aber ich denke das man trotzdem deinen Ansatz mit der vorgeschalteten
Längenprüfung gut verwenden kann, da es den Vergleich deutlich
beschleunigt.
Gast
#1779870
meine email hast in deine uc mailbox :) ich haette da noch eine perversere idee .. jedoch .. sollte mal wissen ob es den bis jetzt was gebracht hat ?? lg roman
Gast
#1779910
Jo danke. Meld mich später mal per EMail. >sollte mal wissen ob es den bis jetzt was gebracht hat ?? Ich denke es wäre sinnig mal mehrere kleine Basic-Programme zu schreiben mit denen man unterschiedliche Szenarien testen kann. Das man immer dieselben Bedingungen hat und somit von Version zu Version vergleichen kann. Dazu muß man sich dann aber auf den gemeinsamen Funktionsumfang beschränken. Sprich kein Call von Uwe und keine Extensions von mir. Aber nochmal zu der SVN-Idee ... Wär das was ? Was hast du denn noch für Schweinereien bezüglich des Basics im Kopf ?
MoinMoin, TheMason schrieb: > Aber nochmal zu der SVN-Idee ... Wär das was ? von der Sache nicht schlecht, aber welche Version willst du als Ausgangspunkt nehmen? Grüße Uwe
Gast
#1780171
@Uwe Im Zweifelsfalle die Ursprungsversion von Adam :)) Was mir da vorschwebt wäre als erste Version ein Mix aus deiner und meiner Version. Von dir die bisherigen Optimierungen und Fehlerkorrekturen. Von mir die Sache mit der einfachen Konfiguration per basic_cfg.h. Die Präprozessor-Befehle brauchen ja keine Laufzeitressourcen bzw Vergrößern den Code ja nicht, sondern dienen lediglich der einfacheren Übersicht bzw Erweiterbarkeit. Ich schnapp mir heute Abend mal deinen Code und modifiziere diesen, sodass der Code an sich derselbe bleibt, aber eben die Wartbarkeit was die Schlüsselworte angeht erhöht wird. Wär das ein Anfang für eine Version ? Im anschluß würde ich dann meine Extensions und die Compiler-Fähigkeit hinzunehmen. Aber da kann man sich ja noch überlegen ob man das in getrennten Verzeichnissen weiterführt.
Gast
#1780186
@mason, fiha, da will's eine wissen.. SVN na ja .. aber wo ?? hast du gleich einen server ?? oder gibt es hier einen ?? Was mich zu erst interessieren wuerde, was den die einzelnen verbesserungen ueber bringen ?? an einfachen programmen.. bevor man was ueberhaupt tut. resp. den code aufraeumt. eine erweiterung resp. wenn man die laenge des keyword schon weiss, warum dann nicht "nur" die keywort tabelle durchsuchen, mit der entsprechenden laenge der keyworter ?? how about it ??
Gast
#1780190
Roman65536 schrieb: > SVN na ja .. aber wo ?? hast du gleich einen server ?? oder gibt es hier > einen ?? http://www.mikrocontroller.net/articles/Hilfe:SVN
Gast
#1780230
>Was mich zu erst interessieren wuerde, was den die einzelnen >verbesserungen ueber bringen ?? Aber dazu muss man ja immer diesselben Test-Bedingungen haben. Sprich gleiche Plattform (sollte ja kein Thema sein) und gleiches Basic-Programm (ist ja auch kein Thema). Ich denke das wenn man sich ein paar Test-Programme überlegt lassen sich auch die Versionen untereinander immer gleich vergleichen. Also das man immer gleiche Bedingungen hat. Ich werd den Andreas mal fragen ob er uns ein SVN-Repository einrichtet.
Gast
#1781209
Also... so wie es aussieht.. bringen die verbesserungen mit der keyword laenge extrem viel... getestet wuerde dieses kleine programm: const char prog1[] PROGMEM= "\ 5 for x = 1 to 100\n\ 10 for i = 1 to 100\n\ 15 for n = 1 to 100\n\ 20 gosub 100\n\ 30 next n\n\ 30 next i\n\ 35 next x\n\ 40 end\n\ 100 print n,i \n\ 105 return\n\ "; dh. 100*100*100 den print raus zu geben.. oder 10'000'000 mal. Das ganze wuerde auf Linux probiert.. hier die resultate : time ./main <cmd.txt >/dev/null real 0m1.764s user 0m1.764s sys 0m0.000s time ./main <cmd.txt >/dev/null real 0m4.686s user 0m4.652s sys 0m0.004s Die erste version ist mit den verbesserungen im bereich von single char und keyword laenge vergleich.. die zweite original .. Ich denke die laufzeiten sprechen fuer sich :D lg roman
@Roman Also die Optimierung ist doch mal ne Erwähnung Wert :-)) Ich werd das mal übernehmen und in mein unverständliches Kauderwelsch an Präprozessor-Anweisungen mit integrieren :)) Was mir zum Thema Speed-Optimierung noch eingefallen ist. Für die Schlüsselwortsuche würde sich auch der Hash-Algorithmus (in Verbindung mit der Länge des Keywords) anbieten. Ich stelle mir das so vor : Sobald ein Token mit einem Buchstaben anfängt wird die Hash von diesem und allen folgenden Alpha-Zeichen gebildet. Gleichzeitig werden die Anzahl der Buchstaben mitgezählt. In einer Tabelle stehen dann die Hashwerte mit den entsprechenden Längen und den zu repräsentierenden Token-Nummern. Und gesucht wird mit n/2. Also sprich die Hash-Tabelle ist aufsteigend sortiert. Man fängt in der Mitte an und schaut ob der Wert größer oder kleiner ist (oder gleich). Ist er kleiner mache ich dasselbe Spiel vom Tabellenanfang bis zur Mitte (also mitte des Bereichs bestimmen, nachgucken ob größer, kleiner). Ist er größer so betrachte ich von der Tabellenmitte an bis Tabellenende. Ob das ganze schneller wird müsste man Testen. Ich werd auch mal mit dem AVR-Simulator Test-Programme durchrödeln und schauen was dabei rauskommt.
Gast
#1781386
Rene !! You got a mail.. hash halte ich in diesem speziellen fall als ungeeignet. den... Stellt dir vor.. ich erweitere "mein" interpreter mit neuen cmd. und dann stimmt der hash nicht mehr.. was dann ?? siehe email.. die idee ist vieeeeelllll besser :) lg roman
@Roman hab dir gerade "kurz" geantwortet :-) Die Idee mit der Hash-Table denke ich ist auch nicht so gut bzw dürfte nicht soviel bringen. Aber ich hab noch 2-3 andere Ideen. Eine davon wäre eine Ergänzung zu deiner Längen-Tabellen-Idee.
Gast
#1781493
so weiter resultate Renes(mason) version ohne und mit compiler... gleiches programm wie oben: time ./main <cmd.txt >/dev/null real 0m27.257s user 0m27.110s sys 0m0.000s VORSICHT dies ist noch nicht compilierte code.. und jetzt ... Meine damen und Herren, der Hammer !! time ./main <cmd.txt >/dev/null real 0m0.775s user 0m0.772s sys 0m0.000s Mein Respekt !!! das ist 35 mal schneller als original !! oder 2.27 mal schneller als die oben erwaehnte beste resultat.
Gast
#1781505
Hier noch schnell ein vergleich mit C compilierte version ...
#include <stdio.h>
main()
{
short a,b,c;
for(a=0;a<100;a++)
for(b=0;b<100;b++)
for(c=0;c<100;c++)
printf("%d %d\n",b,c);
}
gcc -O6 -o test test.c
time ./test >/dev/null
real 0m0.262s
user 0m0.236s
sys 0m0.000s
fyi.
@Roman Erstmal Danke für deine unermüdliche Profiling-Arbeit :-)) Das das so viel schneller ist hätte ich nicht gedacht. Aber freut mich und bestärkt mich darin weiter mit dem "parallel" Ansatz (sprich Basic Quell-Code zu interpretieren, bzw vorinterpretierten Code ausführen) zu machen, bzw weitere Schweinereien in dem Basic-Interpreter unterzubringen.
Gast
#1781538
@rene das ist doch noch kein profiling die execution zeit zu messen ;) Du solltest dir mal Linux entwicklungs moeglichkeiten anschauen ... auch wenn nur in der virtualBox oder so was. lg roman
@roman Bin leider mehrmals gescheitert effektiv mit Linux zu arbeiten. Drum bleib ich bei Windoof. Aber hast schon recht. Ne Zeitmessung ist noch kein richtiges Profiling. Selbst wenn man mal auf die schnelle schauen kann ob Optimierungen den erhofften erfolg bringen.
Gast
#1781829
So lieber Rene...
ich nehmen mal dein code unter mein performance adler auge...
ein beispiel .. relation()..
int relation(void)
{
int r1, r2;
char op;
r1 = expr();
op = tokenizer_token();
;
while (
# 1 "basic_cfg.h" 1
# 104 "basic_cfg.h"
(op == TOKENIZER_EQ) ||
(op == TOKENIZER_GT) ||
(op == TOKENIZER_LT) ||
(op == TOKENIZER_NE) ||
(op == TOKENIZER_GE) ||
(op == TOKENIZER_LE) ||
# 333 "ubasic.c" 2
(0)) {
tokenizer_next();
r2 = expr();
;
switch (op)
{
# 1 "basic_cfg.h" 1
# 104 "basic_cfg.h"
case TOKENIZER_EQ : r1 = basic_rel_eq (r1, r2); break;
case TOKENIZER_GT : r1 = basic_rel_gt (r1, r2); break;
case TOKENIZER_LT : r1 = basic_rel_lt (r1, r2); break;
case TOKENIZER_NE : r1 = basic_rel_ne (r1, r2); break;
case TOKENIZER_GE : r1 = basic_rel_ge (r1, r2); break;
case TOKENIZER_LE : r1 = basic_rel_le (r1, r2); break;
# 346 "ubasic.c" 2
}
op = tokenizer_token();
}
return r1;
}
hier der compiler generierte code mit avr-gcc -O3 .. dh. funktionen
werden auch inlined..
.global relation
.type relation, @function
relation:
push r14
push r15
push r17
push r28
push r29
/* prologue: function */
/* frame size = 0 */
rcall expr
mov r28,r24
mov r29,r25
rcall tokenizer_token
mov r17,r24
rjmp .L208
.L227:
cpi r17,lo8(22)
breq .L217
cpi r17,lo8(23)
breq .L218
cpi r17,lo8(24)
brne .L226
rcall tokenizer_next
rcall expr
ldi r18,lo8(0)
ldi r19,hi8(0)
cp r24,r28
cpc r25,r29
brlt .L215
.L225:
ldi r18,lo8(1)
ldi r19,hi8(1)
.L215:
mov r28,r18
mov r29,r19
rcall tokenizer_token
mov r17,r24
.L208:
mov r24,r17
subi r24,lo8(-(-36))
cpi r24,lo8(3)
brsh .L227
rcall tokenizer_next
rcall expr
cpi r17,lo8(37)
breq .L210
cpi r17,lo8(38)
breq .L228
ldi r18,lo8(0)
ldi r19,hi8(0)
cp r28,r24
cpc r29,r25
brge .L215
rjmp .L225
.L217:
rcall tokenizer_next
rcall expr
ldi r18,lo8(0)
ldi r19,hi8(0)
cp r28,r24
cpc r29,r25
brne .L225
rjmp .L215
.L218:
rcall tokenizer_next
rcall expr
ldi r18,lo8(0)
ldi r19,hi8(0)
cp r28,r24
cpc r29,r25
brlt .L215
rjmp .L225
.L228:
ldi r18,lo8(0)
ldi r19,hi8(0)
cp r28,r24
cpc r29,r25
brne .L215
rjmp .L225
.L210:
ldi r18,lo8(0)
ldi r19,hi8(0)
cp r24,r28
cpc r25,r29
brge .L215
rjmp .L225
.L226:
mov r14,r28
mov r15,r29
mov r24,r28
mov r25,r15
/* epilogue start */
pop r29
pop r28
pop r17
pop r15
pop r14
ret
.size relation, .-relation
ziemlich viel code nur fuer ein einfaches while und case ??
ginge es den nicht besser ??
ich denke schon...
baut man das ganze einbisschen um.. geht es schneller...
das musste doch etwas bringen ?? als der case ...
int * func_t []=
{
basic_rel_eq,basic_rel_gt,basic_rel_lt,basic_rel_ne,basic_rel_ge,basic_r
el_le};
/*----------------------------------------------------------------------
-----*/
int relation(void)
{
int r1, r2;
TOKEN op;
unsigned char tmp;
int ( * func)(int,int );
r1 = expr();
op = tokenizer_token();
DEBUG_PRINTF("relation: token %d\n", op);
func=func_t[tmp];
if((op >= TOKENIZER_EQ) && (op <= TOKENIZER_LE))
{
tmp=op-TOKENIZER_EQ;
tokenizer_next();
r2 = expr();
DEBUG_PRINTF("relation: %d %d %d\n", r1, op, r2);
r1=(func)(r1,r2);
op = tokenizer_token();
}
return r1;
}
time ./main <cmd.txt >/dev/null
real 0m0.745s
user 0m0.736s
sys 0m0.000s
hmm.. nicht viel aber immer hin...
ich denke der expr() waere auch so ein kandidat :)
lg roman
Gast
#1782528
vergisst das zeug da oben... funktioniert ohne grobe aenderungen am compiler nicht. tja ... sackgasse
Welches Zeug ?
Gast
#1782638
funktions call ueber pointer , resp. vermeidung von switch{}.
Wenn die TOKENs zu weit liegen (von den Nummerierung) generiert der
compiler ein undurchsichtigen code von if_then_else_may. genau das selbe
gilt auch fuer die while bedinung.
geht nicht.. da die TOKENIZER_ definition in einer bestimmten
reihenfolge sein muesste. Wenn es der fall waere, koennte man es so tun.
Sry, aber ich versteh das Problem nicht ganz. Funktionszeiger wären doch ideal. Gerade bei Tokens die weiter "hinten" stehen, also weiter hinten im switch-case müsste sich eine größere zeitersparnis geben. Was du meinst mit den "zusammenhängenden Tokens" und korrespondierenden Funktionszeigern lässt sich ja ganz einfach dadurch machen indem man der Token-Struktur den Funktionszeiger hinzufügt. Dann ist es egal ob zusammenhängend oder nicht, der Zeiger wird immer mitgeführt und ist immer korrekt. So habe ich es ja in meiner Version (die in dem eigenen Thread, aber auch hier in dem Thread) gemacht. Das alles passiert ja in der "execbasicfunc". Und die könnte man evtl. noch per Makro ersetzen um Zeit zu sparen. Die Checks die da gemacht werden sind wahrscheinlich eh überflüssig.
Hallo, ich wühle jetzt mal diesen Thread wieder raus, denn genau sowas suche ich. Soweit ich das bis jetzt gesehen habe, gibt es 3 Version, eigentlich ja 4. Einmal die Ur-Version, auf der alles aufbaut (Version 4 :-) ) Dann die von Uwe, von Roman und von Rene. Alle drei Versionen haben ihr eigenheiten, die von Uwe ist auf Platz optimiert, die von Roman und Rene auf Geschwindigkeit. Wobei die von Roman auch auf Platz. Sehe ich das richtig? Was den Funktionsumfang betrifft: Da werd ich noch nicht ganz schlau. Was mich persöhnlich angeht: Ich suche etwas, bei dem ich die Programm von SD Karte einlesen kann. Ein Zusatzfunktion, die per USB die Daten auf die SD Karte vom PC ablegen kann. Aber das könnte ich auch selber einbauen. Speziell geht es mir darum, dass ich grundlegende Basic Funktionen habe und das ganze so schnell wie nur mögich abläuft (Also Renes Version) Habe auch mal in den SVN Teil geschaut, da ist leider noch nichts drin. Schade. Wäre schön, wenn es hier weiter geht. Gruß Michael
>Ich suche etwas, bei dem ich die Programm von SD Karte einlesen kann Ich habe mal in der Zwischenzeit was gebastelt (aber noch nicht hier rein gestellt), mit dem sich meine Version mit beliebigen Quellen füttern lassen kann, da es nun nicht mehr zwingend notwendig ist den Quellcode komplett im RAM zu halten. Damit ließe sich sowohl der Text als auch die vorinterpretierten z.b. von einer SD Karte ohne großen Aufwand direkt ausführen lassen. Im moment bin ich noch dabei die Version aufzuhübschen und zu testen. Werde aber die Tage (so ich dazu komme) diese Version in dem anderen Thread posten. >Habe auch mal in den SVN Teil geschaut, da ist leider noch nichts drin. Das SVN-Repository ist für Uwe angelegt worden. Nur er kann Leute hinzufügen die was einchecken können. Aber da er seine Version ja auf seiner Seite gehostet hat weiß ich nicht ob das für ihn zu attraktiv bzw nützlich ist alles über SVN zu machen. >Wäre schön, wenn es hier weiter geht. Das wird es sicherlich :-)
MoinMoin, sitze gerade in einem Internetcafe ca. 10250km von Berlin entfernt, geniesse den letzen Tag Urlaub und werde morgen wieder in Dt. online sein... Deshalb keine besonderen Aktivitaeten in den letzten Wochen und auch noch nichts im SVN. Es geht aber bald weiter. Renes Geschichte mit dem Einlesen des Programmtextes wuerde mich (dann) auch interessieren. Also bis demnaechst, Uwe
Dann genieß den letzten Tag Urlaub mal schön :-) Freu mich schon darauf wenn es weitergeht. Habe meine Version schon aufgehübscht und noch etwas getestet (AVR und WIN32). Wird morgen oder übermorgen gepostet. Bis die Tage dann. PS. Freut mich das Interesse an der Zeichengeräte-Geschichte (so hab ich das Einlesen von untersch. Quellen genannt) besteht.
Um den 0x80sten Eintrag in diesem Thread zu posten, und um nochmal auf mein neuesten Ableger des AVR-uBasics auf Adam Dunkels basierend aufmerksam zu machen entführe ich diesen Thread noch einmal kurz. (Man möge mir verzeihen). Features des Werkes bzw Verbrechens (je nach belieben) sind unter Beitrag "Basic-Interpreter für AVR auf Basis von Adam Dunkels uBasic" zu finden. Viel Spaß damit.
MoinMoin, ich habe mal begonnen die Projektseite für den SVN-Einstieg zur avr-basic (http://www.mikrocontroller.net/articles/AVR_BASIC) aufzubauen. Der Upload der Quelltexte ins SVN erfolgt am Wochenende (wenn ich mich mal ein wenig mit Subversion vertraut gemacht habe; ich verwende privat CVS als Versionskontrollsystem...) @René Du kannst ja mal anfangen auch deine Version auf dieser Seite kurz zu beschreiben, die entsprechende Überschrift ist bereits enthalten.... Grüße Uwe
Nachtrag: die Quellen befinden sich jetzt im SVN: http://www.mikrocontroller.net/svnbrowser/avr-basic/
Habe auch die Quellen in SVN hinzugefügt sowie den Artikel vervollständigt.
Gast
#1811701
habe irgendwo meinen namen gelesen... Ich habe nur Spass an dem basic. eines Tages wenn ich mal gross bin (bin ja erst 41) werde ich was daraus tun. Meine Version (wenn man es so bezeichnen kann) ist, das möglichste aus dem code raus zu holen, speed und platz. Das sich der code zwischen durch verkleinert kommt ganz automatisch. zb. die Anpassung mit der keyword laenge, bringt unglaublich viel performance so wie auch der Anpassung von single_char. Der Trick ist anschliessend den compilierten code anzuschauen. den.. schaut man sich nur die single_char routine an, es sind unglaublich viele if then else if .. das generiert einen viele jumps im code.. die modifizierte Version, man fuegt eine Tabelle mit den entsprechenden Informationen ein und der single_char ist nur noch ein lookup. eine meine Ideen geht leider nur bei der Interpreter Version und nicht bei Rene's version. Rene arbeitet mit include's resp. durch geschickte Anordnung von defines, werden tabellen automatisch erstellt resp. code fuer den Compiler generiert durch den Preprocessor. jedoch.. Routine wie relation() oder auch expr().. wird ja überprüft, ob es sich um die Vergleichs Operationen handelt resp. um die entsprechenden tokens. Was der compiler generiert ist ein unglaublich lange Spagetti code, bei dem jeder token einzeln verglichen wird. Durch geschickte Anlegens von den Tokens, dh. alle tokens die eine Routine braucht sind der reihe nach in der token Tabelle definiert, kann man die while( reduzieren auf ein (token > TOKEN_WAS && token < TOKEN_WAS2) spart viel Assembler Code!!! Der switch konstrukt von GCC, produziert einen unglaublich viel code ... da alles überprüft wird und viel compares und jumps im code einbaut. je nachdem.. muss die CPU alle diese Überprüfungen durchlaufen, bis die eigentlich Arbeit zb. nur eine Addition durchgeführt wird. muss das sein ?? nein.. dies kann man vermeiden, durch geschickte "C" Programmierung.. neben Effekt.. man spart viel code, ein gesparte Code muss nicht von der CPU abgearbeitet werden :) lg roman
Gast
#1813990
Frage an das Publikum.... Wer ist interessiert an frei waehlbaren variable name ?? ala .. 10 gugus=10 20 print gugus, gugus+10 run 10 20 resp. wo kann ich die aenderungen rein platzieren ?? lg roman
Hallo Roman, dir ist aber schon bewußt, dass das "Ur-Basic" auch keine Variablennamen mit mehr als einen Buchstaben zuläßt? Diese "Unsitte" ist erst später dazu gekommen.... Grüße Uwe
Gast
#1814575
ist jedoch sehr hilfreich ... welche der ur-basics meinst du ?? der ms basic hat es schon immer gehabt zb. auch auf den cp/m maschinen. dec basic, ncr basic und und und und... nur dir mini/micro basic hatten es nicht. zu dem .. unter urbasic findet man auch bei der wikipedia nix ?? siehe http://de.wikipedia.org/wiki/Kategorie:Programmiersprache_Basic ist eine lustige argumentation ... findest du nicht ?? sogar der 8052 build in interpretter hat es. ich finde es macht schon sinn auch in kleinen apps. die uebersicht zu bewahren. also.. 10 alarm_status = 1 liesst sich nun mal besser als 10 a=1 oder 10 temperatur_error=temperatur_user - temperatur_current auch ja noch eine sache.. man ist nicht mehr auf die 26 variablem begrenzt :) lg roman
Die Idee die Variablenbegrenzung (zum einen nur 1 Buchstabe und zum zweiten, nur 26 davon) aufzuheben hab ich auch schon seit geraumer Zeit im Kopf. Es stimmt schon das man den Programmfluss besser versteht wenn man sprechende Namen verwendet. Aber das Problem ist das die ordentlich RAM benötigen. Und bei meinetwegen 5-10 Namensbytes sind 2 Bytes für nen Integer dann ziemlicher overkill. Was eine Möglichkeit wäre, wäre z.b. eine Hash-Tabelle anzulegen. Dann hätte man noch einen Kompromiss zw Namenslänge und Nutzdaten. Ist aber auch nicht ganz unkritisch. Ich denke solange man kein wie auch immer angebundenes SRAM (es sei denn man erlabt sich an den 16KByte eines ATMega1284P ;-P, aber das ist ja nicht Sinn und Zweck der Sache) hat, sind lange Variablennamen (ab 3-4 Zeichen) auf begrenztem SRAM nur mit Tricks oder Einschränkungen möglich.
Gast
#1814913
eins verstehe ich dann doch nicht.. warum immer diese nur AVR begrenzung ?? ich verwende ja eine hash table mit pointer auf linked list. die wiederum ist relativ klein. so sind die variablem schnell gefunden. oder soll ich auch noch ein branch anfangen ?? bei der compiler version denke ich mir, koennte es viel einfacher sein. den der muss nicht wirklich die namen speichern, sondern sich nur memory merken wo diese sind. roman
Roman65536 schrieb: > welche der ur-basics meinst du ?? ich kann mich dunkel daran erinnern, dies im Buch "Visionäre der Programmierung: Die Sprachen und ihre Schöpfer" im Interwiew mit dem Basic-Vater gelesen zu haben.... Nebenbei, ein sehr lesenswertes Buch, wenn man sich für Sprachdesign, Softwareergonomie, Projektmanagment u.ä. beschäftigt. Da sind einige sehr interessante (teilweis kontroverse/streitbare) Äusserungen zu diesen Themen von den einzelnen Leuten abgedruckt. Grüße Uwe
roman65536 schrieb: > warum immer diese nur AVR begrenzung ?? :-) naja, weil (zumindestens) mein Hintergedanke bei diesem Interpreter war, dass er auf einem AVR laufen soll. Klar, ohne diese Beschränkung könnte man der sehr viel machen und soll dich auch nicht hindern dies in einem eigenen Zweig zu tun! Grüße Uwe
Gast
#1819917
Hallo Zusammen, habt Ihr euch schon mal Gedanken gemacht, Timing-Funktionen einzuführen? Für Schleifen mit definierter Laufzeit wäre so was sehr nützlich. z.B. so: 10 print "hallo" 20 waitForNextMillisecond(1000) 30 goto 10 Die Funktion "waitForNextMillisecond" wartet dabei 1000 Millisekunden, aber bezogen auf den letzten Aufruf von "waitForNextMillisecond". Damit lässt sich eine definierte Schleifenlaufzeit erreichen. Diese Funktion gibt es z.B. in LabView und auch beim Propellerchip von Parallax ist dieser Mechanismus verfügbar.
@chris
Timerfunktionen lassen sich bei meiner Version einfach über die
C-Erweiterungen einbauen, so ala (keine Gewähr) :
I16 vTimerWaitMs (T_USER_DATAS stDatas)
{
delay_ms (stDatas [0].ulLong);
}
Für ein definiertes Zeitverhalten ist/sind wahrscheinlich noch einige
Zusatz-Funktionen nötig die einen Zeitpunkt (Millisekunden) "festhalten"
und beid der Warte-Funktion eben sich auf diesen Zeitpunkt beziehen.
Eine weitere Idee die mir im Kopf rumschwirrt ist ähnlich dem Microsoft
Basic ein Konstrukt ähnlich dem "on timer x goto y". Weiterhin denkbar
wäre z.b. ein "on error goto y" oder wenn man Fifos hat ein "on fifo x
goto y" oder sowas. Solche Features dürften recht einfach zu realisieren
sein. Evtl nehm ich das mit in die nächste Version als Option mit auf.
Gast
#1820550
Hallo Rene, danke für Deine Antwort. Ich bin schon auf die nächste Version gespannt. Gerade eben habe ich mir mal den Source heruntergeladen. Mit den Copyrights im Programmkopf komme ich allerdings nicht ganz klar. Es gibt eine Version von Dir, in der nur Dein Name im Programmkopf steht und es gibt eine Version von Uwe Berger mit seinem Copyright. Die Grundidee stammt wohl von Adam Dunkel. Sollte im Programmkopf nicht etwa die Liste der Autoren stehen? Bester Gruß, chris
Meiner Erfahrung nach ist ein "on timer x gosub y" besser als ein "on timer x goto y", in einer ähnlichen Form habe ich das auch bei meinem ChipBasic realisiert. Damit kann man dann periodisch anfallende Aufgaben sozusagen "im Hintergrund" laufen lassen. Bei einem Fehler ist GOTO wohl die bessere Lösung, allerdings sollte man einen Fehlercode und/oder die Zeile bei der der Fehler auftrat herausbekommen. Jörg
@Chris In den unter SVN eingecheckten Sourcen sind die Copyright-Abschnitte auch geradliniger. Bei der alten Version hatte ich noch Fehler in den Abschnitten gehabt. @Jörg Ich meinte auch ein Gosub, hatte aber Goto geschrieben.
Gast
#1821165
>Meiner Erfahrung nach ist ein "on timer x gosub y" besser als ein "on >timer x goto y", in einer ähnlichen Form habe ich das auch bei meinem >ChipBasic realisiert Klingt wie ein Timer-Interrupt in C oder Assembler. Das ist natürlich eine nützliche Sache. Ich persönlich würde ein Multitasking-Konzept vorziehen. Also einfach mehrere Basic-Programme gleichzeitig laufen lassen. Die Kommunikation der einzelnen Programme könnte man einfach über gleich Variablen mit gleichem Namen laufen lassen.
Gast
#1821168
Gab es ähnlich schon 1986 (AH-8052 Basic von Intel).
Gast
#1821169
... "on timer x gosub y" ... Gab es ähnlich schon 1986 (AH-8052 Basic von Intel).
Gast
#1821251
Das Basic kann für Windows und AVR compiliert werden.
1 | |
2 | |
3 | |
4 | |
5 | |
In welchem File findet sich
1 | |
Ich habe alle Files durchsucht, aber nichts gefunden?
@chris das ist letztendlich auch eine Art "Timer-Interrupt". Im Timer-Interrupt wird periodisch ein Flag gesetzt. Vor jedem BASIC Befehl werden verschiedene Flags getestet (z.B. CTRL-C für Programmabbruch) und eben auch das Timer-Flag. Ist es gesetzt, wird es gleich wieder gelöscht und das GOSUB in den Programmablauf "hineingemogelt". Das spart Code, denn man kann die normalen GOSUB-Routinen (Stack-Handling) dafür verwenden. Jörg
Gast
#1821304
Hallo Wolfram, was ich schon immer mal sagen wollte: Deine Webseite http://www.jcwolfram.de/projekte/avr/chipbasic32/main.php finde ich richtig gut. Übersichtlich, klar strukturiert, kein SchnickSchnack. Den BASIC Computer finde ich auch recht nett. Ich habe das Projekt schon am Anfang gesehen. Sehr minimalistisch, das maximale aus einem AVR herausgeholt, sehr schön. Beste Grüße, chris
Gast
#1821434
Hier meine erste Version des UBasic-AVR für das XPLAIN Board: http://www.hobby-roboter.de/forum/viewtopic.php?f=4&t=123&p=519
@chris #define AVR bzw #define WIN32 muß man nicht definieren. Diese defines sind Platformabhängig gesetzt, d.h. bei einem WIN32-C-Compiler ist immer WIN32 definiert. Bei einem AVR-GCC ist AVR immer definiert. Auf einem ARM dürfte (sollte) demnach immer ARM vorhanden sein, um auf die jeweiligen Unterschiede der Platform eingehen zu können. @Jörg >Vor jedem BASIC Befehl werden verschiedene Flags getestet Ganz genau so wird das wohl auch implementiert werden. Allerdings mache ich mir gerade Gedanken dazu wie man das Flag-Handling und die korrespondierenden on XXX gosub y Befehle "parametrieren" kann. Dadurch (soll) kann man dann ganz einfach ein "on timer x gosub y", "on flag xy gosub y", "on uart_receive gosub y", "on fifo x gosub y", usw selbst anlegen und das Basic um eigene "ON XXX GOSUB Y" erweitern. Mal schauen wie ich das am besten umsetze (da es ja auch von den Anwendern verstanden werden muß)
Gast
#1822132
>@chris >#define AVR bzw #define WIN32 muß man nicht definieren. OK, aber wie ist es, wenn ich mit dem GCC unter Linux kompilieren will? BTW: Die Trennung zwischen den Plattformen über die defines kann ein wenig ausufern. Jetzt hätten wir schon die Plattformen Win,AVR und XPLAIN. Man könnte vielleicht noch einfach ARM-Boards dazunehmen. Dein Kommandointerface ist ganz nützlich. Schade, dass kein einfacher Editor dabei ist, mit dem man ein Basic-Programm ins EEPROM hacken kann. Bei den früheren Computern wie z.B. ZX81 konnte man ein Programm schreiben, indem man am Anfang eines Befehls die Zeilennummer geschrieben hat. So eine einfach Eingabe wäre sehr nützlich.
@Chris Mmh.. Is'n Argument. Evtl gibt es noch Platform-defines ala X86 die man verwenden könnte. Oder man geht den umgekehrten Weg und ersetzt WIN32 durch X86 (falls es bei allen Betriebssystemen und C-Compilern auf dem PC nicht ohnehin ein X86 define gibt) In der platform.h (o.ä.) macht man dann folgendes #ifdef WIN32 #define X86 #endif #ifdef LINUX_IRGENDWAS #define X86 #endif Somit wäre dann für Windows und Linux gesorgt. >Die Trennung zwischen den Plattformen über die defines kann ein >wenig ausufern Das sollte auch NUR bei den Teilen gemacht werden die wirklich nicht "emuliert" werden können bzw nicht auf gemeinsame Fkt zurückgeführt werden können. Am sinnigsten wäre es alle Hardware-Funktionen so zu kapseln das sie auf jeder Platform benutzt werden können. So wirds in dem Demo ja auch gemacht, allerdings gibt es immer mal wieder Situationen in denen man direkt unterscheiden muß (bei Windows braucht man keine Initialisierung für printf, beim AVR und ARM schon ...) Aber ich geb dir recht, es kann definitiv ausufern :-) >Man könnte vielleicht noch einfach ARM-Boards dazunehmen. Würd ich gerne machen, aber für ARM hab ich noch keine IDE und mit ARM-GCC und den Programmern steh ich ein wenig auf "Kriegsfuß" bzw hab noch keine "einfache" komplett IDE mit der man "mal eben" loslegen kann, ansonsten bin ich auf jedenfall dafür die ARM-Platformen mit aufzunehmen. >Dein Kommandointerface ist ganz nützlich. Danke :-) Hört man doch gerne. Es gibt auch einen (bzw 2) Threads dazu. Der Kommandointerpreter ist ein eigener Zweig der mittlerweile doch schon sehr Leistungsfähig ist. So können in meiner Entwicklungsversion auch Ausdrücke verwendet werden (also z.b. "test 1+2+3+4*(4+5+c_var), c_arr[c_var+3]"), die ebenfalls Funktionsaufrufe in C erlauben (ähnlich wie mit den Extensions in meiner Basic-Version). Es wird auch noch (sofern ich dazu komme) ein XML-Reader geben der sogar auf dem AVR lauffähig ist und einfach zu benutzen ist. Ein Editor müsste in der Tat noch her. Allerdings wird es mit dem RAM denke ich sehr eng. Aber Möglichkeiten gibt es da bestimmt :-)
Gast
#1823759
Hallo Rene, hier ein kleines Update: http://www.hobby-roboter.de/forum/viewtopic.php?f=4&t=123 Ich habe angefangen, die Platformspezifischen Funktionen auszulagern. Das Header-File für alle Plattformen lautet platfom_specific.h Für die einzelnen Plattformen müssen dann die spezifischen C-Files dazukompiliert werden: für Windows: platform_specific_WIN32.c für das XPLAIN-Board: platform_specific_XPLAIN.c Was mir aufgefallen ist: Mit der AVR-printf-Funktion scheint es ein Problem zu geben. Das System stürzt bisweilen ab. Das hatte ich bei früheren Programmen schon mal, wenn ich printf verwendet habe. Es scheint wohl irgendwelchen Speichermüll zu produzieren. Ich bin deshalb bei meinen anderen Programmen dazu übergegangen, kein printf zu verwenden und statt dessen alle print Funktionen selbst zu definieren und nur das eigene serielle putchar zu verwenden. BTW: hier ist der Thread zum XPLAIN-Board Beitrag "XMEGA/ AVR-EXPLAIN code examples und Diskussion"
MoinMoin, habe gerade eine neue Version des "Uwe-Berger-Zweiges" ins SVN gestellt. Folgende (interne) Änderungen: -ein kleiner Codereview -endlich mal wieder für Nicht-AVR-Plattformen übersetzbar gemacht -nextptr eleminiert (als Vorbereitung für andere Ideen) -Speicherplatz- und Performance-Optimierungen -Syntax-Änderung bei Befehlen DIR, IN, OUT, da so logischer -Doku entsprechend angepasst Grüße Uwe
@chris Hatte eig gedacht das ich auf dein Posting geantwortet hätte, aber habs wohl verbaselt abzuschicken ... Also die platformspezifischen Unterschiede auf mehrere Dateien aufzuteilen halte ich nur bedingt für sinnvoll. Dann müsste man das strenggenommen für jedes Modul machen welches zu unterschiedlichen Platformen passt (also z.b. UART_AVR.c/h; UART_WIN32.c/h; UART_ARM.c/h oder MMC_AVR.c/h; MMC_PIC.c/h; MMC_WIN32.c/h usw ...) Dann explodiert der Ordner irgendwann. Sinniger wäre es vllt (zumindest gehe ich langsam dazu über das so zu machen) die platformabhängigen Teile in dem jeweiligen Modul selbst zu integrieren. Also das eine MMC.c/h oder UART.c/h alle Funktionen für alle (!!) Platformen beinhaltet. Das macht die Sache zwar nicht übersichtlicher, aber ich weiß nicht was unübersichtlicher ist : Wenige Module mit vielen Abschnitten, oder viele Module mit nur einem Abschnitt. Wie man es letztenendes realisiert ist denke ich Geschmackssache. Der Quellcode dafür muß ja ohnehin für jede Platform irgendwo vorhanden sein. Also warum nicht im Modul selbst ?! Danke noch für deine Mühe das Ganze für das XPlain Board anzupassen. @uwe Bin mal gespannt auf deine Änderungen. Werd mir mal das SVN aktualisieren. @All Habt ihr eig. schon eine Verwendung für das Basic (egal ob von Uwe, mir oder sonstwem) ? Was habt ihr mit dem Basic vor ? Habt ihr es schon getestet ? Mich würde es freuen etwas Feedback zu dem Thema zu bekommen.
Rene Böllhoff schrieb: > @uwe > > Bin mal gespannt auf deine Änderungen. Werd mir mal das SVN > aktualisieren. > ein paar Anregungen zu den aktuellen Änderungen habe ich aus deiner letzten Version gewonnen... Grüße Uwe
@Uwe Freut mich wenn jemand Teile oder Ideen aus meinem Code weiterverwenden kann :-) Mal schauen ob ich deine Umsetzungen finde :-)) Gruß Rene
Gast
#1844410
>Also die platformspezifischen Unterschiede auf mehrere Dateien >aufzuteilen halte ich nur bedingt für sinnvoll. Dann müsste man das >strenggenommen für jedes Modul machen welches zu unterschiedlichen >Platformen passt (also z.b. UART_AVR.c/h; UART_WIN32.c/h; UART_ARM.c/h >oder MMC_AVR.c/h; MMC_PIC.c/h; MMC_WIN32.c/h usw ...) Dann explodiert >der Ordner irgendwann. Ja, es stimmt, die Anzahl der Dateien steigt mit jeder Plattform an. Am besten wäre es, für jede Plattform einen Unterordner anzulegen. Das von Dir beschriebene Beispiel passt gut: Die Uart ist ja wirklich bei jeder Plattform völlig verschieden. Also muss man den Code für jede Plattform so oder so erstellen. Hilfreich ist da ein gemeinsames Headerfile
1 | |
in diesem File steht dann der Prototyp für die UART z.B.
1 | |
Die dann im Plattformfile "platform_WIN32.c" für den PC so aussieht:
1 | |
2 | |
3 | |
4 | |
So umständlich es scheinen mag, so praktisch ist es: die Plattform spezifischen Funktionen sind wirklich vom eigentlichen Programm getrennt und damit wird das BASIC wirklich portabel.
MoinMoin, im SVN von mikrocontroller.net ist eine neue Version des Basic-Interpreters (mein Entwicklungszweig...) verfügbar: http://www.mikrocontroller.net/svnbrowser/avr-basic/ U.a. sind folgende Änderungen eingeflossen: * Erweiterung/Verbesserung des Basic-Syntax: *berechenbare Sprungziele in einer Reihe von Basic-Befehlen *Verbesserung PRINT-Anweisung *Kurzform für if-then (then kann weggelassen werden) * die wichtigste Änderung: der Basic-Quelltext kann von einem beliebigen Speichermedium eingelesen werden. Dazu gibt es ein paar Defines, welche entsprechend umdefiniert werden müssen. Im Archiv sind 3 Beispiele für Speichermedien enthalten. * Anpassung der Dokumentation Grüße Uwe Berger
Werde ich mir direkt mal anschauen :-)
MoinMoin, eine neue Version ist im SVN (http://www.mikrocontroller.net/svnbrowser/avr-basic/uwe_berger/) verfügbar: -Bugfix in ubasic_call.c und ubasic_cvars.c (es gab einen Seg. fault, wenn Prozedure bzw. Variable nicht definiert, aber doch angesprochen wurde...) -neue Zahlenformate: Hexadezimal und Dual -Doku entsprechend angepasst. Grüße Uwe Berger
MoinMoin, ich habe mal mit meiner Basic-Interpreter-Lib ein kleines Referenz-Projekt erstellt (siehe Anhang): * die Basic-Programme liegen auf einer SD-Karte und werden während der Interpretation auch direkt von dort gelesen; damit wollte ich vor allem testen, ob meine letztens implemtierte Schnittstelle zum Zugriff auf beliebige Speichermedien auch wirklich funktioniert... (und ja, sie funktioniert!) * als SD-Karten-Lib ab ich http://www.mikrocontroller.net/articles/AVR_FAT32 verwendet * Ein-/Ausgabe erfolgt über die serielle Schnittstelle * ein ATmega168, wie ich ihn bisher verwendet habe, stößt jetzt an seine Grenzen (SRAM); es laufen nicht mehr alle Basic-Programme, die ich zum Testen verwendet hatte, weil teilweise der dynamische Speicher zu klein ist; habe mir deswegen auch gerade ein paar ATmega328 bestellt... * in Punkto Ausführungsgeschwindigkeit ist der Interpreter natürlich langsamer geworden, was am Speichermedium SD-Karte und der entsprechenden Lib liegt (was jetzt aber keine Kritik an Daniel sein soll!); ich hatte auch nichts anderes erwartet * eine "Doku" befindet sich am Anfang von main.c Ausblick, wenn ich dann einen ATmega328 in der Hand haben sollte: * Grafik-Display oder TV-Out * PC-Tastatur (...eigentlich wollte ich keinen Basic-Computer aufbauen/implementieren, aber irgendwie reizt es doch...) Grüße Uwe
Gast
#1967298
Vorschlag zum RAM-Problem: Was hältst Du von einem Treiber für ein serielles RAM wie das 23x256 von Microchip: ww1.microchip.com/downloads/en/DeviceDoc/22100D.pdf
MoinMoin, chris schrieb: > Vorschlag zum RAM-Problem: > > Was hältst Du von einem Treiber für ein serielles RAM wie das 23x256 von > zum Zwischenspeichern des Basic-Programmes? Prinzipiell ja, es müssten dann nur die Defines in tokenizer_access.* entsprechend angepasst werden. Dann stellt sich aber die Frage, wie das Basic-Programm in den SRAM kommt... Grüße Uwe
... nochmal zum Thema externer SRAM: Mir ist gerade der "bekloppte" Gedanke gekommen, die ganzen internen Stacks/Caches des Interpreters (GOSUB-, FOR/NEXT-Stack und den GOTO/GOSUB-Cache) dorthin auszulagern. Man könnte eine ähnlich universelle Schnittstelle, wie für das Speichermedium der Basic-Programme schreiben... Allerdings würde dabei auch ein gewisser Overhead entstehen, der sich wahrscheinlich mit dem gewonnenen SRAM im MC die Waage hält, aber dafür könnte man die Größen der Stacks/Caches drastisch erhöhen... Die Ausführungsgeschwindigkeit des Interpreters dürfte sich vermutlich entscheidend verschlechtern. Für Rene's Pre-Compiler wäre die Geschichte aber gar nicht so uninteressant, weil dann der vorübersetzte Code auch dorthin ausgelagert und im 2.Lauf von dort wieder eingelesen werden könnte. Rene, was meinst du dazu? Grüße Uwe
Ich hatte noch ne viel beklopptere Idee :-)
Meine Idee war/ist das ich neben dem Basic ein Dateisystem aufziehe
indem alle möglichen Medien (zu denen es Treiber gibt) integriere und
quasi eine Oberfläche damit bilde. Dadurch das die Programme ja auch mit
meiner Version compilierbar sind ist es letztenendes egal wo das
Programm (Compiliert oder uncompiliert) landet. Also ein Basic-Programm
ist auf SD-Karte und wird compiliert und in ein Datenflash abgelegt.
Dadurch das der Basic-Byte-Code relativ schnell ausgeführt werden kann
hätte man da einiges an Möglichkeiten, und könnte sich nach und nach ein
System aufbauen.
Ich hab auch die Idee Basic-Programme als Unterprogramme ausführen zu
lassen.
Bsp :
SD-Karte :
Main.bas
UP1.bas
in Main.bas steht bspweise :
...
230 call ("UP1.bas", a = 20, b = 40, c = 50)
...
Das UP1.bas wird geladen, die Variablen a b und c initialisiert und
UP1.bas dann ausgeführt. Ist UP1.bas dann beendet kehrt die
Programmbearbeitung zur nächsten Zeile nach dem Call zurück.
Im Prinzip ist das auch relativ einfach machbar (auch bei Uwe müsste
sich das einfach nachrüsten lassen soweit ich seinen Quellcode gesehen
habe).
Da gibts bestimmt noch ein paar andere schöne Ideen (z.b. das
Dateisystem ähnlich wie in Linux mit Ein/Ausgabegeräten versehen).
Langfristig werde ich sicherlich diese Ideen (bzw die ein oder andere
davon) umsetzen.
Allerdings ist es ja auch immer eine Frage des Speicherplatzes, und ich
denke bei solchen möglichkeiten werden selbst 32K eng. Es sei denn man
macht alles in Assembler. Aber dafür ist der Jörg Wunsch mit seinem
genialen AVR-ChipBasic zuständig :-)
Gast
#1968269
"... nochmal zum Thema externer SRAM: Mir ist gerade der "bekloppte" Gedanke gekommen, die ganzen internen Stacks/Caches des Interpreters (GOSUB-, FOR/NEXT-Stack und den GOTO/GOSUB-Cache) dorthin auszulagern. Man könnte eine ähnlich universelle Schnittstelle, wie für das Speichermedium der Basic-Programme schreiben..." Sehr gut. Ins RAM müssen ja nur veränderbare Variablen. Das eigentliche Basic-Programm kann ja auf einer SD-Karte residieren. Damit dürfte es dann möglich sein, ziemlich große BASIC-Programme laufen zu lassen. Was die Geschwindigkeit anbelangt, wäre ich mir nicht so sicher, ob das Ganze wirklich so langsam wird. Theoretisch könnte man das RAM oder die SD-Karte im Block-Mode auslesen, also z.B. immer 128Byte und dann so eine Art Cache implementieren.
@chris Da wäre dann aber der Ansatz mit dem Vorcompilieren noch deutlich schneller, da die komplette Text-erkennung entfällt, und die Gosub/Goto-Sprungziele vorher schon ermittelt werden und dann quasi in "Null-Zeit" angesprungen werden können. Bei der Ursprungsvariante wurde bei einem Gosub/Goto ja das gesamte Programm von vorne durchsucht. Die Caches beschleunigen das ganze erheblich. Aber der Ansatz des vorcompilierens dürfte unschlagbar schnell sein :-) Nur ist der "Text" danach eben nicht mehr lesbar. Es sei denn ....
Gast
#1968326
Also beim ZX81 gab es einen Tokenizer, der hat die Texterkennung übernommen. Da der Computer nur 1K SRAM hatte und die Hälfte davon in der Basisversion noch als Bildschirmspeicher benutzt hat, wurde das gesamte Programm "tokeniziert" abgespeichert. Mit dem Befehl "list" wurde dann das Programm wieder lesbar dargestellt.
Mmh ... Einen Tokenizer ohne wirklich Speicher zu verbrauchen ... Erinnert mich irgendwie an meinen ersten Parser uShell :-) Der kam (bzw kommt) mit sehr wenig RAM aus um ein Token zu erkennen (etwa : (Anzahl Tokens / 8) + 2).
MoinMoin, Rene Böllhoff schrieb: > Ich hab auch die Idee Basic-Programme als Unterprogramme ausführen zu > lassen. > die Anfrage wurde auch schon von jemanden aus der c't-Bot-Ecke, wo der Basic-Interpreter derzeit im Develop-Zweig integriert ist, an mich herangetragen... Ich habe erst mal abgewiegelt, weil: Es müsste so ziemlich alles, was im aufrufenden Programm an dynamischen Werten (Rücksprungadresse, for/next-Stack, Gosub-Stack, Goto-Gosub-Cache, die 26 Variablen usw.) angefallen ist, in einen Stack wandern. Lustig wird die Geschichte, wenn vom Unterprogramm noch weiter verschachtelt wird... Achso, und es müsste wahrscheinlich eine SD-Lib her, wenn man denn z.B. mit dem Medium arbeiten will, die das gleichzeitige Öffnen von mehreren Files ermöglicht. Die ich gestern verwendet habe kann es nicht, da müsste man tricksen. Das alles dürfte für einen AVR SRAM-mäßig zuviel werden, es sei denn man verwendet einen Mega1284 (gibt es den schon zu kaufen?) oder einen exteren RAM... Grüße Uwe
MoinMoin, chris schrieb: > Sehr gut. Ins RAM müssen ja nur veränderbare Variablen. Das eigentliche > Basic-Programm kann ja auf einer SD-Karte residieren. Damit dürfte es > dann möglich sein, ziemlich große BASIC-Programme laufen zu lassen. > mittlerweile finde ich die Idee auch nicht mehr soooo bekloppt. Es gibt nicht allzu viele Stellen im Quelltext, wo auf interne "Stack-Bereiche" zurückgegriffen wird. Ich muss mal eine Weile darüber nachdenken, wie eine universelle Schnittstelle, ähnlich wie das Einlesen des Basic-Programms, aussehen müsste. Große Basic-Programme...: der meiste Speicher wird durch die vielen Rekursionen in ubasic.c verbraten, den man nicht auslagern kann. Typisches Beispiel dürfte eine Expression sein, die mit mehreren Klammern ausgestattet ist... chris schrieb: > Was die Geschwindigkeit anbelangt, wäre ich mir nicht so sicher, ob das > Ganze wirklich so langsam wird. > naja, kommt halt wieder auf das Speichermedium an, bei dem oben erwähnten seriellen RAM dürfte es schon einiges an Zeit kosten, die Werte auszulagern bzw. wieder einzulesen... Grüße Uwe
Uwe Berger schrieb: > Es müsste so ziemlich alles, was im aufrufenden Programm an dynamischen > Werten (Rücksprungadresse, for/next-Stack, Gosub-Stack, > Goto-Gosub-Cache, die 26 Variablen usw.) angefallen ist, in einen Stack > wandern. Lustig wird die Geschichte, wenn vom Unterprogramm noch weiter > verschachtelt wird... > ähmm, nochmal zu dem Thema und ganz wage formuliert...: ein Kompromiss wäre doch eigentlich, die Stack-Bereiche einfach gültig zu halten und weiter zu verwenden. Ein Unterprogramm ist doch nichts weiter als quasi eingeschobener Quelltext. Also alte Datei zu, neue Datei auf und weiterlesen usw., als wenn alles ein einziges Programm wäre... Die Zeilennummern sollten aber eineindeutig sein und eine Variablenübergabe, wie sie Rene vorschwebt geht dann bestimmt auch nicht, plus einiger weiterer Problemchen, die ich noch nicht benennen kann (den Textpointer des aufrufenden Programms müsste man sich vielleicht irgendwie doch merken). Grüße Uwe PS.: und es geht nur mit einem Speichermedium, auf dem ich den Programmen auch einen vernünftigen Namen geben kann, irgendwelche Speicherbereiche mit komischen Zeigern fallen dann aus...
MoinMoin, Uwe Berger schrieb: > ähmm, nochmal zu dem Thema und ganz wage formuliert...: > ein Kompromiss wäre doch eigentlich, die Stack-Bereiche einfach gültig > zu halten und weiter zu verwenden. Ein Unterprogramm ist doch nichts > weiter als quasi eingeschobener Quelltext. > nach dem ich mal eine Nacht drüber geschlafen habe, gefällt mir diese Idee immer besser. Am besten würde die Geschichte zum GOSUB-Befehl passen, der dann 2 Varianten aufweisen würde: 10 GOSUB 1000 20 GOSUB "up.bas" 10: wie bisher als Zeilennummer 20: Erkennung eines Strings, statt einer Zeilennummer; Dateiname des UPs GOSUB deshalb: * erscheint vom Syntax am logischsten (wenn man keinen neuen Befehl kreieren will) * in den entsprechenden Routinen (gosub-/return-Statement) werden eigentlich schon die entsprechenden Stack-/Cache-Bereiche verwaltet, die nur noch nur noch um den UP-Namen aufgebohrt werden müssten. Ein UP übernimmt die Variableninhalte des aufrufenden Programms und müsste natürlich mit einem RETURN enden. Eineindeutige Zeilennummern, sind wahrscheinlich doch nicht nötig, da die erweiterten Stack-/Cache-Bereich auch den UP-Namen enthalten müssen und damit wieder eineindeutig werden. Ich denke ich werde das mal zuende denken und in meiner Interpreterversion einbauen... Grüße Uwe
@Uwe Diese ganzen gedanklichen Ergüsse hatte ich ebenfalls bei der Überlegung :-) Das mit der SD-Lib hätte ich fast vergessen. Aber bei meinen Vorüberlegungen hatte ich mir ohnehin eine eigene FAT-Lib zusammengeschrieben weshalb das ganze Projekt auch ein wenig eingeschlafen ist. Bin beim programmieren von Höksken auf Stöcksken gekommen (AVR-Basic -> FAT-Lib) (FAT-Lib -> SD_Core für FPGA) (SD_Core für FPGA -> eigener Prozessor) (eigener Prozessor -> eigener Assembler). Irgendwie kein Wunder warum ich mit meinen Projekten nie 100% fertig werde (abgesehen von so Mini-Projekten) :-) >Es müsste so ziemlich alles, was im aufrufenden Programm an dynamischen >Werten (Rücksprungadresse, for/next-Stack, Gosub-Stack, >Goto-Gosub-Cache, die 26 Variablen usw.) angefallen ist, in einen Stack >wandern. Da hab ich auch zuerst mit ein wenig Bauchschmerzen gehabt. Aber bei meiner Variante fällt das etwas leichter, da ich keinen Goto/Gosub-Cache habe. Und die 26x2 Bytes Variablen-Stack, For/Next und Gosub-Stack pro Programmaufruf sind zwar auch nicht wenig, aber evtl wäre da eine "dynamische" Variablenverwaltung ala DIM sinnvoll, denn man benötigt ja nur in wenigen Fällen alle 26 Variablen. Meist sind es ja weniger. Und je nachdem wie man sich das Variablen-System zurechtlegt (also den Programmkontext) spart man sogar noch gegenüber der Brute-Force-Methode alle 26 Variablen immer abzuspeichern, bzw vorzuhalten. >einen Mega1284 (gibt es den schon zu kaufen?) Jup, gibt es. Hatte auch gehofft das der schon Mitte letzten Jahres rausgekommen wäre (mir 64kB kam ich schnell an die grenzen meines Audio-Projektes). Mit den 16kB RAM lässt sich schon ne ganze Menge anstellen. Nur wäre es sehr doof wenn das Basic (obwohl soviel Flexibilität) nur auf einem einzigen AVR laufen würde. >der meiste Speicher wird durch die vielen Rekursionen in ubasic.c >verbraten, den man nicht auslagern kann. Da hab ich im Endeffekt auch mehr bauchschmerzen gehabt als bei den anderen Betrachtungen. Aber mit dem Auslagern wäre ich mir nicht so sicher das das nicht doch "einfach" machbar ist :-)))). Ich habe da eine ziemlich perverse Idee. Ich muß die nochmal reifen lassen, aber wenn das klappt dann .... :-) >Typisches Beispiel dürfte eine Expression sein, die mit mehreren >Klammern ausgestattet ist... Genau dieses Problem hab ich (in einem kleinen Experimentier-Projekt) vollständig OHNE rekursive Aufrufe hinbekommen. Erzähl ich mal mehr wenn die Idee noch was weiter gereift ist. Der Ansatz ist denke ich nicht schlecht, und erlaubt auch eine grundlegende Kontrolle die bei allen Rekursionen NICHT möglich ist :-) Das Stichwort ist da : "Stack-Overflow" passe ... >naja, kommt halt wieder auf das Speichermedium an, bei dem oben >erwähnten seriellen RAM dürfte es schon einiges an Zeit kosten, die >Werte auszulagern bzw. wieder einzulesen... Daher hab ich mir auch eine eigene AVR-Basic-Projekt-Platine gebastelt mit 512k SRAM, (später noch mit HDD) und einem Grafik-Display. Aber das ganze hat sich ja leider etwas nach "hinten" verschoben. >PS.: und es geht nur mit einem Speichermedium, auf dem ich den >Programmen auch einen vernünftigen Namen geben kann, irgendwelche >Speicherbereiche mit komischen Zeigern fallen dann aus... Nicht zwangsweise. Daher habe ich bei meiner Variante auch vorwiegend den Datentyp INT (16-bit) verwendet. Wenn ich das noch richtig im Kopf habe, habe ich alles auf INT umgestellt mit der Bedeutung das sich INT immer relativ auf den Anfang des Textes bezieht. Damit sind Speicher ebenso wie Dateien direkt gleich behandelbar. Und bei Fehlern lässt sich leicht die Stelle im Text ermitteln an der es gekracht hat. >nach dem ich mal eine Nacht drüber geschlafen habe, gefällt mir diese >Idee immer besser. Ging mir ähnlich :-)) >Ein UP übernimmt die Variableninhalte des aufrufenden Programms und >müsste natürlich mit einem RETURN enden. Auch nicht zwnagsweise :-) Da der interpreter das Programmende ja erkennt muß er nur (wenn es nicht das Hauptprogramm ist) ganz normal in das alte Programm zurückkehren. Ich hatte mir neben dem übergeben von Variablen an das Unterprogramm auch überlegt das man Variablen VOM UP ans HP zurückübergeben kann. Müsste da aber nochmal genauer überlegen. Hatte mir da schon einen schicken Lösungsansatz überlegt, aber die Idee irgendwie verlegt :-)) >da die erweiterten >Stack-/Cache-Bereich auch den UP-Namen enthalten müssen Ich denke in deinem Falle wäre ein eigener Kontext sinnvoller, da du dich dann nicht um eineindeutigkeit und evtl Löschen von Cache Einträgen vornehmen musst um nicht im weiteren Programmverlauf auf die Nase zu fallen. Dann wirds denke ich aufwendig. Eig. könnte man (wenn es nicht doch einige größere Unterschiede gäbe) die beiden Versionen ohne weiteres zusammenführen. Aber von den unterschiedlichen Ansätzen profitieren denke ich alle. Deine Version ist einfach und kompakt gehalten. Meine eben etwas "aufgeblähter", aber dafür auch mit einigen mächtigen Features. PS. Ich habe das Gefühl das ich die Idee mit dem Gosub 10+20+3*(a) ebenfalls mit bei mir implementieren könnte. Ich müsste zwar dann jedesmal suchen, aber in der vorcompilierten Form wäre das denke ich auch zeitlich einigermaßen machbar. PPS. Immer weiter so Uwe :-) Ich werd mich auch wieder in absehbarer Zeit mit daran beteiligen.
Gast
#1968978
Wenn Ihr nicht zu sehr auf die Geschwindigkeit achten müsst, dann wäre ein serielles SRAM ganz toll. Viellicht noch in Kombination mit einem seriellen EEPROM oder eben SD-Karte Die resultierende Platine würde sehr klein, z.B. Atmega168+serielles SRAM und könnte ordentliche Programme ausführen. Man wäre dann ( falls es nicht um die Geschwindigkeit geht) nicht von Spezialprozessoren wie den Atmega1284 angewiesen. Wenn ich mich nicht täusche war das die Arbeitsweise der Basic-Stamp und dem Basic-Tiger.
@Chris Die Idee mit dem seriellen SRAM finde ich gar nicht mal schlecht. Ich überlege schon die ganze Zeit wo ich mein FRAM mal verwenden kann :-) Ne, mal im Ernst. Ich denke FRAM wäre das beste. Da es die FRAMs ja zw 8-64kB (oder größer) gibt hätte man die Möglichkeit einfach Programme abzulegen und hätte gleichzeitig noch irre viel SRAM für Variablen, Stacks usw. Die Geschwindigkeit ist dann zwar nicht mehr so berauschend, aber wenn man entweder damit leben kann, oder aber mit vorcompilierten Texten arbeitet hält sich die Geschwindigkeitseinbuße in grenzen. Oder aber wie von dir schon vorgeschlagen einen kleinen "Seiten-Cache", wobei die Ausführung dadurch ja auch nicht wirklich schneller wird, da man ja ohnehin erst immer eine Seite einlesen muß. Wäre dann mal durch Performancemessungen zu ermitteln. Wenn du eine Platine machen lässt sag mal bescheid :-) Zum experimentieren wäre aber vllt ein Mega328 evtl sinnvoller. Man hätte immer noch etwas "Reserven" nach oben, und ich denke bei diesem Projekt wo im Laufe der Zeit immer mehr an Features dazukommt kann das nicht verkehrt sein.
Gast
#1969297
"Die Geschwindigkeit ist dann zwar nicht mehr so berauschend" Hängt die Geschwindigkeit nicht vom Verhältnis Interpreter/Speicherzugriff ab? Ich vermute, dass die Interpretation eines Befehls mindestens so lange geht wie ein Speicherzugriff. D.h. würde die Sache vielleicht nur halb so schnell.
MoinMoin, habe gerade die "Weihnachtsversion" meines Entwicklungszweiges ins SVN eingespielt. Folgende wesentlichen Änderungen/Neuerungen: - Erweiterung des GOSUB-Befehls, es können externe Unterprogramme geladen/ausgeführt werden - Dateien/Verzeichnisse neu strukturiert - SD-Karten-Version jetzt im Zeig mit enthalten (SD-Karten-Lib: http://www.roland-riegel.de/sd-reader/index.html) - Dokumentation entsprechend angepasst Grüße & ein ruhiges Weihnachtsfest, Uwe
Wow, ich glaub ich muß demnächst auch mal wieder nachlegen :-) Sehr schön gemacht. Hab mir zwar die Quellcodes noch nicht weiter angeschaut, aber schön das das Unterprogramm-Feature nun auch mit drin ist. Weiter so. Bei mir wirds wahrscheinlich noch etwas dauern, da ich noch einige andere Ideen in der Richtung verfolge die evtl auch mit in das Basic einfließen werden. Dir Uwe, aber auch allen anderen ein frohes besinnliches und friedliches Weihnachtsfest und ruhige Feiertage. Rene
Gast
#1998034
Hallo, schönes Ding was ihr ausgetüftelt habt. Nun, wie kann man eigentlich Basicprogramme über das Terminal rüberschieben? Ich benutze zur Zeit eine SD-Karte. Gruss
basicfan schrieb: > Nun, wie kann man eigentlich Basicprogramme über das Terminal > rüberschieben? > na das liegt ganz an deinen Programmierkünsten und was du um den Basic-Interpreter herum implementierst. Was du in den Archiven findest, sind nur Beispiele, was man mit dem Ding machen könnte... > Ich benutze zur Zeit eine SD-Karte. > na ist doch schon mal ein Anfang und als Speichermedium hervorragend geeignet. Vielleicht jetzt noch eine kleine Routine, die Daten über die serielle Schnittstelle empfängt und auf der Karte abspeichert...? Oder doch gleich einen kleinen Editor auf dem MC (analog dem ChipBasic von Wolfram)...? Grüße Uwe
Gast
#1998779
hmmm..., ich glaubte irgendwo etwas gelesen zu haben, das man Daten/Programme bei diesem Basic über aRS232 rüberschieben kann. Na mal schauen. Ihc bin nämlich kein Programmierkünstler in "C" mit 62 jahren... Vielleicht gibt es bald eine Erweiterung oder ein Tipp dafür. Ich finde keinen zur Zeit keinen Ansatz, wo man das Gerüst reinsetzen soll, und wie es die Daten schieben soll. Vielleicht gibt es etwas für den AVR1284p mit 16kb SRam. Damit könnte man auch etwas anstellen. Gruss
Gast
#1998789
Ah...., habe die Quelle jetzt gefunden: hier: http://bralug.de/wiki/UBasic-avr ...Über die serielle Schnittstelle können zu Testzwecken Basic-Programme geladen und gestartet werden (gesteuert über die Hauptschleife im Testprogramm). Die Ausgaben (PRINT-Befehl, Debug-Ausgaben) erfolgen ebenfalls über diese Schnittstelle.... Das wollte ich Testen mit der RS232. Gibt es das Programm noch irgendwo? Gruss
Gast
#1998808
...Als Referenz für das Einbinden des Basic-Interpreters in eigene Programme, ist das Studium von main.c, welche im Quellcode-Archiv enthalten ist, angeraten.... Hmm..., die main.c finde ich nicht im Ordner. Gruss
Gast
#1998846
...Da wird der BB erst langsam Munter ;)... ist ein bisschen teuer das Ding. Gruss
Gast
#1998877
Hallo, wie sieht hier die " MAKEFILE" aus : ---------------------------- Autor: TheMason (Gast) Datum: 02.07.2010 17:27 Angehängte Dateien: * ubasic-0.2a.zip (24.5 KB, 30 Downloads) >>>> "MAKEFILE" ? ------------------------------- Danke. Gruss
Gast
#1999011
@basicfan Wenn du meine Version hast (uBasic0.2a) macht AVR-Studio das Makefile. Daher hab ich mir das hier "geschenkt", da ich auch nur mit AVR-Studio arbeite. Wenn Fragen zur Version von "TheMason" (myself) sind, frag ruhig. :-)
Gast
#1999043
Kannst du die hier mal als Beispiel reinstellen, damit ich dein Programm mal zum laufen bringe. Ich compiliere nur mit dem Notepad von Winavr. Gruss
Hallo basicfan, wie schon erwähnt, es handelt sich um eine Bibliothek, die man in eigene Applikationen einbinden kann. Sämtliche Beispiele sollen nur die Möglichkeiten aufzeigen und zu eigenen (Programmier-)Experimenten anregen. basicfan schrieb: > Hmm..., die main.c finde ich nicht im Ordner. Ich hatte letztens (meinen) Quellcode etwas umstrukturiert, das "zitierte" Beispiel gibt es der Form nicht mehr, da in ihm auf eine Systematik aufgesetzt wurde, die sehr viel dynamsichen RAM verbraucht hatte. In der SVN-Version 27 findest du in meinem Verzeichnis noch main.c, welche die Datenübertragung via serieller Schnittstelle beinhaltet. Aber ohne dein Zutun wird da nichts auf einer SD-Karte abgespeichert, kann also nur als Anhaltpunkt für deine Weiterentwicklungen dienen...! Grüße Uwe
Gast
#1999130
...In der SVN-Version 27 findest du in meinem Verzeichnis noch main.c,... Wie komm ich da ran? Es ist nur die neueste Version drauf, oder bin ich da falsch? Gruss
basicfan schrieb: > Wie komm ich da ran? > Es ist nur die neueste Version drauf, oder bin ich da falsch? > SVN ist ein Versionsverwaltungssystem innerhalb dem man auf alle jemals eingespielte Versionen zugreifen kann, also auch auf nicht mehr aktuelle Versionen. Klicke im Webfrontend auf die Revisionsnummer (derzeit die 31), dann erhält man eine Liste aller Versionen, dort dann auf die Revision 27. In dieser Version gab es das letzte mal eine Änderung in der Datei main.c... Aber diese main.c kannst du nicht einfach mit der aktuellen Version der Bibliothek mischen! Man könnte aber dort beispielhaft sehen, wie man Basic-Programme über die serielle Schnittstelle auf den Mikrocontroller bekommt, wobei sie aber nicht auf einer SD-Karte, sondern in einem RAM-Bereich des MC abgelegt werden. D.h. also du musst schon etwas Programmierkunst anwenden, um das zu erreichen, was du gern möchtest... Aber warum steckst du deine SD-Karte nicht einfach in einen Kartenleser am PC rein und speicherst die Programme so ab? Grüße Uwe
Gast
#1999540
...Aber warum steckst du deine SD-Karte nicht einfach in einen Kartenleser am PC rein und speicherst die Programme so ab?.... Das mach ich auch und das Basic funktioniert mit meinem selbstgebauten Terminal von Purebasic wunderbar. Ich bin halt neugierig wie es machbar wäre. Danke. gruss
@basicfan Der Ansatz ist ein anderer. Es geht sich darum das Basic in ein bestehendes System zu integrieren. Die Idee mit der "ladbarkeit" von SD-Karte kam später durch ein paar "Spinnereien" zw Spielereien von Uwe und mir. Der eigentliche Fokus liegt also nicht auf dem Basic als Stand-Alone-Projekt, sondern vielmehr als Grundgerüst um es in bestehende Projekte einzubauen.
Gast
#2000647
..... #ifndef _UBASIC_CONFIG_H_ #define _UBASIC_CONFIG_H_ // AVR-spezifischen einschalten #ifndef USE_AVR #define USE_AVR 1 #endif // regulaere Standardausgabe #if USE_AVR #define PRINTF(...) usart_write(_VA_ARGS_) #else #define PRINTF(...) printf(_VA_ARGS_) #endif // grml..., sollte man besser loesen! #if !USE_AVR #define uint8_t unsigned char #endif // max. Stringlaenge (Basic) #ifndef MAX_STRINGLEN #define MAX_STRINGLEN 20 #endif ...... Benutze jetzt die SVN-Version 27, ist schön übersichtlich um diesen Interpreter kennenzulernen. Frage: Wie werden eigentlich diese vielen "#ifndef" freigegeben damit die benutzt werden? gruss
Gast
#2000741
Die #ifndef sind ja eigentlich nur drin, damit Standardwerte vorhanden sind. Das man Quasi direkt "loslegen" kann. Wenn du andere Werte haben willst musst du bevor du die basic.h (und damit auch die config-Datei) inkludierst diese definieren. So würde ein #define MAX_STRINGLEN 40 den Standardwert 20 für MAX_STRINGLEN "überschreiben". >Benutze jetzt die SVN-Version 27, ist schön übersichtlich um diesen >Interpreter kennenzulernen. Schau dir dann besser nicht meinen Zweig an, da wirds dann sehr hässlich :-) und das obwohl es diesselbe Basis ist. Aber dafür bekommt man ja auch noch ein paar nette Zusatzfeatures. Allerdings ist das wirklich nicht einfach zu lesen !
Gast
#2000758
Nachtrag : Die ifndefs müssen nicht "freigegeben" werden. ifndef sagt nur aus wenn das Symbol hinter ifndef nicht (!) definiert ist so "blende" den Teil hinter dem ifndef bis zum nächsten ifdef/endif/elsif ein. Schau dir das mal bei den Headern an. Da hat das den hintergrund das nicht zweimal derselbe Header "eingefügt" wird. Alle #-"Anweisungen" sind nur für den Präprozessor der reine Text-ersetzung macht. Der Präprozessor weiß nichts von C oder irgendwelchen "Freigaben". Es können beim Compilieren bestenfalls ein paar vordefinierte Symbole mit übergeben werden. Aber was der Präprozessor daraus macht hängt von den #-Anweisungen ab.
Antwort schreiben
Bitte melde dich an, um einen Beitrag zu schreiben.