


ECO-C - Handbuch |
- Installation
- Schnelleinstieg
- Compiler
- Hilfsprogramme
- Versionsunterschiede
- Systemnahe Programmierung
- Assembler
- Bibliotheken und Initialisierung
- Bei Problemen
Autor: Stefan Vollmar
Copyright © MCT Paul & Scherer Mikrocomputertechnik GmbH. Alle Rechte vorbehalten.
Dieses Handbuch, wie auch das beschriebene Produkt, wurde sorgfältig erstellt und geprüft. Trotzdem können Fehler und Irrtümer nicht ausgeschlossen werden. MCT übernimmt keinerlei Verantwortung für die uneingeschränkte Richtigkeit und Anwendbarkeit des Handbuchs oder des beschriebenen Produkts und für die aus eventuell vorhandenen Fehlern resultierenden Schäden.
Änderungen ohne vorherige Ankündigung vorbehalten.
![]()
1 Installation
1.1 Systemvoraussetzungen
PC mit CPU ab 386
Mindestens 640KB Arbeitsspeicher. Ein kleiner Speicher führt insbesondere bei grossen Programmen zu einer erheblichen Verringerung der Übersetzungsgeschwindigkeit, da die virtuelle Speicherverwaltung bei kleinerem freiem Arbeitsspeicher häufiger Daten auf die Platte auslagern muss. Um auch grosse Programme schnell übersetzen zu können, empfehlen wir einen Arbeitsspeicher mit mindestens 4MB.
Mindestens 5MB freier Plattenspeicher. Zusätzlich ist Plattenplatz für temporäre Dateien vorzusehen, der Bedarf richtet sich nach der Grösse der zu übersetzenden Dateien.
MS-DOS Betriebssystem ab Version 3.3
![]()
1.2 Registrierung
ECO-C wird ständig verbessert und weiterentwickelt. Damit Sie innerhalb des Update-Services an Neuentwicklungen teihaben können, ist es erforderlich, den beiliegenden Registrierungsbogen an MCT zu schicken.
![]()
1.3 Notationen
Da der Compiler und die gesamte Entwicklungsumgebung ursprünglich für Unix geschrieben wurde, werden alle Pfadnamen mit '/' - nicht mit Backslash - getrennt. Alle Backslashes in Environmentvariablen und der Kommandozeile des Compilers werden als erstes in '/' gewandelt. Daher werden alle Pfade mit '/' ausgegeben. In diesem Handbuch wird ebenfalls '/' als Pfadtrenner verwendet, falls dies möglich ist.
![]()
1.4 Installation Schritt für Schritt
Legen Sie die mitgelieferte CD in Ihr CD-ROM-Laufwerk ein.
An der MS-DOS-Eingabeaufforderung wechseln Sie zum Verzeichnis ecoc auf der CD. Dann geben Sie install ein. Wenn Ihr CD-ROM-Laufwerk z.B. D: ist, geben Sie also folgendes ein:
d: cd ecoc installAlle weiteren Angaben werden vom Installationsprogramm interaktiv erfragt. Zunächst werden Sie nach dem Pfad gefragt, in dem ECO-C installiert werden soll. Hierbei ist es wichtig, den absoluten Pfadnamen einschliesslich Laufwerk anzugeben, z.B.:
c:\ecocDamit wird ECO-C im Verzeichnis c:\ecoc installiert. Falls dieses Verzeichnis noch nicht existiert, wird es automatisch angelegt. Existiert das Verzeichnis bereits, wird nachgefragt, ob dieses Verzeichnis überschrieben werden soll. Die komprimierten Dateien werden entpackt und installiert. Wenn das Installationsprogramm Sie auffordert, die zweite Diskette einzulegen, drücken Sie einfach Enter.
Als nächstes werden Sie nach der Zielhardware gefragt, für die normalerweise (d.h. ohne spezielle Angaben) Programme erzeugt werden sollen. Eine Liste der implementierten Zielhardware wird angezeigt.
Nach Eingabe des Targetnamens z.B. "sc332" wird die Datei ecoc.bat nach c:\ecoc geschrieben. Ihr Inhalt, im Beispiel:
@ECHO OFF ECHO "" ECHO Setze ECO-C Umgebung ECHO "" SET PATH=c:\ecoc\bin;%PATH% SET ECODIR=c:/ecoc SET ECOTARGET=sc332Am besten rufen Sie "ecoc.bat" in Ihrer autoexec.bat mit
call c:\ecoc\ecoc.batauf. Der Environment Eintrag ECODIR ist für alle Compilerphasen erforderlich, damit die einzelnen Programme, sowie Include- und Library-Dateien gefunden werden.
Nach dem Editieren von autoexec.bat ist die Installation beendet.
Sollte jetzt beim Booten die Fehlermeldung "Kein Speicherplatz mehr im Umgebungsbereich" auftreten, ergänzen Sie config.sys um folgende Zeile:
shell=command.com /e:1024 /pDamit wird der Umgebungsbereich (auch als Environment bezeichnet) auf ein Kilobyte vergrössert
![]()
1.5 Aktuelle Version
In der Datei doc/news.txt finden Sie eine Zusammenfassung der Änderungen und Erweiterungen bis zur aktuellen Version. Die höchste, zuerst genannte Versionsnummer bezeichnet die aktuelle Version. In diesem Handbuch wird eventuell nicht die aktuellste Version beschrieben, in diesem Fall gelten die Angaben in der Datei.
![]()
1.6 Systembeschränkungen
Der C-Compiler, Assembler und Linker des ECO-C Crosscompiler Pakets laufen im Protected-Mode des 80386. Damit ist es möglich, nahezu beliebig grosse Programme zu schreiben oder sehr grosse Datenmengen zu verwalten. Diese besonderen Vorzüge kommen ECO-C zugute, indem es keinerlei Begrenzungen bezüglich Modulgrösse und Verschachtelungstiefe gibt. Desweiteren können umfangreiche Optimierungsstrategien angewendet werden, die ansonsten wegen Speichermangels nicht realisiert werden könnten.
![]()
1.7 Copyright
Für die Programme finden die Copyrightbestimmungen der FSF Anwendung, sie finden diese in der Datei doc/copying. Entsprechend den Copyrightbestimmungen der FSF (copyleft) sind die Quelltexte der Programme auf Anfrage gegen eine Unkostenpauschale erhältlich.
Ausgenommen davon sind folgende Utilities (Copyright MCT):
- Das Terminalprogramm tm
- Das Konvertierprogramm cnvaout
- Der ROM-File Generator mkrom
Die Bibliotheken und der C-Start können ohne urheberrechtliche Beschränkungen (Objektcode und Quelltexte) frei benutzt werden. Das Copyright liegt beim jeweiligen Copyrightinhaber.
![]()
2 Schnelleinstieg
0x0d2c
May all your signals trap,
may your references be bounded
All memory aligned,
floats to ints be rounded.Remember...
Nonzero is TRUE,
++ adds one.
Arrays start with [0],
NULL points to none.For octal use zero,
0x means in hex.
Use = to set
and == for a test.Use -> for a pointer,
a dot if it's not.
?: is confusing,
use this a lot!a.out is your program,
there's no 'u' in foobar
and char (*(*x())[])() is
a function returning a pointer
to an array of pointers
to functions returning a char.Gedicht eines unbekannten C Liebhabers
Um es Ihnen möglichst schnell zu ermöglichen, mit ECO-C eigene Applikationen zu realisieren, werden in den folgenden Abschnitten zwei Beispiele mit steigendem Schwierigkeitsgrad ausführlich besprochen. Sie dienen gleichzeitig dazu, die umfangreichen Möglichkeiten von ECO-C zu demonstrieren. Jedoch versteht sich dieses Kapitel weder als Einführung in C, noch werden hier alle erwähnten Details von ECO-C vollständig erklärt, dazu dienen die folgenden Kapitel.
Falls Sie Programme für einen nicht implementierten Zielrechner entwickeln wollen, ist es zunächst nötig, die systemabhängige Bibliothek entsprechend anzupassen. Sehen Sie dazu auch das Kapitel Hardwareanpassung.
![]()
2.1 Ein kleines Beispiel
Wie nicht anders zu erwarten, hier das klassische erste Programm:
#include <sys/nofloat.h> #include <stdio.h> main() { while(1) printf("Hello world.\n"); }Es dient im wesentlichen dazu, Ihnen den Umgang mit den einzelnen Programmen zu demonstrieren. Sie finden es, wie alle Beispiele, in ECODIR im Unterverzeichnis samples unter dem Namen hello.c. Wechseln Sie zunächst in dieses Verzeichnis und compilieren Sie wie folgt:
ecoc hello.c -o hello"hello" wurde damit erzeugt, es ist das fertig gelinkte Programm. Die Standardeinstellung des Compilers erlaubt es, dieses direkt auf die Standard-Zielhardware zu laden und dort zu starten. Im Normalfall geschieht dies mit "tm", dem Terminalprogramm (Com1 mit 19200Baud ist default), oder einfach mit dem Batchprogramm "download.bat":
download helloDamit ist, falls ihr Zielrechner korrekt angeschlossen ist, das Programm geladen. Gestartet wird es mit dem entsprechenden Monitorkommando, bei NICO mit "sp", daraufhin sollte "Hello world." auf Ihrem Monitor erscheinen. Die Ausgabe erfolgt zu Demonstrationszwecken in einer Endlosschleife. Mit einer Control-C Eingabe lässt sich jedes Programm während der Ein- oder Ausgabe abbrechen, genau so wie sie es von ihrem PC gewohnt sind.
In der Includedatei "sys/nofloat.h" werden die Funktionen zur formatierten Eingabe und Ausgabe als die entsprechenden Versionen ohne Fliesskommaunterstützung definert. Dies macht die Programme kleiner, verhindert aber die Ein- und Ausgabe von Fliesskommazahlen. Diese Includedatei sollte immer verwendet werden, wenn keine Fliesskommazahlen benutzt werden.
Um das gleiche Progamm in ein EPROM zu brennen, ist folgende Kommandosequenz erforderlich:
ecoc -rom hello.c mkrom a.out hello.romDas gelinkte Programm heisst "a.out", wenn kein Name mit "-o filename" explizit angegeben wurde. Die erzeugte Datei "hello.rom" ist das im ROM ausführbare Programm im Binärformat. Es enthält die Resetvektoren und alle nötigen Initialisierungsfunktionen. Die Datei kann mit jedem gängigen EPROM-Programiergerät ins EPROM gebrannt werden.
![]()
2.2 Ein Zähler
Diese Beispiel ist schon etwas umfangreicher, es ist ein 8Bit-Zähler, der über die time-Funktion seinen Wert um eins erhöht. Dieser Wert wird über stdout als String ausgegeben und gleichzeitig auf einen hardwarespezifischen 8Bit-Port ausgegeben. Die Libraryfunktion "time" verwendet die rechnereigene RTC, falls vorhanden, oder die Timerperipherie.
Das Beispiel zeigt, wie einfach ein Zugriff auf die Peripherie im Adressraum des Prozessors ist. Die systemspezifischen Adressen sind mittels Preprozessormakros definiert, damit erreicht man eine einfache Anpassung an eine neue Hardware. Es ist zu empfehlen, alle Adressen und andere Informationen des Systems in einer Headerdatei zu halten, Beispiele dazu finden Sie in den mitgelieferten Headerdateien "target.h".
/* * Zählerbeispiel */ #include <stdio.h> #include <sys/nofloat.h> #include <time.h> #include <target.h> #if defined scotty08 || defined sc332 /* defined in target.h */ #define PORT (OPb) /* Pin A19 - A26 auf VG-Leiste */ #define INIT #elif defined mega301 #define PORT (IO_REG_BLK.pio_data) /* ungerade Pins 3 - 17 auf ST2 */ #define INIT (IO_REG_BLK.pio_ctl = 0, IO_REG_BLK.pio_dir = 0xff) #elif defined mega340 #define PORT (INTERN.sim.porta) /* Port A auf Pin 51 - 58 von J31 */ #define INIT (INTERN.sim.ppara1 = 0xff, INTERN.sim.ddra = 0xff) #else /* define dummys */ #define PORT (puts("output value on port"), count) #define INIT puts("initialize port") #endif void main(void) { int count; time_t oldtime = 0; /* Init-Makro ausführen. */ INIT; /* Die Schleife durchläuft einmal jede 8Bit Kombination. */ for(count = 0; count < 256; count++) { /* Warte bis eine Sekunde vergangen ist. */ while(oldtime == time(NULL)) ; /* Aktuelle Zeit setzen. */ oldtime = time(NULL); /* Port auf neuen Wert setzen. */ PORT = count; /* Wert und Zeit ausgeben. */ printf("Count: %d; Zeit: %s", count, ctime(&oldtime)); } }Um das Programm zu übersetzen, müssen Sie die hardwareabhängigen Teile für Ihre Hardware implementieren.
ecoc -s count.c -o countDer "-s" Switch gibt an, dass keine Symboltabelle generiert wird. Das macht das Programm kürzer und den Download schneller.
![]()
2.3 Beispiele
Weitere Beispiele befinden sich im Verzeichnis samples, unter anderem:
clock Beispiel zum Timerinterrupt. Der Gangunterschied zwischen RTC und Timer wird angezeigt und in jedem Interrupt ein Ausgabeport inkrementiert. dhry Der bekannte Dhrystone-Benchmark. Wenn mit -DFAST compiliert wird, werden alle Funktionen des Benchmarks als inline definiert und Integervariablen erhalten den Type short (16Bit gross). Nach dem Start des Programms werden Sie nach der Anzahl der Durchläufe gefragt. Das Programm meldet sich nach der Abarbeitung mit dem errechneten Wert oder einer Fehlermeldung, wenn die Anzahl der Durchläufe zu gering war. Hier die Werte für einige Zielsysteme (ohne -DFAST übersetzt): Der Dhrystonewert enspricht der Anzahl der Durchläufe je Sekunde.
Targetname Dhrystone CPU-Kern zwerg332
scotty08
scotty332
mega301
mega332
mega3402060
570
3560
2100
3560
3560CPU32
68008
CPU32
68000
CPU32
CPU32
lcd_alfa Ein Demoprogramm zur Ansteuerung von alphanumerischen LC-Displays über den TLX-Bus. Dabei kommt auch die dynamische Deviceverwaltung von ECO-C zur Anwendung. Um dieses Beispiel zu benutzen, benötigen Sie eine eine kleine Logikschaltung zwischen dem TLX-Bus und der alphanumerischen Anzeige. lcd_graf Beispielprogramm zur Ansteuerung von grafischen LC-Displays. loopback Datenstrom einer seriellen Schnittstelle umlenken (z.B. "tty0" als Eingang und "tty2" als Ausgang). mathlib Beispielprogramm zur Bestimmung von Rundungsfehlern und der Laufzeit der mathematischen Bibliotheksfunktionen. noblock Zeichen direkt von einer seriellen Schnittstelle einlesen (ohne Return). recinter Zeichen von einer seriellen Schnittstelle im Interruptmode einlesen, so dass keine Zeichen verloren gehen. Interrupthandler für die sofortige Interpretation eines vom Programmierer festgelegten Zeichens im Interruptbetrieb. Im Programmbeispiel wird das Zeichen 'U' verwendet. signal Beispielprogramm zur Installation eines Ctrl-C- und Ctrl-Backslash-Handlers unter ECO-C. tlxexcep Beispiel für einen Exceptionhandler mit "#pragma interrupt". Der TLX-Interrupteingang erzeugt die Exceptions und ist entsprechend der Hardwarebeschreibung zu bedienen. Im Verzeichnis samples gibt es für jedes Zielsystem ein eigenes Unterverzeichnis. Damit ist es möglich, für verschiedene Zielsysteme gleichzeitig die Beispiele zu generieren. Das Makefile erlaubt folgende Maketargets:
l f=progname Das Programm progname wird erzeugt und auf das Zielsystem geladen. local Alle Programme werden generiert. Ein Aufruf von make ohne Maketarget erzeugt ebenfalls alle Programme. localrom Alle Programme werden für die Ausführung im ROM generiert. localclean Alle mit local erzeugten Dateien werden gelöscht. all Alle Programme, auch in den Unterverzeichnissen, werden generiert. allrom Alle Programme, auch in den Unterverzeichnissen, werden für die Ausführung im ROM generiert, sie erhalten die Endung .rom. clean Alle erzeugten Dateien, auch in den Unterverzeichnissen, werden gelöscht. Make kann auch in den targetspezifischen Unterverzeichnissen aufgerufen werden. Die einzelnen Aktionen werden - unabhängig davon ob sie in der globalen oder lokalen Variante aufgerufen werden - nur im aktuellen Verzeichnis ausgeführt.
![]()
3 Compiler
Die Beschreibung ist nur eine Auswahl. Zur vollständigen Dokumentation dient die mitgelieferte englische GNU-Dokumentation zu gcc, cpp, as und ld.
![]()
3.1 Kommandozeilenoptionen
Die hier aufgelisteten Kommandozeilenoptionen gelten für den Aufruf von ecoc, die einzelnen Compilerphasen besitzen zum Teil andere Aufrufparameter. Erwartet eine Option einen Parameter, dann kann dieser direkt folgen, oder durch Leerzeichen getrennt sein. Die Reihenfolge von Optionen und Dateinamen ist beliebig.
![]()
3.1.1 Globale Optionen
-2filename Alle Fehlermeldungen werden in die Datei filename geschrieben. -c Unterdrückt das Linken der Objektdateien. -E Nur der Preprozessor wird ausgeführt. -mmachinespec Die folgenden Schalter beeinflussen die prozessorspezifische Codegenerierung. Alle hier genannten Schalter beeinflussen alle Compilerphasen. -m68000 Es wird Code für MC68000/MC68010 erzeugt, dies ist Standard. -m68020 Es wird Code für MC68020 erzeugt. -m68030 Es wird Code für MC68030 erzeugt. -m68040 Es wird Code für MC68040 erzeugt. -m68020-40 Es wird Code für die Prozessoren MC68020, MC68030 und MC68040 erzeugt. Der Code ist für den MC68040 optimiert. -mcpu32 Es wird Code für die CPU32 (Prozessor der Controller MC6833X, MC6834X und MC6836X) erzeugt. -M Es wird eine Abhängigkeitsliste von Includedateien für make erzeugt, dazu werden die Dateinamen in den #include Direktiven vom Preprozessor ausgewertet. -MM Es wird eine Abhängigkeitsliste von Includedateien für make erzeugt, dabei werden nur lokale Includedateien (#include "filename") ausgewertet. -ofilename Die erzeugte Datei erhält den Namen filename. -S Nur Preprozessor und Compiler werden ausgeführt, die Ausgabe erhält - wenn nichts anderes angegeben - den Namen der Eingabedatei mit der Extension ".s". -target targetname Es wird für das Zielsystem targetname (statt für das vorinstallierte bzw. durch die Environmentvariable ECOTARGET spezifizierte System) übersetzt. -v Der Compilertreiber gibt für jedes ausgeführte Kommando die Kommandozeile aus. Die einzelnen Compilerphasen melden sich mit der Versionsnummer. ![]()
3.1.2 Preprozessor
-IDirectory Das Verzeichnis Directory wird nach Includedateien durchsucht. -iFilename Die Datei Filename wird vor allen anderen Dateien vom Preprozessor gelesen. -nostdinc Die Standardverzeichnisse, spezifiziert durch ECODIR und ECOINC, werden nicht nach Includedateien durchsucht. -DMACRO Definiert MACRO als 1. -DMACRO=DEFN Definiert MACRO als DEFN. -UMACRO Entspricht der Preprozessordirektive #undef MACRO. ![]()
3.1.3 C-Compiler
-ansi Diese Option blockiert alle Erweiterungen von ECO-C, die mit dem ANSI-Standard kollidieren. Um die Erweiterungen von ECO-C zu verwenden, müssen diese als __asm__, __inline__ und __typeof__ geschrieben werden. Ausserdem wird das Makro __STRICT_ANSI__ definiert. -traditional Dieser Schalter wird benötig, um zu 'traditionelleren' Compilern kompatibel zu sein:
- Alle externen Deklarationen sind global, auch wenn sie in Funktionen auftreten.
- Die Schlüsselwörter const, inline, signed, typeof und volatile sind unbekannt.
- Der Vergleich zwischen Zeigern und Integer-Variablen ist immer erlaubt.
- Die Typen unsigned char und unsigned short werden auf unsigned int erweitert.
- Stringkonstanten müssen nicht konstant sein, sie werden im Datensegment allokiert.
- Der Preprozessor expandiert Argumente von Makros in Stringkonstanten der Makrodefinition.
- Das Makro __STDC__ ist nicht definiert.
-g Es wird die nötige Information für einen Sourceleveldebugger erzeugt. -O0 Es wird keine Optimierung vorgenommen. Manchmal kann es sinnvoll sein, mit -O0 zu übersetzen, da der generierte Assemblercode dann leichter zu verstehen ist. -O/-O1 Die verschiedenen Optimierungsphasen des Compilers werden eingeschaltet. Die Prozessorregister werden automatisch zugeordnet, Sprünge und Schleifen optimiert, Schleifeninvarianten erkannt und herausgenommen. Diese Optimierung ist standardmässig eingeschaltet. Die Übersetzungszeit erhöht sich geringfügig mit dieser Option, jedoch kann es durch den erheblich höheren Speicherplatzbedarf zur Auslagerung auf die Harddisk kommen, was zu einer drastischen Verlangsamung führt. -O2 Verschiedene zeitaufwendigere Optimierungen werden zusätzlich ausgeführt. -w Keine Warnungen ausgeben. -W Folgende Warnungen werden zusätzlich ausgegeben:
- Eine automatische Variable wird benutzt bevor sie initialisiert wird.
- Kein Resultat wird zurückgegeben.
- Ein C-Ausdruck ohne Effekt.
-Wimplicit Eine Funktion ist implizit deklariert. -Wunused Eine lokale Variable wird nicht benutzt. -Wcomment Ein Kommentaranfang /* in einem Kommentar. -Wall Alle Warnungen werden ausgegeben. -mmachinespec Die folgenden Schalter beeinflussen die prozessorspezifische Codegenerierung. Sie werden alle mit -m eingeleitet. -m68881 Es wird Code für die Fliesskommacoprozessoren MC68881 und MC68882 erzeugt, statt die entsprechenden Libraryfunktionen zu verwenden. Soll auch für die Fliesskommafunktionen (z.B. sin(), log()) die Coprozessorbefehle verwendet werden, muss die Headerdatei sys/math_688.h included werden. -minterrupt Funktionen sichern zusätzlich die Register D0, D1, A0 und A1, und werden mit rte statt rts beendet. Damit können Funktionen in Modulen, die mit diesem Switch übersetzt werden, direkt als Exceptionhandler dienen. Ein direkter Aufruf einer Exceptionhandler-Funktion ist nicht möglich (der Stack würde durcheinander geraten). __INTERRUPT__ wird definiert wenn mit dieser Option übersetzt wird. -msoft-float Die Libraryfunktionen für Fliesskommaarithmetik werden verwendet, dies ist Standard. -mshort Der Type int ist 16Bit statt 32Bit lang. Module die mit dieser Option übersetzt wurden, können nicht mit standard (32Bit int) Funktionen und Bibliotheken gelinkt werden, da Parameter auf dem Stack nicht auf mindestens 4 Byte, sondern auf mindestens 2 Byte erweitert werden. -mnobitfield Keine Bitfield-Instruktionen verwenden. Dies ist nötig, um Code für Mikrocontroller mit dem CPU32-Prozessorkern von Motorola zu erzeugen (-mcpu32 beinhaltet diese Funktion). -fflag Die -f Optionen sind für prozessorunabhängige Compilereinstellungen. -ffloat-store Fliesskommazahlen werden nicht in den Coprozessorregistern gehalten, sondern nach jeder Rechenoperation als 64Bit-Zahl abgespeichert. Dies ist nötig, um zu verhindern, dass Zwischenergebnisse eine höhere Genauigkeit besitzen und damit die Rechenergebnisse sich gegenüber der Berechnung im 64Bit-IEEE-Format unterscheiden. -fno-asm Die Schlüsselwörter asm, inline und typeof werden durch __asm__, __inline__ und __typeof__ ersetzt. -fomit-frame-pointer Die Benutzung des Framepointers A6 wird immer unterdrückt - wenn möglich. Dies erzeugt schnelleren Code (kein link und unlink), kann aber den Einsatz eines Debuggers verhindern. -finline-functions Erzeugt automatisch Inline-Code für kleine Funktionen im gleichen Modul. -fwritable-strings Stringkonstanten werden im schreibbaren Datensegment statt im Textsegment abgelegt, doch ist es kein guter Programmierstil in Stringkonstanten zu schreiben. -fvolatile Alle Speicherreferenzen werden als volatile interpretiert. Dies ist nützlich, wenn auf Peripherieregister, die sich ändern können, zugegriffen wird. Es ist jedoch besser, solche Peripherieregister als volatile zu deklarieren, da der Compiler mit diesem Schalter bestimmte Optimierungen nicht ausführen kann. -funsigned-char Mit diesem Schalter ist der Type char vorzeichenlos, die Standardeinstellung ist mit Vorzeichen (signed). ![]()
3.1.4 Assembler
-aa Es werden sowohl Assemblerlisting als auch ein Symbollisting ausgegeben. -al Es wird ein Assemblerlisting ausgegeben. -as Es wird ein Symbollisting ausgegeben. -ac In den Bereichen von Assemblerquellcode die mit #APP definiert sind, wird das Zeichen '|' als bitweise Veroderung interpretiert und als Zeichen für den Anfang eines Kommentars wird das Zeichen ';' benutzt. Normalerweise wird das Zeichen '|' als Kommentarzeichen verwendet und die bitweise Veroderung ist daher nicht möglich. Dieser Schalter ist nur notwendig, wenn Assemblerquellcodes direkt übersetzt werden sollen, während mit #APP der Preprozessor des Assemblers aktiviert ist. -R Das Datensegment wird in das Textsegment integriert. Dies verkleinert und beschleunigt den Code, da globale Daten PC relativ adressiert werden können, ist aber nur möglich, wenn das Textsegment schreibbar ist, sich also nicht im ROM befindet. ![]()
3.1.5 Linker
Einige Optionen in disem Abschnitt erfordern numerische Parameter. Diese werden wie in C üblich interpretiert, d.h. mit 0 beginnende Zahlen sind oktal und mit 0x beginnende Zahlen sind hexadezimal.
-am Es wird eine Linkermap auf der Standardausgabe ausgegeben. -copydata Mit diesem Switch wird statt des Standard C-Start crt0.o, cpy0.o als C-Start verwendet. Dieser hat die zusätztliche Aufgabe, eine Kopie des Datensegments im Speicher anzulegen, um bei einem erneuten Programmstart Die Daten wieder in den Initialisierungszustand bringen zu können. Dies ist immer dann erforderlich, wenn ein Programm im RAM mehrfach ausgeführt werden soll. -d Die Daten im BSS werden allokiert. Dies ist nur dann erforderlich, wenn relozierbarer Output erzeugt wird, da dann normalerweise die BSS-Daten nicht allokiert werden. -lLibrary Die Bibliothek Library wird vor den Standardbibliotheken an den Linker übergeben. Der Name der Bibliothek ist libLibrary.a. Wird z.B. -lmy eingegeben, sucht der Linker nach libmy.a. -LDirectory Das Verzeichnis Directory wird vor allen anderen Verzeichnissen nach den Bibliotheken, dem C-Start, dem Linkerscript und der specs-Datei durchsucht. -nostartfiles Anstatt des C-Starts wird die erste spezifizierte Objektdatei verwendet. -nostdlib Der Standard C-Start und die drei Standardbibliotheken werden nicht dazugelinkt. -r Die Relozierinformation wird in die Ausgabedatei geschrieben. -rom Das Programm wird für das ROM gelinkt. Dazu ist ein anderer C-Start und ein anderes Linkerscript notwendig. Der a.out Header muss anschliessend noch mit dem Programm mkrom entfernt werden. -s Keine Symboltabelle. -t Alle Dateien die zum Programm gebunden werden, werden aufgelistet. Wenn Dateien aus Bibliotheken verwendet werden, wird die Bibliothek und dahinter in Klammern der Dateiname ausgegeben. -T Linkerscript Die Datei Linkerscript wird statt der Standarddatei als Steuerdatei für den Linker verwendet. Der Suchpfad für Bibliotheken wird ebenfalls nach der Datei durchsucht, dabei wird ein .ld an den angegebenen Namen angehängt. ![]()
3.2 Vordefinierte Makros
Neben den von ANSI vorgegebenen Makros __LINE__, __FILE__, __DATE__, __TIME__ und __STDC__ sind folgende Makros von ECO-C definiert:
__GNUC__ Dieses Makro ist bei allen GNU-GCC-Derivaten definiert. Damit wird angegeben, dass der Compiler über die GNU spezifischen Erweiterungen verfügt. __ECOC__ Dieses Makro wird von ECO-C mit der aktuellen Versionsnummer definiert. Es kann daher zum Abfragen der Version dienen, z.B.: Die Nummer '202' für Version '2.02'. __STRICT_ANSI__ Dieses Makro ist definiert, wenn -ansi als Option angegeben ist. __OPTIMIZE__ Dieses Makro ist definiert, wenn die Optimierung (-O) eingeschaltet ist. __INTERRUPT__ Diese Makro ist definiert, wenn mit -minterrupt übersetzt wird. __M68K__ Dieses Makro ist definiert, wenn Code für einen Prozessor der MC68XXX Familie erzeugt wird. __MC68000__ Dieses Makro ist definiert, wenn Code für den Prozessor MC68000 oder MC68008 erzeugt wird. __MC68010__ Dieses Makro ist definiert, wenn Code für den Prozessor MC68010 erzeugt wird. __MC68020__ Dieses Makro ist definiert, wenn Code für den Prozessor MC68020 erzeugt wird. __MC68030__ Dieses Makro ist definiert, wenn Code für den Prozessor MC68030 erzeugt wird. __MC68040__ Dieses Makro ist definiert, wenn Code für den Prozessor MC68040 erzeugt wird. __CPU32__ Dieses Makro ist definiert, wenn Code für einen Controller mit CPU32-Kern (z.B. MC68332, MC68340) erzeugt wird. ![]()
3.3 Targetumschaltung und Suchpfade
Die folgende Beschreibung ist nur dann für Sie interessant, wenn Sie für einen anderen als den standardmässig installierten Rechner Programme compilieren wollen.
Die Targetumschaltung erfolgt mit der Environmentvariable ECOTARGET oder mit dem Switch -target der die Environmentvariable überschreibt. Intern erfolgt die Umschaltung durch Verändern der Standardsuchpfade. Diese werden aus den beiden Environmentvariablen ECODIR und ECOTARGET gebildet. Gross- und Kleinschreibung werden nicht unterschieden. ECODIR ist der Pfad des Verzeichnisses in dem das Compilerpaket installiert ist. ECOTARGET spezifiziert die Hardware, die mit drei Teilen, die durch einen Doppelpunkt getrennt sind, angegeben wird: targetsystem:targetcpu:cpufamily
targetsystem Ist der Name des Hardwaresystems, z.B. scotty08, mega340. targetcpu Ist der Name des CPU-Kerns, z.B. mc68000, cpu32. cpufamily Ist der Name der CPU-Familie, z.B. m68k. Beispiel:
ECODIR=C:/ECOCFür die mitgelieferten Targetanpassungen kann auch einfach nur der Targetname angegeben werden. Folgende Kurznamen sind z.Z. implementiert:
Kurzname Systembeschreibung zwerg332
scotty08
sc332
mega301
mega332
mega340zwerg332:cpu32:m68k
scotty08:mc68000:m68k
sc332:cpu32:m68k
mega301:mc68000:m68k
mega332:cpu32:m68k
mega340:cpu32:m68kBei der Targetspezifizierung wird nicht zwischen Gross- und Kleinschreibung unterschieden.
Programme
Der Compilertreiber "ecoc" sucht die Programme der einzelnen Compilerphasen nacheinander in folgenden Verzeichnissen:
In den mit dem Kommandozeilenswitch -B path angebenen Pfaden.
Das Verzeichnis das aus dem Environmenteintrag ECODIR mit angehängtem /BIN/ und dem Cpufamilyteil von ECOTARGET zusammengesetzt ist. Im Beispiel: C:/ECOC/BIN/M68K
Das Verzeichnis das aus dem Environmenteintrag ECODIR und angehängtem /BIN zusammengesetzt ist. Im Beispiel: C:/ECOC/BIN
Bibliotheken, C-Start, Linkerscript und Specsfile
Der Linker sucht die Bibliotheken, den C-Start, Linkerscript und Specsfile nacheinander in folgenden Verzeichnissen:
Die Verzeichnisse die mit dem -B Switch angegeben sind.
Die Verzeichnisse die mit dem -L Switch angegeben sind.
Das Verzeichnis das aus dem Environmenteintrag ECODIR mit angehängtem /LIB/ und dem Targetsystemteil von ECOTARGET zusammengesetzt ist. Im Beispiel: C:/ECOC/LIB/MEGA301
Das Verzeichnis das aus dem Environmenteintrag ECODIR mit angehängtem /LIB/ und dem Targetcputeil von ECOTARGET zusammengesetzt ist. Im Beispiel: C:/ECOC/LIB/MC68000
Das Verzeichnis das aus dem Environmenteintrag ECODIR mit angehängtem /LIB/ und dem Cpufamilyteil von ECOTARGET zusammengesetzt ist. Im Beispiel: C:/ECOC/LIB/M68K
Das Verzeichnis das aus dem Environmenteintrag ECODIR und angehängtem /LIB zusammengesetzt ist. Im Beispiel: C:/ECOC/LIB
Includedateien
Der Preprozessor sucht die Includedateien nacheinander in folgenden Verzeichnissen:
Die Verzeichnisse die mit dem -I Switch angegeben sind.
Das Verzeichnis das aus dem Environmenteintrag ECODIR mit angehängtem /INCLUDE/ und dem Targetsystemteil von ECOTARGET zusammengesetzt ist. Im Beispiel: C:/ECOC/INCLUDE/MEGA301.
Das Verzeichnis das aus dem Environmenteintrag ECODIR und /INCLUDE zusammengesetzt ist. Im Beispiel: C:/ECOC/INCLUDE.
Temporäre Dateien
Der Compiler verwendet für temporäre Dateien den ersten existierenden Environmenteintrag aus folgender Liste:
- Der Environmenteintrag TMPDIR.
- Der Environmenteintrag TEMP.
- Der Environmenteintrag ECODIR mit angehängtem /tmp.
![]()
3.4 Compilerphasen
Der Compiler besteht aus dem Compilertreiber (ecoc.exe) und vier Compilerphasen:
cpp.exe Der C-Preprozessor. cc1.exe Der eigentliche Compiler inclusive Optimierer. as.exe Der Assembler. ld.exe Der Linker. Die einzelnen Phasen sollten nicht direkt ausgeführt werden. Um Namenskonflikte mit anderen Compilerpaketen zu vermeiden werden sie über die Environmentvariable ECODIR gesucht, statt über PATH. Der Compilertreiber ecoc startet die einzelnen Programme automatisch entsprechend der Kommandozeile. Dabei wird ausser den angegebenen Optionen die Bearbeitung der Files über deren Extension gesteuert, sie haben folgende Bedeutung:
.c diese Dateien sind C-Quellprogramme, die zuvor den Preprozessor durchlaufen. .s diese Dateien sind Assembler-Quelltexte, sie werden assembliert. .* alle anderen Endungen werden als Objektfiles interpretiert, sind also Linkereingabedateien. Damit ist es möglich, Dateien der verschiedenen Typen mit einem Kommando zu einem Programm zu binden, z.B.:
ecoc main.c handle.s util.o mylib.a -o test![]()
3.5 Erweiterungen
ECO-C besitzt gegenüber dem ANSI-Standard einige interessante Erweiterungen. Mit -pedantic gibt ECO-C für jede verwendete Erweiterung eine Warnung aus. Um portablen Code zu schreiben ist es möglich, diese Erweiterungen nur dann zu verwenden wenn __ECOC__ definiert ist.
Interruptroutinen
Die Generierung von Interruptfunktionen ist mit dem #pragma Preprozessorkommando möglich, z.B:
#pragma interrupt int exceptcount; void tlxhandle(void) { exceptcount++; } #pragma endinterruptDas vollständige Beispiel findet sich in der Datei samples/tlxexcep.c.
Das "#pragma interrupt" Kommando schaltet die Generierung von interruptfähigem Code ein, d.h. auch die temporären Register werden gerettet und die Funktion mit rte statt rts beendet. Mit "#pragma endinterrupt" wird wieder auf die normale Codegenerierung zurückgeschlatet. Die Umschaltung darf nur ausserhalb von Funktionen erfolgen. Die Funktionen dürfen nicht direkt aufgerufen werden, sondern dienen ausschliesslich als Interrupthandler.
Die Verwendung dieser #pragma Kommandos ersetzt die Komanndozeilenoption -minterrupt.
Zusammengesetzte Anweisungen in Ausdrücken
ECO-C ermöglicht es, zusammengesetzte Anweisungen (K&R: compound- statement) in Ausdrücken (K&R: expression) zu verwenden. Damit können Variablen innerhalb von Ausdrücken verwendet werden. Diese Erweiterung ist für Makrodefinitionen sehr nützlich. Als Beispiel hier eine Implementierung des MAX Makros, das einen Seiteneffekt vermeidet:
#define MAX(x,y) ({ int _x=(x), _y=(y); _x > _y ? _x : _y; })Benennen des Typs eines Ausdrucks
Es ist möglich, den Typ eines Ausdrucks zu benennen:
typedef name=ausdruck; name variable;Damit ist name der Type von ausdruck. Mit diesem Beispiel ist es möglich, variable den Type von ausdruck zu geben.
typeof Schlüsselwort
Mit dem Schlüsselwort typeof lässt sich das Beispiel aus dem letzten Abschnitt verkürzen auf:
typeof (ausdruck) variable;Um einen Zeiger auf ein beliebiges Objekt zu erhalten, kann man schreiben:
typeof (objekt) *pointer;Damit ist pointer ein Zeiger auf objekt.
Arrays der Länge Null
Arrays mit der Länge Null lassen sich als Header für ein Array verwenden, das zur Laufzeit allokiert wird.
Arrays mit variabler Länge
Ein interessantes Feature von ECO-C sind Autoarrays mit variabler Länge. Hier ein Beispiel, um den Gebrauch zu veranschaulichen:
void func(int *array, int len) { int tmparray[len]; ... }Arithmetik auf Void- und Funktions-Zeigern
Es ist möglich, mit diesen Zeigern Additionen und Subtraktionen durchzuführen. Dabei wird angenommen, dass sie ein Objekt mit Länge 1 referenzieren.
Inline Funktionen
Eine Funktion kann als inline deklariert werden, um ihren Code in die aufrufende Funktion zu integrieren. Der Overhead für den Funktionsaufruf wird damit gespart, und die Funktion wird damit ebenso schnell wie ein Makro. Wenn die Funktion zusätzlich als static deklariert ist, wird keine eigene Funktion erzeugt, da keine externe Referenz darauf zugelassen ist. Ist die Funktion als inline und zusätzlich als extern deklariert, wird sie keinesfalls erzeugt, selbst wenn ihre Adresse bestimmt wird.
Inlineassembler
Der Inlineassembler hat folgende Syntax:
asm(" ein Assembler Kommando noch eine Zeile Assembler ");Das asm Statement kann überall, auch ausserhalb von Funktionen stehen. Der Preprozessor bearbeitet den Assemblertext nicht - wie jeden anderen String. Der Compiler reicht den String an den Assembler durch, dabei muss '"' mit '\"' und '\' mit '\\' dargestellt werden, ausserdem erfolgt eine Substitution von '%', wie im folgenden beschrieben.
Hier ein Beispiel für eine Funktion, um die Interrupts im Statusregister zu sperren. Die Funktion kann mit disable_int() aufgerufen werden (kein Unterstrich zu Beginn des Funktionsnamens, da dieser vom Compiler erzeugt wird).
asm(" .text .even .globl _disable_int _disable_int: movew #0x2700,sr rts ");ECO-C bietet eine sehr komfortable Möglichkeit, Assemblercode in C-Code zu integrieren und dabei bestimmte Details (wie Registerallokierung) dem Compiler zu überlassen. Ausserdem ist es möglich, auf alle C-Variablen (auch Auto-, Registervariablen) mit ihrem Namen zuzugreifen. Die Kommunikation zwischen Assembler und C erfolgt über zusätzliche Parameter im asm Statment.
Im Beispiel werden zwei short int Variablen zu einer int Variablen multipliziert, dies geschieht aus Effizienzgründen mit Inline-Assembler. In C würden die short int Variablen zuerst auf int erweitert, und dann eine Funktion zur Multiplikation von Integervariablen aufgerufen werden. Volatile zeigt dem Compiler an, dass keine Optimierung des Assemblerausdrucks erfolgen soll.
asm volatile("mul %1,%0" : "=d" (result) : "0" (result), "g" (mult));Diese Syntax scheint zunächst etwas kryptisch, erweist sich aber bei genauerem Hinsehen als sehr zweckmässig. Der schematische Aufbau ist folgender:
asm("Assemblermnemo %Opnummer,%Opnummer" : "=Optype" (Variable0) : "=Op" (Variable0), "Optype" (Variable1));Es sind 3 Felder durch Doppelpunkt getrennt:
Die Assembleranweisung, wie sie auch im Assemblercode auftritt, als String. Dabei sind die einzelnen Operanden durch Platzhalter vertreten, die die Nummer der zugeordneten Variable (bei 0 beginnend) angeben. In unserem Beispiel besitzt result die Nummer 0 und mult die Nummer 1.
Der Rückgabewert, bestehend aus = mit dem Operandentyp als String und der Variablen, der der Wert zugewiesen wird in Klammern. Im Beispiel bezeichnet d Datenregister als zulässige Operanden und result die Variable, der der Wert zugewiesen wird.
Die Operanden sind durch Komma getrennt. Die einzelnen Operanden bestehen aus einem String mit Operandentyp und geklammerter Variable. Ist ein Operand identisch mit einem vorhergehenden oder dem Ergebnis, erhält er die entsprechende Operandennummer statt des Operandentyps, als String. Im Beispiel ist also result sowohl Ergebnis, als auch der erste Operand.
Der Operandentyp kann folgende Werte, entsprechend den Adressierungsarten, die für den Assemblerbefehl zugelassen sind, annehmen:
m Jede Speicheradressierung. d Jedes Datenregister (D0 - D7). a Jedes Adressregister, das keine spezielle Funktion erfüllt (A0 - A5), ausser dem Stackpointer (A7) und dem Framepointer (A6). f Jedes Fliesskommaregister, soweit Code mit Fliesskommakoprozessor Unterstützung erzeugt werden soll. r Jedes der oben genannten allgemeinen Register. i Ein immediate Integeroperand. g Jeder Speicher-, Register- und immediate Operand. p Jede gültige Speicheradresse, dies ist für lea und pea erforderlich. Hier noch ein Beispiel um den Stackpointer neu zu setzen und die Interruptmaske des Statusregisters zu setzen:
asm("movel %0,sp" : : "g" (new_stackpointer)); asm("movew %0,sr" : : "id" (0x2000 | (interruptmask & 7) << 8));Attribute von Variablen
Variablen können mit dem Schlüsselwort __attribute__ mit Attributen versehen werden. Das folgende Beispiel zeigt, wie man damit das vorgegebene Alignment von Variablen verändert:
/* for compatibility with other compilers and older versions of gcc */ #if __GNUC__ < 2 #define __attribute__(x) #endif /* no attributes */ typedef struct char a; char b; char c; int i; s; s x = 1, 2, 3, 4,; /* packed */ typedef struct char a; char b; char c; int i __attribute__ ((packed)); t; t y = 1, 2, 3, 4,; /* chars with alignment of 2 */ typedef struct char a; char b __attribute__ ((aligned (2))); char c __attribute__ ((aligned (2))); int i; u; u z = 1, 2, 3, 4,;Der erzeugte Assemblercode zeigt die Unterschiede, die sich durch die Variablenattribute der Strukturen y und z gegenüber der ansonsten identischen Struktur x ergeben:
#NO_APP gcc2_compiled.: __gnu_compiled_c: .globl _x .data .even _x: .byte 1 .byte 2 .byte 3 .skip 1 .long 4 .globl _y .even _y: .byte 1 .byte 2 .byte 3 .long 4 # der int i liegt nun auf einer ungeraden Adresse .skip 1 .globl _z .even _z: .byte 1 .skip 1 .byte 2 # der char b hat nun ein Alignment von 2 .skip 1 .byte 3 .skip 1 # der char c hat nun ein Alignment von 2 .long 4Das Attribut packed erzwingt die dichteste mögliche Packung, das Attribut alignment(alignment) erlaubt es, das Alignment einzustellen. Das Programmfragment finden Sie in samples/etc/alignmen.c
![]()
4 Hilfsprogramme
4.1 Archivierer
Der Archivierer ist funktional unixkompatibel. Eine ausführliche Beschreibung befindet sich im Handbuch Binary Utilities im Kapitel "ar" der GNU-Dokumentation. Der Aufruf ist wie folgt:
ecoar [-]command[flags] archive file ...Folgende Kommandos sind möglich:
d Löscht die angegebenen Dateien im Archiv. m Bewegt die angegebene Datei ans Ende des Archivs. p Listet die angegebenen Dateien mit ihren Attributen auf. q Die angegebenen Dateien werden an das Archiv angehängt, ohne dass überprüft wird, ob sie bereits im Archiv enthalten sind. Dies wird deutlich schneller ausgeführt, als das Ersetzen mit -r. r Ersetzt die angegebenen Dateien im Archiv. Falls sie noch nicht darin enthalten sind werden sie angehängt. t Listet alle Dateien mit ihren Attributen auf. u Ersetzt die angegebenen Dateien falls sie jünger sind als die im Archiv enthaltenen. x Die angegebenen Dateien werden aus dem Archiv extrahiert. Folgende Flags sind möglich:
c Die Meldung über ein neu erzeugtes Archiv wird unterdrückt. s Eine Symboltabelle wird vor alle andern Dateien im Archiv geschrieben. Dies ist nur möglich, falls zuvor noch keine Symboltabelle vorhanden war. Die Dateien, die zu einem Archiv zusammengefasst werden, müssen alle das ECO-C Objektformat besitzen. v Gibt für jede Datei eine Meldung aus. Bei der Arbeit mit Bibliotheken mit Symboltabelle ist darauf zu achten, dass eine Bibliothek mit Symboltabelle nicht verändert werden kann, daher ist es erforderlich zunächst die Symboltabelle (__.SYMDEF) zu entfernen. Danach können dann Module der Bilbliothek angehängt, ausgetauscht, oder entfernt werden. Nach der Veränderung ist es dann nötig, dass die Symboltabelle wieder erzeugt wird.
Beispiele:
ecoar d libc00.a __.SYMDEF ecoar rs libc00.a read.o write.oDie Dateien read.o und write.o werden in der C-Lib ersetzt, dabei wird die Symboltabelle zunächst entfernt und anschliessend wieder generiert.
ecoar rcs libultra.a maximal.o hyper.o mega.oDie drei Objektdateien werden in das Archiv libultra.a aufgenommen und anschliessend wird eine Symboltabelle in das Archiv geschrieben. Falls das Archiv noch nicht existierte wird es ohne Meldung erzeugt. Vor dem Aufruf darf das Archiv keine Symboltabelle enthalten haben.
![]()
4.2 Dump-Utility
Mit dem Dump-Utility können Informationen über ein Objektfile ausgegeben werden. Ein ausführliche Beschreibung findet sich im Handbuch Binary Utilities im Kapitel objdump der GNU-Dokumentation. Programme, die mit mkrom für das ROM konvertiert wurden können nicht als Eingabe für ecodump dienen.
Aufruf:
ecodump [-d] [-h] [-r] [-t] objectfileDie einzelnen Optionen haben folgende Bedeutung:
-d Disassembliert die Datei. -h Informationen über den Objektfileheader wird ausgegeben. Diese Funktion ist nützlich um den Speicherbedarf eines Programms zu ermitteln. -r In einer Tabelle wird die Relozierinformation ausgegeben. -t Die Symboltabelle wird ausgegeben. ![]()
4.3 Terminalprogramm
Das Terminalprogramm tm dient zur Kommunikation zwischen PC und Zielrechner über die serielle Schnittstelle. Das Programm wird folgendermassen aufgerufen:
tm [options] [+string] [file]Optionen werden am führenden Minuszeichen und Zeichenketten am führenden Pluszeichen erkannt. Alle anderen Parameter werden als Dateinamen interpretiert. Die Reihenfolge von Optionen, Dateinamen und Zeichenketten ist beliebig.
Die angegebenen Dateinamen file und Zeichenketten string werden in der eingegebenen Reihenfolge übertragen.
In Zeichenketten werden alle C-Escapesequenzen unterstützt.
Liste der Kommandozeilenoptionen:
-d dev# Der serielle Kanal mit der Nummer dev# wird zur Übertragung verwendet. Es ist eine Ziffer zwischen 1 und 4 möglich. Die Standardeinstellung ist der serielle Kanal Nummer 1. -b baud Die Baudrate wird auf den Wert baud gesetzt. Die Standardeinstellung ist 19200Baud. Die angegebene Baudrate wird nur dann akzeptiert, wenn sie sich mit einem Fehler von weniger als 3% realisieren lässt. Die gängigen Baudraten (1200, 2400, 9600, 19200, 38400, 115200) werden exakt eingehalten. -t Werden Dateien oder Zeichenketten übertragen, verhindert dieser Switch das Beenden des Terminalprogramms. -w ms Dieser Switch gibt die Zeit in Millisekunden an die tm bei einem leeren String (ein einzelnes Pluszeichen) wartet. -c ms Dieser Switch gibt die Zeit in Millisekunden an die tm nach jedem übertragenen Zeichen wartet. -s ms Dieser Switch gibt die Zeit in Millisekunden an die tm nach jeder übertragenen Zeile wartet. -k cs Mit diesem Switch können die Zeichenketten die für eine spezielle Taste (Cursortasten, Funktionstasten) gesendet werden belegt werden. Dabei ist das erste Zeichen des Parameters die Tastennummer (Scancode) und alle folgenden die Ersatzzeichenkette. Alle C-Escapesequenzen werden unterstützt. Ohne Angabe dieses Schalters sendet jede spezielle Taste eine Null und danach ihre Tastennummer. -+o file Die Ausgabe wird auf die Datei file umgelenkt. Ohne diesen Schalter erfolgt die Ausgabe auf der Konsole (Monitor). -m Dieser Schalter bewirkt die Ausgabe der empfangenen Zeichen zusätzlich auf dem Monitor, falls die Standardausgabe mit -o umgelenkt ist. -a Alle Dateien werden im ASCII-Mode geöffnet. -n Die Meldung beim Start des Terminalprogramms wird unterdrückt. -v Zusätzliche Statusinformationen werden ausgegeben. Das Kommando download erhält einen Dateinamen als Parameter. tm wird von dem Batchprogramm aufgerufen und lädt im Zusammenspiel mit dem Monitorprogramm NICO die Datei ins RAM des Zielsystems.
![]()
4.4 ROM-Code Generator
Dieses Programm generiert ROM-Code aus a.out-Binärdateien. Dazu wird lediglich der a.out-Header entfernt. Aufruf:
mkrom [-sc332-8bit] inputfile outputfileMit der Option -sc332-8bit wird der ROM-Code zusätzlich für den 8Bit-Betrieb des Scotty332 vorbereitet. Weiterführende Informationen dazu finden Sie im Scotty332-Handbuch.
![]()
4.5 Konverter für Binärdateien
Konvertiert das spezielle Binärformat der Version 1 in das standard a.out Format. Programme die mit -z gelinkt wurden, können nicht konvertiert werden.
Aufruf:
cnvaout oldbinary newbinary![]()
4.6 S-Record Generator
Die S-Records werden ab der spezifizierten Adresse für das Text- und Datensegment generiert. Dies ermöglicht, im Gegensatz zur direkten Generierung durch den Linker, die Daten auf die nach dem Textsegment folgenden Adressen zu legen ohne ihre Relozieradresse zu berücksichtigen. Das Kopieren des Datensegments auf die Relozieradresse wird vom C-Start erledigt (siehe C-Start Beschreibung). Als Eingabeformat wird a.out erwartet.
-3 Das Adressfeld wird auf 3 Bytes eingestellt. Der Default sind 4 Bytes. -a address Die Adresse des ersten S-Record. Der Default ist die im Header angegebene Einsprungadresse. Für eine korrekte Funktion sollte die Relozieradresse des Textsegments angegeben werden. -l length Die Zahl der Bytes je S-Record, Default sind 32. Aufruf:
ecosrec [-3] [-aaddress] [llen] binary S-Record![]()
4.7 Make Utility
Das Make besitzt alle von dem UNIX-Standard-Make bekannten Fähigkeiten und darüber hinaus viele sinnvolle Erweiterungen. Eine umfassende Beschreibung findet sich in der GNU-Dokumentation.
MS-DOS-Erweiterungen
Um auch Kommandozeilen mit mehr als 126 Zeichen an Programme übergeben zu können, ist eine Übergabe der Argumente in einer Datei vorgesehen. Als Argument wird dann "@filename" übergeben. Die vordefinierte Variable ARGSINFILE enthält eine Liste der Programme die ihre Argumente auf diese Weise aus einer Datei lesen können. Die vordefinierte Liste von Programmen enthält ecoc ecoar bcc. Diese Liste kann um andere Programme erweitert werden, z.B.:
ARGSINFILE := $(ARGSINFILE) testDie Liste enthält nun zusätzlich "test".
Vordefinierte Variablen
Häufig benötigte Make-Variablen und Abhängigkeitsregeln wie $(CC) sind vordefiniert. Sie können wie alle Make-Variablen verwendet und überschrieben werden. Liste der Vordefinitionen:
.SUFFIXES: .exe .obj .c .o .bin .y .l .asm .dvi .tex .obj.exe: $(BLINK) $LDFLAGS) $^ .asm.obj: $(BAS) $(ASFLAGS) $< .c.obj: $(BCC) $(CFLAGS) $(CPPFLAGS) -c $< .o.bin: $(CC) $(LDFLAGS) -o $@ $^ .s.bin: $(CC) $(LDFLAGS) $(ASFLAGS) $(CPPFLAGS) -o $@ $^ .c.bin: $(CC) $(LDFLAGS) $(CFLAGS) $(CPPFLAGS) -o $@ $^ .s.o: $(CC) $(ASFLAGS) $(CPPFLAGS) -c $< .c.o: $(CC) $(CFLAGS) $CPPFLAGS) -c $< .y.c: $(YACC) $(YFLAGS) $< mv -f ytab.c $@ .l.c: &(LEX) $(LFLAGS) -t $< mv -f lexyy.c $@ .text.dvi: -$(TEX) $< AR = ecoar ARFLAGS = BAS = tasm BCC = bcc BLINK = bcc CC = ecoc CPP = $(ECOC) -E LEX = flex YACC = bison -y TEX = latex RM = rm -f![]()
5 Versionsunterschiede
5.1 Erweiterungen und Verbesserungen gegenüber ECO-C Version 1
- Linkerscripte.
- Verbesserte Optimierung.
- Unterstützung verschiedener Objektformate, z.B. S-Records können nun direkt durch den Linker erzeugt werden.
- Unterstützung aller Assemblerbefehle der M68k Familie (MC68000 - MC68040, CPU32), auch bei der Optimierung durch den Compiler.
- Generierung von Assemblerlisting und Linkermap.
- DPMI kompatibel (ECO-C ist nun mit Windows und EMM386 verträglich).
- Flexible Targetumschaltung.
- Vollständige Quelltexte der Libraries.
![]()
5.2 Änderungen gegenüber ECO-C Version 1
Kommandozeilen Optionen
Um eine vollständige Aufwärtskompatibilität von ECO-C gegenüber dem orginal GCC zu erhalten, war es nötig, einige Switches in ihrer Funktion oder dem Namen zu verändern. Diese Funktionen betreffen alle lediglich die Bedienung des Compilers auf der Kommandozeile. Der Quelltext kann ohne Veränderungen übernommen werden (Aussnahme: siehe Includedateien).
'-A' wird durch -nostartfiles ersetzt.
'-O' Optimierung ist nun default. Mit '-O0' wird die Optimierung unterbunden. Die Angabe von '-O' ist redundant und entspricht '-O1'. Mit '-O2' werden weitere Optimierungen aktiviert.
'-T' erwartet jetzt den Namen des Linkscripts und nicht mehr die Startadresse. Die Funktionalität von '-T' entfällt.'-Tdata address' und '-Ttext address' die Adresse wird als hexadezimal Zahl interpretiert, die Angabe von 0x muss entfallen. Um mit zukünftigen Versionen kompatibel zu bleiben, ist es sinnvoll die Relozieradressen im Linkerskript anzugeben, statt mit '-Ttext' und '-Tdata' auf der Kommandozeile. Es ist bei Verwendung dieser Switches zusätzlich ein Linkerscript mit -T anzugeben.
'-z' wird durch '-rom' ersetzt; dabei ist eine nachträgliche Konvertierung mit mkrom erforderlich.
'-zsize', '-zstack' und '-ztext' entfallen. Die Einstellung der ROM-Vektortabelle erfolgt nun im Linkerscript rom.ld.Includedateien
Die standardisierte Targetumschaltung machte es erforderlich, einige targetabhängige Includedateien umzubenennen:
Version 1 Version 2 'sys/target.h'
'sys/signal.h'
'sys/mega301.h'
'sys/sigm301.h'
'sys/scotty08.h'
'sys/sigscot.h''target.h'
'except.h'
'target.h' Target ist mega301
'except.h' Target ist mega301
'target.h' Target ist scotty08
'except.h' Target ist scotty08Objektformat
Das Objektformat wurde auf das Standard a.out Format umgestellt. Alte Objektdateien könnnen mit dem Programm cnvaout konvertiert werden. Programme die mit -z gelinkt wurden können nicht konvertiert werden. Bei Programmen die direkt im (EP)ROM ausführbar sein sollen wird mit dem Hilfsprogramm mkrom der a.out Header entfernt.
Das Programm ecosrec ist hinfällig da S-Records nun direkt mit dem Linker generiert werden.
Monitorprogramm
Das Monitorprogramm NICO ist vor der Version 1.18 nicht für das neue Objektformat eingerichtet. Wenden Sie sich für einen Update an ihren Händler oder direkt an MCT. Bei älteren Versionen können Sie trotz Fehlermeldung ein Programm das auf 0x2000 geladen wurde mit s 0x2020 starten.
Environmentvariablen
Die Environmnetvariblen ECOBIN, ECOLIB und ECOINC werden nicht mehr ausgewertet. Die Suchpfade werden nur noch aus den Environmentvariablen ECODIR und codeECOTARGET gewonnen.
![]()
5.3 Erweiterungen gegenüber GCC Version 2
- Der Suchpfad für den C-Start, das specs File, die Libraries und das Linkerscript ist der gleiche.
- Die Environmentvariablen ECODIR und ECOTARGET werden für die Suchpfade ausgewertet.
- Die Switches -as und -al werden an den Assembler durchgereicht, der Switch -aa wird als -a an den Assembler übergeben, der Switch -am wird als -m an den Linker übergeben.
- Mit -copydata wird das Datensegment beim ersten Start über das Textsegment kopiert und danach bei jedem Neustart mit dieser Copy initialisiert.
- Mit -minterrupt wird der Compilercode für die direkte Verwendung von Funktionen (des damit übersetzten Moduls) als Interrupthandler erzeugt. Nicht in Verbindung mit -mrtd verwenden.
- Als Standard wird das Linkerscript std.ld verwendet.
- Mit dem Switch -rom wird ROM-Code erzeugt. Das Linkerscript ist dann rom.ld statt std.ld und der C-start srt0.o statt crt0.o.
- Der Switch -target targetalias/targetspec spezifiziert das Zielsystem. Für die mitgelieferten Targetanpassungen kann einfach der Name angegeben werden. Für sonstige Targetanpassungen muss eine dreiteilige Beschreibung angegeben werden. Dieser Switch überschreibt die Environmentvariable ECOTARGET.
- Mit dem Switch -2 file werden die Fehlermeldungen auf die Datei file umgelenkt.
Um Konflikte mit dem original GCC zu vermeiden, werden die Environmentvariablen C_INCLUDE_PATH, LIBRARY_PATH und COMPILER_PATH nicht ausgewertet.
![]()
6 Systemnahe Programmierung
Die Programmierung mit Crossystemen im Embedded-Controller Bereich erfordert spezielle Techniken aufgrund der spezifischen Anforderungen, wie eingeschränkter Speicherausbau oder extensiver Zugriff auf Peripherieregister. Die folgenden Abschnitte sollen einen Überblick über relevante Interna des Crossystems geben und darauf aufbauend spezielle Programmiertechniken darstellen.
![]()
6.1 Datenformate
ECO-C besitzt die in C bekannten Datenformate und darüber hinaus noch 64Bit Integerzahlen mit und ohne Vorzeichen, sie heissen long long.
C-Name kleinster Wert grösster Wert Bits unsigned char 0 255 8 short -32768 32767 16 unsigned short 0 65535 16 int -2147483648 2147483647 32 unsigned int 0 4294967296 32 long -2147483648 2147483647 32 unsigned long 0 4294967296 32 long long -263 263-1 64 unsigned long long 0 264-1 64 float ±1,2*10-38 ±3,4*1038 32 double ±2,2*10-308 ±1,8*10308 64 Wenn mit der Option '-mshort' übersetzt wird, sind 'int' und 'unsigned int' nur 16Bit lang.
Die beiden Floatingpoint-Formate float und double entsprechen dem IEEE-754 Standard, denormalisierte Zahlen, Unendlich und Nan's werden entsprechend dem Standard unterstützt. Das angegebene Interval für float und double beinhaltet nur die normalisierten Zahlen.
![]()
6.2 Compilercode
Der vom Compiler erzeugte Code ist wesentlich davon abhängig ob die Optimierung eingeschaltet ist oder nicht. Ist die Optimierung eingeschaltet, werden Sprünge optimiert und lokale Variablen in Prozessorregistern gehalten.
Parameterübergabe
Die Parameterübergabe geschieht wie bei C-Compilern üblich auf dem Stack. Der letzte Parameter der Argumentliste wird als erster auf den Stack geladen und der erste zuletzt. Die Parameter werden auf die Grösse von int vorzeichenrichtig erweitert, zum Beispiel sieht der Stack beim Aufruf von 'test(char c, double val, int len);' folgendermassen aus:
Offset Typ Name 4 char c 8 double val 16 int len Der Compiler verwendet einen Framepointer (A6). Über diesen findet der Zugriff auf die Parameter einer aufgerufenen Funktion statt. Die Verwendung eines Framepointers kann in bestimmten Fällen unterdrückt werden, dazu muss -fomit-frame-pointer angegeben werden. In diesem Fall findet die Adressierung der Parameter über den Stackpointer statt. Der Stack wird von der aufrufenden Funktion wieder in Ordnung gebracht.
Rückgabewerte
Ergebnisse werden normalerweise im Register D0 zurückgegeben. Fliesskommazahlen im Doubleformat verwenden die Register D0 und D1. Strukturen werden, falls sie nicht grösser als 8 Bytes sind, in D0 und D1 zurückgegeben. Andernfalls wird der Speicherplatz für die Struktur von der aufrufenden Funktion auf dem Stack allokiert und die Adresse im Register A1 übergeben.
Registernutzung
Die einzelnen Register haben in ECO-C-Programmen folgenden Verwendung:
D0/D1 Ergebnisse einer Funktion. D2-D7 Dienen als universelle Datenregister und müssen von der aufgerufenen Funktion vor der Nutzung gesichert werden. A0 Universelles Adressregister, muss nicht gesichert werden. A1 In diesem Adressregister wird die Adresse des Adressbereichs für die Rückgabe von Funktionen übergeben. Ansonsten ist es ein universelles Adressregister, das nicht gesichert werden muss. A2-A5 Universelle Adressregister, die von der aufgerufenen Funktion gesichert werden müssen. A6 Framepointer. A7 Stackpointer. ![]()
6.3 Assemblerfunktionen
Um Assemblerfunktionen in C-Programme integrieren zu können, sind die compiler-internen Konventionen zu beachten, insbesondere:
- Eine Funktion muss die Register D2-D7 und A2-A7 restaurieren, falls sie zum Aufrufer zurückkehrt.
- Die Parameterübergabe muss der Compilerkonvention entsprechen.
- Referenzen auf C-Namen erhalten im Assemblercode einen Unterstrich (_).
![]()
6.4 Standard Binärformat
Als Binärformat findet das von Unix bekannte 'a.out' Format Verwendung. Der Linker unterstützt jedoch zusätzlich die meisten anderen bekannten Objektformate.
Die Segmente des Objektcodes sind wie folgt angeordnet:
Header Textsegment Datasegment BBS Relozierinformationen Symboltabelle In 'inc/sys/a_out.h' sind die einzelnen Strukturen und Definitionen enthalten.
Header
Der Modulheader enthält alle Informationen über Grösse der einzelnen Segmente und die Einsprungadresse. Er ist durch die Struktur struct exec beschrieben. Das erste Langwort enthält spezielle Informationen über die Art des Binärfiles und eine Konstante.
Textsegment
Das Textsegment enthält den Programmcode. Es ist nicht beschreibbar. Strings landen ebenfalls im Textsegment. Der C-Compiler schreibt zuerst die (String-)Konstanten und danach den Programmcode in das Textsegment eines Moduls.
Datensegment
Das Datensegment enthält, wie der Name sagt, die Programmdaten. Der C-Compiler schreibt hier alle globalen Variablen ungleich Null hinein.
BSS
Das BSS ist im Binärfile nicht enthalten, sondern besitzt lediglich einen Eintrag im Header, der seine Grösse angibt. Das BSS ist ein Speicherbereich der vom C-Start mit Nullen initialisiert wird, er befindet sich direkt hinter dem Datensegment und ist gewissermassen der auf Null gesetzte Teil des Datensegments. Der C-Compiler allokiert hier alle globalen Variablen, die mit Null initialisiert werden.
Relozierinformation
Die Relozierinformation wird vom Linker benötigt, um eine Binärdatei ab einer bestimmten Adresse ausführbar zu machen. Sie besteht aus einzelnen 8 Bytes langen Einträgen. Die einzelnen Einträge werden durch die Struktur relocation_info beschrieben.
Symboltabelle
Die Symboltabelle enthält alle Symbole mit Wert und Namen. Der erste Teil der Symboltabelle besteht aus den einzelnen Symbolen, die jeweils durch die 12 Bytes lange Struktur nlist beschrieben werden. Danach folgt die Gesamtlänge der Symbolnamen als 32Bit-Zahl und anschliessend die Symbolnamen, jeweils mit einem Null-Zeichen beendet.
Linkersymbole
Die mit ECO-C gelieferten Linkerscripte setzen einige Symbole für Anfang- und Endadresse der einzelnen Programmsegmente:
Symbolname Beschreibung __btext
__etext
__bdata
__edata
__endBeginn des Textsegments
Ende des Textsegments
Beginn des Datensegments
Ende des Datensegments und Beginn des BBS
Ende des BBSDie Symbole werden im C-Start ausgewertet um Datensegment und BSS zu initialisieren. Sollen die Symbole in einem C-Programm ausgewertet werden, muss ein '_' weggelassen werden, zum Beispiel:
#include <stdio.h> extern _btext, _etext, _bdata, _edata, _end; void main(void) { printf("btext: 0x%x, etext: 0x%x, \ bdata: 0x%x, edata: 0x%x, end: 0x%x\n", &_btext, &_etext, &_bdata, &edata, &_end); }![]()
6.5 Andere Binärformate
Zusätzlich werden noch S-Record, Symbol S-Record, IEEE-695 und COFF unterstützt. Folgende Tabelle enthält die Kommandozeilenparameter um beim Linken die entsprechenden Binärformate zu generieren:
Binärformat RAM-Code ROM-Code S-Record
Symbol S-Record
IEEE-695
COFF-Tsrecstd
-Tssrecstd
-Tieeestd
-Tcoffstd-Tsrecrom
-Tssrecrom
-Tieeerom
-TcoffromFormate wie IEEE-695 enthalten die Namen der einzelnen Segmente, in diesem Fall werden die gleichen Namen wie beim a.out Format verwendet. Um die Namen zu ändern ist das entsprechende Linkerscript zu ändern, z.B. für MEGA340 RAM-Code im IEEE-Format: 'libsrc/mega340/ieeestd.ld'. Anschliessend muss make install gestartet werden, um die Datei in das lib Verzeichnis zu kopieren.
Eine genaue Beschreibung der Linkerscript-Syntax findet sich in der originalen Dokumentation zum Linker.
Die Fremdformate lassen sich zur Zeit jedoch nicht debuggen.
![]()
6.6 ROM-Code
Der ROM-Code wird intern durch Umschalten auf ein spezielles Linkerscript generiert. Um das Programm im ROM lauffähig zu machen, muss mit 'mkrom' noch der Header entfernt werden.
![]()
6.7 Speicherlayout
Das Objektformat von ECO-C (a.out) unterstützt 3 Segmente. Diese Segmente haben sowohl in C als auch im Speicher des Zielsystems eine Entsprechung:
- Im Textsegment befinden sich der Programmtext und alle Daten die konstant sind, dazu gehören Stringkonstanten und mit dem Schlüsselwort const deklarierte statische Variablen. Dieses Segment belegt nur ROM-Speicher und kann zur Laufzeit nicht modifiziert werden.
- Das Datensegment enthält alle statischen Variablen die initialisiert werden. Dieses Segment muss schreibbar sein. Die initialen Daten werden bei Progammstart aus dem ROM kopiert. Es wird ROM- und RAM-Speicher belegt.
- Das BSS enthält alle statischen Variablen die mit Null initialisiert werden. Dieses Segment muss schreibbar sein. Der entsprechende Bereich im RAM wird bei Progammstart gelöscht (mit Nullen beschrieben).
![]()
6.8 Speicherbedarf
In der folgenden Tabelle ist der Speicherbedarf im ROM und im RAM bei der Übersetztung mit verschiedenen Optionen angegeben:
ROM RAM ohne Optionen
mit -copydata
mit -rom0
0
Text + DataText + Data + BSS
Text + 2 * Data + BSS
Data + BSSMit 'ecodump -h filename' kann der Speicherbedarf der einzelnen Segmente ausgegeben werden.
![]()
6.9 Verwendung von const
Bei Controlleranwendungen ist häufig auf Speicherbeschränkungen Rücksicht zu nehmen. Dabei ist aus Kostengründen häufig der RAM-Speicher limitiert, wohingegen (EP)ROM-Speicher zur Genüge vorhanden ist. Mit dem Schlüsselwort const ist es möglich konstante Daten im Textsegment und damit ohne RAM-Speicherbedarf abzulegen.
Hier eine Anwendung zur Initialisierung eines Stringarrays:
const char * strings[]={ "Pointer", "im", "Datensegment", };Erzeugt folgenden Assemblercode:
.globl _strings .text LC0: .ascii "Pointer\0" LC1: .ascii "im\0" LC2: .ascii "Datensegment\0" .data .even _strings: .long LC0 .long LC1 .long LC2Es fällt auf, dass sich die Pointer im Daten- und nicht im Textsegment befinden. Dies ist eine Schwäche des ANSI-C Standards, in dem ein als const deklarierter Pointer das durch den Pointer referenzierte Objekt als Konstant deklariert. Diese Unzulänglichkeit ist beim GNU-C-Compiler der Free-Software-Foundation durch eine Erweiterung der Syntax beseitigt. const wird dem * nachgestellt. Die Version mit konstanten Pointern sieht folgendermassen aus:
char * const string[]={ "Pointer", "im", "Datensegment", };Erzeugt folgenden Assemblercode:
.globl _strings .text LC0: .ascii "Pointer\0" LC1: .ascii "im\0" LC2: .ascii "Datensegment\0" .even _strings: .long LC0 .long LC1 .long LC2![]()
6.10 Hardwarezugriff in C
Dieses Thema wird für Leute mit grösserer Erfahrung in hardwarenaher Programmierung kein Thema sein. Für alle mit weniger Erfahrung soll gezeigt werden wie in C ohne Effizienzeinbusse gegenüber Assembler direkt auf die Hardware zugegriffen werden kann. Ein einfaches Beispiel für den Zugriff auf einen fiktiven Peripheriebaustein auf einer beliebige Adresse:
#define abc (*(unsigned char *)1234)Das Byte an der Adresse 1234 wird als "abc" definiert und genauso behandelt wie die Definition:
unsigned char abc;Mit dem Unterschied, dass die Adresse nicht vom Compiler, sondern vom Programmierer bestimmt wird. Das Programmstück
... abc = 0xff; abc++; ...erzeugt nicht anders, als in Assembler geschrieben, folgenden Code:
moveb #0xff,1234:w addqb #0x1,1234:w![]()
6.11 Verwendung von volatile
Mit dem Schlüsselwort volatile hat ANSI-C die Möglichkeit geschaffen, Speicherstellen die einem 'Eigenleben' unterliegen zu kennzeichnen. Mit 'Eigenleben' ist jede Veränderung von Daten gemeint die der Compiler nicht vorhersehen kann, hierunter fallen im speziellen Peripherieregister die durch äussere Ereignisse verändert werden oder ein Zugriff auf ein Peripherieregister Seiteneffekte besitzt.
Beispiel:
#define PORT (*(unsigned char *)123456) void tougle(int cycles) { while(cycles--) { PORT = 1; PORT = 0; } }Daraus erzeugt der Compiler:
.globl _tougle _tougle: link a6,#0 movel a6@(8),d0 subql #1,d0 moveq #-1,d1 cmpl d0,d1 jeq L3 movel #123456,a0 L4: clrb a0@ ; Der einzige Zugriff auf 'PORT' dbra d0,L4 clrw d0 subql #1,d0 jcc L4 L3: unlk a6 rtsDer Compiler hat den ersten Zugriff auf PORT wegoptimiert, da er nicht wissen kann, dass das alternierende Ein- und Ausschalten des Ports vom Programmierer gewünscht wird. Es wird nur erkannt, dass das Ergebnis in PORT am Ende der Schleife Null ist und damit der erste Zugriff für den Inhalt der Speicherstelle unerheblich.
Nun das Beispiel mit volatile:
#define PORT (*(volatile unsigned char *)123456) void tougle(int cycles) { while(cycles--) { PORT = 1; PORT = 0; } }Erzeugt den Assemblercode:
.globl _tougle _tougle: link a6,#0 movel a6@(8),d0 subql #1,d0 moveq #-1,d1 cmpl d0,d1 jeq L3 movel #123456,a0 L4: moveb #1,a0@ ; Der erste Zugriff bleibt nun erhalten moveb #0,a0@ dbra d0,L4 clrw d0 subql #1,d0 jcc L4 L3: unlk a6 rts![]()
6.12 Optimierung
Gerade bei Embedded Control Anwendungen, wo es auf effizienten Objektcode ankommt ist Optimierung ein wichtiger Aspekt bei der Beurteilung eines Compilers. Ein wichtiger Punkt ist die automatische Allokierung von Registern, die das Schlüsselwort register ausser in Sonderfällen überflüssig macht.
Im folgenden zeigen wir ein Optimierungsbeispiel, bei dem zwei 16Bit Zahlen zu einer 32Bit Zahl multipliziert werden. Die verwendeten Variablen sind wegen der besseren Anschaulichkeit alle global:
short i, j; long result; ... result = i * j; ...Erzeugt den Assemblercode:
... movew _i,d0 muls _j,d0 movel d0,_result ...Bemerkenswert hierbei ist die Tatsache, dass der Compiler auch die Länge der Operanden in die Optimierung einbezieht und damit den Befehlsvorrat der CPU optimal ausnutzt.
![]()
6.13 Linkerscript
Bei Crosscompilern für Controller muss der erzeugte Output sich dem variablen Speicherlayout und Objektfomat des Targetsystems anpassen lassen. Die Standardaufgabe hierbei ist die Festlegung der Adressbereiche von RAM und ROM.
Weitergehende Forderungen sind die Generierung von Exceptionvektor oder das Relozieren von zeitkritischen Programmteilen in Speicherbereiche mit besonders schnellem RAM (z.B.: internes RAM des MC68332). Das Ausgabeobjektformat lässt sich ebenfalls im Linkerscript festlegen. Die Funktionalität und Syntax von Linkerscripten ist in der original Dokumentation zum Linker beschrieben.
Da die Relozieradresse im Linkerscript angegeben wird, sind die Scripte im allgemeinen targetspezifisch. Die verwendeten Scripte befinden sich in den Targetunterverzeichnissen in lib. Für jedes Zielsystem existieren 4 verschieden Scripte:
std.ld Standardscript. rom.ld Script für ROM-Code. stdsrec.ld Standardscript, S-Record Ausgabeformat. romsrec.ld Script für ROM-Code, S-Record Ausgabeformat. ![]()
7 Assembler
ECO-C verwendet die Unix Assembler Syntax und nicht die gewohnte Motorola Syntax.
Mnemonics
Der grösste Unterschied ist das Fehlen des Punkts zwischen Mnemonic und Operandengrösse, z.B.: movew statt move.w. Die Operandengrösse muss auch bei Wortoperanden immer angegeben werden. Bei relativen Sprüngen kann die Grössenangabe weggelassen werden, der Sprung mit dem kürzesten Datenformat wird erzeugt. Statt der Mnemonics für unmittelbare Daten können auch die Standardmnemonics verwendet werden, z.B.: addw statt addiw.
ECO-C-/Motorola-Mnemonics
Adressierungsarten
Adressierung Unix Syntax Motorola Syntax Unmittelbar
Datenregister
Adressregister
Adressregister Ind.
Adressregister Ind. mit Postink
Adressregister Ind. mit Präink.
Adressregister Ind. mit Offset
Ind. mit Index und Offset
Ind. mit Index
Ind. mit Index und Offset
Ind. mit Index und Offset
Ind. mit Offset
Ind. mit Index und Offset
Ind. mit Index und Offset
Absolut#n
dr
ar
ar@
ar@+
ar@-
ar@(w)
ap@(n,r:sz:sc)
ap@(r:sz:sc)
ap@(n)@(n2,r:sz:sc)
ap@(n)@(r:sz:sc)
ap@(n)@(n2)
ap@(n,r:sz:sc)@(n2)
ap@(r:sz:sc)@(n2)
n#n
dr
ar
(ar)
(ar)+
-(ar)
(w,ar)
(n,ap,r.sz*sc)
(ap,r.sz*sc)
([n,ap],r.sz*sc,n2)
([n,ap],r.sz*sc)
([n,ap],n2)
([n,ap,r.sz*sc],n2)
([ap,r.sz*sc],n2)
nKürzelerklärung:
Kürzel Erklärung Ind.
w
dr
r
ar
ap
n, n2
sz
scIndirekt
16Bit Zahl
Datenregister 0-7
Adress- oder Datenregister
Adressregister
Adressregister, PC oder leerer String
16 oder 32Bit Zahl
w oder l, bei fehlender Angabe l
1, 2, 4 oder 8, bei fehlender Angabe 1![]()
8 Bibliotheken und Initialisierung
8.1 ANSI-Funktionen
Die Funktionen sind nach den Includedateien, die die Prototypen enthalten, geordnet. Die implementierten ANSI-Funktionen sind, soweit sie keine Erweiterungen gegenüber dem Standard enthalten, nur aufgelistet.
assert.h Testfunktion
void assert(char *exp);ctype.h Behandlung von Zeichen
int isalpha(unsigned char c); int isupper(unsigned char c); int islower(unsigned char c); int isdigit(unsigned char c); int isodigit(unsigned char c); int isxdigit(unsigned char c); int isalnum(unsigned char c); int isspace(unsigned char c); int ispunct(unsigned char c); int isprint(unsigned char c); int isgraph(unsigned char c); int iscntrl(unsigned char c); int isascii(unsigned char c); int toupper(int c); int tolower(int c);math.h Mathematische Funktionen
double acos(double x); double acosh(double x); double asin(double x); double asinh(double x); double atan(double x); double atanh(double x); double atan2(double x, double y); double ceil(double x); double cos(double x); double cosh(double x); double exp(double x); double fabs(double x); double floor(double x); double frexp(double v, int *e); double hypot(double x, double y); double ldexp(double v, int e); double log(double x); double log10(double x); double pow(double x, double y); double sin(double x); double sinh(double x); double tan(double x); double tanh(double x);setjmp.h Nichtlokale Sprünge
int setjmp(jmp_buf jmpbuf); void longjmp(jmp_buf jmpbuf, int i);signal.h Behandlung von Signalen
Ein Beispiel zum arbeiten mit der Signalfunktion befindet sich in der Datei samples/signal.c.
void (*signal(int sig, void (*fp)(int)))(int); int raise(int sig);stdarg.h Variable Argumenteliste
void va_start(AP, LASTARG); void va_end(va_list); TYPE va_arg(AP,TYPE);stdio.h Eingabe und Ausgabe
void clearerr(FILE *fp) int fclose(FILE *); int feof(FILE *fp) int ferror(FILE *fp) int fileno(FILE *fp) /* Berkeley */ FILE *fdopen(int, char *); /* Berkeley */ int fflush(FILE *); int fgetc(FILE *); char *fgets(char *, int, FILE *); FILE *fopen(const char *, const char *); int fprintf(FILE *, const char *, ...); int fputc(int, FILE *); int fputs(const char *, FILE *); int fread(void *, size_t, size_t, FILE *); FILE *freopen(const char *, const char *, FILE *); int fscanf(FILE *, const char *, ...); int fseek(FILE *, long int, int); long int ftell(FILE *); int fwrite(const void *, size_t, size_t, FILE *); int getc(FILE *pf); int getchar(void); char *gets(char *); int printf(const char *, ...); int putc(char c, FILE *fp); int putchar(int c); int puts(const char *); void rewind(FILE *); int scanf(const char *, ...); int setbuf(FILE *, char *); int setbuffer(FILE *, char *, int); /* Berkeley */ int setlinebuf(FILE *); /* Berkeley */ int setvbuf(FILE *, char *, int, size_t); int sprintf(char *, const char *, ...); int sscanf(const char *, const char *, ...); int ungetc(int, FILE *); int vfprintf(FILE *, const char *, va_list); int vprintf(const char *, va_list); int vsprintf(char *, const char *, va_list);stdlib.h Verschiedenes
void abort(void); int abs(int i); int atexit(void (*fn)(void)); double atof(char *s); int atoi(char *s); long atol(char *s); void *bsearch(void *key, void *base0, size_t nmemb, size_t size, int (*compar)()); void *calloc(size_t n, size_t s); div_t div(int num, int denom); int exit(int code); void free(void *p); char *getenv(const char *name); long labs(long l); ldiv_t ldiv(long num, long denom); void *malloc(size_t s); void qsort(void *base, size_t n, size_t s, int(*cfu)()); int rand(void); void *realloc(void *p, size_t s); void srand(unsigned seed); double strtod (const char *s, char **ptr); long strtol(char *sp, char **ep, int b); unsigned long strtoul(char *sp, char **ep, int b);string.h Stringfunktionen
int bcmp(const void *p1, const void *p2, size_t n); /* Berkley */ void bcopy(const void *s, void *d, size_t n); /* Berkley */ void bzero(void *p, size_t n); /* Berkley */ int ffs(long mask); /* Berkley */ void *memccpy(void *, const void *, int, size_t); /* no ansi */ void *memchr(const void *, int, size_t); int memcmp(const void *, const void *, size_t); void *memcpy(void *, const void *, size_t); void *memmove(void *, const void *, size_t); void *memset(void *, int, size_t); char *strcat(char *d, const char *s); char *strchr(const char *p, int c); int strcmp(const char *s1, const char *s2); char *strcpy(char *d, const char *s); size_t strcspn(char *s, const char *set); char *strerror(int n); size_t strlen(const char *s); char *strncat(char *d, const char *s, size_t n); int strncmp(const char *s1, const char *s2, size_t n); char *strncpy(char *d, const char *s, size_t n); char *strpbrk(const char *s, const char *set); char *strrchr(const char *s, int c); size_t strspn(const char *s, const char *set); char *strstr(const char *s, const char *pat); char *strtok(char *s, const char *sep);time.h Datum und Zeit
char *asctime(const struct tm *); char *ctime(const time_t *); double difftime(const time_t, const time_t); struct tm *gmtime(const time_t *); struct tm *localtime(const time_t *); time_t mktime(const struct tm *); time_t time(time_t *);![]()
8.2 Systemspezifische Funktionen
In diesem Abschnitt sind alle Funktionen aufgeführt, die nicht vom ANSI-Komitee festgelegt sind. Zum einen sind das Implementierungen von häufig benötigten Funktionen aus C-Bibliotheken verbreiteter Systeme, darunter so bekannte wie open und close, zum andern Funktionen, die ECO-C spezifisch sind, vor allem solche, die die Programierung in Steuerungsumgebungen erleichtern.
Die ECO-C spezifischen Funktionen beginnen -- dem ANSI-Standard entsprechend -- mit einem Unterstrich. Alle hier aufgeführten Funktionen sind unabhängig vom Zielrechner implementiert und bedienen sich beim Zugriff auf die Hardware den Funktionen aus dem nächsten Abschnitt.
![]()
8.2.1 I/O-Funktionen
Die Quellen für diese Funktionen befinden sich in 'libsrc/system'.
- int close(int handle);
- Schliesst eine Datei. Wenn ein Fehler auftritt wird errno gesetzt und -1 zurückgegeben.
- int creat(const char *filename, ... );
- Dies ist eine Dummyfunktion, sie gibt immer -1 zurück.
- int dup(int handle);
- Dupliziert einen Dateideskriptor. Falls ein Fehler auftritt wird errno gesetzt und -1 zurück gegeben.
- int isatty(int handle);
- Diese Funktion gibt eine 1 zurück, wenn es sich um eine serielle Schnittstelle handelt, ansonsten eine Null.
- size_t lseek(int handle, size_t offset, int mode);
- Eine Dummyfunktion, die immer Null zurückgibt.
- int open(const char *filename, int access, ... );
- Die Funktion öffnet den I/O-Kanal mit dem Namen filename. Die Funktion gibt den Dateideskriptor zurück. Falls ein Fehler auftritt, wird -1 zurückgegeben und errno gesetzt.
- size_t read(int handle, void *buf, size_t nbyte);
- Liest maximal nbyte Bytes vom Dateideskriptor handle in den Speicher, auf den buf zeigt. Es wird die Anzahl der gelesenen Bytes zurückgegeben. Falls ein Fehler auftritt, wird -1 zurückgegeben und errno gesetzt.
- size_t write(int handle, void *buf, size_t nbyte);
- Schreibt maximal nbyte Bytes zum Dateideskriptor handle vom Speicher, auf den buf zeigt. Es wird die Anzahl der geschriebenen Bytes zurückgegeben. Falls ein Fehler auftritt, wird -1 zurückgegeben und errno gesetzt.
![]()
ioctl()
Die Funktion ioctl() ist mit Sicherheit weitaus erklärungsbedürftiger als viele andere Funktionen von ECO-C. Daher haben wir dieser Funktion ein eigenes Kapitel gewidmet.
int ioctl(int handle, int code, void *buf);Die Funktion liest und verändert die Parameter des I/O-Kanals, der mit handle identifiziert ist. Mit dieser Funktion lassen sich Baudrate, Übertragungsparameter, Echo, Zeicheninterpretation und ähnliches einstellen beziehungsweise auslesen. Die möglichen Funktionen hängen von der Art des I/O-Kanals ab, sie sind in sys/path.h definiert. Falls ein Fehler auftritt wird errno gesetzt und -1 zurückgegeben.Die Übergabewerte haben folgende Funktion:
handle handle identifiziert den I/O-Kanal auf dem die Parameter geändert werden sollen. handle kann z.B. fileno(stdin) sein. code Mit code wird ioctl() angewiesen eine dem code entsprechende Anweisung auszuführen. buf buf ist ein Pointer auf die Daten die zur Ausführung benötigt werden. ECO-C unterstützt zur Zeit zwei unterschiedliche Devicetypen. Zum einem wären das die asynchronen seriellen Schnittstellen und zum anderen der Typ für LC-Displays. Im folgenden finden Sie die möglichen Werte für code die für beide Devicetypen identisch sind:
IOCW_ASSOC IOCR_ASSOC Diese Funktion ist nur für Devices bei denen auf einem Kanal gleichzeitig geschrieben und gelesen werden kann. Wird z.Z. von ECO-C nicht unterstützt. IOCW_CHARS Spezialzeichen schreiben. IOCR_CHARS Spezialzeichen lesen.
In den I/O-Routinen gibt es spezielle Zeichen die eine Aktion zur Folge haben. Mit den beiden Befehlen können Sie diese Zeichen ändern. Es gibt 6 dieser sogenannten Spezialzeichen die ECO-C in der Empfangsroutine verwendet, wenn mit IOCW_FLAG der RAW-Mode abgeschaltet wurde:Zum Schreiben bzw. Lesen benötigt man einen String für den Übergabewert buf an ioctl(). Ein Lesezugriff würde z.B. so aussehen:
TC_INTRC Es wird eine Signal SIGINT ausgelöst. TC_QUITC Es wird eine Signal SIGTERM ausgelöst. TC_STARTC Start Ausgabe.Setzt die Empfangsroutine in den ungeblocktem Mode. Dies entspricht dem Zurücksetzen des Flags NO_BLOCK. TC_STOPC Stop Ausgabe. Setzt die Empfangsroutine in den geblocktem Mode. Dies entspricht dem Setzen des Flags NO_BLOCK. TC_ERASEC Lösche letztes Zeichen TTC_KILLC Lösche bis Zeilenanfang char s[6]; ioctl(0,IOCR_CHARS,s); /* s enthält nun in der obrigen */ /* Reihenfolge die Spezialzeichen */Sehen Sie dazu auch das Beispielprogramm signal.c in dem die Installation eines Signal-Handlers für die beiden Signale SIGINT und SIGTERM gezeigt wird.IOC_CLRERR Löscht alle Fehlerflags
Die Fehlerbits die mit der Funktion IOCR_STATUS gelesen werden können, werden mit IOC_CLERR gelöscht.
Beispiel:int s; ioctl(0,IOC_CLRERR, &s);IOCW_FLAG Flags schreiben IOCR_FLAG Flags lesen
Folgende Flags können mit dieser Funktion gesetzt bzw. gelöscht werden:
RAW Es werden keine Kontrollzeichen interpretiert. ECHO Jedes empfangene Zeichen wird wieder zurückgeschickt. CBREAK Wenn der RAW-Mode inaktiv ist kann die Interpretation der Zeichen TC_ERASEC und TC_KILLC abgeschaltet werden. CRMOD Das Zeichen CR wird in ein LF gewandelt und LF wird generell in die beiden Zeichen CR und LF gewandelt. NO_BLOCK Sind keine Zeichen im Empfangspuffer vorhanden, so wird die Programmabarbeitung nicht blockiert bis ein Zeichen eintrifft. Beim Senden können Zeichen verschwinden wenn die serielle Schnittstelle die Zeichen nicht schnell genug abholt. Signalisiert die Schnittstelle FULL, so werden die noch zu sendenden Zeichen verworfen. Es ist in der Regel sinnvoll NO_BLOCK nur für die Empfangsseite zu setzen.
Beispiel: Sie möchten ECHO abschal;ten, NO_BLOCK und RAW einschaltenint s; ioctl(0,IOCR_FLAG,&s); /* Flags lesen */ s&=~ECHO; /* ECHO-Flag löschen */ s|=NO_BLOCK; /* NOBLOCK setzen */ s|=RAW; /* RAW setzen */ ioctl(0,IOCW_FLAG,&s); /* Flags schreiben */IOCW_MODE Schreibe Mode IOCR_MODE Lese Mode
Mit dieser Funktion lässt sich nachträglich der Mode ändern, mit dem das Device geöffnet wurde. Gültig Einträge sind:Alle anderen Werte sind für ECO-C ohne Relevanz.
O_RDONLY nur Lesen O_WRONLY nur Schreiben O_RDWR Lesen und Schreiben IOCW_STATUS Liefert immer FAIL zurück! IOCR_STATUS Lese den Status des I/O-Kanal
Mit diesen Funktionen kann der Status des entsprechenden Devices gelesen bzw. verändert werden(z.B. ein Fehlerbit zurücksetzen). Im folgenden finden Sie die Bedeutung der einzelnen Bits, die im Übergabewert buf zu finden sind:Beispiel: Sie möchten gezielt OVERF_ERR zurücksetzen
BLOCKED Geblockte Übertragung ist eingeschaltet. CTL_DIRTY Wenn dieses Flag gesetzt ist, sind die anderen Flags nicht gültig. Falsche Einträge können nur vorkommen, wenn der Filepointer ungültig ist. VALID Der mit ioctl() übergebene Filepointer ist gültig. ERR_BITS Zeigt an ob ein Fehlerflag gesetzt wurde. PARITY_ERR Parity-Fehler, Spezielles Flag für serielle Schnittstellen. OVERF_ERR Overflow-Fehler FRAME_ERR Frame-Fehler, Spezielles Flag für serielle Schnittstellen. FULL Puffer ist voll. Gilt nur für Devices mit Schreibzugriff. NO_CHAR Es ist kein Zeichen im Puffer vorhanden. Gilt nur für Devices mit Lesezugriff. BREAK_DET Es wurde ein Break-Zeichen empfangen. Spezielles Flag für serielle Schnittstellen. int s; ioctl(0,IOCR_STATUS,&s); s&=~OVERF_ERR; /* Status verändern */ ioctl(0,IOCW_STATUS,%amp;s);Im folgenden finden Sie die spezifischen Code-Funktionen für serielle asynchrone Schnittstellen:
IOCW_ASYMODE Schreibe Mode für die serielle Schnittstelle IOCR_ASYMODE Lies Mode der seriellen Schnittstelle
Mit diesen Funktion lassen sich direkt die Schnittstellenparameter einer seriellen Schnittstelle verändern. D.h. die Datenbits, Parity oder auch Stopbits lassen sich hiermit einstellen. Im folgenden finden Sie die möglichen Einstellungen:Beispiel: Es soll 8 Datenbits, even Parity und 1 Stopbit gesetzt werden
BITS7 7 Datenbits, nicht gesetzt bedeutet 8 Datenbits EVENP Gesetzt: Gerade Parität ODDP Gesetzt: Ungerade Parität. Sind EVENP und ODDP nicht gesetzt, wird ohne Parität gearbeitet. LONGSTOP Gesetzt: 2 Stopbits, nicht gesetzt: 1 Stopbit NO_REC Gesetzt: Receiver ist ausgeschaltet NO_TRANS Gesetzt: Transmitter ist ausgeschaltet CLR_RTS Negiere die RTS-Leitung CLR_ERR Lösche alle Fehlerflags CLR_REC Resette den Receiver CLR_TRANS Resette den Transmitter CTL_CTS Transmittercontrol über CTS CTL_RTS Kontrolliere RTS-Leitung SET_RTS Schalte RTS-Leitung frei REC_INTERRUPT Der Receiver arbeitet im Interruptbetrieb int s; ioctl(0,IOCR_ASYMODE,&s); s&=~BITS7; /* 7 Bits zurücksetzen */ s|=EVENP; /* gerade Parität */ s&=~LONGSTOP; /* 2 Stopbits zurücksetzen */ ioctl(0,IOCW_ASYMODE,%amp;s);IOCW_BAUDRATE Schreibe Baudrate IOCR_BAUDRATE Lies Baudrate
Mit dieser Funktion kann die Baudrate gelesen und verändert werden.
Beispiel: Baudrate lesenint s; ioctl(0,IOCR_BAUDRATE,&s); printf("Baudrate: %d\n",s);Beispiel: Baudrate schreibenint s=9600; ioctl(0,IOCW_BAUDRATE,%amp;s);IOCW_CSWITCH_FU Schreibe die Adresse des Handlers für das Sonderkontrollzeichen IOCR_CSWITCH_FU Lies die Adresse des Handlers für das Sonderkontrollzeichen IOCW_CSWITCH_CHAR Schreibe das Sonderkontrollzeichen IOCR_CSWITCH_CHAR Lies das Sonderkontrollzeichen
Die beiden Funktionen können nur benutzt werden, wenn die serielle Schnittstelle mit IOCW_ASYMODE in den Interruptmode geschaltet wurde. Mit diesen Funktionen können Sie eine Routine einhängen, die direkt bei Eintreffen eines bestimmten Zeichens aufgerufen wird. Das Sonderkontrollzeichen kann mit der Funktion IOCW_CSWITCH_CHAR und die Funktion mit IOCW_CSWITCH_FU angegeben werden. Sehen Sie dazu auch das Beispielprogramm recinter.c in dem das Zeichen 'U' eine Sonderroutine aufruft. Beachten Sie aber bitte dabei, dass solange diese Routine aktiv ist keine weiteren Zeichen eingelesen werden können, da nach Beendigung dieser Routine in die eigentliche Interruptroutine zurückgekehrt wird und anschliessend diese erst termniniert. Das Sonderkontrollzeichen taucht im Empfangspuffer nicht mehr auf.IOCW_SWITCH_PORT Setze Portleitungen der seriellen Schnittstelle IOCR_SWITCH_PORT Liefert immer FAIL zurück.
Als Parameter können dieser Funktion folgende Werte übergeben werden:
SET_DTR Setzt die DTR-Leitung auf High CLS_DTR Setzt die DTR-Leitung auf Low Im folgenden finden Sie die spezifischen Code-Funktionen für grafische LC-Displays. Zur Zeit gibt es nur eine Funktion:
IOCW_PIXEL Schreibt ein Pixel IOCR_PIXEL Liefert immer FAIL zurück. Mit dieser Funktion können einzelne Pixel gesetzt bzw. zurückgesetzt werden. Als Übergabewert erhält die Funktion ioctl() ein Array mit drei Einträgen. Dabei ist in der festgelegten Reihenfolge der Erste der X-Wert, der Zweite der Y-Wert und der Dritte gibt an ob der Punkt gesetzt oder zurückgesetzt werden soll.
Beispiel: Es soll das Pixel an der Koordinate X=10,Y=11 gesetzt werdenint s[3]=10,11,1; ioctl(0,IOCW_PIXEL,&s);Sehen Sie dazu auch das Beispielprogramm 'lcd_graf.c'.![]()
8.2.3 Signale
Alternativ zu signal ist eine ähnliche Funktion implementiert:
- _sig_context(int signum, void (*fp)(int, struct frame *));
- Der Unterschied zu signal besteht in den Parametern, die der Funktion fp übergeben werden. Bei _sig_context wird dem Signalhandler ausser der Signalnummer zusätzlich ein Zeiger auf den gesicherten Prozessorkontext übergeben. Die Struktur frame ist in sys/frame.h beschrieben. Dort befindet sich auch der Prototype der Funktion. Diese Funktion ist brauchbar, um bei mehreren nebenläufigen Prozessen vom einen zum andern zu schalten. Die Rückgabewerte sind gleich denen von signal.
- void _do_context(struct frame *context);
- Diese Funktion ist gewissermassen die Umkehrung von _sig_context. Sie dient zum Wechsel zu einem neuen Kontext.
![]()
8.3 Targetspezifische Funktionen
Eingabe und Ausgabe
Die höheren I/O-Funktionen verwenden zum systemspezifischen Zugriff auf die Hardware eine verkettete Liste von Strukturen namens _device_ary. Die einzelne Strukturen enthalten alle Angaben, die zum Betrieb des Kanals nötig sind und einen Zeiger auf das nächste Device.
name Der Name, der zum Öffnen des Kanals nötig ist. devnum Die Nummer des Kanals. Diese wird bei jedem Aufruf einer Funktion der DEVICE-Struktur mit übergeben und ermöglicht die Verwendung der Funktionen für meherere ähnliche Kanäle, z.B. für Kanäle, die sich nur durch ihre Adresse unterscheiden. devtype Spezifiziert die Art des Kanals. Dies ist nötig, um die möglichen ioctl Aufrufe zu bestimmen. flag Falls erforderlich zusätzliche Optionen, die für die Kommunikation mit den darüberliegenden Funktionsebenen erforderlich sind. Dies ist zur Zeit nicht benutzt. p_read Dieser Funktionszeiger dient zum Lesen eines Bytes. Auch wenn kein Zeichen empfangen wurde kehrt die Funktion mit dem entsprechenden Rückgabewert zurück. p_write Dieser Funktionszeiger dient zum Schreiben eines Bytes. Auch wenn kein Zeichen geschrieben werden konnte kehrt die Funktion mit dem entsprechenden Rückgabewert zurück. p_cntl Dieser Funktionszeiger wird zum Einstellen von Schnittstellenparametern von ioctl aus verwendet. Die möglichen Einstellungen richten sich nach dem Kanaltyp (devtype). Der Funktionscode für ioctl muss mit IOC_DEV (in sys/path.h definiert) über ein Oder verknüpft werden, damit der Aufruf an p_cntl durchgereicht werden. p_init Mit diesem Funktionszeiger wird der entsprechende Kanal im C-Start über die _init_port Funktion initialisiert. p_open Dieser Funktionszeiger wird beim Öffnen des Kanals aufgerufen. Bei den vorhandenen Beispielen wird eine funktionslose Dummyfunktion aufgerufen. p_close Dieser Funktionszeiger wird beim Schliessen des Kanals aufgerufen. Bei den vorhandenen Beispielen wird eine funktionslose Dummyfunktion aufgerufen. next Ein Zeiger auf die nächste Device-Struktur. Gibt es kein weiteres Device mehr, so ist dieser Zeiger NULL. Die Struktur (DEVICE) ist -- wie alle systemspezifischen Strukturen und Makros zur Ein-, Ausgabe -- in der Includedatei sys/path.h definiert. Die Quelltexte befinden sich in libsrc/target/io.c.
Speicherverwaltung
Die Speicherverwaltung besteht aus einem systemunabhängigen Teil, der die ANSI-Funktionen zur Speicherverwaltung zur Verfügung stellt, und einem systemspezifischen Teil, der die angeforderten Blocks zur Verfügung stellt. Die Funktion _more_core in libsrc/morecore.c dient zur Anforderung von Speicher vom System, sie wird von malloc aufgerufen. In dieser Form ist _more_core für alle Systeme verwendbar, die einen zusammenhängenden RAM-Speicherblock für Speicherallokierung zur Verfügung stellen, dies dürfte bei den meisten Systemen der Fall sein. Die Einstellung des von _more_core verwendeten Speicherbereichs erfolgt mit zwei globalen Variablen.
_static_top gibt die Obergrenze des Speicherbereichs für globale und statische Variablen an, dies ist zugleich die Untergrenze des von _more_core verwalteten Speicherbereichs.
long _save_stacksize gibt die Grösse des Stacks an. _more_core verwaltet den Speicher bis zum Stackpointer (aktueller Stackpointer beim Aufruf von _more_core) minus des Werts von _save_stacksize. _save_stacksize wird mit 400 initialisiert. Um die Grösse des Stacks zu ändern, kann _save_stacksize ein anderer Wert zugewiesen werden, dies sollte möglichst vor dem ersten Aufruf einer Speicherverwaltungsfunktion erfolgen (am Anfang von main).
Signale
Ein Teil der Signale sind Prozessorexceptions zugeordnet, diese sind targetabhängig. Zur Anpassung an eine bestimmte Hardware sind zum einen die systemspezifischen Signale in der Headerdatei target/except.h zu definieren, zum anderen das targetspezifische globale Array _hard_sig_tab in der Grösse zu modifizieren. Die Quellen für die einzelnen Targets befinden sich in libsrc/target/param.c.
Die eigentliche Signalbehandlung ist von der Interruptquelle unabhängig.
Zeitfunktionen
Die hardwareabhängigen Zeitfunktionen dienen zum Setzen und Lesen der Echtzeituhr des Zielrechners. Die Prototypen befinden sich in sys/time.h. Die Zeit wird im C-üblichen Format (Anzahl der Sekunden die seit dem 1.1.1970 0 Uhr vergangen sind) übergeben.
Die folgenden Funktionen gehen alle davon aus, dass eine RTC vom Typ RTC4553, RTC72421 oder RTC63421 verwendet wird. Die Quelltexte für die RTC72421 und RTC63421 sind hardwareunabhängig geschrieben. Die Quelltexte befinden sich im Verzeichnis libsrc/sysshare.
Die Funktion _test_rtc testet ob eine RTC installiert ist, ob sie gesetzt wurde und um welchen Typ es sich handelt. Die Rückgabewerte sind in sys/time.h definiert. Die globale Variable _rtc_type enthält ebenfalls den Rückgabewert der Funktion. Die Funktion befindet sich in libsrc/sysshare/trtc421.c bzw. libsrc/sysshare/trtc4553.c. Die Funktion erkennt an bestimmten Registerinhalten der RTC, ob diese initialisiert ist. Um Fehlfunktionen zu vermeiden sollten Sie bei einem direkten Zugriff auf die RTC nicht verändert werden.
Die Uhrzeit wird mit _settime() gestellt. Der Rückgabewert ist Null falls das Stellen erfolgreich war.
Die Realisierung in libsrc/sysshare/st72421.c stellt die RTC vom Typ RTC72421 oder RTC63421 und in libsrc/sysshare/st4553.c die RTC4553 zur Verfügung...
Die Uhrzeit wird mit _gettime() gelesen. Die gelesene Zeit wird zurückgegeben. Die Realisierung in libsrc/sysshare/gt72421.c liest die RTC vom Typ RTC72421 oder RTC63421. Die Anpassung für die RTC4553 ist in dem File libsrc/sysshare/gt4553.
Falls die verwendete RTC eine Alarmfunktion unterstützt kann mit _setalarm die Alarmzeit gesetzt werden, mit _getalarm die Alarmzeit gelesen werden und mit _clearalarm kann ein vorhandener Alarm gelöscht werden. Die RTC63421 unterstützt diese Alarmfunktion, die Alarmzeit ist auf ein Jahr ab dem aktuellen Zeitpunkt beschränkt. Die Quelltexte für diese RTC befinden sich in den Dateien libsrc/sysshare/sa63421.c, libsrc/sysshare/ga63421.c und libsrc/sysshare/ca63421.c.
Da die Programmierung der RTC4553 sehr hardwareabhängig ist und möglichst wenig Inkompatibilitäten zwischen den RTCs erzeugt werden sollten, gibt es für diese RTC die Alarmroutinen ebenfalls. Die RTC4553 besitzt zwar gar keinen Alarmtimer, aber die Verwendung der Alarmroutinen erzeugen keine Fehlermeldung bei der Compilierung, da die Routinen dennoch in der Library als leere Funktionen enthalten sind. So ist es möglich Programme zu schreiben die völlig unabhängig von der Hardware sind. Die Quelltexte der leeren Alarm-Funktionen für die RTC4553 befinden sich in libsrc/sysshare/sa4553.c, libsrc/sysshare/ga4553.c und libsrc/sysshare/ca4553.c.
Clockfunktion
clock_t _clock(void (**)(void)) ist die systemspezifische Funktion zum Initialisieren und Lesen des Timerinterrupts. Der Funktion wird ein Funktionspointer übergeben. Falls dieser Pointer ungleich Null ist wird in jedem Timerinterrupt diese Funktion aufgerufen. Die Funktion liefert den aktuellen Timerzählerstand seit der Initialisierung des Timerinterrupts. In time.h wird das ANSI-Makro CLOCKS_PER_SEC und der Type clock_t definiert. Das Makro CLOCKS_PER_SEC wird als die globale Variable _clocks_per_sec definiert, die in sysclock.c gesetzt wird.
![]()
8.4 C-Start
ECO-C Programme sollen direkt auf der Hardware (ohne Betriebssystem oder sonstige Laufzeitumgebung) lauffähig sein, die dazu nötigen Vorarbeiten und Initialisierungen werden vom C-Start erledigt. ECO-C unterscheidet zwischen zwei verschiedenen C-Starts.
Der eine ist für den Betrieb unter dem Monitorprogramm (NICO) ausgelegt, er geht davon aus, dass die Hardware initialisiert ist. Der Quelltext befindet sich in libsrc/cpushare/crt0.c.
Der andere erledigt die gesamte Initialisierung und dient zur Ausführung von Programmen nach dem Reset im EPROM, also solche die allein ablauffähig sein sollen. Der Quelltext befindet sich in libsrc/target/srt0.c und libsrc/sysshare/srt0.h.
Da die _exit() Funktion direkt davon abhängig ist welcher C-Start verwendet wird, befindet sie sich im gleichen Modul. Einige Aktionen des C-Starts sind optional und benötigen entsprechende Definitionen beim Übersetzen.
Der C-Start "crt0.c" erledigt folgende Aufgaben:
- Kopieren des Datensegments.
- Löschen des BSS.
- Öffnen der Standard Dateideskriptoren (optional).
- Aufrufen von main mit den Argumenten argc, argv und dem Environment envp falls diese gültig sind.
- Aufruf von _cleanup() für die gepufferten Stdio-Funktionen in der Exit-Funktion (optional).
Die folgenden C-Starts, die aus crt0.c generiert wurden, werden für die implementierten Targets mitgeliefert:
crt0.o Alle genannten Aufgaben werden ausgeführt. cpy0.o Alle genannten Aufgaben werden ausgeführt. Auch wenn Relozier- und Ladeadresse des Datensegments übereinstimmen wird das Datensegment kopiert und bei einem erneuten Programmstart mithilfe dieser Kopie der initiale Zustand wieder hergestellt. Bei Angabe des -copydata Switch wird dieser C-Start dazugelinkt. crtmini0.o Minimalversion von crt0.o, optionale Aufgaben werden nicht ausgeführt. cpymini0.o Minimalversion von cpy0.o, optionale Aufgaben werden nicht ausgeführt. Der allein ablauffähige C-Start "srt0.c" erledigt folgende Aufgaben:
- Initialisieren der Speicherbereiche und sonstigen grundlegenden Hardware, falls der Targetrechner dies erfordert.
- RAM Test (optional).
- Grösse des RAMs bestimmen (optional).
- Kopieren des Datensegments.
- Löschen des BSS.
- Testen der Peripherie mit _test_port (optional).
- Initialisieren der Peripherie mit _init_port (optional).
- Öffnen der Standard Dateideskriptoren (optional).
- Aufrufen von main mit dem Environment envp falls dies gültig ist.
Folgende 3 C-Starts, die aus srt0.c generiert wurden, werden mitgeliefert:
srtmini0.o Nur die nötigsten Aufgaben werden vom C-Start ausgeführt, also alles was in obriger Liste nicht optional ist. srtnoio0.o Alle Aufgaben, ausser dem Öffnen der Standard Dateideskriptoren, werden ausgeführt. srt0.o Alle Aufgaben werden ausgeführt. ![]()
8.5 Libraries
Die erforderliche Laufzeitumgebung ist in drei Libraries aufgeteilt:
libc.a Die eigentliche C-Library, sie enthält alle ANSI-Funktionen und alle targetunabhängigen Erweiterungen. Sie wurde für den ensprechenden CPU Typ übersetzt. libs.a Diese enthält alle targetspezifischen Funktionen und unterscheidet sich somit für jedes Target. libgcc.a Die Library enthält alle vom Compiler implizit generierten Funktionsaufrufe wie die 32Bit Multiplikation für die MC68000 CPU. Diese Library ist prozessorspezifisch. Zusätzlich finden Sie drei weitere Bibliotheken für jedes Target:
libsd.c Die Biblithek für Programme die seriell Debugged werden sollen. Enhält die notwendigen Funktionen zur Kommunikation mit dem EDB. liblcda.a Bibliothek zur Ansteuerung eines alfanumerischen LC-Displays über den TLX-Bus mit Hilfe eines kleinen Zusatzboards. liblcdg.a Bibliothek zur Ansteuerung eines grafischen LC-Displays über den TLX-Bus. Übersetzen der Libraries
Das Verzeichnis libsrc enthält die vollständigen Quelltexte für die Bibliotheken und die C-Start Module. Folgenden Maketargets sind möglich:
all Alles wird in allen Verzeichnissen generiert. Dieses Maketarget ist Standard. clean Alle generierten Dateien werden gelöscht. install Alle Bibliotheksdateien und C-Start Module werden in der Verzeichnisstruktur auf die ECODIR zeigt installiert. Bei Tests sollten Sie sicherstellen, dass ihnen in den Verzeichnissen lib und include keine Daten überschrieben werden, da dies bei fehlerhaft modifizierten Quellen eine Neuinstallation erforderlich machen kann. uninstall Alle Dateien die aus dem Verzeichnis libsrc in die Verzeichnisse lib und include installiert werden, werden dort gelöscht.
Achtung! Machen Sie ein Sicherungskopie dieser Verzeichnisse bevor sie make uninstall starten.Sie können diese Prozeduren auch in den Unterverzeichnissen für die einzelnen Prozessoren bzw. Zielsysteme mit dem Makeutility starten. In den Unterverzeichnissen include bzw. include/sys können sie die Maketargets install und uninstall verwenden.
![]()
8.6 Hardwareanpassung
Die Anpassung der Hardware beschränkt sich auf wenige grundlegende Funktionen. Beispiele für die Anpassung an verschiedene Targetsysteme finden Sie im Verzeichnis libsrc. Für ein neues System ist es erforderlich ein Verzeichnis für das Targetsystem und ein Verzeichnis für die Targetcpu, falls es sich um einen noch nicht implementierten Prozessor handelt, einzurichten.
Die Anpassung beschränkt sich auf 6 Funktionsbereiche:
- Initialisierung und Test im C-Start. Es ist nur eine Anpassung des allein ablauffähigen C-Starts erforderlich. Der Quelltext defindet sich in libsrc/target/srt0.c. Es wird der systemunabhängige Teil in libsrc/sysshare/srt0.h verwendet.
- Die grundlegenden Eingabe- und Ausgabefunktionen. Die Quellen für die implementierten Zielsysteme befinden sich in libsrc/target/io.c.
- Die grundlegende Speicherverwaltung. Alle implementierten Systeme verwenden die gleiche Funktion _more_core, diese wird von malloc und Co. aufgerufen um vom System Speicherplatz anzufordern. Der Quelltext befindet sich in libsrc/sysshare/morecore.c.
- Die Verwaltung von Signalen. Die eigentlichen Funktionen sind systemunabhängig. Es müssen nur die unterstützten Signalnummern angegeben werden. Dies geschieht über die Parameterdatei, die Quelltexte befinden sich in libsrc/target/param.c.
- Die Grundfunktionen zum Setzen, Lesen und Testen der Echtzeituhr. Die vorhandenen Implementierungen benutzen alle die RTC72421 oder RTC63421. Die Quelletexte sind für alle Zielsysteme identisch, sie befinden sich in libsrc/sysshare. Für die RTC63421 wird darüberhinaus die Alarmfunktion unterstützt.
- Die Timerinterruptfunktion _clock wird von der ANSI-Funktion clock aufgerufen. Ein Timer im Targetsystem wird für den Timerinterrupt verwendet. Der Quelltext befindet sich in libsrc/target/sysclock.c.
Alternativ zur Anpassung der Library für ein neues Targetsystem lässt sich auch die Standardlibrary verwenden. Dann muss man aber auf alle Funktionen verzichten, die eine Hardwareunterstützung benötigen. Die nötigen systemspezifischen Funktionen können dann als Speziallösungen direkt auf die Hardware zugreifen. Dies mag für eine schnelle Problemlösung sinnvoll sein, bedeutet aber den Verlust von Portabilität und auf Dauer einen Mehraufwand, da sich immer wieder Reibungspunkte zwischen der eigenen Hardware- und der Standard-Library ergeben werden.
In jedem Fall wird der erste Schritt einer Systemanpassung die Erstellung eines oder mehrerer Header sein, in denen die Adressen und Namen der Peripherieregister und andere systemspezifische Parameter definiert sind. Beispiele hierfür befinden sich im Verzeichnis include/sys und den Targetunterverzeichnissen in libsrc.
![]()
9 Bei Problemen
- Das Programm lässt sich zwar übersetzen, aber funktioniert überhaupt nicht, selbst eine Testausgabe am Programmbeginn wird nicht ausgegeben.
Überprüfen Sie die Targeteinstellung, möglicherweise wurde für ein anderes System compiliert, oder es wurden falschen Relozieradressen angegeben. Zur Überprüfung können sie mit -v die Kommandozeile der einzelnen Compilerstufen ausgeben lassen.
- Das Programm lässt sich im RAM nur einmal ausführen oder verhält sich beim 2. Programmstart fehlerhaft.
Abhilfe: Mit -copydata übersetzten.
Die Daten werden standardmässig nicht neu initialisiert da hierfür eine zusätzliche Kopie des Datensegments erforderlich wäre. Mit -copydata übersetzt, werden die Daten kopiert, nachteilig ist der zusätztliche Speicherbedarf für die Kopie.- Make wird im Verzeichnis libsrc oder samples mit einer Syntaxfehlermeldung im Makefile abgebrochen.
Durch Namenskonflikte (z.B.: Turbo-C Make) wird nicht der mitgelieferte GNU-Make verwendet. Die Makefiles verwenden spezifische Erweiterungen, siehe auch GNU-Dokumentation.
Abhilfe: Entweder PATH entsprechend ändern oder den Namen des GNU-Make.- Make führt die Kommandos des Makefiles nicht aus.
Abhilfe: Es ist erforderlich, die Kommandozeilen im Makefile mit einem Tab-Zeichen und nicht mit Leerzeichen zu beginnen.
- Der Compiler bricht mit der Meldung "No include path in which to find includefile" ab, obwohl es sich um eine existierende Datei handelt.
Abhilfe: Der Compilertreiber ecoc.exe wird in der alten Version 1 verwendet. Die Environmentvariable PATH ist zu ändern.
- Der Compiler bricht mit der Meldung "cpp.exe: Invalid option '-lang-c'" ab.
Abhilfe: Es wurde der Preprozessor der Version 1 aufgerufen. ECODIR muss auf die aktuelle Version gesetzt werden.
- Die Compilierung endet nicht, jedoch wird häufig, oder gar ständig, auf die Platte zugegriffen.
Abhilfe: Es steht zu wenig freier Arbeitspeicher zur Verfügung, so dass die virtuelle Speicherverwaltung häufig Daten auf die Platte auslagern muss. Normalerweise müssten 2MB freier Speicher (incl. Erweiterungsspeicher, XMS, EMS) für eine zügige Compilierung ausreichen. Bei besonders grossen Programmen ist der Speicherbedarf eventuell grösser.
www.mct.de: Produkte: Software: ECO-C