# Bedienungsanleitung
Version 14.1

Erstellt ab 1. Oktober 2005
von Jens Gürtler und Dietmar Harlos
letztes Update: 16. August 2026

## Vorwort

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

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.

## Die Open-Micro und Open-Mini

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.

### Die Vorteile der Open-Micro im Vergleich zur C-Control/Micro

- Sorgsam erstelltes und ausführlich getestetes Betriebssystem.
- 64 Byte freier RAM-Speicher, wobei einige Betriebssystemfunktionen abgeschaltet werden können und so der Speicher auf bis zu 80 Byte erweitert werden kann. Das heißt, es sind bis zu 80 Byte-Variablen nutzbar. Auf der Conrad-Micro sind es nur 24.
- Es steht 2430 Byte Programmspeicher zur Verfügung. Das ist 386 Byte mehr als auf der Conrad-Micro. Darüber hinaus läßt sich durch den optimierten OCBASIC-Compiler ein Programm um typisch 20 % verkleinern.
- Eine Open-Mini bietet 8 zusätzliche Digitalports und die Open-Midi zusätzlich mehr Speicher und neue Hardwaremodule.
- LOOKTAB arbeitet im Byteformat, dadurch belegen Tabellen nur halb soviel Programmspeicher.
- Durch den in OCBASIC integrierten 6808-Assembler ist die Assemblerprogrammierung so komfortabel, wie auf keinem anderen C-Control-kompatiblen Controllersystem. Assemblerroutinen belegen nicht mehr 256 Byte Speicher, sondern nur noch soviel wie nötig. Außerdem sind mehrere Assemblermodule gleichzeitig nutzbar.
- Höhere Ausführungsgeschwindigkeit durch neue Tokenbefehle in Verbindung mit OCBASIC.
- Keine Störungen ("glitches") mehr an den Digitalports, da das Betriebssystem nicht wie die Conrad-Micro die Richtungsregister vor den Datenregistern setzt.
- Kein Überschreiben von Systemdaten durch Überlauf des Rechen- oder GOSUB-Stacks wie bei der Conrad-Micro möglich.
- Die C-Control/Micro unterstützt viele BASIC-Befehle nicht mehr, die auf der OM wieder zur Verfügung stehen. Es handelt sich um PULSE, INPUT, NAND, NOR, ABS, SQR, MIN, MAX, SGN, RANDOMIZE, INTERRUPT und RETURN INTERRUPT.
- Einsparung eines Pins durch Semi-Dual-Wire-RS232. Diese RS232 verhält sich wie eine normale serielle Schnittstelle mit getrenntem RXD und TXD.
- FREQ liegt nicht mehr an Port[4] sondern an Port[3]. Dadurch bleibt Port[4] voll nutzbar.
- Das Betriebssystem läßt sich konfigurieren (durch PrintHex, RunOnErr, End2Host und vieles mehr).
- Fehlerhandling ist in BASIC möglich.
- Fehlerfreie Unterstützung der BASIC-Interruptroutine.
- Die Ports lassen sich auch im Byteformat ansprechen.
- Auf die Variablen im RAM kann per Pointer zugegriffen werden.
- User-Assemblerroutinen lassen sich in jeden Interrupt und selbst in den Interpreter einbinden.
- In BASIC kann auch ohne Assembler auf den gesamten Speicher zugegriffen werden.
- Das Carry-Flag der letzten Berechnung wird zurückgeliefert.
- Bei kritischen Fehlern in der Software wird in den ROM-Monitor MON08 gesprungen. Der MON08 läßt sich direkt nutzen.
- Der Controller 68HC908QT4/QY4, auf dem die Open-Micro und Open-Mini basieren, läßt sich ohne Einschränkungen nutzen.
- Betriebssystemupdate ist möglich.
- Dateioperationen (OPEN# FOR, PRINT#, INPUT#, etc.) werden voll unterstützt und sind im Betriebssystem bereits integriert. Das Betriebssystem ist während des Schreibens in den User-Flash geschützt.
- Variableninhalte können mittels Dateifunktionen dauerhaft im User-Flash gespeichert werden
- In Assemblerprogrammen sind diverse Firmwareroutinen nutzbar.
- Controller startet nicht mehr automatisch nach Programmdownload, sondern kann über RS232 gestartet werden.
- Controller kann aus BASIC heraus in den Programmiermodus wechseln, unabhängig von der Stellung des HOST-Schalters.
- Der interne Oszillator der bei CCTools erhältlichen Controller wurde bereits auf typisch 1% genau getrimmt. Die Conrad-Micro weist dagegen einen Fehler von bis zu 25% auf.
- Viele weitere Verbesserungen wie optimierter Aufruf bei SYS, besseres RAND und viele neue interne Variablen.

### Die Nachteile der Open-Micro und Open-Mini

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.

## Handhabung der OM-Controller

### Die Versorgungsspannung

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 Programmierung

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.

### Pinbelegung der Open-Micro

### Pinbelegung der Open-Mini

### Pinbelegung der Open-Midi

### Funktionen der Pins

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. Siehe Beispiel. 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.

### Einfache Programmierschaltung

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, bei dem die beiden Leitungen RxD und TxD gekreuzt sind. Der Controller läßt sich per "Power-On Reset" (POR), also Einschalten der Betriebsspannung, und per "Low-Voltage Inhibit"-Reset (LVI) resetten. Letzteres wird auf dem zweiten PE-Board gemacht.
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.

Größeres Bild

## Programmieren in OCBASIC

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.

### Beschreibung

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.

### Entwicklungsumgebungen (IDEs) für OCBASIC

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.

### Der echte Compiler OCBASR

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.

### Hierarchie der Operatoren

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

In einem Term mit mehreren Operatoren werden zuerst die Operationen durchgeführt, die einen hohen Rang besitzen und in der Tabelle am Anfang stehen. Erst danach werden die Rechenergebnisse mit den weiter unten stehenden Operatoren weiterverarbeitet. Die Rangfolge der Operatoren von OCBASIC entspricht exakt denen von CCBASIC.

##### Beispiel

`IF a+b*c>d THEN 'entspricht IF ((a+(b*c))>d) THEN
...
END IF`

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.

##### Beispiel

`PRINT 1 OR 2 + 3 SHL (4 + 5 / SQR(6 + 7))`

### Sprungziele (#)

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.

##### Beispiel

`#endlos 'alternativ kann WHILE TRUE verwendet werden
PRINT funktion
subroutine 'das entspricht GOSUB, nicht GOTO wie in CCBASIC
GOSUB subroutine
GOTO endlos 'alternativ kann WEND verwendet werden
#funktion 'alternativ kann FUNCTION verwendet werden
...
RETURN 123
#subroutine 'alternativ kann PROCEDURE verwendet werden
...
RETURN`

### Bekannte Schlüsselwörter

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.

#### &B

##### Beschreibung

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.

##### Hinweis

Der Taschenrechner von Windows kann dezimale in binäre und hexadezimale Zahlen umrechnen, wenn er auf wissenschaftliche Ansicht gestellt wird.

##### Syntax

`&B...`

##### Beispiel

`&B10011001`

#### &H

##### Beschreibung

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.
Statt &H kann auch $ verwendet werden.

##### Hinweis

Der Taschenrechner von Windows kann dezimale in binäre und hexadezimale Zahlen umrechnen, wenn er auf wissenschaftliche Ansicht gestellt wird.

##### Syntax

`&H...`

##### Beispiel

`&H1FFE`

#### ABS

##### Beschreibung

Berechnet den Absolutbetrag von "x"
Da die OM ausschließlich mit nicht vorzeichenbehafteten Bytewerten rechnet, hat dieser Befehl auf der OM keine Funktion.

##### Syntax

`ABS(x)`

##### Beispiel

`y = ABS(-1)`

#### AD

##### Beschreibung

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.

##### Hinweis

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.

##### Verfügbarkeit

`Open-Micro: AD[1], AD[2], AD[5] und AD[6]
Open-Mini: AD[1], AD[2], AD[5] und AD[6]
Open-Midi: AD[1], AD[2], AD[5], AD[6], AD[9] bis AD[14]`

##### Syntax

`AD[index]`

##### Beispiel

`DEFINE port1 AD[1]
DEFINE port2 AD[2]
DEFINE port3 AD[3] 'port3 entspricht port5
DEFINE port4 AD[4] 'port4 entspricht port6
DEFINE port5 AD[5]
DEFINE port6 AD[6]
PRINT port2`

#### AND

##### Beschreibung

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

Weitere logische Operatoren sind NOT, NAND, OR, NOR und XOR.

##### Syntax

`AND`

##### Beispiel

`IF x>10 AND x<20 THEN y=x 'y auf x setzen, wenn sowohl x größer
'10 als auch x kleiner 20 ist, x also
'im Wertebereich 11 bis 19 liegt
y = x AND &H7F 'das oberste Bit ausmaskieren`

#### APPEND

##### Beschreibung

Der Befehl wird im Zusammenhang mit OPEN# verwendet. APPEND bedeutet, daß die Datei zum Anhängen von Daten geöffnet wird.

##### Syntax

`APPEND`

##### Beispiel

`OPEN# FOR APPEND`

#### BAUD

##### Beschreibung

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.

##### Syntax

`BAUD rate`

##### Beispiel

`BAUD R9600 'Syntax der C-Control 1.1
BAUD 9600 'Syntax der Conrad-Micro`

#### BEEP

##### Beschreibung

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.

##### Hinweis

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.

##### Verfügbarkeit

`Open-Micro: PORT[1], PORT[2], PORT[4], PORT[5], PORT[6]
Open-Mini: PORT[1], PORT[2], PORT[4], PORT[5], PORT[6] und
PORT[9] bis PORT[16]
Open-Midi: PORT[1], PORT[2], PORT[4], PORT[5], PORT[6] und
PORT[9] bis PORT[16]`

##### Syntax

`BEEP ton, tondauer, port`

##### Beispiel

`DEFINE ton_port PORT[6]
BEEP 250, 50, ton_port '1 Sekunde an PORT[6]`

#### BIT

##### Beschreibung

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.

##### Syntax

`DEFINE bezeichner BIT[index]
DEFINE bezeichner BIT[index] OF bytevariable
DEFINE bezeichner BIT[index] OF wordvariable`

##### Beispiel

`DEFINE zahl BYTE[1]
DEFINE bit0 BIT[1] ' Die 8 BITs belegen die gleiche Speicherzelle
DEFINE bit1 BIT[2] ' wie BYTE[1]. Daher ändert sich die Variable
DEFINE bit2 BIT[3] ' "zahl", wenn eine Änderung an den einzelnen
DEFINE bit3 BIT[4] ' BITs vorgenommen wird.
DEFINE bit4 BIT[5]
DEFINE bit5 BIT[6]
DEFINE bit6 BIT[7]
DEFINE bit7 BIT[8]
DEFINE bit7vonzahl BIT[8] OF zahl
zahl=0 'die Variable "zahl" entspricht jetzt binär 00000000
print "Zahl=";zahl
bit0=ON 'die Variable "zahl" entspricht jetzt binär 00000001
bit7=ON 'die Variable "zahl" entspricht jetzt binär 10000001
bit2=ON 'die Variable "zahl" entspricht jetzt binär 10000101
PRINT "Zahl=";zahl
IF bit7vonzahl THEN 'wenn Bit Nummer 7 von "zahl" gesetzt ist, dann
bit5=ON 'setze auch das Bit Nummer 5
ELSE
PRINT "Das Most Significant Bit (MSB) der Variable ist nicht gesetzt."
END IF`

##### Hinweis

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.

##### Hinweis

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.

#### BYTE

##### Beschreibung

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.

##### Hinweis

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.

##### Syntax

`DEFINE bezeichner BYTE[index]
DEFINE bezeichner BYTE`

##### Beispiel

`DEFINE variable BYTE
variable = 13`

##### Hinweis

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.

#### BYTEPORT

##### Beschreibung

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.

##### Verfügbarkeit

`Open-Micro: BYTEPORT[1] (die unteren 6 Bits)
Open-Mini: BYTEPORT[1] (die unteren 6 Bits) und BYTEPORT[2]
Open-Midi: BYTEPORT[1] (die unteren 6 Bits) und BYTEPORT[2]`

##### Syntax

`DEFINE bezeichner BYTEPORT[port]`

##### Beispiel

`DEFINE port_a BYTEPORT[1]
DEFINE port_b BYTEPORT[2]`

#### CLOSE#

##### Beschreibung

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.

##### Syntax

`CLOSE#`

##### Beispiel

`OPEN# FOR WRITE
PRINT# 123
CLOSE#`

#### CTS

##### Beschreibung

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.

##### Syntax

`CTS`

##### Beispiel

`IF CTS THEN GET x`

#### DA

##### Beschreibung

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.

##### Syntax

`DEFINE bezeichner DA[index]`

##### Beispiel

`DEFINE pwm_port DA[1]
pwm_port=128`

#### DAY

##### Beschreibung

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).

##### Syntax

`DAY`

##### Beispiel

`DAY = 1
PRINT DAY`

#### DEACT

##### Beschreibung

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.

##### Syntax

`DEACT port`

##### Beispiel

`DEFINE port1 PORT[1]
DEFINE byteport2 BYTEPORT[2]
port1 = ON
DEACT port1
DEACT byteport2`

#### DEFINE

##### Beschreibung

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.

##### Hinweis

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.

##### Syntax

`DEFINE bezeichner [AS] art [OF variable]`

##### Beispiel

`DEFINE bitport PORT[2] 'BIT-Port (Digitalport) vereinbaren.
DEFINE variable BYTE[1] 'BYTE-Variable auf der ersten Speicherzelle.
DEFINE b0 BIT[1] OF variable 'BIT-Variable in einer BYTE-Variable ablegen.
DEFINE konstante 64 'Eine symbolische Konstante.
DEFINE sekunde SECOND 'Alternativer Name fuer SECOND.
DEFINE x BYTE 'Eine Variable definieren. Die Speicherzelle
'wird vom Compiler automatisch vergeben.
DEFINE a,b,c BYTE 'Drei Variablen hintereinander ablegen.
DEFINE d,e,f BYTE[5] 'Drei Variablen belegen gleichen Speicher.
a=123
b=a+konstante
PRINT sekunde`

##### Hinweis

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.

#### DOW

##### Beschreibung

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.

##### Syntax

`DOW`

##### Beispiel

`IF DOW = 1 THEN PRINT "Montag"`

#### ELSE

##### Beschreibung

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.

##### Syntax

`ELSE`

##### Beispiel

`IF x = 0 THEN 'wenn x gleich 0 ist,
y = 2 ' dann setze y auf 2 und gehe hinter das "END IF"
ELSE 'wenn x dagegen ungleich 0 war,
y = 13 ' dann setze zunächst y auf 13 und
x = 12 ' danach x auf 12 und
END IF 'fahre mit der Befehlsausführung hinter "END IF" fort`

#### END

##### Beschreibung

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.

##### Syntax

`END`

##### Beispiel

`IF x = 0 THEN END`

#### EOF

##### Beschreibung

Test auf Dateiende beim Auslesen der Datei mittels INPUT#. EOF liefert erst nachdem die Datei geöffnet wurde ein sinnvolles Ergebnis.

##### Syntax

`EOF`

##### Beispiel

`IF EOF THEN GOTO dateiende`

#### FILEFREE

##### Beschreibung

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.

##### Syntax

`FILEFREE`

##### Beispiel

`IF FILEFREE THEN GOTO istnochfrei`

#### FOR TO NEXT

##### Beschreibung

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.

##### Syntax

`FOR variable=anfangswert TO endwert [STEP schrittweite]
anweisungen
NEXT variable`

##### Beispiel

`DEFINE wert BYTE
FOR wert = 0 TO 255
Print "Wert"; wert
NEXT wert
'Das Beispiel entspricht dem folgenden Code:
wert=0
#schleife
Print "Wert"; wert
IF wert=255 THEN GOTO schleifenende
wert=wert+1 'die Schrittweite ist 1, falls kein STEP angegeben wurde
GOTO schleife
#schleifenende`

#### FREQ

##### Beschreibung

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.

##### Verfügbarkeit

`Open-Micro: an PORT[3]
Open-Mini: an PORT[3]
Open-Midi: an PORT[3]`

##### Syntax

`FREQ
FREQ1`

##### Beispiel

`FREQ = 50 'Torzeit, bzw. Meßzeit: 1 Sekunde
PRINT FREQ`

#### FREQ2

##### Beschreibung

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.

##### Syntax

`FREQ2`

##### Beispiel

`PRINT FREQ2
FREQ2=128 'das ist der Defaultwert unmittelbar nach Reset`

##### Hinweis

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.

#### GET

##### Beschreibung

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.

##### Syntax

`GET variable`

##### Beispiel

`DEFINE variable BYTE
GET variable
PRINT "gelesen wurde ";variable`

#### GOSUB

##### Beschreibung

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.

##### Syntax

`GOSUB label`

##### Beispiel

`GOSUB unterprogramm1
unterprogramm2 'entspricht GOSUB unterprogramm2
PRINT "Programmende"
END
#unterprogramm1
PRINT "Unterprogramm 1 aufgerufen"
RETURN
PROCEDURE unterprogramm2
PRINT "Unterprogramm 2 aufgerufen"
RETURN
END PROCEDURE`

#### GOTO

##### Beschreibung

Mit dem Befehl wird ein Programmsprung realisiert. Das BASIC-Programm arbeitet dann hinter dem Label weiter.

##### Syntax

`GOTO label`

##### Beispiel

`#endlos
PRINT "Endlosschleife"
GOTO endlos`

#### HANDSHAKE

##### Beschreibung

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.

##### Syntax

`HANDSHAKE ON
HANDSHAKE OFF`

##### Beispiel

`HANDSHAKE ON`

#### HOUR

##### Beschreibung

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.

##### Syntax

`HOUR`

##### Beispiel

`PRINT HOUR
IF HOUR>23 THEN 'Tageswechsel erkennen
HOUR=0
tag=tag+1
END IF`

#### IF

##### Beschreibung

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.

##### Syntax

`IF bedingung THEN anweisungen1 [ELSE anweisungen2] 'einzeiliges IF
IF bedingung1 THEN 'mehrzeiliges IF
anweisungen1
[ELSE IF bedingung2 THEN
anweisungen2]
[ELSE
anweisungen3]
END IF`

##### Beispiel

`IF x THEN END 'beende das Programm, wenn x ungleich Null ist
IF i=0 THEN
PRINT "Null";
ELSE IF i=1 THEN 'statt "ELSE IF" kann auch "ELSEIF" und
PRINT "Eins"; 'statt "END IF" auch "ENDIF" geschrieben werden
ELSE IF i=2 THEN
PRINT "Zwei"; 'ausserdem ist das "THEN" bei "IF" optional
ELSE IF i=3 THEN
PRINT "Drei";
ELSE IF i=4 OR i=5 THEN
PRINT "Vier oder Fuenf";
ELSE IF i>=10 THEN
PRINT "groesser oder gleich 10";
ELSE
PRINT "Nichts von alledem."
END IF`

##### Hinweis

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.

#### INPUT

##### Beschreibung

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.

##### Syntax

`INPUT variable`

##### Beispiel

`DEFINE variable BYTE
PRINT "Bitte eine Zahl eingeben und mit der Eingabe-Taste bestätigen: ";
INPUT variable
PRINT "Es wurde die Eingabe ";variable;" erkannt."`

#### INPUT#

##### Beschreibung

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.

##### Syntax

`INPUT# variable
INPUT#`

##### Beispiel

`INPUT# variable
PRINT variable
PRINT INPUT#`

#### INTERRUPT

##### Beschreibung

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").

##### Syntax

`INTERRUPT label
INTERRUPT OFF`

##### Beispiel

`INCLUDE "om.def" 'auf der Open-Midi: INCLUDE "omid.def"
CONFIG2=&b01000000 'IRQEN im CONFIG2-Register des Controllers setzen
INTERRUPT basicirq 'BASIC-Interruptroutine vereinbaren
#endlos 'Hauptprogramm
...
GOTO endlos
#basicirq 'Interruptroutine
PRINT "Interrupt"
RETURN INTERRUPT`

#### LOOKTAB

##### Beschreibung

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.

##### Syntax

`LOOKTAB tabellenbezeichner, index, variable
LOOKTAB(tabellenbezeichner, index)`

##### Beispiel

`LOOKTAB tabelle1, mess, temp
PRINT LOOKTAB(tabelle1, mess)
END
TABLE tabelle1 BYTE
122 95 34 211
END TABLE`

#### MAX

##### Beschreibung

Ermittlung des größeren Wertes.

##### Syntax

`MAX(x,y)`

##### Beispiel

`z = MAX(x, 10)`

#### MIN

##### Beschreibung

Ermittlung des kleineren Wertes.

##### Syntax

`MIN(x,y)`

##### Beispiel

`z = MIN(x, 10)`

#### MINUTE

##### Beschreibung

Minuten-Wert der Echtzeituhr.

##### Syntax

`MINUTE`

##### Beispiel

`PRINT MINUTE`

#### MOD

##### Beschreibung

Operator für Modulodivision. Es wird also der Rest einer ganzzahligen Division zurückgeliefert.

##### Syntax

`MOD`

##### Beispiel

`PRINT 10 MOD 6 '10 durch 6 ist 1, der Rest ist 4
printdiv 22,7 '22 durch 7 mit Nachkommastellen
END
DIM i,t1,t2 BYTE
PROCEDURE printdiv(t1,t2)
PRINT t1/t2;
PUT ASC(".")
FOR i=0 TO 9
t1=(t1 MOD t2)*10
PUT ASC("0")+t1/t2
NEXT i
RETURN
END PROCEDURE`

#### MONTH

##### Beschreibung

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).

##### Syntax

`MONTH`

##### Beispiel

`PRINT MONTH`

#### NAND

##### Beschreibung

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

Weitere logische Operatoren sind NOT, AND, OR, NOR und XOR.

##### Syntax

`NAND`

##### Beispiel

`x = a NAND b`

#### NOR

##### Beschreibung

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

Weitere logische Operatoren sind NOT, AND, NAND, OR und XOR.

##### Syntax

`NOR`

##### Beispiel

`x = a NOR b`

#### NOT

##### Beschreibung

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

Weitere logische Operatoren sind AND, NAND, OR, NOR und XOR.

##### Syntax

`NOT`

##### Beispiel

`x = NOT y`

#### OFF

##### Beschreibung

Vordefinierte Konstante für 0, also logisch unwahr. Es kann auch die Konstante FALSE verwendet werden.

##### Syntax

`OFF`

##### Beispiel

`Led = OFF`

#### ON

##### Beschreibung

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. Diese Konstante ist gleichbedeutend mit TRUE.

##### Hinweis

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.

##### Syntax

`ON`

##### Beispiel

`Led = ON`

#### ON GOSUB

##### Beschreibung

Unterroutinenaufruf in Abhängigkeit von x. Wenn x gleich 0 ist, wird das erste Label der Liste aufgerufen.

##### Hinweis

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.

##### Syntax

`ON x GOSUB`

##### Beispiel

`ON x GOSUB label1, label2, label3`

#### ON GOTO

##### Beschreibung

Programmsprung in Abhängigkeit von x. Wenn x gleich 0 ist, wird das erste Label der Liste aufgerufen.

##### Hinweis

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.

##### Syntax

`ON x GOTO`

##### Beispiel

`ON x GOTO label1, label2, label3`

#### OPEN#

##### Beschreibung

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.

##### Syntax

`OPEN#`

##### Beispiel

`OPEN# FOR WRITE
IF FILEFREE THEN PRINT# 123
CLOSE#`

#### OR

##### Beschreibung

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

Weitere logische Operatoren sind NOT, AND, NAND, NOR und XOR.

##### Syntax

`OR`

##### Beispiel

`y = a OR b`

#### PAUSE

##### Beschreibung

Programmunterbrechung für t*20 Millisekunden.
Dieser Befehl vermindert für die Dauer der Wartezeit die Stromaufnahme der CPU. Intern wird der Assemblerbefehl WAIT ausgeführt, wodurch auf den nächsten Interrupt gewartet wird.
Der Befehl "PAUSE 0" wartet überhaupt nicht.
Der Befehl "PAUSE t" wartet zwischen (t-1)*20 ms und t*20 ms.

##### Syntax

`PAUSE t`

##### Beispiel

`PAUSE 50 '1 Sekunde Pause`

#### PORT

##### Beschreibung

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.

##### Verfügbarkeit

`Open-Micro: PORT[1] bis PORT[6]
Open-Mini: PORT[1] bis PORT[6] und PORT[9] bis PORT[16]
Open-Midi: PORT[1] bis PORT[6] und PORT[9] bis PORT[16]`

##### Syntax

`PORT[index]`

##### Beispiel

`DEFINE taste PORT[2] 'an diesem Port muß eine Taste angeschlossen werden
'(siehe "Beschaltung von Eingangsports", Bild 1)
DEFINE led PORT[4] 'an diesem Port muß eine LED angeschlossen werden
'(siehe "Beschaltung von Ausgangsports", Bild 1)
WHILE taste 'führe die Schleife aus, solange Taste nicht gedrückt
led=ON
PAUSE 10
led=OFF
PAUSE 10
WEND
END`

#### PRINT

##### Beschreibung

Ausgabe von Werten und Texten über die Serielle Schnittstelle.
PRINT ohne abschließendes Semikolon führt zu einer zusätzlichen Übertragung der ASCII-Codes 13 und 10 (Carriage Return und Line Feed). Mehrere Parameter können durch Komma oder Semikolon voneinander getrennt angegeben werden. Ein Komma fügt dabei ein Tabulatorzeichen ein.
Die Kurzform von PRINT ist ein Fragezeichen ("?").

##### Syntax

`PRINT wert
PRINT "Hallo Welt!"`

##### Beispiel

`PRINT "Anzeige = "; wert`

#### PRINT#

##### Beschreibung

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.

##### Hinweis

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.

##### Syntax

`PRINT# ausdruck`

##### Beispiel

`OPEN# FOR APPEND
IF FILEFREE THEN PRINT# 123
CLOSE#`

#### PULSE

##### Beschreibung

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.

##### Verfügbarkeit

`Open-Micro: PORT[1], PORT[2], PORT[4], PORT[5], PORT[6]
Open-Mini: PORT[1], PORT[2], PORT[4], PORT[5], PORT[6] und
PORT[9] bis PORT[16]
Open-Midi: PORT[1], PORT[2], PORT[4], PORT[5], PORT[6] und
PORT[9] bis PORT[16]`

##### Syntax

`PULSE port`

##### Beispiel

`led=OFF
PULSE led`

#### PUT

##### Beschreibung

Ausgabe eines Bytes über die serielle Schnittstelle.

##### Syntax

`PUT wert`

##### Beispiel

`PUT x`

#### RAND

##### Beschreibung

Zufallszahl im Integerformat generieren.
Die interne Variable RAND belegt auf der OM den gleichen RAM-Speicher wie DBNZCTR und ERR.

##### Hinweis

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.

##### Syntax

`RAND`

##### Beispiel

`x = RAND`

#### RANDOMIZE

##### Beschreibung

Zufallszahlengenerator neu initialisieren.

##### Hinweis

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.

##### Syntax

`RANDOMIZE x`

##### Beispiel

`RANDOMIZE TIMER`

#### READ

##### Beschreibung

Datei öffnen zum Auslesen von Daten.

##### Syntax

`READ`

##### Beispiel

`OPEN# FOR READ`

#### RETURN

##### Beschreibung

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.

##### Syntax

`RETURN
RETURN wert`

##### Beispiel

`RETURN
RETURN 123`

#### RETURN INTERRUPT

##### Beschreibung

Rückkehr aus der BASIC-Interruptroutine. Siehe INTERRUPT-Befehl.

##### Syntax

`RETURN INTERRUPT`

##### Beispiel

`RETURN INTERRUPT`

#### RXD

##### Beschreibung

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.

##### Syntax

`RXD`

##### Beispiel

`IF RXD THEN GET x`

#### SECOND

##### Beschreibung

Sekunden-Wert der Echtzeituhr.
Auf der OM wird TIMER automatisch auf Null gesetzt, wenn SECOND beschrieben wird.

##### Hinweis

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.

##### Syntax

`SECOND`

##### Beispiel

`DEFINE mySecond BYTE
DEFINE myMinute BYTE
DEFINE myHour BYTE
REPEAT
mySecond=SECOND 'Uhrzeit auslesen
myMinute=MINUTE
myHour=HOUR
UNTIL mySecond<>59 OR SECOND<>0 'Wiederholen, falls Minute zuende
PRINT myHour;":";myMinute;":";mySecond`

#### SGN

##### Beschreibung

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.

##### Syntax

`SGN(x)`

##### Beispiel

`z = SGN(x)`

#### SHL

##### Beschreibung

Operator für bitweises arithmetisches Linksschieben. Das zuletzt herausgeschobene Bit wird in der internen Variable Carry zurückgeliefert.

##### Syntax

`SHL`

##### Beispiel

`x = x SHL 3 'entspricht x=x*2^3 oder x=x*8`

#### SHR

##### Beschreibung

Operator für bitweises arithmetisches Rechtsschieben. Das zuletzt herausgeschobene Bit wird in der internen Variable Carry zurückgeliefert.

##### Syntax

`SHR`

##### Beispiel

`x = x SHR 3 'entspricht x=x/2^3 oder x=x/8`

#### SLOWMODE

##### Beschreibung

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.

##### Hinweis

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.

##### Syntax

`SLOWMODE ON
SLOWMODE OFF`

##### Beispiel

`DEFINE taste PORT[2]
WHILE taste 'stromsparend auf einen Tastendruck warten
SLOWMODE ON
WEND
PRINT "Taste gedrueckt!"`

#### SQR

##### Beschreibung

Diese Funktion liefert einen ganzzahligen Näherungswert für die Berechnung der Quadratwurzel.
Auf der OM wird das mathematisch korrekt gerundete Ergebnis zurückgeliefert.

##### Syntax

`SQR(x)`

##### Beispiel

`z = SQR(x)`

#### STEP

##### Beschreibung

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.

##### Syntax

`STEP`

##### Beispiel

`FOR a = 1 TO 10 STEP 2 'Das ist eine Endlosschleife, da die Variable
PRINT a 'immer nur ungerade Werte annimmt (1, 3, 5, 7,
NEXT '9, 11, 13, ...), der Endwert aber gerade ist.`

#### SYS

##### Beschreibung

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 zu vieler 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.

##### Syntax

`SYS adresse
SYS adresse wert1, wert2, wert3`

##### Beispiel

`SYS &H101
SYS &HFD00
a=SYS(&HFD00)`

##### Hinweis

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.

#### SYSCODE

##### Beschreibung

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.

##### Syntax

`SYSCODE`

##### Beispiel

`SYSCODE "test.s19"`

##### Hinweis

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.

#### SYSEND

##### Beschreibung

Abschluß einer mit SYSCODE direkt eingegebenen Byte-Liste mit Assembler-Routinen.

##### Syntax

`SYSEND`

##### Beispiel

`SYSCODE &H81 SYSEND`

#### TABLE

##### Beschreibung

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.

##### Hinweis

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.

##### Syntax

`TABLE tabellenname [BYTE]
TABEND`

##### Beispiel

`TABLE nummer1 'Tabelle im Wordformat (ungünstig auf der OM)
56 77 89
TABEND 'statt "TABEND" ist auch "END TABLE" verwendbar
TABLE nummer2 BYTE 'Tabelle im Byteformat (besonders für die OM)
4,5,"Es werden jetzt auch Strings unterstützt",55,6,"A","B","C"
TABEND
TABLE nummer3 BYTE "datei.tab" 'Tabelle aus Datei lesen`

#### TABEND

##### Beschreibung

Abschluß bei mit TABLE direkt eingegebenen konstanten Werten.

##### Syntax

`TABEND`

##### Beispiel

`TABLE nummer1
17 25 4 5
TABEND`

#### TIMER

##### Beschreibung

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.

##### Syntax

`TIMER`

##### Beispiel

`x = TIMER`

#### TOG

##### Beschreibung

Umschalten eines Digitalports. Der Port muß vorher mittels "port = ON" oder "port = OFF" aktiviert werden.

##### Verfügbarkeit

`Open-Micro: PORT[1], PORT[2], PORT[4], PORT[5], PORT[6]
Open-Mini: PORT[1], PORT[2], PORT[4], PORT[5], PORT[6] und
PORT[9] bis PORT[16]
Open-Midi: PORT[1], PORT[2], PORT[4], PORT[5], PORT[6] und
PORT[9] bis PORT[16]`

##### Syntax

`TOG`

##### Beispiel

`TOG led`

#### WAIT

##### Beschreibung

Warten auf Eintreten einer Bedingung.

##### Syntax

`WAIT bedingung`

##### Beispiel

`WAIT taste=ON`

#### WORD

##### Beschreibung

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.

##### Syntax

`DEFINE bezeichner WORD
DEFINE bezeichner WORD[index]`

##### Beispiel

`DEFINE variable1 WORD`

#### WORDPORT

##### Beschreibung

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.

##### Verfügbarkeit

`Open-Micro: WORDPORT[1]
Open-Mini: WORDPORT[1]
Open-Midi: WORDPORT[1]`

##### Syntax

`DEFINE bezeichner WORDPORT[1]`

##### Beispiel

`DEFINE alle_ports WORDPORT[1]`

#### WRITE

##### Beschreibung

Datei öffnen zum Schreiben neuer Daten.

##### Syntax

`WRITE`

##### Beispiel

`OPEN# FOR WRITE`

#### XOR

##### Beschreibung

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

Weitere logische Operatoren sind NOT, AND, NAND, OR und NOR.

##### Syntax

`XOR`

##### Beispiel

`x = a XOR b`

#### YEAR

##### Beschreibung

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).

##### Syntax

`YEAR`

##### Beispiel

`PRINT YEAR`

### Neue Schlüsselwörter

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.

#### ADD

##### Beschreibung

Dient zur schnellen und speicherplatzschonenden Addition bei User-Variablen. Das Carry-Flag wird nicht gesetzt.

##### Syntax

`ADD variable,ausdruck`

##### Beispiel

`DEFINE a BYTE
ADD a,1 'entspricht a=a+1`

#### ASC

##### Beschreibung

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.

##### Syntax

`ASC("x")
CVI("xx")`

##### Beispiel

`PUT ASC("?") 'entspricht PUT 63
lcd_writechar ASC("L") 'entspricht lcd_writechar 76
lcd_writechar ASC("C") 'entspricht lcd_writechar 67
lcd_writechar ASC("D") 'entspricht lcd_writechar 68
END
#lcd_writechar
...
RETURN`

#### ASSEMBLER (!)

##### Beschreibung

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. Zudem gibt es einen Assemblerkurs für die OM-Mikrocontroller: Teil 1, Teil 2, Teil 3 und Teil 4.

##### Syntax

`! assemblerbefehl`

##### Beispiel

`INCLUDE "om.def" 'auf der Open-Midi: INCLUDE "omid.def"
DEFINE variable BYTE
variable=5
assembler 'Aufruf der Assemblerroutine
PRINT variable
END
PROCEDURE assembler INLASM
! lda variable 'Lade den Inhalt der Variable in den Akkumulator
! add #123 'Addiere den konstanten Wert 123 zum Akku
! sta variable 'Speichere den Akku-Inhalt in die Variable
! rts 'Rücksprung zum BASIC-Interpreter
END PROCEDURE`

#### CSTKPTR

##### Beschreibung

Mit dieser internen Variable kann der Zeiger (Pointer) des Rechenstacks gelesen oder verändert werden.

##### Syntax

`CSTKPTR`

##### Beispiel

`PRINT CSTKPTR 'liefert auf der OM eine 8-Bit-Adresse im RAM zurück`

#### DBNZ

##### Beschreibung

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")

##### Syntax

`DBNZ label`

##### Beispiel

`DBNZCTR=15
#loop
PRINT DBNZCTR;" ";
DBNZ loop`

#### DBNZCTR

##### Beschreibung

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.

##### Syntax

`DBNZCTR`

#### DIM

##### Beschreibung

Dieser Befehl kann alternativ statt DEFINE benutzt werden und hat die gleiche Funktion wie dieser. Siehe DEFINE.

##### Syntax

`DIM bezeichner [AS] art [OF variable]`

#### ERR

##### Beschreibung

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 | Zu viele GOSUB-Verschachtelungen
2 | RETURN ohne GOSUB
3 | File-I/O-Error
128 | Illegales Token

Siehe dazu auch RunOnErr, ConOnErr, ERRADR, PRINT# und Beispiel im Forum.

##### Syntax

`ERR`

##### Beispiel

`RunOnErr=ON
IF ERR THEN
PRINT "FEHLER: ";ERR
END
ELSE
PRINT "keine Fehler!"
END IF
' restliches Programm`

#### ERRADR

##### Beschreibung

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.
Siehe dazu auch RunOnErr, ConOnErr, ERR, PRINT# und Beispiel im Forum.

##### Syntax

`ERRADR`

##### Beispiel

`INCLUDE "om.def" 'auf der Open-Midi: INCLUDE "omid.def"
RunOnErr=ON
IF ERR THEN
PrintHex=ON
PRINT "Fehleradresse: $";dERRADRh;dERRADRl
End2Host=ON
END
END IF
' restliches Programm`

#### FILEPOS

##### Beschreibung

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.

##### Syntax

`FILEPOS`

##### Beispiel

`INCLUDE "om.def" 'auf der Open-Midi: INCLUDE "omid.def"
DEFINE a BYTE
dFILEPOSh=&hf4 'Das Anwender-Programm beginnt an Adresse &hf482
dFILEPOSl=&h82 '(auf der Open-Midi an Adresse &he4c2)
FOR a=0 TO 255 'Die ersten 256 Bytes des Programms ausgeben
PRINT INPUT#;" ";
NEXT a`

#### FREERAM

##### Beschreibung

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.

##### Hinweis

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.

##### Syntax

`FREERAM
FREERAM1
FREERAM2
FREERAM3
FREERAM4
FREERAM5`

##### Beispiel

`FREERAM=123 'entspricht FREERAM1=123
DEFINE datenbyte FREERAM2 'alternativer Name für FREERAM2
datenbyte=24`

#### FUNCTION

##### Beschreibung

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.

##### Syntax

`FUNCTION funktionsname1 (parameterliste)
anweisungen
RETURN ausdruck
END FUNCTION
FUNCTION funktionsname2 (parameterliste)
anweisungen
RETURN ausdruck`

##### Beispiel

`DEFINE a,b,c BYTE
PRINT funktion(81,64) 'User-Funktion aufrufen und Parameter übergeben
c=funktion(81,64) 'Rückgabewert in einer Variable speichern
funktion 81,64 'Funktion aufrufen und Rückgabe ignorieren
PRINT LOOKTAB(tabelle1,5) 'das sechste Byte aus der Tabelle
PRINT SYS(&h101 1,2,3) 'Assemblerprogramm als Funktion aufrufen
PRINT INPUT# 'aus der Datei lesen
PRINT GET+INPUT 'von der seriellen Schnittstelle lesen
END
' Eine Funktion mit zwei Parametern und Rueckgabewert
FUNCTION funktion(a,b) 'a und b werden mittels Stack uebergeben.
PRINT "funktion"
RETURN SQR(a)+SQR(b) 'Das RETURN pusht einen Wert auf den Stack.
' Eine Tabelle im Byteformat
TABLE tabelle1 BYTE 'Natürlich wird auch die alte Syntax unter-
1,2,3,4,5,6,7 'stützt. Tabellen enthalten standardmäßig
END TABLE 'nach wie vor Words, keine Bytes.`

##### Hinweis

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.

#### GSTKPTR

##### Beschreibung

Mit dieser internen Variable kann der Zeiger (Pointer) des GOSUB-Stacks gelesen oder verändert werden.

##### Syntax

`GSTKPTR`

##### Beispiel

`PRINT GSTKPTR 'liefert auf der OM eine 8-Bit-Adresse im RAM zurück`

#### INCLUDE

##### Beschreibung

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.

##### Syntax

`INCLUDE dateiname`

##### Beispiel

`INCLUDE "omid.def" 'auf der Open-Micro: INCLUDE "om.def"
' restliche Definitionen
INCLUDE "extports.iia" '1. Systemerweiterung einbinden
INCLUDE "omidesci.iia" '2. Systemerweiterung einbinden
#main
' Hauptprogramm
END
INCLUDE "om_i2c.pro" 'Hilfsroutinen
INCLUDE "om_fw.pro"`

#### INLINE

##### Beschreibung

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.

##### Syntax

`INLINE byteliste`

##### Beispiel

`'Text auf dem LCD ausgeben
SYS lcd_string : INLINE "Pizza-Uhr von D.H.",0`

#### IVAR

##### Beschreibung

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.

##### Syntax

`DEFINE bezeichner IVAR[index]`

##### Beispiel

`DEFINE PULLUPA IVAR[140] ' Register für interne Pull-Up-Widerstaende an
' PORTA (liegt im Speicher an Adresse $0b)
PRINT PULLUPA
PULLUPA=0`

#### OPTION

##### Beschreibung

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.

##### Syntax

`OPTION option`

##### Beispiel

`OPTION ccbas
OPTION openmicro
OPTION openmini
OPTION openmidi
OPTION opencontrol`

#### PCADR

##### Beschreibung

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.

##### Syntax

`PCADR`

##### Beispiel

`INCLUDE "om.def" 'auf der Open-Midi: INCLUDE "omid.def"
PRINT dPCADRh
PRINT dPCADRl`

#### POP

##### Beschreibung

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.

##### Syntax

`POP`

##### Beispiel

`DEFINE a BYTE
PUSH 123
POP a 'auch a=POP ist möglich`

#### PROCEDURE

##### Beschreibung

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.

##### Syntax

`PROCEDURE subroutine1 (parameterliste)
anweisungen
RETURN
END PROCEDURE
PROCEDURE subroutine2 (parameterliste)
anweisungen
RETURN`

##### Beispiel

`DEFINE a BYTE
subroutine 123 'Aufruf einer Subroutine mit Parameteruebergabe
'(entspricht GOSUB, nicht GOTO wie in CCBASIC)
END
' Eine Subroutine mit nur einem Parameter
PROCEDURE subroutine(a)
PRINT "Der Subroutine wurde der Wert ";a;" uebergeben."
RETURN`

##### Hinweis

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.

#### PUSH

##### Beschreibung

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.

##### Syntax

`PUSH ausdruck`

##### Beispiel

`DEFINE a BYTE
PUSH 123 'diese beiden Befehle entsprechen
a=POP 'exakt einem "a=123"`

##### Hinweis

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.

#### REMOVETOS

##### Beschreibung

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.

##### Syntax

`REMOVETOS`

##### Beispiel

`DEFINE a,b BYTE
a=POP
REMOVETOS
b=POP`

#### REPEAT UNTIL

##### Beschreibung

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.

##### Syntax

`REPEAT
...
UNTIL bedingung`

##### Beispiel

`DEFINE a BYTE
a=65
REPEAT
PUT a 'alle Zeichen von 65 bis einschließlich 90 ausgeben
a=a+1
UNTIL a>90 'wiederhole, bis a größer 90 ist
PRINT a 'a ist nun 91`

#### SUB

##### Beschreibung

Dient zur schnellen und speicherplatzschonenden Subtraktion bei User-Variablen. Das Carry-Flag wird nicht gesetzt.

##### Syntax

`SUB variable,ausdruck`

##### Beispiel

`DEFINE a BYTE
SUB a,1 'entspricht a=a-1`

#### WHILE WEND

##### Beschreibung

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.

##### Syntax

`WHILE bedingung
...
WEND`

##### Beispiel

`DEFINE i BYTE
i=0
WHILE i<10 'führe die Schleife aus, solange i kleiner als 10 ist
PUT i+48 'Zeichen 48 bis einschließlich 57 ausgeben
i=i+1
WEND
PRINT i 'i ist 10`

#### ZEIGER (^, @)

##### Beschreibung

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.

##### Syntax

`DEFINE ^bezeichner BYTE 'Zeiger auf ein Byte im USER-RAM definieren
DEFINE ^bezeichner WORD 'Zeiger auf ein Word (unterstuetzt die OM nicht)
^bezeichner 'Adreß-Operator (in C ist das "&")
@bezeichner 'Derefenzierungsoperator (in C ist das "*")`

##### Beispiel

`DEFINE ^pb BYTE 'Zeiger auf ein Byte im USER-RAM und
DEFINE d BYTE 'eine normale Variable definieren
d=123 'Wert in der Variable d speichern
PRINT "Die Variable d liegt im USER-RAM an Adresse: ";^d
PRINT "Und hat den Wert: ";d
pb=^d 'Zeiger mit Adresse von d laden
PRINT "Der Zeiger enthaelt als 'Wert' eine Adresse: ";pb
PRINT "Der Wert von d, gelesen mittels Zeiger: ";@pb
@pb=234 'Byte an der Stelle aendern, auf die pb zeigt
PRINT "Nun enthaelt d den Wert: ";d
FOR pb=0 TO 127 'In der Schleife wird der gesamte RAM-Speicher
PRINT @pb;" "; ' der Open-Micro und Open-Mini, beginnend mit
NEXT pb ' dem User-RAM, ausgegeben.`

### Neue interne Variablen

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.

##### Beispiel

`INCLUDE "om.def" 'auf der Open-Midi: INCLUDE "omid.def"`

##### Hinweis

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.

#### CARRY

##### Beschreibung

Diese interne Bitvariable liefert das Carry nach Berechnungen zurück.

##### Beispiel

`DEFINE a BYTE
a=128 SHL 1
PRINT Carry`

#### CLOCKDIS

##### Beschreibung

Mit diesem Bit läßt sich das Weiterzählen der Uhr deaktivieren.

##### Beispiel

`ClockDis=ON`

#### CONFIG1

##### Beschreibung

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.

##### Beispiel

`PRINT CONFIG1`

#### CONFIG2

##### Beschreibung

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

##### Beispiel

`CONFIG2=&b01000000 'IRQEN im CONFIG2-Register des Controllers setzen`

##### Hinweis

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.

#### CONONERR

##### Beschreibung

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 "Zu viele 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.

##### Hinweis

Der Fehler "Illegales Token" ist fatal. Das heißt, eine Fortsetzung des Programms ist nicht möglich.

##### Beispiel

`ConOnErr=ON
ConOnErr=OFF`

#### DERRADRH

##### Beschreibung

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.

##### Beispiel

`PRINT dERRADRh`

#### DERRADRL

##### Beschreibung

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.

##### Beispiel

`PRINT dERRADRl`

#### DFILEPOSH

##### Beschreibung

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.

##### Beispiel

`PRINT dFILEPOSh`

#### DFILEPOSL

##### Beschreibung

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.

##### Beispiel

`PRINT dFILEPOSl`

#### DPCADRH

##### Beschreibung

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.

##### Beispiel

`PRINT dPCADRh`

#### DPCADRL

##### Beschreibung

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.

##### Beispiel

`PRINT dPCADRl`

#### END2HOST

##### Beschreibung

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.

##### Beispiel

`End2Host=ON
END`

#### FILECHG

##### Beschreibung

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.

##### Beispiel

`PRINT FileChg`

#### IIARFND

##### Beschreibung

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.

##### Beispiel

`PRINT IiarFnd`

#### IRQACT

##### Beschreibung

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.

##### Beispiel

`PRINT IrqAct`

#### IRQREQ

##### Beschreibung

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.

##### Beispiel

`PRINT IrqReq`

#### IRQSET

##### Beschreibung

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.

##### Beispiel

`PRINT IrqSet`

#### PRINTHEX

##### Beschreibung

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.

##### Beispiel

`PrintHex=ON
PRINT &hf4;
PRINT &h82`

#### PRINTSPC

##### Beschreibung

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.

##### Beispiel

`PrintSpc=ON
PRINT 1;2;3`

#### PULLUPA

##### Beschreibung

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.

##### Verfügbarkeit

`Open-Micro: ja
Open-Mini: ja
Open-Midi: ja`

##### Beispiel

`' PTAx: x -> 543210
PULLUPA = &b00011111 'Pull-Up-Widerstand an PORT[6] (gleich PTA5)
'deaktivieren, alle anderen aktiviert lassen`

#### PULLUPB

##### Beschreibung

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.

##### Verfügbarkeit

`Open-Micro: nein (Ports sind nicht herausgeführt)
Open-Mini: ja
Open-Midi: ja`

##### Beispiel

`' PTBx: 76543210
PULLUPB = &b11111111 'alle Pull-Up-Widerstände ein`

#### REALPOP

##### Beschreibung

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.

##### Beispiel

`RealPop=ON 'echtes Stackhandling aktivieren`

#### RUNONERR

##### Beschreibung

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.
Siehe dazu auch ConOnErr, ERR, ERRADR, PRINT# und Beispiel im Forum.

##### Hinweis

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.

##### Beispiel

`RunOnErr=ON
IF ERR THEN
PRINT "Es trat der Fehler Nummer ";ERR;" auf!"
END
END IF
' restliches Programm`

#### UIRADC

##### Beschreibung

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.

##### Beispiel

`UIRAdc=ON`

#### UIRIRQ

##### Beschreibung

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.

##### Beispiel

`UIRIrq=ON`

#### UIRKYB

##### Beschreibung

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.

##### Beispiel

`UIRKyb=ON`

#### UIRSWI

##### Beschreibung

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.

##### Beispiel

`UIRSwi=ON`

#### UIRTIM0

##### Beschreibung

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.

##### Beispiel

`UIRTim0=ON`

#### UIRTIM1

##### Beschreibung

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.

##### Beispiel

`UIRTim1=ON`

#### UIRTOFL

##### Beschreibung

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.

##### Beispiel

`UIRTofl=ON`

#### URILLTOK

##### Beschreibung

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.

##### Beispiel

`URIllTok=ON`

#### URTOK

##### Beschreibung

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.

##### Beispiel

`URTok=ON`

#### VERSION

##### Beschreibung

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.

##### Hinweis

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.

##### Beispiel

`PRINT VERSION`

### Schlüsselwortliste

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

## Softwaremodule für die OM

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.

### Inter-Integrated-Circuit-Bus (I²C, IIC)

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.

### Modellbau-Servos

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.

### Pulsweitenmodulation (PWM)

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.

### DCF77-Signalauswertung

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.

### Extended Ports

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.

### 8 MHz Bustakt

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.

### Erweiterte Zahlenformate

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

Weitere Softwaremodule und Beispielprogramme befinden sich auf der offiziellen OM-Infosite. Sie wird laufend erweitert. Die "Neuigkeiten"-Seite sollte regelmäßig besucht werden.

## Beschaltung der Ports

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.

### Beschaltung von Eingangsports

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.

### Beschaltung von Ausgangsports

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.

## Tips und Tricks

### Die Adreßaufteilung

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.

### Informationen in der Definitionsdatei

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.

### Dauerhafte Datenspeicherung an fester Adresse

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.

##### Beispiel

`INCLUDE "om.def" 'auf der Open-Midi: INCLUDE "omid.def"
dFILEPOSh=&hfd 'vier Bytes auf Adresse $fdc0 speichern
dFILEPOSl=&hc0
PRINT# 3 'Das erste PRINT# schreibt auf das erste Byte der
PRINT# 255 'Flash-Page und führt deshalb zum Löschen der
PRINT# 123 'gesamten Page. Mit jedem Löschvorgang wird eine
PRINT# 98 'von 10.000 Schreib/Lösch-Zyklen verbraucht.
...
dFILEPOSh=&hfd 'vier Bytes von Adresse $fdc0 lesen
dFILEPOSl=&hc0
PRINT INPUT# 'Lesen geht beliebig oft, aber löschen läßt sich
PRINT INPUT# 'jede Flash-Page laut Angaben des Controller-
PRINT INPUT# 'herstellers "nur" 10.000 mal.
INPUT# a`

### Besonderheiten der Controller

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.

### Die ConTEXT-Entwicklungsumgebung

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 %n -withfilename eingetragen. Bei Fenster wird Normal ausgewählt. Bei Hinweis wird BASIC-Compiler eingetragen und bei Speichern wird aktuelle Datei eingetragen. Jetzt folgen noch ein paar Häkchen und der BASIC-Compiler ist fertig eingerichtet. Bei Benutze kurze DOS-Dateinamen, bei Konsolenausgabe aufzeichnen und bei Konsolenausgabe zur letzten Zeile scrollen wird ein Häkchen gesetzt. Bei Regel für Compiler-Ausgabe wird %n(%l) eingetragen. Damit sind wir fertig. Diese Einstellungen werden erst einmal mit Übernehmen abgespeichert.
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.

### Dateinamenserweiterungen anzeigen

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.

### DOS-Programme unter Windows

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.

## Herstellung eines OM-Controllers

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.

### Download der benötigten Dateien

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.

### Entpacken des heruntergeladenen ZIP-Archivs

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.

### Starten der DOS-Eingabeaufforderung

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.

### Das Programmier- und Experimentier-Board

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.

### Controller in das Board einsetzen

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.

### Controller trimmen

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.

### Auswahl der Betriebssystem-Version

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.

### Betriebssystem in den Controller übertragen

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.

### Ein Beispielprogramm in den Controller übertragen und ausführen

Wir haben gerade das Open-Micro-Betriebssystem in den Controller übertragen und stehen noch im Verzeichnis mit dem Betriebssystem und wechseln daher wie im folgenden Bild gezeigt per "cd ..\bsp" in das Verzeichnis mit den Beispielprogrammen.
Die Open-Micro und Open-Mini fragt nach jedem Einschalten der Betriebsspannung (POR-Reset) und nach jedem Drücken des Reset-Tasters (LVI-Reset) den Hostmode-Schalter ab. Steht dieser in der Stellung "HOST" (LED an PTA2 verlischt) geht der Controller nach jedem Reset zunächst in den sogenannten Hostmode und wartet auf die Übertragung eines CCPLUS- oder BASIC-Programms vom PC. Steht der Schalter hingegen in der Stellung "RUN" (LED an PTA2 leuchtet) wird das im Speicher des Controllers stehende CCPLUS- oder BASIC-Programm nach einem Reset sofort ausgeführt.
Wir schalten nun die Versorgungsspannung am Board ein. Da wir gerade das Betriebssystem übertragen haben steht der Hostmode-Schalter noch in der Stellung "HOST". Die LED an PTA2 leuchtet also nicht und der Controller geht in den Hostmodus. Alle anderen LEDs mit Ausnahme der gelben LED an PTA4 leuchten nun, da das Open-Micro-Betriebssystem nach jedem Reset alle internen Pull-Up-Widerstände an den I/O-Ports aktiviert. Der 4049-Porttreiber erkennt dadurch High-Pegel und die zugehörigen LEDs leuchten. Werden die zugehörigen Taster gedrückt, gehen die LEDs für die Dauer des Tastendrucks aus. Wird der Taster PTA0 gedrückt, dann geht der Controller vom Hostmode in den sogenannten "User Monitor Mode". Dieses Feature wird verwendet, um Betriebssystemupdates durchzuführen. Die Hilfsprogramme OSCTRIM und S19MON08 können den Controller automatisch vom Hostmode in den "User Monitor Mode" versetzen. In diesem Modus sind die Pull-Up-Widerstände nicht aktiv. Der "User Monitor Mode" kann beendet werden, indem der Controller resettet wird.
Der Port PTA4 wird vom Controller aktiv auf "low" gezogen, wenn der Controller im Hostmode auf einen Befehl vom PC wartet. Die gelbe PTA4-LED leuchtet daher nicht. Wird dagegen ein Hostmode-Befehl, wie zum Beispiel das Übertragen eines BASIC-Programms in den Flash des Controllers, ausgeführt, wird der I/O-Port PTA4 deaktiviert (auf Eingang geschaltet) und der interne Pull-Up-Widerstand sorgt wie bei den übrigen Ports dafür, daß die gelbe PTA4-LED leuchtet. Während der Datenübertragung über die serielle Schnittstelle flackert die rote PTA0-LED, da die serielle Kommunikation über den Port PTA0 stattfindet.
Zum Übertragen eines Anwenderprogramms in die Open-Micro und Open-Mini gibt es verschiedene Programme. CCPLUS, CCBASIC für Windows, Basic++ und mBasic enthalten bereits die benötigten Routinen und eine Übertragung kann durch einen Klick auf die entsprechende Schaltfläche innerhalb der Entwicklungsumgebung eingeleitet werden. In der C-Control/BASIC-DOS-IDE oder in alternativen IDEs wie zum Beispiel ConTEXT werden dagegen eigenständige Downloadprogramme verwendet. Für die C-Control 1.1 wurde innerhalb der DOS-IDE das Programm CCDL.EXE benutzt. Besonders für Anwender neuerer Windows-Versionen ist das Downloadtool OMDLWIN geeignet, das auf der offiziellen Infosite zur OM heruntergeladen werden kann. Im ZIP-Archiv OMOS06.ZIP liegt ein Downloadtool namens OMDL.EXE bei, das genauso wie OMDLWIN speziell für die Open-Micro und Open-Mini erstellt wurde. Das OMDL-Programm funktioniert gut unter DOS, Windows 3.1, 95, 98 und Me, aber leider meist nicht unter Windows 2000 und XP. Um das im ZIP-Archiv enthaltene Beispielprogramm "micro_bm.bas" zur Open-Micro und Open-Mini zu übertragen muß im Fenster der Eingabeaufforderung "..\tools\omdl micro_bm.dat 2" gestartet werden. In den folgenden zwei Bildern wird statt OMDL dagegen CCDL benutzt. Deshalb wird "ccdl micro_bm.dat 2" gestartet. Bei entsprechend installiertem OMDLWIN kann die Übertragung mittels "omdlwin micro_bm.dat 2" durchgeführt werden. Die "2" am Ende der Eingabe steht wie immer für die Nummer der verwendeten seriellen Schnittstelle. Falls OMDLWIN die serielle Schnittstelle nicht öffnen kann, muß unter Umständen das Fenster der Eingabeaufforderung geschlossen werden.
Nach wenigen Sekunden sollte das Programm fehlerfrei in die Open-Micro oder Open-Mini übertragen worden sein. Wir starten nun ein sogenanntes Terminalprogramm, mit dem auf einfache Weise Daten über die serielle Schnittstelle zum Controller gesendet und PRINT- und PUT-Ausgaben vom Controller empfangen werden können. Alle über die PC-Tastatur eingegebenen Zeichen und Texte werden direkt zum Controller geschickt.
Der Controller befindet sich noch immer im Hostmodus und wartet auf Befehle vom PC. Wird in dieser Situation innerhalb des Terminalprogramms die Eingabetaste (RETURN) gedrückt, interpretiert der Controller das als Start-Kommando. Das heißt, das im Speicher des Controllers stehende CCPLUS- oder BASIC-Programm wird so ausgeführt, als ob der Hostmode-Schalter nach einem Reset auf "RUN" stehen würde. Wir drücken also die Eingabetaste, um das gerade übertragene Programm zu starten.
Das von uns in die Open-Micro oder Open-Mini übertragene Beispielprogramm ist ein Geschwindigkeitstest (Benchmark) für die Micro, der vom Entwickler der neuen C-Control-1-Varianten erstellt wurde. Die Original-Micro von Conrad Electronic schafft in diesem Benchmark rund 11500 BASIC-Instruktionen pro Sekunde. Die Open-Micro und Open-Mini ist ein klein wenig langsamer und erreicht im Standard-OCBASIC ungefähr 11320 BASIC-Instruktionen pro Sekunde. Das entspricht rund 34000 sogenannter Bytecode-Instruktionen pro Sekunde. Der Grund für die etwas geringere Ausführungsgeschwindigkeit liegt im höheren Funktionsumfang der Open-Micro und Open-Mini. Außerdem ist es möglich, den Benchmark durch die Verwendung neuer OCBASIC-Syntax zu beschleunigen. Mehr dazu steht in "micro_bm.bas", der Datei mit dem CCBASIC-, bzw. OCBASIC-Sourcecode.
Um den Controller zu stoppen drücken wir kurz die rote Reset-Taste. Der Controller ist danach wieder im Hostmode, da der Hostmode-Schalter immer noch in Stellung "HOST" steht. Das Terminalprogramm können wir mit einem Druck auf die Taste ESC (oben links auf der Tastatur) beenden. Das Fenster der Eingabeaufforderung kann durch den Befehl "exit" beendet werden. Alternativ auf das Schließen-Symbol oben rechts klicken.

## Das ROM-Listing

    ; ===========================================================================
    ; =    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                                                =
    ; ===========================================================================

## Seiten im Internet

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)
https://www.nxp.com/products/HC08Q
Offizielle Webside des Mikrocontrollers MC9S08AW60 (Open-Maxi)
https://www.nxp.com/products/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