Die vorliegende Bedienungsanleitung soll den Umgang mit der Open-Micro, Open-Mini und der Open-Midi erleichtern. Der größte Teil beschäftigt sich mit der Programmierung im neuen OCBASIC-Dialekt, doch wird auch auf die Herstellung der OM-Controller eingegangen. Abgerundet wird die Doku durch ein disassembliertes Listing des Betriebssystems der Open-Micro und Open-Mini. Zunächst beginnen wir aber mit der Beschreibung, was die OM eigentlich ist und wie sie entstanden ist.
Die Dokumentation besteht aus einer einzigen HTML-Seite, die dementsprechend unkompliziert ausgedruckt oder in ein anderes Dateiformat (wie DOC, PDF, PS, HLP, etc.) konvertiert werden kann.
Es läßt sich auch sehr einfach nach einem Stichwort mittels Ctrl+F, bzw. Strg+F suchen.
Um schneller in die einzelnen Hauptkapitel zu gelangen, wird in Frame-fähigen Internetbrowsern am linken Rand ein Menü angezeigt. Jeder Internetbrowser formatiert diese Dokumentation etwas anders. Die beste Formatierung wird mit dem Internet Explorer erzielt.
Diese Bedienungsanleitung ist nicht perfekt. Wer möchte, kann die Anleitung um zusätzliche Kapitel ergänzen oder die vorhandenen Erklärungen erweitern. Dann aber nicht diese HTML-Datei editieren, sondern mir den Text in einer E-Mail zusenden.
Das Open-Control-Projekt wurde von Dietmar Harlos ins Leben gerufen, um eine Alternative
zur C-Control/Micro und einen würdigen Nachfolger für die C-Control I Version 1.1 M-Unit anzubieten. Das Ziel dieses Projektes ist es,
unkompliziert zu programmierende Controller zu entwickeln, die weitgehend kompatibel zur C-Control/Micro und zur C-Control I Version 1.1 M-Unit sind, aber gleichzeitig einen deutlich größeren Funktionsumfang besitzen.
Außerdem wurde großen Wert auf Fehlerfreiheit und gute Dokumentation gelegt.
Als Ergebnis ist die Open-Micro entstanden. Und so ganz nebenbei entstand
die Open-Mini gleich mit dazu. Diese beiden Controller unterscheiden sich
nur von der Anzahl der zur Verfügung stehenden Ports.
Die Open-Midi ist die große Schwester der Open-Mini. Denn sie ist maßgeblich kompatibel zu den bisherigen OM-Controllern, bietet jedoch deutlich mehr Speicher.
Die Open-Macro unterstützt 16 Bit Handling und die Open-Maxi ist - soweit sinnvoll und möglich - in Hard- und Software abwärtskompatibel zur weitverbreiteten C-Control I Version 1.1 M-Unit, unterstützt aber gleichzeitig viele Features der bisherigen OM-Mikrocontroller.
Als Basis für die
Open-Micro wurde wie auf der Original-Micro ein 68HC908QT4 von Freescale (früher Motorola) im PDIP-8-Gehäuse verwendet. Die
Open-Mini basiert auf einem 68HC908QY4-Controller in PDIP-16. Somit stehen auf der
Open-Micro 6 Ports und auf der Open-Mini 14 Ports zur Verfügung, die mehrfach belegt sind.
Die Open-Midi und Open-Macro basieren auf dem 68HC908QB8, der die gleiche Gehäuseform und Pinbelegung wie die Open-Mini hat.
Die Open-Maxi basiert auf der Open-Control M-Unit-Hardware von CCTools, die pinkompatibel zur C-Control I Version 1.1 M-Unit ist. Sie ist mit dem HCS08-Mikrocontroller MC9S08AW60 bestückt. Auf der Open-Maxi stehen deshalb über 24 I/O-Ports, 8 AD-Ports, 2 Frequenzmeßeingänge, 2 PWM-Analogausgänge, BEEP, TEST, eine herausgeführte Referenzspannung für den AD-Wandler und andere Features gleichzeitig zur Verfügung.
Die Open-Maxi bietet alle Features der C-Control I Version 1.1 M-Unit und darüber hinaus noch sehr viel mehr Ressourcen.
Das "Open" im Namen symbolisiert, daß diese Controller nicht verschlossen sind wie die neue C-Control (Version 1.2 und 2.0), sondern im Gegenteil offen für einen einfachen Einstieg in die Welt der 68HC08er. Es sind alle Controller-Ressourcen nutzbar und es können unter anderem eigene Assembler-Routinen entwickelt
und sehr komfortabel in das BASIC-Programm eingebunden werden. Es ist sogar
möglich, ein Betriebssystem-Update durchzuführen. Damit können auch ältere OMs
immer auf den neuesten Stand gehalten werden, um neue Features nutzen
zu können.
Das Betriebssystem der OM-Controller wurde auf geringe Größe bei maximalem
Funktionsumfang optimiert, um mehr Platz für Anwenderprogramme zu schaffen.
Dem Anwender stehen nun 2430 Byte (auf der Open-Midi sogar 6462 Byte) zur Programmspeicherung zur Verfügung
und Assemblersystemerweiterungen belegen nicht mehr 256 Byte, sondern nur
noch soviel wie nötig. Darüber hinaus gibt es die Möglichkeit, den speziell
an die Open-Micro angepaßten Open-Control/BASIC-Compiler zu verwenden, der
durch Verwendung neuer Tokenbefehle die Programmgröße um typisch 20% reduziert.
Der große Vorteil von OCBASIC ist die hohe Kompatiblität zu CCBASIC. Der auf
der Open-Micro und Open-Mini zur Variablenspeicherung zur Verfügung stehende RAM-Speicher
ist 64 Byte groß, wobei einige Betriebssystemfunktionen abgeschaltet werden
können und so der Speicher auf mehr als 80 Byte ausgedehnt werden kann.
Die Open-Midi bietet dem Anwender sogar über 170 Byte Variablenspeicher.
Interessant an den OM-Controllern ist vor allen Dingen, daß ein Anfänger seine
ersten Programme in einfachstem BASIC schreiben kann, um sich dann nach und
nach tiefer in die 68HC08-Materie einzuarbeiten. Man darf sich von den
geringen Ausmaßen nicht täuschen lassen: Es handelt sich um
vollwertige Mikrocontroller und durch die Möglichkeit der Programmierung
in Assembler steht der gesamte Controller offen.
Leider haben einige Vorteile auch Nachteile mit sich gebracht. Die OM ist nicht 100% pinkompatibel zur C-Control/Micro. Die OM benötigt nur einen Port für die RS232-Schnittstelle und FREQ liegt nicht mehr an PORT[4], sondern an PORT[3]. Standardmäßig unterstützt die RS232-Schnittstelle nur 9600 Baud und ist wie auf der C-Control/Micro nicht interruptgesteuert. DA-, bzw. PWM-Funktionalität wird auf der OM durch ein optionales Assemblermodul realisiert. Ein weiterer Nachteil ist, daß die OM, genauso wie die C-Control/Micro, byteorientiert arbeitet. Dadurch können keine WORD-Variablen verarbeitet werden. Allerdings läßt sich fehlende Funktionalität und Kompatiblität auf der OM durch nachladbare Assemblermodule realisieren, die nur wenig Speicherplatz kosten.
Auf keinen Fall darf die OM direkt mit 230 Volt Netzspannung verbunden werden. Es ist
ausschließlich eine Schutzkleinspannung zu verwenden.
Genauso wie die C-Control/Micro arbeitet die OM mit einer stabilisierten
Versorgungsspannung von 5 Volt +/- 0,4 Volt. Ein keramischer 100-nF-Kondensator, so nah wie möglich an den Versorgungsspannungs-Pins der OM, verbessert einige elektrische Eigenschaften des Controllers. Des weiteren ist Port[3] sehr
empfindlich gegenüber elektromagnetischen Störungen. Aus diesem Grund
sollte immer eine Z-Diode 5,1 V an Port[3] gegen Masse mit eingebaut werden.
Alternativ läßt sich Überspannung auch mittels herkömmlicher Diode 1N4148 nach VDD (5 Volt) ableiten.
Allerdings ist es auch möglich, die OM mit 3 Volt zu betreiben.
Dazu muß ein speziell angepaßtes Betriebssystem aufgeladen werden.
Dadurch läßt sich die OM mobil mit drei 1,5-Volt-Batterien oder 1,2-Volt-Akkus betreiben.
Die Versorgungsspannung ist zugleich die Referenzspannung für den Analog-Digital-Umsetzer. Aus diesem Grund sollte die Versorgungsspannung möglichst stabil sein und wenig "Brumm" enthalten.
Die OM ist in OCBASIC, CCBASIC, mBasic, Basic++, CCPLUS und Assembler programmierbar. Sie ist also sehr kompatibel zu vorhandenen Programmiersprachen und Tools, die bereits zum Programmieren der C-Control-1 verwendet werden. Welche man davon einsetzt bleibt jedem selber überlassen. Es wurde aber extra für die OM der neue BASIC-Compiler OCBASIC entwickelt, der das ganze Potential der OM ausschöpft. In dieser Anleitung wird ausschließlich auf OCBASIC näher eingegangen. Da der OCBASIC-Dialekt auf der CCBASIC-Syntax basiert, sollte es keine Schwierigkeiten bereiten, die beschriebenen Codesegmente an CCBASIC für Windows, CCBASIC für DOS oder CCBASIC für Linux anzupassen.
Serielle Schnittstelle
An diesem Port liegt die serielle Schnittstelle (RS232), über die Daten eingelesen und
ausgegeben werden können. Außerdem wird die OM über diesen Port programmiert. Die serielle Schnittstelle belegt also nur noch einen Port. Mit Hilfe eines kleinen Interfaces für Semi-Dual-Wire-RS232 verhält sich diese Schnittstelle allerdings genauso, wie eine herkömmliche RS232 mit den zwei Leitungen RXD und TXD.
PORT[Nummer]
Hierbei handelt es sich um einen digitalen Port. Diese Ports können die Zustände ON (-1)
und OFF (0) annehmen. Jeder Port kann als Eingang oder Ausgang fungieren. PORT[3] ist allerdings nur als Eingang zu verwenden und bei dessen Beschaltung ist zu beachten, daß dieser Port zugleich als HOST/RUN-Pin dient. Nach einem Reset muß dieser Pin auf High-Potential (5 Volt) liegen, sonst arbeitet der Controller das Anwenderprogramm nicht ab, sondern geht in den Hostmodus. Mittels Hostmodus lassen sich über die serielle Schnittstelle Anwenderprogramme in den Controller übertragen.
AD[Nummer]
Hierbei handelt es sich um einen Analog-Digital-Port.
Analoge Spannungen lassen sich in digitale Werte umsetzen.
Ein AD-Port kann nur als Eingang
verwendet werden und Werte zwischen 0 (0 Volt) und 255 (5 Volt) annehmen.
BEEP
An jedem dieser Ports kann ein Rechtecksignal mit unterschiedlicher Frequenz ausgegeben werden. Durch Anschluß eines Piezo-Schallwandlers (ohne Elektronik) wird ein Ton erzeugt. Es ist normalerweise nicht möglich, daß
mehrere BEEP-Ports gleichzeitig einen Ton ausgeben.
FREQ1
Mit diesem Port kann eine Frequenz gemessen werden. Es findet eine Impulszählung statt. Die Fensterbreite ist in 20-ms-Schritten frei wählbar. Die Frequenzmessung ist nach Programmstart deaktiviert. Durch ein entsprechendes Assemblermodul können auch andere Pins zur Impulszählung verwendet werden.
IRQ
Es kann eine Assembler- oder BASIC-Interruptroutine gestartet werden, sobald an diesem Pin eine High-Low-Flanke detektiert wird. Die Interruptfunktionalität ist nach Programmstart deaktiviert.
Hier ein Schaltplan für eine einfache, vielbewährte Programmierschaltung mit Semi-Dual-Wire-RS232, die sowohl zur Übertragung von Anwenderprogrammen als auch zum Datenaustausch (mittels PRINT, INPUT, etc.) geeignet ist. Als Transistoren können alle Standard-Kleinsignal-NPN-Typen wie beispielsweise BC547B verwendet werden. Die Werte der Widerstände sollten jedoch nicht verändert werden.
Der Schalter S1 ist der Hostmodeschalter. In Stellung "HOST" verbindet er PORT[3] mit Masse. In Stellung "RUN" wird dieser Port über den Pull-Up-Widerstand R1 auf High-Potential gelegt. Die Schaltung wird mittels Nullmodemkabel an die serielle Schnittstelle (RS232) eines PCs angeschlossen. Es kann das Nullmodemkabel der C-Control-1 verwendet werden.
Zur bequemen Programmentwicklung sollte das zweite PE-Board oder das Starterboard von CCTools verwendet werden, da es viel umfangreicher ist und eine bessere Testumgebung bietet. Auf diesen Boards wird der Portzustand mittels LEDs angezeigt, die über Porttreiber (4049 oder 4050) versorgt werden und daher, anders als beim Board von Conrad Electronic, nicht die Ports belasten.
Moderne Rechner haben allerdings keine Serielle Schnittstelle mehr. Der Anwender muß deshalb zusätzlich einen USB-nach-Seriell-Adapter einsetzen. Reichelt bietet so einen Adapter unter der Bestellnummer "DELOCK 61425" an. Damit wird unter Windows 7 ein maximal großes Programm in unter 12 Sekunden auf die Open-Maxi übertragen. Bei USB-nach-Seriell-Adaptern mit FTDI-Chip kann die Übertragungsgeschwindigkeit bedeutend erhöht werden, indem im Geräte-Manager die Wartezeit bei den BM-Einstellungen auf 1 ms gesetzt wird.
Die Beschreibung der Befehle und reservierten Wörter in den folgenden Kapiteln bezieht sich auf den OCBASIC-Compiler.
Wird ein anderer Compiler verwendet gibt es eventuell Einschränkungen bei den
Befehlen, bzw. einige Befehle stehen nicht mehr zur Verfügung oder werden anders
realisiert. Welche Programmiersprache, bzw. welcher BASIC-Dialekt verwendet wird bleibt aber natürlich jedem selbst überlassen.
Besonders leicht kommt man über die OCBASIC-Schlüsselwortliste zum gewünschten Befehl.
Da das Copyright zu CCBASIC bei Conrad Electronic liegt, wurde während des OM-Projekts
der "Open-Control/BASIC-Compiler" entwickelt. Der Compiler ist (soweit sinnvoll) 100% kompatibel zur CCBASIC-Syntax, bietet aber trotzdem viele neue Features wie beispielsweise mehrzeiliges als auch einzeiliges IF, WHILE..WEND, REPEAT..UNTIL, Funktionen und Subroutinen (Prozeduren) in Standardsyntax. Außerdem gibt es eine neue Syntax, mit der BIT-Variablen nicht mehr auf festen Positionen im User-RAM gespeichert werden müssen, sondern innerhalb von BYTE- oder WORD-Variablen als "Bitarrays" abgelegt werden können. Dadurch müssen keine Variablen mehr an festen Positionen plaziert werden. Der Compiler besitzt sogar einen integrierten 6808-Assembler. Damit können BASIC- und Assemblerroutinen innerhalb derselben Sourcecode-Datei erstellt werden. Assemblerroutinen sind so deutlich komfortabler zu erstellen, da in Assembler auf vom Anwender definierte Variablen und auf die meisten internen Variablen direkt zugegriffen werden kann. Assemblerroutinen lassen sich von BASIC aus auf die gleiche Weise aufrufen wie BASIC-Routinen.
Standardmäßig wird Tokencode erzeugt, der die neuen Befehle der OM nutzt und auf diese Weise die Programmgröße um typisch 20% reduziert. Es ist aber auch möglich, zu bisherigen C-Control-Versionen kompatiblen Tokencode mit bis zu 64 KB Größe zu erzeugen. Der Compiler kann statt CCBAS.EXE in die DOS-IDE oder in ConTEXT eingebunden werden.
Es wurde einige Zeit ins Austesten gesteckt und insbesondere darauf geachtet, daß der Compiler möglichst sinnvolle Meldungen ausgibt, falls während der Kompilierung Fehler auftreten. Der Compiler hat eine ausführliche Betatestphase durchlaufen und die offiziell erste Version wurde anhand diverser Beispielprogramme vor der Veröffentlichung ausführlich getestet.
Der Compiler besteht aus der Datei OCBAS.EXE, die sich in beliebige integrierte Entwicklungsumgebungen (IDEs), wie zum Beispiel ConTEXT oder die DOS-IDE von C-Control/BASIC anstelle des C-Control/BASIC-Compilers CCBAS.EXE einbinden läßt.
Die IDE stellt einen Texteditor bereit, mit dem der BASIC-Quellcode erstellt und verändert werden kann.
Mit einem einfachen Tastendruck läßt sich jederzeit der Compiler starten, der den Quellcode in Tokencode übersetzt.
Mit einem ebenfalls in die IDE eingebundenen Downloadprogramm kann dieser Tokencode zum Controller übertragen werden.
Wie die ConTEXT-IDE in eine Entwicklungsumgebung für die Open-Micro, Open-Mini und Open-Midi umfunktioniert werden kann, wird in dieser Dokumentation bei Tips und Tricks ausführlich beschrieben.
Für erste Versuche kann der Compiler natürlich auch zunächst auf der MS-DOS-Eingabeaufforderung
benutzt werden. In jedem Fall erhält OCBAS.EXE als Eingabe eine BAS-Datei mit dem BASIC-Quellcode und gibt eine DAT-Datei mit dem Tokencode aus. Diese DAT-Datei kann mit einem Downloadtool wie OMDLWIN.EXE, OMDL.EXE oder CCDL.EXE zur OM geschickt werden.
Es gibt zwei Varianten des Compilers: Einmal die 16-Bit-Version OCBAS.EXE und die 32-Bit-Version OCBAS32.EXE. Nur die 32-Bit-Variante ist zu allen Windows-Betriebssystemen kompatibel und sollte deshalb vorzugsweise verwendet werden.
Beim 16-Bit-Compiler OCBAS.EXE handelt es sich um ein DOS-Programm, das auch auf älteren Rechnern benutzbar ist. Aus Kompatiblitätsgründen können dem Programm aber nur kurze DOS-Dateinamen übergeben werden. Die Windows-Betriebssysteme unterstützen das auch bei Dateien mit langem Namen, indem sie jede Datei ebenfalls über einen DOS-Dateinamen ansprechbar machen.
Die unter Windows angelegte Datei "Dateiname.bas" kann zum Beispiel mittels "CCBAS datein~1.bas" fehlerfrei übersetzt werden. In der ConTEXT-IDE läßt sich eine Option für kurze DOS-Dateinamen aktivieren. Innerhalb ConTEXT dürfen Quellcode-Dateien daher auch lange Dateinamen besitzen.
Um BASIC-Programme noch weiter zu beschleunigen, wurde für die OM-Controller ein Compiler namens OCBASR erstellt, mit dem der Anwender BASIC-Quellcode sehr komfortabel in Maschinensprache, bzw. Assembler, umwandeln kann. Der Standard-Compiler OCBASIC übersetzt bekanntlich BASIC-Quellcode in Tokencode, der vom Interpreter in der OM ausgeführt wird. Gegenüber diesem interpretierten Tokencode wird die vom Compiler OCBASR erzeugte Maschinensprache erheblich schneller ausgeführt.
Und dabei ist die Benutzung des neuen Compilers denkbar einfach. Zur Installation muß man einfach das Programm OCBASR32.EXE in das Verzeichnis kopieren, in dem schon OCBAS32.EXE steht, und in ConTEXT bei den Benutzerbefehlen "OCBAS32.EXE" durch "OCBASR32.EXE" ersetzen. Nun wird der BASIC-Compiler OCBASR statt OCBASIC gestartet.
Noch einfacher ist es, BASIC-Quellcode in Assemblercode, bzw. Maschinensprache übersetzen zu lassen. Der zu beschleunigende Code muß in einer Subroutine oder in einer Funktion stehen. Wird hinter die Startzeile dieser Subroutine oder Funktion das Schlüsselwort COMPILE angefügt, wird der OCBASR-Compiler aktiv und übersetzt den in ihr stehenden BASIC-Sourcecode in die Maschinensprache. Dieser Programmteil wird nun üblicherweise 3 bis 4 mal schneller ausgeführt. Einige BASIC-Befehle sind sogar 10 bis 20 mal schneller.
Der echte Compiler OCBASR kann auf der offiziellen Infosite zum Open-Control-Projekt heruntergeladen werden.
Die Berechnung eines Terms, in dem mehrere Operatoren verwendet werden, erfolgt in einer bestimmten Reihenfolge. Sehr bekannt ist die Hierarchie "Punktrechnung vor Strichrechnung". Der Programmierer kann die Reihenfolge durch Verwendung von Klammern genau festlegen. Auf die Klammerung kann verzichtet werden, wenn die gewünschte Reihenfolge mit der Operatoren-Rangfolge übereinstimmt. Das sollte man aber genau prüfen und im Zweifelsfall besser eine Klammer zuviel setzen.
Rang | Operator | Beschreibung |
8 | ( ) | Klammern |
7 | + - | positives und negatives Vorzeichen |
6 | * / MOD SHL SHR | Multiplikation, Division, Restwert und Schiebebefehle |
5 | + - | Addition und Subtraktion |
4 | = == <> > >= < <= | numerischer Vergleich |
3 | NOT | logisches Vorzeichen |
2 | AND NAND | logisches UND |
1 | OR NOR XOR | logisches ODER |
Von C-Control-kompatiblen Controllern werden Berechnungen grundsätzlich mit Hilfe eines sogenannten Rechenstacks durchgeführt, der Platz für maximal 7 Elemente bietet. Damit läßt sich das Ergebnis selbst komplexer Terme errechnen. Falls jedoch sehr verschachtelte Ausdrücke berechnet werden müssen, kann der Stack unbemerkt überlaufen, wodurch auf dem Stack gespeicherte Werte verlorengehen. Das berechnete Ergebnis ist dann fehlerhaft. In der Praxis treten Stacküberläufe jedoch so gut wie nie auf, weil komplexe Terme nur sehr selten vorkommen. Das folgende Beispiel benötigt alle 7 Stackelemente, da die einzelnen Berechnungen aufgrund des höheren Rangs nachfolgender Operatoren bis zum Schluß hinausgezögert werden.
Sprungziele ("Labels") markieren bestimmte Punkte in der Folge der Programmoperationen. Labels sind Ziele
der Sprungoperationen GOSUB und GOTO innerhalb eines Algorithmus. In OCBASIC stehen Labels am Anfang
einer Zeile und beginnen wie in CCBASIC stets mit einem Doppelkreuz, dann folgt (ohne Leerzeichen) der Bezeichner des Labels. Zu beachten ist, daß OCBASIC etwas strengere Regeln als CCBASIC für gültige Labelnamen besitzt.
Das folgende Beispiel demonstriert eine Endlosschleife. Wenn man mehrere Labels erstellt,
dann kann man im Programm von Label zu Label springen und das BASIC-Programm auf diese
Weise verzweigen. Es ist sinnvoll, dem Label einen ausdrucksstarken Namen zu geben, um
die Programmübersicht zu wahren.
In OCBASIC kann in vielen Fällen auf Labels und die Befehle GOTO und GOSUB verzichtet werden, weil hinter IF..THEN mehr als ein Befehl angegeben werden darf und die neuen Schleifenbefehle WHILE..WEND und REPEAT..UNTIL existieren. Außerdem lassen sich Funktionen mittels FUNCTION und Subroutinen mittels PROCEDURE definieren. Aber trotzdem wird weiterhin die bekannte und im folgenden Beispiel demonstrierte CCBASIC-Syntax unterstützt.
Die Syntax von OCBASIC entspricht in fast allen Punkten der bekannten Syntax von CCBASIC. Wer bisher die C-Control in CCBASIC programmiert hat, wird sicher keine Schwierigkeiten haben, zu OCBASIC zu wechseln. Trotzdem werden im folgenden noch einmal die bekannten Schlüsselwörter von CCBASIC im Zusammenhang mit der OM kurz vorgestellt. Eine umfangreiche Beschreibung der CCBASIC-Befehle befindet sich in der Dokumentation zur C-Control Version 1.1 und in der Online-Hilfe von CCBASIC für Windows und DOS.
Angabe einer Konstante im binären Zahlensystem. In manchen Fällen ist es übersichtlicher, eine Binärzahl zu verwenden um Port- oder Variablenzustände besser zuordnen zu können.
Der Taschenrechner von Windows kann dezimale in binäre und hexadezimale Zahlen umrechnen, wenn er auf wissenschaftliche Ansicht gestellt wird.
Angabe einer Konstante im hexadezimalen Zahlensystem. In manchen Fällen ist es übersichtlicher, eine Hexzahl zu verwenden um insbesondere Speicheradressen besser zuordnen zu können.
Der Taschenrechner von Windows kann dezimale in binäre und hexadezimale Zahlen umrechnen, wenn er auf wissenschaftliche Ansicht gestellt wird.
Berechnet den Absolutbetrag von "x"
Da die OM ausschließlich mit nicht vorzeichenbehafteten Bytewerten rechnet, hat dieser Befehl auf der OM keine Funktion.
Definiert einen Port als analogen Eingang. Daraufhin kann die Spannung am Port mittels Analog-Digital-Umsetzer (ADU) gemessen werden. Der Port liefert Werte zwischen 0 und 255 zurück, was den Spannungswerten 0 Volt bis 5 Volt entspricht. Die Versorgungsspannung des Controllers ist zugleich die Referenzspannung für den Analog-Digital-Umsetzer. Der Innenwiderstand einer am AD-Wandler angeschlossenen Signalquelle sollte laut Controllerhersteller grundsätzlich nicht mehr als 10 kOhm betragen.
Die OM verfügt über interne Pull-Up-Widerstände an den Digitalports, die vom Betriebssystem nach jedem Reset aktiviert werden.
Diese Widerstände führen oft zur Verfälschung des AD-Meßwerts, da sie vom Controller vor jeder Messung deaktiviert und nach der Messung wieder aktiviert werden. Deshalb sollten die Widerstände an den entsprechenden Ports mit Hilfe von PULLUPA oder PULLUPB abgeschaltet werden.
Die AD-Ports können weiterhin als Digitalports genutzt werden, da das Betriebssystem den Controllerpin nur für die Dauer einer Messung auf den Analog-Digital-Umsetzer umschaltet. Falls diese Vorgehensweise zu Problemen führt, kann der Pin auch dauerhaft auf AD-Funktion geschaltet werden. Dazu sind die AD-Register des Controllers entsprechend zu programmieren. In diesem Fall sind einige Einschränkungen zu beachten, die im Manual zum Controller beschrieben werden.
Logische und bitweise UND-Verknüpfung, auch Konjunktion genannt.
a | b | a AND b |
0 | 0 | 0 |
0 | 1 | 0 |
1 | 0 | 0 |
1 | 1 | 1 |
Der Befehl wird im Zusammenhang mit OPEN# verwendet. APPEND bedeutet, daß die Datei zum Anhängen von Daten geöffnet wird.
Mit dem Befehl wird die Baudrate der seriellen Schnittstelle eingestellt. Es können beliebige Baudraten eingestellt werden. Die OM unterstützt aber nur eine Baudrate von 9600 und ignoriert daher standardmäßig diesen Befehl.
Auf der C-Control 1.1 wird mit diesem Befehl ein Interrupt aktiviert, der
am BEEP-Pin des Controllers ein Rechtecksignal generiert. Durch Anschluß eines Piezo-Schallwandlers ohne Elektronik wird ein Ton erzeugt.
Auf der OM wurde wie auf der Original-Micro die Befehls-Syntax etwas abgewandelt und es kann daher an
fast jedem Digitalport ein Ton ausgegeben werden.
Zu beachten ist allerdings, daß während der Tonausgabe der Controller
stillsteht und auch keine Interrupts verarbeitet.
Aus diesem Grund läuft die interne Echtzeituhr nicht weiter und
es ist mit diesem Verfahren nicht möglich, an mehreren BEEP-Ports gleichzeitig einen Ton
zu erzeugen.
Da der Parameter "ton" auf der OM nur im Byteformat vorliegt, wurde
diese Funktion entsprechend angepaßt. Die höchste Frequenz bei ton=0 ist
27,6 kHz, die niedrigste bei ton=255 ist 16 Hz. Es wird also der gesamte
Audiofrequenzbereich abgedeckt. Insbesondere niedrige Frequenzen lassen
sich auch gut zur Taktung von Hardware einsetzen.
Die exakte Frequenz ist
f = 3.2E6 / (3*ton^2+17*ton+116).
Aus gegebener Frequenz läßt sich auch der Parameter "ton" berechnen zu
ton = -17/6 + SQR(3.2E6/(3*f)-1103/36).
Zwischentöne sind durch Variation des internen Takts (siehe FREQ2) realisierbar.
Bei Frequenzen kleiner 25 Hz hält der Ton länger an als bei "dauer" angegeben, da der zur Generierung des Rechtecksignals verwendete Timer nur am Ende jeder Halbwelle abgefragt wird.
Mit dem Befehl wird eine Bitvariable definiert.
Ein Bit ist die kleinste Speichereinheit und kann die Zustände ON oder OFF annehmen. In OCBASIC sind Adressen, bzw. Index-Positionen zwischen 1 und 256 zulässig.
Alle definierten Bit-, Byte- und Wordvariablen werden im USER-RAM gespeichert und können sich überlagern, wenn
bei der Definition irgendeiner Variable ein Index angegeben wird. Mit BIT[1] bis BIT[8] können beispielsweise alle Bits von BYTE[1] einzeln angesprochen werden. Mit BIT[1] bis BIT[16] alle Bits von WORD[1]. Mit BIT[17] beginnt das BYTE[3] und WORD[2]. Die Bitvariablen werden in den ersten 32 Bytes des USER-RAMs abgelegt.
Um dem Anwender die Aufteilung des zur Verfügung stehenden RAM-Speichers zu erleichtern, unterstützt der OCBASIC-Compiler eine neue OF-Syntax, mit der Bitvariablen nicht mehr auf festen Positionen im USER-RAM gespeichert werden müssen, sondern innerhalb von Byte- oder Wordvariablen als Bitfelder ("Bitarrays") abgelegt werden können. Die Byte- oder Wordvariablen lassen sich vom Compiler automatisch verwalten. Durch dieses Verfahren müssen keine Variablen mehr manuell an festen Positionen plaziert werden.
Nach Programmstart ohne Fehler wird vom Betriebssystem der gesamte Variablenspeicher mit Ausnahme von VERSION auf 0 gesetzt. Damit alle Variablen auf 0 stehen, sollte der Anwender bei Programmstart VERSION=0 ausführen.
Auf der OM ist es guter Programmierstil, alle Variablen vom Compiler OCBASIC automatisch zuweisen zu lassen. Mit der neuen "OF"-Syntax klappt das auch bei BIT-Variablen. Der Controller kann aber nur 256 BIT-Variablen ansprechen und diese müssen in den ersten 256/8=32 Byte des USER-RAMs liegen. Deshalb sollten die BYTE-Variablen, in denen die BIT-Variablen liegen, zu Beginn definiert werden.
Mit dem Befehl wird eine Byte-Variable definiert. Die Variable kann also einen Wert von 0 bis 255 annehmen. Alle Variablen, die im BASIC-Programm verwendet werden, müssen vorher definiert werden. Bei der Open-Micro und Open-Mini stehen 64 Variablen (80, wenn einige Funktionen nicht benötigt werden) zur Verfügung. Auf der Open-Midi sind es sogar über 170 Byte. Nach Programmstart sind alle Variablen gelöscht, das heißt sie enthalten den Wert 0.
Der Anwender sollte bei der manuellen Vergabe von Speichernummern (z.B.: DEFINE var BYTE[1]) besonders vorsichtig sein, denn bei allen vom Compiler automatisch positionierten Variablen werden der Reihe nach Speicherstellen vergeben, wobei alle manuellen Speicherzuweisungen ignoriert werden. Es kann dadurch zu ungewollten Überlagerungen der Variablen und daher zwangsläufig zu Fehlern im Programm kommen. Die manuell zugewiesenen Variablen werden vom Compiler auch nicht der Anzahl der benutzen RAM-Bytes hinzuaddiert.
Nach Programmstart ohne Fehler wird vom Betriebssystem der gesamte Variablenspeicher mit Ausnahme von VERSION auf 0 gesetzt. Damit alle Variablen auf 0 stehen, sollte der Anwender bei Programmstart VERSION=0 ausführen.
Mit dem Befehl wird ein 8-Bit-Digitalport definiert. Daraufhin können PORT[1] bis PORT[6] und PORT[9] bis PORT[16] im Byteformat angesprochen werden. Das entspricht PORTA und PORTB vom OM-Controller.
Schließt die Datei, nachdem sie mit OPEN# FOR geöffnet wurde. Benötigt wird der Befehl nur, wenn die Datei verändert worden ist. Der Befehl ist mit Vorsicht zu genießen, da der Controllerhersteller nur 10.000 Schreibzyklen garantiert.
Test, ob die CTS-Leitung aktiv ist. Das dient für Hardware-Handshake (RTS und CTS) an der seriellen Schnittstelle, das nur die C-Control Unit 1.1 bietet.
Auf der OM liefert diese Funktion immer ON zurück. Für interruptgesteuerten RS232-Empfang existiert ein spezielles Assembler-Modul.
Stellt einen Digital-Analog-Port (PWM) bereit.
Die OM unterstützt diese
Ports standardmäßig nicht. Wenn ein PWM-Port benötigt wird, muß er über ein nachladbares Assembler-Modul realisiert werden.
Tag-Wert der Echtzeituhr.
Die OM unterstützt standardmäßig keine Datumsfunktion in der Echtzeituhr und
die interne Variable DAY liegt deshalb auf dem gleichen RAM-Speicherplatz wie die Torzeit zur Frequenzmessung (siehe FREQ1).
Mit dem Befehl wird ein Port, der vorher als Ausgang benutzt wurde, wieder auf Eingang geschaltet, also in den hochohmigen Anfangszustand versetzt.
Die OM besitzt interne Pull-Up-Widerstände, die standardmäßig aktiviert sind und die Digitalports mit High-Potential (5 Volt) verbinden, solange die Ports auf Eingang geschaltet sind.
Mit dem Befehl wird ein Bezeichner definiert. Alle Bezeichner (Ports, Variablen
und Konstanten) müssen definiert werden, bevor sie verwendet werden können.
Falls der exakt gleiche Bezeichner mehrfach definiert wird, gibt der Compiler nur eine Warnmeldung aus. Bei gleichnamigen, sonst aber ungleichen Bezeichnern wird während der Übersetzung ein Fehler ausgegeben.
Der Anwender sollte bei der manuellen Vergabe von Speichernummern (z.B.: DEFINE var BYTE[1]) besonders vorsichtig sein, denn bei allen vom Compiler automatisch positionierten Variablen werden der Reihe nach Speicherstellen vergeben, wobei alle manuellen Speicherzuweisungen ignoriert werden. Es kann dadurch zu ungewollten Überlagerungen der Variablen und daher zwangsläufig zu Fehlern im Programm kommen. Die manuell zugewiesenen Variablen werden vom Compiler auch nicht der Anzahl der benutzen RAM-Bytes hinzuaddiert.
Auf der OM ist es guter Programmierstil, alle Variablen vom Compiler OCBASIC automatisch zuweisen zu lassen. Mit der neuen "OF"-Syntax klappt das auch bei BIT-Variablen. Der Controller kann aber nur 256 BIT-Variablen ansprechen und diese müssen in den ersten 256/8=32 Byte des USER-RAMs liegen. Deshalb sollten die BYTE-Variablen, in denen die BIT-Variablen liegen, zu Beginn definiert werden.
Wochentag-Wert der Echtzeituhr.
Die OM unterstützt standardmäßig keine Datumsfunktion in der Echtzeituhr und
die interne Variable DOW liegt deshalb auf dem gleichen RAM-Speicherplatz wie FREQ1.
Der Befehl wird im Zusammenhang mit IF..THEN verwendet. Falls die bei IF abgefragte Bedingung nicht erfüllt ist, werden die Anweisungen im ELSE-Zweig ausgeführt.
Der Befehl beendet die Programmausführung. Die OM befindet sich danach in einer Endlosschleife, sofern End2Host nicht aktiviert wurde.
Durch Angabe eines weiteren Schlüsselworts hinter dem END sind auch andere Bedeutungen denkbar: Das "END IF" entspricht dem "ENDIF" und beendet ein mehrzeiliges IF..THEN. Das "END TABLE" entspricht "TABEND" und beendet eine Datentabelle. Und ein "END SYSCODE" entspricht "SYSEND" und beendet eine SYSCODE-Liste.
Test auf Dateiende beim Auslesen der Datei mittels INPUT#. EOF liefert erst nachdem die Datei geöffnet wurde ein sinnvolles Ergebnis.
Test auf freien Speicherplatz vor dem Schreiben in die Datei mittels PRINT#.
FILEFREE liefert erst nachdem die Datei geöffnet wurde ein sinnvolles Ergebnis.
Auf der C-Control 1.1 wird die Anzahl der noch freien Words in der
Datei zurückgeliefert. Da die OM nur im Byteformat rechnet und in
der Datei über 2048 Byte frei sein können,
nimmt der Einfachheit halber diese interne Variable nur
die Werte ON und OFF an. Es sieht also bis zum
letzten Byte so aus, als ob noch 255 Byte frei wären.
Mit diesen Befehlen läßt sich eine Programmschleife realisieren.
Dadurch kann die Programmgröße verringert und die Verständlichkeit des Programmflusses erhöht werden.
Die Variable wird durch das FOR zunächst auf den Anfangswert gesetzt, dann die Anweisungen innerhalb der FOR..NEXT-Schleife ausgeführt und anschließend wird die Variable bei NEXT mit dem Endwert verglichen. Falls die Variable ungleich dem Endwert ist, wird die Variable inkrementiert und es werden die Anweisungen innerhalb der Schleife und das NEXT erneut ausgeführt. Durch die FOR..NEXT-Schleife lassen sich Anweisungen also mehrfach ausführen.
Auf C-Control-kompatiblen Controllern wird anders als in den meisten anderen BASIC-Dialekten der Endwert auf Gleichheit getestet, nicht auf "größer". Dadurch kann der gesamte Wertebereich einer Variable ausgenutzt werden. Außerdem wird der Inhalt der Schleife wenigstens einmal ausgeführt.
Optional läßt sich mittels STEP-Befehl eine Schrittweite für die Inkrementierung angeben. Durch Angabe eines negativen STEP-Werts wird eine Dekrementierung erreicht. Das funktioniert auch auf der OM, obwohl diese eigentlich nur mit positiven Zahlen im Byteformat umgehen kann.
Mit dieser internen Variable kann die Frequenz am FREQ-Pin (PORT[3]) abgefragt werden. Die Messung
erfolgt im Hintergrund, parallel zur Programmausführung.
Die Frequenzmessung wird erst aktiviert, nachdem
die Torzeit (Meßzeit) eingestellt wurde. Die Torzeit wird
in 20-ms-Schritten angegeben und wird erst nach Ablauf der vorherigen wirksam.
Durch "FREQ=0" oder "FREQ=OFF" läßt sich die Frequenzmessung deaktivieren.
Während BEEP, GET, INPUT und SLOWMODE werden alle Interrupts deaktiviert und deshalb wird auch FREQ nicht mehr aktualisiert.
Zurückgeliefert wird die Anzahl der innerhalb der Meßzeit aufgetretenen Impulse. Und zwar wird bei jeder fallenden Flanke am KBI2-Pin (liegt am gleichen Pin wie PORT[3]) ein Interrupt ausgelöst. Die Frequenz, gezählt in Impulse pro Sekunde, läßt sich daraus berechnen zu
f = FREQ / MESSZEIT.
Achtung: Nach einem Reset muß PORT[3] auf High-Potential (5 Volt) liegen, sonst geht der Controller in den Hostmodus und arbeitet das BASIC-Programm nicht ab.
Auf der Original-C-Control diente FREQ2 zur Bestimmung der Frequenz eines Signals am FREQ2-Pin.
Auf der OM läßt sich mit dieser internen Variable der Systemtakt von 3,2 MHz justieren (kalibrieren). Anders als auf der Conrad-Micro läßt sich FREQ2 auch auslesen. FREQ2 entspricht dem Controller-Register OSCTRIM auf Adresse $38.
Mit diesem Register wird die Größe des internen Kondensators verändert, der Verwendung im internen Oszillator findet. Durch Erhöhen des FREQ2-Werts um eins wird auch die Periodendauer um rund 0,2% erhöht. Die Taktfrequenz wird also niedriger. Beim Verringern des FREQ2-Wert verhält es sich entsprechend umgekehrt. Die maximale Abweichung vom benötigten TRIM-Wert liegt somit bei 0,1%.
Mit einem Controller, der mehr als etwa 5% vom Nominaltakt abweicht, kann nicht mehr
über die serielle Schnittstelle kommuniziert werden. Das OSCTRIM-Register wird deshalb vom Betriebssystem nach jedem Reset mit dem vom OSCTRIM.EXE-Programm ermittelten und im Flash-Speicher auf Adresse $ffc0 stehenden Wert geladen, um den Controllertakt auf 3,2 MHz zu trimmen.
Die OM wird über einen internen Oszillator getaktet, der mit einer relativ stabilen Frequenz arbeitet. Vom Controllerhersteller Freescale wird die Abweichung der einmal kalibrierten Frequenz zwar mit bis zu ±5% angegeben, aber diese Angabe bezieht sich auf den gesamten Temperatur- und Versorgungsspannungsbereich. Wird der Controller kalibriert und danach bei konstanter Temperatur und Spannung eingesetzt, ist es denkbar, daß die Echtzeituhr dagegen pro Tag nur um maximal 1,44 Minuten falsch geht. Je niedriger die Versorgungsspannung, desto empfindlicher reagiert der Controller allerdings auf Temperaturschwankungen. Wer eine exakte Zeitbasis benötigt, kann die OM wie die C-Control/M-Unit V2.0 mit einem externen Taktgeber ausstatten. Dafür muß allerdings ein I/O-Pin geopfert werden und der Stromverbrauch steigt drastisch. Siehe CONFIG2 im Controllermanual.
Nach den Angaben von Freescale kann der Controller Schaden nehmen, falls bestimmte Zeiten beim Beschreiben und Löschen des Flash-Speichers nicht eingehalten werden. Die Zeitverzögerungen im Betriebssystem der OM gehen von einem Systemtakt von 3,2 MHz aus. Bei anderen Taktraten sollten deshalb PRINT# und CLOSE# nicht verwendet werden.
Mit dem Befehl kann ein Byte (Zeichen) von der seriellen Schnittstelle gelesen
werden. Das BASIC-Programm wartet dann solange, bis ein Zeichen über die serielle
Schnittstelle empfangen wurde, bevor die Befehlsausführung fortgesetzt wird.
Zu beachten ist, daß während des Wartens keine Interrupts verarbeitet werden. Aus diesem Grund läuft die interne Echtzeituhr nicht weiter.
Für interruptgesteuerten RS232-Empfang existiert auf der OM ein spezielles optionales Assembler-Modul.
Mit dem Befehl wird eine Unterroutine aufgerufen. Die Unterroutine wird
durch einen RETURN-Befehl beendet und anschließend die Programmausführung mit dem Befehl hinter dem GOSUB fortgesetzt. Innerhalb einer Unterroutine läßt sich eine andere Unterroutine aufrufen. Die Verschachtelung von Subroutinen (die Größe des GOSUB-Stacks) ist auf vier Ebenen begrenzt. Auf der OM stehen fünf Ebenen (auf der Open-Midi acht) zur Verfügung, allerdings wird eine davon für den Aufruf von BASIC-Interruptroutinen verwendet.
Statt Unterroutine sind auch die Bezeichnungen Subroutine, Prozedur und Unterprogramm üblich. Eine Sonderform der Unterroutine ist die Funktion. Auch zum Aufruf einer Funktion wird intern ein GOSUB verwendet.
Mit dem Befehl wird ein Programmsprung realisiert. Das BASIC-Programm arbeitet dann hinter dem Label weiter.
Der Controller wird veranlaßt, das Hardware-Handshake auf der RTS- und CTS-Leitung an der seriellen Schnittstelle zu aktivieren.
Bisher sind nur bei der Unit der C-Control 1.1 die benötigten Leitungen herausgeführt. Deshalb ist nur die Unit 1.1 in der Lage, Hardware-Handshake zu aktivieren. Auf der OM wurde daher der HANDSHAKE-Befehl wie bei der C-Control 2.0 umfunktioniert, um Zeigeroperationen (bzw. Array-Funktionen) zu ermöglichen.
Stundenwert der Echtzeituhr.
Auf der C-Control 1.1 nimmt diese interne Variable Werte von 0 bis 23 an. Der Wert 24 wird nie erreicht, sondern die DAY-Variable automatisch hochgezählt, da ein neuer Tag begonnen hat. Die OM unterstützt standardmäßig keine Datumsfunktion in der Echtzeituhr. Deshalb zählt der Stundenwert über 23 hinaus bis 255. Das hat den Vorteil, daß die Echtzeituhr erst nach 10 Tagen und 16 Stunden wieder auf Null steht. Außerdem kann der Anwender vor der Abfrage der Uhrzeit einen Tageswechsel erkennen, die Echtzeituhr also beliebig erweitern, sofern das erforderlich ist.
Mit dem Befehl läßt sich eine bedingte Befehlsausführung realisieren. Der Befehl vergleicht eine Bedingung auf wahr (ungleich Null) oder unwahr (gleich Null). Die Bedingung ist also ein logischer Ausdruck. Nach dem Vergleich kann festgelegt werden, wie es weitergehen soll. Wenn die Bedingung wahr ist, werden die Befehle im THEN-Zweig ausgeführt. Falls die Bedingung unwahr ist, wird, sofern vorhanden, mit ELSEIF, ELSE oder hinter dem ENDIF weitergemacht. Ein einzeiliges IF darf nicht mit ENDIF beendet werden, kann aber im THEN- und ELSE-Zweig, anders als in CCBASIC, mehrere Anweisungen enthalten.
Wie in CCBASIC ist es möglich, auch in OCBASIC die Syntax "IF bedingung THEN label" zu benutzen. In CCBASIC wird ein unbedingter Sprung zum Sprungziel ausgeführt, wenn die Bedingung erfüllt ist. Das gilt in OCBASIC jedoch nur, wenn die Programmzeile unmittelbar hinter diesem IF-Konstrukt endet oder aber ein "ELSE" folgt. In allen anderen Fällen wird ein Subroutinenaufruf kodiert. Der Anwender sollte deshalb bei diesem IF-Konstrukt sicherheitshalber ein GOTO oder GOSUB vor dem Label angeben, um sicherzustellen, daß wie gewünscht übersetzt wird.
Einlesen eines Zahlenwertes im ASCII-Textformat von der seriellen Schnittstelle. Der INPUT-Befehl wartet solange, bis eine komplette Eingabe, beendet mit Carriage Return (CR, ASCII-Code 13) oder einem anderen Sonderzeichen kleiner "0", empfangen wurde. In einem Terminalprogramm wird Carriage Return von der Eingabe-Taste, auch Enter oder Return genannt, generiert. Zu beachten ist, daß während des Wartens auf ein Zeichen keine Interrupts verarbeitet werden. Aus diesem Grund läuft die interne Echtzeituhr nicht weiter.
Einlesen eines Zahlenwertes aus der Datei.
Auf der OM wird ein Byte aus der Datei gelesen, auf der C-Control 1.1 dagegen ein Word.
Vereinbart eine BASIC-Interruptroutine. Beendet wird die Routine mit RETURN INTERRUPT.
Auf der Open-Micro und Open-Mini wird vor jedem Start der Interruptroutine der Rechenstack in den letzten 8 Bytes des User-RAMs gesichert. Deshalb stehen BYTE[57] bis BYTE[64] nicht mehr für andere Zwecke, insbesondere zur Variablenspeicherung, zur Verfügung, solange die BASIC-Interruptroutine starten könnte. Mittels RealPop kann man die OM veranlassen, den Rechenstack nicht mehr zu sichern. Die Open-Midi sichert den Rechenstack in einem dem Anwender üblicherweise nicht zugänglichen Speicherbereich außerhalb des User-RAMs.
Wenn die Interruptroutine starten soll, wenn am IRQ-Pin eine negative Flanke detektiert wird, dann muß zunächst diese Funktionalität im CONFIG2-Register des Controllers aktiviert werden. Das Register läßt sich
nach jedem Reset nur einmal beschreiben ("write once"), nachfolgendes
Beschreiben wird ignoriert.
Nach Aktivieren der IRQ-Funktion läßt sich der Port nicht mehr als Digitalport oder zur Frequenzmessung verwenden.
Mit Hilfe eines kleinen Assemblermoduls läßt sich die BASIC-Interruptroutine zum Beispiel auch periodisch in einem bestimmten Zeitabstand starten ("ON TIMER"-Funktion) oder dann, wenn sich der Zustand an einem Digitalport verändert ("Keyboard Interrupt").
Lesen eines Wertes aus einer Tabelle. Der erste Wert aus der Tabelle steht auf der Index-Position 0. Auf der OM arbeitet LOOKTAB im Byteformat. Auf der C-Control 1.1 dagegen im Wordformat.
Ermittlung des größeren Wertes.
Ermittlung des kleineren Wertes.
Minuten-Wert der Echtzeituhr.
Operator für Modulodivision. Es wird also der Rest einer ganzzahligen Division zurückgeliefert.
Monats-Wert der Echtzeituhr.
Die OM unterstützt standardmäßig keine Datumsfunktion in der Echtzeituhr und
die interne Variable MONTH liegt deshalb auf dem gleichen RAM-Speicherplatz wie der temporäre Zwischenspeicher zur Frequenzmessung (siehe FREQ1).
Logische und bitweise UND-Verknüpfung mit anschließender Negation (1er Komplement).
a | b | a NAND b |
0 | 0 | 1 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 0 |
Logische und bitweise ODER-Verknüpfung mit anschließender Negation (1er Komplement).
a | b | a NOR b |
0 | 0 | 1 |
0 | 1 | 0 |
1 | 0 | 0 |
1 | 1 | 0 |
Logische und bitweise Negation (1er Komplement). Jedes Bit des Operanden wird invertiert (umgedreht, getoggelt).
a | NOT a |
OFF (gleich 0) | ON (gleich -1, bzw. 255) |
ON (gleich -1, bzw. 255) | OFF (gleich 0) |
1 | -2, bzw. 254 |
Vordefinierte Konstante für 0, also logisch unwahr.
Vordefinierte Konstante für -1 (oder &HFFFF), also logisch wahr. Da die OM ausschließlich mit nicht vorzeichenbehafteten Bytewerten rechnet, entspricht ON dem Wert 255 (&HFF). Trotzdem kann ON wie gewohnt verwendet werden.
In allen BASIC-Dialekten liefern logische Ausdrücke wie "a>10" oder "7<=9" entweder den Wert -1 oder 0 zurück, je nachdem, ob sie erfüllt oder nicht erfüllt sind. Die liefern also nicht 1 und 0 zurück, wie zum Beispiel in C. Im Zweierkomplement entspricht -1 der vorzeichenbehafteten Integerzahl, bei der alle Bits gesetzt sind. Aus diesem Grund ist es in BASIC nicht erforderlich, logische Operatoren von binären Operatoren zu unterscheiden. Auch für logische Operationen können die binären Operatoren verwendet werden. Ein weiterer Vorteil sind Verknüpfungsmöglichkeiten wie "a AND b<10". In diesem Fall liefert der Ausdruck den Wert a zurück, wenn b kleiner 10 ist, andernfalls wird 0 zurückgeliefert.
Unterroutinenaufruf in Abhängigkeit von x.
In OCBASIC kann für "x" anders als in CCBASIC jeder beliebige Ausdruck verwendet werden. Allerdings führt das zu unnötig langen Programmen. Sinnvollerweise sollte deshalb der Ausdruck einer Variablen zugewiesen und anschließend diese Variable abgefragt werden.
Programmsprung in Abhängigkeit von x.
In OCBASIC kann für "x" anders als in CCBASIC jeder beliebige Ausdruck verwendet werden. Allerdings führt das zu unnötig langen Programmen. Sinnvollerweise sollte deshalb der Ausdruck einer Variablen zugewiesen und anschließend diese Variable abgefragt werden.
Die zur permanenten Datenspeicherung vorgesehene Datei öffnen. Die Daten werden im internen Flash-Speicher des Controllers abgelegt und gehen auch nach Entfernen der Betriebsspannung nicht verloren. Es stehen die Modi WRITE, APPEND und READ zur Verfügung.
Mit OPEN# FOR WRITE wird die Datei zunächst gelöscht und alle per FILEFREE und PRINT# in die Datei geschriebenen Daten überschreiben unwiederbringlich die alten Daten. Erst nach CLOSE# wird der geänderte Dateiende-Zeiger dauerhaft gespeichert.
Mit OPEN# FOR APPEND werden neue Daten per FILEFREE und PRINT# an die bereits in der Datei stehenden angehängt. Erst nach CLOSE# wird der geänderte Dateiende-Zeiger dauerhaft gespeichert.
Mit OPEN# FOR READ können die in der Datei stehenden Daten mittels EOF und INPUT# ausgelesen werden. Der Befehl CLOSE# ist überflüssig und wird ignoriert.
Logische und bitweise ODER-Verknüpfung, auch Disjunktion genannt.
a | b | a OR b |
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 1 |
Programmunterbrechung für t*20 Millisekunden.
Der Befehl "PAUSE 0" wartet überhaupt nicht.
Der Befehl "PAUSE t" wartet zwischen (t-1)*20 ms und t*20 ms.
Definiert einen Digitalport.
Der Port kann den Zustand 0 (OFF, 0 Volt, GND, Low-Potential) oder -1 (ON, 5 Volt, VDD, High-Potential) annehmen.
Bei Programmstart nach einem Reset sind alle Digitalports auf Eingang geschaltet. Standardmäßig werden die Ports dann durch Pull-Up-Widerstände, die sich im Controller befinden, auf High-Potential gelegt. Die Widerstände lassen sich bei Bedarf durch PULLUPA und PULLUPB abschalten, wodurch die Digitalports sehr hochohmig werden. Sobald einem Digitalport ein Wert zugewiesen worden ist, ist dieser auf Ausgang geschaltet und wird vom Controller niederohmig auf 0 Volt oder 5 Volt gelegt ("Push/Pull"). Die Ports sind in dieser Betriebsart in der Lage, relativ hohe Ströme zu liefern, bzw. nach Masse abzuleiten. Laut Datenblatt dürfen es kurzzeitig maximal 15 mA an PORT[9] bis PORT[16] und sogar 25 mA an PORT[1] bis PORT[6] sein. Im sicheren Bereich arbeitet man aber nur, wenn die Summe der Ströme an allen Ports auf 50 mA beschränkt ist.
Siehe auch OFF, ON, DEACT, TOG, PULSE, BYTEPORT, WORDPORT, PULLUPA und PULLUPB.
Ausgabe von Werten und Texten über die serielle Schnittstelle.
Schreiben eines Wertes in die Datei.
Auf der OM wird ein Byte in die Datei geschrieben, auf der C-Control 1.1 dagegen ein Word.
Nachdem die OM ein Byte in die Datei geschrieben hat, wird kontrolliert, ob das Byte fehlerfrei in den User-Flash programmiert werden konnte. Ist das nicht der Fall, weil zum Beispiel der Flash-Speicher erschöpft ist, wird ein File-I/O-Fehler ausgelöst, der normalerweise die Programmausführung unterbricht. Der Fehler kann aber auch vom Anwender mittels RunOnErr ausgewertet werden. In diesem Fall bleibt das Interruptsystem deaktiviert und einige Bytes im User-RAM werden nicht restauriert.
Wenn der Anwender hingegen versucht, auf eine Adresse außerhalb des User-Flashs zu schreiben, weil z.B. PRINT# unkorrekterweise ohne vorheriges OPEN# benutzt wurde, wird das Schreiben nicht ausgeführt, um unter anderem das Betriebssystem vor dem Überschreiben zu schützen. Bei einem Schreibversuch außerhalb des User-Flash wird jedoch nicht in jedem Fall ein File-I/O-Fehler ausgelöst.
Weiterhin ist zu beachten, daß im User-Flash nicht nur die Datei, sondern auch das Anwenderprogramm gespeichert wird. Unkorrekte Benutzung von PRINT# kann daher das Anwenderprogramm überschreiben.
Ausgabe eines Pulses an einem Digitalport. Der Port muß vorher mittels "port = ON" oder "port = OFF" aktiviert worden sein und wird durch PULSE zweimal geTOGgelt. Die Pulsbreite beträgt auf der OM mindestens 4 µs.
Ausgabe eines Bytes über die serielle Schnittstelle.
Zufallszahl im Integerformat generieren.
Die interne Variable RAND belegt auf der OM den gleichen RAM-Speicher wie DBNZCTR und ERR.
Der Algorithmus zur Generierung der Zufallszahlen ("Methode der linearen Kongruenz") ist auf der OM zwar besser als auf der C-Control, aber systembedingt wird immer wieder die gleiche aus 256 Elementen bestehende Folge von Pseudo-Zufallszahlen generiert. RANDOMIZE verschiebt nur den Beginn der Folge. Durch eine eigene Erweiterung auf 16 Bit könnte man das Problem etwas entschärfen. Aber echte Zufallszahlen zu generieren ist gar nicht so einfach. Dazu könnte man zum Beispiel eine Variable schnell hochzählen, bis der Anwender eine Taste losläßt.
Mit der Methode der linearen Kongruenz wird das nächste Glied der Folge aus dem vorherigen allgemein berechnet zu
x[i+1] = (a*x[i]+1) mod m .
Die Open-Micro benutzt die Gleichung
x=(21*x+1) mod 256 .
Welche Eigenschaften die durch "Methode der linearen Kongruenz" erzeugten Pseudo-Zufallzahlen genau besitzen kann in Fachliteratur wie beispielsweise Donald Knuth, The Art of Computer Programming, Vol. 2, USA 1981 (Addison-Wesley) nachgelesen werden. Um den Wertebereich der erzeugten Zufallszahlen einzuschränken ist MOD offensichtlich nicht so gut geeignet wie eine Division. Mittels "RAND/64" beispielsweise erzeugt die OM brauchbare Zufallszahlen zwischen 0 und 3.
Zufallszahlengenerator neu initialisieren.
Nach einem Reset läuft der Timer in der OM von Null los. Ein RANDOMIZE TIMER am Programmbeginn ist also nichts anderes als ein RANDOMIZE 0. In diesem Fall würden also nach jedem Reset die gleichen Zufallszahlen generiert. Solange Synchronität zwischen Systemzeit und RANDOMIZE TIMER besteht, wird sich daran nichts ändern.
Datei öffnen zum Auslesen von Daten.
Rückkehr aus einer Subroutine (Unterprogramm) oder einer Funktion. Bei Funktionen läßt sich ein Wert über den sogenannten Rechenstack an den Aufrufer zurückgeben.
Rückkehr aus der BASIC-Interruptroutine. Siehe INTERRUPT-Befehl.
Test, ob ein Byte seriell empfangen wurde.
Auf der OM liefert diese Funktion immer ON zurück. Für interruptgesteuerten RS232-Empfang existiert ein spezielles Assembler-Modul.
Sekunden-Wert der Echtzeituhr.
Auf der OM wird TIMER automatisch auf Null gesetzt, wenn SECOND beschrieben wird.
Während des Zugriffs auf die Variablen der Echtzeituhr läuft die interne Uhr weiter. Das muß insbesondere beim Auslesen der Uhrzeit beachtet werden. Es muß zunächst der Sekundenwert in einer Variable zwischengespeichert, dann die übrige Uhrzeit gelesen und anschließend das Auslesen wiederholt werden, falls der zwischengespeicherte Sekundenwert 59 und gleichzeitig der aktuelle Sekundenwert 0 ist. In diesem Fall könnte der Minuten- und sogar der Stundenwert unkorrekt sein.
Die mathematische Signum-Funktion.
Die Funktion SGN arbeitet auf der OM anders als auf der Original-C-Control (Version 1.1). Sie liefert das Most Significant Bit (MSB) vom Ausdruck zurück. Nicht in jedem Fall ist dieses MSB ein Vorzeichen, da die OM ausschließlich mit nicht vorzeichenbehafteten Bytewerten rechnet.
Operator für bitweises arithmetisches Linksschieben. Das zuletzt herausgeschobene Bit wird in der internen Variable Carry zurückgeliefert.
Operator für bitweises arithmetisches Rechtsschieben. Das zuletzt herausgeschobene Bit wird in der internen Variable Carry zurückgeliefert.
Der Controller wird in einen stromsparenden Modus versetzt.
Auf der OM wird der Parameter ignoriert und ein "STOP"-Befehl ausgeführt, wodurch fast der gesamte Controller stillsteht, bis er durch das Auto-Wakeup-Modul (AWU) aufgeweckt wird. Das geschieht nach ungefähr 16 ms, wobei diese Zeit stark von der Betriebsspannung und der Temperatur abhängig ist. In dieser Zeitspanne benötigt der Controller nur einige wenige µA Strom.
Der Befehl STOP und somit auch SLOWMODE verträgt sich offenbar nicht mit aktivierter KEYB-Pin-Funktionalität und somit aktiver Frequenzmessung (siehe FREQ1): Ist der KEYB-Pin low, wenn STOP ausgeführt wird, fällt der Controller ins Koma und wacht nur nach einem Power-On-Reset (POR) wieder auf.
Das Betriebssystem der OM konfiguriert den Controller so, daß der LVI-Reset im STOP-Mode nicht aktiv ist. Aus diesem Grund funktioniert der Reset-Taster auf dem ersten und zweiten PE-Board während SLOWMODE nicht. Wenn der Controller resettet werden soll kann aber ein Power-On-Reset (POR) durch kurze Wegnahme der Betriebsspannung durchgeführt werden.
Diese Funktion liefert einen ganzzahligen Näherungswert für die Berechnung der Quadratwurzel.
Auf der OM wird das mathematisch korrekt gerundete Ergebnis zurückgeliefert.
Angabe der Schrittweite bei einer FOR-Schleife. Ohne Angabe von STEP wird eine Schrittweite von eins verwendet.
Auch negative STEP-Werte sind möglich. Das funktioniert auch auf der OM, obwohl diese eigentlich nur mit positiven Zahlen im Byteformat umgehen kann.
Zu beachten ist, daß die Variable mit dem bei FOR angegebenen Endwert auf Gleichheit verglichen wird. Es ist also durchaus möglich, nie endende Schleifen zu erzeugen.
Aufruf einer Maschinensprache-, bzw. Assembler-Routine, die mit dem Befehl "RTS" abgeschlossen werden muß. Optional lassen sich bis zu sieben Parameter (auf Open-Midi acht) über den sogenannten Rechenstack an die Assembler-Routine übergeben. Eine vom Anwender per AS05 erstellte Assembler-Routine wird üblicherweise mittels SYSCODE in den BASIC-Quellcode eingebunden. Bei der C-Control 1.1 beginnt dieser SYSCODE-Bereich an Adresse &h101. Bei der OM an Adresse &hFD00.
Die Controller, auf denen die Open-Micro, Open-Mini und Open-Midi basieren, stammen von Freescale (heute NXP). Auf den Websides zu den 68HC908Q-Mikrocontrollern können neben diversen Application Notes mit Beispielcode auch das Data Sheet zum QY4/QT4 heruntergeladen werden.
Es gibt auch ein Data Sheet zum QB8 für die Open-Midi.
Für Assemblerprogrammierer besonders interessant ist das Central Processor Unit Reference Manual CPU08RM von Freescale, in dem alle Befehle der 6808-CPU detailliert erläutert werden.
Auf der offiziellen OM-Infosite gibt es bei den Downloads auch eine Assemblerbeschreibung in deutscher Sprache.
Interessante Informationen zum Controller stehen auch im Timer Interface Module Reference Manual TIM08RM. Auf dem deutschen Büchermarkt war vor einiger Zeit das Franzis'-Buch "68HC08-Mikrocontroller erfolgreich anwenden" erhältlich. Das sollte 128,- DM kosten, wurde aber wegen zuvieler Fehler später für wenige Mark verramscht. Auf dem Second-Hand-Markt ist es sicherlich noch immer erhältlich. Es behandelt natürlich nicht die aktuellen 68HC08-Controller.
Der SYS-Befehl ist sehr mächtig. Die CPU kann damit veranlaßt werden, an beliebiger Adresse stehenden Maschinencode auszuführen. Der Anwender sollte deshalb vor der Verwendung dieses Befehls genau prüfen, ob an der Zieladresse ein korrektes Maschinenspracheprogramm steht. Ansonsten könnte der Controller abstürzen oder Schaden nehmen. Ein Sprung in nichtimplementierten Speicher wird allerdings abgefangen und bewirkt einen "Illegal Address Reset" und bei der Ausführung von ungültigen Maschinensprachebefehlen wird ein "Illegal Opcode Reset" ausgelöst, woraufhin das Betriebssystem in den Systemmonitor MON08 verzweigt. Dieser kann über die serielle Schnittstelle an PORT[1] (gleich PTA0) bedient werden.
Einbinden einer S19-Datei mit Assembler-Routinen in das BASIC-Programm. Die Assembler-Routinen werden in Form von sogenannten "SYS-Bytes" an den Controller übertragen und in den SYSCODE-Bereich geschrieben.
Beim Dateinamen werden nun auch beliebige Pfade unterstützt.
Falls kein Pfad angegeben wird, dann sucht der OCBASIC-Compiler zuerst im Verzeichnis
mit dem BASIC-Sourcecode nach der Datei. Ist sie dort nicht zu finden,
wird im Compiler-Verzeichnis und schließlich im aktuellen Verzeichnis gesucht.
Um eine S19-Datei zu erzeugen, kann der bekannte AS05-Assembler von Frank A. Vorstenbosch verwendet werden: Der Aufruf "as05 -sl datei.asm" erzeugt die Ausgabedatei "datei.s19".
Die Bezeichnung Assembler-Routine ist nicht ganz korrekt. Genaugenommen handelt es sich um Maschinencode-Routinen.
Auf der Open-Micro, Open-Mini und Open-Midi können Assemblerroutinen genauso wie bisher auf der C-Control Version 1.1 per SYSCODE in den BASIC-Sourcecode eingebunden und bei Bedarf per SYS aufgerufen werden. Darüber hinaus ist es möglich, ausgetestete Assemblerroutinen in sogenannte INLINE-Assemblerroutinen und INLINE-Interrupt-Assemblerroutinen umzuwandeln und auf diese Weise platzsparend in ein CCBASIC-Programm einzubinden. OCBASIC besitzt einen integrierten 6808-Assembler und ist deshalb nicht mehr auf SYSCODE angewiesen. Weitere Informationen sind in den Beispielprogrammen zur OM enthalten.
Abschluß einer mit SYSCODE direkt eingegebenen Byte-Liste mit Assembler-Routinen.
Definition einer Datentabelle. Die Werte in der Tabelle lassen sich mit dem LOOKTAB-Befehl auslesen. Datentabellen und Programmcode sollten nicht vermischt werden, da dann unter Umständen die Daten in der Tabelle als Programmcode ausgeführt werden. Sicherheitshalber sollten Datentabellen deshalb an das Ende des BASIC-Quellcodes hinter ein END gesetzt werden. Kommata zwischen den Werten in der Tabelle sind optional.
Auf der OM existieren spezielle Tabellenformen, die INLINE-Assemblerprogramme enthalten. Es gibt auch Tabellen mit Tokencode. Nähere Information dazu befindet sich in den Beispielprogrammen.
Tabellen enthalten standardmäßig konstante Werte im Wordformat. LOOKTAB arbeitet auf der OM aber im Byteformat. Um Probleme zu vermeiden, kann das Schlüsselwort BYTE hinter dem Tabellennamen eingefügt werden. Die Daten in der Tabelle werden anschließend als Bytes behandelt und belegen nur halb soviel Speicher.
Abschluß bei mit TABLE direkt eingegebenen konstanten Werten.
Abfrage des Timers. Der Timer wird alle 20 Millisekunden automatisch erhöht (inkrementiert) und nimmt Werte von 0 bis 49 an. Falls das Weiterzählen der Echtzeituhr mittels ClockDis deaktiviert wurde, zählt der Timer von 0 bis 255.
Während BEEP, GET, INPUT und SLOWMODE werden alle Interrupts deaktiviert und deshalb wird auch der Timer nicht mehr automatisch weitergezählt.
Wenn sich der TIMER zwischen zwei TIMER-Abfragen um eins weiterbewegt
hat sind mehr als 0 ms und weniger als 40 ms vergangen. Im Durchschnitt sind das 20 ms.
Umschalten eines Digitalports. Der Port muß vorher mittels "port = ON" oder "port = OFF" aktiviert werden.
Warten auf Eintreten einer Bedingung.
Definiert eine vorzeichenbehaftete Word-Variable. Diese Funktion steht auf der OM standardmäßig nicht zur Verfügung. Allerdings gibt es ein Softwaremodul, das direkt in den Interpreter eingebunden wird und so gut wie jede Systemfunktion auf 16-Bit erweitert. Dadurch lassen sich unter anderem Wordvariablen und Wordpointer genauso nutzen wie auf den großen C-Controls.
Definiert einen 16-Bit-Digitalport. Somit kann auf alle 16 Digitalports der C-Control gleichzeitig zugegriffen werden.
Auf der OM können nur auf die unteren 8 Bits zugegriffen werden. Dieser Befehl bietet somit eine schnelle Zugriffsmöglichkeit auf BYTEPORT[1]. Bei einem Zugriff auf den Wordport wird also nicht auf BYTEPORT[2] zugegriffen.
Datei öffnen zum Schreiben neuer Daten.
Logische und bitweise Exclusiv-ODER-Verknüpfung, auch Antivalenz genannt.
a | b | a XOR b |
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 0 |
Jahr-Wert der Echtzeituhr.
Die OM unterstützt standardmäßig keine Datumsfunktion in der Echtzeituhr und
die interne Variable YEAR liegt deshalb auf dem gleichen RAM-Speicherplatz wie die temporäre Torzeit zur Frequenzmessung (siehe FREQ1).
Der OCBASIC-Compiler unterstützt einige neue Befehle und interne Variablen, die in CCBASIC nicht zur Verfügung stehen. Sie erzeugen Tokencode, der meist nur von der OM ausgeführt werden kann.
Dient zur schnellen und speicherplatzschonenden Addition bei User-Variablen. Das Carry-Flag wird nicht gesetzt.
Der OCBASIC-Dialekt ermöglicht die Verwendung von Stringkonstanten, die mittels ASC()-Funktion in ihren ASCII-Code, also eine Zahl, umgewandelt werden können. Vergleichbar mit Stringkonstanten in Assembler (z.B. #'0' für #48). Dadurch lassen sich ohne Umrechnerei und umfangreiche Konstantendefinitionen komfortabel einzelne ASCII-Zeichen in den Sourcecode einfügen.
Mittels ASC() ausgewertete Stringkonstanten können ein Zeichen (ein Byte) lang sein. Der Compiler unterstützt bei CVI() aber bis zu zwei Zeichen lange Stringkonstanten, die als Word, bzw. Integerzahl, ausgewertet werden. Es wird das auf Motorola-CPUs übliche Big-Endian-Format verwendet.
Im OCBASIC-Compiler ist ein 6808-Assembler integriert. Assemblerbefehle können mit dem !-Kommando komfortabel, ohne externen Assembler, in das Programm eingefügt werden. Diese Assemblerbefehle sollten immer innerhalb einer INLASM-PROCEDURE oder einer INLASM-FUNCTION stehen. Weitere Informationen zur Assemblerprogrammierung stehen bei SYS. Dem OCBASIC-Compiler liegen außerdem etliche Beispielprogramme bei.
Mit dieser internen Variable kann der Zeiger (Pointer) des Rechenstacks gelesen oder verändert werden.
DBNZ-Schleifen sind eine Alternative zu FOR..NEXT-Schleifen und werden deutlich schneller als diese ausgeführt. Der Zähler in der Schleife ist immer DBNZCTR und belegt auf der OM den gleichen RAM-Speicher wie RAND und ERR. Durch den DBNZ-Befehl wird der Zähler um eins verringert (dekrementiert) und anschließend gesprungen, falls der Zähler ungleich Null ist. ("Decrement and Branch if Not Zero")
Diese interne Variable ist der Zähler für DBNZ-Schleifen. Der Schleifenzähler DBNZCTR hat Byte-Format und der Wert 0 entspricht bei DBNZ dem Wert 256.
Dieser Befehl kann alternativ statt DEFINE benutzt werden und hat die gleiche Funktion wie dieser. Siehe DEFINE.
Sobald ein Fehler auftritt, wird in dieser internen Variable der Fehlergrund als Fehlernummer kodiert abgelegt. Nach Programmstart ohne Fehler enthält ERR den Wert 0. ERR belegt auf der OM den gleichen RAM-Speicher wie DBNZCTR und RAND.
Fehlercode | Erklärung |
0 | kein Fehler |
1 | Zuviele GOSUB-Verschachtelungen |
2 | RETURN ohne GOSUB |
3 | File-I/O-Error |
128 | Illegales Token |
Mit Hilfe dieser Word-Variable läßt sich die Stelle ermitteln, an der ein Fehler auftrat. Es wird die Adresse aus dem Programmzähler (BASIC-PC) nach Auftreten des Fehlers zurückgeliefert, deshalb steht in ERRADR die Adresse des ersten Bytes hinter dem fehlerhaften Tokenbefehl.
ERRADR belegt auf der OM den gleichen RAM-Speicher, der zur Speicherung der Adresse der BASIC-Interruptroutine verwendet wird (siehe INTERRUPT).
Da die Open-Micro kein 16-Bit-Stackhandling unterstützt, läßt sich die Word-Systemvariable ERRADR leider nicht als interne Variable, sondern nur über High- und Lowbyte ansprechen.
Welche Adresse per INPUT# ausgelesen und mit PRINT# beschrieben wird, legt der
Dateipositions-Zeiger FILEPOS im RAM fest. Außerdem
existiert ein Dateiende-Zeiger FILEEND im Flash-Speicher, der bei CLOSE# mit FILEPOS beschrieben wird, falls die Datei verändert wurde.
Da die Open-Micro kein 16-Bit-Stackhandling unterstützt, läßt sich die Word-Systemvariable FILEPOS leider nicht als interne Variable, sondern nur über High- und Lowbyte ansprechen.
Einige Bytes im RAM-Speicher wurden freigehalten, damit Assemblerprogramme
ihre Daten speichern können. Der Speicher läßt sich auch gut zur Parameterübergabe
an Assembler-Interruptroutinen verwenden und die ersten 5 Bytes können in BASIC und Assembler über die internen Variablen
FREERAM1 bis FREERAM5 angesprochen werden. Im Adreßraum des Controllers entspricht das dem
RAM-Speicher ab Adresse $e6 (Open-Micro und Open-Mini), bzw. $6d (Open-Midi).
Auf der Open-Micro und Open-Mini befindet sich hinter dem FREERAM-Bereich der Hardwarestack. Dieser wächst von oben (Adresse $ff)
nach unten.
Zwei Funktionen im Betriebssystem der OM haben einen besonders hohen
Hardwarestack-Bedarf: PRINT# benötigt 21 Byte
und CLOSE# nach OPEN# FOR WRITE oder PRINT# braucht 20 Byte.
Auf der Open-Midi befinden sich hinter dem FREERAM-Bereich zunächst drei unbenutzte Bytes und dann folgt ab Adresse $75 der Speicherbereich, in den der Rechenstack bei Aufruf der BASIC-Interruptroutine gespeichert wird. Siehe auch RealPop.
Assemblerroutinen wird vom Betriebssystem automatisch im
X-Register die Adresse
von FREERAM1 übergeben. Daten lassen sich also schnell z.B. per "STA ,x"
ablegen und mit "LDA ,x" bis "LDA 4,x" lesen.
Die interne Variable FREERAM1 enthält nach Programmstart den Wert aus dem "SIM Reset Status Register" (SRSR) oder 13, falls das Programm durch Drücken von RETURN im Hostmode gestartet wurde. FREERAM1 wird außerdem vom Betriebssystem zur Parameterübergabe an einige IIARs benutzt. Siehe URIllTok und URTok. Es sollte daher nicht in Assembler-Interruptroutinen verwendet werden.
Der Anwender kann eigene Funktionen definieren, die
im Programm genauso wie die Standardfunktionen (z.B. "a=SQR(x)") benutzbar sind.
Funktionen sind eine Sonderform von Unterprogrammen (Subroutinen).
Im Gegensatz zu den Unterprogrammen ist die Rückgabe eines numerischen Wertes
oder einer Stringkonstante möglich.
Im Programm werden Funktionen durch ihren Namen aufgerufen. Hinter dem Namen kann eine Liste von Übergabeparametern angegeben werden. Diese Liste sollte in Klammern eingeschlossen werden. Der Rückgabewert der Funktion kann einer Variablen zugewiesen, über die serielle Schnittstelle ausgegeben, als Parameter einer Funktion oder eines Befehls verwendet werden, etc.
Das FUNCTION-Kommando erzeugt ein Sprungziel (Label), das den Namen der Funktion trägt, danach folgen die Anweisungen zum Holen der Übergabeparameter vom Stack, dann folgt der Funktions-Körper mit den Anweisungen und schließlich wird die Funktion mit einem RETURN verlassen, das zugleich den Rückgabewert der Funktion auf dem Rechenstack ablegt. Das FUNCTION-Schlüsselwort kann zu FUNC verkürzt werden.
Innerhalb einer Funktion besteht vollkommen freier Zugriff auf alle Variablen
des Hauptprogramms. Anders als in anderen BASIC-Dialekten können keine lokalen Variablen definiert werden, die nur innerhalb der Funktion Gültigkeit besitzen.
Der OCBASIC-Compiler überprüft nicht die korrekte Vorgehensweise bei der Übergabe und dem Holen der Parameter mittels Rechenstack. Es fällt in den Aufgabenbereich des Anwenders, darauf zu achten, daß die Anzahl der Parameter übereinstimmt. Andernfalls kommt es zu Stackfehlern, die aber wegen des besonderen Stackhandlings der C-Control normalerweise keine Schwierigkeiten verursachen. Stackfehler sind nur in zwei Fällen kritisch: Wenn innerhalb einer BASIC-Interruptroutine bei aktiviertem RealPop Stackfehler auftreten, führt das zu Fehlern bei Berechnungen und Zuweisungen im Hauptprogramm. Außerdem können bei deaktiviertem RealPop Stackfehler kritisch sein, welche durch Funktionen auftreten, die in einem Ausdruck wie "b+funktion" verwendet werden. Das "b" wird vor dem Funktionsaufruf auf den Rechenstack gelegt und geht durch Stackfehler, die innerhalb der Funktion auftreten, verloren. Die anschließende Addition liefert somit ein falsches Ergebnis. Durch Umstellung der Berechnung zu "funktion+b" läßt sich das Problem beheben. Ein weiterer Vorteil dieser Umformung ist, daß nun in der Funktion der gesamte Rechenstack zur Verfügung steht. Wenn mehr als eine Anwenderfunktion in einem Ausdruck verwendet werden soll, sollten die Funktionen einzeln aufgerufen und deren Rückgabewert zunächst in Variablen zwischengespeichert werden.
Mit dieser internen Variable kann der Zeiger (Pointer) des GOSUB-Stacks gelesen oder verändert werden.
Mit diesem Befehl wird der Compiler angewiesen, eine andere Datei an die aktuelle Position des Sourcecodes einzufügen. Bei der 16-Bit-Version von OCBASIC muß der Pfad und Name der Datei DOS-Konventionen genügen. Der Name darf also maximal 8 Zeichen plus Punkt und Endung enthalten. Bei Dateien mit längerem Namen muß der von Windows bereitgestellte kurze DOS-Name verwendet werden.
Wird kein Pfad angegeben, sucht der Compiler nach der Datei zunächst im Verzeichnis, in dem der BASIC-Sourcecode steht. Falls er dort nicht fündig wird, sucht er im Verzeichnis, in dem sich der Compiler OCBAS.EXE befindet. Falls die Datei auch dort nicht vorhanden ist, wird das aktuelle Verzeichnis ("current directory") benutzt und gegebenenfalls ein Fehler gemeldet.
Die Endungen der INCLUDE-Dateien wurden mit Bedacht gewählt. Die Endung DEF signalisiert dem Anwender, daß diese Datei nur Definitionen (also keine BASIC-Befehle oder Tabellen) enthält und deshalb mittels INCLUDE am Beginn seines Sourcecodes eingefügt werden sollte. Dateien mit der Endung IIA enthalten Systemerweiterungen, die vom Anwender direkt hinter die Definitionen, vor den ersten Befehl seines Programms, gestellt werden müssen.
Die Endung PRO signalisiert, daß in der Datei BASIC- oder Assembler-Unterprogramme stehen. Sie darf vom Anwender deshalb nicht an den Beginn seines Sourcecodes gestellt werden.
Mit diesem Befehl wird der Compiler angewiesen, Daten an die aktuelle Position der Ausgabedatei einzufügen.
Der Befehl muß mit Bedacht verwendet werden. Es besteht die Gefahr, daß die Daten als Tokencode ausgeführt werden und somit die OM abstürzen lassen.
Mit diesem Schlüsselwort lassen sich neue interne Variablen definieren. Sinn macht das natürlich nur, wenn diese auch vom Controller unterstützt werden. Für "index" wird die auf der C-Control übliche Zählweise benutzt, die bei 1 beginnt. Dagegen ist im Tokencode der niedrigste Index-Wert 0.
Mit dieser Anweisung wird der OCBASIC-Compiler konfiguriert. Beispielsweise kann der Compiler durch "OPTION CCBAS" veranlaßt werden, von sich aus keine neuen Tokenbefehle der OM zu verwenden, d.h. das Programm ist auf der Original-C-Control Version 1.1 lauffähig, sofern der Anwender keine neuen Befehle (z.B. DBNZ, ADD, SUB, REMOVETOS), Zeiger oder neue interne Variablen benutzt. Diese Option sorgt dafür, daß der Compiler fast den gleichen Code erstellt wie der CCBASIC-Compiler. Das Programm sollte somit auch auf der C-Control 1.2, 2.0, der Conrad-Micro und der B-Control funktionieren, soweit diese Controller kompatibel zur 1.1er und zu CCBASIC sind.
Mit Hilfe dieser Word-Variable läßt sich der Programmzähler (Programmcounter, PC) im
tokensierten BASIC-Programm auslesen. Auf der OM enthält PCADR eine Adresse im Speicherraum des Controllers, nicht im virtuellen Speicherraum.
Da die OM kein 16-Bit-Stackhandling unterstützt, läßt sich die Word-Systemvariable PCADR leider nicht als interne Variable, sondern nur über High- und Lowbyte ansprechen.
Diese Funktion dient dazu, den "Top-Of-Stack" (TOS) des Rechenstacks auszulesen. Auf der OM kann der TOS genauso wie auf der Original-C-Control beliebig oft gelesen und bei Bedarf mittels REMOVETOS vom Stack entfernt werden. POP und REMOVETOS können zum Beispiel benutzt werden, um an Subroutinen oder Funktionen mittels Rechenstack übergebene Parameter vom Stack zu holen. Damit lassen sich auch Funktionen realisieren, die mehr als einen Wert zurückliefern. Siehe auch PUSH.
Der Anwender kann eigene Unterprogramme (Subroutinen, Prozeduren) definieren, die
im Programm genauso wie die Standardbefehle (z.B. "PRINT a") benutzbar sind.
Unterprogramme werden hauptsächlich dazu verwendet, um einerseits
komplexe Programme in überschaubare Teileinheiten zu zerlegen,
und um andererseits mehrfach benötigte Programmteile von unterschiedlichen
Orten des Programms zu verwenden.
Eine Sonderform der Unterprogramme sind Funktionen. Sie können einen Wert zurückliefern.
Im Programm werden Unterprogramme üblicherweise mittels GOSUB aufgerufen.
In OCBASIC ist aber auch der Aufruf durch ihren Namen möglich. Hinter dem Namen kann durch Komma getrennt eine Liste von Übergabeparametern angegeben werden. Diese Liste sollte in Klammern eingeschlossen werden.
Das PROCEDURE-Kommando erzeugt ein Sprungziel (Label), das den Namen des Unterprogramms trägt, danach folgen die Anweisungen zum Holen der Übergabeparameter vom Stack, dann folgt der Prozedur-Körper mit den Anweisungen und schließlich wird das Unterprogramm mit einem RETURN verlassen. Das PROCEDURE-Schlüsselwort kann zu PROC verkürzt werden.
Innerhalb eines Unterprogramms besteht vollkommen freier Zugriff auf alle Variablen
des Hauptprogramms. Anders als in anderen BASIC-Dialekten können keine lokalen Variablen definiert werden, die nur innerhalb des Unterprogramms Gültigkeit besitzen.
Der OCBASIC-Compiler überprüft nicht die korrekte Vorgehensweise bei der Übergabe und dem Holen der Parameter mittels Rechenstack. Es fällt in den Aufgabenbereich des Anwenders, darauf zu achten, daß die Anzahl der Parameter übereinstimmt. Andernfalls kommt es zu Stackfehlern, die aber wegen des besonderen Stackhandlings der C-Control normalerweise keine Schwierigkeiten verursachen.
Dieser Befehl dient dazu, einen Wert auf dem Rechenstack abzulegen. Dieser Wert kann anschließend als "Top-Of-Stack" (TOS) des Rechenstacks per POP ausgelesen werden. Auf der OM kann der TOS beliebig oft gelesen und bei Bedarf mittels REMOVETOS vom Stack entfernt werden. PUSH kann zum Beispiel benutzt werden, um Parameter an Subroutinen oder Funktionen mittels Rechenstack zu übergeben.
In CCBASIC auf der C-Control 1.1 war es bisher möglich, Assemblermodule per "SYS &H101" aufzurufen. Wie in der Hilfe zu CCBASIC beschrieben, konnte man diesen Assemblermodulen beim Aufruf auch ein paar Parameter, also Daten, übergeben. Zum Beispiel werden bei "SYS &H101 2,3,4" nacheinander die Zahlen 2, dann 3 und schließlich 4 auf den sogenannten Rechenstack (deutsch: Rechenstapel) gePUSHt (geschoben). Im Assemblermodul kann man die Daten vom Stack holen, wozu im Normalfall die Funktion POP benutzt wird.
Einen Stack kann man sich üblicherweise so vorstellen, wie mehrere übereinandergestapelte Teller. Auf den Teller "2" wird der Teller "3" gelegt, auf den wiederum der Teller "4" gestellt wird. Möchte man nun Teller vom Stapel holen, muß man zuerst Teller "4" und anschließend Teller "3" entfernen, bevor man schließlich an den Teller "2" kommt. Mehrere Teller gleichzeitig vom Stapel zu holen ist üblicherweise nicht möglich. Es ist also so, daß der Teller, der zuletzt auf den Stapel gelegt wurde, zwangsläufig als erster Teller vom Stapel geholt werden muß. Es handelt sich also um einen sogenannten LIFO-Stapel ("Last In - First Out", "zuletzt hinein - zuerst hinaus"). Für andere Aufgabengebiete sind auch andere Stapel-Funktionsweisen denkbar.
Auf der C-Control 1.1 arbeitet der Stack nicht exakt nach diesem LIFO-Prinzip. Es lassen sich zwar beliebig viele Werte auf dem Stapel ablegen, sie werden jedoch beim Lesen nicht vom Stapelspeicher entfernt. (Von einer Ausnahme abgesehen.) Gelesen wird vielmehr immer der zuletzt auf den Stack geschriebene Wert TOS ("Top Of Stack", "Spitze des Stapels"). Bei der C-Control 1.1 gehen ältere Einträge verloren, wenn mehr als 7 Einträge auf dem Stack gespeichert werden. Es gibt übrigens noch einen zweiten Stack auf der C-Control: Nämlich den GOSUB-Stack. Auf ihm werden bei einem GOSUB die Rücksprungadresse gespeichert, die bei einem RETURN vom Stack gelesen wird. Dieser Stack arbeitet im Gegensatz zum Rechenstack exakt nach dem oben angesprochenen LIFO-Prinzip.
Der Rechenstack kann in OCBASIC nicht nur wie bisher in CCBASIC bei SYS, sondern auch beim Aufruf von Subroutinen oder Funktionen zur Parameterübergabe benutzt werden. Bei "hallo 1,2,3" werden "1", dann "2" und schließlich "3" auf den Rechenstack gelegt und anschließend ein "GOSUB hallo" ausgeführt. Was in der Subroutine "hallo" mit den Daten geschieht ist nebensächlich. Selbst wenn sie nicht vom Stack gelesen werden führt das im Normalfall zu keinen Fehlern. (Ausnahme: Bei aktiviertem RealPop muß der Rechenstack innerhalb von BASIC-IRQ-Routinen korrekt aufgeräumt werden.)
Mit der Übergabe über den Rechenstack kann man oft Programm- und auch Variablenspeicher sparen. Werden die Parameter an die Subroutine wie bisher auf der C-Control üblich per Variablen an die Soubroutine übergeben, also z.B. "a=1 : b=2 : b=3 : GOSUB hallo", dann führt das im Normalfall abgesehen von der geringeren Übersichtlichkeit zu längeren Programmen, da bei jedem Aufruf ein "PUSH 1 : POP a : PUSH 2 : POP b : PUSH 3 : POP c : GOSUB hallo" kodiert werden muß. Bei der Übergabe über den Stack wird hingegen "PUSH 1 : PUSH 2 : PUSH 3 : GOSUB hallo" kodiert, d.h. das POP wird in der Subroutine durchgeführt. Falls die Subroutine "hallo" nur einmal benutzt wird ergibt sich aber natürlich kein Größengewinn.
Dieser Befehl dient dazu, den "Top-Of-Stack" (TOS) des Rechenstacks vom Stack zu entfernen. Auf der OM kann der TOS beliebig oft gelesen und bei Bedarf mittels REMOVETOS vom Stack entfernt werden. POP und REMOVETOS können zum Beispiel benutzt werden, um an Subroutinen oder Funktionen mittels Rechenstack übergebene Parameter vom Stack zu holen.
Falls das richtige Stackhandling per RealPop aktiviert wurde, dann kann der Befehl REMOVETOS entfallen. Wird er trotzdem benutzt, hat das keine negativen Auswirkungen, da er ignoriert wird.
Mit REPEAT..UNTIL kann eine fußgesteuerte Schleife realisiert werden. Sie entspricht der DO..LOOP..UNTIL-Schleife aus anderen BASIC-Dialekten. Die Befehle in der Schleife werden wenigstens einmal durchlaufen, unabhängig davon, ob die bei UNTIL angegeben Bedingung erfüllt (wahr) oder nicht erfüllt (unwahr) ist. Die Bedingung bei UNTIL muß unwahr (also gleich Null) sein, damit die Schleife erneut durchlaufen wird. Es handelt sich somit um die Abbruchbedingung.
Dient zur schnellen und speicherplatzschonenden Subtraktion bei User-Variablen. Das Carry-Flag wird nicht gesetzt.
Die WHILE..WEND-Schleife ist kopfgesteuert
und die Bedingung bei WHILE muß erfüllt
(wahr, das heißt ungleich Null) sein, damit die Schleife durchlaufen
wird, sonst wird hinter WEND weitergemacht.
Nach jedem Schleifendurchlauf wird die
Bedingung erneut überprüft und die Schleife
gegebenenfalls verlassen.
Wird als Bedingung "TRUE" oder "ON" angegeben, optimiert der OCBASIC-Compiler die Schleife zu einem einfachen GOTO.
Zeiger sind vor allem aus der Programmiersprache C bekannt. Sie existieren aber auch in neueren BASIC-Dialekten. Im Prinzip sind Zeiger gewöhnliche Variablen, deren Inhalt die Adresse einer anderen Variable ist. Mit Hilfe von Zeigern ist es auf der OM möglich, Feldfunktionen (Arrays), Stapel, Strukturen, Listen und sogar lokale Variablen zu realisieren. Beispielsweise können Daten im RAM-Speicher auf fortlaufenden Adressen abgelegt werden.
Die OM besitzt zusätzlich viele neue interne Variablen, die größtenteils als Flags, also als Bitvariablen, ausgelegt sind und mit denen sich das Betriebssystem konfigurieren läßt. Außerdem kann auf Systembereiche zugegriffen werden. Wenn die neuen internen Variablen verwendet werden sollen, müssen sie zunächst definiert werden, denn sie sind nicht Bestandteil der OCBASIC-Syntax. Am einfachsten geht das mit Hilfe einer zum verwendeten Controller passenden Definitionsdatei, die auf der offiziellen Informationssite zur OM heruntergeladen werden kann. Sie wird mit dem folgenden INCLUDE-Befehl in das Anwenderprogramm eingebunden. Dieser INCLUDE-Befehle sollte am Beginn jedes Programms stehen.
In CCBASIC lassen sich die meisten neuen Variablen nicht so ohne weiteres nutzen, da keine IVAR-Variablen definiert werden können. Insbesondere auf die internen Bit-Variablen müßte über BYTE-Variablen und entsprechende Bitmasken zugegriffen werden.
Diese interne Bitvariable liefert das Carry nach Berechnungen zurück.
Mit diesem Bit läßt sich das Weiterzählen der Uhr deaktivieren.
Eines der beiden Konfigurationsregister vom Mikrocontroller. Sie lassen sich nach jedem
Reset nur einmal beschreiben ("write once").
Das Register CONFIG1 wurde bereits vom Betriebssystem beschrieben und erneutes Beschreiben wird daher vom Controller ignoriert.
Eines der beiden Konfigurationsregister vom Mikrocontroller. Sie lassen sich nach jedem
Reset nur einmal beschreiben ("write once").
Dieses Register kann zur Aktivierung des IRQ- und RST-Pins und zum Wechseln der Taktquelle verwendet werden. Nach Reset sind alle Bits gelöscht, das Register enthält also nur 0-Bits.
Bitnummer | Open-Micro und Open-Mini | Open-Midi | Beschreibung |
0 | RSTEN | RSTEN | RST Pin Function Selection |
1 | reserviert | OSCENINSTOP | Oscillator Enable in Stop Mode |
2 | reserviert | ESCIBDSRC | ESCI Baud Rate Clock Source |
3 | OSCOPT0 | reserviert | Selection Bit for Oscillator Option |
4 | OSCOPT1 | reserviert | Selection Bit for Oscillator Option |
5 | reserviert | reserviert | --- |
6 | IRQEN | IRQEN | IRQ Pin Function Selection Bit |
7 | IRQPUD | IRQPUD | IRQ Pin Pullup Control Bit, 1=deaktivieren |
Nach den Angaben von Freescale kann der Controller Schaden nehmen, falls bestimmte Zeiten beim Beschreiben und Löschen des Flash-Speichers nicht eingehalten werden. Die Zeitverzögerungen im Betriebssystem der OM gehen von einem Systemtakt von 3,2 MHz aus. Bei anderen Taktraten sollten deshalb PRINT# und CLOSE# nicht verwendet werden.
Mit diesem Bit kann festgelegt werden, daß die Programmausführung nach Auftreten eines Fehlers nicht einfach abgebrochen, sondern trotz Fehler mit dem nächsten Tokenbefehl fortgesetzt wird. Die Fehlernummer steht in der internen Variable ERR.
Mit "Continue On Error" ist meist keine effektive Fehlerbehandlung möglich.
Beim Fehler "Zuviele GOSUB-Verschachtelungen" wird die gewünschte Subroutine nicht aufgerufen und bei "RETURN ohne GOSUB" wird der RETURN-Befehl ignoriert und mit dem Befehl hinter RETURN fortgefahren. Falls ein Fehler bei PRINT# auftritt bleibt das Interruptsystem deaktiviert und einige Bytes im User-RAM werden nicht restauriert. Meist ist es daher sinnvoller, mit der Option RunOnErr zu arbeiten.
Der Fehler "Illegales Token" ist fatal. Das heißt, eine Fortsetzung des Programms ist nicht möglich.
Da die Open-Micro kein 16-Bit-Stackhandling unterstützt, läßt sich die Word-Systemvariable ERRADR leider nicht direkt, sondern nur über High- und Lowbyte ansprechen.
Da die Open-Micro kein 16-Bit-Stackhandling unterstützt, läßt sich die Word-Systemvariable ERRADR leider nicht direkt, sondern nur über High- und Lowbyte ansprechen.
Da die Open-Micro kein 16-Bit-Stackhandling unterstützt, läßt sich die Word-Systemvariable FILEPOS leider nicht direkt, sondern nur über High- und Lowbyte ansprechen.
Da die Open-Micro kein 16-Bit-Stackhandling unterstützt, läßt sich die Word-Systemvariable FILEPOS leider nicht direkt, sondern nur über High- und Lowbyte ansprechen.
Da die Open-Micro kein 16-Bit-Stackhandling unterstützt, läßt sich die Word-Systemvariable PCADR leider nicht direkt, sondern nur über High- und Lowbyte ansprechen.
Da die Open-Micro kein 16-Bit-Stackhandling unterstützt, läßt sich die Word-Systemvariable PCADR leider nicht direkt, sondern nur über High- und Lowbyte ansprechen.
Mit dieser Anweisung kann man nach einem Programmende (siehe END) gleich in den Host-Modus wechseln. Auf diese Weise kann man nach Programmende ein neues Programm laden, ohne die OM resetten zu müssen.
Dieses Flag signalisiert dem Betriebssystem, daß die Datei verändert wurde
und CLOSE# deshalb das neue FILEEND in den Flash-Speicher schreiben muß.
Diese interne Variable sollte der Anwender nur auslesen und unverändert lassen.
Dieses Flag signalisiert dem Betriebssystem, daß eine INLINE-Interrupt-Assemblerroutine gefunden wurde und diese anstatt einer Interruptroutine im SYCODE-Bereich verwendet werden soll.
Diese interne Variable sollte der Anwender nur auslesen und unverändert lassen.
Dieses Flag signalisiert dem Betriebssystem, daß eine
BASIC-Interrupt-Routine augenblicklich ausgeführt wird.
Diese interne Variable sollte der Anwender nur auslesen und unverändert lassen.
Dieses Flag signalisiert dem Betriebssystem, daß ein IRQ-Interrupt detektiert wurde und deshalb die BASIC-Interruptroutine starten soll. Das Flag kann zum Beispiel verwendet werden, um die BASIC-Interruptroutine mittels Assemblerroutine im TIMOFL-Interrupt in periodischen Zeitabständen aufzurufen.
Dieses Flag signalisiert dem Betriebssystem, daß eine
BASIC-Interrupt-Routine per "INTERRUPT label" definiert wurde.
Diese interne Variable sollte der Anwender nur auslesen und unverändert lassen.
Mit diesem Bit kann das Betriebssystem veranlaßt werden, alle mit PRINT über die serielle Schnittstelle ausgegebenen Zahlen nicht im Dezimal- sondern im Hexadezimalformat auszugeben. Das ist besonders sinnvoll bei Adressen, die aus High- und Lowbyte aufgebaut sind. Solange die hexadezimale Ausgabe aktiv ist, wird PrintSpc ignoriert.
Mit diesem Bit wird das Betriebssystem veranlaßt, vor die Ausgabe einer dezimalen Zahl mittels PRINT ein Leerzeichen (Space) zu setzen. Gerade während der Entwicklungsphase, in der zum Debugging oft Variableninhalte über PRINT-Befehle ausgegeben werden, führt das zu einer erheblichen Verringerung der Programmgröße. Solange die hexadezimale Ausgabe aktiv ist, wird PrintSpc ignoriert.
Mit diesem Register kann man die internen Pull-Up-Widerstände im Controller
an den Digitalports PORT[1] bis PORT[6] abschalten.
Nach jedem Reset aktiviert das Betriebssystem die Pull-Up-Widerstände an allen Ports.
Die Widerstände haben eine Größe von 26 kOhm (+/- 10 kOhm) und legen die Ports auf High-Potential (5 Volt).
Grundsätzlich wird der Pull-Up-Widerstand an einem Port vom Controller automatisch deaktiviert, solange der entsprechende Port kein auf Eingang geschalteter Digitalport ist.
Wenn die Analog-Digital-Ports AD[1] bis AD[6] verwendet werden, führen die internen Pull-Up-Widerstände an den Digitalports oft zur Verfälschung des Meßwerts, da sie vom Controller vor jeder Messung deaktiviert und nach der Messung wieder aktiviert werden. Deshalb sollten die Widerstände an den entsprechenden Ports mit Hilfe von PULLUPA abgeschaltet werden.
Zusätzlich steuert das oberste Bit in diesem Register, ob an PORT[5] der Takt des internen Oszillators (BUSCLKX4) ausgegeben werden soll.
Mit diesem Befehl kann man die internen Pull-Up-Widerstände im Controller
an den Digitalports PORT[9] bis PORT[16] abschalten.
Nach jedem Reset aktiviert das Betriebssystem die Pull-Up-Widerstände an allen Ports. Die Widerstände haben eine Größe von 26 kOhm (+/- 10 kOhm) und legen die Ports auf High-Potential (5 Volt). Grundsätzlich wird der Pull-Up-Widerstand an einem Port vom Controller automatisch deaktiviert, solange der entsprechende Port kein auf Eingang geschalteter Digitalport ist.
Auf der Open-Midi können die Pull-Up-Widerstände an PORT[9] bis PORT[14] das Meßergebnis der AD-Ports AD[9] bis AD[14] verfälschen. Es gilt das bei PULLUPA Geschriebene.
Mit diesem Bit kann man die OM veranlassen, beim Rechenstack ein echtes
LIFO-Stackhandling zu benutzen. Das heißt, ein POP entfernt den
"Top-Of-Stack" (TOS) und ein REMOVETOS ist unnötig.
RealPop bewirkt ein langsameres Stackhandling und hat, wenn keine
BASIC-Interruptroutine verwendet werden soll, kaum einen Vorteil. Wenn aber
RealPop und eine BASIC-Interruptroutine verwendet werden, teilen sich das
Hauptprogramm und die Interruptroutine den Rechenstack, so daß der
Rechenstack bei Start der Interruptroutine nicht mehr
in den letzten 8 Bytes des User-RAMs gespeichert
werden muß und so diese 8 Byte Variablenspeicher auch
bei Benutzung einer BASIC-Interruptroutine zur Verfügung stehen.
Da dann aber insgesamt nur 7 Stackeinträge zur Verfügung stehen, besteht
die Gefahr von Stackfehlern, die zu heimtückischen Rechenfehlern im Hauptprogramm
führen können.
Normalerweise unterbricht die OM die Programmausführung, sobald ein Fehler auftritt. Durch Setzen dieses Bits kann man die OM jedoch anweisen, das BASIC-Programm bei einem Fehler neu zu starten. Die Daten der verwendeten Variablen bleiben dabei erhalten. Das Betriebssystem wird reinitalisiert. Der Fehlergrund steht in der internen Variable ERR und die Adresse des Bytes hinter dem fehlerhaften Tokenbefehl steht in ERRADR. Siehe dazu die Anmerkungen bei ERR.
Diese Funktion ist dazu vorgesehen, um an den Beginn des Programms eine Fehlerbehandlungsroutine zu stellen und die Programmausführung bei Eintritt eines Fehlers zu beenden. Das Auftreten eines Fehlers ist ein Hinweis, daß irgendetwas im Programm nicht korrekt abläuft. Das Programm sollte deshalb nicht einfach nochmals durchlaufen werden. Zumal der RAM-Speicher und somit auch das User-RAM mit den Variablen nicht reinitalisiert wurden.
Durch Setzen dieses Bits läßt sich eine Inline-Interrupt-Assemblerroutine (IIAR) oder eine Assemblerroutine im SYSCODE-Bereich in den ADC-Interrupt einbinden. Sobald der Interrupt auftritt, wird die Assemblerroutine aufgerufen, wobei im Akkumulator der Wert #iADC und im X-Register die Adresse von FREERAM übergeben wird. Die Routine sollte mit "RTS" und gelöschtem Carry-Bit ("CLC") beendet werden.
Durch Setzen dieses Bits läßt sich eine Inline-Interrupt-Assemblerroutine (IIAR) oder eine Assemblerroutine im SYSCODE-Bereich in den IRQ-Interrupt einbinden. Sobald der Interrupt auftritt, wird die Assemblerroutine aufgerufen, wobei im Akkumulator der Wert #iIRQ und im X-Register die Adresse von FREERAM übergeben wird. Die Routine sollte mit "RTS" und gelöschtem Carry-Bit ("CLC") beendet werden.
Durch Setzen dieses Bits läßt sich eine Inline-Interrupt-Assemblerroutine (IIAR) oder eine Assemblerroutine im SYSCODE-Bereich in den KEYB-Interrupt einbinden. Sobald der Interrupt auftritt, wird die Assemblerroutine aufgerufen, wobei im Akkumulator der Wert #iKYB und im X-Register die Adresse von FREERAM übergeben wird. Die Routine sollte mit "RTS" und gelöschtem Carry-Bit ("CLC") beendet werden.
Durch Setzen dieses Bits läßt sich eine
Inline-Interrupt-Assemblerroutine (IIAR)
oder eine Assemblerroutine im SYSCODE-Bereich in den
SWI-Interrupt einbinden.
Sobald der Interrupt auftritt, wird die Assemblerroutine aufgerufen, wobei im Akkumulator der Wert #iSWI und im X-Register die Adresse von FREERAM übergeben wird. Die Routine sollte mit "RTS" und gelöschtem Carry-Bit ("CLC") beendet werden.
Auf der OM werden Firmwareroutinen per "LDX #funktionsnummer : SWI" aufgerufen, damit
Assemblermodule, die für die OM geschrieben werden, auch bei verändertem
Speicherlayout lauffähig bleiben.
Wenn der Anwender eine Routine in diesen SWI-Interrupt einbindet, dann muß das X-Register vor Beendigung der Routine wieder mit der Funktionsnummer geladen werden, falls die Betriebssystemfunktion nach Ausführung der Userroutine aktiviert bleiben soll ("CLC"). Es ist natürlich auch denkbar, auf diese
Weise eigene Firmwareroutinen in das System einzubinden.
Durch Setzen dieses Bits läßt sich eine
Inline-Interrupt-Assemblerroutine (IIAR)
oder eine Assemblerroutine im SYSCODE-Bereich in den
TIM0-Interrupt einbinden.
Sobald der Interrupt auftritt, wird die Assemblerroutine aufgerufen, wobei im Akkumulator der Wert #iTIM0 und im X-Register die Adresse von FREERAM übergeben wird. Die Routine sollte mit "RTS" und gelöschtem Carry-Bit ("CLC") beendet werden.
Der TIM0-Interrupt wird auf der OM normalerweise nicht verwendet und muß daher vom Anwender zunächst per "BSET bCH0IE,TSC0" aktiviert werden.
Durch Setzen dieses Bits läßt sich eine
Inline-Interrupt-Assemblerroutine (IIAR)
oder eine Assemblerroutine im SYSCODE-Bereich in den
TIM1-Interrupt einbinden.
Sobald der Interrupt auftritt, wird die Assemblerroutine aufgerufen, wobei im Akkumulator der Wert #iTIM1 und im X-Register die Adresse von FREERAM übergeben wird. Die Routine sollte mit "RTS" und gelöschtem Carry-Bit ("CLC") beendet werden.
Der TIM1-Interrupt wird auf der OM normalerweise nicht verwendet und muß daher vom Anwender zunächst per "BSET bCH1IE,TSC1" aktiviert werden.
Durch Setzen dieses Bits läßt sich eine
Inline-Interrupt-Assemblerroutine (IIAR)
oder eine Assemblerroutine im SYSCODE-Bereich in den
TIMOFL-Interrupt einbinden.
Sobald der Interrupt auftritt, wird die Assemblerroutine aufgerufen, wobei im Akkumulator der Wert #iTOFL und im X-Register die Adresse von FREERAM übergeben wird. Die Routine sollte mit "RTS" und gelöschtem Carry-Bit ("CLC") beendet werden.
Der TIMOFL-Interrupt wird auf der OM als TIMER-Interrupt verwendet und periodisch alle 20 ms aufgerufen.
Durch Setzen dieses Bits läßt sich eine Inline-Interrupt-Assemblerroutine (IIAR) oder eine Assemblerroutine im SYSCODE-Bereich starten, sobald der Interpreter auf einen illegalen Tokenbefehl stößt. Sobald dieser Fall eintritt, wird die Assemblerroutine aufgerufen, wobei im Akkumulator der Wert #iTOK und im X-Register die Adresse von FREERAM übergeben wird. Auf FREERAM steht das illegale Token. Die Routine sollte mit "RTS" beendet werden. Falls mit gelöschtem Carry-Bit ("CLC") beendet wird, führt der Interpreter das im Akkumulator stehende Token aus. Wird dagegen mit gesetztem Carry-Bit ("STC") beendet, fährt das System mit dem nächsten Tokenbefehl fort.
Durch Setzen dieses Bits läßt sich eine
Inline-Interrupt-Assemblerroutine (IIAR)
oder eine Assemblerroutine im SYSCODE-Bereich in den
Interpreter einbinden.
Die Routine wird jedesmal gestartet, nachdem ein neuer Tokenbefehl aus dem Speicher geholt wurde. Das ist unmittelbar bevor der Tokenbefehl vom Interpreter ausgeführt wird. Mit Hilfe der Routine läßt sich zum Beispiel ein In-System-Debugger realisieren. Oder man könnte die Programmausführung unterbrechen, sobald eine Variable einen illegalen Wert annimmt.
Der Assemblerroutine wird im Akkumulator der Wert #iTOK und im X-Register die Adresse von FREERAM übergeben. Auf FREERAM steht das aus dem Speicher geholte Token.
Die Routine sollte mit "RTS" beendet werden.
Falls mit gelöschtem Carry-Bit ("CLC") beendet wird, führt der Interpreter das im Akkumulator stehende Token aus. Wird dagegen mit gesetztem Carry-Bit ("STC") beendet, fährt das System mit dem nächsten Tokenbefehl fort.
Mit dieser Variable kann die Versionsnummer vom geladenen Betriebsystem abgefragt werden. Nach Anlegen der Versorgungsspannung wird in BYTE[24] diese Nummer hinterlegt. Eine Variable auf BYTE[24] kann ohne Einschränkungen verwendet werden, wenn die Versionsnummer nicht benötigt wird.
Nach Programmstart ohne Fehler wird vom Betriebssystem der gesamte Variablenspeicher mit Ausnahme von VERSION auf 0 gesetzt. Damit alle Variablen auf 0 stehen, sollte der Anwender bei Programmstart VERSION=0 ausführen.
Hier die thematisch sortierte Liste aller vom OCBASIC-Compiler derzeit unterstützten Schlüsselwörter und durch die Definitionsdateien erstellten neuen internen Variablen. Durch Anklicken des jeweiligen Schlüsselworts kann zur Stelle in dieser Dokumentation gesprungen werden, an der die ausführliche Beschreibung zu finden ist. Einige Schlüsselwörter passen in verschiedene Themenbereiche. Sie wurden dennoch nur einmal aufgeführt.
Anweisungen an den Compiler |
||
Schlüsselwort | Kurzbeschreibung | Beispiel |
INCLUDE |
Datei an die aktuelle Position des Sourcecodes einfügen | INCLUDE "omid.def" |
INLINE |
Daten an die aktuelle Position der Ausgabedatei einfügen | INLINE "Pizza-Uhr von D.H.",0 |
OPTION |
Compiler auf Controllertype konfigurieren | OPTION openmicro |
Definitionen |
||
Schlüsselwort | Kurzbeschreibung | Beispiel |
AD |
Analogen Eingang definieren | DEFINE adpoti AD[6] |
BIT |
Bitvariable definieren | DEFINE bit7vonzahl BIT[8] OF zahl |
BYTE |
Bytevariable definieren | DEFINE variable BYTE |
BYTEPORT |
Byteport definieren | DEFINE port_a BYTEPORT[1] |
DA |
Digital-Analog-Port (PWM) definieren | DEFINE pwm_port DA[1] |
DEFINE |
Bezeichner (Port, Variable oder Konstante) definieren | DEFINE a,b,c BYTE |
DIM |
Bezeichner (Port, Variable oder Konstante) definieren | DIM a,b,c BYTE |
IVAR |
Neue interne Variable definieren | DEFINE PULLUPA IVAR[140] |
PORT |
Digitalport (Bitport) definieren | DEFINE led PORT[4] |
WORD |
Wordvariable definieren | DEFINE variable1 WORD |
WORDPORT |
Wordport definieren | DEFINE alle_ports WORDPORT[1] |
ZEIGER (^, @) |
USER-RAM-Zeiger definieren und verwenden | @pb=234 |
Port-Ein-/Ausgabe |
||
Schlüsselwort | Kurzbeschreibung | Beispiel |
BEEP |
Rechtecksignal (Ton) an einem Digitalport ausgeben | BEEP 250, 50, ton_port |
DEACT |
Ausgabeport auf Eingang schalten | port1=OFF : DEACT port1 |
FREQ |
Frequenz am FREQ-Pin (PORT[3]) abfragen | FREQ=50 : PRINT FREQ |
PULLUPA |
Interne Pull-Up-Widerstände an BYTEPORT[1] | PULLUPA=&b00111111 |
PULLUPB |
Interne Pull-Up-Widerstände an BYTEPORT[2] | PULLUPB=&b11111111 |
PULSE |
Puls an einem Ausgabedigitalport ausgeben | led=OFF : PULSE led |
TOG |
Ausgabedigitalport umschalten | led=ON : TOG led |
Mathematische Berechnungen |
||
Schlüsselwort | Kurzbeschreibung | Beispiel |
&B |
Konstante im binären Zahlensystem | &B10011001 |
&H |
Konstante im hexadezimalen Zahlensystem | &H1FFE |
ABS |
Berechnung des Absolutbetrags | y = ABS(-1) |
ADD |
Schnelle und platzsparende Addition bei User-Variablen | ADD a,1 |
AND |
Logische und bitweise UND-Verknüpfung | IF x>10 AND x<20 THEN y=x |
ASC |
Stringkonstante in ASCII-Zeichen wandeln | PUT ASC("?") |
CARRY |
Carry nach Berechnungen | PRINT Carry |
MAX |
Größeren Wert ermitteln | z=MAX(x,10) |
MIN |
Kleineren Wert ermitteln | z=MIN(x,10) |
MOD |
Rest einer ganzzahligen Division | PRINT 10 MOD 6 |
NAND |
UND-Verknüpfung mit anschließender Negation (NOT) | x=a NAND b |
NOR |
ODER-Verknüpfung mit anschließender Negation (NOT) | x=a NOR b |
NOT |
Logische und bitweise Negation (1er Komplement) | x=NOT(y) |
OFF |
Vordefinierte Konstante für 0 (logisch unwahr, Port low) | Led=OFF |
ON |
Vordefinierte Konstante für -1 (logisch wahr, Port high) | Led=ON |
OR |
Logische und bitweise ODER-Verknüpfung | y=a OR b |
POP |
Top-Of-Stack (TOS) des Rechenstacks auslesen | a=POP |
PUSH |
Wert auf dem Rechenstack ablegen | PUSH 123 |
RAND |
Zufallszahl im Integerformat generieren | x=RAND |
RANDOMIZE |
Zufallszahlengenerator initialisieren | RANDOMIZE TIMER |
REMOVETOS |
Top-Of-Stack (TOS) des Rechenstacks entfernen | REMOVETOS |
SGN |
Mathematische Signum-Funktion | z=SGN(x) |
SHL |
Bitweises arithmetisches Linksschieben | x=x SHL 3 |
SHR |
Bitweises arithmetisches Rechtsschieben | x=x SHR 3 |
SQR |
Ganzzahliger Näherungswert für die Quadratwurzel | z=SQR(x) |
SUB |
Schnelle und platzsparende Subtraktion bei User-Variablen | SUB a,1 |
XOR |
Logische und bitweise Exclusiv-ODER-Verknüpfung | x=a XOR b |
Programmablaufsteuerung |
||
Schlüsselwort | Kurzbeschreibung | Beispiel |
DBNZ |
Schnelle DBNZ-Schleife ausführen | DBNZ loop |
DBNZCTR |
Zähler für DBNZ-Schleifen | DBNZCTR=15 |
ELSE |
Alternativzweig bei IF..THEN | IF x=0 THEN x=1 ELSE x=2 |
END |
Programmausführung beenden | END |
END2HOST |
Nach Programmende in den Host-Modus wechseln | End2Host=ON : END |
FOR TO NEXT |
Programmschleife | FOR i=32 TO 255 : PUT i : NEXT i |
FUNCTION |
Funktion definieren | FUNCTION funktion(a,b) |
GOSUB |
Subroutine aufrufen | GOSUB unterprogramm1 |
GOTO |
Unbedingter Sprung zu einem Label (Sprungziel) | GOTO endlos |
IF THEN |
Bedingte Befehlsausführung | IF x>10 THEN END |
ON GOSUB |
Unterroutinenaufruf in Abhängigkeit eines Wertes | ON x GOSUB label1, label2 |
ON GOTO |
Programmsprung in Abhängigkeit eines Wertes | ON x GOTO label1, label2 |
PAUSE |
Programmunterbrechung für Vielfache von 20 Millisekunden | PAUSE 50 |
PROCEDURE |
Unterprogramm (Subroutine, Prozedur) definieren | PROCEDURE subroutine(a,b) |
REPEAT UNTIL |
Fußgesteuerte Schleife mit Abbruchbedingung | a=0 : REPEAT : a=a+1 : UNTIL a>9 |
RETURN |
Aus einer Subroutine oder einer Funktion zurückkehren | RETURN |
Sprungziele (#) |
Ziele der Sprungoperationen GOSUB und GOTO | #label |
STEP |
Schrittweite bei einer FOR-Programmschleife | FOR i=1 TO 9 STEP 2 : NEXT i |
WAIT |
Auf Eintreten einer Bedingung warten | WAIT taste=ON |
WHILE WEND |
Kopfgesteuerte Schleife mit Einstiegsbedingung | WHILE taste : WEND |
BASIC-Interruptroutine |
||
Schlüsselwort | Kurzbeschreibung | Beispiel |
INTERRUPT |
BASIC-Interruptroutine vereinbaren | INTERRUPT basicirq |
RETURN INTERRUPT |
Aus der BASIC-Interruptroutine zurückkehren | RETURN INTERRUPT |
Serielle Schnittstelle |
||
Schlüsselwort | Kurzbeschreibung | Beispiel |
BAUD |
Baudrate der seriellen Schnittstelle einstellen | BAUD R9600 |
CTS |
Test, ob die CTS-Leitung aktiv ist | IF CTS THEN GET x |
GET |
Byte (Zeichen) von der seriellen Schnittstelle lesen | GET x |
HANDSHAKE |
Hardware-Handshake auf der RTS- und CTS-Leitung (obsolet) | HANDSHAKE ON |
INPUT |
Zahlenwert im Textformat von der seriellen Schnittstelle einlesen | INPUT x |
PRINT |
Werte und Texte über die serielle Schnittstelle ausgeben | PRINT "Wert ist ";wert |
PRINTHEX |
Bei PRINT alle Zahlen im Hexadezimalformat ausgeben | PrintHex=ON |
PRINTSPC |
Bei PRINT dezimale Zahlen mit führendem Leerzeichen ausgeben | PrintSpc=ON |
PUT |
Byte über die serielle Schnittstelle ausgeben | PUT x |
RXD |
Test, ob ein Byte seriell empfangen wurde | IF RXD THEN GET x |
Datenspeicherung |
||
Schlüsselwort | Kurzbeschreibung | Beispiel |
APPEND |
Datei zum Anfügen von Daten öffnen | OPEN# FOR APPEND |
CLOSE# |
Datei schließen | CLOSE# |
EOF |
Test auf Dateiende beim Auslesen der Datei | IF NOT EOF THEN INPUT# a |
FILEFREE |
Test auf freien Speicherplatz vor dem Schreiben in die Datei | IF FILEFREE THEN PRINT# a |
FILEPOS |
Dateipositions-Zeiger | PRINT FILEPOS |
INPUT# |
Zahlenwert aus der Datei einlesen | INPUT# x |
LOOKTAB |
Wert aus einer Datentabelle lesen | PRINT LOOKTAB(tabelle1, mess) |
OPEN# |
Datei im Modus Schreiben, Anfügen oder Lesen öffnen | OPEN# FOR READ |
PRINT# |
Wert in die Datei schreiben | IF FILEFREE THEN PRINT# 123 |
READ |
Datei zum Auslesen von Daten öffnen | OPEN# FOR READ |
TABLE |
Datentabelle definieren | TABLE tab2 BYTE "datei.tab" |
TABEND |
Abschluß einer bei TABLE eingegebenen Werteliste | TABLE tab1 : 17 25 4 5 : TABEND |
WRITE |
Datei zum Schreiben neuer Daten öffnen | OPEN# FOR WRITE |
Echtzeituhr |
||
Schlüsselwort | Kurzbeschreibung | Beispiel |
CLOCKDIS |
Weiterzählen der Uhr deaktivieren | ClockDis=ON |
DAY |
Tagwert der Echtzeituhr | PRINT DAY |
DOW |
Wochentagwert der Echtzeituhr | IF DOW = 1 THEN PRINT "Montag" |
HOUR |
Stundenwert der Echtzeituhr | PRINT HOUR |
MINUTE |
Minutenwert der Echtzeituhr | PRINT MINUTE |
MONTH |
Monatswert der Echtzeituhr | PRINT MONTH |
SECOND |
Sekundenwert der Echtzeituhr | PRINT SECOND |
TIMER |
20-ms-Timer | x=TIMER |
YEAR |
Jahrwert der Echtzeituhr | PRINT YEAR |
Fehlerverfolgung |
||
Schlüsselwort | Kurzbeschreibung | Beispiel |
CONONERR |
Programmausführung trotz Fehler fortsetzen | ConOnErr=ON |
ERR |
Fehlergrund ermitteln | IF ERR THEN PRINT ERR : END |
ERRADR |
Fehlerstelle im Tokencode | PRINT ERRADR |
RUNONERR |
Programm bei einem Fehler neustarten | RunOnErr=ON |
Assemblerprogrammierung |
||
Schlüsselwort | Kurzbeschreibung | Beispiel |
ASSEMBLER (!) |
Assemblerbefehl in das Programm einfügen | ! lda variable |
FREERAM |
Für Assemblerroutinen reservierter RAM-Speicher | FREERAM=123 |
SYS |
Aufruf einer Maschinensprache-Routine | SYS &HFD00 |
SYSCODE |
S19-Datei mit Assembler-Routinen in das Programm einbinden | SYSCODE "test.s19" |
SYSEND |
Abschluß einer mit SYSCODE direkt eingegebenen Byteliste | SYSCODE &H81 SYSEND |
UIRADC |
Assemblerroutine in den ADC-Interrupt einbinden | UIRAdc=ON |
UIRIRQ |
Assemblerroutine in den IRQ-Interrupt einbinden | UIRIrq=ON |
UIRKYB |
Assemblerroutine in den KEYB-Interrupt einbinden | UIRKyb=ON |
UIRSWI |
Assemblerroutine in den SWI-Interrupt einbinden | UIRSwi=ON |
UIRTIM0 |
Assemblerroutine in den TIM0-Interrupt einbinden | UIRTim0=ON |
UIRTIM1 |
Assemblerroutine in den TIM1-Interrupt einbinden | UIRTim1=ON |
UIRTOFL |
Assemblerroutine in den TIMOFL-Interrupt einbinden | UIRTofl=ON |
URILLTOK |
Assemblerroutine bei einem illegalen Tokenbefehl starten | URIllTok=ON |
URTOK |
Assemblerroutine in den Interpreter einbinden | URTok=ON |
Konfiguration |
||
Schlüsselwort | Kurzbeschreibung | Beispiel |
CONFIG1 |
Erstes Konfigurationsregister vom Mikrocontroller | PRINT CONFIG1 |
CONFIG2 |
Zweites Konfigurationsregister vom Mikrocontroller | CONFIG2=&b01000000 |
FREQ2 |
Systemtakt justieren (kalibrieren) | FREQ2=128 |
REALPOP |
LIFO-Stackhandling aktivieren und Stacksichern deaktivieren | RealPop=ON |
SLOWMODE |
Controller in einen stromsparenden Modus versetzen | SLOWMODE ON |
Betriebssystemstatus |
||
Schlüsselwort | Kurzbeschreibung | Beispiel |
CSTKPTR |
Zeiger (Pointer) des Rechenstacks | PRINT CSTKPTR |
FILECHG |
Signalflag für Dateiveränderungen | PRINT FileChg |
GSTKPTR |
Zeiger (Pointer) des GOSUB-Stacks | PRINT GSTKPTR |
IIARFND |
Signalflag für existierende INLINE-Interrupt-Assemblerroutine | PRINT IiarFnd |
IRQACT |
Signalflag für Ausführung der BASIC-Interruptroutine | PRINT IrqAct |
IRQREQ |
Signalflag für Anforderung der BASIC-Interruptroutine | PRINT IrqReq |
IRQSET |
Signalflag für Vereinbarung der BASIC-Interruptroutine | PRINT IrqSet |
PCADR |
Programmzähler im Tokencode | PRINT PCADR |
VERSION |
Versionsnummer vom geladenen Betriebsystem abfragen | PRINT VERSION |
Um die Funktionalität der OM zu erhöhen, existieren einige Softwaremodule, die meist in INLINE-Assembler geschrieben wurden und so in jedes beliebige BASIC-Programm eingebunden werden können. Wie die Module im einzelnen zu bedienen sind, wird in den ZIP-Archiven erklärt. Die folgenden Erläuterungen sollen nur eine kurze Übersicht über die Softwaremodule vermitteln.
Das Softwaremodul stellt an den beiden Digitalports PORT[5] und PORT[6] einen I²C-Bus mit 100 kHz Bustakt zur Verfügung. Außerdem wird in den beiliegenden Beispielprogrammen gezeigt, wie ein I²C-EEPROM, das Videotext-IC SAA5246AP/E und mittels PCF8574-Portexpander ein 4x20-LCD über den I²C-Bus angesprochen werden kann. Damit kann nun erstmals auch an der Open-Micro ein LCD mit hoher Geschwindigkeit betrieben werden und es muß nicht mehr der gesamte zweite Byteport der Open-Mini oder -Midi belegt werden.
I²C ist ein von Philips in den frühen 80er Jahren entwickelter serieller Bus, der
benutzt wird, um Integrierte Schaltkreise (ICs) mit geringer Übertragungsgeschwindigkeit zu verbinden.
Daten lassen sich beliebig langsam über den Bus senden. Die Standardgeschwindigkeit beträgt 100 kBit/s, was einer Datenrate von ungefähr 10 Kilobyte pro Sekunde entspricht.
Der Bus besteht aus den drei Leitungen SDA, SCL und GND. SDA dient
zur bidirektionalen Übermittlung von Adressen und Daten zwischen bis zu 256
verschiedenen Geräten am Bus. SCL ist die CLOCK-Leitung. Sie signalisiert,
zu welchem Zeitpunkt SDA einen gültigen Zustand hat.
Die Geräte am I²C-Bus werden in die Kategorien Master und Slave unterteilt.
Der Master sorgt für das Takten der SCL-Leitung sowohl beim Senden als auch
beim Empfangen von Daten zum, bzw. vom Slave.
Der Ruhezustand beim I²C-Bus ist auf beiden Leitungen HIGH-Potential (5 Volt). Das wird dadurch erreicht, indem die beiden Leitungen
über Pullup-Widerstände mit 5 Volt verbunden sind. Wenn nach dem
Ruhezustand SDA auf LOW (0 Volt) geht, während SCL weiter HIGH ist, ist das
das Startsignal für die Datenübertragung. Danach wird in einem Byte (8 Bit)
die Kennung des anzusprechenden Geräts am Bus
beginnend mit dem MSB übertragen, indem SDA entsprechend dem Bit gesetzt,
und anschließend mittels SCL ein kurzer LOW/HIGH/LOW-Impuls gesendet wird. Nach
der Übertragung der 8 Bits deaktiviert der Sender die SDA-Leitung, damit
der Empfänger auf dieser Leitung ein Acknowledge-Signal senden kann, indem
SDA auf LOW gezogen wird. Fehlt das ACK-Signal ist der Empfänger entweder
beschäftigt, oder - wenn der Empfänger ein Master ist - dem Sender wird
signalisiert, daß das Senden von Daten beendet werden soll. Wenn mehr als
ein Byte übertragen wird, folgen die nächsten 8 Bit direkt an das ACK-Signal
(ohne neues Startsignal). Beendet wird die Übertragung, wenn im
Anschluß an die ACK-Quittierung SDA auf HIGH geht, während SCL bereits
HIGH ist. Somit ist dann wieder der Ausgangszustand erreicht.
Um verschiedene Geräte über den I²C-Bus ansprechen zu können, erhält
jedes Gerät eine Kennung, bzw. Bus-Adresse. Ein Sender muß bei einer Übertragung mit dem
START-Impuls und der Gerätekennung beginnen. Die ersten 4 Bit der Kennung
geben den Schaltungstyp an. Dieser wird vom
Hersteller vorgegeben.
Bei einem I²C-EEPROM wie dem 24C256 ist die Kennung des Schaltungstyps
dualkodiert 1010. Die folgenden 3 Bits (A2, A1 und A0) können mittels
Verdrahtung an den Pins des EEPROMs gesetzt werden. Das
achte Bit (R/W) gibt an, ob der Sender im folgenden Byte lesen (R/W ist
HIGH) oder schreiben (R/W ist LOW) will. So ergibt sich eine Lesekennung
für das serielle EEPROM von $a1 (dezimal 161) und eine Schreibkennung
von $a0 (dezimal 160). Auf diese Art und Weise können bis zu acht EEPROMs
am Bus betrieben werden, vorausgesetzt A2, A1 und A0 sind bei jedem
EEPROM anders kodiert.
Das Bild zeigt den Anschluß des EEPROMs 24C256 an die OM. Das IC
bietet 32 Kilobyte EEPROM zur permanenten Datenspeicherung.
Die im Bild eingezeichneten Pull-Up-Widerstände R1 und R2 können bei
der OM entfallen, da das Softwaremodul SCL im Push-Pull-Modus anspricht
und an SDA der interne Pull-Up-Widerstand im Controller aktiv ist.
Für größere Leitungslängen empfiehlt es sich aber, zusätzliche externe Widerstände
bis hinunter zu 1 kOhm vorzusehen.
Mit dem Servo-Assemblermodul ist es möglich, einen Modellbau-Servo (positiver Impuls) mit der Open-Micro, Open-Mini und Open-Midi anzusteuern. Man kann den Servo-Impuls an jedem Port außer PORT[3] der OM erzeugen. Außerdem wurde auf einen großen Stellwinkel (erweiterter Stellbereich) Wert gelegt, um das Potential des Servos voll auszunutzen.
Wenn man den Servo an der gleichen Stromquelle wie die OM betreibt, dann ist darauf zu achten, daß die Stromquelle einen ausreichenden Strom zur Verfügung stellen kann. Denn sonst liefert die OM keine sauberen Impulse und der Servo fährt die vorgesehene Position nicht an.
Mit dem PWM-Assemblermodul kann mit der OM ein pulsweitenmoduliertes Signal erzeugt werden. Im folgenden wird erklärt, was ein PWM-Signal ist und wozu es nützlich ist.
Bild a zeigt ein Rechtecksignal, bei dem das Tastverhältnis 1-zu-1 beträgt. Die Zeit, die das Signal auf 0 Volt (GND) liegt, ist also genauso groß wie die Zeit, während der das Signal auf 5 Volt (VDD) liegt. Beide Zeiten zusammenaddiert ergeben die Periodendauer des Signals. Und der Kehrwert der Periodendauer ist die Frequenz des Signals, gemessen in Schwingungen pro Sekunde oder Hertz (Hz).
In Bild b beträgt das Tastverhältnis nicht mehr 1:1, sondern ungefähr 1:3. Über größere Zeitabschnitte betrachtet ist das Signal also 25% der Zeit auf 5 Volt und 75% der Zeit auf 0 Volt. Und in Bild c ist das Tastverhältnis in etwa umgekehrt.
Signale, wie die im Bild gezeigten, lassen sich von einem Mikrocontroller meist sehr einfach erzeugen. Die Frequenz bleibt konstant und es wird nur das Tastverhältnis verändert. Man spricht von Pulsweitenmodulation (PWM). Wird ein pulsweitenmoduliertes Signal über einen größeren Zeitraum gemittelt, zum Beispiel mit Hilfe eines Tiefpaßfilters mit ausreichend geringer Grenzfrequenz, so ergibt sich eine Spannung am Ausgang des Filters, die proportional zum Tastverhältnis ist. In Bild a ist das Signal 50% der Zeit auf 5 Volt. Gemittelt ergibt sich also der Spannungswert 2,5 Volt. In Bild b ergeben sich 25% von 5 Volt, also 1,25 Volt. Und das Signal in Bild c würde zu einer Gleichspannung von 3,75 Volt.
Auf diese Weise erhält man einen sehr einfach aufzubauenden Digital-Analog-Umsetzer. Der Mikrocontroller kann hiermit jede beliebige Gleichspannung erzeugen. Falls die Frequenz des PWM-Signals ausreichend groß und die Grenzfrequenz des Tiefpaßfilters nicht zu niedrig gewählt wurde, lassen sich auch Wechselspannungen, z.B. Tonsignale, mit Hilfe des PWM-Signals erzeugen. Im Prinzip handelt es sich um einen 1-Bit-DA-Wandler.
Um ein PWM-Signal zu filtern, würde ein einfaches passives Tiefpaßfilter, bestehend aus einem Widerstand und einem Kondensator, ausreichen. Allerdings sind die damit erzielten Ergebnisse nicht optimal. Sinnvoller ist der Einsatz eines Tiefpaßfilters höherer Ordnung.
Der linke Operationsverstärker LM358, die Widerstände R2, R3 und R4 und die Kondensatoren C2, C3 und C4 bilden ein Tiefpaßfilter 3. Ordnung. Der rechte Operationsverstärker ist als nichtinvertierender Spannungsverstärker geschaltet, der mit R1 und R5 auf eine Verstärkung von 2 eingestellt wurde. Der Verstärker wirkt zugleich als Impedanzwandler. Zum Einsatz kam der LM358, da dieser Operationsverstärker mit einfacher Versorgungsspannung auskommt.
Die Bauteilwerte des Filters wurden experimentell ermittelt.
Die Schaltung bietet eine gute Werteauflösung, aber eine schlechte Zeitauflösung.
Das heißt, es dauert einige Zeit (rund 0,3 Sekunden), bis die Ausgangsspannung den gewünschten Spannungswert erreicht. Der Wert wird aber sehr präzise erreicht. Die Taktfrequenz des PWM-Signals wird sehr stark unterdrückt.
Für höhere Ansprüche kann man ein Filter nach einer bestimmten Filtercharakteristik entwerfen. In der Meßtechnik wird sehr häufig die Besselcharakteristik gewählt, da diese im Gegensatz zu Tschebyscheff und Butterworth nicht zum Überschwingen im Zeit- und/oder Frequenzbereich neigt, aber einen steileren Übergang vom Durchlaß- in den Sperrbereich bietet als ein passives Filter. Siehe "Halbleiter-Schaltungstechnik" von Tietze und Schenk.
Mit dem Zeitgesetz vom 25. Juli 1978 hat die Physikalisch-Technische
Bundesanstalt (PTB) den Auftrag erhalten, für die Bundesrepublik
Deutschland die Gesetzliche Zeit darzustellen und zu verbreiten.
Zur Wahrnehmung der ersten Aufgabe benutzt das Labor "Zeiteinheit" der PTB
Braunschweig vier Atomuhren, CS1 bis CS4, die zu den genausten Frequenznormalen
gehören. Die Bezeichnung "CS" ist vom Cäsium abgeleitet,
dessen Eigenschaften in den verwendeten Atomuhren genutzt werden.
Zur Verbreitung der codierten Zeitinformation dient der Sender DCF77 in
Mainflingen, etwa 30 km südöstlich von Frankfurt am Main. Er benutzt die
Langwellenfrequenz 77,5 kHz, wodurch eine maximale Reichweite von 1000 bis
1500 km bei nur 30 kW Sendeleistung erreicht wird. Auf diese Weise kann das
Zeitsignal zumindest nachts in ganz Mitteleuropa empfangen werden.
Mittels DCF-Modul (Conrad-Best.Nr. 641138) und der DCF-Routinen von René Stadler läßt sich das Zeitsignal auch auf der Open-Micro und Open-Mini nutzen und auswerten.
Es muß der invertierte Ausgang des Empfängermoduls verwendet werden.
Selbstverständlich sollte man das Modul an einer stabilisierten Spannungsquelle (ohne Brumm) betreiben. Außerdem sollen einige verkaufte Module kein sauberes Signal ausgeben. Es soll helfen, einen 100-nF-Kondensator an den Ausgang gegen Masse zu schalten.
Tip zum Aufstellen des DCF77-Empfängermoduls: Die Stelle, die für den Empfänger vorgesehen ist, mit einem tragbaren Radio "abhören". Hört man an der Stelle Langwellensender (Langwelle von 150-285 kHz) ist schon fast sicher, daß man dort auch DCF77 empfangen kann. Durch Drehen um die Längsachse läßt sich hier die beste Stellung des Moduls finden. Vermeiden sollte man die Nähe von Lautsprechern, Monitoren, Fernsehern, Computern und schaltenden Relais. In der Nähe von Fenstern hat man dagegen meistens einen guten Empfang. Falls es tagsüber trotzdem nicht klappt, sollte man es einmal nachts versuchen. Und falls es immer noch nicht kappt, kann es daran liegen, daß der DCF77-Sender augenblicklich gewartet wird. Das geschieht etwa 20 mal pro Monat und in der Zeit wird der Sender für 2 bis 10 Minuten abgeschaltet.
Ein Nachteil der Open-Micro, Open-Mini und Open-Midi war bisher deren relativ geringe Portanzahl. Aus diesem Grund lassen sie sich jetzt mit diesem Softwaremodul um bis zu 64 Extended Ports erweitern. Die Extended Ports werden mittels PCF8574-I2C-Portexpandern realisiert und lassen sich in BASIC nach der Aktivierung des Softwaremoduls genauso wie die normalen Ports ansprechen.
Der Bustakt wird von 3.2 MHz auf 8 MHz erhöht. Die OM wird also genauso schnell getaktet wie die C-Control I UNIT M 2.0. Da die OM oft nur mit Bytevariablen arbeiten muß, schneidet sie in Benchmark-Tests deutlich schneller ab als die neue C-Control. Für die Open-Micro und Open-Mini ist eine Anpassung der seriellen Schnittstelle an den erhöhten Bustakt enthalten, die direkt in den Interpreter eingebunden wird. Auf der Open-Midi kann im laufenden Betrieb zwischen 3.2 und 8 MHz hin- und hergewechselt werden.
Es wird ein Quarzoszillator im DIP-14-Gehäuse verwendet, den Reichelt unter der Artikelnummer "OSZI 32,000000" für 0,86 EUR anbietet. Auch ein SMD-Quarzoszillator läßt sich verwenden. Dieser hat eine Größe von nur 3,2 x 2,5 mm, ist aber für eine Lastkapazität von nur 15 pF ausgelegt.
Für 32-Bit-Ganzzahl-Arithmetik existiert ein in Assembler erstelltes Softwaremodul. Es bietet hohe Verarbeitungsgeschwindigkeit, vorzeichenbehaftete Zahlen und ein schnelles PRINT32. Es ist zu allen OM-Controllern kompatibel.
Weitere Softwaremodule und Beispielprogramme befinden sich auf der Download-Seite der offiziellen OM-Infoside. Sie wird laufend erweitert.
In den nachfolgenden Beispielen wird beschrieben, wie man die Eingangs- und Ausgangsports der Open-Micro, Open-Mini und Open-Midi beschalten kann. Da es sehr viele Möglichkeiten zur Beschaltung gibt, soll nur ein kleiner Einblick in die Grundlagen vermittelt werden.
Bild 1
In diesem Beispiel führt der Port ein "High"-Signal (5 Volt, ON), wenn der Schalter offen ist. Wird der Schalter geschlossen, wird am Port ein "Low"-Signal (0 Volt, OFF) erkannt. Da die OM interne Pull-Up-Widerstände besitzt, läßt sich der Widerstand R1 meist einsparen.
Bild 2
Ist genau das gleiche wie in Bild 1, nur invertiert. Bei einem offenen Schalter wird ein "Low"-Signal (OFF) erkannt. Damit die internen Pull-Up-Widerstände der OM das Meßergebnis nicht verfälschen, sollten sie deaktiviert werden (siehe PULLUPA und PULLUPB).
Bild 3
Hier wird ein Optokoppler zur galvanischen Trennung verwendet. Damit können Spannungen, die größer als 5 Volt sind, oder auf ungünstigem Potential liegen mit der OM detektiert werden.
Bild 4
Diese Schaltung basiert auf dem Prinzip des Spannungsteilers. Durch die Reihenschaltung der Widerstände wird die Spannung herabgesetzt. In diesem Beispiel können so Gleichspannungen bis 16 Volt gemessen werden. Das Spannungsverhältnis wird durch die beiden Widerstände festgelegt und beträgt rund 3/(6,8+3)=0,3. Bei 16 Volt liegen also etwa 16*0.3=4,9 Volt am Ausgang.
Bild 5
Hier wird das Signal eines KTY10 (Temperatursensor) ausgewertet. Wie in Bild 4 basiert diese Schaltung auf einem Spannungsteiler. Ändert sich die Temperatur, so ändert sich auch der Widerstand vom KTY10. Die gemessene Spannung läßt sich mit Hilfe einer Tabelle (siehe LOOKTAB) linearisieren.
Bild 1
Hier wird eine LED über einen Vorwiderstand direkt an einem Port betrieben. Die Ports der OM können maximal 10 mA schalten. Ist der Port "high", dann ist die LED an. Ist der Port "low", dann ist die LED aus. Sogenannte Low-Current-LEDs kommen mit weniger Strom aus und der Widerstand R1 läßt sich dann vergrößern.
Bild 2
Um die Schaltleistung zu verstärken kann man einen Transistor verwenden. Je nach Type lassen sich so mehrere Ampere schalten. Bei diesem Beispiel mit PNP-Transistor ist die Lampe aus, wenn der Port "high" ist.
Bild 3
Mit dieser Schaltung wird gezeigt, wie ein Relais angeschlossen werden kann. Durch die Verwendung eines Relais erhält man gleichzeitig eine galvanische Trennung. So können Geräte geschaltet werden die eine größere Spannung haben. Die Freilaufdiode D1 ist unbedingt erforderlich, da der Transistor oder der Controller sonst zerstört werden könnte.
Bild 4
Der ULN2803 besteht aus mehreren Transistoren. Mit diesem Schaltkreis können 8 Ports verstärkt werden. Wenn mehrere Ausgänge verwendet werden, dann ist das IC eine gute Alternative zu Transistoren. Die maximale Strombelastbarkeit liegt bei 1 Ampere.
Bild 5
Bei einigen Anwendungen ist eine galvanische Trennung zwingend notwendig, ohne daß man eine große Schaltleistung benötigt. Dafür kann man auch einen Optokoppler verwenden.
Bild 6
Ein Solid-State-Relais ist ein elektronisches Relais. Es verfügt auch über eine galvanische Trennung. Allerdings besitzen Solid-State-Relais keine Mechanik und können daher schneller schalten und sie haben keine Verschleißteile, was eine hohe Lebensdauer garantiert.
Der Flash-Speicher vom QT4/QY4-Controller reicht von Adresse $ee00 bis einschließlich $fdff. Er hat also eine Größe von 4096 Byte. Der Speicher wird in Flash-Pages mit jeweils 64 Byte Größe verwaltet. Das Betriebssystem der Open-Micro reicht von Adresse $ee00 bis einschließlich $f47f und belegt somit nur 1664 Byte. Der Zeiger mit der Startadresse der Datendatei folgt dahinter auf $f480 und der
Tokencode beginnt bei $f482. Falls ein Assemblermodul mit SYSCODE in den BASIC-Sourcecode eingebunden wird, belegt es die letzten 256 Byte des Flash-Speichers ab Adresse $fd00.
Beim QB8-Controller reicht der Flash-Speicher von Adresse $de00 bis einschließlich $fdff. Er hat also mit 8192 Byte die doppelte Größe wie auf dem QT4/QY4. Der Speicher wird ebenfalls in Flash-Pages mit 64 Byte Größe verwaltet. Das Betriebssystem der Open-Midi reicht von Adresse $de00 bis einschließlich $e4bf und belegt mit 1728 Byte nur eine Flashpage mehr als auf der Open-Micro und Open-Mini. Der Zeiger mit der Startadresse der Datendatei steht direkt hinter dem Betriebssystem auf $e4c0 und der
Tokencode beginnt bei $e4c2. Falls ein Assemblermodul mit SYSCODE in den BASIC-Sourcecode eingebunden wird, belegt es wie bisher die letzten 256 Byte des Flash-Speichers ab Adresse $fd00.
Es lohnt sich besonders für fortgeschrittene Anwender, auch einmal einen Blick in die Definitionsdatei der verschiedenen OM-Controller zu werfen. Die jeweils aktuelle Datei kann auf der offiziellen Informationssite heruntergeladen werden. Die Definitionsdatei für die Open-Micro und Open-Mini trägt den Namen OM.DEF und die zur Open-Midi passende heißt OMID.DEF. Die Datei enthält die unbedingt notwendigen Definitionen für die neuen internen Variablen zum Konfigurieren des Betriebssystems, zur Statusabfrage und zum Einbinden der User-Interruptroutine in verschiedene Interrupts. Außerdem sind am Ende der Datei die Definitionen für den integrierten Assembler aufgeführt, um auf alle Register der Controller-Module zugreifen zu können. Damit lassen sich alle vom Controller bereitgestellten Funktionen nutzen. Die Informationen in der Datei beschränken sich jedoch nicht nur auf reine Definitionen, sondern es wurden auch viele nützliche Bemerkungen und Hinweise eingefügt.
Manchmal ist es wünschenswert, Daten dauerhaft zu speichern.
Dafür können normalerweise die Dateifunktionen verwendet werden.
Allerdings werden die Daten in der Datei gelöscht, sobald ein neues Programm in den Controller geladen wird. Außerdem werden sie immer im Flash-Speicher auf der ersten Seite (Page)
hinter dem BASIC-Programm abgelegt. Die Adresse ist also abhängig von der Größe des BASIC-Programms.
Durch Umbiegen des Dateizeigers FILEPOS ist es allerdings möglich, jede beliebige Adresse des Flash-Speichers auszulesen und zu beschreiben. Der Flash-Speicher läßt sich aber nur pageweise löschen. Dieser Löschvorgang wird von den Dateifunktionen nur dann automatisch durchgeführt, wenn auf den Beginn einer Page geschrieben wird.
Das jeweils erste Byte einer jeden Flash-Page steht an Adresse $xx00,
$xx40, $xx80 und $xxc0. Wenn ein SYSCODE-Assemblermodul vorhanden ist und
nicht überschrieben werden darf, dann könnte man Daten auf der Flash-Page
vor dem Assemblermodul, auf Adresse $fcc0 abspeichern. Wenn kein
SYSCODE-Assemblermodul vorhanden ist oder es eine Größe von 192 Byte oder
weniger hat, dann könnte man die Flash-Page $fdc0 benutzen.
Auf diese Weise werden bis zu 64 Byte Daten jeweils am Ende des nutzbaren Flash-Speichers abgelegt.
Entgegen den Angaben im Manual zum 68HC908QY4/QT4 dürfen die I/O-Pins von
PORTA beim Einstieg in den Forced Monitor Mode offenbar nicht konnektiert
(n.c., not connected) sein. Beim ersten PE-Board müssen also vor
dem ersten OS-Download die LEDs durch Jumper deaktiviert werden.
In einigen (älteren?) QT4-Controllern (u.a. der Micro V2.00 und V2.01) steht
auf Adresse $2da6 der Wert $1e statt $1f. Als Konsequenz arbeitet der MON08 mit
einer um 2,5% höheren Baudrate (9846 statt 9610 Baud). In Grenzfällen könnte
das zu Übertragungsfehlern führen.
Die meisten Controller werden von Freescale offenbar für 3 Volt Betriebsspannung
kalibriert, so daß sie bei 5 Volt rund 2,5% zu langsam arbeiten. Dadurch kann
es bei CCPLUS und bei der CCBASIC-Windows-IDE während des Programmdownloads
zu Übertragungsfehlern kommen. Um die Taktrate dauerhaft zu kalibrieren kann das
Utility OSCTRIM verwendet werden. Die bei CCTools verkauften Controller wurden
bereits getrimmt.
Der Pin PTA2/IRQ ist sehr empfindlich gegenüber elektromagnetischen Störungen
(EMV). In gestörter Umgebung genügt bereits ein Berühren des Ports, um einen
Reset auszulösen. In der späteren Applikation sollte deshalb wie im Bild gezeigt eine Z-Diode 5V1 vom
Pin nach Masse geschaltet werden. Alternativ läßt sich Störspannung auch mittels herkömmlicher Diode 1N4148 nach VDD (5 Volt) ableiten. Letztere Variante ist aber weniger effektiv. Natürlich könnte man den Pin auch direkt auf VDD legen. In diesem Fall läßt sich der Port allerdings nicht mehr in der Applikation verwenden.
Um ein BASIC-Programm komfortabel zu erstellen und auf die OM zu übertragen, benötigt man eine sogenannte integrierte Entwicklungsumgebung (IDE). Eine Entwicklungsumgebung besteht aus mehreren Tools, die aufeinander abgestimmt werden müssen. Bei vielen Anwendern ist die Entwicklungsumgebung ConTEXT sehr beliebt. ConTEXT enthält unter anderem einen komfortablen Programm-Editor zum Erstellen der BASIC-Programme. Das Tool kann auf der Internetseite http://www.contexteditor.org kostenlos heruntergeladen werden. Da ConTEXT aber nur den Editor bereitstellt, benötigt man noch einen BASIC-Compiler und ein Übertragungstool ("Downloadtool") um das erstellte und übersetzte BASIC-Programm auf die OM zu laden. Als BASIC-Compiler wird am sinnvollsten der eigens für die OM entwickelte Open-Control/BASIC-Compiler (OCBAS.EXE) von Dietmar Harlos verwendet. Als Übertragungstool hat sich unter Windows besonders OMDLWIN bewährt. Beide Programme können bei den Downloads heruntergeladen werden. OMDLWIN läßt sich komfortabel in die ConTEXT-IDE einbinden, auf Tastendruck starten und kann den OM-Controller nach einer erfolgreichen Übertragung automatisch starten. ZIP-Archive lassen sich übrigens mittels WinZip entpacken.
Bevor man mit der Programmierung der OM beginnen kann, muß man aber erst noch diese Tools aufeinander abstimmen. Außerdem ist es sinnvoll, noch ein Terminal-Programm mit in die Programmierumgebung einzubinden. Mit dem Terminal-Programm kann man sich dann die Daten anschauen, die von der OM über die Serielle Schnittstelle per PRINT oder PUT gesendet werden. Man kann damit auch Zeichen und Texte über die PC-Tastatur eingeben und zur OM senden. In der Betatestphase hat sich das Terminal-Programm für die C-Control von Dietmar Harlos (TERMINAL.EXE oder Terminal32.exe) hervorragend bewährt. Das Terminal-Programm liegt dem ZIP-Archiv mit den Übertragungstools bei.
Terminal32.exe sollte unter neueren Windowsversionen verwendet werden.
Nachfolgend wird detailliert beschrieben, wie solch eine Konfiguration aussehen kann.
Zuerst erstellt man auf Laufwerk C: ein Verzeichnis mit dem Namen OM-Tools. Es läßt sich also über den Pfad C:\OM-Tools ansprechen. In dieses Verzeichnis wird nun der BASIC-Compiler OCBAS.EXE, der BASIC-Compiler OCBAS32.EXE, das Übertragungstool OMDLWIN.EXE und das Terminal-Programm TERMINAL.EXE oder Terminal32.exe hineinkopiert. Es befinden sich also nach dem Kopieren die vier Dateien in dem Verzeichnis C:\OM-Tools. Selbstverständlich ist es auch möglich, die Hilfsprogramme in einem anderen Verzeichnis abzulegen. Dann müssen in der folgenden Anleitung allerdings die Pfade zu den Dateien angepaßt werden.
Jetzt wird ConTEXT in der deutschen Version installiert. Dabei wird immer den Anweisungen gefolgt, bis die Installation abgeschlossen ist. Nach der Installation sollte sich ein ConTEXT-Icon auf dem Desktop befinden. Damit ist die halbe Arbeit bereits getan und die Programme müssen nur noch aufeinander abgestimmt werden.
Dazu startet man jetzt ConTEXT und geht auf den Menüpunkt Einstellungen. Danach auf den Menüpunkt Umgebungseinstellungen. Dann wechselt man auf Benutzerbefehle. Jetzt werden die Einstellungen der einzelnen Tools festgelegt. Dazu ruft man Neu auf. Es öffnet sich ein neues Fenster in dem jetzt bas eingefügt und dann auf OK geklickt wird. Jetzt wird als Benutzerbefehl bas mit den Zweigen F9, F10, F11, F12 angezeigt.
Man klickt jetzt einfach auf F9 und kann dann Angaben zum Tool machen, das starten soll, wenn die Taste F9 in ConTEXT gedrückt wird. Wir belegen jetzt F9 mit dem BASIC-Compiler.
Bei Ausführen wird jetzt C:\OM-Tools\OCBAS32.EXE eingegeben. Bei Start in ein %p (das p unbedingt klein schreiben). Bei Parameter wird ein
Jetzt klickt man auf F10 und gibt die Parameter des Übertragungstools ein. Bei Ausführen wird C:\OM-Tools\OMDLWIN.EXE eingegeben. Bei Start in ein %p (das p unbedingt klein schreiben). Bei Parameter wird %F.DAT COM1 autobaud autostart (das F unbedingt groß schreiben, kein Leerzeichen zwischen %F und .DAT) eingetragen. Das COM1 bezieht sich auf die erste Serielle Schnittstelle. Wird die OM an einem anderen COM-Port angeschlossen, so muß natürlich der entsprechende COM-Port eingetragen werden (COM2, COM3, COM4 oder andere). Es existieren Konverter, die USB nach RS232 wandeln können. Damit läßt sich die OM auch über USB programmieren. In diesem Fall muß der virtuelle COM-Port des Treibers eingetragen werden. Durch das "autostart" wird der OM-Controller nach einer erfolgreichen Programmübertragung automatisch gestartet. Durch "autobaud" wird automatisch die maximal mögliche Übertragungsgeschwindigkeit gewählt. Das ist nützlich auf der Open-Macro und Open-Maxi.
Bei Fenster wird wieder Normal ausgewählt und bei Hinweis wird DAT übertragen eingetragen. Bei Benutze kurze DOS-Dateinamen wird ein Häkchen gesetzt.
In den anderen Feldern wird wieder nichts eingetragen. Diese Einstellungen werden mit Übernehmen gespeichert.
Jetzt klickt man auf F11 und trägt das Terminal-Programm ein. Bei Ausführen wird C:\OM-Tools\TERMINAL.EXE oder C:\OM-Tools\Terminal32.exe eingegeben. Bei Fenster wird Normal ausgewählt und bei Hinweis wird Terminal starten eingetragen. In den anderen Feldern wird wieder nichts eingetragen und es werden auch keine Häkchen gesetzt. Die Einstellungen werden jetzt mit Übernehmen gespeichert und danach wird die Umgebungseinstellung mit OK verlassen.
ConTEXT ist jetzt konfiguriert und es können BASIC-Programme editiert, compiliert und auf die OM geladen werden. Wer im externen Assembler AS05 programmieren möchte, kann diese Einstellungen natürlich auch für den Assembler AS05.EXE vornehmen. Dazu
bei den Benutzerbefehlen wieder Neu aufrufen und die neue Gruppe asm anlegen. Sinnvollerweise legt man den Assembler auf die Taste F9. Als Parameter sollte -sl %n eingetragen werden.
Auf die beschriebene Weise können natürlich auch andere Programme in ConTEXT eingebunden werden, die den Umgang mit der OM erleichtern. Zum Beispiel das Hilfsprogramm zum Starten der OM über die Serielle Schnittstelle.
Um ein Programm in die OM zu laden muß der Schalter auf dem PE-Board in Stellung Host stehen. Danach wird die Versorgungsspannung eingeschaltet und der Schalter sinnvollerweise auf Run zurückgestellt.
Zuerst sollte man ein Demo-Programm auf die OM laden, um sich zu vergewissern, daß die vorgenommenen Einstellungen korrekt sind.
Dazu eignet sich gut die Datei MICRO_BM.BAS aus dem ZIP-Archiv mit den diversen Beispielen. Dazu öffnet man die Datei MICRO_BM.BAS in ConTEXT. Unmittelbar nach dem Öffnen wird der Quellcode der Datei im Editor angezeigt, den man mit den üblichen Editierkommandos verändern könnte.
Oberhalb im Menü sind 4 kleine Köpfe (Benutzerbefehle) abgebildet. Bewegt man den Mauszeiger über den ersten Kopf so wird BASIC-Compiler (F9) angezeigt. Sobald man ihn anklickt, wird eine Datei namens MICRO_BM.DAT erzeugt. Sie enthält den fertig übersetzten sogenannten Tokencode.
Falls Fehler im Quellcode enthalten sein sollten, dann wird das im unteren Teil (in der Konsolenausgabe) von ConTEXT angezeigt und die DAT-Datei nicht erstellt. Durch Doppelklicken auf die Fehlermeldung springt der Cursor zur Zeile mit dem Fehler in der Quellcodedatei.
Danach klickt man auf den zweiten Kopf bei dem DAT übertragen (F10) angezeigt wird. Jetzt wird die Datei MICRO_BM.DAT auf die OM geladen. Es muß also immer zuerst die BAS-Datei in eine DAT-Datei compiliert und diese dann übertragen werden. Nachdem die Datei erfolgreich übertragen wurde, wird jetzt der dritte Kopf bei dem Terminal starten angezeigt wird, angeklickt. Es öffnet sich das Terminal-Programm in einem Fenster.
Falls das Programm in der OM noch nicht per OMDLWIN und "autostart" gestartet wurde, kann es durch den Reset-Knopf auf dem PE-Board oder durch Drücken der Eingabetaste (auch Enter oder Return genannt) im Terminal-Fenster gestartet werden. Wenn alles ordnungsgemäß funktioniert, dann wird im Terminal-Programm jetzt immer wieder Ergebnis: 11320 I/s angezeigt. Wenn man genug davon hat, kann man den Controller ausstellen und das Terminal-Programm durch Drücken auf die Taste ESC beenden.
Im Menüpunkt Einstellungen gibt es den Eintrag Einstellungen exportieren (Registry), mit dem man die oben vorgenommenen Einstellungen sichern kann. Damit stehen diese Einstellungen für eine Neuinstallation von ConTEXT zur Verfügung.
Nicht zuletzt zum Schutz vor Viren und anderen bösartigen Programmen ist es sinnvoll, die sogenannte Dateinamenserweiterung, auch Dateiendung oder Suffix genannt, von grundsätzlich allen Dateitypen im Windows Explorer anzeigen zu lassen. Zum Beispiel haben ausführbare Programme die Endung "EXE", während ASCII-Textdateien meist die Endung "TXT" besitzen. Unter jeder Windowsversion läßt sich die Anzeige dieser zusätzlichen Information auf etwas unterschiedliche Art und Weise aktivieren. In den folgenden zwei Bildern wird gezeigt, wie es unter Windows 2000 funktioniert.
Zur Herstellung der OM-Controller und zur Programmentwicklung werden überwiegend sogenannte DOS-Programme eingesetzt. Neuere Windowsversionen arbeiten bei der Ausführung von DOS-Programmen etwas anders als beispielsweise Windows 95 oder 98. Nachdem ein DOS-Programm zum Beispiel im Windows Explorer per Doppelklick gestartet wurde, wird wie bisher ein DOS-Fenster geöffnet, in dem die Ausgaben des Programms erscheinen. Falls das Programm nur einen kurzen Hinweistext über dessen Benutzung ausgibt wird das Fenster nun aber sehr schnell wieder geschlossen und die Ausgaben gehen verloren. Das läßt sich konfigurieren: Im Windows Explorer muß das DOS-Programm ausgewählt und anschließend die rechte Maustaste gedrückt werden. Es erscheint das sogenannte Kontextmenü, dessen letzter Eintrag "Eigenschaften" lautet. In diesen Eigenschaften kann "Nach Beenden schließen", wie im folgenden Bild gezeigt, deaktiviert werden.
In der folgenden Anleitung wird gezeigt, wie das Betriebssystem der Open-Micro Version 0.6 in einen QT4- oder QY4-Controller programmiert und auf diese Weise sehr einfach eine Open-Micro oder Open-Mini hergestellt werden kann. Alle Schritte werden detailliert erläutert und auf mögliche Fehler und deren Behebung hingewiesen. Der Vorgang wurde auf einem PC unter Windows 2000 durchgeführt. Es ist also durchaus möglich, auch unter neueren Windowsversionen eine Open-Micro oder Open-Mini herzustellen.
Mittlerweile kann die aktuelle Betriebssystem-Version 1.0 von der offiziellen Internetsite der Open-Micro heruntergeladen werden. Deshalb muß OM10.ZIP anstatt OM06.ZIP heruntergeladen und die Verzeichnisnamen entsprechend angepaßt werden.
Zuerst muß das aktuelle ZIP-Archiv mit dem Betriebssystem und den zugehörigen Hilfsprogrammen von der Side für Betatester heruntergeladen werden. Im folgenden Bild wird gezeigt, wie der Internet Explorer hierfür verwendet wird. Der Mauszeiger wird auf den blau hinterlegten und unterstrichenen Link bewegt und die rechte Maustaste gedrückt. Es erscheint das dargestellte Kontext-Menü, in dem "Ziel speichern unter..." angeklickt wird.
Es erscheint ein "Datei speichern unter"-Dialog, der uns dazu auffordert, festzulegen, an welcher Stelle auf dem Computersystem wir die Datei abspeichern wollen. Wir bleiben bei dem, was der PC vorschlägt und speichern im Verzeichnis mit den Eigenen Dateien. Wir bestätigen den Download mit einem Mausklick auf die Schaltfläche "Speichern". Je nach Geschwindigkeit des Internetanschlusses kann es nun ein paar Sekunden dauern, bis der Download beendet ist. Danach kann der Internet Explorer und die Verbindung zum Internet beendet werden.
Der Grund, warum wir im Ordner mit den eigenen Dateien gespeichert haben ist der, daß der Zugriff auf dieses Verzeichnis sehr einfach möglich ist. Ein Icon namens "Eigene Dateien" liegt auf dem Desktop. Wenn es mit der Maus doppelt angeklickt wird, öffnet sich ein Fenster, das den Inhalt des Ordners anzeigt.
Im Fenster "Eigene Dateien" muß nun eine Datei namens OM06.ZIP stehen. Je nach Konfiguration des Windowssystems ist es denkbar, daß die Endung "ZIP" nicht angezeigt wird und nur "OM06" erscheint. Wir wählen die Datei aus und kontrollieren auf diese Weise deren Größe. Sie sollte etwa 638 KB betragen. Falls die Größe der Datei abweicht ging vermutlich irgendetwas beim Download schief. Dann sollte die Datei erneut von der Side für Betatester heruntergeladen werden. Andernfalls wird die rechte Maustaste gedrückt und im erscheinenden Kontext-Menü die Aktion "Extract to" oder "Entpacken nach" angeklickt. Falls so ein Punkt nicht vorhanden ist, muß zunächst ein Programm zum Entpacken von ZIP-Archiven aus dem Internet heruntergeladen und installiert werden. Man kann zum Beispiel WinZIP oder WinRAR installieren. Es gibt aber auch diverse kostenlose Programme. Viele Windowsversionen können auch von sich aus mit ZIP-Archiven umgehen. Entscheidend ist auf jeden Fall, daß das Archiv komplett und mit Verzeichnisstruktur in das neue Verzeichnis "OM06" entpackt wird.
Einige Dateien im ZIP-Archiv sind paßwortgeschützt, deshalb erscheint ein Dialog, in dem das Paßwort einzugeben ist. Durch eine erneute Anmeldung können inaktive Betatester das Paßwort erhalten.
Falls während des Entpackens Fehlermeldungen erscheinen wurde vermutlich ein falsches Paßwort eingegeben oder das ZIP-Archiv ist fehlerhaft. Im letzteren Fall muß die Datei erneut von der Side für Betatester heruntergeladen werden. Ansonsten wurde ein neues Verzeichnis namens OM06 angelegt, in das wir nun wechseln, um dessen Inhalt anzuzeigen. Vier Textdateien laden zum Lesen ein. Wer Lust hat, kann diese Dateien doppelt anklicken, wodurch sich normalerweise ein Notepad-Fenster öffnet, in dem der Text gelesen werden kann. Die Informationen in diesen Dateien könnten nützlich sein, wenn die hier beschriebene Anleitung nicht zum gewünschten Ziel führt. Nach dem Studium kann das Fenster "Eigene Dateien", bzw. "om06" geschlossen werden.
Das Betriebssystem wird mit Hilfe von DOS-Programmen auf den Controller geladen. Diese Programme werden über einen sogenannten Console-Interpreter bedient. Aus diesem Grund muß unter Windows die MS-DOS-Eingabeaufforderung gestartet werden. Je nach Windowsversion unterscheidet sich der Aufruf etwas. Im Bild ist gezeigt, wie es im Start-Menü von Windows 2000 funktioniert.
Bei der Eingabeaufforderung handelt es sich um ein Fenster, das mehr oder weniger die im folgenden Bild gezeigte Form besitzt. Der blinkende Textcursor lädt uns ein, Befehle über die Tastatur einzugeben. Denn die Eingabeaufforderung ist textbasiert.
Um etwas vertraut mit der Eingabeaufforderung zu werden, geben wir einen ersten Befehl ein. Wir drücken "d", dann "i" und schließlich die Taste "r" auf der Tastatur. Um diesen Befehl zu starten wird die Eingabe mit der Eingabetaste (auch Enter oder Return genannt) bestätigt. Wir haben dadurch den Befehl "dir" aufgerufen, der den Inhalt des aktuellen Verzeichnisses ("current directory") anzeigt. In welchem Verzeichnis wir uns derzeitig befinden steht vor dem blinkenden Cursor. In den dargestellten Bildern ist es das Verzeichnis "Administrator", welches im Verzeichnis "Dokumente und Einstellungen" steht und auf dem Laufwerk "J:" liegt. Bei "J:\Dokumente und Einstellungen\Administrator" handelt es sich um einen sogenannten Pfad, ähnlich der URL (Adresse) im Internet. Jede Datei und jedes Verzeichnis auf einem Computer kann eindeutig durch Pfad und Namen angesprochen werden.
Leider stehen wir noch nicht im richtigen Verzeichnis. Wir erinnern uns: Das ZIP-Archiv wurde ins Verzeichnis "om06" entpackt, das in den "Eigenen Dateien" liegt. Glücklicherweise sind wir nicht weit davon entfernt. Mit Hilfe des CD-Befehls ("change directory") kann in ein anderes Verzeichnis gewechselt werden. Wie im Bild gezeigt benötigen wir drei CD-Befehle, um in das richtige Verzeichnis mit den Hilfsprogrammen zu wechseln. Diese drei Befehle müssen nun in der richtigen Reihenfolge eingegeben werden. Bei Bedarf kann zwischendurch wieder der Befehl "dir" aufgerufen werden.
Je nach Windowsversion und Konfiguration kann es vorkommen, daß wir nach Start der MS-DOS-Eingabeaufforderung auch in einem völlig anderen Verzeichnis stehen. Unter Windows 95 und 98 kann es passieren, daß wir in "C:\WINDOWS" oder direkt im Verzeichnis mit den eigenen Dateien landen. In diesem Fall muß mit Hilfe des CD-Befehls in das korrekte Verzeichnis mit den Hilfsprogrammen gewechselt werden.
Um eine Open-Micro oder Open-Mini herzustellen wird natürlich auch ein wenig Hardware benötigt. In dieser Anleitung wird der Controller in das zweite PE-Board eingesetzt. Selbstverständlich ist es möglich, ein anderes Board zu benutzen. Es ist dann nur darauf zu achten, daß die Ports PTA0 bis PTA6 des Controllers korrekt mit dem richtigen Spannungspegel verbunden sind.
Die Informationen auf der Side für Betatester sollten ausreichen, um ein Board nach eigenen Wünschen herzustellen. Ansonsten kann das Schaltbild, das Layout und der Bestückungsplan für das zweite PE-Board im Eagle-Format heruntergeladen werden. Um das zweite PE-Board herzustellen muß man die BRD-Datei in der Freeware-Version von Eagle laden, das Layout ausdrucken, die Platine belichten und ätzen (pro Europaplatine lassen sich zwei Boards herstellen), dann zuschneiden, passend bohren (meist 0.8 mm, 1.0 mm, 1.2 mm und 1.5 mm) und bestücken (Brücken zuerst, dann Dioden, etc.). Es ist eine feine Lötkolbenspitze und eine ruhige Hand erforderlich.
Das Poti zur Einstellung der Versorgungsspannung wird nach dem Bestücken in Mittelstellung gebracht. Beim Einsetzen des 4049 ist zu beachten, daß dieses IC keine Schutzdioden an seinen Eingängen besitzt, deshalb dieses IC besser nur geerdet berühren. Nun das Board ohne Controller, ohne Jumper aber mit eingestecktem 4049 testweise in Betrieb nehmen: Eine Spannung von mindestens 8 Volt an die NV-Buchse oder die Schraubklemmen geben. Zum Beispiel aus einem 9-Volt- oder 12-Volt-Netzteil, das auch unstabilisiert sein darf. Die Stromaufnahme liegt bei unter 25 mA, deshalb ist auch eine 9-Volt-Block-Batterie oder ein Akku geeignet. Das Board besitzt einen Verpolungsschutz. Einschalten des Boards geht mit Hilfe des Ein-/Aus-Schalters unten links.
Nun den Jumper zum Aktivieren der LEDs (links neben dem 4049) setzen. Es leuchtet nun zumindest die rechte LED (PTA0/RS232). Die LED Mitte rechts (PTA2/RUN/HOST) läßt sich durch den Hostmode-Schalter ("RUN/HOST") rechts neben dem roten Reset-Taster aktivieren und deaktivieren. Durch Drücken der zugehörigen Taster verlöschen diese zwei LEDs, müssen nach dem Loslassen aber sofort wieder leuchten. Der Zustand der übrigen LEDs ist undefiniert, weil die Eingänge des 4049 "in der Luft hängen", da kein Controller eingesetzt ist. Idealerweise sollte jeder Eingang des 4049 auf Kurzschluß ausgetestet werden. Also jeden Eingang nacheinander mit Masse und VDD verbinden. Alle Eingänge des 4049 liegen an der Stiftleiste PORTA. Bei einem Kurzschluß verlöschen alle LEDs.
Der Port PTA2 muß während des ersten Betriebssystem-Downloads auf low (Masse, GND, 0 Volt) liegen, deshalb den Hostmode-Schalter in Stellung "HOST" setzen. Die LED Mitte rechts (PTA2) verlischt dadurch. Der Port PTA0 muß auf high (VDD, 5 Volt) liegen, was die Schaltung für das RS232-Interface auf dem Board bereits erledigt. Die LED rechts außen (PTA0) muß also leuchten. Jetzt die übrigen Jumper setzen: Den Jumper "PROTECT" rechts neben dem 4049 in Stellung 1-2, also obere Stellung. Den Jumper "RS232" rechts außen ebenso. Bei diesem Jumper auf guten Kontakt achten! Der Jumper AD/BEEPER ganz links muß unbestückt bleiben oder in deaktivierter Stellung (2-3). Schließlich die Betriebsspannung des Boards mit dem Ein-/Aus-Schalter ausstellen.
Einen leeren, unbenutzten Controller (d.h. Controller ohne Betriebssystem) in das Board einsetzen. Das kann ein QT4 oder ein QY4 sein. Es dürfen *NICHT* zwei Controller gleichzeitig eingesetzt werden. Alle Ports außer PTA0 und PTA2 müssen während des Betriebssystem-Downloads N.C. (not connected, nicht konnektiert) sein, deshalb keinen Taster drücken und keine Hardware an die Stiftleiste PORTA oder PORTB anschließen! Nun das Board über RS232-Kabel (Kabel der C-Control, Nullmodem-Kabel) mit dem PC verbinden.
An dieser Stelle muß ich darauf hinweisen, daß solange das Betriebssystem noch nicht auf den Controller geladen ist, der Reset-Taster ("LVI-Reset") keine Funktion hat. Resetten läßt sich der Controller bis auf weiteres nur mittels POR ("Power On Reset"), das heißt nur durch Wegnahme und erneutes Anlegen der Betriebsspannung. Auf dem Board also am komfortabelsten durch den Ein-/Aus-Schalter. Nachdem die Betriebsspannung an den Controller gelegt wird befindet sich dieser im sogenannten "Forced Monitor Mode" und wartet auf die Übermittlung von acht "Security Bytes" über die RS232-Schnittstelle. Falls während des Übertragens des Betriebssystems irgendetwas schief ging und ein neuer Versuch gestartet werden soll, muß der Controller zunächst durch Wegnahme und erneutes Anlegen der Betriebsspannung wieder in den "Forced Monitor Mode"-Zustand gebracht werden.
Nicht zuletzt um die Verbindung zwischen PC und Controller zu testen sollte nun zunächst jeder Controller getrimmt werden. Das heißt, der interne Controller-Systemtakt wird auf 3.2 MHz kalibriert. Ohne diese Kalibierung könnten Probleme beim Betriebssystemdownload auftauchen, da die Verbindung über die serielle RS232-Schnittstelle bei mehr als etwa 5% Abweichung vom Nominaltakt abbrechen könnte.
Nun den Controller durch Wegnahme und erneutes Anlegen der Betriebsspannung resetten. Auf dem Board kann dazu der Ein-/Aus-Schalter verwendet werden. Danach im Fenster der Eingabeaufforderung das Programm OSCTRIM starten. Durch Leerzeichen (Space) getrennt wird noch die Nummer der zu verwendenden seriellen Schnittstelle übergeben. Also "osctrim 2" starten, falls das Board an die zweite RS232-Schnittstelle ("COM2") angeschlossen ist und "osctrim 1" für die erste serielle Schnittstelle ("COM1").
Wenn alles funktioniert, dann werden, wie in den folgenden zwei Bildern dargestellt, zunächst die acht "Security Bytes" übertragen und die Inhalte einiger Register und Speicherzellen des Controllers ausgegeben. Anschließend wird ein Maschinenspracheprogramm namens OSCTRIM.S19 eingelesen, zum Controller geschickt und dort ausgeführt. Es sorgt nun für eine fortlaufende Messung und Anpassung des Prozessortaktes und so wird rund ein Dutzend mal kalibiert, um sich langsam an das exakte Ergebnis anzunähern. Zum Schluß fordert das Programm dazu auf, "Y" (ein großes Ypsilon) zu drücken. Das sollte man unbedingt machen, denn nur dann wird das kalibrierte Ergebnis in den Flashspeicher des Controllers programmiert. Es ist sinnvoll, sich den neuen OSCTRIM-Wert zu notieren. In den dargestellten Bildern ist das der Wert $6e.
Falls Fehler auftreten: Wird bereits das erste Security Byte mit einem "TIMEOUT!" quittiert konnte offenbar überhaupt keine Verbindung zum Controller aufgebaut werden. Dafür kann es natürlich viele Gründe geben. Kann es sein, daß irgendein anderes Programm die serielle Schnittstelle belegt, ein Hardwareproblem vorliegt oder der angegebene COM-Port auf diesem PC gar nicht existiert? Falls während des Sendens des zweiten bis achten Security Bytes ein Timeout-Fehler auftritt, dann sollte das im Normalfall daran liegen, daß der Controller nicht vorschriftsgemäß durch Wegnahme und erneutes Anlegen der Betriebsspannung resettet wurde. Falls der Fehler "WRONG MCU ECHO $.. SENDING $4a" auftritt, dann ist der Controller offenbar von Werk aus so schlecht kalibriert, daß auch das OSCTRIM-Hilfsprogramm keine RS232-Verbindung zum Controller aufbauen kann. Der Prozessortakt weicht also vermutlich etliche Prozent vom Nominaltakt ab. In diesem Fall kann der Prozessortakt durch Absenken der Betriebsspannung VDD auf 4 Volt um 2% erhöht oder durch Erwärmen des Controllers mittels Fön um rund 1% erniedrigt werden. Die Kalibrierung muß man dann unter Normalbedingungen wiederholen. Falls auch diese Maßnahme nichts bringt, dann handelt es sich wohl um ein Exemplar, das leider nicht per "Forced Monitor Mode" zu beschreiben ist. Es muß der im Manual zum 68HC908QY4-Controller beschriebene normale "Monitor Mode" verwendet werden, bei dem der Systemtakt aus einem externen Taktgenerator gewonnen wird. Näheres steht im Manual.
Wer hartnäckigen Übertragungsproblemen auf die Spur kommen möchte, kann den Controller mit Hilfe eines einfachen Terminalprogramms testen. Am besten geeignet ist dafür das im ZIP-Archiv beiliegende TERMINAL.EXE. Solange das Betriebssystem noch nicht aufgespielt wurde, erwartet der Controller direkt nach dem Anlegen der Betriebsspannung (POR, Power On Reset) den Empfang von acht sogenannten Security-Bytes. Man sollte daher "terminal.exe 2" starten (die "2" steht für "COM2"), den Controller durch Wegnehmen und wieder Anlegen der Betriebsspannung resetten und dann über die Tastatur beliebige acht Zeichen eingeben. Jedes Zeichen sollte vom Controller empfangen und als Echo zum PC zurückgesendet werden. Wird z.B. "A" als nichts anderes als ein "A" zurückgesendet oder gibt es bereits dabei Probleme? Nach den acht Zeichen sollte ein "[I/O-ERR]" auftreten, da der Controller ein Break über die serielle Schnittstelle zum PC schickt. Wenn alles klappt, erwartet der Controller danach Monitor-Mode-Kommandos, die sich nicht so ohne weiteres über die Tastatur eingeben lassen. Also den Controller wieder resetten und einen erneuten Anlauf mit OSCTRIM unternehmen.
Boards mit Single-Wire-RS232 arbeiten mit den aktuellen Versionen von OSCTRIM und S19MON08 nicht zusammen. Theoretisch könnte man dafür die Tools aus dem alten OM01.ZIP-Archiv benutzen. Wie in der beiliegenden Dokumentation beschrieben, muß dann aber der OSCTRIM-Wert auf Adresse $ffc0 manuell gesetzt werden.
Wir stehen im Verzeichnis mit den Hilfsprogrammen und wechseln daher wie im folgenden Bild gezeigt per "cd ..\os" in das Verzeichnis mit dem Betriebssystem. In diesem Verzeichnis steht die Textdatei INFO.TXT, die nützliche Informationen enthält. Sie läßt sich sehr schnell per "type info.txt" darstellen. Wir entscheiden uns für die normale 5-Volt-Version vom Betriebssystem.
Nun den Controller durch Wegnahme und erneutes Anlegen der Betriebsspannung resetten. Auf dem Board kann dazu der Ein-/Aus-Schalter verwendet werden. Danach im Fenster der Eingabeaufforderung das Programm S19MON08 starten. Da dieses Programm im Verzeichnis mit den Hilfsprogrammen steht, muß beim Aufruf der relative Pfad zur Programmdatei angegeben werden. Durch Leerzeichen (Space) getrennt wird der Name der zu übertragenden Betriebssystem-Datei angegeben. In unserem Fall ist das "OMOS06.S19". Und schließlich wird durch ein weiteres Leerzeichen getrennt auch noch die Nummer der zu verwendenden seriellen Schnittstelle übergeben. Also muß man "..\tools\s19mon08 omos06.s19 2" starten, falls das Board an die zweite RS232-Schnittstelle ("COM2") angeschlossen ist und "..\tools\s19mon08 omos06.s19 1" für die erste serielle Schnittstelle ("COM1").
Es wird nun wie im folgenden Bild gezeigt das Betriebssystem übertragen. Je nach PC kann das zwischen einer halben Minute und zwei Minuten dauern. Danach die Betriebsspannung ausschalten. Wenn korrekt getrimmt und resettet wurde, dürften keine Fehler auftreten. Falls es doch zu Fehlern kommt, dann oben bei den Fehlerbeschreibungen beim Trimmen nachlesen. Der Jumper AD/BEEPER kann nun auf dem Board aktiviert werden.
; =========================================================================== ; = Projekt : ROM-Listing vom Open-Micro-Betriebssystem V1.0 = ; = Controller: MC68HC908QT4CP und MC68HC908QY4CP von Freescale = ; = Mnemonic : AS05 for M6805 [1.40] from Frank A. Vorstenbosch = ; = Copyright : Dietmar Harlos ADPC = ; = Datum : ab 2005/11/16 = ; =========================================================================== ; --------------------------------------------------------------------------- ; DATA - Tabelle zur Berechnung der Tokenroutinen-Adressen ; --------------------------------------------------------------------------- ee00 : 76 73 6d 64 5a 54 51 b5 51 45 51 4a ba ca 26 78 vsmdZTQÁQEQJ¦-&x ee10 : 51 3b 08 35 20 1a d8 d2 c3 c9 be b8 ac ab a4 9e Q;.5 .ÏÊ++¥©¼½ñ× ee20 : 99 95 91 8c 70 87 96 8c 87 c1 82 7c 77 48 3e 6c Öòæîpçûîç-é|wH>l ee30 : 74 2d 59 54 74 4d f2 f9 98 bf 80 51 e6 6b 51 c9 t-YTtM_¨ÿ+ÇQµkQ+ ee40 : 48 dc 50 27 51 30 65 6b a6 76 7a d3 dc c2 34 46 H_P'Q0ekªvzË_-4F ee50 : 57 80 25 92 d8 56 WÇ%ÆÏV ; --------------------------------------------------------------------------- ; CODE - Nach Reset wird der User-Monitor und danach $ee56 gestartet ; --------------------------------------------------------------------------- ee56 : 8c clrh ; clear h-register (index register high) ee57 : 5f clrx ; clear x-register (index register low) ee58 : 3504 sthx $04 ; store index register (hx) to address $04 ee5a : 3500 sthx $00 ; store index register (hx) to address $00 ee5c : 453fff ldhx #$3fff ; load index register (hx) with value ee5f : 350b sthx $0b ; store index register (hx) to address $0b ee61 : cefff0 ldx $fff0 ; load x-register with value at addr. $fff0 ee64 : 518a01 cbeqx #$8a,$ee68 ; compare x-reg with value & branch if equal ee67 : 32 db $32 ; illegal opcode ee68 : 6e8b1f mov #$8b,$1f ; store value #$8b to address $1f ee6b : 040004 brset #2,$00,$ee72 ; branch to $ee72 if bit #2 at $00 is set ee6e : 9c rsp ; reset stack pointer (sp=$00ff) ee6f : cdf1f8 jsr $f1f8 ; jump to subroutine at address $f1f8 ee72 : 9b sei ; set interrupt mask (disable interrupts) ee73 : 8c clrh ; clear h-register (index register high) ee74 : aee6 ldx #$e6 ; load x-register with value #$e6 ee76 : f7 sta ,x ; store accumulator at address in hx ee77 : 4f clra ; clear accumulator ee78 : 5a decx ; decrement x-register ee79 : 2bfb bmi $ee76 ; branch if minus (N bit is set) ee7b : 6e8a97 mov #$8a,$97 ; store value #$8a to address $97 ee7e : 3cc5 inc $c5 ; increment at address $c5 ee80 : 9b sei ; set interrupt mask (disable interrupts) ee81 : 8c clrh ; clear h-register (index register high) ee82 : 5f clrx ; clear x-register (index register low) ee83 : 35e2 sthx $e2 ; store index register (hx) to address $e2 ee85 : 3fe4 clr $e4 ; clear memory at address $e4 ee87 : ae40 ldx #$40 ; load x-register with value #$40 ee89 : 351a sthx $1a ; store index register (hx) to address $1a ee8b : bf3f stx $3f ; store x-register at address $3f ee8d : 6e5620 mov #$56,$20 ; store value #$56 to address $20 ee90 : 4503e7 ldhx #$03e7 ; load index register (hx) with value ee93 : 3523 sthx $23 ; store index register (hx) to address $23 ee95 : 3f1d clr $1d ; clear memory at address $1d ee97 : 6ed5df mov #$d5,$df ; store value #$d5 to address $df ee9a : 6ecdd4 mov #$cd,$d4 ; store value #$cd to address $d4 ee9d : 45f482 ldhx #$f482 ; load index register (hx) with value eea0 : 35e0 sthx $e0 ; store index register (hx) to address $e0 eea2 : f6 lda ,x ; load accu with value at address in hx eea3 : ee03 ldx $03,x ; load x-register with value at $03+hx eea5 : 87 pusha ; push accu onto stack eea6 : 8a poph ; pop (pull) h-register from stack eea7 : 650312 cmphx #$0312 ; compare index register (hx) with value eeaa : 2602 bne $eeae ; branch if not equal (Z is clear) eeac : 1ae3 bset #5,$e3 ; set bit #5 at memory address $e3 eeae : 9a cli ; clear interrupt mask (enable interrupts) eeaf : 9c rsp ; reset stack pointer (sp=$00ff) eeb0 : ad40 bsr $eef2 ; branch to subroutine at address $eef2 eeb2 : 03e3fb brclr #1,$e3,$eeb0 ; branch to $eeb0 if bit #1 at $e3 is clear eeb5 : 06e3f8 brset #3,$e3,$eeb0 ; branch to $eeb0 if bit #3 at $e3 is set eeb8 : 05e3f5 brclr #2,$e3,$eeb0 ; branch to $eeb0 if bit #2 at $e3 is clear eebb : ad59 bsr $ef16 ; branch to subroutine at address $ef16 eebd : 13e3 bclr #1,$e3 ; clear bit #1 at memory address $e3 eebf : bec0 ldx $c0 ; load x-register with value at address $c0 eec1 : b6c1 lda $c1 ; load accu with value at address $c1 eec3 : ad65 bsr $ef2a ; branch to subroutine at address $ef2a eec5 : 04e408 brset #2,$e4,$eed0 ; branch to $eed0 if bit #2 at $e4 is set eec8 : ae08 ldx #$08 ; load x-register with value #$08 eeca : e6cc lda $cc,x ; load accu with value at addr. $cc+hx eecc : e7b7 sta $b7,x ; store accumulator at address $b7+hx eece : 5bfa dbnzx $eeca ; decrement x-reg. and branch if not zero eed0 : 16e3 bset #3,$e3 ; set bit #3 at memory address $e3 eed2 : 20dc bra $eeb0 ; branch always to $eeb0 eed4 : 0ee497 brset #7,$e4,$ee6e ; branch to $ee6e if bit #7 at $e4 is set eed7 : 41fffa cbeqa #$ff,$eed4 ; compare accu with value & branch if equal eeda : 08e41f brset #4,$e4,$eefc ; branch to $eefc if bit #4 at $e4 is set eedd : a67d lda #$7d ; load accumulator with value #$7d eedf : 4c inca ; increment accumulator eee0 : 4c inca ; increment accumulator eee1 : 4c inca ; increment accumulator eee2 : b7cc sta $cc ; store accumulator at address $cc eee4 : 2b03 bmi $eee9 ; branch if minus (N bit is set) eee6 : 0ae4c6 brset #5,$e4,$eeaf ; branch to $eeaf if bit #5 at $e4 is set eee9 : 55e0 ldhx $e0 ; load index register (hx) from address $e0 eeeb : 35c0 sthx $c0 ; store index register (hx) to address $c0 eeed : 0ce490 brset #6,$e4,$ee80 ; branch to $ee80 if bit #6 at $e4 is set eef0 : 20fb bra $eeed ; branch always to $eeed eef2 : 55e0 ldhx $e0 ; load index register (hx) from address $e0 eef4 : f6 lda ,x ; load accu with value at address in hx eef5 : af01 aix #$01 ; add signed value to index register (hx) eef7 : 35e0 sthx $e0 ; store index register (hx) to address $e0 eef9 : 0fe209 brclr #7,$e2,$ef05 ; branch to $ef05 if bit #7 at $e2 is clear eefc : b7e6 sta $e6 ; store accumulator at address $e6 eefe : a680 lda #$80 ; load accumulator with value #$80 ef00 : cdf430 jsr $f430 ; jump to subroutine at address $f430 ef03 : 251f blo $ef24 ; branch if reg. is lower (C is set) ef05 : 8c clrh ; clear h-register (index register high) ef06 : 97 tax ; transfer accumulator to x-register ef07 : 53 comx ; inverse x-register (one's complement) ef08 : deed56 ldx $ed56,x ; load x-register with value at $ed56+hx ef0b : 5151c9 cbeqx #$51,$eed7 ; compare x-reg with value & branch if equal ef0e : 46 rora ; rotate accumulator right through carry ef0f : 47 asra ; arithmetic shift right accumulator ef10 : 47 asra ; arithmetic shift right accumulator ef11 : 47 asra ; arithmetic shift right accumulator ef12 : 4a deca ; decrement accumulator ef13 : 87 pusha ; push accu onto stack ef14 : 8a poph ; pop (pull) h-register from stack ef15 : fc jmp ,x ; unconditional jump to address in hx ef16 : 8c clrh ; clear h-register (index register high) ef17 : 4f clra ; clear accumulator ef18 : bedf ldx $df ; load x-register with value at address $df ef1a : a3df cmpx #$df ; compare x-register with value #$df ef1c : 24c3 bhs $eee1 ; branch if reg. is higher or same (C clear) ef1e : 5ee0 mov $e0,x+ ; copy from $e0 to loc. addr. by hx & inc hx ef20 : 5ee1 mov $e1,x+ ; copy from $e1 to loc. addr. by hx & inc hx ef22 : bfdf stx $df ; store x-register at address $df ef24 : 81 rts ; return from subroutine ef25 : 55e0 ldhx $e0 ; load index register (hx) from address $e0 ef27 : e601 lda $01,x ; load accu with value at addr. $01+hx ef29 : fe ldx ,x ; load x-register with value at addr. in hx ef2a : ab7e add #$7e ; add value #$7e to accumulator ef2c : b7e1 sta $e1 ; store accumulator at address $e1 ef2e : 9f txa ; transfer x-register to accumulator ef2f : a9f4 adc #$f4 ; add with carry value #$f4 to accu ef31 : b7e0 sta $e0 ; store accumulator at address $e0 ef33 : 81 rts ; return from subroutine ef34 : 4f clra ; clear accumulator ef35 : 07e3a8 brclr #3,$e3,$eee0 ; branch to $eee0 if bit #3 at $e3 is clear ef38 : 04e409 brset #2,$e4,$ef44 ; branch to $ef44 if bit #2 at $e4 is set ef3b : 8c clrh ; clear h-register (index register high) ef3c : ae08 ldx #$08 ; load x-register with value #$08 ef3e : e6b7 lda $b7,x ; load accu with value at addr. $b7+hx ef40 : e7cc sta $cc,x ; store accumulator at address $cc+hx ef42 : 5bfa dbnzx $ef3e ; decrement x-reg. and branch if not zero ef44 : 17e3 bclr #3,$e3 ; clear bit #3 at memory address $e3 ef46 : 8c clrh ; clear h-register (index register high) ef47 : 4f clra ; clear accumulator ef48 : bedf ldx $df ; load x-register with value at address $df ef4a : a3d5 cmpx #$d5 ; compare x-register with value #$d5 ef4c : 2392 bls $eee0 ; branch if reg. is lower or same (unsigned) ef4e : 5a decx ; decrement x-register ef4f : 5a decx ; decrement x-register ef50 : bfdf stx $df ; store x-register at address $df ef52 : 7ee0 mov x+,$e0 ; copy from loc. addr. by hx to addr. $e0 ef54 : 7ee1 mov x+,$e1 ; copy from loc. addr. by hx to addr. $e1 ef56 : 81 rts ; return from subroutine ef57 : 55e0 ldhx $e0 ; load index register (hx) from address $e0 ef59 : af02 aix #$02 ; add signed value to index register (hx) ef5b : 35e0 sthx $e0 ; store index register (hx) to address $e0 ef5d : adb7 bsr $ef16 ; branch to subroutine at address $ef16 ef5f : 55e0 ldhx $e0 ; load index register (hx) from address $e0 ef61 : affe aix #$fe ; add signed value to index register (hx) ef63 : 20c2 bra $ef27 ; branch always to $ef27 ef65 : cdf2ed jsr $f2ed ; jump to subroutine at address $f2ed ef68 : 5f clrx ; clear x-register (index register low) ef69 : 2005 bra $ef70 ; branch always to $ef70 ef6b : cdf2ed jsr $f2ed ; jump to subroutine at address $f2ed ef6e : ad6d bsr $efdd ; branch to subroutine at address $efdd ef70 : f7 sta ,x ; store accumulator at address in hx ef71 : a6ff lda #$ff ; load accumulator with value #$ff ef73 : e704 sta $04,x ; store accumulator at address $04+hx ef75 : 81 rts ; return from subroutine ef76 : b600 lda $00 ; load accu with value at address $00 ef78 : 2003 bra $ef7d ; branch always to $ef7d ef7a : ad61 bsr $efdd ; branch to subroutine at address $efdd ef7c : f6 lda ,x ; load accu with value at address in hx ef7d : ccf0ce jmp $f0ce ; unconditional jump to address $f0ce ef80 : cdf2ed jsr $f2ed ; jump to subroutine at address $f2ed ef83 : 27a0 beq $ef25 ; branch if equal (Z is set) ef85 : 55e0 ldhx $e0 ; load index register (hx) from address $e0 ef87 : e601 lda $01,x ; load accu with value at addr. $01+hx ef89 : af02 aix #$02 ; add signed value to index register (hx) ef8b : 35e0 sthx $e0 ; store index register (hx) to address $e0 ef8d : defffe ldx $fffe,x ; load x-register with value at $fffe+hx ef90 : 8c clrh ; clear h-register (index register high) ef91 : 81 rts ; return from subroutine ef92 : cdf2ed jsr $f2ed ; jump to subroutine at address $f2ed ef95 : b7e5 sta $e5 ; store accumulator at address $e5 ef97 : 0a204e brset #5,$20,$efe8 ; branch to $efe8 if bit #5 at $20 is set ef9a : 97 tax ; transfer accumulator to x-register ef9b : 2708 beq $efa5 ; branch if equal (Z is set) ef9d : b6cb lda $cb ; load accu with value at address $cb ef9f : 8f wait ; enable interrupts & stop processor efa0 : 31cbfc cbeq $cb,$ef9f ; compare accu with $cb and branch if equal efa3 : 5bf8 dbnzx $ef9d ; decrement x-reg. and branch if not zero efa5 : 81 rts ; return from subroutine efa6 : cdf2ed jsr $f2ed ; jump to subroutine at address $f2ed efa9 : b7e5 sta $e5 ; store accumulator at address $e5 efab : ad6a bsr $f017 ; branch to subroutine at address $f017 efad : 87 pusha ; push accu onto stack efae : 3de5 tst $e5 ; test for negative or zero at address $e5 efb0 : 2703 beq $efb5 ; branch if equal (Z is set) efb2 : fa ora ,x ; OR accu with value at address in hx efb3 : 2002 bra $efb7 ; branch always to $efb7 efb5 : 43 coma ; inverse accumulator (one's complement) efb6 : f4 and ,x ; AND accu with value at address in hx efb7 : f7 sta ,x ; store accumulator at address in hx efb8 : 86 popa ; pop (pull) accu from stack efb9 : ea04 ora $04,x ; OR accu with value at addr. $04+hx efbb : e704 sta $04,x ; store accumulator at address $04+hx efbd : 81 rts ; return from subroutine efbe : 87 pusha ; push accu onto stack efbf : f8 eor ,x ; XOR accu with value at address in hx efc0 : 20f5 bra $efb7 ; branch always to $efb7 efc2 : adc1 bsr $ef85 ; branch to subroutine at address $ef85 efc4 : 5b03 dbnzx $efc9 ; decrement x-reg. and branch if not zero efc6 : aefc ldx #$fc ; load x-register with value #$fc efc8 : 4a deca ; decrement accumulator efc9 : 5c incx ; increment x-register efca : 87 pusha ; push accu onto stack efcb : 89 pushx ; push x-register onto stack efcc : bed4 ldx $d4 ; load x-register with value at address $d4 efce : f6 lda ,x ; load accu with value at address in hx efcf : aee6 ldx #$e6 ; load x-register with value #$e6 efd1 : 4d tsta ; test accumulator for negative or zero efd2 : 81 rts ; return from subroutine efd3 : ad42 bsr $f017 ; branch to subroutine at address $f017 efd5 : f4 and ,x ; AND accu with value at address in hx efd6 : 2678 bne $f050 ; branch if not equal (Z is clear) efd8 : 4f clra ; clear accumulator efd9 : ccf0ce jmp $f0ce ; unconditional jump to address $f0ce efdc : 8e stop ; enable ext. interrupt pin, stop oscillator efdd : 55e0 ldhx $e0 ; load index register (hx) from address $e0 efdf : fe ldx ,x ; load x-register with value at addr. in hx efe0 : 8c clrh ; clear h-register (index register high) efe1 : 3ce1 inc $e1 ; increment at address $e1 efe3 : 2602 bne $efe7 ; branch if not equal (Z is clear) efe5 : 3ce0 inc $e0 ; increment at address $e0 efe7 : 81 rts ; return from subroutine efe8 : 55e0 ldhx $e0 ; load index register (hx) from address $e0 efea : af0b aix #$0b ; add signed value to index register (hx) efec : ad9d bsr $ef8b ; branch to subroutine at address $ef8b efee : ad29 bsr $f019 ; branch to subroutine at address $f019 eff0 : 9b sei ; set interrupt mask (disable interrupts) eff1 : 1820 bset #4,$20 ; set bit #4 at memory address $20 eff3 : 1b20 bclr #5,$20 ; clear bit #5 at memory address $20 eff5 : 3de5 tst $e5 ; test for negative or zero at address $e5 eff7 : 271a beq $f013 ; branch if equal (Z is set) eff9 : 87 pusha ; push accu onto stack effa : 89 pushx ; push x-register onto stack effb : adc1 bsr $efbe ; branch to subroutine at address $efbe effd : b6cb lda $cb ; load accu with value at address $cb efff : 4c inca ; increment accumulator f000 : 97 tax ; transfer accumulator to x-register f001 : 5bfe dbnzx $f001 ; decrement x-reg. and branch if not zero f003 : 4bfb dbnza $f000 ; decrement accu and branch if not zero f005 : 88 popx ; pop (pull) x-register from stack f006 : 86 popa ; pop (pull) accu from stack f007 : 0e2004 brset #7,$20,$f00e ; branch to $f00e if bit #7 at $20 is set f00a : 62 nsa ; swap nibbles in accumulator ($af -> $fa) f00b : 62 nsa ; swap nibbles in accumulator ($af -> $fa) f00c : 20eb bra $eff9 ; branch always to $eff9 f00e : 1f20 bclr #7,$20 ; clear bit #7 at memory address $20 f010 : 3be5e6 dbnz $e5,$eff9 ; decrement at $e5 and branch if not zero f013 : 3fcb clr $cb ; clear memory at address $cb f015 : 9a cli ; clear interrupt mask (enable interrupts) f016 : 81 rts ; return from subroutine f017 : adc4 bsr $efdd ; branch to subroutine at address $efdd f019 : 4f clra ; clear accumulator f01a : 54 lsrx ; logical shift right x-register f01b : 49 rola ; rotate accumulator left through carry f01c : 4c inca ; increment accumulator f01d : 54 lsrx ; logical shift right x-register f01e : 2402 bhs $f022 ; branch if reg. is higher or same (C clear) f020 : 48 lsla ; shift left accumulator f021 : 48 lsla ; shift left accumulator f022 : 54 lsrx ; logical shift right x-register f023 : 2401 bhs $f026 ; branch if reg. is higher or same (C clear) f025 : 62 nsa ; swap nibbles in accumulator ($af -> $fa) f026 : 81 rts ; return from subroutine f027 : 55e0 ldhx $e0 ; load index register (hx) from address $e0 f029 : af01 aix #$01 ; add signed value to index register (hx) f02b : 89 pushx ; push x-register onto stack f02c : 8b pushh ; push h-register onto stack f02d : 8c clrh ; clear h-register (index register high) f02e : 209c bra $efcc ; branch always to $efcc f030 : adab bsr $efdd ; branch to subroutine at address $efdd f032 : 4f clra ; clear accumulator f033 : 54 lsrx ; logical shift right x-register f034 : 2701 beq $f037 ; branch if equal (Z is set) f036 : 4c inca ; increment accumulator f037 : 49 rola ; rotate accumulator left through carry f038 : ae02 ldx #$02 ; load x-register with value #$02 f03a : b73c sta $3c ; store accumulator at address $3c f03c : 0f3cfd brclr #7,$3c,$f03c ; branch to $f03c if bit #7 at $3c is clear f03f : 5bf9 dbnzx $f03a ; decrement x-reg. and branch if not zero f041 : b63e lda $3e ; load accu with value at address $3e f043 : 6e1f3c mov #$1f,$3c ; store value #$1f to address $3c f046 : 200a bra $f052 ; branch always to $f052 f048 : adcd bsr $f017 ; branch to subroutine at address $f017 f04a : af80 aix #$80 ; add signed value to index register (hx) f04c : 8c clrh ; clear h-register (index register high) f04d : f4 and ,x ; AND accu with value at address in hx f04e : 277e beq $f0ce ; branch if equal (Z is set) f050 : a6ff lda #$ff ; load accumulator with value #$ff f052 : 207a bra $f0ce ; branch always to $f0ce f054 : ad87 bsr $efdd ; branch to subroutine at address $efdd f056 : 9f txa ; transfer x-register to accumulator f057 : a319 cmpx #$19 ; compare x-register with value #$19 f059 : 250c blo $f067 ; branch if reg. is lower (C is set) f05b : 5d tstx ; test x-register for negative or zero f05c : 2a03 bpl $f061 ; branch if plus (N bit is clear) f05e : af80 aix #$80 ; add signed value to index register (hx) f060 : 81 rts ; return from subroutine f061 : adb6 bsr $f019 ; branch to subroutine at address $f019 f063 : afda aix #$da ; add signed value to index register (hx) f065 : 8c clrh ; clear h-register (index register high) f066 : 81 rts ; return from subroutine f067 : def257 ldx $f257,x ; load x-register with value at $f257+hx f06a : 81 rts ; return from subroutine f06b : cdf2ed jsr $f2ed ; jump to subroutine at address $f2ed f06e : b7e5 sta $e5 ; store accumulator at address $e5 f070 : ada5 bsr $f017 ; branch to subroutine at address $f017 f072 : af80 aix #$80 ; add signed value to index register (hx) f074 : 8c clrh ; clear h-register (index register high) f075 : 3de5 tst $e5 ; test for negative or zero at address $e5 f077 : 2703 beq $f07c ; branch if equal (Z is set) f079 : fa ora ,x ; OR accu with value at address in hx f07a : f7 sta ,x ; store accumulator at address in hx f07b : 81 rts ; return from subroutine f07c : 43 coma ; inverse accumulator (one's complement) f07d : f4 and ,x ; AND accu with value at address in hx f07e : f7 sta ,x ; store accumulator at address in hx f07f : 81 rts ; return from subroutine f080 : 8c clrh ; clear h-register (index register high) f081 : bed4 ldx $d4 ; load x-register with value at address $d4 f083 : 7ee5 mov x+,$e5 ; copy from loc. addr. by hx to addr. $e5 f085 : cdef85 jsr $ef85 ; jump to subroutine at address $ef85 f088 : bbe5 add $e5 ; add value at address $e5 to accumulator f08a : 2401 bhs $f08d ; branch if reg. is higher or same (C clear) f08c : 5c incx ; increment x-register f08d : 89 pushx ; push x-register onto stack f08e : 8a poph ; pop (pull) h-register from stack f08f : 97 tax ; transfer accumulator to x-register f090 : d6f47e lda $f47e,x ; load accu with value at address $f47e+hx f093 : 8c clrh ; clear h-register (index register high) f094 : bed4 ldx $d4 ; load x-register with value at address $d4 f096 : f7 sta ,x ; store accumulator at address in hx f097 : 81 rts ; return from subroutine f098 : cdf2ed jsr $f2ed ; jump to subroutine at address $f2ed f09b : b7e5 sta $e5 ; store accumulator at address $e5 f09d : adb5 bsr $f054 ; branch to subroutine at address $f054 f09f : 27d4 beq $f075 ; branch if equal (Z is set) f0a1 : a108 cmp #$08 ; compare accumulator with value #$08 f0a3 : 2602 bne $f0a7 ; branch if not equal (Z is clear) f0a5 : 1a20 bset #5,$20 ; set bit #5 at memory address $20 f0a7 : 410909 cbeqa #$09,$f0b3 ; compare accu with value & branch if equal f0aa : a106 cmp #$06 ; compare accumulator with value #$06 f0ac : 2602 bne $f0b0 ; branch if not equal (Z is clear) f0ae : 3fcb clr $cb ; clear memory at address $cb f0b0 : 5ee5 mov $e5,x+ ; copy from $e5 to loc. addr. by hx & inc hx f0b2 : 81 rts ; return from subroutine f0b3 : b6e5 lda $e5 ; load accu with value at address $e5 f0b5 : 2705 beq $f0bc ; branch if equal (Z is set) f0b7 : b7c7 sta $c7 ; store accumulator at address $c7 f0b9 : 141b bset #2,$1b ; set bit #2 at memory address $1b f0bb : 81 rts ; return from subroutine f0bc : 151b bclr #2,$1b ; clear bit #2 at memory address $1b f0be : 81 rts ; return from subroutine f0bf : ad93 bsr $f054 ; branch to subroutine at address $f054 f0c1 : 278a beq $f04d ; branch if equal (Z is set) f0c3 : 410b7c cbeqa #$0b,$f142 ; compare accu with value & branch if equal f0c6 : f6 lda ,x ; load accu with value at address in hx f0c7 : 2005 bra $f0ce ; branch always to $f0ce f0c9 : cdefdd jsr $efdd ; jump to subroutine at address $efdd f0cc : e680 lda $80,x ; load accu with value at addr. $80+hx f0ce : 8c clrh ; clear h-register (index register high) f0cf : bed4 ldx $d4 ; load x-register with value at address $d4 f0d1 : a3cd cmpx #$cd ; compare x-register with value #$cd f0d3 : 2202 bhi $f0d7 ; branch if reg. is higher (unsigned) f0d5 : aed4 ldx #$d4 ; load x-register with value #$d4 f0d7 : 5a decx ; decrement x-register f0d8 : bfd4 stx $d4 ; store x-register at address $d4 f0da : f7 sta ,x ; store accumulator at address in hx f0db : 81 rts ; return from subroutine f0dc : 55e0 ldhx $e0 ; load index register (hx) from address $e0 f0de : e601 lda $01,x ; load accu with value at addr. $01+hx f0e0 : af02 aix #$02 ; add signed value to index register (hx) f0e2 : 35e0 sthx $e0 ; store index register (hx) to address $e0 f0e4 : 20e8 bra $f0ce ; branch always to $f0ce f0e6 : cdf2ed jsr $f2ed ; jump to subroutine at address $f2ed f0e9 : cdefdd jsr $efdd ; jump to subroutine at address $efdd f0ec : e780 sta $80,x ; store accumulator at address $80+hx f0ee : 81 rts ; return from subroutine f0ef : 9f txa ; transfer x-register to accumulator f0f0 : ad5e bsr $f150 ; branch to subroutine at address $f150 f0f2 : cdefdd jsr $efdd ; jump to subroutine at address $efdd f0f5 : 5d tstx ; test x-register for negative or zero f0f6 : 26f7 bne $f0ef ; branch if not equal (Z is clear) f0f8 : 81 rts ; return from subroutine f0f9 : cdf2ed jsr $f2ed ; jump to subroutine at address $f2ed f0fc : 97 tax ; transfer accumulator to x-register f0fd : 01e410 brclr #0,$e4,$f110 ; branch to $f110 if bit #0 at $e4 is clear f100 : 62 nsa ; swap nibbles in accumulator ($af -> $fa) f101 : ad01 bsr $f104 ; branch to subroutine at address $f104 f103 : 9f txa ; transfer x-register to accumulator f104 : a40f and #$0f ; AND accu with value #$0f f106 : aa30 ora #$30 ; OR accumulator with value #$30 f108 : a139 cmp #$39 ; compare accumulator with value #$39 f10a : 2344 bls $f150 ; branch if reg. is lower or same (unsigned) f10c : ab27 add #$27 ; add value #$27 to accumulator f10e : 2040 bra $f150 ; branch always to $f150 f110 : 03e405 brclr #1,$e4,$f118 ; branch to $f118 if bit #1 at $e4 is clear f113 : a620 lda #$20 ; load accumulator with value #$20 f115 : ad39 bsr $f150 ; branch to subroutine at address $f150 f117 : 9f txa ; transfer x-register to accumulator f118 : ae0a ldx #$0a ; load x-register with value #$0a f11a : 3fe5 clr $e5 ; clear memory at address $e5 f11c : 3ce5 inc $e5 ; increment at address $e5 f11e : 8c clrh ; clear h-register (index register high) f11f : 52 div ; divide h:a by x, a=result, h=remainder f120 : 8b pushh ; push h-register onto stack f121 : 26f9 bne $f11c ; branch if not equal (Z is clear) f123 : 86 popa ; pop (pull) accu from stack f124 : ab30 add #$30 ; add value #$30 to accumulator f126 : ad7a bsr $f1a2 ; branch to subroutine at address $f1a2 f128 : 9a cli ; clear interrupt mask (enable interrupts) f129 : 3be5f7 dbnz $e5,$f123 ; decrement at $e5 and branch if not zero f12c : 81 rts ; return from subroutine f12d : cdf30b jsr $f30b ; jump to subroutine at address $f30b f130 : cdefdd jsr $efdd ; jump to subroutine at address $efdd f133 : 5d tstx ; test x-register for negative or zero f134 : 97 tax ; transfer accumulator to x-register f135 : 2795 beq $f0cc ; branch if equal (Z is set) f137 : 89 pushx ; push x-register onto stack f138 : cdf2ed jsr $f2ed ; jump to subroutine at address $f2ed f13b : 88 popx ; pop (pull) x-register from stack f13c : 20ae bra $f0ec ; branch always to $f0ec f13e : a601 lda #$01 ; load accumulator with value #$01 f140 : 208c bra $f0ce ; branch always to $f0ce f142 : 45fd00 ldhx #$fd00 ; load index register (hx) with value f145 : cdf32e jsr $f32e ; jump to subroutine at address $f32e f148 : 8c clrh ; clear h-register (index register high) f149 : bed4 ldx $d4 ; load x-register with value at address $d4 f14b : 73 com ,x ; inverse memory at hx (one's comp.) f14c : 81 rts ; return from subroutine f14d : cdf2ed jsr $f2ed ; jump to subroutine at address $f2ed f150 : ad50 bsr $f1a2 ; branch to subroutine at address $f1a2 f152 : 9a cli ; clear interrupt mask (enable interrupts) f153 : 81 rts ; return from subroutine f154 : ad54 bsr $f1aa ; branch to subroutine at address $f1aa f156 : 9a cli ; clear interrupt mask (enable interrupts) f157 : 20e7 bra $f140 ; branch always to $f140 f159 : 4f clra ; clear accumulator f15a : 97 tax ; transfer accumulator to x-register f15b : ad4d bsr $f1aa ; branch to subroutine at address $f1aa f15d : 9a cli ; clear interrupt mask (enable interrupts) f15e : a030 sub #$30 ; subtract value #$30 from accumulator f160 : b7e5 sta $e5 ; store accumulator at address $e5 f162 : 9f txa ; transfer x-register to accumulator f163 : 25db blo $f140 ; branch if reg. is lower (C is set) f165 : ae0a ldx #$0a ; load x-register with value #$0a f167 : 42 mul ; unsigned multiply of accu & x-register f168 : bbe5 add $e5 ; add value at address $e5 to accumulator f16a : 20ee bra $f15a ; branch always to $f15a f16c : a60d lda #$0d ; load accumulator with value #$0d f16e : ade0 bsr $f150 ; branch to subroutine at address $f150 f170 : a60a lda #$0a ; load accumulator with value #$0a f172 : 20dc bra $f150 ; branch always to $f150 f174 : ccf050 jmp $f050 ; unconditional jump to address $f050 f177 : cdf2de jsr $f2de ; jump to subroutine at address $f2de f17a : f4 and ,x ; AND accu with value at address in hx f17b : 81 rts ; return from subroutine f17c : cdf2de jsr $f2de ; jump to subroutine at address $f2de f17f : f4 and ,x ; AND accu with value at address in hx f180 : 43 coma ; inverse accumulator (one's complement) f181 : 81 rts ; return from subroutine f182 : cdf2de jsr $f2de ; jump to subroutine at address $f2de f185 : fa ora ,x ; OR accu with value at address in hx f186 : 81 rts ; return from subroutine f187 : cdf2de jsr $f2de ; jump to subroutine at address $f2de f18a : f8 eor ,x ; XOR accu with value at address in hx f18b : 81 rts ; return from subroutine f18c : cdf2de jsr $f2de ; jump to subroutine at address $f2de f18f : fe ldx ,x ; load x-register with value at addr. in hx f190 : 2703 beq $f195 ; branch if equal (Z is set) f192 : 48 lsla ; shift left accumulator f193 : 5bfd dbnzx $f192 ; decrement x-reg. and branch if not zero f195 : 81 rts ; return from subroutine f196 : cdf2de jsr $f2de ; jump to subroutine at address $f2de f199 : fe ldx ,x ; load x-register with value at addr. in hx f19a : 2703 beq $f19f ; branch if equal (Z is set) f19c : 44 lsra ; logical shift right accumulator f19d : 5bfd dbnzx $f19c ; decrement x-reg. and branch if not zero f19f : 81 rts ; return from subroutine f1a0 : ad08 bsr $f1aa ; branch to subroutine at address $f1aa f1a2 : 1104 bclr #0,$04 ; clear bit #0 at memory address $04 f1a4 : 9b sei ; set interrupt mask (disable interrupts) f1a5 : 1100 bclr #0,$00 ; clear bit #0 at memory address $00 f1a7 : cc2d7d jmp $2d7d ; unconditional jump to address $2d7d f1aa : 1104 bclr #0,$04 ; clear bit #0 at memory address $04 f1ac : a680 lda #$80 ; load accumulator with value #$80 f1ae : 9b sei ; set interrupt mask (disable interrupts) f1af : 0000fd brset #0,$00,$f1af ; branch to $f1af if bit #0 at $00 is set f1b2 : cd2da1 jsr $2da1 ; jump to subroutine at address $2da1 f1b5 : 25f8 blo $f1af ; branch if reg. is lower (C is set) f1b7 : cd2da1 jsr $2da1 ; jump to subroutine at address $2da1 f1ba : 46 rora ; rotate accumulator right through carry f1bb : 24fa bhs $f1b7 ; branch if reg. is higher or same (C clear) f1bd : 0100fd brclr #0,$00,$f1bd ; branch to $f1bd if bit #0 at $00 is clear f1c0 : 81 rts ; return from subroutine f1c1 : cdf2de jsr $f2de ; jump to subroutine at address $f2de f1c4 : fa ora ,x ; OR accu with value at address in hx f1c5 : 43 coma ; inverse accumulator (one's complement) f1c6 : 81 rts ; return from subroutine f1c7 : add7 bsr $f1a0 ; branch to subroutine at address $f1a0 f1c9 : 97 tax ; transfer accumulator to x-register f1ca : adde bsr $f1aa ; branch to subroutine at address $f1aa f1cc : 87 pusha ; push accu onto stack f1cd : ab82 add #$82 ; add value #$82 to accumulator f1cf : b7c3 sta $c3 ; store accumulator at address $c3 f1d1 : 9f txa ; transfer x-register to accumulator f1d2 : a9f4 adc #$f4 ; add with carry value #$f4 to accu f1d4 : b7c2 sta $c2 ; store accumulator at address $c2 f1d6 : 55c2 ldhx $c2 ; load index register (hx) from address $c2 f1d8 : af3f aix #$3f ; add signed value to index register (hx) f1da : 9f txa ; transfer x-register to accumulator f1db : a4c0 and #$c0 ; AND accu with value #$c0 f1dd : 97 tax ; transfer accumulator to x-register f1de : 89 pushx ; push x-register onto stack f1df : 8b pushh ; push h-register onto stack f1e0 : ad55 bsr $f237 ; branch to subroutine at address $f237 f1e2 : 45f480 ldhx #$f480 ; load index register (hx) with value f1e5 : 86 popa ; pop (pull) accu from stack f1e6 : ad3b bsr $f223 ; branch to subroutine at address $f223 f1e8 : 86 popa ; pop (pull) accu from stack f1e9 : ad38 bsr $f223 ; branch to subroutine at address $f223 f1eb : 86 popa ; pop (pull) accu from stack f1ec : adb4 bsr $f1a2 ; branch to subroutine at address $f1a2 f1ee : adba bsr $f1aa ; branch to subroutine at address $f1aa f1f0 : ad31 bsr $f223 ; branch to subroutine at address $f223 f1f2 : adae bsr $f1a2 ; branch to subroutine at address $f1a2 f1f4 : 75c2 cmphx $c2 ; compare index reg. (hx) with value at $c2 f1f6 : 26f6 bne $f1ee ; branch if not equal (Z is clear) f1f8 : 1900 bclr #4,$00 ; clear bit #4 at memory address $00 f1fa : 1804 bset #4,$04 ; set bit #4 at memory address $04 f1fc : adac bsr $f1aa ; branch to subroutine at address $f1aa f1fe : 1904 bclr #4,$04 ; clear bit #4 at memory address $04 f200 : 27fb beq $f1fd ; branch if equal (Z is set) f202 : 4102c2 cbeqa #$02,$f1c7 ; compare accu with value & branch if equal f205 : 41080e cbeqa #$08,$f216 ; compare accu with value & branch if equal f208 : 410dbb cbeqa #$0d,$f1c6 ; compare accu with value & branch if equal f20b : 45ffe2 ldhx #$ffe2 ; load index register (hx) with value f20e : f6 lda ,x ; load accu with value at address in hx f20f : 27e7 beq $f1f8 ; branch if equal (Z is set) f211 : ad8f bsr $f1a2 ; branch to subroutine at address $f1a2 f213 : 5c incx ; increment x-register f214 : 20f8 bra $f20e ; branch always to $f20e f216 : 45fd00 ldhx #$fd00 ; load index register (hx) with value f219 : ad8f bsr $f1aa ; branch to subroutine at address $f1aa f21b : 97 tax ; transfer accumulator to x-register f21c : 35c2 sthx $c2 ; store index register (hx) to address $c2 f21e : 5f clrx ; clear x-register (index register low) f21f : 20cb bra $f1ec ; branch always to $f1ec f221 : 55c2 ldhx $c2 ; load index register (hx) from address $c2 f223 : 65f480 cmphx #$f480 ; compare index register (hx) with value f226 : 259d blo $f1c5 ; branch if reg. is lower (C is set) f228 : 65fe00 cmphx #$fe00 ; compare index register (hx) with value f22b : 2498 bhs $f1c5 ; branch if reg. is higher or same (C clear) f22d : ad13 bsr $f242 ; branch to subroutine at address $f242 f22f : 558a ldhx $8a ; load index register (hx) from address $8a f231 : f6 lda ,x ; load accu with value at address in hx f232 : af01 aix #$01 ; add signed value to index register (hx) f234 : 81 rts ; return from subroutine f235 : 55c2 ldhx $c2 ; load index register (hx) from address $c2 f237 : 358c sthx $8c ; store index register (hx) to address $8c f239 : 45ffbd ldhx #$ffbd ; load index register (hx) with value f23c : 358a sthx $8a ; store index register (hx) to address $8a f23e : 5a decx ; decrement x-register f23f : 4f clra ; clear accumulator f240 : 2005 bra $f247 ; branch always to $f247 f242 : b78c sta $8c ; store accumulator at address $8c f244 : 358a sthx $8a ; store index register (hx) to address $8a f246 : 9f txa ; transfer x-register to accumulator f247 : 9b sei ; set interrupt mask (disable interrupts) f248 : 6e0c89 mov #$0c,$89 ; store value #$0c to address $89 f24b : a43f and #$3f ; AND accu with value #$3f f24d : 2605 bne $f254 ; branch if not equal (Z is clear) f24f : 3f88 clr $88 ; clear memory at address $88 f251 : cd2806 jsr $2806 ; jump to subroutine at address $2806 f254 : cc2809 jmp $2809 ; unconditional jump to address $2809 ; --------------------------------------------------------------------------- ; DATA - Tabelle mit den Adressen der internen Variablen ; --------------------------------------------------------------------------- f250 : c5 c6 c7 c4 c8 c9 ca cb cb +ãÃ-++--- f260 : c4 38 1f 1e cc cc c3 e1 c1 d4 df e6 e7 e8 e9 ea -8..¦¦+ß-È_µþÞÚÛ ; --------------------------------------------------------------------------- ; CODE ; --------------------------------------------------------------------------- f270 : b6cc lda $cc ; load accu with value at address $cc f272 : ae15 ldx #$15 ; load x-register with value #$15 f274 : 42 mul ; unsigned multiply of accu & x-register f275 : 4c inca ; increment accumulator f276 : b7cc sta $cc ; store accumulator at address $cc f278 : ccf0ce jmp $f0ce ; unconditional jump to address $f0ce ; --------------------------------------------------------------------------- ; DATA - Tabelle mit den Firmwareroutinen-Adressen ; --------------------------------------------------------------------------- f270 : f3 0b f1 a2 f1 ¾.±ó± f280 : aa ee f9 f2 23 f0 56 ¬¯¨_#-V¡ ; --------------------------------------------------------------------------- ; CODE ; --------------------------------------------------------------------------- f287 : ad64 bsr $f2ed ; branch to subroutine at address $f2ed f289 : b7cc sta $cc ; store accumulator at address $cc f28b : 81 rts ; return from subroutine f28c : 8c clrh ; clear h-register (index register high) f28d : bed4 ldx $d4 ; load x-register with value at address $d4 f28f : 70 neg ,x ; negate memory at hx (two's complement) f290 : 81 rts ; return from subroutine f291 : ad4b bsr $f2de ; branch to subroutine at address $f2de f293 : fb add ,x ; add value at address hx to accumulator f294 : 81 rts ; return from subroutine f295 : ad47 bsr $f2de ; branch to subroutine at address $f2de f297 : f0 sub ,x ; subtract value at address in hx from accu f298 : 81 rts ; return from subroutine f299 : ad43 bsr $f2de ; branch to subroutine at address $f2de f29b : fe ldx ,x ; load x-register with value at addr. in hx f29c : 42 mul ; unsigned multiply of accu & x-register f29d : 81 rts ; return from subroutine f29e : ad3e bsr $f2de ; branch to subroutine at address $f2de f2a0 : fe ldx ,x ; load x-register with value at addr. in hx f2a1 : 52 div ; divide h:a by x, a=result, h=remainder f2a2 : 8c clrh ; clear h-register (index register high) f2a3 : 81 rts ; return from subroutine f2a4 : ad38 bsr $f2de ; branch to subroutine at address $f2de f2a6 : fe ldx ,x ; load x-register with value at addr. in hx f2a7 : 52 div ; divide h:a by x, a=result, h=remainder f2a8 : 8b pushh ; push h-register onto stack f2a9 : 86 popa ; pop (pull) accu from stack f2aa : 8c clrh ; clear h-register (index register high) f2ab : 81 rts ; return from subroutine f2ac : ad42 bsr $f2f0 ; branch to subroutine at address $f2f0 f2ae : 2707 beq $f2b7 ; branch if equal (Z is set) f2b0 : 4a deca ; decrement accumulator f2b1 : 44 lsra ; logical shift right accumulator f2b2 : 7f clr ,x ; clear memory at index register (hx) f2b3 : 7c inc ,x ; increment memory at index register (hx) f2b4 : f0 sub ,x ; subtract value at address in hx from accu f2b5 : 24fc bhs $f2b3 ; branch if reg. is higher or same (C clear) f2b7 : 81 rts ; return from subroutine f2b8 : ad24 bsr $f2de ; branch to subroutine at address $f2de f2ba : 2401 bhs $f2bd ; branch if reg. is higher or same (C clear) f2bc : f6 lda ,x ; load accu with value at address in hx f2bd : 81 rts ; return from subroutine f2be : ad1e bsr $f2de ; branch to subroutine at address $f2de f2c0 : 22fa bhi $f2bc ; branch if reg. is higher (unsigned) f2c2 : 81 rts ; return from subroutine f2c3 : ad19 bsr $f2de ; branch to subroutine at address $f2de f2c5 : 2408 bhs $f2cf ; branch if reg. is higher or same (C clear) f2c7 : 4f clra ; clear accumulator f2c8 : 81 rts ; return from subroutine f2c9 : ad13 bsr $f2de ; branch to subroutine at address $f2de f2cb : 2202 bhi $f2cf ; branch if reg. is higher (unsigned) f2cd : 4f clra ; clear accumulator f2ce : 81 rts ; return from subroutine f2cf : a6ff lda #$ff ; load accumulator with value #$ff f2d1 : 81 rts ; return from subroutine f2d2 : ad0a bsr $f2de ; branch to subroutine at address $f2de f2d4 : 25f9 blo $f2cf ; branch if reg. is lower (C is set) f2d6 : 4f clra ; clear accumulator f2d7 : 81 rts ; return from subroutine f2d8 : ad04 bsr $f2de ; branch to subroutine at address $f2de f2da : 23f3 bls $f2cf ; branch if reg. is lower or same (unsigned) f2dc : 4f clra ; clear accumulator f2dd : 81 rts ; return from subroutine f2de : 86 popa ; pop (pull) accu from stack f2df : 88 popx ; pop (pull) x-register from stack f2e0 : ad13 bsr $f2f5 ; branch to subroutine at address $f2f5 f2e2 : bed4 ldx $d4 ; load x-register with value at address $d4 f2e4 : f7 sta ,x ; store accumulator at address in hx f2e5 : 2403 bhs $f2ea ; branch if reg. is higher or same (C clear) f2e7 : 10e3 bset #0,$e3 ; set bit #0 at memory address $e3 f2e9 : 81 rts ; return from subroutine f2ea : 11e3 bclr #0,$e3 ; clear bit #0 at memory address $e3 f2ec : 81 rts ; return from subroutine f2ed : 04e41b brset #2,$e4,$f30b ; branch to $f30b if bit #2 at $e4 is set f2f0 : 8c clrh ; clear h-register (index register high) f2f1 : bed4 ldx $d4 ; load x-register with value at address $d4 f2f3 : f6 lda ,x ; load accu with value at address in hx f2f4 : 81 rts ; return from subroutine f2f5 : 89 pushx ; push x-register onto stack f2f6 : 87 pusha ; push accu onto stack f2f7 : 8c clrh ; clear h-register (index register high) f2f8 : bed4 ldx $d4 ; load x-register with value at address $d4 f2fa : 89 pushx ; push x-register onto stack f2fb : 5c incx ; increment x-register f2fc : a3d4 cmpx #$d4 ; compare x-register with value #$d4 f2fe : 2502 blo $f302 ; branch if reg. is lower (C is set) f300 : aecd ldx #$cd ; load x-register with value #$cd f302 : bfd4 stx $d4 ; store x-register at address $d4 f304 : f6 lda ,x ; load accu with value at address in hx f305 : 88 popx ; pop (pull) x-register from stack f306 : f1 cmp ,x ; compare accu with value at address in hx f307 : 81 rts ; return from subroutine f308 : 04e40e brset #2,$e4,$f319 ; branch to $f319 if bit #2 at $e4 is set f30b : 8c clrh ; clear h-register (index register high) f30c : bed4 ldx $d4 ; load x-register with value at address $d4 f30e : f6 lda ,x ; load accu with value at address in hx f30f : 5c incx ; increment x-register f310 : a3d4 cmpx #$d4 ; compare x-register with value #$d4 f312 : 2502 blo $f316 ; branch if reg. is lower (C is set) f314 : aecd ldx #$cd ; load x-register with value #$cd f316 : bfd4 stx $d4 ; store x-register at address $d4 f318 : 4d tsta ; test accumulator for negative or zero f319 : 81 rts ; return from subroutine f31a : adc2 bsr $f2de ; branch to subroutine at address $f2de f31c : 27b1 beq $f2cf ; branch if equal (Z is set) f31e : 4f clra ; clear accumulator f31f : 81 rts ; return from subroutine f320 : adbc bsr $f2de ; branch to subroutine at address $f2de f322 : 26ab bne $f2cf ; branch if not equal (Z is clear) f324 : 4f clra ; clear accumulator f325 : 81 rts ; return from subroutine f326 : 45ffbc ldhx #$ffbc ; load index register (hx) with value f329 : f6 lda ,x ; load accu with value at address in hx f32a : ee01 ldx $01,x ; load x-register with value at $01+hx f32c : 87 pusha ; push accu onto stack f32d : 8a poph ; pop (pull) h-register from stack f32e : 75c2 cmphx $c2 ; compare index reg. (hx) with value at $c2 f330 : ada8 bsr $f2da ; branch to subroutine at address $f2da f332 : ccf0ce jmp $f0ce ; unconditional jump to address $f0ce f335 : adb6 bsr $f2ed ; branch to subroutine at address $f2ed f337 : ab80 add #$80 ; add value #$80 to accumulator f339 : 20f5 bra $f330 ; branch always to $f330 f33b : adb0 bsr $f2ed ; branch to subroutine at address $f2ed f33d : cdefdd jsr $efdd ; jump to subroutine at address $efdd f340 : eb80 add $80,x ; add value at $80+hx to accumulator f342 : e780 sta $80,x ; store accumulator at address $80+hx f344 : 81 rts ; return from subroutine f345 : adf4 bsr $f33b ; branch to subroutine at address $f33b f347 : ccef25 jmp $ef25 ; unconditional jump to address $ef25 f34a : ada1 bsr $f2ed ; branch to subroutine at address $f2ed f34c : cdefdd jsr $efdd ; jump to subroutine at address $efdd f34f : e180 cmp $80,x ; compare accu with value at addr. $80+hx f351 : 27f4 beq $f347 ; branch if equal (Z is set) f353 : ccef85 jmp $ef85 ; unconditional jump to address $ef85 f356 : 5b15 dbnzx $f36d ; decrement x-reg. and branch if not zero f358 : 18e3 bset #4,$e3 ; set bit #4 at memory address $e3 f35a : 2040 bra $f39c ; branch always to $f39c f35c : 86 popa ; pop (pull) accu from stack f35d : ad8e bsr $f2ed ; branch to subroutine at address $f2ed f35f : cdf221 jsr $f221 ; jump to subroutine at address $f221 f362 : 35c2 sthx $c2 ; store index register (hx) to address $c2 f364 : ae05 ldx #$05 ; load x-register with value #$05 f366 : 318c44 cbeq $8c,$f3ad ; compare accu with $8c and branch if equal f369 : 4f clra ; clear accumulator f36a : cceedf jmp $eedf ; unconditional jump to address $eedf f36d : 5bfa dbnzx $f369 ; decrement x-reg. and branch if not zero f36f : 55c2 ldhx $c2 ; load index register (hx) from address $c2 f371 : f6 lda ,x ; load accu with value at address in hx f372 : af01 aix #$01 ; add signed value to index register (hx) f374 : 35c2 sthx $c2 ; store index register (hx) to address $c2 f376 : 20ba bra $f332 ; branch always to $f332 f378 : c6ffc0 lda $ffc0 ; load accumulator with value at $ffc0 f37b : b738 sta $38 ; store accumulator at address $38 f37d : ad4d bsr $f3cc ; branch to subroutine at address $f3cc f37f : 5a decx ; decrement x-register f380 : 2704 beq $f386 ; branch if equal (Z is set) f382 : 5b0a dbnzx $f38e ; decrement x-reg. and branch if not zero f384 : 18e3 bset #4,$e3 ; set bit #4 at memory address $e3 f386 : 45f480 ldhx #$f480 ; load index register (hx) with value f389 : 7ec2 mov x+,$c2 ; copy from loc. addr. by hx to addr. $c2 f38b : 7ec3 mov x+,$c3 ; copy from loc. addr. by hx to addr. $c3 f38d : 81 rts ; return from subroutine f38e : 5b05 dbnzx $f395 ; decrement x-reg. and branch if not zero f390 : 45ffbc ldhx #$ffbc ; load index register (hx) with value f393 : 20f4 bra $f389 ; branch always to $f389 f395 : 5bbf dbnzx $f356 ; decrement x-reg. and branch if not zero f397 : 09e31a brclr #4,$e3,$f3b4 ; branch to $f3b4 if bit #4 at $e3 is clear f39a : 19e3 bclr #4,$e3 ; clear bit #4 at memory address $e3 f39c : 9b sei ; set interrupt mask (disable interrupts) f39d : ae7a ldx #$7a ; load x-register with value #$7a f39f : e60e lda $0e,x ; load accu with value at addr. $0e+hx f3a1 : 87 pusha ; push accu onto stack f3a2 : 5c incx ; increment x-register f3a3 : 2afa bpl $f39f ; branch if plus (N bit is clear) f3a5 : 08e3b4 brset #4,$e3,$f35c ; branch to $f35c if bit #4 at $e3 is set f3a8 : cdf235 jsr $f235 ; jump to subroutine at address $f235 f3ab : ae06 ldx #$06 ; load x-register with value #$06 f3ad : 8c clrh ; clear h-register (index register high) f3ae : 86 popa ; pop (pull) accu from stack f3af : e787 sta $87,x ; store accumulator at address $87+hx f3b1 : 5bfb dbnzx $f3ae ; decrement x-reg. and branch if not zero f3b3 : 9a cli ; clear interrupt mask (enable interrupts) f3b4 : 81 rts ; return from subroutine f3b5 : 3bcc8f dbnz $cc,$f347 ; decrement at $cc and branch if not zero f3b8 : 2099 bra $f353 ; branch always to $f353 f3ba : ad97 bsr $f353 ; branch to subroutine at address $f353 f3bc : bfc0 stx $c0 ; store x-register at address $c0 f3be : b7c1 sta $c1 ; store accumulator at address $c1 f3c0 : bac0 ora $c0 ; OR accu with value at address $c0 f3c2 : 2703 beq $f3c7 ; branch if equal (Z is set) f3c4 : 14e3 bset #2,$e3 ; set bit #2 at memory address $e3 f3c6 : 81 rts ; return from subroutine f3c7 : 15e3 bclr #2,$e3 ; clear bit #2 at memory address $e3 f3c9 : 81 rts ; return from subroutine f3ca : 1104 bclr #0,$04 ; clear bit #0 at memory address $04 f3cc : ccefdd jmp $efdd ; unconditional jump to address $efdd f3cf : a601 lda #$01 ; load accumulator with value #$01 f3d1 : 8b pushh ; push h-register onto stack f3d2 : ad57 bsr $f42b ; branch to subroutine at address $f42b f3d4 : 2502 blo $f3d8 ; branch if reg. is lower (C is set) f3d6 : 12e3 bset #1,$e3 ; set bit #1 at memory address $e3 f3d8 : 8a poph ; pop (pull) h-register from stack f3d9 : 80 rti ; return from interrupt f3da : 1f25 bclr #7,$25 ; clear bit #7 at memory address $25 f3dc : a604 lda #$04 ; load accumulator with value #$04 f3de : 2004 bra $f3e4 ; branch always to $f3e4 f3e0 : 1f28 bclr #7,$28 ; clear bit #7 at memory address $28 f3e2 : a608 lda #$08 ; load accumulator with value #$08 f3e4 : 8b pushh ; push h-register onto stack f3e5 : ad44 bsr $f42b ; branch to subroutine at address $f42b f3e7 : 8a poph ; pop (pull) h-register from stack f3e8 : 80 rti ; return from interrupt f3e9 : 1f20 bclr #7,$20 ; clear bit #7 at memory address $20 f3eb : a610 lda #$10 ; load accumulator with value #$10 f3ed : 8b pushh ; push h-register onto stack f3ee : ad3b bsr $f42b ; branch to subroutine at address $f42b f3f0 : 25e6 blo $f3d8 ; branch if reg. is lower (C is set) f3f2 : 8c clrh ; clear h-register (index register high) f3f3 : aecb ldx #$cb ; load x-register with value #$cb f3f5 : 7c inc ,x ; increment memory at index register (hx) f3f6 : 06e40e brset #3,$e4,$f407 ; branch to $f407 if bit #3 at $e4 is set f3f9 : a632 lda #$32 ; load accumulator with value #$32 f3fb : f1 cmp ,x ; compare accu with value at address in hx f3fc : 2609 bne $f407 ; branch if not equal (Z is clear) f3fe : 7f clr ,x ; clear memory at index register (hx) f3ff : 5a decx ; decrement x-register f400 : 7c inc ,x ; increment memory at index register (hx) f401 : a63c lda #$3c ; load accumulator with value #$3c f403 : a3c8 cmpx #$c8 ; compare x-register with value #$c8 f405 : 22f4 bhi $f3fb ; branch if reg. is higher (unsigned) f407 : 051b09 brclr #2,$1b,$f413 ; branch to $f413 if bit #2 at $1b is clear f40a : 3bc506 dbnz $c5,$f413 ; decrement at $c5 and branch if not zero f40d : 55c6 ldhx $c6 ; load index register (hx) from address $c6 f40f : 35c4 sthx $c4 ; store index register (hx) to address $c4 f411 : 3fc6 clr $c6 ; clear memory at address $c6 f413 : 8a poph ; pop (pull) h-register from stack f414 : 80 rti ; return from interrupt f415 : a620 lda #$20 ; load accumulator with value #$20 f417 : 8b pushh ; push h-register onto stack f418 : ad11 bsr $f42b ; branch to subroutine at address $f42b f41a : 2505 blo $f421 ; branch if reg. is lower (C is set) f41c : 0c0002 brset #6,$00,$f421 ; branch to $f421 if bit #6 at $00 is set f41f : 3cc6 inc $c6 ; increment at address $c6 f421 : 141a bset #2,$1a ; set bit #2 at memory address $1a f423 : 8a poph ; pop (pull) h-register from stack f424 : 80 rti ; return from interrupt f425 : b63e lda $3e ; load accu with value at address $3e f427 : a640 lda #$40 ; load accumulator with value #$40 f429 : 20b9 bra $f3e4 ; branch always to $f3e4 f42b : 98 clc ; clear carry bit f42c : b4e2 and $e2 ; AND accu with value at address $e2 f42e : 273c beq $f46c ; branch if equal (Z is set) f430 : 8c clrh ; clear h-register (index register high) f431 : aee6 ldx #$e6 ; load x-register with value #$e6 f433 : 0ae351 brset #5,$e3,$f487 ; branch to $f487 if bit #5 at $e3 is set f436 : ccfd00 jmp $fd00 ; unconditional jump to address $fd00 f439 : a602 lda #$02 ; load accumulator with value #$02 f43b : 8b pushh ; push h-register onto stack f43c : aded bsr $f42b ; branch to subroutine at address $f42b f43e : 2598 blo $f3d8 ; branch if reg. is lower (C is set) f440 : 8c clrh ; clear h-register (index register high) f441 : a706 ais #$06 ; add signed value to stack pointer (sp) f443 : 58 lslx ; shift left x-register f444 : d6f27a lda $f27a,x ; load accu with value at address $f27a+hx f447 : 87 pusha ; push accu onto stack f448 : d6f279 lda $f279,x ; load accu with value at address $f279+hx f44b : 87 pusha ; push accu onto stack f44c : 9e db $9e ; replace ,x with ,sp in next instruction f44d : e605 lda $05,x ; load accu with value at addr. $05+hx f44f : 87 pusha ; push accu onto stack f450 : a7fd ais #$fd ; add signed value to stack pointer (sp) f452 : 8a poph ; pop (pull) h-register from stack f453 : 80 rti ; return from interrupt f454 : cdf017 jsr $f017 ; jump to subroutine at address $f017 f457 : f8 eor ,x ; XOR accu with value at address in hx f458 : f7 sta ,x ; store accumulator at address in hx f459 : 81 rts ; return from subroutine f45a : cdf017 jsr $f017 ; jump to subroutine at address $f017 f45d : 87 pusha ; push accu onto stack f45e : cdefbe jsr $efbe ; jump to subroutine at address $efbe f461 : 86 popa ; pop (pull) accu from stack f462 : 20f3 bra $f457 ; branch always to $f457 f464 : cdf017 jsr $f017 ; jump to subroutine at address $f017 f467 : 43 coma ; inverse accumulator (one's complement) f468 : e404 and $04,x ; AND accu with value at address $04+hx f46a : e704 sta $04,x ; store accumulator at address $04+hx f46c : 81 rts ; return from subroutine f46d : cdefdd jsr $efdd ; jump to subroutine at address $efdd f470 : 6f04 clr $04,x ; clear memory at $04+hx f472 : 81 rts ; return from subroutine f473 : 3f04 clr $04 ; clear memory at address $04 f475 : 81 rts ; return from subroutine f476 : 55e0 ldhx $e0 ; load index register (hx) from address $e0 f478 : f6 lda ,x ; load accu with value at address in hx f479 : af01 aix #$01 ; add signed value to index register (hx) f47b : 35e0 sthx $e0 ; store index register (hx) to address $e0 f47d : ccf0ce jmp $f0ce ; unconditional jump to address $f0ce ; --------------------------------------------------------------------------- ; --------------------------------------------------------------------------- ; DATA - Trimmwert OSCTRIM für den internen Oszillator ; --------------------------------------------------------------------------- ffc0 : 80 Ç ; --------------------------------------------------------------------------- ; CODE - Der User-Monitor ; --------------------------------------------------------------------------- ffc1 : c6ffc0 lda $ffc0 ; load accumulator with value at $ffc0 ffc4 : b738 sta $38 ; store accumulator at address $38 ffc6 : c6fe01 lda $fe01 ; load accumulator with value at $fe01 ffc9 : a53c bit #$3c ; AND accu with value #$3c (without alter) ffcb : 2603 bne $ffd0 ; branch if not equal (Z is clear) ffcd : ccee56 jmp $ee56 ; unconditional jump to address $ee56 ffd0 : 101f bset #0,$1f ; set bit #0 at memory address $1f ffd2 : cc2db2 jmp $2db2 ; unconditional jump to address $2db2 ffd5 : 031f03 brclr #1,$1f,$ffdb ; branch to $ffdb if bit #1 at $1f is clear ffd8 : ccf439 jmp $f439 ; unconditional jump to address $f439 ffdb : cc2cf9 jmp $2cf9 ; unconditional jump to address $2cf9 ; --------------------------------------------------------------------------- ; DATA - Interruptvektoren Teil 1 ; --------------------------------------------------------------------------- ffd0 : f4 25 ¶% ffe0 : f4 15 ¶. ; --------------------------------------------------------------------------- ; DATA - Kennungsstring des Betriebssystems ; --------------------------------------------------------------------------- ffe0 : 43 43 54 52 4c 2d 42 41 53 49 43 20 4f 4d CCTRL-BASIC OM fff0 : 8a 00 è. ; --------------------------------------------------------------------------- ; DATA - Interruptvektoren Teil 2 ; --------------------------------------------------------------------------- fff0 : f3 e9 f3 e0 f3 da 44 48 f3 cf ff d5 ff c1 ¾Ú¾Ó¾+DH¾¤ i - ; =========================================================================== ; = Ende des ROM-Listings = ; ===========================================================================
Diese Seiten enthalten wertvolle Hinweise, Demos und Tools rund um die C-Control und die OM. Für den Fall, daß man nicht weiter weiß, kann man versuchen, hier eine Lösung für
das Problem zu finden.
Die offizielle Informationssite zur OM
http://om.dharlos.de
Das Forum zur C-Control-1 und zur OM
http://ccintern.dharlos.de/forum
CCTools - Bezug der OM-Controller
http://www.cctools.eu
C-Control-intern - Informationen rund um das Betriebssystem der CC1.1
http://ccintern.dharlos.de
Besonders wichtig sind die offiziellen Websides zu den Controllern 68HC908QT4, QY4, QB8 und MC9S08AW60. Dort können Manuals, Beispielcode und Application Notes zum Controller und zur CPU heruntergeladen werden. Im Anhang des Manuals zum Controller befinden sich auch die umfangreichen Technischen Daten.
Offizielle Webside der Mikrocontroller MC68HC908QT4 (Open-Micro), MC68HC908QY4 (Open-Mini) und MC908QB8 (Open-Midi und Open-Macro)
http://www.nxp.com/products/microcontrollers-and-processors/more-processors/8-16-bit-mcus/8-bit-hc08/8-bit-eeprom-emulation-q-mcus:HC08Q
Offizielle Webside des Mikrocontrollers MC9S08AW60 (Open-Maxi)
http://www.nxp.com/products/microcontrollers-and-processors/more-processors/8-16-bit-mcus/8-bit-s08/8-bit-general-purpose-aw60-48-32-16-mcus:S08AW
Der AS05-Assembler von Frank A. Vorstenbosch ist der Defacto-Standard auf der C-Control 1.1, kann aber auch auf 68HC908-Mikrocontrollern wie der Open-Micro, Open-Mini, Open-Midi und Open-Macro eingesetzt werden, da die 6808-CPU objektcode- und quellcodekompatibel zur 6805er ist.
Die HCS08-CPU des AW60s ist abwärtskompatibel zur 6808.
Die Betriebssysteme wurden vollständig mit dem sehr zuverlässigen AS05-Assembler und Macros für die neuen 6808-Befehle erstellt.
Frank's Cross Assemblers
http://www.kingswood-consulting.co.uk/assemblers