Dies ist eine alte Version des Dokuments!


MPBASIC

REIHE AUTOMATISIERUNGSTECHNIK Band 216

Programmieren mit BASIC Siegmar Müller

1. Auflage © VEB Verlag Technik, Berlin, 1985

S. 60 ff.

7. Tiny MPBASIC

Tiny1) BASIC ist eine aus BASIC abgeleitete, sehr stark vereinfachte Programmiersprache. Die Vereinfachungen bestehen im wesentlichen in folgenden Punkten:

  1. Es sind nur die Variablen A … Z zugelassen.
  2. Die einzige Datenart sind ganze Zahlen.
  3. Es gibt keine Felder.
  4. Es gibt keine Strings.
  5. Tiny BASIC hat weniger Anweisungen.
  6. Es fehlen eine Reihe von numerischen Funktionen.

Diese Vereinfachungen gestatten es, Interpreter für Tiny BASIC mit sehr geringem Speicherbedarf zu schreiben. Der Speicherplatz für einen „normalen„ BASIC-Interpreter liegt zwischen 8 bis über 20 KByte, während man Interpreter für Tiny BASIC in 2 … 4 KByte unterbringt. Die ausschließliche Verwendung ganzzahliger Variablen und Konstanten ergibt dazu eine verhältnismäßig schnelle Arithmetik. Diese beiden Vorteile stehen dem Nachteil des geringeren Komforts gegenüber. Damit ergeben sich für Tiny BASIC andere Anwendungsbereiche als für ein „großes“ BASIC. Man setzt diese Sprache in sehr kleinen Rechnern mit wenig Speicherplatz zum Steuern und Regeln ein. Dabei ist man durchaus in der Lage, auch Echtzeit Probleme zu lösen.

Ein Echtzeitproblem besteht dann, wenn der Rechner auf ein externes Ereignis innerhalb einer vorgegebenen Zeit in einer diesem Ereignis entsprechenden Art und Weise reagieren muß. Ob eine Sprache mit einem gegebenen Rechner echtzeitfähig ist, hängt also auch von dem zu lösenden Problem ab. Wenn es um Zehntelsekunden geht, dann ist Tiny BASIC oft schon einsetzbar, während man im Mikrosekundenbereich nur mit Maschinenprogrammen Herr der Lage bleiben kann.

Ein generelles Problem bei der Anwendung höherer Programmiersprachen in Echtzeitproblemen besteht darin, daß es nicht möglich ist, wie bei der Assemblerprogrammierung, die Abarbeitungszeit exakt zu berechnen, In der Praxis genügt es aber fast immer, diese Zeit zu messen. Dabei sollte man jedoch genau überlegen, mit welchen Eingangsdaten man bei dem zu testenden Programmteil den ungünstigsten Fall erzeugt. Dadurch wird sichergestellt, daß sich nicht bei anderen Daten eine längere Abarbeitungszeit ergibt. Zum Messen genügt meist schon die Armbanduhr, wenn man das zu testende Programmstück in eine Schleife faßt. Zum Beispiel:

10 FOR I=1 TO 1000
programmstück
1000 NEXT I

Mißt man hier x Sekunden, dann dauert die Abarbeitung des Programmstücks etwas weniger als x Millisekunden. (Weniger, weil FOR und NEXT noch hinzu kommen)

In der Praxis sind jedoch meist nur wenige Programmstellen zeitkritisch, und Tiny BASIC läßt hier noch die Möglichkeit offen, auf die Maschinenebene auszuweichen (CALL, Interrupts im Hintergrund), wenn das eben Vorgeschlagene nicht zu befriedigenden Ergebnissen führt. Hat man nach eingehender Prüfung entschieden, Tiny BASIC einzusetzen, dann wird man wesentlich schneller zu einem fertigen Programm kommen als mit dem Assembler.

Wie bei BASIC gibt es auch bei Tiny BASIC eine kaum überschaubare Vielzahl von Versionen. Tiny MPBASIC (winziges Mikroprozessor-BASIC) wurde ursprünglich für den Einchipmikrorechner U883 geschrieben, kann aber prinzipiell auch für jeden anderen Prozessor implementiert werden. Zum Verständnis der folgenden Ausführungen sind dennoch einige Grundkenntnisse über den Aufbau und die Programmierung des Einchipmikrorechners U881 erforderlich. Hierzu sei z. B. auf [11] verwiesen. Wegen des begrenzten Programmspeichers (2 KByte) wurde im U883 lediglich ein lnterpreterprogramm untergebracht, das ein fertiges, sowohl syntaktisch als auch semantisch fehlerfreies Programm abarbeiten kann. Es fehlen die im Abschn. 5. behandelten Kommandos zum Editieren und Testen. Sie werden von einem externen Programm mit der Bezeichnung „Editor/Debugger„ realisiert. Es kann in dem. mit dem U883 realisierten System implementiert sein, oder auf einem Wirtsrechner laufen, Diese Trennung ermöglichte die Schaffung eines verhältnismäßig leistungsfähigen Interpreters, während der Editor/Debugger in fertigen Geräten (mit fertig entwickeltem und getestetem Programm) entfallen kann. Der Interpreter gestattet die Einbindung von Programmen in Maschinensprache. Wir sprechen hierbei von Prozeduren. Eine Prozedur in Tiny MPBASIC ist ein in Maschinensprache geschriebenes Programm, das Daten vom Interpreter übernimmt, verarbeitet, Daten an ihn zurückgibt und mit einem Namen aufgerufen werden kann. Eine Funktion ist demzufolge eine Prozedur, die genau einen Wert an den Interpreter zurückgibt. Die Datenübergabe wird vom Interpreter unterstützt. Die Zuordnung des Prozedurnamens zur Startadresse des Maschinenprogramms erfolgt vermittels einer Tabelle, der Prozedurtabelle. Sie ist vom Nutzer zu erstellen. Dadurch können mit diesem Tiny BASIC, trotz Einbeziehens der Maschinenebene, maschinenunabhängige Programme verfaßt werden. Beim Übergang von einem Rechner auf einen anderen genügt es, die benutzten Prozeduren neu oder umzuschreiben, während das BASIC- Programm unverändert übernommen werden kann. Neben der Möglichkeit, selbst Prozeduren (und Funktionen) zu erstellen, bietet der Interpreter dem Anwender eine Reihe bereits fest vorgegebener Prozeduren und Funktionen. Tiny MPBASIC gestattet darüber hinaus die Verarbeitung von Traps (Fallen). Das sind Programmunterbrechungen, die beim Erfülltsein einer gewissen Bedingung vom Interpreter softwaremäßig ausgelöst werden. Die Bedingung wird vom Programmierer festgelegt (s. Abschn. 7.3.2.). Ein weiteres Problem bei der Schaffung eines möglichst geräteunabhängigen Interpreters sind die Eingabe- und die Ausgabeschnittstellen für INPUT bzw. PRINT. Um hier alle Möglichkeiten offen zu lassen, ruft PRINT ein Unterprogramm PUTCHR auf, das ein Zeichen ausgibt und vom Anwender des U883 erstellt wird. Wenn PRINT eine Zeile ausdruckt, dann wird PUTCHR so oft aufgerufen, wie Zeichen in dieser Zeile sind. Zum Einlesen von Zahlen ruft INPUT ein, ebenfalls vom Anwender geschriebenes, Programm GETCHR auf, das bei jedem Aufruf ein Zeichen einliest.

Wir unterscheiden in Tiny MPBASIC arithmetische und logische Ausdrücke. Letztere haben immer die Gestalt

aausdruck vop aausdruck ,

wobei aausdruck ein arithmetischer Ausdruck und vop einer der Vergleichsoperatoren

>, <,>=, <=, = und < >

ist. Das Ergebnis eines logischen Ausdrucks ist eine logische Größe (wahr oder falsch) und kein Zahlenwert. Hier besteht ein grundsätzlicher Unterschied zu anderen BASIC- Interpretern (vgl. Abschn. 2.4.2.), der allerdings für die Praxis untergeordnete Bedeutung hat. Logische Ausdrücke dürfen nur in Anweisungen stehen, die logische Größen verarbeiten können. In arithmetischen Ausdrücken werden ganzzahlige Konstanten, Variablen und Funktionen verknüpft. Da es Gleitkommagrößen ohnehin nicht gibt, entfällt die Kennzeichnung mit %. Dieses Zeichen hat in Tiny MPBASIC eine andere Bedeutung. Vor einer Konstanten kennzeichnet es die hexadezimale Darstellung. Der zulässige Bereich liegt zwischen %0000 und %FFFF bei hexadezimaler und zwischen -32767 und 32767 bei dezimaler Schreibweise. (Die Zahl -32768 kann intern verarbeitet, aber nicht ein- bzw. ausgegeben werden.) Gerechnet wird im Zweierkomplement. Näheres zu dieser Darstellung und dem Rechnen damit findet der Leser z. B. in [9]. Es gibt die arithmetischen Operatoren +, -, *, / und $MOD. $MOD, modulo, liefert den Rest, den eine ganze Zahl bei der Division durch eine andere ganze Zahl gibt. Es gilt

| A $MOD B | = | A - (A/B*B) | .

Bei der Division wird der gebrochene Teil weggelassen. Das Dollarzeichen vor MOD hat nichts mit einem String zu tun; Strings sind nicht implementiert. Es kennzeichnet vielmehr $MOD als einen Operator. Das trifft auch auf die bitweisen logischen Operatoren

$AND, $OR und $XOR

zu. Diese arbeiten anders als die im Abschn. 2.4.2. beschriebenen logischen Operatoren. Sie führen für jedes Paar von gleichwertigen Binärstellen ihrer Operanden in der internen Zweierkomplementdarstellung die betreffende logische Operation aus. Ihre Benutzung setzt voraus, daß Zweierkomplement, binäre Darstellung und hexadezimale Darstellung für den Programmierer vertraute Begriffe sind. Beispielsweise kann man $AND zum „Maskieren“ von Bits in einem Datenwort benutzen. Mit

A $AND 1

erhält man den Zustand vom Bit 0 in A, indem mit der UND-Verknüpfung die restlichen Bits zurückgesetzt werden. Mit $OR können Bits auf Eins gesetzt werden. $XOR eignet sich u. a. zum bitweisen Vergleich von Daten. Der Ausdruck

A $XOR %70 $AND %F0

liefert z. B. genau dann den Wert Null, wenn in A die Bits 4, 5 und 6 Eins sind und Bit 7 Null ist. Alle bitweisen logischen und arithmetischen Operatoren haben dieselbe Priorität. Sie dürfen gemeinsam in arithmetischen Ausdrücken auftreten. Damit werden arithmetische Ausdrücke streng von links nach rechts abgearbeitet. Wird eine andere Reihenfolge der Berechnung gewünscht, so ist das mit Klammern zu regeln. Weiterhin stehen die in Tafel 8 aufgeführten allgemeinen, d. h. maschinenunabhängigen Funktionen zur Verfügung. Die Funktionsparameter werden grundsätzlich in eckige Klammern geschrieben. Beim Rotieren nach links werden alle 16 Bits um eine Stelle nach links geschoben, und das höchstwertige Bit wird zum niederwertigsten Bit. Rotiert man z. B. die Zahl -32767, die intern die binäre Darstellung 1000 0000 0000 0001 hat, nach links, so ergibt das 0000 0000 0000 0011 (binär) bzw. 3 in dezimaler Schreibweise. Das Rotieren nach rechts funktioniert prinzipiell genauso, nur eben in die andere Richtung. RR ist somit die inverse Funktion zu RL. Programmiert man RR[RL[x]], so ergibt das immer x. Man braucht diese Funktion z. B., wenn Eingabeports multiplex abgefragt werden sollen.

Tafel 8, Funktionen in Tiny MPBASIC
ABS[x]	absoluter Betrag von x
NOT[x]	bitweise logische Negation von x
GTC	ein Zeichen von der Konsole holen (benutzt GETCHR)
INPUT	Zahl von der Konsole holen
RL[x]	x um ein Bit nach links rotieren
RR[x)	x um ein Bit nach rechts rotieren
Tafel 9. Maschinenorientierte Funktionen im U8S3
GETR[r]	Inhalt des Registers r lesen
GETRR[r]	Inhalt des Registerpaares r, r+1 lesen
GETEB[a]	Inhalt des auf der Adresse a abgespeicherten Bytes im Datenspeicher lesen
GETEW(a)	Inhalt der auf den Adressen a, a+1 abgespeicherten Bytes im Daten-Speicher lesen

Darüber hinaus gibt es für den U883 einige Funktionen, mit denen man auf Register bzw. auf den Datenspeicher zugreifen kann (Tafel 9). Dadurch hat der Anwender sofort, d. h. ohne erst eigene Funktionen schreiben zu müssen, Zugriff auf fast alle Schaltkreisfunktionen. BASIC-Programme, die von diesen Funktionen Gebrauch machen, sind natürlich nicht unverändert auf einen anderen Rechner übertragbar. Deshalb sind diese vier Funktionen nicht Bestandteil der Sprache Tiny MPBASIC. Würde man etwa einen Interpreter für den U880 schreiben, so wären hier die Funktionen

GETB(a) Byte aus dem Speicher von Adresse a lesen 
GETW[a] Bytes aus dem Speicher von den Adressen a und a+1 lesen 
IN[p] Port p lesen

sinnvoll.

Da uns die BASIC-Anweisungen i. allg. schon bekannt sind, genügt es, hier die davon in Tiny MPBASIC implementierten mit der Syntaxvorschrift aufzuführen, und nur die Besonderheiten zu erläutern (Tafel 10). Eine Anweisung besteht aus ihrem Namen, z. B. LET, und den Argumenten, z. B. A=0. Soll eine Anweisung mehrmals nacheinander ausgeführt werden, so genügt es, den Namen nur einmal und lediglich die Argumente mehrfach aufzuschreiben, wenn man letztere mit Komma voneinander trennt. Zum Beispiel

10 LET A=0, B=0, I=10
Tafel 10. Anweisungen in Tiny MPBASIC
LET variable = aausdruck GOTO aausdruck 
IF lausdruck THEN anweisung
IF lausdruck THEN anweisungsblock 
ELSE anweisung
ELSE anweisungsblock 
GOSUB aausdruck 
RETURN
INPUT {"text"} variable 
PRINT {"text"} {aausdruck}
PRINTHEX {"text") {aausdruck}
STOP
END
REM {kommentar}
CALL aausdruck
PROC {variablenliste =} prozedurname {parameterliste}
TRAP lausdruck TO aausdruck
CLRTRP
WAIT aausdruck

Bei der Anweisung GOTO ist ein Ausdruck zur Angabe der Zeilennummer zugelassen. Gleiches trifft auf GOSUB zu. Die Anweisungen ON…GOTO und ON…GOSUB sind dadurch leicht zu umgehen. Ein Anweisungsblock besteht aus beliebig vielen, durch Semikolon getrennten Anweisungen, die auf einer Zeile stehen. Es ist möglich, mehrere Anweisungen auf eine Zeile zu schreiben, wenn man diese durch Semikolon voneinander trennt. Bei dem in Anführungsstriche gesetzten Text in den Anweisungen INPUT, PRINT und PRINTHEX handelt es sich im Grunde um eine Stringkonstante. Strings gibt es ansonsten nicht. INPUT druckt zunächst den programmierten Text aus und erwartet dann die Eingabe einer Zahl (dezimal oder hexadezimal). Wie wir bereits sahen (Tafel 8), gibt es INPUT auch als Funktion. Dort kann kein Text ausgedruckt werden. Es erscheint statt dessen ein Fragezeichen. INPUT hat als Funktion den Vorteil, daß es wie jede Funktion in Ausdrücken benutzt werden kann. Will man z. B. den Nutzer seines Programms eine Temperatur in Zehntel Grad Celsius eingeben lassen, die jedoch eigentlich in Zehntel Grad Kelvin benötigt wird, so kann man einfach

10 LET T = INPUT+2732 

programmieren.

Die Anweisung PRINTHEX unterscheidet sich von PRINT nur dadurch, daß das Ergebnis des Ausdrucks hexadezimal ausgegeben wird. Will man mehrere Ausgaben auf eine Zeile bringen, so benutzt man bei Tiny MPBASIC anstelle des Semikolons (dieses wird schon zum Trennen von Anweisungen benutzt) das Komma. Das Drucken in Spalten ist nicht möglich.

Mit der Anweisung PROC werden Prozeduren vom BASIC-Interpreter aufgerufen. Die Variablenliste sind in eckige Klammern gesetzte und durch Komma getrennte Variablen. Bei der Parameterliste handelt es sich um in eckige Klammern gesetzte und durch Komma getrennte Ausdrücke. Zum Beispiel

10 PROC [A, 3, X] = MPROG [3*A, X, 2] .

Es gibt bereits implementierte Prozeduren (Tafel 11). Der Sinn der Prozeduren besteht allerdings vor allem darin, daß der Anwender, wenn er selbst welche schreibt, Tiny MPBASIC einem gegebenen Anwendungsfall optimal anpassen kann. Mit Prozeduren realisiert man elementare Steuerfunktionen, die in BASIC nicht oder nur umständlich zu programmieren wären, oder deren Realisierung in BASIC ein zu langsames Programm ergeben würde.

Tafel 11. Prozeduren im U883
PTC[z]	Zeichen z an die Konsole ausgeben (benutzt PUTCHR)
SETR(r,w]	Register r mit dem Wert w belegen
SETRR[r,w]	Registerpaar r, r+1 mit dem Wert w belegen
SETEB[a,w]	Byte im Datenspeicher auf der Adresse a mit dem Wert w belegen
SETEW[a,w]	Bytes im Datenspeicher auf den Adressen a, a+1 mit dem Wert w belegen

Die SET-Prozeduren in Tafel 11 sind, wie die GET-Funktionen, wiederum nicht Bestandteil der Sprache Tiny MPBASIC. Sie haben ebenfalls nur den Zweck, den Interpreter im U883 für den Anwender sofort, d. h. ohne daß er selbst erst eine Prozedur schreiben muß, nutzbar zu machen.

Eine weitere, speziell für Steuerungen gedachte Anweisung, ist TRAP (Falle). Nachdem TRAP abgearbeitet wurde, testet der Interpreter vor der Abarbeitung jeder neuen Programmzeile die gesetzte Trapbedingung (lausdruck). Sobald diese erfüllt ist, erfolgt automatisch ein GOSUB zu der durch aausdruck gegebenen Zeile. Die Bedingung wird vorher gelöscht. Danach wird die Traproutine abgearbeitet, bei der es sich um ein gewöhnliches Unterprogramm handelt, welches mit RETURN endet. RETURN bringt die Programmabarbeitung schließlich wieder zu der Stelle, an der sie unterbrochen wurde. Soll die Trapbedingung danach weiter überwacht werden, so muß in der Traproutine eine entsprechende TRAP-Anweisung gestanden haben. Es kann immer nur eine Bedingung aktiv sein. Das Setzen einer neuen führt zum Löschen der alten Bedingung. Oft ist es jedoch möglich, eine Bedingung so zu formulieren, daß sie mehrere in sich vereint, wenn das nötig sein sollte. Welche dann davon das Trap ausgelöst hat, kann in der Traproutine ermittelt werden. An einem Anwendersystem mögen z. B. zwei 8-Bit-A/D-Wandler angeschlossen sein, die mit den selbst geschriebenen Funktionen AD1 und AD2 abgefragt werden können. Wenn AD1>200 oder AD2>180 wird, soll ein Trap ausgelöst werden. Dann kann man

10 TRAP AD1/201 $OR (AD2/181)>0 TO ... 

programmieren. Die Division AD1/201 ergibt so lange Null, wie AD1⇐200 ist, weil die Stellen nach dem Komma abgeschnitten werden. Genauso bleibt AD2/181 Null, wenn AD2⇐180 ist. Überschreitet einer der beiden, AD1 oder AD2, den vorgeschriebenen Maximalwert, so liefert die Verknüpfung $OR ein Ergebnis >0, und es kommt zum Trap.

Will man die Trapbedingung nur löschen, ohne dabei eine neue zu setzen, so gibt man die Anweisung CLRTRP (clear trap, lösche Trap).

Schließlich sei noch WAIT erläutert. Diese Anweisung berechnet zunächst den Ausdruck und ruft danach eine Software-Warteschleife auf, die so oft durchlaufen wird, wie das Ergebnis des Ausdrucks angibt. Ein Durchlauf dauert genau eine Millisekunde. (Beim U883 wird dabei ein 8-MHz-Quarz vorausgesetzt.) Bei der Anwendung von WAIT beachte man, daß die Berechnung des Ausdrucks und die vor- und nachher abzuarbeitenden Anweisungen ebenfalls Zeit brauchen. Weiterhin hat man bei WAIT mit großen Zeiten zu beachten, daß während des Wartens keine Trapbedingung getestet wird.


1)
Tiny bedeutet klein, winzig
  • elektronik/u883/mpbasic.1627911451.txt.gz
  • Zuletzt geändert: 2021/08/02 13:37
  • von volkerp