![]() |
MikrocomputertechnikProf. Jürgen Plate |

16-Bit-Instruktionen
Vorteile bei der Programmierung
(z. B. bei Schleife: A zum Rechnen, B als Schleifenzähler)
64 K Adreßraum
I-Bit auf 0 setzen). Bleibt
solange gesperrt, solange der Stackpointer nicht initialisiert wurde.

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
| AH | AL |
| ea | ea+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.

LDAA #20 - Lade Akku A mit dem Wert 20

LDAA $A174 - Lade Akku A mit dem Inhalt der Speicherzelle $A174

LDAA <$80 - Lade Akku A mit dem Inhalt der Speicherzelle $80


CLR 5,X (ergibt als Code: 6F 05) CLR -5,X (ergibt als Code: 6F FB)

Befehlsaufbau: offset = Zieladresse - Befehlsadresse - Befehlslänge
Befehlsausführung: Zieladresse = Befehlszählerstand + offset


Maschinensprache:
| Marke | OP-Code | Operand/Adresse | Kommentar |
Beispiel:
START LDAA DAT1 ; Zähler initialisieren
Anweisung
für den Assembler, Genaueres später.
BOTTOM EQU TOP + 10
Zusätzlich sind auch reine Kommentarzeilen möglich, die mit einem "*" in Spalte 1 beginnen.
| 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 |
| 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 |
| 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 |
Die Symboltabelle nimm neben den Sprungadressen auch alle Namen von Konstanten und Variablen auf. Im zweiten Lauf werden dann auch diese Namen durch die ermittelten Werte ersetzt.
Der Assembler führt einen Zähler, der Auskunft über den aktuellen Adreßpegel gibt. Dieser 'Location Counter' dient beispielsweise zum Berechnen von Sprungadressen. Er entspricht im Prinzip dem 'Program Counter' im realen Prozessor. 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. Er legt damit die Adresse fest, an der der Code später im Speicher des Controllers abgelegt wird, 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,$03Neben 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:
TEXT DC.B "Hello World!\n\r"Bei manchen Assemblern wird anstelle von DC.B das Kürzel FCB (Form Constant Byte) verwendet (bei unserem geht beides). |
| 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,$FFA0Bei manchen Assemblern (auch unserem) wird anstelle von DC.W das Kürzel FDB (Form Double Byte) verwendet. |
| 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 Elemente (Bytes, Worte oder Langworte) an, z. B.: PUFFER DS.B 100 ADRTAB DS.W 23Der Reservierungsvorgang hat eher informatorischen Charakter. Es findet seitens des Assemblers keinerlei Adress- oder Bereichsüberprüfung statt! Bei manchen Assembler wird stattdessen nur eine Reservierung in Bytes mit dem Befehl RMB (Reserve Memory Byte) vorgenommen. |
| 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 vorher platzierten NOLIST-Befehl. |
| END | Ende des Assemblerprogramms. Optionaler Befehl am Programmende. Wird aus Gründen der Kompatibilität vom Assembler akzeptiert, kann aber weggelassen werden. Text nach diesem Befehl wird vom Assembler ignoriert. |
Von der Hardwareseite her sind z.B. RAM- und ROM-Bereiche verschiedener Größe gegeben, denn Programme sollen häufig im ROM liegen während variable Datenbereiche natürlich nur im RAM sein können. Eine effiziente Nutzung der physikalisch vorhandenen Speicher verlangt auch die Definition von lückenlos zusammenhängenden Teilbereichen sowohl von Programmteilen (Hauptprogramm, Unterprogramme, Interrupt-Service-Routinen) als auch von Datenbereichen (Datenpuffer, Ausgabetexte, Zustandsvariablen, Stacks, Peripherie-Baustein-Register etc).
Als Grundregel gilt: Je eine ORG-Anweisung für den Daten- und den Programmbereich genügt, denn dann sind beide Bereiche jeweils zusammenhängend. Es ist nicht nötig, z.B. für jedes Unterprogramm oder jede Interrupt-Service-Routine ein neues ORG zu setzen. Jede weitere ORG-Anweisung steigert nur die Gefahr von Fehlern (z.B. überlappende Bereiche) bzw. Speicherverschwendung durch unnötige Lücken. Bedenken Sie, dass Sie vom Assembler nicht gewarnt werden, wenn sich Speicherbereiche überlappen oder Sie Bereiche wählen, an denen kein realer Speicher vorhanden ist.
Ganz auf die ORG-Anweisung zu verzichten ist zwar möglich (Programm- und Datenbereich hängen dann zusammen und beginnen bei Null), aber nicht zu empfehlen. Durch kommentierte ORG-Anweisungen zeigt man, das diese wohlüberlegt gewählt wurden; also besser ORG $0000 angeben als einfach weglassen.
Anmerkung: Da bei einem Mikrocontroller ja 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.
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 100Wichtig 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.
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
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
END
Häufiger ist jedoch die Variante mit vorangestelltem Hauptprogramm anzutreffen. Beim folgenden Rumpfprogramm sind auch noch einige weitere Besonderheiten berücksichtigt:
stak equ $7FFF ; Stackbereich ab $7FFF (RAM, abwaerts)
prog equ $8000 ; Programmbereich ab $8000 (EEPROM)
data equ $2000 ; Datenbereich ab $2000 (RAM)
rvec equ $FFFE ; Reset-Vektor
. ; ggf. weitere Vektoren
org data ; Beginn Datenbereich
. ; Variablen-Definitionen
.
org prog ; Beginn Programmbereich
main lds #stak ; Stackpointer setzen
.
. ; alle Initialisierungen
.
loop . ; Befehle des Programms
.
.
jmp loop
. ; ggf. fixe Tabellen dahinter
.
. ; ggf. Setzen der Interrupt-Vektoren
. ; (Reihenfolge der Adressen beachten!)
org rvec ; Setzen des Reset-Vektors
fdb main
end
Beim Programmieren einer Controller-Anwendung ist u. a. auch zu berücksichtigen, dass
RAM-Inhalte nach dem Ausschalten und späteren Wiedereinschalten verloren gehen. Das bedeutet,
dass feste Werte wie Ausgabestrings, Anfangswerte von Variablen usw. nicht im RAM (Datenspeicher),
sondern im EEPROM (Programmspeicher) abgelegt werden müssen. Das hat aber Konsequenzen:

Load Register from Memory or Constant
CLR Clear Akkumulator: 0
Akkumulator-Register

Store Register into Memory (8 Bit/16 Bit)
adr.
adr., adr+1
Clear Memory

Kopie des Inhalts eines Registers in ein anderes (transfer)
(A)
B
(A)
Statusregister
(B)
A
(Statusregister)
A
(S)
X
(S)
Y
(X)
S
(Y)
S
Austausch von Registerinhalten (exchange)
D
X
D
Y
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
Beispiel: Unterprogramm zum Kopieren eines Speicherbereichs (nullterminierte Zeichenkette). Die Adresse des Quellstrings steht im X-Register, die Adresse des Zielbereichs im Y-Register:
strcpy ldaa 0,x ; lade ein Zeichen aus der Quelle
staa 0,y ; ablegen im Zielbereich
beq strc_e ; wenn Nullbyte, dann fertig
inx ; beide Zeiger inkementieren
iny
bra strcpy ; und naechstes Zeichen
strc_e rts
Soll ein beliebiger Speicherbereich kopiert werden, müsste noch ein Blocklängenzähler
implementiert werden.

(A) + (adr.)
A
(B) + (adr.)
B
(D) + (adr., adr+1)
D (16-Bit-Addition)

(A) + (B)
A
(X) + (B)
X (Adreßrechnung),
B als vorzeichenlose Zahl aufgefaßt
(Y) + (B)
Y (Adreßrechnung),
B als vorzeichenlose Zahl aufgefaßt
Addieren mit Übertrag (add with carry)
(A) + (adr.) + C
A
(B) + (adr.) + C
B
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)
(A) + 1
A
(B) + 1
B
(S) + 1
S
(kein Flag beeinflußt)
(X) + 1
X
(nur Z-Flag beeinflußt)
(Y) + 1
Y
(nur Z-Flag beeinflußt)
(adr.) + 1
adr.
(Increment eines Speicherbytes)
(A) - (adr.)
A
(B) - (adr.)
B
Subtraktion mit Übertrag (subtract with carry)
(A) - (adr.) + C
A
(B) - (adr.) + C
B
Erniedrigen (decrement)
(A) - 1
A
(B) - 1
B
(S) - 1
S
(kein Flag beeinflußt)
(X) - 1
X
(nur Z-Flag beeinflußt)
(Y) - 1
Y
(nur Z-Flag beeinflußt)
(adr.) - 1
adr.
(Decrement eines Speicherbytes)
Vorzeichenwechsel (negate)
A komplementieren
B komplementieren
adr. komplementieren
Vorzeichenlose Multiplikation (A) * (B)
D
Dividieren (divide)
Vorzeichenlose Division, ganzzahliger Anteil (integer divide)
für D > X: (D) / (X)
X, Rest in D
Vorzeichenlose Division, Quotient echter Bruch (fractional divide)
für D < X: (D) / (X)
X, Rest in D
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
Decimal Adjust A
Akku enthält zwei BCD-Ziffern
$98,
-3
$97, usw.).

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

(A) and (adr.)
A
(B) and (adr.)
B
Beispiele:
1) Löschen der 4 höherwertigen Bits von Akku A ANDA #$0F 2) Rücksetzen des 5. Bits ANDA #%11101111 3) Rücksetzen des 4. Bits einer Variablen namens FLAG LDAA FLAG ANDA #%11110111 STAA FLAG
(A) or (adr.)
A
(B) or (adr.)
B
Beispiele:
1) Setzen der 4 höherwertigen Bits von Akku A
ORA #$F0
2) Setzen des 5. Bits
ORAA #%00010000
3) Setzen des 4. Bits einer Variablen namens FLAG
LDAA FLAG
ORAA #%00001000
STAA FLAG
(A) exor (adr.)
A
(B) exor (adr.)
B
Beispiele:
1) Invertieren der 4 höherwertigen Bits von Akku A
EORA #%11110000
2) Invertieren der Bits 0 und 3 einer Variablen namens FLAG
LDAA FLAG
EORA #%00000101
STAA FLAG
(A) and (adr.)
Nur Flags neu setzen, A bleibt unverändert
(B) and (adr.)
Nur Flags neu setzen, B bleibt unverändert
Invertieren aller Bits.
not (A)
A
not (B)
B
not (adr.)
adr.
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.

MSB
C-Flag
Akku A nach rechts schieben
Akku B nach rechts schieben
Akku D nach rechts schieben
Speicherwort adr. nach rechts schieben
LSLx Logical Shift Left:
von rechts wird 0 nachgeschoben: 0
LSB
MSB
C-Flag
Akku A nach links schieben
Akku B nach links schieben
Akku D nach links schieben
Speicherwort adr. nach links schieben
C-Flag
Akku A nach rechts schieben
Akku B nach rechts schieben
Speicherwort adr. nach rechts schieben
ASLx Arithmetic Shift Left:
von rechts wird 0 nachgeschoben: 0
LSB
MSB
C-Flag
Identisch mit LSL!
Akku A nach links schieben
Akku B nach links schieben
Akku D nach links schieben
Speicherwort adr. nach links schieben
ROLx Rotate Left
C-Flag
LSB
MSB
C-Flag
Akku A linksrotieren
Akku B linksrotieren
Speicherwort adr. linksrotieren
RORx Rotate Right
C-Flag
MSB
LSB
C-Flag
Akku A rechtsrotieren
Akku B rechtsrotieren
Speicherwort adr. rechtsrotieren
Beispiele:
1) Multiplikation von Akku A mit 10: (A) * 10A 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
Programmfortsetzung abhängig
von Dateneigenschaften
Steuerbefehle.
Beispiele bei den Sprungbefehlen.
Akku A = 0 ?
Akku B = 0 ?
(adr.) = 0 ?
| 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).
(A) - (adr.)
Flags
(B) - (adr.)
Flags
(D) - (adr., adr.+1)
Flags
(X) - (adr., adr.+1)
Flags
(Y) - (adr., adr.+1)
Flags
(A) - (B)
Flags
| clear carry flag | X X X X X X X 0 |
| clear interrupt flag | X X X 0 X X X X |
| clear overflow flag | X X X X X X 0 X |
| set carry flag | X X X X X X X 1 |
| set interrupt flag | X X X 1 X X X X |
| set overflow flag | X X X X X X 1 X |
LDAA #mask
ORAA adr.
STAA adr.
LDAA #mask
COMA
ANDA adr.
STAA adr.
XIRQ & X=1: next instruction
XIRQ & X=0: service interrupt
IRQ & I=1: no activity
IRQ & I=0: service interrupt
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.
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.

adr.
PC
Bei JMP erfolgt die Zielangabe mit vollständiger absoluter Adressierung (extended direct) oder indizierter Adressierung (mit X oder Y). Es ist ein Sprung im gesamten Adressraum möglich, denn der Operand ist ein 16-Bit-Wert.
Der Sprungbefehl hat im Assemblerprogramm normalerweise eine so genannte Marke als Operand. Diese Marke steht am Sprungziel, wie Sie da schon eingangs des Kapitels bei der Erklärung der beiden Assemblerdurchläufe sehen konnten. Der Assembler entnimmt die Adresse dieser Marke aus der Symboltabelle und fügt sie dem JMP-Befehl als Operand hinzu. Der Programmierer kann daher immer mit symbolischen Adressen arbeiten.
BRA dest.
Hier wird keine absolute Adresse, sondern ein Offset relativ zum Program Counter angegeben (-128 ... +127). Hier berechnet der Assembler die Distanz zwischen BRA-Befehl und Sprungmarke und trägt als Offset eine 8-Bit-Zahl in Komplementntdarstellung ein.
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.
Vergleich von Hochsprache und Assembler-Programmierung:

Die folgende Tabelle fasst alle bedingten Sprünge zusammen. Die erste Gruppe springt abhängig vom Wert bestimmter Flags des Condition Code Registers. Weitere Gruppen bilden die Sprungbefehle, die von arithmetischen Vergleichen abhängen.
Achtung: Die Flags werden nicht nur bei arithmetischen Operationen oder Vergleichen (Compare-Befehl) gesetzt, sondern bei fast jedem Befehl (z. B. Ladebefehle, logische Operationen).
| Befehl | Bedeutung | Sprungbedingung |
|---|---|---|
| einfach (simple) | ||
| BCC dest. | carry clear | C=0 |
| BCS dest. | carry set | C=1 |
| BVC dest. | overflow clear | V=0 |
| BVS dest. | overflow set | V=1 |
| BNE dest. | result not equal zero | Z=0 (BZC) |
| BEQ dest. | result equal zero | Z=1 (BZS) |
| BPL dest. | result plus | N=0 (BNC) |
| BMI dest. | result minus | N=1 (BNS) |
| natürlich (unsigned) | ||
| BHI dest. | higher | Op1 > Op2 |
| BLS dest. | lower or same | Op1 <= Op2 |
| BLO dest. | lower | OP1 < Op2 |
| BHS dest. | higher or same | Op1 >= Op2 |
| konegativ (signed) | ||
| BGT dest. | greater than | Op1 > Op2 |
| BLE dest. | less or equal | Op1 <= Op2 |
| BLT dest. | less than | Op1 < Op2 |
| BGE dest. | greater or equal | Op1 >= Op2 |
| bitabhängig | ||
| BRCLR | adr. 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. |
| BRSET | adr. 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. |
Der Vergleich bei natürlichen Zahlen und konegativen Zahlen kann zu unterschiedlichen Ergebnissen führen. Betrachten Sie dazu die beiden folgenden Befehle:
LDAA #$F8 CMPA #$12Beim "unsigned"-Vergleich ist $F8 > $12, der Sprungbefehl BHI würde also ausgeführt. Betrachtet man die Zahlen jedoch als ganze (konegative) Zahlen, handelt es sich um die Zahl -8dez., die natürlich kleiner als $12 (18dez.) ist. Der Befehl BGT würde daher nicht ausgeführt.

Der "Jump to Subroutine" dient zum Sprung zu einem Unterprogramm (Function, Procedure in höheren Programmiersprachen). Der Unterschied zum "normalen" Sprung besteht darin, dass am Ende des Unterprogramms mit einem "Return from Subroutine" (RTS) an die Stelle des "Aufrufs" (Absprungs) zurückgekehrt werden muss.
Möglichkeit der Rückkehr
Beim JSR wird also vor dem Überschreiben des PC der aktuelle PC-Stand auf den Stack gerettet, d.h. der Stack wächst dabei um 2 Bytes! So kann der RTS-Befehl auf dem Stack den ursprünglichen PC-Stand wiederfinden (Rücksprung aus dem Unterprogramm zurück in das aufrufende Programm).


Typische Anfängerfehler:
No Operation
Branch Never
LIFO-Speicher (Last 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:

MSB wird beim PUL zuerst geladen
Schreiben in den Stack:
Register A
Stack
Register B
Stack
Register X
Stack
Register Y
Stack
Lesen aus dem Stack:
Stack
Register A
Stack
Register B
Stack
Register X
Stack
Register Y
Man kann normalerweise nur immer das "oberste" Stackelement lesen. Die Stack-"Verwaltung" muss sehr sorgfältig geschehen, damit jederzeit klar ist, welche Information gerade "oben" liegt oder - besser ausgedrückt - wo der Stackpointer gerade hinzeigt. Unsauberer Umgang mit dem Stack führt fast unweigerlich zu unverständlichen Programmabstürzen!
Wichtig ist der sorgfältige und überlegte Umgang mit Stack- und SP-Operationen. Damit kein Stack-Überlauf(-Unterlauf) auftritt:
PSHA
PSHB
.
.
.
PULB
PULA
UP-Bibliotheken.
Sie sollen möglichst universell programmiert (und damit für wechselnde
Datenwerte) sein
Parameter müssen
übergeben werden (Eingabe- und Ausgabeparameter).
UP müssen an 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:
Programmerstellung durch mehrere Programmierer möglich
Anmerkungen zur Dokumentation:
Aufgaben des aufrufenden Programms:
Sprung in das UP: JSR, BSR
Unterscheidung in der Adressierungsart. Wirkung:
Rücksprung aus dem UP: RTS
Letzter Befehl des UP, Wirkung:
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:
verschachtelte UP
UP
UP
Möglichkeiten der Parameterübergabe:
indirekte 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 wirklich 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?

Anwendungen:
Aufgaben der CPU bei einen Interrupt:

Start der ISR
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).
| Signal | RESET | Unbedinger Abbruch und Neustart |
| Signal | XIRQ & (X-Bit = 0) | Unbedingte Unterbrechung (non maskable interrupt) |
| Signal | IRQ & (I-Bit = 0) | Bedingte Unterbrechung
(interrupt request) (kann auch interne Ursachen haben) I-Bit beeinflußbar durch die Befehle SEI/CLI |
| Befehl | SWI | Befehlsgesteuerte Unterbrechung (software interrupt) |
| Befehl | Illegal Opcode | Nicht 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 vorhanden 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:
Zum vorhergehenden Abschnitt |
Zum Inhaltsverzeichnis |
Zum nächsten Abschnitt |