Nanopascal ist ein Einphasen-Compiler mit direkter Codeerzeugung für Mikrocontroller aus der 8051-Familie.
1. Aufruf und Parameter
Der Compiler wird aufgerufen mit
nano51 <OPTION>... <QUELLDATEI>
Option | Bedeutung |
---|---|
-w | Der Compiler erzeugt Fehler statt Warnungen. |
-e | Der Compiler beendet die Übersetzung beim ersten Fehler. |
-l | Der Compiler schreibt ein Programmlisting in eine ".lst"-Datei. |
-c | Der Compiler schreibt auch den erzeugten Code in das Listing. |
-s | Der Compiler schreibt auch die Symboltabelle in das Listing. |
-t | Der Compiler schreibt jedes lexikalische Element in das Listing. |
-L [Dateiname] | Der Compiler schreibt das Listing in die angegebene Datei.-steht für die Standard-Ausgabe. |
-H [Dateiname] | Der Compiler schreibt den Maschinencode in die angegebene Datei. |
-V | Der Compiler zeigt seine Version an. |
Bei fehlerfreier Übersetzung speichert der Compiler den erzeugten Maschinencode in eine ".hex"-Datei im Intel-hex-Format.
2. Präprozessor
Der Compiler enthält einen sehr einfachen Präprozessor.
Die Steuerbefehle werden mit einem Punkt am Anfang der Zeile markiert (Wordstar lässt grüßen):
.fi
Dateiname- Fügt an dieser Stelle eine Include-Datei ein.
.ifdef
<name>- Bedingte Übersetzung: übersetzt die folgenden Zeilen nur, wenn der angegebene Name im Programm als Konstante, Variable oder Prozedur definiert ist.
.ifndef
<name>- Bedingte Übersetzung: übersetzt die folgenden Zeilen nur, wenn der Name nicht definiert ist.
.else
.endif
- Ende eines bedingt übersetzten Bereiches.
.show
text- Gibt während der Übersetzung den Text auf die Konsole aus.
.eof
- Beendet eine Include-Datei vorzeitig.
.
o+
.
o-
.
o=
zahl-
o
steht für eine Optiona
...z
. Setzt den Wert der angegebenen Option auf1
,0
bzw. die angegebene Zahl.
- Der Präprozessor besitzt keine eigene Symboltabelle, sondern fragt die Symboltabelle des Compilers ab.
- Am Ende einer Include-Datei enden automatisch und ohne Warnung alle mit
.ifdef
und.ifndef
begonnenen Bereiche.
3. Lexikalische Elemente
Programme werden aus folgenden Elementen zusammengesetzt:
- Namen:
-
Die Namen von Konstanten, Variablen, Prozeduren und Parametern dürfen
bis zu 80 Zeichen
lang sein. Die Großschreibung ist bedeutungslos. Als erstes Zeichen sind nur die Buchstaben
A
bisZ
erlaubt, danach dürfen weitere Buchstaben, Ziffern und die Zeichen_
und?
stehen. - Zahlenkonstanten:
-
Das Programm verarbeitet die Zahlen von 0 bis 65535 (216-1). Zahlen können auch
hexadezimal in den Schreibweisen
und$2a
oder binär in den Schreibweisen0x2a
und%00101010
angegeben werden.0b00101010
- Zeichenkonstanten:
-
Es gibt keinen Datentyp
CHAR
. Zeichenkonstanten in der Schreibweise
liefern den Zahlenwert des angegebenen Zeichens, also z.B. 65 für ein'c'
.A
- Zeichenkettenkonstanten:
-
Zeichenketten werden mit doppelten Anführungszeichen gebildet:
"Das ist ein Text.\n"
Das Zeichen
beginnt spezielle Gruppen:\
\n
fügt ein ZeichenLinefeed
(0x0a) ein.\r
fügt ein ZeichenCarriage return
(0x0d) ein.\b
fügt ein ZeichenBackspace
(0x08) ein.\e
fügt ein ZeichenEscape
(0x1b) ein.\"
fügt das doppelte Anführungszeichen (") (0x22) ein, das ohne\
die Zeichenkette beenden würde.\\
fügt ein Zeichen\
(0x5c) ein.
- Schlüsselwörter:
-
ADDR
,AND
,ARRAY
,
BEGIN
,BREAK
,BYTE
,
CASE
,CHAR
,CLR
,CONST
,CONTINUE
,CPL
,
DECR
,DIV
,DO
,
ELSE
,END
,EXIT
,EXTERN
,
FOR
,
HI
,
IF
,IN
,INCR
,INLINE
,INTEGER
,INTERN
,INTERRUPT
,
LO
,LOCK
,LOOP
,
MOD
,
NOT
,
OF
,OR
,
PROCEDURE
,PROGRAM
,
REPEAT
,RETURN
,
SET
,SHL
,SHR
,
STRING
,TABLE
,THEN
,TO
,
UNTIL
,
VAR
,
WHILE
,WORD
,
XOR
Auch bei den Schlüsselwörtern ist die Großschreibung bedeutungslos.
- Operatoren:
-
Arithmetisch:
+
!+
-
!-
*
~
Vergleiche:
<
<=
<>
=
>
>=
Logisch:
!
Speicherzugriff:
@
^
- Interpunktion:
-
(
)
,
.
..
:
:=
;
;
[
]
- Kommentare:
-
Kommentare beginnen mit
und enden mit{
. Kommentare können nicht geschachtelt werden und enden automatisch am Ende einer Datei.}
- In Namen und Schlüsselwörten ist die Großschreibung ohne Bedeutung.
- Es gibt keine negativen Zahlen.
- Es gibt keinen Datentyp
CHAR
. - Es gibt keinen Kommentar der Form
(* ... *)
.
4. Hauptprogramm
Das Hauptprogramm besitzt die Form:
PROGRAM <Name>; <Deklarationen> BEGIN <Befehle> END.
Der Einphasen-Compiler legt Programm und Variablen auf Speicheradressen ab und muß den zur Verfügung stehenden Speicherbereich kennen. Als Default wird verwendet:
Speicherart | Segment | von | bis | Größe | Kommentar |
---|---|---|---|---|---|
Programmspeicher | code | 0040 |
1fff |
8 KByte Flash/Eprom | Programmcode und Zeichenketten |
Interner Datenspeicher | data | 20 |
bf |
160 Bytes | davor: 4 Registerbanks, dahinter: Stack |
Externer Datenspeicher | xdata | 8000 |
ffff |
32 KByte RAM | Variablen der Klasse EXTERN |
Im Codebereich werden abgelegt (beginnend bei den niedrigen Adressen): der Code für die Prozeduren, Tabellen und das Hauptprogramm. Unter dem Codebereich muß Platz bleiben für die Interruptvektoren der verwendeten Interrupts. Vom oberen Ende absteigend werden die Zeichenkettenkonstanten abgelegt.
Im Programmkopf kann der zur Verfügung stehende Speicher spezifiziert werden:
PROGRAM <Name> (
[
mincode..maxcode]
[
minxdata..maxxdata]
[
mindata..maxdata]
);
- Wenn Sie Prozeduren oder Interrupts verwenden, sollten Sie am Anfang des Hauptprogrammes dem Stackpointer einen Startwert zuweisen.
- Der Stack wächst in Richtung größerer Adressen.
5. Prozeduren
PROCEDURE <Name> ( <Name1>: <Typ1>
, ... ) ;
<Deklarationen>
BEGIN
<Befehle>
END;
Parameter können durch Voranstellung des Schlüsselworts VAR
als VAR-Parameter markiert werden. An Var-Parameter können nur
interne Variablen
übergeben werden:
PROCEDURE <Name> ( VAR <Name1>: <Typ1>, ... ) ;
Parametern kann eine bestimmte Adresse zugeordnet werden:
PROCEDURE <Name> ( <Name1> [<Konstante>]: <Typ1> , ... ) ;
Parameter und lokale Variablen der Prozeduren werden nicht auf dem Stack angelegt, sondern auf festen Adressen im internen RAM. Deshalb sind keine rekursiven Aufrufe möglich.
- Keine rekursiven Aufrufe
- Keine Funktionen
6. Interrupt-Handler
Bei bestimmten Ereignissen kann der Prozessor die Programmabarbeitung unterbrechen und ein
Unterprogramm aufrufen. Das Ereignis ist ein Interrupt
und die dem Ereignis zugeordnete
Adresse der Interrupt-Vektor
.
Die möglichen Ereignisse sind durchnummeriert; jedem Ereignis ist eine feste Adresse
zugeordnet, und zwar dem Ereignis mit der Nummer i
die Adresse i*8-5
.
Bei Ereignis 2 (Überlauf des Timers 0) wird also das Unterprogramm auf Adresse 0x000b
aufgerufen.
Interrupt-Handler werden wie Prozeduren deklariert. Allerdings wird kein Name, sondern die Ereignisnummer angegeben:
INTERRUPT <Nummer>;
<Deklarationen>
BEGIN
<Befehle>
END;
Alternativ kann auch der Interrupt-Vektor angeben werden:
INTERRUPT @
<Adresse>;
An der vorgegebenen Adresse ist nur Platz für 8 Bytes. Ein Interrupt-Handler braucht zumeist mehr Platz, deshalb legt der Compiler den Interrupthandler an eine andere Stelle und belegt die vorgegebene Adresse nur mit einen Sprungbefehl.
Der Compiler erzeugt am Beginn des Interrupt-Handlers Code, um die Register A
,
B
, PSW
und DPTR
auf den Stack zu sichern.
Am Ende des Interrupt-Handlers werden die Ursprungswerte vom Stack zurückgeladen.
Wenn mehr als diese Register benötigt werden, kann eine alternative Registerbank ausgewählt werden mit der Schreibweise:
INTERRUPT <Nummer> [<Bank>];
Der Compiler erzeugt dann zusätzlich eine Zuweisung an das PSW
-Register.
Sie können die Register-Banks 1, 2 und 3 benutzen. Die Registerbank 0 ist dem Hauptprogramm
vorbehalten.
Bei extrem zeitkritischen Interrupt-Handlern können Sie den Compiler anweisen, keinen Code für das Retten und Restaurieren der Register zu erzeugen, und diese Aufgabe selbst übernehmen:
INTERRUPT <Nummer> [0];
7. Konstanten
Sie können Konstanten definieren:
CONST <Name> = <Ausdruck>;
Auf der rechten Seite sind nicht nur numerische und Zeichenketten-Konstanten erlaubt, sondern beliebige Ausdrücke. Natürlich müssen diese konstant auswertbar sein.
- Die
CONST
-Deklaration ersetzt auch das#define
.
8. Variablen
Die Prozessoren der 8051-Familie unterstützen zwei Speichersegmente für Daten:
- bis zu 256 Bytes internen Speicher und
- bis zu 64KBytes externen Speicher.
Auf den internen Speicher kann mit einer Vielzahl von Instruktionen zugegriffen werden, während für den externen Speicher nur Lesen und Schreiben möglich ist.
Zur Auswahl des Speicherbereiches verwenden Sie die Schlüsselwörter INTERN
und EXTERN
:
VAR <Name> : <Typ>;
VAR <Name> : <Typ> INTERN;
VAR <Name> : <Typ> EXTERN;
Es gibt folgende Typen:
BYTE
- Die vorzeichenlose Zahl mit dem Wertebereich 0 bis 255 belegt ein Byte.
WORD
- Die vorzeichenlose Zahl mit dem Wertebereich 0 bis 65535 belegt zwei Byte.
STRING
- Die Adresse einer Zeichenkettenkonstante im Programmspeichersegment belegt zwei Byte.
Array-Variablen werden immer beginnend mit 0 indiziert. Sie geben nur die Anzahl der Elemente und nicht den Indexbereich an:
VAR <Name> : ARRAY <Konstante> OF <Typ>;
VAR <Name> : ARRAY <Konstante> OF <Typ> INTERN;
VAR <Name> : ARRAY <Konstante> OF <Typ> EXTERN;
Der Compiler ordnet den Variablen Adressen in einem der Speichersegmente zu:
Gruppe | Auswahl | Segment | Adressvergabe |
---|---|---|---|
Skalar | - | Intern | aufsteigend |
INTERN |
|||
EXTERN |
Extern | aufsteigend | |
ARRAY |
- | Intern | absteigend |
INTERN |
aufsteigend | ||
EXTERN |
Extern | aufsteigend |
Sie können diese automatische Zuordnung umgehen:
VAR <Name> [<Konstante>]: <Typ>;
Auf diese Weise können zum Beispiel die Prozessorregister mit Namen versehen werden:
VAR PSW [0xd0] : BYTE;
9. Bitvariablen
Die acht Bits einer BYTE
-Variablen können benannt und dann über ihren Namen
angesprochen werden:
VAR <Name> : [ <Bit7> <Bit6> <Bit5> <Bit4> <Bit3> <Bit2> <Bit1> <Bit0> ];
Invertierte Bits werden durch ein vorangestelltes
markiert; nicht benutzte Bits werden durch !
ersetzt:
*
VAR <Name> : [ * * * * * * !<Bit1> <Bit0> ];
Auf diese Weise werden die Bits der Prozessorregister mit Namen versehen:
VAR PSW[0xd0]: [Cy AC F0 RS1 RS0 OV * Parity];
Man kann sie dann abfragen und ändern:
IF Parity THEN SET(Cy);
10. Tabellen (initialisierte konstante Arrays)
Tabellen von Zahlen oder Zeichenketten werden im Programmspeicher abgelegt:
TABLE <Name>: ARRAY <Konstante> OF <Typ> = [ <Konstante0>, <Konstante1> ... ];
Tabelleneinträge werden wie Elemente von Arrays angesprochen, können aber zur Laufzeit des Programmes nicht geändert werden.
11. Ausdrücke
Basiselemente sind Konstanten, Variable, Array-Elemente und TABLE
-Einträge:
- Elementen vom Typ
kann einWORD
oder.LO
angehängt werden, um nur das nieder- oder höherwertige Byte anzusprechen..HI
- Mit
kann die Adresse einer Variable abgefragt werden.ADDR(
<Variable>)
Die Basiselemente können mit Operatoren und Klammern zu komplexen Ausdrücken verknüpft werden:
- Arithmetische Operatoren:
-
+
Vorzeichenlose Addition 8bit oder 16bit -
Vorzeichenlose Subtraktion 8bit oder 16bit !+
Vorzeichenlose Addition mit Berücksichtigung des Carry-Flags 8bit oder 16bit Wird nur bei sehr maschinennaher Programmierung genutzt. !-
Vorzeichenlose Subtraktion mit Berücksichtigung des Carry-Flags 8bit oder 16bit *
Vorzeichenlose Multiplikation 8bit × 8bit = 16bit Der 8051 unterstützt keine 16bit Multiplikation oder Division. DIV
Vorzeichenlose Division 8bit÷8bit MOD
Vorzeichenloses Modulo 8bit÷8bit - Vergleichsoperatoren:
-
Mit diesen sechs Operatoren können Zahlenwerte und Zeichenketten jeweils untereinander verglichen werden:
=
<>
<
<=
>
>=
Außerdem können Sie abfragen, ob ein bestimmtes Bit in einem Byte gesetzt ist:
<Bitnummer> IN <Ausdruck>
Die Bitnummer muß eine Konstante zwischen 0 und 7 sein.
- Logische Operatoren:
-
Operator Bedeutung als Ausdruck Bedeutung als Bedingung AND
Bitweises Und Logisches Und OR
Bitweises inklusives Oder Logisches Oder XOR
Bitweises exklusives Oder ~
Bitweises Komplement NOT
Ungleich 0 Logisches Nicht - Klammern:
-
Klammerausdrücke werden mit
und(
gebildet. Die Klammertiefe ist durch die verfügbaren Register begrenzt.)
12. Befehle
- Zuweisung:
-
<Variable> := <Ausdruck>
- Befehlsfolge:
-
BEGIN
<Befehl1>;
<Befehl2>;
...
<Befehln>
END - If-Abfrage:
-
IF <Ausdruck> THEN <Befehl1>
IF <Ausdruck> THEN <Befehl1> ELSE <Befehl2>
- Case-Verzweigung:
-
CASE <Byte-Ausdruck> OF
<Konstantei1>: <Befehli>
<Konstantej1>..<Konstantej2>: <Befehlj>;
<Konstantek1>, <Konstantek2>: <Befehlk>;
<Konstantel1>, <Konstantel2>..<Konstantel3>: <Befehlj>;
ELSE <Befehln>
ENDDer ELSE-Teil darf entfallen.
- Es wird nur das niederwertige Byte des CASE-Ausdrucks ausgewertet.
- Als CASE-Konstanten sind nur Werte von 0 bis 255 zulässig.
- Die CASE-Verzweigung wird mit einer verketteten Liste von Vergleichen und Sprungbefehlen übersetzt. Einträge weiter vorne brauchen weniger Zeit.
- While-Schleife:
-
WHILE <Ausdruck> DO <Befehl>
So oft der Ausdruck einen Wert ungleich 0 hat, wird der Befehl ausgeführt.
- Repeat-Schleife:
-
REPEAT <Befehl> UNTIL <Ausdruck>
Der Befehl wird mindestens einmal ausgeführt und dann wiederholt, solange der Ausdruck einen Wert von 0 hat.
- For-Schleife:
-
FOR <Variable> := <Ausdruck1> TO <Ausdruck2> DO <Befehl>
- Der Ausdruck2 muß größer oder gleich dem Ausdruck1 sein.
- Der Befehl benutzt eine unsichtbare BYTE-Variable im internen Speicher.
- Die Anzahl der Durchläufe ist auf 256 begrenzt.
- Loop-Schleife:
-
LOOP
<Befehl1>;
<Befehl2>;
...
<Befehln>;
ENDBedingslose Schleife. Wird mit
EXIT
,BREAK
oderRETURN
verlassen. - Sprünge:
-
EXIT
BREAK
Mit
EXIT
oderBREAK
wird die innerste Schleife verlassen.CONTINUE
Der Befehl
CONTINUE
startet den nächsten Durchlauf der innersten Schleife.RETURN
Springt zum Ende einer Prozedur und verläßt damit die Prozedur.
- Es gibt kein
GOTO
('cause goto considered harmful)
- Es gibt kein
- Kritischer Bereich:
-
LOCK
<Befehl1>;
<Befehl2>;
...
<Befehln>;
ENDDie Folge von Befehlen wird mit gesperrtem Interrupt durchlaufen. Beim
END
wird der alte Zustand des Interrupt-Freigabe-Registers wiederhergestellt.- Im
LOCK
-Bereich sind die BefehleEXIT
,BREAK
,CONTINUE
undRETURN
nicht erlaubt.
- Im
13. Vordefinierte Prozeduren
- Bit-Bearbeitung:
-
Sie können ein einzelnes Bit setzen:
SET (<Bit-Variable>)
SET (<Byte-Variable>, <Bitnummer>)
Sie können ein einzelnes Bit löschen:
CLR (<Bit-Variable>)
CLR (<Byte-Variable>, <Bitnummer>)
Sie können ein einzelnes Bit invertieren:
CPL (<Bit-Variable>)
CPL (<Byte-Variable>, <Bitnummer>)
- Die Bitnummer muß eine Konstante zwischen 0 und 7 sein.
- Bei Word-Variablen wird nur das niederwertige Byte bearbeitet.
- Operationen auf Variablen im externen Segment sind nicht atomar.
- Inkrementieren und Dekrementieren:
-
Sie können den Wert einer Variable um 1 erhöhen:
INCR (<Variable>)
Sie können den Wert einer Variable um 1 erniedrigen:
DECR (<Variable>)
- Schieben:
-
Sie können die Bits einer Variable nach links schieben:
SHL (<Variable>)
Sie können die Bits einer Variable nach rechts schieben:
SHR (<Variable>)
14. Fehlermeldungen
- Ausdruck vom Typ "WORD" erwartet
- Ausdruck vom Typ "STRING" erwartet
- Der vorgefundene Ausdruck hat nicht den erwarteten Typ.
- Befehl erwartet
- Syntax-Fehler in Befehl oder Befehlsfolge.
- Bedingung wird immer zu "TRUE" ausgewertet.
- Bedingung wird immer zu "FALSE" ausgewertet.
- Eine logische Bedingung wird immer zu Wahr oder immer zu Falsch ausgewertet. Das kann ein Hinweis auf einen Programmierfehler sein.
- Compilerrestriktion 'case:range'
-
Ein
CASE
-Bereich darf nur 255 Elemente umfassen. - Die interne Grenze "MaxIncludeLevel" wurde erreicht.
- Die maximale Schachteltiefe für Include-Dateien ist erreicht.
- Die interne Grenze "MaxNameLength" wurde erreicht.
- Das lexikalische Element ist zu lang.
- Die interne Grenze "MaxSymbols" wurde erreicht.
- Die Symboltabelle ist voll.
- Die interne Grenze "MaxNodes" wurde erreicht.
- Der Ausdruck ist zu kompliziert.
- Die Resource "<name>" ist aufgebraucht.
- Der verfügbare Speicher oder die verfügbaren Register sind aufgebraucht. Der Speicher ist zu klein oder das Programm zu groß, es gibt zuviele Daten, oder ein Ausdruck ist zu komplex.
- Ein "<element>" wurde erwartet, aber nicht gefunden.
- Fehler in der Programmsyntax
- Fehler beim Lesen von "<datei>": <meldung>
- Fehler beim Oeffnen von "<datei>": <meldung>
- Fehler beim Zugriff auf eine Datei. Die Meldung des Betriebssystems wird durchgereicht.
- Interner Fehler "<procname>"
-
Kann nicht vorkommen.
Wenn doch, bitte senden Sie ein möglichst kurzes Programm, das den Fehler auslöst, an mich. - Konstanter Ausdruck erwartet
- Es wird eine Konstante benötigt, der vorgefundene Ausdruck läßt sich aber nicht konstant auswerten.
- Name "<name>" ist nicht definiert.
- Der benutzte Name wurde noch nicht definiert.
- Name "<name>" wurde mehrfach definiert.
- Der Name wurde im aktuellen Gültigkeitsbereich bereits einmal definiert.
- Typangabe (BYTE, WORD, STRING) erwartet
- Integer, Short, Float, Real, Double usw. kennt der Compiler nicht.
- Überlauf beim Berechnen eines konstanten Ausdrucks
- Das Ergebnis eines konstanten Ausdrucks läßt sich nicht als 16bit-Wert darstellen.
- Unbekanntes Praeprozessorkommando "<command>"
- Benutzen Sie nur die definierten Kommandos.
- Unerwartetes Ende der Datei
- Die Datei ist zu Ende, das Programm aber noch nicht.
- Ungueltiger Wert fuer "register bank"
- Als Register-Bank sind nur 1 bis 3 und die 0 erlaubt.
- Ungueltiger Wert fuer "interrupt vector"
- Der angegebene Interrupt-Vektor ist unzulässig.
- Ungueltiger Wert fuer "segment"
- Das Speichersegment, in dem die Variable definiert wurde, ist hier nicht erlaubt.
- Variable erwartet
-
In einer
FOR
-Scheife, als Argument für einenVAR
-Parameter einer Prozedur oder als Argument einer eingebauten Prozedur wird eine Variable erwartet. - Variable, Konstante oder '(' erwartet
- Syntax-Fehler in einem Ausdruck.
- Variable oder Konstante erwartet
- In einem Ausdruck darf ein Name nur eine Variable oder eine Konstante bezeichnen.
- Verirrtes Zeichen "<zeichen>" im Programm
- Mit diesem Zeichen beginnt kein gültiges lexikalisches Element.
- Zeichenkette nicht beendet
- Zeichenketten sind auf eine Zeile begrenzt und müssen auf dieser Zeile mit einem doppelten Anführungszeichen beendet werden.
15. Beschränkungen
Diese Beschränkungen können durch Neuübersetzung des Compilers verändert werden:
- MaxIncludeLevel=4
-
Maximale Schachteltiefe für Dateien inklusive der Hauptdatei.
Auf dieser Tiefe werden weitere.fi
-Direktiven ignoriert. - MaxInputLineLength=4096
-
Maximale Länge einer Quellcode-Zeile.
Längere Zeilen werden rücksichtslos in mehrere Zeilen umgebrochen. - MaxNameLength=80
-
Maximale Länge eines
lexikalischen Elementes.
Längere Elemente werden rücksichtslos beschnitten. - MaxSymbols=1000
- Größe der Symboltabelle.
- MaxNodes=100
-
Maximale Größe eines
Ausdrucks
in der internen Repräsentation.
Komplexere Ausdrücke werden nicht mehr übersetzt.