Mikrocomputertechnik


Prof. Jürgen Plate

3. Programmierung des Mikroprozessors 68HC11

3.1 Überblick

Programmiermodell

Da man wohl kaum einen Schaltplan des CPU-Herstellers bekommt und da dieser auch viel zu komplex ist, wird für die Programmierung mit dem sogenannten "Programmiermodell" gearbeitet. Dies umfaßt: Der Prozessor 68HC11 ist eine Weiterentwicklung des 6800 (weitgehend befehlskompatibel, erweiterter Befehlssatz, zusätzliche Register). Der 68HC11 besitzt vier 8-Bit-Register und fünf 16-Bit-Register:

Einteilung der Befehle

Der 68HC11 kennt 59 verschiedene Grundbefehle überschaubarer Befehlssatz. Anwendung der Befehle mit unterschiedlichen Registern und Adressierungsarten führt zu 1464 verschiedenen OP-Codes. Der OP-Code besteht (einschließlich Angabe über Adressierungart) aus einem, zwei oder drei Bytes: Der Adreßteil (falls vorhanden) besteht aus einem oder zwei Bytes; es gibt also 1-, 2-, 3-, 4- und 5-Byte-Befehle. Das Befehlsformat kann also sein:

Das "prebyte" erweitert den Befehlsumfang, so daß mehr als 256 Befehle möglich sind. Der Nachteil dabei ist, daß die Abarbeitung des Befehls länger dauert und der Befehl natürlich auch ein Byte mehr an Speicher braucht.

Adressen werden in zwei aufeinanderfolgenden Speicherbytes abgelegt und zwar in der Form

AHAL
eaea+1

Mit "ea" wird die effektive Adresse = echte Adresse = Adresse des Operanden im Speicher bezeichnet. Je nach Adressierungsart (siehe unten) ist eine Adreßberechnung notwendig. Das Ergebnis der Adreßrechnung landet im Adreßregister und wird dann zur Adressierung des Speichers verwendet.

Befehlsklassen:

Angaben über gültige Adressierungsarten, Länge, CC-Beeinflussung, Dauer (Anzahl der Taktzyklen) stehen in der Befehlstabelle.

3.2 Adressierungsarten

Die meisten Befehle gestatten mehrere Adressierungsarten, aber nicht alle Adressierungarten lassen sich auf jeden Befehl anwenden. Jede zulässige Adressierungsart bei einem Befehl führt zu einen eigenen OP-Code. Die zulässigen Adressierungsarten und der jeweilige OP-Code lassen sich der Befehlsliste entnehmen. Die tatsächliche Länge und Ausführungszeit eines Befehls hängen von der Adressierungsart ab. Die Zahl der Bytes eines Befehls und seine Ausführungszeit lassen sich aus der Befehlsliste entnehmen.

Konstanten-Adressierung (Immediate Addressing)

Die Daten folgen unmittelbar auf den OP-Code. Je nach Befehl/Register sind es 8 Bit (1 Byte) oder 16 Bit (2 Byte). Im Assembler wird dies durch Voranstellen von "#" kenntlich gemacht, zum Beispiel:
   
LDAA #20 - Lade Akku A mit dem Wert 20 

Absolute Adressierung

Absolut vollständige Adressierung (Extended Addressing)

Der Adreßteil des Befehls enthält eine vollständige 16-Bit-Adresse. Es ist eine Adressierung des gesamten Adreßraums möglich. Zum Beispiel:
 
LDAA $A174 - Lade Akku A mit dem Inhalt der Speicherzelle $A174 

Absolut unvollständige Adressierung (Zero Page / Direct Addressing)

Der Adreßteil enthält nur den niederwertigen Teil (LSB) der Adresse. Der höherwertige Teil wird auf Null gesetzt. Damit lassen sich die Adressen zwischen 0 und 255 ($0 - $FF) erreichen. Der Befehl ist ein Byte kürzer als bei der absoluten Adressierung und deshalb auch schneller in der Ausführung. Im Assembler wird dies durch Voranstellen von "<" gekennzeichnet. Zum Beispiel:
 
LDAA <$80 - Lade Akku A mit dem Inhalt der Speicherzelle $80 

Implizite Adressierung (Inherent Addressing)

Alle Adreßangaben sind im OP-Code des Befehls enthalten, es gibt keinen Adreßteil.

Indizierte Adressierung (Indexed Addressing)

Die effektive Adresse ergibt sich hier aus dem Inhalt eines Adreßregisters (z. B. X oder Y) und einer dazu addierten Distanz (Offset), die auch Null sein kann. Der Offset wird grundsätzlich konegativ bewertet (-128 .. +127). Die Adreßrechnung erfolgt modulo 64 K (bleibt also immer im Adreßraum des 68HC11). Zum Beispiel:
CLR  5,X        (ergibt als Code: 6F 05)
CLR -5,X        (ergibt als Code: 6F FB)

Anmerkungen zur Progam Counter-relativen Adressierung:

Es wird der Stand des Program Counterszum Zeitpunkt der Befehlsausführung verwendet (Dies ist nicht die Adresse, an der der Befehl beginnt, denn der Program Counter enthält ja immer die Adresse des nächsten, auszuführenden Befehls. Der Program Counter ist gleich der Adresse des Befehls + Länge des Befehls). Die Program Counter-relative Adressierung wird oft bei Sprungbefehlen verwendet. Zweck: lageunabhängige Programme (Ablauf an jeder Position im Speicher).

Befehlsaufbau: offset = Zieladresse - Befehlsadresse - Befehlslänge

Befehlsausführung: Zieladresse = Befehlszählerstand + offset

Alle Adressierungsarten im Überblick

zeigt die folgende Grafik:

3.3 Assembler-Befehlsaufbau

Der Assembler ist ein Programm zur Übersetzung mnemotechnischer Befehlsbezeichnungen in die Maschinensprache. Es erfolgt 1:1-Abbildung der Befehle in die Maschinensprache, d. h. eine Assemblerzeile entspricht einem Befehl.

Maschinensprache:

Assemblersprache: Allgemeiner Aufbau eines Assemblerbefehls:

Marke OP-Code Operand/Adresse Kommentar

Beispiel:

  START        LDAA         DAT1          ; Zähler initialisieren 

Marken-Feld: (optional)
Das Feld beginnt in Spalte 1 (also ganz vorne). Die Marke besteht aus Buchstaben, Ziffern und Sonderzeichen (".", "_", "@") und beginnt mit einem Buchstaben. Die Länge ist auf 31 Zeichen begrenzt. Die Marke kann durch einen ":" abgeschlossen werden, dieser wird vom Assembler ignoriert. Es wird bei manchen Assemblern - wie beispielsweise auch in C - zwischen Groß- und Kleinschreibung unterschieden. Registernamen (A, B, C, D, X, Y, S) sind reserviert und dürfen nicht verwendet werden. Marken bezeichnen Konstante oder Speicheradressen.

OP-Code-Feld: (muß vorhanden sein)
Falls keine Marke vorhanden, muß wenigstens ein Leerzeichen davor stehen. Sinvoller ist es jedoch die Opcodes in einer Spalte untereindander zu schreiben. Die Befehle werden durch Mnemonics aus zwei bis vier Buchstaben dargestellt. Bei einigen Befehlen ist eine Registerangabe direkt an das Mnemonic anzuhängen (z. B. ADDA, ADDB, ADDD). In diesem Feld kann auch eine Assembler-Direktive (Pseudobefehl) stehen Anweisung für den Assembler, Genaueres später.

Operanden/Adreß-Feld: (abhängig vom OP-Code)
Dieses Feld enthält den Operanden des Befehls, meist eine Adresse oder eine Konstante. Aufbau hängt von der Adressierungsart ab (später mehr dazu). Falls eine Adressierungsart mehrere Operanden verlangt (z. B. Register + Offset) erfolgt Trennung der Operanden mit Komma.
Bei vielen Assemblern sind arithmetische und logische Ausdrücke aus Konstanten möglich. Die Adreßangabe (Offset) kann erfolgen als: Im Operandenfeld kann auch mit den vier Grundrechenarten (+ - * /) und Klammern gearbeitet werden. Beachten Sie, daß die Ausdrücke zur Assemblierungszeit ausgewertet werden - nicht zur Laufzeit des Programms!

Kommentar-Feld: (optional)
Das Kommentarfeld wird durch den Strichpunkt oder zwei Schrägstriche eingeleitet. Es enthält beliebige Zeichenfolgen und wird vom Assembler ignoriert.

Zusätzlich sind auch reine Kommentarzeilen möglich, die mit einem "*" in Spalte 1 beginnen.

Der Assembler hat zwei Durchläufe:
  1. Durchlauf (pass one): Syntaxprüfung und Aufbau des Adreßbuchs.
  2. Durchlauf (pass two): Vollständigkeitsprüfung und Aufbau des Zielprogramms.
Beispiel:

1. Quellcode

Adresse Inhalt Marke Befehl Operand Kommentar
                   ORG $8000 In den Speicher ab $8000
    MAIN BRA MARKE relativer Vorwärtssprung
      JMP MARKE absoluter Vorwärtssprung
      BRA MAIN relativer Rückwärtssprung
    MARKE JMP MAIN absoluter Rückwärtssprung
      END    

2. Erster Assemblerlauf: Adresspegel und Code eintragen

Adresse Inhalt Marke Befehl Operand Kommentar
      ORG $8000 In den Speicher ab $8000
8000 20 ?? MAIN BRA MARKE relativer Vorwärtssprung
8002 7E ?? ??   JMP MARKE absoluter Vorwärtssprung
8005 20 ??   BRA MAIN relativer Rückwärtssprung
8007 7E ?? ?? MARKE JMP MAIN absoluter Rückwärtssprung
      END    

3. Zweiter Assemblerlauf: Adressen absättigen

Adresse Inhalt Marke Befehl Operand Kommentar
      ORG $8000 In den Speicher ab $8000
8000 20 05 MAIN BRA MARKE relativer Vorwärtssprung
8002 7E 80 07   JMP MARKE absoluter Vorwärtssprung
8005 20 F9   BRA MAIN relativer Rückwärtssprung
8007 7E 80 00 MARKE JMP MAIN absoluter Rückwärtssprung
      END    

3.4 Pseudobefehle (Assembler-Direktiven)

Pseudobefehle erzeugen keine Maschinenbefehle sondern

Der Assembler führt einen Zähler, der Auskunft über den aktuellen Adreßpegel gibt. Dieser 'Location Counter' dient beispielsweise zum Berechnern von Sprungadressen. Die Pseudobefehle werden im OP-Code-Feld einer Assemblerzeile angegeben. Unser Assembler kennt folgende Direktiven:

ORG Origin
Der Location Counter wird auf die im Operandenfeld angegebene Adresse gesetzt, z. B.:
     ORG  $8000
EQU Equate
Definition eines Namens (Konstante, Marke). Der im Operandenfeld angegebene Wert wird dem im Markenfeld angegebenen Namen gleichgesetzt, z. B.:
ETX  EQU  3
CR   EQU  $D
LF   EQU  $A
PUFL EQU  $100
DC.B Define Constant Byte
Reservierung von Speicherplatz und Belegung mit den im Operandenfeld angegebenen konstanten Byte-Werten. Mehrere Operanden durch Komma trennen! Z. B.:
TAB  DC.B  $0D,$0A,$03
Neben einfachen Konstanten sind in (einfache oder doppelte) Hochkomma eingeschlossene Zeichenketten möglich. Die Hochkommas gehören dabei nicht zur Zeichenkette. In den Zeichenketten sind folgende Sonderzeichen erlaubt:
  • \a Bell ($07)
  • \f Formfeed ($0C)
  • \n Newline ($0A)
  • \r Carriage Return ($0D)
  • \t Tabulator ($09)
  • \\ \
Z. B.:
TEXT  DC.B "Hello World!\n\r"
DC.W Define Constant Word
Reservierung von Speicherplatz und Belegung mit den im Operandenfeld angegebenen konstanten 16-Bit-Werten. Mehrere Operanden durch Komma trennen, z. B.:
ATAB  DC.W  $FF30,$FF45,$FFA0
DC.L Define Constant Longword
Reservierung von Speicherplatz und Belegung mit den im Operandenfeld angegebenen konstanten 32-Bit-Werten. Mehrere Operanden durch Komma trennen, z. B.:
XTAB  DC.L  $FF30FF45,$F000FFA0
DS.B
DS.W
DS.L
Define Storage Bytes/Words/Longwords
Reservieren von Speicherbytes, Speicherworten (16 Bit) oder Langworten (32 Bit) ohne Vorbelegung. Der Operand gibt die Anzahl der zu reservierenden Bytes an, z. B.:
PUFFER  DS.B 100
ADRTAB  DS.W 23
INCLUDE Einfügen einer Quelldatei an der entsprechenden Stelle. Der Dateiname wird entweder in < ... > (voreingestelltes Include-Verzeichnis) oder " ... " (beliebiger Dateipfad) eingeschlossen, z. B.:
     INCLUDE "definitionen.a"
     INCLUDE <HC11.h>
NOLIST Unterdrücken der Erzeugung eines Programmlistings bis zum nächsten LIST-Befehl.
LIST Fortsetzen des Listings nach einem voher pazierten NOLIST-Befehl.
END Ende des Assemblerprogramms. Optionaler Befehl am Programmende. Wird aus Gründen der Kompatibilität vom Assembler akzeptiert, kann aber weggelassen werden.

Anmerkung: Da bei einem Microcontroller zwischen ROM bzw. EEPROM-Speicher und RAM unterschieden werden muß, ist auch die Reservierung von Speicher von dieser Unterscheidung betroffen. Mit DC.B; DC.W oder DC.L vordefinierte Konstante liegen im ROM/EEPROM-Bereich, Mit DS.B, DS.W oder DS.L reservierter Speicher im RAM-Bereich.

Grundsätzlicher Aufbau eines 68HC11-Assemblerprogramms

1. Konstantendefinitionen

Hier werden alle Definitionen für Werte und Adressen (EQU-Anweisung) vorgenommen. Diese "Namensdefinitionen" belegen keinen Speicherplatz, sondern dienen nur der besseren Lesbarkeit und Wartbarkeit des Programms. Zum Beispiel:
   STACK    EQU  $7FFF  Stackbereich
   PROG     EQU  $8000  Programmbereich
   DATA     EQU  $2000  Datenbereich (auch Adr. 0 möglich)
   RVECT    EQU  $FFFE  Reset-Vektor

   SCCR1    EQU  $102C
   SCCR2    EQU  $102D
   CR       EQU  13     Carriage-Return-Zeichen 
   LF       EQU  10     Linefeed-Zeichen 
   ETX      EQU  3      Textende 
   MAXDATA  EQU  100 
Wichtig sind die ersten fünf Zeilen, die bei keinem Programm fehlen dürfen. Sie legen die Adressbereiche für RAM und EEPROM fest und definieren die Adressen der beiden Vektoren, die immer gesetzt werden müssen.

2. Variablendefinitionen

Hier werden alle Daten definiert, die Speicherplatz belegen. Es ist ratsam, den Datenbereich durch eine eigene ORG-Anweisung einzuleiten, um die Daten an definierter Stelle im Speicher abzulegen. Bei einem Steuerungsrechner muß auf jeden Fall in Datenbereich (RAM) und Programmbereich (PROM) unterschieden werden. Man kann die Variablen auch nach dem Programmcode ablegen. Zum Beispiel:
            ORG  DATA           Datenbereich z.B. ab Adresse $2000 
   COUNT    DC.B 0              Wert mit Null vorbelegen 
   PUFFER   DS.B 80             80 Byte Pufferbereich reservieren 
   TEXT     DC.B 'Hello World'  Text, abgeschlossen durch Zeilen- 
            DC.B CR, LF, ETX    wechsel und Nullbyte 

3. Programmbereich

Nun folgt das eigentliche Progamm. Es wird immer mit einer ORG-Anweisung an eine definierte Adresse gelegt. Diese Adresse ist auch Startadresse des Programms.

Achtung: Der Assembler kümmert sich nicht um die Adreßlage des Programms, auch Überlappungen mit dem Datenbereich werden nicht als Fehler gemeldet aufpassen.

Es gibt zwei Möglichkeiten der Anordnung von Haupt- und Unterprogrammen. Üblich ist es, das Hauptprogramm am Anfang zu postieren und dann die Unterprogramme folgen zu lassen. Wer lieber die UP zuerst stehen hat, muß das HP mit einer Marke versehen (z. B. "MAIN") und nach der ORG-Anweisung als erste Anweisung einen Sprung zum HP eintragen, z. B.:

            ....... Definitionen (1. und 2.) 

            ORG  PROG           Programmbereich, z. B. $8000
            JMP  MAIN 

            ....... Unterprogramme 

   MAIN     LDS  #STACK         Stackpointer setzen, sonst geht nichts
             .
             .
             .
             .

            ORG  RVECT
            DC.W MAIN           Reset-Vektor auf Programmanfang setzen
             .
             .                  ggf. weitere Vektoren setzen
             .                  (z. B. INT)
             .
      
            END 

3.5 CPU-Befehle

Anmerkungen:

Transport-Befehle

Ladebefehle:

Load Register from Memory or Constant

CLR Clear Akkumulator: 0 Akkumulator-Register

Speicherbefehle:

Store Register into Memory (8 Bit/16 Bit)

  • STAA, STAB (8 Bit): (Reg) adr.
  • STD, STS, STX, STY (16 Bit): (Reg) adr., adr+1

    Clear Memory

    Transport zwischen Registern:

    Kopie des Inhalts eines Registers in ein anderes (transfer)

    Bit X im Statusregister kann mittels TAP nicht gesetzt werden.

    Austausch von Registerinhalten (exchange)

    Beispiel: Tausch A B
    Benötigt wird eine Hilfsvariable im Daten-Speicherbereich

        STAA   HILF
        TBA
        LDAB   HILF
    
    
    Beispiel: Tausch X Y
    *             Beispielbelegung  X  Y  D
    *                               1  2  3
        XGDX                        3  2  1
        XGDY                        3  1  2
        XGDX                        2  1  3
    

    Arithmetische Befehle

    Dies sind Befehle, die arithmetische Operationen bewirken. Im allgemeinen steht ein Operand in einem Akku und das Ergebnis wieder im gleichen Akku.

    Additionsbefehle:

    Addieren (add)

    Addieren mit Übertrag (add with carry)

    Beispiel: Addition zweier 4-Byte-Zahlen

    1. Operand: Adresse $41 - $44 
    2. Operand: Adresse $45 - $48 
    Ergebnis:   Adresse $49 - $4C 
    
          LDAA $44      Niederwertiges Byte addieren 
          ADDA $48 
          STAA $4C 
          LDAA $43      Zweites Byte addieren 
          ADCA $47 
          STAA $4B 
          LDAA $42      Drittes Byte addieren 
          ADCA $46 
          STAA $4A 
          LDAA $41      Höchstwertiges Byte addieren 
          ADCA $45 
          STAA $49 
    

    Erhöhen (increment)

    Subtraktionsbefehle:

    Subtraktion (subtract) = Addition des Zweierkomplements

    Subtraktion mit Übertrag (subtract with carry)

    Erniedrigen (decrement)

    Vorzeichenwechsel (negate)

    Multiplikation und Division:

    Multiplizieren (multiply)

    Dividieren (divide)

    Beispiele:

    IDIV:                             FDIV:
    
    $8421 : $0004 = $2108  R $0001    $1000 : $2000 = $.8000  R $0000
    $0001 : $FFFF = $0000  R $0001    $0001 : $FFFF = $.0001  R $0001
    $FFFF : $0001 = $FFFF  R $0000    $FFFF : $0001 = unzulässig, V=1
    

    Weitere arithmetische Befehle:

    DAA Decimal Adjust A

    Beispiel: Addition zweier 4-stelligen BCD-Zahlen

    1. Zahl:  Adr. $31-$32 
    2. Zahl:  Adr. $33-$34  
    Ergebnis: Adr. $35-$36 
    
        LDAA $32   niederwertige 2 Ziffern 
        ADDA $34   binär addieren 
        DAA        BCD-Justage 
        STAA $36 
        LDAA $31   höherwertige 2 Ziffern 
        ADCA $34   binär addieren mit Carry! 
        DAA        BCD-Justage 
        STAA $35 
    

    Logische Befehle

    Bei logischen Befehlen erfolgt die bitweise logische Verknüpfung eines Akku-Inhalts mit einem Operanden (Speicherinhalt oder Konstante).

    Und-Verknüpfung (and)

    Beispiele:

    1) Löschen der 4 höherwertigen Bits von Akku A 
    
       ANDA #$0F
    
    2) Rücksetzen des 5. Bits
    
       ANDA #%11101111 
    

    Oder-Verknüpfung (or)

    Beispiele:

    1) Setzen der 4 höherwertigen Bits von Akku A 
    
         ORA #$F0
    
    2) Setzen des 5. Bits 
    
         ORAA #%00010000 
    

    Exclusiv Oder (exor)

    Beispiel:

    Invertieren der 4 höherwertigen Bits von Akku A 
    
        EORA  #%11110000 
    

    Bit prüfen (bit test)

    Invertieren (complement)

    1-Komplement Invertieren aller Bits
    Auch auf auf Speicherbytes anwendbar.

    Beispiel: Verwendung des Speicher-Bytes $41 als Software-Flip-Flop. Zu Beginn des Programms muß der Inhalt von $41 gelöscht werden!

      COM $41       Inhalt wird $FF 
       . 
       . 
      COM $41       Inhalt wird wieder 0 
       . 
       . 
      usw. 
    

    Schiebe- und Rotationsbefehle

    Die Schiebebefehle bewirken die Verschieben eines Akku- oder Speicherinhalts um eine Stelle nach rechts oder links. Bei den Rotationsbefehlen erfolgt die Ringverschiebung eines Akku- oder Speicherinhalts unter Einbeziehung des C-Flags nach links oder rechts. Die Befehle sind auf A, B, D oder einen Speicherinhalt anwendbar. Die Realisierung auf Speicherinhalt erfolgt durch: Laden in Rechenregister, Schiebeoperation im Rechenregister, Zurückspeichern des Register-Inhalts).

    Logische Verschiebung:

    LSRx Logical Shift Right:
    von links wird 0 nachgeschoben: 0 MSB
    LSB C-Flag

    LSLx Logical Shift Left:
    von rechts wird 0 nachgeschoben: 0 LSB
    MSB C-Flag

    Arithmetische Verschiebung:

    ASRx Arithmetic Shift Right:
    Entspricht Division durch 2
    MSB MSB (Vorzeichen bleibt erhalten)

    ASLx Arithmetic Shift Left:
    von rechts wird 0 nachgeschoben: 0 LSB
    MSB C-Flag
    Identisch mit LSL!

    Rotationsbefehle:

    Hin- und Herschieben des Akku- oder Speicherinhalts ohne ihn zu zerstören (anschließend Test des C-Flags). Vorzeichenlose duale Multiplikation und Division.

    ROLx Rotate Left
    C-Flag LSB
    MSB C-Flag

    RORx Rotate Right
    C-Flag MSB
    LSB C-Flag

    Beispiele:

    1) Multiplikation von Akku A mit 10: (A) * 10  A 
       ASLA        (A)*2  A 
       STAA $40    Erg. zwischenspeichern 
       ASLA        (A)*4  A 
       ASLA        (A)*8  A 
       ADDA $40    (A)*8 + (A)*2  A 
    
    2) Akku D nach links schieben: 
                                           A         C         B
       ASLB   MSB von B --> Carry     +----------+__+-+__+----------+
       ROLA   Carry --> LSB von A     +----------+  +-+  +----------+
    
    3) Zerlegung eines Bytes in zwei Halbbytes: 
    Die Halbbytes sind in getrennten Speicherzellen als niederwertiges Halbbyte 
    zu speichern (höherwertiges Halbbyte ist auf 0  zu setzen). 
    
       $70: zu zerlegendes Byte 
       $71: höherwertiges Nibble 
       $72: niederwetiges Nibble 
    
       LDAA $70     Byte holen   (binär: xxxxyyyy) 
       ANDA #$0F    niederwertiges Nibble maskieren 
       STAA $72     speichern     (binär: 0000yyyy)  
       LDAA $70     Byte nochmal holen 
       LSRA         4 x nach rechts schieben  (0xxxxyyy) 
       LSRA                                   (00xxxxyy) 
       LSRA                                   (000xxxxy) 
       LSRA                                   (0000xxxx) 
       STAA $71     höherwertiges Nibble speichern 
    

    Test- und Statusregisterbefehle

    Sie dienen der Überprüfung von Datenwerten auf bestimmte Eigenschaften. Die Überprüfung erfolgt durch arithmetische und logische Operationen. Als Ergebnis werden bestimmte Flags des CC-Register beeinflusst, der Inhalt der Akkus bleibt unverändert. Die Flags können zur Steuerung bestimmter Sprungbefehle verwendet werden Programmfortsetzung abhängig von Dateneigenschaften Steuerbefehle. Beispiele bei den Sprungbefehlen.

    Test

    Die Befehle sind anwendbar auf A, B und den Speicher. Test auf Null und negativ.

    Vergleich (compare)

    ("Gedachte Subtraktion")
    Die Befehle sind anwendbar auf A, B, D, X und Y. Es wird jeweils ein Registerinhalt mit einem Speicherinhalt oder einer Konstanten verglichen (Ausnahme: CBA). Der Vergleich erfolgt durch eine Subtraktion ohne Anfallen des Ergebnisses. Der Vergleich auf Null und "negativ" ermöglicht alle Vergleiche von Werten. Mathematisch gesehen werden die links stehenden Ungleichungen in die rechts stehenden umgesetzt:

    X1 = X2 X1 - X2 = 0
    X1 < X2 X1 - X2 < 0
    X1 > X2 X1 - X2 > 0

    Durch bedingte Sprungbefehle kann dann abhängig vom Ergebnis im Programm verzweigt werden. Es gibt 8-Bit- und 16-Bit-Vergleiche (je nach Wortbreite des Registers).

    Bitorientierte Befehle

    Bitbefehle Statusregister

    Diese Befehle erlauben das komfortable Setzen und Rücksetzen einiger Bits des Statusregisters.
    • CLC
    clear carry flag X X X X X X X 0
    • CLI
    clear interrupt flag X X X 0 X X X X
    • CLV
    clear overflow flag X X X X X X 0 X
    • SEC
    set carry flag X X X X X X X 1
    • SEI
    set interrupt flag X X X 1 X X X X
    • SEV
    set overflow flag X X X X X X 1 X

    Bitbefehle Speicher

    Diese Befehle erlauben das Setzen oder Löschen einzelner Bits in Speicherworten. Als einzige Befehle haben sie zwei Operanden, die Adresse des zu ändernden Speicherbytes und eine konstante Maske. Da die Maske immer konstant sein muß, entfällt hier auch das "#"-Zeichen. Für jedes 1-Bit in der Maske wird das entsprechende Speicherbit gesetzt (BSET) oder gelöscht (BCLR).

    Beeinflussung des Systemzustands

    Dies sind Befehle, welche die Programmausführung anhalten. Sie dienen der Synchronisation des Programmablaufs mit externen Ereignissen.

    Warte auf Interrupt (wait for interrupt)

    Ablage aller Register auf dem Stack, dann warten auf Interrupt.

    Stop

    Reaktion hängt ab von S-Flag im Statusregister: Freigabe des STOP-Befehls:
            TPA               Statusreg. nach Akku A
            ANDA   #$7F       Freigabe (S-Flag auf 0)
            TAP               Akku A nach Statusreg.
    
    Für den Fall S=0 und X=1 wird beim Aktivieren des XIRQ-Eingangs der Prozessor nach einem STOP-Befehl "aufgeweckt" und er macht im Programm ganz normal weiter.

    Sprungbefehle

    Alle Sprungbefehle verändern den PC-Inhalt Programmausführung wird an anderer Stelle fortgesetzt. Das Statusregister wird nicht beeinflußt. Einige Befehle speichern Registerinhalte auf dem Stack oder holen Registerinhalte von dort wieder zurück.

    Unbedingte Sprungbefehle (jump, branch)

    JMP adr. adr. PC

    BRA dest.

    Bedingte Sprungbefehle (Bedingte "Verzeigungsbefehle")

    Hier gibt es nur PC-relative Adressierung (branch conditionally). Es gibt 8 Paare von zueinander komplementären Befehlen. Die Unterscheidung erfolgt in der jeweiligen Verzweigungsbedingung. Als Verzweigungsbedingung dienen (1 oder mehr) Bits des des Statusregisters Verzweigung abhängig vom Ergebnis der vorhergehenden Operation ist die Bedingung erfüllt, erfolgt ein Sprung; im anderen Fall wird die Programmausführung beim nächsten Befehl fortgesetzt. Der Assembler übernimmt die Offset-Berechnung.

    BefehlBedeutungSprungbedingung
    einfach (simple)
    BCC dest.carry clearC=0
    BCS dest.carry setC=1
    BVC dest.overflow clearV=0
    BVS dest.overflow setV=1
    BNE dest.result not equal zeroZ=0 (BZC)
    BEQ dest.result equal zeroZ=1 (BZS)
    BPL dest.result plusN=0 (BNC)
    BMI dest.result minusN=1 (BNS)
    natürlich (unsigned)
    BHI dest.higherOp1 > Op2
    BLS dest.lower or sameOp1 <= Op2
    BLO dest.lowerOP1 < Op2
    BHS dest.higher or sameOp1 >= Op2
    konegativ (signed)
    BGT dest.greater thanOp1 > Op2
    BLE dest.less or equalOp1 <= Op2
    BLT dest.less thanOp1 < Op2
    BGE dest.greater or equalOp1 >= Op2
    bitabhängig
    BRCLRadr. mask dest springe, wenn die in der Maske mit "1" markierten Bits alle "0" sind alle Bits des Speicherbytes adr. werden mit der Maske UND-verknüpft und wenn das Ergebnis 0 ist, wird gesprungen.
    BRSETadr. mask dest springe, wenn die in der Maske mit "1" markierten Bits alle "1" sind alle Bits des Speicherbytes adr. werden mit der Maske UND-verknüpft und wenn das Ergebnis gleich der Maske ist, wird gesprungen.

    Unterprogrammsprünge (jump/branch to subroutine)

    JSR adr.

    BSR dest.

    RTS (return from subroutine)

    Unterprogramme lassen sich beliebig schachteln.

    Programmunterbrechungsbefehle

    SWI (software interrupt)

    RTI (return from interrupt)

    Reine Verzögerungsbefehle

    Anwendungen:

    3.6 Stapelspeicher (Stack)

    Der Stack ist ein wortorganisierter Speicher mit implizitem Zugriff LIFO-Speicher (Lat In First Out). Die Realisierung beim 68HC11 erfolgt als externer Stack (Software-Stack). Er ist Teil des Arbeitsspeichers, Die automatische Adressierung erfolgt über den Stapelzeiger (Stackpointer, S). Der Stackpointer ist ein 16-Bit-Register und zeigt immer auf die nächste freie Zelle. Der Stack wächst zu kleiner werdenden Adressen ("nach unten").

    Am Programmanfang muß der Stack mit einem sinnvollen Wert besetzt werden.

    Die Vorteile des SW-Stacks mit Stackpointer-Adressierung sind unter anderem:

    Befehle für den Stack-Zugriff

    Anwendung des Stack

    Der Stack dient dem Zwischenspeichern von Registerinhalten um:

    Wichtig ist der sorgfältige und überlegte Umgang mit Stack- und SP-Operationen. Damit kein Stack-Überlauf(-Unterlauf) auftritt:

    3.7 Unterprogramme (Subroutines)

    Unterprogramme sind Programmteile (=Befehlsfolgen), die mit einem Namen versehen sind und unter diesem Namen aufgerufen werden. UP können in den Programmen, die diese verwenden, oder außerhalb derselben definiert sein UP-Bibliotheken. Sie sollen möglichst universell programmiert (und damit für wechselnde Datenwerte) sein Parameter müssen übergeben werden (Eingabe- und Ausgabeparameter). Subroutines müssen am beliebiger Stelle des Hauptprogramms aufgerufen werden können (und damit auch an mehreren Stellen), sie sollten daher den Inhalt derjenigen Register sichern (z. B. auf dem Stack), die innerhalb des UP verändert werden. Typische Anwendungen der UP-Technik sind: Programme mit UP dauern etwas länger als ohne (meist überwiegen jedoch die Vorteile der UP).

    Anmerkungen zur Dokumentation:

    Aufgaben des aufrufenden Programms:

    Unterprogramm-Sprungbefehle

    sind spezielle Sprungbefehle, die vor dem Sprung die Rücksprungadresse auf dem Stack sichern. Beim Rücksprung wird diese Adresse automatisch geladen.

    Sprung in das UP: JSR, BSR

    Unterscheidung in der Adressierungsart. Wirkung:

    Rücksprung aus dem UP: RTS

    Letzter Befehl des UP, Wirkung:

    Achtung: Vor dem Rücksprung muß der Stackpointer i. a. auf die gleiche Adresse zeigen, wie nach dem Ansprung des UP Sorgfalt bei Stackoperationen notwendig! Beim UP-Sprung (und Rücksprung) wird nur der PC-Inhalt geändert, der Inhalt aller anderen Register bleibt erhalten.
    Register können zur Parameterübergabe verwendet werden
    Im UP verwendete Register müssen gegebenenfalls gesichert werden

    Vorteil der Speicherung der Rücksprungadresse auf dem Stack:

    Parameter-Übergabe

    Eingabeparameter: Werte UP
    Ausgabeparameter: Werte UP

    Möglichkeiten der Parameterübergabe:

    Beispiel: Verzögerungsroutine

    Es soll eine Verzögerung von 1 ms erreicht werden. Das Prinzip der Routine ist recht einfach: Ein Akkumulator wird mit einem Anfangswert besetzt und dann in einer Schleife solange dekrementiert, bis sein Wert Null geworden ist.

    
           ORG   PROG     Programm soll bei Adresse PROG beginnen 
    MAIN   BSR   VERZ     Aufruf Unterprogramm 
    STOP   BRA   STOP     Endlosschleife, stoppt Programm 
                          ("Wiederbeleben" des Computers mit Reset) 
    
    * Verzögerungs-Unterprogramm 
    * Verzögerungszeit: 1 ms 
    * Keine Parameter 
    *
    VERZ   LDAA  #221     Anfangswert (Konstante) laden (siehe unten) 
    VZ     NOP            No Operation (Zeitverzoegerung)
           NOP            No Operation (Zeitverzoegerung)
           DECA           A runterzählen bis 0 
           BNE   VZ       solange A != 0, weiterer Durchlauf 
           RTS            Verzögerung erreicht (A = 0) 
    
           END 
    
    Berechnung der Verzögerungszeit: Aus der Befehlstabelle kann man die Anzahl der Taktzyklen für jeden Befehl entnehmen. Wenn man nun noch die Taktfrequenz weiß, kann man die Ablaufzeit eines Programms ausrechnen. Wir müssen vier Zeiten wissen:
         tl   Ausführungszeit LDAA         2 Taktzyklen 
         td           -"-          DECA + 2 NOP 6    -"- 
         tb           -"-          BNE          3    -"- 
         tr           -"-          RTS          5    -"- 
    
    Bei unserem Praktikumsrecher dauert ein Taktzyklus 0,5 Mikrosekunden. Die Gesamtzeit tg ergibt sich zu:
         tg = 0,5 * n * (td + tb) + tl + tr 
            = 0,5 * n * (6  + 3 ) + 2  + 5 
            = 0,5 * (n * 9 + 7) 
    
    Da tg = 1 ms = 1000 Mikrosekunden sein soll, berechnet sich n zu
         n = (1000 - 3,5) / 4,5 
           = 996,5 / 4,5 
           = 221,44  
    
       n = 221 
    

    Es ergibt sich also ein kleiner Fehler; unsere Schleife ist einen knappen Taktzyklus zu langsam. Will man wiklich genaue Zeitintervalle, greift man auf den integrierten Timer zurück (siehe später).

    Im gezeigten Beispiel "Verzögerungsroutine" ist die maximale Verzögerungszeit durch die Wortbreite des Akku A (8 Bit 0..255) auf etwas mehr als 1 ms begrenzt.

    Für größere Verzögerungen:

    Beispiel: Gegeben ist folgende Verzögerungsroutine mit 16-Bit-Register:

    * Verzögerungs-Unterprogramm 
    * Verzögerungszeit: 500 ms 
    * Keine Parameter 
    *
    VERZ   LDX  #ANF      Anfangswert (Konstante) laden 
    VZ     NOP            No Operation (Zeitverzoegerung)
           NOP            No Operation (Zeitverzoegerung)
           DEX            X runterzählen bis 0 
           BNE            solange X != 0, weiterer Durchlauf 
           RTS            Verzögerung erreicht (X = 0) 
    
    Frage: Welchen Wert muß ANF haben, um eine Verzögerung von 500 ms zu erreichen?

    3.8 Interrupts (Programmunterbrechungen)

    Hierbei handelt es sich um die Unterbrechung des laufenden Programms durch die Hardware (IRQ-Signal, z. B. von der Peripherie) oder durch die Software. Es wird ein anderes Programm (= Interrupt Service Routine, ISR) nach Beendigung des laufenden Befehls gestartet.

    Anwendungen:

    Aufgaben der CPU bei einen Interrupt:

    1. Abspeichern von Registerinhalten (bei 68HC11 im Stack)

    2. Setzen der Interruptmaske zum Sperren weiterer Interrupts
      (Unter Berücksichtigung der Priorität)

    3. Laden der Startadresse (= Interrupt-Vektor) der ISR in den PC
      • Der Interruptvektor steht an einer hardwaremäßig vorgegebenen Adresse, die von der Art des Interrupts abhängt.
      • Je nach Interrupt-Art wird die entsprechende Interrupt-Vektor-Adresse auf den Bus gegeben.
      • In den so adressierten Speicherzellen steht dann die Startadresse der ISR (= Interrupt-Vektor), die in den PC gebracht wird Start der ISR

    4. Fortsetzung des unterbrochenen Programms nach der Interrupt-Bearbeitung: RTI-Befehl
      • weggespeicherte Register werden vom Stack geholt
      • PC erhält Adresse des nächsten Befehls des unterbrochenen Programms

    Im allgemeinen wird ein Hardware-IRQ erst am Ende eine Befehlszyklus bedient. Außer dem PC und dem Stackpointer werden keine Register beeinflußt. Interrupts können geschachtelt werden (3 Ebenen).

    Interruptsystem des 68HC11:

    SignalRESETUnbedinger Abbruch und Neustart
    SignalXIRQ & (X-Bit = 0)Unbedingte Unterbrechung (non maskable interrupt)
    SignalIRQ & (I-Bit = 0)Bedingte Unterbrechung (interrupt request)
    (kann auch interne Ursachen haben)
    I-Bit beeinflußbar durch die Befehle SEI/CLI
    BefehlSWIBefehlsgesteuerte Unterbrechung (software interrupt)
    BefehlIllegal OpcodeNicht implementierter Befehl

    Beim Auftreten eines Interrupts wird das I-Bit auf 1 gesetzt (Sperren IRQ), bei Reset und XIRQ auch das X-Bit. Beim RTI wird durch das Zurückschreiben des Statusregisters diese Sperre automatisch wieder aufgehoben. Das X-Bit kann nicht per Programm beeinflußt werden.

    Jedem dieser Interrupts ist eine eindeutige Interrupt-Vektor-Adresse (= Adresse, unter welcher der Interrupt-Vektor, also die Adresse der Interrupt-Serviceroutine, zu finden ist) zugeordnet:

    Zu jeder Interrupt-Art ist eine eigene ISR möglich. Zumindest der RESET-Vektor muß bei Einschalten vohanden sein ROM im höchsten Adreßbereich nötig.

    Beispiel: Starten der Interrupt-Service-Routine für XIRQ

    Grundsätzlich gilt für Programme mit Interrupt-Serviceroutinen, daß vier Schritte für die Initialisierung (zu Beginn des Hauptprogramms) nötig sind:

    1. Setzen des System-Stackpointers
      Solange er nicht gesetzt ist, werden keine Interrupts entgegengenommen.

    2. Initialisierung der Hardware
      In der Regel werden Interrupts von E/A-Bausteinen ausgelöst. Diese müssen entsprechend programmiert werden.

    3. Setzen des Interruptvektors
      Siehe oben.

    4. Freigeben des Interrupts durch Setzen des entsprechenden I-Flags im CC-Register auf 0. Bei einer ISR für XIRQ entfällt dieser Punkt natürlich.

    Zum vorhergehenden Abschnitt Zum Inhaltsverzeichnis Zum nächsten Abschnitt


    Copyright © FH München, FB 04, Prof. Jürgen Plate
    Letzte Aktualisierung: 09. Apr 2009