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...
... 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.
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
10 call("function1", 1, 20)
3
...
oder auch
1
...
2
10 a=call("function2", 120)
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
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
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).
@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
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
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.
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.
<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 ;)
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
@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
@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
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
@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 ;-)
@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
@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).
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
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
@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
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
@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.
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
@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).
@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
@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
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
@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
@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
@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
@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
@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
@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.
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 :)
@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
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....
@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...
@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
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
... 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
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.
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
@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 :-)
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
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.
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..
@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.
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
@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
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
@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.
@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.
@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.
@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
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 ?
@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
@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.
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
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
@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.
@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 ??
>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.
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.
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.
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.
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.
@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.
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
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
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
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.
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
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.
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.
>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.
@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
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
@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ß)
>@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 :-)
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
>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
platform_specific.h
in diesem File steht dann der Prototyp für die UART z.B.
1
voidplatform_putchar(uint8_tzeichen);
Die dann im Plattformfile "platform_WIN32.c" für den PC so aussieht:
1
voidplatform_putchar(uint8_tzeichen)
2
{
3
putchar(zeichen)
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
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
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 :-)
"... 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 ....
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.
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.
"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
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
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
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
...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
@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. :-)
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
...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
...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.
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 !
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.