Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen angezeigt.

Link zu dieser Vergleichsansicht

Beide Seiten der vorigen Revision Vorhergehende Überarbeitung
elektronik:u883:mpbasic [2022/05/13 10:22] – [7.3.2. Anweisungen] volkerpelektronik:u883:mpbasic [2024/02/02 13:47] (aktuell) volkerp
Zeile 24: Zeile 24:
 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. 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 Echtzeitproblem besteht dann, wenn der Rechner auf ein externes Ereignis innerhalb einer vorgegebenen Zeit in einer diesem Ereignis entsprechenden Art und Weise reagieren muss. 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:+Ein generelles Problem bei der Anwendung höherer Programmiersprachen in Echtzeitproblemen besteht darin, dass 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, dass 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 fasst. Zum Beispiel:
  
   10 FOR I=1 TO 1000   10 FOR I=1 TO 1000
Zeile 32: Zeile 32:
   1000 NEXT I   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)+Misst 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.+In der Praxis sind jedoch meist nur wenige Programmstellen zeitkritisch, und Tiny BASIC lässt 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.
  
 ===== 7.2. Konzept von Tiny MPBASIC ===== ===== 7.2. Konzept von Tiny MPBASIC =====
  
-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 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.+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 Interpreterprogramm untergebracht, das ein fertiges, sowohl syntaktisch als auch semantisch fehlerfreies Programm abarbeiten kann. Es fehlen 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.+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 verfasst 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.).  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.). 
Zeile 63: Zeile 63:
 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 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   $AND, $OR und $XOR
-zu. Diese logischen logischen Operatoren 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 +zu. Diese logischen logischen Operatoren 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, dass 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   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. \\ 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. \\
Zeile 80: Zeile 80:
   INPUT Zahl von der Konsole holen   INPUT Zahl von der Konsole holen
   RL[x] x um ein Bit nach links rotieren   RL[x] x um ein Bit nach links rotieren
-  RR[x) x um ein Bit nach rechts rotieren+  RR[x] x um ein Bit nach rechts rotieren
  
   Tafel 9. Maschinenorientierte Funktionen im U8S3   Tafel 9. Maschinenorientierte Funktionen im U8S3
Zeile 86: Zeile 86:
   GETRR[r] Inhalt des Registerpaares r, r+1 lesen   GETRR[r] Inhalt des Registerpaares r, r+1 lesen
   GETEB[a] Inhalt des auf der Adresse a abgespeicherten Bytes im Datenspeicher 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+  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 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(aByte aus dem Speicher von Adresse a lesen +  GETB[aByte aus dem Speicher von Adresse a lesen 
   GETW[a] Bytes aus dem Speicher von den Adressen a und a+1 lesen    GETW[a] Bytes aus dem Speicher von den Adressen a und a+1 lesen 
   IN[p] Port p lesen   IN[p] Port p lesen
Zeile 111: Zeile 111:
   INPUT {"text"} variable    INPUT {"text"} variable 
   PRINT {"text"} {aausdruck}   PRINT {"text"} {aausdruck}
-  PRINTHEX {"text"{aausdruck}+  PRINTHEX {"text"{aausdruck}
   STOP   STOP
   END   END
Zeile 121: Zeile 121:
   WAIT aausdruck   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 +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, dass 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    10 LET T = INPUT+2732 
 programmieren. 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.+Die Anweisung **PRINTHEX** unterscheidet sich von PRINT nur dadurch, dass 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  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, B, X] = MPROG [3*A, X, 2] .+  10 PROC [A, B, 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.+Es gibt bereits implementierte Prozeduren (Tafel 11). Der Sinn der Prozeduren besteht allerdings vor allem darin, dass 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   Tafel 11. Prozeduren im U883
   PTC[z] Zeichen z an die Konsole ausgeben (benutzt PUTCHR)   PTC[z] Zeichen z an die Konsole ausgeben (benutzt PUTCHR)
-  SETR(r,w] Register r mit dem Wert w belegen+  SETR[r,w] Register r mit dem Wert w belegen
   SETRR[r,w] Registerpaar r, r+1 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   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   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.+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 muss, 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 +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 muss 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, dass 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 ...    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. 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.
Zeile 147: Zeile 147:
 Will man die Trapbedingung nur löschen, ohne dabei eine neue zu setzen, so gibt man die Anweisung **CLRTRP** (clear trap, lösche 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.+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, dass 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, dass während des Wartens keine Trapbedingung getestet wird.
  
 ===== 7.4. Anwendungsbeispiele ===== ===== 7.4. Anwendungsbeispiele =====
Zeile 159: Zeile 159:
 wobei Y die zu radizierende Zahl ist. Würde man nun  wobei Y die zu radizierende Zahl ist. Würde man nun 
   LET X = X-(X*X-Y)/(2*X)   LET X = X-(X*X-Y)/(2*X)
-programmieren, so brächte das keine brauchbaren Ergebnisse. Das liegt zum einen daran, daß bei der Berechnung von xn^2 es bereits für Xn>181 zu Bereichsüberschreitungen kommt, und zum anderen wird, was viel schlimmer ist, mit wachsendem n die Differenz zwischen Xn^2 und > immer kleiner und die Division durch 2*Xn deshalb und wegen der Verwendung von ganzen Zahlen viel zu ungenau. Das ist jedoch noch kein Grund, das Newton-Verfahren hier zu verwerfen. Statt dessen überlegt man, wie die Formel eventuell umzustellen ist, um die eben festgestellten Schwierigkeiten zu umgehen. Und tatsächlich ist das möglich. Es ist nämlich+programmieren, so brächte das keine brauchbaren Ergebnisse. Das liegt zum einen daran, dass bei der Berechnung von xn^2 es bereits für Xn>181 zu Bereichsüberschreitungen kommt, und zum anderen wird, was viel schlimmer ist, mit wachsendem n die Differenz zwischen Xn^2 und > immer kleiner und die Division durch 2*Xn deshalb und wegen der Verwendung von ganzen Zahlen viel zu ungenau. Das ist jedoch noch kein Grund, das Newton-Verfahren hier zu verwerfen. Statt dessen überlegt man, wie die Formel eventuell umzustellen ist, um die eben festgestellten Schwierigkeiten zu umgehen. Und tatsächlich ist das möglich. Es ist nämlich
  
 Xn+1 = Xn – (Xn^2 – Y) / (2Xn) = Xn – Xn / 2 + Y / (2Xn) Xn+1 = Xn – (Xn^2 – Y) / (2Xn) = Xn – Xn / 2 + Y / (2Xn)
Zeile 188: Zeile 188:
 Die Zeile 30 lautet verbal umschrieben: Wenn Z durch den Faktor F teilbar ist, dann führe die Division aus und drucke F als ermittelten Primfaktor. Die Zeile 30 lautet verbal umschrieben: Wenn Z durch den Faktor F teilbar ist, dann führe die Division aus und drucke F als ermittelten Primfaktor.
  
-**Beispiel 3**. Wir wollen den Inhalt des Datenspeichers des U883 in einem gewissen Adreßbereich ausdrucken. Dabei soll das folgende Druckbild auf jeder Zeile entstehen:+**Beispiel 3**. Wir wollen den Inhalt des Datenspeichers des U883 in einem gewissen Adressbereich ausdrucken. Dabei soll das folgende Druckbild auf jeder Zeile entstehen:
  
 adresse daten1 daten2 ... daten8 ascii adresse daten1 daten2 ... daten8 ascii
Zeile 251: Zeile 251:
 </code> </code>
  
-Dieses Programm druckt fortwährend alle ASCII-Zeichen mit den Kodes %20 . .. %5E bis während des Druckens über die SIO Zeichen empfangen worden sind. Dann werden die empfangenen Zeichen ausgedruckt, und die Abarbeitung ist beendet. TRAP überwacht im Interrupt-Requestregister des U883 (Reg. %FA) das SIO-Empfängerbit, das in der Trapbedingung mit $AND 8 maskiert wird. Da die Trapbedingung am Ende der Traproutine ohnehin neu gesetzt werden muß, erfolgt das erste Setzen mit GOSUB 250 in Zeile 40. Das spart Speicherplatz, Mit LET A=%1300 in Zeile 30 wird die RAM-Adresse festgelegt, wo die empfangenen Zeichen abgespeichert werden sollen. Die Zeilen 60... 80 bilden eine Warteschleife, die zwischen dem Ausdrucken zweier Zeichen eine Pause erzeugt. Hier wäre es nicht günstig, WAIT einzusetzen, weil dann so lange die Trapbedingung nicht überwacht werden würde. In der Traproutine wird zuerst in Zeile 210 das SIO-Bit zurückgesetzt. Das geschieht dadurch, daß zuerst das gesamte Register gelesen, anschließend das Bit mittels $AND %F7 rückgesetzt und schließlich das Ergebnis mit SETR wieder in das Register eingeschrieben wird. Dadurch bleiben die anderen sieben Bits unverändert. In Zeile 220 wird das empfangene Zeichen gelesen (SIO-Register %F0) und abgespeichert. Wir sehen hier, wie man in Tiny MPBASIC mit einem eindimensionalen Feld arbeiten kann, obwohl Felder gar nicht implementiert sind. Statt DIM A... zu programmieren, legt man mit LET A=%1300 eine Adresse für dieses Feld fest und kann dann mit SETEB[A+I, ...] oder GETEB[A+I] anstelle von LET A(I) = ... bzw. ... A (I) ... auf dieses Feld zugreifen. Selbstverständlich hat man dabei darauf zu achten, daß der Index I keine unzulässigen Werte annimmt (Zeile 240).+Dieses Programm druckt fortwährend alle ASCII-Zeichen mit den Kodes %20 . .. %5E bis während des Druckens über die SIO Zeichen empfangen worden sind. Dann werden die empfangenen Zeichen ausgedruckt, und die Abarbeitung ist beendet. TRAP überwacht im Interrupt-Requestregister des U883 (Reg. %FA) das SIO-Empfängerbit, das in der Trapbedingung mit $AND 8 maskiert wird. Da die Trapbedingung am Ende der Traproutine ohnehin neu gesetzt werden muss, erfolgt das erste Setzen mit GOSUB 250 in Zeile 40. Das spart Speicherplatz, Mit LET A=%1300 in Zeile 30 wird die RAM-Adresse festgelegt, wo die empfangenen Zeichen abgespeichert werden sollen. Die Zeilen 60... 80 bilden eine Warteschleife, die zwischen dem Ausdrucken zweier Zeichen eine Pause erzeugt. Hier wäre es nicht günstig, WAIT einzusetzen, weil dann so lange die Trapbedingung nicht überwacht werden würde. In der Traproutine wird zuerst in Zeile 210 das SIO-Bit zurückgesetzt. Das geschieht dadurch, dass zuerst das gesamte Register gelesen, anschließend das Bit mittels $AND %F7 rückgesetzt und schließlich das Ergebnis mit SETR wieder in das Register eingeschrieben wird. Dadurch bleiben die anderen sieben Bits unverändert. In Zeile 220 wird das empfangene Zeichen gelesen (SIO-Register %F0) und abgespeichert. Wir sehen hier, wie man in Tiny MPBASIC mit einem eindimensionalen Feld arbeiten kann, obwohl Felder gar nicht implementiert sind. Statt DIM A... zu programmieren, legt man mit LET A=%1300 eine Adresse für dieses Feld fest und kann dann mit SETEB[A+I, ...] oder GETEB[A+I] anstelle von LET A(I) = ... bzw. ... A (I) ... auf dieses Feld zugreifen. Selbstverständlich hat man dabei darauf zu achten, dass der Index I keine unzulässigen Werte annimmt (Zeile 240).
  
 ===== 7.5. Benutzen des Interpreters im U883 ===== ===== 7.5. Benutzen des Interpreters im U883 =====
Zeile 303: Zeile 303:
 </code> </code>
  
-Es sei angenommen, daß dieses Programm auf der Adresse %2000 stehe. Dann kann es mit den folgenden Maschinenbefehlen aufgerufen werden:+Es sei angenommen, dass dieses Programm auf der Adresse %2000 stehe. Dann kann es mit den folgenden Maschinenbefehlen aufgerufen werden:
  
 <code> <code>
Zeile 316: Zeile 316:
 </code> </code>
  
-Für die Programme **PUTCHR** und **GETCHR**, die das Benutzen von PRINT, PRINTHEX und INPUT ermöglichen, gelten nachstehende Konventionen: GETCHR hat die Eintrittsadresse %0815, PUTCHR %818. Das eingelesene Zeichen (GETCHR) wird im Arbeitsregister R3, das auszugebende Zeichen (PUTCHR) im Arbeitsregister R5 übergeben. Der Registerzeiger steht auf %10. Zur freien Verfügung hat der Anwender die Register ab %54.+Für die Programme **PUTCHR** und **GETCHR**, die das Benutzen von PRINT, PRINTHEX und INPUT ermöglichen, gelten nachstehende Konventionen: GETCHR hat die Eintrittsadresse %0815, PUTCHR %0818. Das eingelesene Zeichen (GETCHR) wird im Arbeitsregister R3, das auszugebende Zeichen (PUTCHR) im Arbeitsregister R5 übergeben. Der Registerzeiger steht auf %10. Zur freien Verfügung hat der Anwender die Register ab %54.
  
  
 Die **Prozedurtabelle**, die dem Interpreter die Zuordnung vom Prozedurnamen zur Eintrittsadresse ermöglicht, hat folgenden Aufbau: Die **Prozedurtabelle**, die dem Interpreter die Zuordnung vom Prozedurnamen zur Eintrittsadresse ermöglicht, hat folgenden Aufbau:
   Byte 0|1|2|3|4| . . . |N|N+1|N+2| ...   Byte 0|1|2|3|4| . . . |N|N+1|N+2| ...
-Byte 0 enthält die Länge N des Prozedurnamens, die binär abgespeichert wird. (0<N<%FE = 254). Die Bytes 1 ... N enthalten den Namen im ASCII-Kode, und die Bytes N+1 und N+2 enthalten die Eintrittsadresse der Prozedur. Danach darf, beginnend mit der Länge ihres Namens, die nächste Prozedur in die Tabelle eingetragen werden. Das Ende wird mit %FF gekennzeichnet. Beginnen z. B. die Prozeduren PROZ und FUNKT auf den Adressen %2010 bzw. %3000, dann müßte eine Prozedurtabelle damit so aussehen:+Byte 0 enthält die Länge N des Prozedurnamens, die binär abgespeichert wird. (0<N<%FE = 254). Die Bytes 1 ... N enthalten den Namen im ASCII-Kode, und die Bytes N+1 und N+2 enthalten die Eintrittsadresse der Prozedur. Danach darf, beginnend mit der Länge ihres Namens, die nächste Prozedur in die Tabelle eingetragen werden. Das Ende wird mit %FF gekennzeichnet. Beginnen z. B. die Prozeduren PROZ und FUNKT auf den Adressen %2010 bzw. %3000, dann müsste eine Prozedurtabelle damit so aussehen:
   %04|%50|%52|%4F|%5A|%20|%10|%05|%46|%55|%4E|%4B|%54|%30|%00|%FF    %04|%50|%52|%4F|%5A|%20|%10|%05|%46|%55|%4E|%4B|%54|%30|%00|%FF 
  
-Die **Übergabe der Parameter** vom Interpreter an die Prozedur und der Ergebnisse von der Prozedur an den Interpreter erfolgt über den Stack. Wenn die Prozedur m Ergebnisse an den Interpreter übergibt und n Parameter von diesem übernimmt, dann befindet sich der Stack beim Aufruf der Prozedur in dem in Tafel 14 aufgezeigten Zustand. Vor dem letzten RET in der Prozedur, das wieder zum Interpreter führt, muß der Stackpointer um 2n-2 erhöht worden sein (wenn n>1) und natürlich auf die Rückkehradresse zum Interpreter zeigen.+Die **Übergabe der Parameter** vom Interpreter an die Prozedur und der Ergebnisse von der Prozedur an den Interpreter erfolgt über den Stack. Wenn die Prozedur m Ergebnisse an den Interpreter übergibt und n Parameter von diesem übernimmt, dann befindet sich der Stack beim Aufruf der Prozedur in dem in Tafel 14 aufgezeigten Zustand. Vor dem letzten RET in der Prozedur, das wieder zum Interpreter führt, muss der Stackpointer um 2n-2 erhöht worden sein (wenn n>1) und natürlich auf die Rückkehradresse zum Interpreter zeigen.
  
 <code> <code>
Zeile 374: Zeile 374:
 aausdruck => wert {alop wert}* aausdruck => wert {alop wert}*
 wert => konst|var|fkt|(aausdruck) wert => konst|var|fkt|(aausdruck)
-alop => +|-|*|/|$MOD|$AND[SOR|$XOR+alop => +|-|*|/|$MOD|$AND[$OR|$XOR
 konst => pkonst | nkonst konst => pkonst | nkonst
 var => buchst var => buchst
  • elektronik/u883/mpbasic.1652437353.txt.gz
  • Zuletzt geändert: 2022/05/13 10:22
  • von volkerp