Bedienungsanleitung
Version 12.0


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



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

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

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.

RangOperatorBeschreibung
8( )Klammern
7+ -positives und negatives Vorzeichen
6* / MOD SHL SHRMultiplikation, Division, Restwert und Schiebebefehle
5+ -Addition und Subtraktion
4= == <> > >= < <=numerischer Vergleich
3NOTlogisches Vorzeichen
2AND NANDlogisches UND
1OR NOR XORlogisches 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

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

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

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
Hinweis
Syntax
Beispiel

&H

Beschreibung
Hinweis
Syntax
Beispiel

ABS

Beschreibung
Syntax
Beispiel

AD

Beschreibung
Hinweis
Verfügbarkeit
Syntax
Beispiel

AND

Beschreibung
Syntax
Beispiel

APPEND

Beschreibung
Syntax
Beispiel

BAUD

Beschreibung
Syntax
Beispiel

BEEP

Beschreibung
Hinweis
Verfügbarkeit
Syntax
Beispiel

BIT

Beschreibung
Syntax
Beispiel
Hinweis
Hinweis

BYTE

Beschreibung
Hinweis
Syntax
Beispiel
Hinweis

BYTEPORT

Beschreibung
Verfügbarkeit
Syntax
Beispiel

CLOSE#

Beschreibung
Syntax
Beispiel

CTS

Beschreibung
Syntax
Beispiel

DA

Beschreibung
Syntax
Beispiel

DAY

Beschreibung
Syntax
Beispiel

DEACT

Beschreibung
Syntax
Beispiel

DEFINE

Beschreibung
Hinweis
Syntax
Beispiel
Hinweis

DOW

Beschreibung
Syntax
Beispiel

ELSE

Beschreibung
Syntax
Beispiel

END

Beschreibung
Syntax
Beispiel

EOF

Beschreibung
Syntax
Beispiel

FILEFREE

Beschreibung
Syntax
Beispiel

FOR TO NEXT

Beschreibung
Syntax
Beispiel

FREQ

Beschreibung
Verfügbarkeit
Syntax
Beispiel

FREQ2

Beschreibung
Syntax
Beispiel
Hinweis

GET

Beschreibung
Syntax
Beispiel

GOSUB

Beschreibung
Syntax
Beispiel

GOTO

Beschreibung
Syntax
Beispiel

HANDSHAKE

Beschreibung
Syntax
Beispiel

HOUR

Beschreibung
Syntax
Beispiel

IF

Beschreibung
Syntax
Beispiel
Hinweis

INPUT

Beschreibung
Syntax
Beispiel

INPUT#

Beschreibung
Syntax
Beispiel

INTERRUPT

Beschreibung
Syntax
Beispiel

LOOKTAB

Beschreibung
Syntax
Beispiel

MAX

Beschreibung
Syntax
Beispiel

MIN

Beschreibung
Syntax
Beispiel

MINUTE

Beschreibung
Syntax
Beispiel

MOD

Beschreibung
Syntax
Beispiel

MONTH

Beschreibung
Syntax
Beispiel

NAND

Beschreibung
Syntax
Beispiel

NOR

Beschreibung
Syntax
Beispiel

NOT

Beschreibung
Syntax
Beispiel

OFF

Beschreibung
Syntax
Beispiel

ON

Beschreibung
Hinweis
Syntax
Beispiel

ON GOSUB

Beschreibung
Hinweis
Syntax
Beispiel

ON GOTO

Beschreibung
Hinweis
Syntax
Beispiel

OPEN#

Beschreibung
Syntax
Beispiel

OR

Beschreibung
Syntax
Beispiel

PAUSE

Beschreibung
Syntax
Beispiel

PORT

Beschreibung
Verfügbarkeit
Syntax
Beispiel

PRINT

Beschreibung
Syntax
Beispiel

PRINT#

Beschreibung
Hinweis
Syntax
Beispiel

PULSE

Beschreibung
Verfügbarkeit
Syntax
Beispiel

PUT

Beschreibung
Syntax
Beispiel

RAND

Beschreibung
Hinweis
Syntax
Beispiel

RANDOMIZE

Beschreibung
Hinweis
Syntax
Beispiel

READ

Beschreibung
Syntax
Beispiel

RETURN

Beschreibung
Syntax
Beispiel

RETURN INTERRUPT

Beschreibung
Syntax
Beispiel

RXD

Beschreibung
Syntax
Beispiel

SECOND

Beschreibung
Hinweis
Syntax
Beispiel

SGN

Beschreibung
Syntax
Beispiel

SHL

Beschreibung
Syntax
Beispiel

SHR

Beschreibung
Syntax
Beispiel

SLOWMODE

Beschreibung
Hinweis
Syntax
Beispiel

SQR

Beschreibung
Syntax
Beispiel

STEP

Beschreibung
Syntax
Beispiel

SYS

Beschreibung
Syntax
Beispiel
Hinweis

SYSCODE

Beschreibung
Syntax
Beispiel
Hinweis

SYSEND

Beschreibung
Syntax
Beispiel

TABLE

Beschreibung
Hinweis
Syntax
Beispiel

TABEND

Beschreibung
Syntax
Beispiel

TIMER

Beschreibung
Syntax
Beispiel

TOG

Beschreibung
Verfügbarkeit
Syntax
Beispiel

WAIT

Beschreibung
Syntax
Beispiel

WORD

Beschreibung
Syntax
Beispiel

WORDPORT

Beschreibung
Verfügbarkeit
Syntax
Beispiel

WRITE

Beschreibung
Syntax
Beispiel

XOR

Beschreibung
Syntax
Beispiel

YEAR

Beschreibung
Syntax
Beispiel

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
Syntax
Beispiel

ASC

Beschreibung
Syntax
Beispiel

ASSEMBLER (!)

Beschreibung
Syntax
Beispiel

CSTKPTR

Beschreibung
Syntax
Beispiel

DBNZ

Beschreibung
Syntax
Beispiel

DBNZCTR

Beschreibung
Syntax

DIM

Beschreibung
Syntax

ERR

Beschreibung
Syntax
Beispiel

ERRADR

Beschreibung
Syntax
Beispiel

FILEPOS

Beschreibung
Syntax
Beispiel

FREERAM

Beschreibung
Hinweis
Syntax
Beispiel

FUNCTION

Beschreibung
Syntax
Beispiel
Hinweis

GSTKPTR

Beschreibung
Syntax
Beispiel

INCLUDE

Beschreibung
Syntax
Beispiel

INLINE

Beschreibung
Syntax
Beispiel

IVAR

Beschreibung
Syntax
Beispiel

OPTION

Beschreibung
Syntax
Beispiel

PCADR

Beschreibung
Syntax
Beispiel

POP

Beschreibung
Syntax
Beispiel

PROCEDURE

Beschreibung
Syntax
Beispiel
Hinweis

PUSH

Beschreibung
Syntax
Beispiel
Hinweis

REMOVETOS

Beschreibung
Syntax
Beispiel

REPEAT UNTIL

Beschreibung
Syntax
Beispiel

SUB

Beschreibung
Syntax
Beispiel

WHILE WEND

Beschreibung
Syntax
Beispiel

ZEIGER (^, @)

Beschreibung
Syntax
Beispiel

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
Hinweis

CARRY

Beschreibung
Beispiel

CLOCKDIS

Beschreibung
Beispiel

CONFIG1

Beschreibung
Beispiel

CONFIG2

Beschreibung
Beispiel
Hinweis

CONONERR

Beschreibung
Hinweis
Beispiel

DERRADRH

Beschreibung
Beispiel

DERRADRL

Beschreibung
Beispiel

DFILEPOSH

Beschreibung
Beispiel

DFILEPOSL

Beschreibung
Beispiel

DPCADRH

Beschreibung
Beispiel

DPCADRL

Beschreibung
Beispiel

END2HOST

Beschreibung
Beispiel

FILECHG

Beschreibung
Beispiel

IIARFND

Beschreibung
Beispiel

IRQACT

Beschreibung
Beispiel

IRQREQ

Beschreibung
Beispiel

IRQSET

Beschreibung
Beispiel

PRINTHEX

Beschreibung
Beispiel

PRINTSPC

Beschreibung
Beispiel

PULLUPA

Beschreibung
Verfügbarkeit
Beispiel

PULLUPB

Beschreibung
Verfügbarkeit
Beispiel

REALPOP

Beschreibung
Beispiel

RUNONERR

Beschreibung
Hinweis
Beispiel

UIRADC

Beschreibung
Beispiel

UIRIRQ

Beschreibung
Beispiel

UIRKYB

Beschreibung
Beispiel

UIRSWI

Beschreibung
Beispiel

UIRTIM0

Beschreibung
Beispiel

UIRTIM1

Beschreibung
Beispiel

UIRTOFL

Beschreibung
Beispiel

URILLTOK

Beschreibung
Beispiel

URTOK

Beschreibung
Beispiel

VERSION

Beschreibung
Hinweis
Beispiel

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 Download-Seite der offiziellen OM-Infoside. Sie wird laufend erweitert.

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

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)
http://www.nxp.com/products/microcontrollers-and-processors/more-processors/8-16-bit-mcus/8-bit-hc08/8-bit-eeprom-emulation-q-mcus:HC08Q

Offizielle Webside des Mikrocontrollers MC9S08AW60 (Open-Maxi)
http://www.nxp.com/products/microcontrollers-and-processors/more-processors/8-16-bit-mcus/8-bit-s08/8-bit-general-purpose-aw60-48-32-16-mcus:S08AW

Der AS05-Assembler von Frank A. Vorstenbosch ist der Defacto-Standard auf der C-Control 1.1, kann aber auch auf 68HC908-Mikrocontrollern wie der Open-Micro, Open-Mini, Open-Midi und Open-Macro eingesetzt werden, da die 6808-CPU objektcode- und quellcodekompatibel zur 6805er ist. Die HCS08-CPU des AW60s ist abwärtskompatibel zur 6808. Die Betriebssysteme wurden vollständig mit dem sehr zuverlässigen AS05-Assembler und Macros für die neuen 6808-Befehle erstellt.

Frank's Cross Assemblers
http://www.kingswood-consulting.co.uk/assemblers