BasicBeetle-Blog
Zusammenfassung der neuen BasicBeetle 2.00-Befehle
BasicBeetle mit Alzheimer
Die meisten Unterprogramme werden ja mit dem RETURN-Befehl wieder verlassen. Es gibt aber einige Sonderfälle, wo man nicht per RETURN zurück möchte, z.B. bei Fehlerbehandlungen. Dem BasicBeetle muss aber gesagt werden, dass der RETURN-Befehl ausgeführt wurde, damit der Stack sauber bleibt.
Dies kann nun mit dem NORETURN-Befehl erfolgen. Dieser neue Befehl 'vergisst' den letzten GOSUB-Aufruf. Auch für Rekursionen ist dieser Befehl hilfreich.
Grossputzaufräumsortierlöschaktion
Da der BasicBeetle 2 so langsam Form annimmt, ist inzwischen das Layout für die neue Version in Arbeit. Im Zuge der Entwicklung wird die neue Platine, wie auch die schon vorhanden Platinenlayouts leicht überarbeit. Zukünftig kann dann der BasicBeetle komplett alleine, z.B. wer nur etwas mit dem BasicBeetle 'spielen' will, in Stapelaufbau, per Einzelstiftleisten auf Streifenrasterplatine oder in einem 19" Gehäuse eingebaut werden.
Im Zuge der Layoutentwicklung wurde die Pinbelegung noch etwas umstrukturiert. Es sind noch einige Tests notwendig, aber wahrscheinlich gibt dann z.B. die Möglichkeit den Sound 3 stimmig zu machen. Mit Hilfe von SPI-Digital-Potis wäre dann auch eine Lautstärkesteuerung möglich. Und wer möchte, kann dann sogar eine MP3-Wiedergabe implementieren. SD-Kartensteuerung soll noch eingebaut werden.
Aber das nur zur Hardware. Wenn die Platine des BasicBeetle fertig ist, wird sie selbstverständlich vorgestellt.
Aber bei der Hardware ist es selbstverständlich nicht geblieben. Im Betriebssystem wurde noch ein wenig umstrukturiert, die Bibliotheken selbst noch etwas besser dokumentiert aber auch weitere Programmteile sind hinzu gekommen.
Zum Einen den Befehl SEED. Dieser setzt den internen Zähler für die Zufallszahlerzeugung auf einen bestimmten Wert. Damit ist die Erzeugung der Zufallszahl noch etwas besser möglich. SEED erwartet eine Zahl von 0 bis 65535.
Es wurde auch der CASE-Befehl erweitert. Nun können auch mehrere Möglichkeiten angegeben werden, bei der der CASE-Befehl 'zuschlagen' soll. Hierzu ein kleines Beispiel:
|
1500 SELECT ASC(KEY$) 1510 CASE 10,13:GOTO _SUBKEY 1520 CASE 65:GOTO _KEYA |
Ob diese Erweiterung drin bleibt, kann ich noch nicht sagen. Die Erweiterung kostet leider erheblich mehr Rechenzeit. Also nicht böse sein, falls dieses Feature bei der Veröffentlichung nicht mehr vorhanden ist.
Variablen-Gemetzel
Nachdem mein erster BasicBeetle 2 den Umzug ins Büro zum Opfer gefallen ist. Kann es nun mit einer neuen Platine weiter gehen.
Als erstes wurde begonnen die Variablenstruktur zu überarbeiten. Damit sind dann z.B. auch mehrdimensionale Variablen möglich. Hierzu ein Beispiel:
|
50 DIM WERT(50,2,9),STATES(50,3) |
Geplant ist es, bis zu 8 Ebenen möglich zu machen.
Ebenso soll die Deklaration von Variablen geändert werden. Da noch etliche weitere Variablentypen und auch Variablenstrukturen geplant sind, muss auf Identifier als Typerkennung verzichtet werden. Einige Standardtypen können weiterhin über '%', '@', '$' etc. festgelegt werden. Für einige neue Typen wird aber eine andere Definitionsmöglichkeit geschaffen:
|
50 DIM BYTE WERT,QWORD ANZ,REAL TEMP |
Hier wird also eine Variable vom Typ Byte mit den Namen WERT, eine Quad-Variable namens ANZ und TEMP als Fließkomma-Variable festgelegt.
Ist da noch ein Plätzchen für mich?
Bevor man andere Speicherseiten verwendet, möchte man natürlich wissen, ob diese überhaupt leer sind. Um dies zu prüfen wurde die FREE-Funktion erweitert. Gibt man nun die Seitennummer in Klammern an, wird von dieser Seite der freie Speicher ermittelt. Hierbei gibt es noch eine Besonderheit. Gibt FREE(x) einen Wert von 65535 zurück, wurde die Speicherseite noch nicht verwendet. Also weder als Programm noch als reine Datenseite.
Apropos Datenseite. Mit DPAGE kann man nun extra Datenseiten für Variablen anlegen. Wird dann im Programm die Variable gesucht, so startet BeetleBasic mit der aktiven Programmseite und schaltet dann auf, die im Programm definierten Datenseiten um. Um Variablen auf andere Seiten zu definieren, geht man wie folgt vor:
|
100 DIM CNT,VOLTS,CHANNEL 110 DPAGE=5 120 DIM OSZI(16383) 130 DPAGE=11 140 DIM LOGI(16383) |
Hierbei wird jedes Mal, nach dem Wechseln der Datenseite, beim Anlegen einer weiteren Variable, ein Marker mit einer Größe von 3 Byte angelegt. Man sollte also bei der Variablendefination sich genau überlegen, welche Variablen man auf welche Seite haben möchte um nicht die jeweiligen Variablenbereiche mit Markern zu 'überfluten'. Sofern man natürlich Variablen überhaupt auf andere Seiten auslagern möchte.
Aber auch für Variablenfelder gibt es wieder etwas. Ein sehr starker Befehl wurde in den Grundzügen implementiert. Der SORT-Befehl. Derzeit hat er folgende Syntax:
|
SORT <Feldvariable>,[<Feldobergrenze>] |
Hierbei wird der Quicksort-Algorithmus verwendet. Es sollen noch Slave-Variablen eingebunden werden, welche dann entsprechend mit sortiert werden. Es wäre ja sonst nicht so gut, wenn bei einer Transistordatenbank auf einmal ein BC548C einen Kollektorstrom von 25A verträgt und ein SOT23-SMD-Gehäuse besitzt.
Des Weiteren ist hier noch die Sortierrichtung geplant. Bisher wird nur vom Kleinsten zum Größten sortiert. Eventuell gibt es dann auch 2 Sortierbefehle. Das steht aber noch nicht fest.
Vorerst kann man sich aber mit einem weiteren Befehl aushelfen:
|
REVERSE <Feldvariable>,[<Feldobergrenze>] |
Hier wird das Array auf den Kopf gestellt. Das Letzte Element wird das Erste, das Vorletzte das Zweite usw. Wird eine Obergrenze angegeben, bleibt der Bereich über diese Grenze unangetastet.
Feld(Variablen) Angriff
Nach einigen Optimierungen, nun auch wieder ein paar kleine Ergänzungen.
Als erstes wurde der FILL-Befehl in AFILL umbenannt. Sonst kollidiert der Name doch ein wenig mit den neuen Befehlen FILLB, FILLW, FILLQ und XFILLB, XFILLW, XFILLQ. Mit diesen Befehlen können Speicherbereiche im Programm und erweiterten Speicher gefüllt werden.
FILLx <Startadresse>,<Anzahl>,<Wert> für den Programmspeicher und
XFILLx <Startadresse>,<Anzahl>,<Wert> für den erweiterten Speicher.
Die <Startadresse> muss wohl nicht groß erklärt werden, <Anzahl> gibt Anzahl der Elemente an, die geschrieben werden sollen. Wird der Befehl FILLB verwendet, bedeutet die Anzahl 100 auch 100 Byte. Bei FILLQ jedoch, bedeutet Anzahl von 100 das 400 Byte geschrieben werden.
Überschreitet ein FILL-Befehl das Speicherende, wird eine Fehlermeldung erzeugt.
Was bisher, bei der Array-Verwaltung, noch fehlte war das Einfügen und Entfernen von Elementen. Mit dem AINSERT und dem ADELETE-Befehl wurde dies nun nachgeholt.
AINSERT <Array>,<Position>,<Wert>: Hier wird im angegebenen Array an der Position der angegebene Wert eingefügt. Der Datentyp muss natürlich übereinstimmen. Alle nachfolgenden Elemente werden nach oben verschoben. Ist das Array komplett gefüllt, geht das oberste Element verloren.
ADELETE <Array>,<Position>: Das Element an der abgegebenen Position wird gelöscht. Nachfolgende Elemente werden herunter gezogen. Das oberste Element wird mit 0 bzw. mit einem Leerstring gefüllt.
Damit der BasicBeetle nun endlich auch die Trigonometrie beherrscht, wurden erste Routinen für die Fixkommabehandlung eingefügt. Hierzu entstand schon einmal die Systemvariable FIX. Dieser kann einen Wert von 0 bis 9 zugewiesen werden und legt somit fest, mit wie viel Nachkommastellen die Fixkommazahlen arbeiten sollen. Als Standard ist 2 voreingestellt.
Befehlsinterpreter? Bitte schön.
Nach längere Zeit der Schaffenspause, geht es nun weiter mit der Entwicklung des BasicBeetle 2.
Zu Beginn erst noch einmal eine kleinere 'Fingerübung'. Sehr häufig findet man im Programm ähnliche Sequenzen wie:
|
1430 IF {Bedingung} THEN WERT={Wert 1} ELSE WERT={Wert 2} 1430 IF {Bedingung} THEN T$="Text 1" ELSE WERT="Text 2" |
Diese Sequenzen können nun mit der neuen SWITCH bzw. SWITCH$-Funktion abgekürzt werden. Die oberen Beispiele würden nun so aussehen:
|
1430 WERT=SWITCH({Bedingung},{Wert 1},{Wert 2}) 1430 T$=SWICH$({Bedingung},"Text 1","Text 2") |
Wer Anwendungen schreibt, verwendet hier evtl. auch kleine Befehlsinterpreter um dem Programm direkte Befehle zu geben, oder man programmiert eine Sub-Sprache wie, zum Beispiel, ein SPS-System. Aber häufig werden auch so genannte Lookup-Tabellen benötigt, die der BasicBeetle bisher noch nicht direkt unterstützt. Mit READ-DATA muss man immer wieder von Vorne einlesen, wenn man ein bestimmtes Element sucht. Oder die Tabelle muss in eine Variable eingelesen werden, was unnötigen Speicherplatzbedarf bedeutet.
Nun gibt es 3 neue Befehle, die dieses erleichtern: LOOKUP, LOOKUP$, LOOKUPIS. Die Syntaxe sehen wie folgt aus:
|
WERT=LOOKUP({Position}) TXT$=LOOKUP$({Position}) {Position}=LOOKUPIS({Wert/Text}) |
Die Daten werden aus DATA-Zeilen ausgelesen, auf welche der aktuelle Zeiger von RESTORE zeigt. LOOKUP bzw. LOOKUP$ lesen einen Wert oder String aus der angegeben Position in den DATA-Zeilen aus. Der entsprechende Datentyp muss selbstverständlich stimmen. Ist die Position größer als vorhandene Werte, gibt es eine Fehlermeldung. Das erste Element hat hier die Nummer 1. Wird 0 als Position übergeben liefert LOOKUP 0 bzw LOOKUP$ einen Leerstring.
Mit LOOKUPIS kann man einen Wert in der Tabelle suchen. Wurde dieser gefunden, wird die Position zurück gegeben. Ansonsten ergibt die Funktion den Wert 0.
Einen Nachteil möchte ich nicht verschweigen. Da bei jeder Anwendung der LOOKUP-Funktionen die DATA-Zeilen entsprechend neu durchsucht bzw. abgezählt werden, sollte man diese Funktionen nicht dort einsetzen, wo es auf Geschwindigkeit ankommt.
Damit es noch verständlicher wird, hier ein kleines Demo:
|
>10 PRINT "Hallo ";LOOKUP$(3) >20 PRINT "Peter ist ";LOOKUPIS("Peter") >50 DATA "Hans","Dieter","Martin","Peter","Wolfgang","Heinz" >RUN Hallo Martin Peter ist 4 Ready > |
Multiprogrammierale Pagenierfunktionsimplementation
Bei großen Projekten wird es evtl. nötig, dass man alle Speicherseiten löscht. Hierfür gibt es nun den neuen Befehl ALLNEW. Dieser setzt alle gefundenen Seiten zurück.
Die Speicherseitenumschaltung mit PPAGE funktioniert nun einwandfrei. Ab sofort kann man mehrere Programme im Speicher halten. Der Variablenbereich wird derzeit noch auf die gewählte Seite verlegt. Also ist im Grunde jede Speicherseite für sich noch Autonom:
|
>10
PRINT "Dies ist ein Test" Ready >10 PRINT "Hallo, hier ist Seite 5" >PPAGE=0:RUN Dies ist ein Test Ready >PPAGE=5:RUN Hallo, hier ist Seite 5 Ready >LIST 10 PRINT "Hallo, hier ist Seite 5" Ready >PPAGE=0:LIST 10 PRINT "Dies ist ein Test" Ready > |
Es ist zwar möglich, durch die PNAME$-Funktion ein Programm in den Weiten der Speicherseiten zu finden. Dies ist aber trotz allem mühselig. Besser wäre es doch, wenn sich die einzelne Programme beim Basic 'anmelden' und so, wie ein Befehl aufgerufen werden können. Hierzu wurde der COMMAND-Befehl eingebaut. Will man ein Programm anmelden, so sieht dies z.B. so aus:
|
10 COMMAND "TEST",50:PRINT
"Programm wird mit 'TEST' gestartet.":END 50 PRINT "Programm wurde gestartet." |
Wird das Programm auf einer Speicherseite gestartet, so wird, durch den Befehl COMMAND, das Programm bei Basic angemeldet. Nun kann man jederzeit das Programm mit dem definierten Befehl starten. Der optionale zweite Parameter gibt an, ab welcher Zeile das Programm gestartet werden soll. Diese Angabe kann auch weg gelassen werden. Dann wird das Programm ab der 1. gefundenen Zeile gestartet.
Noch alles im Lot?
Bei vielen Anwendungen muss geprüft werden, ob ein Wert sich innerhalb eines bestimmten Bereiches befindet. Bisher wurde hierzu meist eine IF-Abfrage in folgender oder ähnlicher Form gemacht:
| 750 IF (TEMP>=20) OR (TEMP<=30) THEN GOTO ... |
Durch die neue Funktion BOUND wird dies nun einfacher:
| 750 IF BOUND(TEMP,20,30) THEN GOTO ... |
Der erste Parameter ist hierbei der zu überprüfende Wert, der zweite Parameter das Minimum und zuletzt wird das Maximum angegeben. Bei dem obigen Beispiel werden hierdurch 8 Bytes eingespart. Vom Geschwindigkeitsvorteil ganz zu schweigen.
Die Funktion BOTTOM wurde in ATOP umbenannt. BOTTOM ist ja sprachlich falsch. Die Befehle für den Import bzw. Export von Dateien/Speichermodulen wurden wieder entfernt. Dies erfolgt in Kürze ausschließlich durch TKTerm.
Was bei so genannten Hochsprachen schon zum guten Ton gehört, war beim BasicBeetle bisher nicht möglich. Dies hat nun auch ein Ende. Ab sofort kennt der BasicBeetle auch Einrückungen. Ein Beispielprogramm sah bisher so aus:
|
10 DIM A,B 20 FOR A=1 TO 9 30 FOR B=1 TO 9 40 PRINT A;" * ";B;" = ";A*B 50 NEXT 60 NEXT |
Dies kann man nun etwas besser gestalten:
|
10 DIM A,B 20 FOR A=1 TO 9 30 FOR B=1 TO 9 40 PRINT A;" * ";B;" = ";A*B 50 NEXT 60 NEXT |
Es ist hierbei zu bedenken, dass, wenn Einrückungen verwendet werden, je Zeile 2 Bytes mehr Speicherplatz benötigt werden.
Sehr häufig findet man bei IF-Anweisungen den Konstrukt 'THEN GOTO ...' oder 'ELSE GOTO ...'. Auf den GOTO-Befehl kann man zukünftig verzichten. Dies geht einfacher:
|
100 IF READY THEN GOTO 750
ELSE GOTO 1800
(Bisher) 100 IF READY THEN 750 ELSE 1800 (Jetzt möglich) |
Das Gleiche ist natürlich ebenso für den IFF-Befehl gültig.
Inzwischen wurden auch die Routinen für die unteren 4 kB RAM eingebaut. Auf diesen Bereich konnte man bisher ja aufgrund des internen RAMs nicht zugreifen. Durch diese Routinen ist dies möglich und dadurch können bald die Befehle für die Seitenumschaltung und somit die Verwaltung von mehreren Programmen im Arbeitsspeicher eingebaut werden.
Vorerst gibt es aber erst einmal 2 neue Befehle. Da man später bis zu 256 Speicherseiten RAM und evtl. bis zu weiteren 256 Speicherseiten Flash hat, wird es schwierig, sich zu merken, wo welches Programm, welche Routinesammlung und welche Datenseite ist. Mit dem neuen Befehl PNAME kann man jeder Speicherseite einen kurzen Text zuweisen:
|
>PNAME 3,"Hilfsroutinen fuer TKTerm" Ready > |
Die Parameter sind wohl nicht so schwer zu erraten. Der erste Wert gibt die gewünschte Speicherseite an und der folgende Stringteil den Text, der gespeichert werden soll. Bisher max. 255 Zeichen. Wird aber wohl später auf 63 Zeichen begrenzt.
Um diese Beschreibung später wieder auslesen zu können, wurde die PNAME$-Funktion implementiert. Als Parameter wird hier nur die gewünschte Seite benötigt und die Funktion gibt den gespeicherten Text zurück:
|
>PRINT PNAME$(3) Hilfsroutinen fuer TKTerm Ready > |
Will man nun eine Auflistung haben, was man alles im Speicher hat, kann man dies mit einer kurzen Sequenz machen:
|
>DIM P:FOR P=0 TO MAXPAGE:PRINT P;": ";PNAME$(P):NEXT 0: Logikus 2.0 - Logiktester und Analysestem 1: Desk 1.0 - Textorientierte Benutzeroberflaeche (Routinesammlung) 2: Datenseite fuer Desk 1.0 3: Hilfsroutinen fuer TKTerm 4: Discman 3.0 - Verwaltungsprogramm fuer Speichermodule 5: 6: 7: Programmdaten von Logikus 2.0 Ready > |
Mit dem baldigen Einbau der Seitenumschaltung ist dann auch der Grundstein für die Multitasking-Fähigkeit gesetzt.
Wenn die Seitenumschaltung, auch für Variablen, funktioniert, gibt es da ein Problem. Möchte man mehrere Datenseiten oder globale Variablen haben, so ist dies nicht so ohne Weiteres möglich. Es muss dann dafür gesorgt werden, dass Variablen auf der aktuellen Programmseite genauso beachtet werden, wie jene, die auf der aktuellen Datenseite abgelegt sind. Somit wurde eine neue Funktion eingeführt, welche abfragt, auf welcher Speicherseite das Programm gestartet wurde. Variablen werden dann auf der aktuellen DPAGE-Seite und auf der MPAGE-Seite gesucht:
|
>10 DIM
TXT$,NM$,ANZ
(Variablen werden auf der
Seite gespeichert, welche gestartet wurde) >30 PRINT MPAGE:TXT$="Hallo":WINX=43 >RUN 7 (Wenn sich das Programm auf Seite 7 befindet) Ready >PRINT TXT$,WINX Hallo 43 Ready > |
Der BasicBeetle und der Rest der Welt
Ein großes Problem ist es, dass man keine Daten-Dateien vom bzw. zum BasicBeetle empfangen/senden kann. Mit Hilfe von 4 neuen Befehlen hat dies bald ein Ende. FEXPORT oder FIMPORT ermöglichen das Exportieren oder Importieren einzelner Dateien zum/vom PC, während DEXPORT oder DIMPORT ganze Module exportiert oder importiert.
Die entsprechenden Gegenroutinen werden in TKTerm eingebaut. Das Übertragungsformat ermöglicht es auch, dass solche Dateien in Schriftform beliebig verteilt werden können. Es ist also ein Posten in Foren etc. dann möglich.
Das Ganze sieht dann ungefähr so aus:
|
>FEXPORT
"WORDHANG.TWR"
(Dieser Befehl wird
automatisch von TKTerm gesendet) |
Durch entsprechende Programme werden diese neuen TKTerm-Funktionen auch mit dem BasicBeetle 1.xx verwendbar.
Aber auch neue String-Spielereien gibt es. Mit der COUNT-Funktion wird die Anzahl eines bestimmten Zeichens in einem String gezählt:
|
>PRINT
COUNT("e","Dies ist der BasicBeetle") Ready > |
Wird der erweiterte Speicher fleißig verwendet, müssen öfter Speicherbereiche kopiert werden. Um größere Speicherblöcke zu kopieren, gibt es nun den MOVE und XMOVE-Befehl. MOVE ist auf dem Programmspeicher anwendbar während XMOVE für den erweiterten Speicher gedacht ist:
|
>XMOVE
&H1000,&HA000,&H800 Ready > |
Da die Fehlermeldung 'Subscript out of Range' bei Überschreitung der Speichergrenze nun nicht wirklich passend und z.B. auch 'Memory full' es nicht wirklich ist, wurde eine weitere Meldung implementiert:
| Error 29: Memory range overlapped |
Darf's ein paar Befehle mehr sein?
Bei größeren Anwendungen ist man oft in der Verlegenheit, dass z.B. eine Sicherheitsabfrage machen muss oder die Routine nur einige wenige Tasten zulässt, wie z.B. Cursortasten. Bisher musste die Tastaturabfrage immer mit diversen IFs etc. ausgewertet werden. Dies wird nun mit der FETCH$-Funktion einfacher. Als Parameter wird eine Liste mit erlaubten Tasten übergeben. Die Funktion wird nur dann verlassen, wenn eine der angegebenen Tasten betätigt wird. Eine Sicherheitsabfrage könnte dann so aussehen:
| 100 IF UPPER$(FETCH$("jJnN"))="J" THEN GOTO ... |
Aber auch kommt es immer wieder vor, dass man auf eine Tastenbetätigung warten muss. Bisher musste dafür eine Schleife aufgebaut werden. Mit der neuen Funktion WAITKEY$ wird dies nun einfacher:
|
100 DO:K$=INKEY$:WHILE K$=""
(Bisher) 100 K$=WAITKEY$ (Jetzt möglich) |
Während der Ausführung von FETCH$ und WAITKEY$ werden keine Interrupts ausgeführt. Sind also AFTER oder EVERY-Timer aktiv, so muss die Tastaturauswertung in herkömmlicher Weise gestaltet werden.
Neben dem Einfügen von Strings in einen anderen String durch INSERT$, wird auch eine Routine nötig, die Stringteile ersetzt. Hierfür gibt es die REPLACE$-Funktion:
|
>PRINT
REPLACE$(14,"Basic","Dies ist der ForthBeetle") Ready > |
Was beim BasicBeetle teilweise schon massiv vermisst wird, ist die Möglichkeit, zumindest einfache Töne auszugeben. Dank einiger freier Pins und Umstrukturierung der Timer-Belegung, ist dies nun auch möglich. Mit Hilfe des SOUND-Befehls können nun einfache Ton- und Geräuschfolgen an Pin B7 ausgegeben werden. Um die gewünschte Ausgabefrequenz zu berechnen muss man die folgende Formel anwenden: Wert=62500 / Frequenz / 2. Wird als Wert 0 übergeben, wird die Soundausgabe gestoppt. Der Ton wird so lange gehalten, bis ein anderer Wert gesetzt wird.
Somit ist es z.B. möglich, ein Martinshorn nachzubilden:
|
>DO:SOUND
53:DELAY 75:SOUND 71:DELAY 75:WHILE INKEY$="":SOUND 0 Ready > |
Hier noch einmal eine kleine Melodie:
|
10 DIM N,L 20 SOUND 0:READ N,L:SOUND N:IF L=65535 THEN RESTORE 10000: GOTO 20 30 DELAY 200/L: GOTO 20 10000 REM *** Abba: Money Money *** 10010 DATA 36,9,32,8,30,8,36,8,32,8,30,8,0,4,30,8,36,8,32,8,30,8,0,4,32,8 10020 DATA 36,8,30,8,30,8,0,8,36,8,0,1 65535 DATA 0,65535 |
Leider muss man beachten, dass, wenn die Soundausgabe läuft, die Rechenleistung im Durchschnitt um 20% abnimmt. Daher sollte man Soundausgaben, bei rechenintensiven Routinen unterlassen.
Bei Datenreihen ist es unter Umständen nötig, dass man wissen muss, welche Maximal- bzw. Minimalwerte es in der Datenreihe gibt, um z.B. die Datenreihe in einer Grafik zu zeigen. Um diese Werte schnell zu ermitteln, gibt es nun die Funktionen HIGHEST und LOWEST.
HIGHEST liefert den Maximumwert der angegeben numerischen Feldvariable. LOWEST dem entsprechend den Minimumwert. Wurde die Feldvariable nicht voll belegt, so kann man mit einem optionalen 2. Parameter eine Obergrenze der Elemente angeben.
|
>DIM
T(9),C:FOR C=0 TO 9:T(C)=RND:NEXT:PRINT HIGHEST(T) Ready >PRINT LOWEST(T) 985 Ready >PRINT LOWEST(T,3) 48101 Ready > |
Mit Volldampf geht es weiter
Wer C kennt, der kennt wahrscheinlich auch die einfache Schreibweise für das Bitweise links oder rechts schieben. Im Beetle-Basic ist das nun auch möglich. Anstelle SHL oder SHR kann ab sofort auch << oder >> verwendet werden.
Damit auch kompliziertere String-Ausdrücke einwandfrei und auch geschachtelt ausgeführt werden können, wurde ein String-Stack eingerichtet. Dieser wächst vom Ende der aktuellen Programmseite nach unten. Damit der Stack keine Daten oder Programm zerstört, wurde eine neue Fehlermeldung eingebaut:
| Error 28: String stack overflow |
Man sollte also immer ein paar Bytes, empfohlen sind min. 256 Bytes, im Programmspeicher frei lassen, sobald man im Programm Stringfunktionen verwendet.
Um den String-Stack anzuwenden gibt es auch gleich mehrere neue Funktionen.
Zum Beispiel gibt es jetzt die INSERT$-Funktion. Hier wird ein String in einen anderen eingefügt und das Ergebnis zurück gegeben:
|
>PRINT
INSERT$(14,"Basic","Dies ist der Beetle") Ready > |
Der erste Parameter gibt die Position an, an dem der Substring eingefügt werden soll. Ist die Position größer als die Länge des Zielstrings, so wird der Substring an den String angehängt. Der zweite Parameter ist der Substring, welcher in den 3. Parameter eingefügt wird.
Manchmal kommt man in die Verlegenheit, dass in einem String eines von mehreren Zeichen gesucht werden muss. Hierfür gibt es jetzt die Funktion ANY:
|
>PRINT
ANY(" ,:","cls:print free") Ready > |
Im ersten Parameter wird ein String übergeben, welche die gewünschten Suchzeichen enthält die im 2. String gesucht werden soll. Die Funktion liefert die erste Position zurück an dem eines der Suchzeichen gefunden wurde. Ergab die Suche kein Ergebnis, wird 0 zurück geliefert.
Da es in Kürze weitere Datentypen gibt, musste überlegt werden, wie man dem Basic sagt, dass man nun diesen oder jenen Datentyp haben möchte. In vielen Basic-Interpretern wird dies einfach so geregelt, dass jede Zahl erst einmal Fließkomma ist und dann entsprechend umgerechnet. Dies kostet aber immens viel Rechenzeit. Daher wurde nun ein System entworfen, damit man dem Basic sagt, dass es ein bestimmter Datentyp ist.
Die Zahl, welche z.B. vorzeichenbehaftet im Programm geschrieben werden soll, wird in geschweiften Klammern geschrieben. Der Zahl wird dann das gewünschte Zahl-Identifikationszeichen angehängt. Somit wäre es z.B. auch möglich, eine Zahl die als Byte erkannt wird, zwingend als Quad ins Programm zu schreiben:
|
100 Z~={-342~}
Weist der Integer-Variablen Z den Integer-Wert -342 zu 100 PRINT {1.75!}*4 Gibt das Ergebnis von 1,75*4 zurück |
Bei einigen Anwendungen ist es nötig, die Summe eines Datenfeldes zu berechnen. Damit dies nicht etliche Sekunden dauert, wurde die Funktion SUM entwickelt:
|
>DIM
Z(3):Z(0)=7:Z(1)=65:Z(2)=812:Z(3)=34:PRINT SUM(Z) Ready > |
Aber nicht nur die Summe eines Datenfeldes muss öfters gebildet werden. Häufig kommt es auch vor, dass man den Durchschnitt eines Feldes haben muss. Mit der Funktion AVERAGE wird dieser berechnet:
|
>DIM
Z(3):Z(0)=7:Z(1)=65:Z(2)=812:Z(3)=34:PRINT AVERAGE(Z) Ready > |
Immer wieder ist es vonnöten, dass eine Feldvariable komplett mit einem bestimmten Wert beschrieben werden muss, um es z.B. zurück zu setzen. Nun kann man dies mit einer FOR-NEXT-Schleife und dem manuellen Zuweisen der einzelnen Feldvariablen erledigen. Dies ist nicht nur rechenintensiv, sondern kostet auch Speicherplatz. Mit dem neuen Befehl FILL kann man Feldvariablen in einem Rutsch mit einem Wert beschreiben:
|
>DIM
B&(9):FILL B&,147:PRINT B&(5) Ready >DIM W(9):FILL W,34175:PRINT W(5) 34175 Ready >DIM Q@(9):FILL Q@,912364:PRINT Q@(5) 912364 Ready >DIM T$(9):FILL T$,"BasicBeetle":PRINT T$(5) BasicBeetle Ready > |
Was bei älteren Basic-Versionen oft zu finden war, waren die so genannten Funktionsvariablen. Solche sind nun auch beim BasicBeetle, in etwas komfortablere Version, möglich. Definiert werden diese Variablen über den DEFFN-Befehl. Bei der Definition ist darauf zu achten, dass der angegebene Ausdruck auch ausführbar ist. Die beiden Beispiele zeigen einmal eine Dreiecksberechnung und eine einfache String-Anwendung:
|
>10
DIM A,B:DEFFN C=SQRT((A*A)+(B*B)) >30 PRINT "C = ";C >RUN A = 100 B = 200 C = 224 Ready
>10
DIM N$:DEFFN T$="Guten Tag, "+N$ >30 PRINT T$ >RUN Name: Herbert Guten Tag, Herbert Ready > |
Damit ist es nun auch möglich, sich eigene Systemfunktionen zu definieren:
|
>10
DEFFN BASSIZE=PEEKW(&H0156)-LOMEM >RUN Aktuelle Programmgroesse : 75 Bytes Ready > |
Die Zeit wird's zeigen
Der BasicBeetle verfügt nun auch über eine softwaregesteuerte Hintergrunduhr. Gesteuert wird diese über die Systemvariable TIME$. Beim Reset oder Neustart ist die Uhr deaktiviert. Sie wird erst aktiviert, wenn der TIME$-Variablen eine Uhrzeit in Form eines Strings mit dem Inhalt 'hh:mm:ss' übergeben wird. Bei dieser Zuweisung ist man aber sehr flexibel:
|
>TIME$="16:45:30"
Setzt die Uhrzeit auf
16:45:30 >TIME$="7:8:3" Setzt die Uhrzeit auf 07:08:03 Ready >TIME$="16:45" Setzt die Stunden und die Minuten. Sekunden bleiben unverändert Ready >TIME$="16" Setzt die Stunden. Minuten und Sekunden bleiben unverändert Ready >TIME$="" Deaktiviert die Hintergrunduhr Ready >PRINT TIME$ 16:45:37 Ready > |
Durch die neuen Funktionen MKB$, MKW$ und MKQ$ können numerische Werte im Binärformat in Strings abgelegt werden. So können Daten Platz sparend komprimiert in Strings gespeichert werden.
Um die Daten wieder auszulesen, wurden die Funktionen CVB, CVW und CVQ implementiert. Ein kleines Beispiel verdeutlicht dies:
|
>DIM
T$ >T$=MKB$(47) Ready >PRINT LEN$(T$) 1 Ready >PRINT CKB(T$) 47 Ready >T$=MKQ$(453197) Ready >PRINT LEN(T$) 4 Ready >PRINT CVQ(T$) 453197 Ready > |
Es gab immer wieder das Problem, dass man Strings an einer bestimmten Position ausgeben musste. Also linksbündig, rechtsbündig oder zentriert. Hierfür sind in Basic recht umfangreiche Anweisungen vonnöten, um dies zu ermöglichen. Mit LSET$, CSET$ und RSET$ ist dies nun nicht mehr nötig. LSET$ setzt den Text linksbündig, CSET$ zentriert den Text und RSET$ formatiert den Text rechtsbündig.
Diese drei Funktionen benötigen zwei Parameter. Zum einen die gewünschte Ziel-Stringlänge und zum anderen den eigentlichen String. Auch hierzu entsprechende Beispiele:
|
>10 PRINT
LSET$(10,"Text");"|" >30 PRINT RSET$(10,"Text");"|" >RUN Text | Text | Text| Ready > |
Wenn Daten in Arrays gesucht werden, muss dies bisher mit einer Basic-Routine geschehen. Bei umfangreichen Datenfeldern, kann dies unter Basic eine Weile dauern. Jetzt gibt es dafür die SEARCH-Funktion:
| Fund=SEARCH(<Argument>,<Array>[,<Start>]) |
Das Argument kann beliebigen Typs sein. Dieses muss natürlich mit dem Typ des Array-Feldes übereinstimmen. Ganzzahlentypen werden bei der Suche angeglichen. Durch das optionale Argument Start wird festgelegt, bei welchem Feldelement die Suche beginnen soll. So können z.B. mehrere Elemente im Feld gesucht werden. Wird das Argument weg gelassen, so beginnt die Suche beim Element 0. Wurde das gesuchte Element nicht gefunden, so wird 65535 zurück gegeben.
Da zukünftig noch weitere Array-Befehle folgen, wird auch hier noch eine weitere Fehlermeldung nötig:
| Error 27: Array needed |
Da es, spätestens nach der Implementierung des REDIM-Befehls, nötig sein kann, zu erfahren, wie viele Elemente eine Feldvariable hat, wurde die BOTTOM-Funktion entworfen. Sie liefert die Nummer des höchsten Feldelementes zurück:
|
>DIM
T(999) >PRINT BOTTOM(T) 999 Ready > |
Was immer wieder massiv gestört hat, war es, dass man Strings nur auf Gleich oder Ungleich prüfen konnte. Damit ist nun Schluss. Jetzt können auch Strings auf Größe verglichen werden. Somit können also einige Befehle erheblich einfacher aufgebaut werden:
|
100 IF (ASC(K$)<48) OR (ASC(K$)>57)
THEN GOTO ...
(Bisher) 100 IF (K$<"0") OR (K$>"9") THEN GOTO ... (Jetzt möglich) |
Sollen laufende Programme abgebrochen werden, so hatte man bisher 2 Möglichkeiten. Zum Einen durch CTRL+C wodurch das Programm sofort gestoppt wird oder per Programmsteuerung wobei man sich hier aber erst durch ein Menü etc. hangeln muss. Soll das Programm nun aber jederzeit beendet werden können, aber nicht abrupt abbrechen, so kann dies nun über den 'ON BREAK GOSUB'-Befehl geschehen. Hierbei kann ein Programm jederzeit unterbrochen werden und hat trotzdem die Sicherheit z.B. keine Daten zu verlieren. Nun kann, bei laufender BREAK-Routine, problemlos der LOCKC-Befehl gesetzt werden. LOCKC hat keinen Einfluss auf den Break-Interrupt.
|
>10
DIM K$*1:ON BREAK GOSUB 100 >RUN **************** (Irgendwann CTRL+C betätigen) Willst du mich wirklich beenden? (J/N) |
Um die Break-Überwachung wieder zu deaktivieren, muss als Sprungziel die Zeilennummer 0 angegeben werden.
Sollen Daten übertragen werden, benötigt man eine Kontrollfunktion. Eine einfache Möglichkeit hierfür ist die Bildung der Parität aus den Daten. Hierfür gibt es nun die PARITY-Funktion:
|
>PRINT
PARITY(7) Ready >PRINT PARITY(132) 0 Ready > |
Dabei ergibt ein Wert von 0 eine gerade Parität (Even) und 65535 eine ungerade Parität (Odd).
Jetzt geht's richtig los
Nachdem die Probleme mit dem Speicher gelöst sind, kann die Weiterentwicklung endlich voll beginnen. Als erstes wurde der Quellcode noch weiter aufgeräumt und in kleinere Module aufgeteilt um Routinen besser zu finden.
Dann wurde die Steuerung der Status-LED wieder implementiert. Diese zeigt den aktuellen Zustand des Prozessors an und wird durch eine Duo-LED realisiert, welche die sonst übliche Power-LED im Modul ersetzt. Durch 3 Farben kann man den Status erkennen:
Grün: Der BasicBeetle befindet sich im Direktmodus und wartet auf Eingaben.
Orange: Es wird ein Programm abgearbeitet. Dieses kann jederzeit mit CTRL+C unterbrochen werden.
Rot: Der Computer ist voll beschäftigt und kann zur Zeit nicht unterbrochen werden.
Ebenso wurde die Zeilenverwaltung erweitert und es ist nun möglich auch Sprungmarken einzusetzen:
|
10 DIM A@ 20 GOSUB _AUSGABE:INC A@:GOTO LINE 100 _AUSGABE 110 PRINT CR$;"Zahl ";A@;:RETURN |
Hierbei muss die Sprungmarke als erstes in der Zeile stehen und mit einem Unterstrich beginnen. Es können auch direkt nach der Sprungmarke gleich weitere Befehle folgen. In diesem Fall müssen die Marke und die Befehle durch einen Doppelpunkt getrennt werden.
|
10 DIM A@ 20 GOSUB _AUSGABE:INC A@:GOTO LINE 100 _AUSGABE:PRINT CR$;"Zahl ";A@;:RETURN |
Damit es möglich ist, dass Marken z.B. auch in DATA-Zeilen verwendet werden können, z.B. für Menüverwaltung, liefert die Abfrage der Sprungmarke die Zeilennummer in der die Marke gefunden wurde:
|
>PRINT
_AUSGABE > |
Es gibt ebenso einer Ergänzung bei der Eingabe-Routine. Wird z.B. der INPUT-Befehl ausgeführt, war ein Programmabbruch durch CTRL+C nicht möglich. Dieses Problem wurde nun behoben.
Und zu guter Letzt für heute gibt es noch eine neue Funktion. Mit USING$ können ganze Zahlen in Tabellenform ausgegeben werden. Durch diese Funktion ist es ebenso möglich, aus ganzen Zahlen eine Fixkomma-Zahl zu machen:
|
>PRINT
USING$(12345,"Es gab ########## Ereignisse") >PRINT USING$(12345,"Kontostand: ########.## Eur") Kontostand: 123.45 Eur Ready >PRINT USING$(5,"Kontostand: ########.## Eur") Kontostand: 0.05 Eur Ready > |
USING$ machte es nötig, dass eine weitere Fehlermeldung implementiert werden musste:
| Error 26: String structure corrupt |
Diese Meldung erscheint immer dann, wenn ein String-Aufbau nicht verwertbar ist. Im Falle von USING$ würde diese ausgelöst, wenn man z.B. keinen Platzhalter '#' angibt oder die Anzahl der Platzhalter geringer sind als die vorhandenen Ziffer der Zahl.
BasicBeetle zieht erneut um
Nachdem etliche Versuche das RAM an einen ATmega644 (und später evtl. ATmega1284) anzusteuern gescheitert sind und ich keine vernünftige Arbeitsgeschwindigkeit mit der manuellen RAM-Ansteuerung hin bekomme, wurde nun das Ganze mit einem ATmega128 versucht. Dieser besitzt ein XRAM-Interface, wodurch ich im Betriebssystem nur sehr wenig anpassen muss.
Damit man diesen Prozessor auch auf einem Steckboard oder später im Modul besser verwenden kann, habe ich eine Adapterplatine entworfen, welche es wohl auch bald im Shop gibt. Wer nicht SMD-löten kann und später dennoch diese Version haben möchte, dem werde ich wohl noch den Bestückungsdienst anbieten müssen. Aber das Problem werde ich erst in einigen Monaten haben und so kann ich mir evtl. noch etwas einfallen lassen.
Jedenfalls lief der BasicBeetle 2.0 auf dem ATmega128 mit ein paar ganz geringen Änderungen nahezu auf Anhieb. Nur die Ports und die Timer würden geringfügig angepasst. Alleine dadurch ergab es schon wieder eine kleine Geschwindigkeitssteigerung von 0,24% da bei diesem AVR der Prescaler einen günstigeren Teilungsfaktor besitzt als der ATmega162 und somit die CPU weniger belastet.
Nachdem der Speicher verdrahtet wurde, klappte es hier auch auf Anhieb. Es werden sauber die 512 kB RAM erkannt. Auch evtl. den Maximalausbau von 16 MB erkennt er einwandfrei. Hier dauert der Test zwar fast 30 Sekunden. Aber, wenn man bedenkt, dass hier während des Test über 132 Millionen Zugriffe auf das RAM stattfindet, ist es doch schon recht flott. Und die wenigsten werden wohl den Speicher voll ausbauen.
Die Speichermeldung wurde geändert. Nun kann man das Testen des Speichers verfolgen und die Endmeldung wurde ebenso angepasst. Bei 512 kB sieht die Startmeldung nun so aus:
|
**** BasicBeetle 2.00 **** (C) 2007-2010 by Thomas Krueger 489472 bytes in 8 pages available Ready > |
Da es, bedingt durch den internen RAM, keinen linearen Speicher geben kann, wurde die Variante mit den Speicherseiten gewählt. Zwar fehlen für das direkte Basic je Speicherseite erst einmal 4 kB. Diese sind aber nicht verloren. Dieser Speicher ist später beim Multitasking-Betrieb noch sehr hilfreich. Hier können, durch manuelle Ansteuerung (siehe Datenblatt), die Task-Daten abgelegt werden und so muss kein Speicher im sichtbaren Bereich belegt werden.
Als erstes wurde die Verwaltung des erweiterten Speichers eingerichtet. Mit der Variablen XPAGE kann man nun wählen welche Speicherseite für den erweiterten Speicher verwendet werden soll. Ebenso kann mit dieser Variablen die aktuelle Speicherseite abgefragt werden. Soll eine Seite aktiviert werden, als wirklich vorhanden ist, gibt es eine neue Fehlermeldung:
| Error 25: Page not available |
Durch MAXPAGE kann man die höchst mögliche Speicherseite abfragen. Beim Systemstart wird für XPAGE automatisch 1 festgelegt.
Die Systemvariablen für das Wechseln der Programmseite (PPAGE) und der erweiterten Daten (DPAGE) wurden zwar implementiert und können auch gesetzt und abgefragt werden. Besitzen aber bisher noch keine weitere Funktion.
Der Systemstack (Für GOSUBs, FOR-NEXT, DO etc.) wurde auf eine Tiefe von 64 erhöht.
Ja, wo isser denn?
Nachdem ich feststellen musste, dass der AD-Wandler nicht nur gestoppt, sondern komplett deaktiviert werden muss, arbeite ich nun an der Erkennungs- und Testroutine für den Speicher. Linearen Speicher erkennt er schon sehr gut. Sieht schon nett aus, wenn der Beetle 512 kB RAM meldet. Jetzt muss ich ihn nur noch dazu bekommen, dass er auch segmentierten Speicher erkennt. Die 'Dummy'-Befehle WRAM und RRAM werde ich wohl drin lassen. Könnten später ganz hilfreich sein. So könnte der Anwender den Speicher später auch linear nutzen, z.B. für MicroData etc.
Die Divisionsroutine wurde ausgetauscht und arbeitet nun noch etwas schneller.
|
10 DIM A@:CLS 20 PRINT "*** BasicBeetle 2.00 ***" 30 PRINT "(C) 2007-2012 by Thomas Krueger" 40 FOR A@=0 TO (2^32)-1 50 IF A@%1024=0 THEN PRINT CR$;A@/1024+1;" kB Testing..."; 60 WRAM A@,&B10101010:IF RRAM(A@)=255 THEN BREAK 90 70 WRAM A@,&B1010101:IF RRAM(A@)<>&B1010101 THEN GOSUB 100 80 WRAM A@,0:NEXT 90 PRINT CR$;A@-1/1024+1;" kbytes XRAM found":END 100 PRINT:PRINT "Error in XRAM at ";RIGHT$(8,"0000000"+HEX$(A@)):RETURN |
Husch, husch. Ab in den Speicher
Die Verdrahtung der Adress-Latches und eines 512 kB RAMs wurde fertig gestellt. Wird der Speicher nun mit Einzelbefehlen angesteuert, funktioniert alles perfekt. Nur wenn ich, selbst im 'langsamen' Basic, den Speicher ansteuern will, kommt alle paar kB Müll. Vermute, dass durch die längeren Leitungen die Kabelkapazität zu hoch ist. Werde die Tage mal einen Bustreiber zwischen schalten.
Hier einmal ein kleines Testprogramm für den Speicher (Läuft nicht auf Beetle 1.xx):
|
10 DIM A@:CLS 20 PRINT "*** BasicBeetle 2.00 ***" 30 PRINT "(C) 2007-2012 by Thomas Krueger" 40 FOR A@=0 TO (2^32)-1 50 IF A@%1024=0 THEN PRINT CR$;A@/1024+1;" kB Testing..."; 60 WRAM A@,&B10101010:IF RRAM(A@)=255 THEN BREAK 90 70 WRAM A@,&B1010101:IF RRAM(A@)<>&B1010101 THEN GOSUB 100 80 WRAM A@,0:NEXT 90 PRINT CR$;A@-1/1024+1;" kbytes XRAM found":END 100 PRINT:PRINT "Error in XRAM at ";RIGHT$(8,"0000000"+HEX$(A@)):RETURN |
Die ersten Gehversuche
Nach dem Beheben der 57 Fehlermeldung beim Assemblieren auf dem ATmega644, konnte der BasicBeetle das erste mal gestartet werden. Auf Anhieb gab es die Startmeldung und Eingaben konnten gemacht werden. Selbstverständlich wurde kein Speicher erkannt, da es kein XRAM gab.
XRAM wurde künstlich durch einen Teil des internen RAMs simuliert und so stehen schon einmal 3 kB zum 'rumspielen' zur Verfügung.
Die Timer wurden angepasst und arbeiten nach ersten Tests wieder sehr genau. Bei einem Timer konnte die Struktur geändert werden und wird so noch etwas Resourcensparender abgearbeitet.
Die Funktionen für die Status-LED wurden eingebaut und arbeiten sehr zufrieden stellend. In einigen Routinen muss die Steuerung noch ergänzt werden.
Dummy-Routinen für die Steuerung des RAMs wurden erstellt. Somit kann die Speicherverwaltung getestet werden, bevor das ganze System entsprechend umgeschrieben wird.