| 1.3 Der Mikroprozessor 6809 | A: relative Adressierungsarten, Postbyte |
In diesem Abschnitt wollen wir Ihnen anhand einer Beispielaufgabe
1) aufzeigen, wie Sie die Lösung der Übungsaufgaben systematisch und erfolgversprechend durchführen können. Die folgende Musterlösung zeigt Ihnen dazu die einzelnen Schritte einer Problemlösung auf. Die Aufgabe lautet:
|
Aufgabe:
Schreiben Sie ein Programm, das der Reihe nach
|
Lösung:
Das Flußdiagramm in Bild 1.4-1 stellt den Aufgabentext ohne Verfeinerungen dar. Lediglich die Adreßausgaben von Punkt 7. und 8. wurden zusammengeführt. Die Aufgabenstellung dieses ersten Diagramms ohne inhaltliche Änderung empfiehlt sich, weil die Darstellung übersichtlicher als der Text ist. Dadurch werden Mißverständnisse geklärt bzw. vermieden und ähnliche oder gleiche Funktionsblöcke sind leichter erkennbar. In diesem Fall kommt der Ablauf "Anzeige löschen, Kennung 'XX' ins Operationsfeld schreiben " fünfmal vor. Die Programmlänge und damit die Schreib- und "Eintipp"-Arbeit läßt sich wesentlich verringern, wenn dieser Ablauf als Unterprogramm geschrieben wird, das an den entsprechenden Stellen im Hauptprogramm aufgerufen wird. Die Kennung muß als Parameter übergeben werden, am einfachsten in einem CPU-Register.
| Bild 1.4-1: | Grobes Flußdiagramm nach Aufgabentext |
Bei der folgenden Verfeinerung des Programmablaufs ist zu beachten, welche Aufgaben von schon vorhandenen Hilfsroutinen ( siehe Abschnitt 1.2.2) übernommen werden können: "Anzeige löschen " entspricht "CLRDISP", "Kennung 'X' ins Operationsfeld schreiben " wird von "SHOWB7SG" geleistet, wenn 'XX' in B steht und das X-Register den Wert $0006 enthält. Beim Aufruf des oben erwähnten Unterprogramms sollte die Kennung also gleich im B-Register übergeben werden. Eingabe und Anzeige eines bzw. zweier Bytes wird von "SHOWDATA" bzw. "SHOWADR" ausgeführt. Die Anzeige der aktuellen Adresse wird mit "SHOWD7SG" gelöst.
Ohne Hilfsroutinen muß nur die Aufgabe "Adreßbereich durchsuchen " gelöst werden. Hierzu legen wir zunächst ein allgemeines Flußdiagramm an (s. Bild 1.4-2 ).
| Bild 1.4-2: | Verfeinerung der Suchroutine |
Bevor wir die Diagramme unter Berücksichtigung der CPU-Architektur verfeinern, müssen wir entscheiden, wie die Variablen abgelegt und die CPU-Register benutzt werden:
Da Vergleiche nur zwischen Registerinhalt und Speicherinhalt stattfinden können, sollten die drei zu suchenden Bytes in Registern gehalten werden, z.B. in A und U. Die aktuelle Adresse kann im X-Register stehen. Die Endadresse-2 muß für den Vergleich im Speicher stehen. Die Endadresse kann im Y-Register abgelegt werden. PC, DP und S werden für den Programmablauf gebraucht. B steht zur freien Verfügung bereit. Die Anfangsadresse
muß im Speicher abgelegt werden, weil in der CPU kein 16-bit-Register frei ist.
Wenn wir das Flußdiagramm in Bild 1.4-2 an diese Festlegungen anpassen, erhalten wir das Flußdiagramm in Bild 1.4-3. Ein zusätzlicher Unterschied zu Bild 1.4-2 besteht in der Übergabe der Fundortadresse: Das X-Register wird vom Unterpro-gramm KENNUNG verwendet, das Y-Register wird
dagegen nicht benötigt. Deshalb wird die auszugebende Adresse im Y-Register an das Hauptprogramm übergeben. Im rechten Zweig des Flußdiagramms in Bild 1.4-3 wird das Y-Register nicht modifiziert, da es die Endadresse enthält. Im linken Zweig wird die Endadresse in Y mit der aktuellen Adresse überschrieben.
| Bild 1.4-3: | Suchroutine, an Prozessorarchitektur angepaßt |
Für das Unterprogramm "KENNUNG" erhalten wir:
CLRDISP aufrufen X := #$0006 SHOWB7SG aufrufen Rückkehr zum Hauptprogramm
Beachten Sie, daß in diesem Unterprogramm keine Registerinhalte auf den Stack "gerettet" werden. Zumindest der Inhalt des X-Registers wird innerhalb des Unterprogramms verändert. In unserem kleinen Musterprogramm ergeben sich daraus keine Probleme. In komplexeren Progammen ist es jedoch ratsam, zu Beginn jedes Unterprogramms alle Registerinhalte, die im Verlauf der Unterroutine verändert werden, auf dem Stack abzulegen und vor der Rückkehr zum aufrufenden Programm wieder in die Register zu holen.
Das Hauptprogramm enthält nun keine Verzweigungen mehr. Wir verzichten daher auf ein Flußdiagramm. Die Bezeichner "ERSTBYTE", "ZWEITBYTE", "DRITTBYTE", "ANFADR" und "LETZTADR" entsprechen je einer bzw. zwei konsekutiven Speicheradressen. Nach Anpassung an die Prozessorarchitektur erhalten wir:
| B := "DA" | ( * Kennung *) |
| KENNUNG aufrufen | |
| X := #$0004 | |
| SHOWDATA aufrufen | ( * erstes Byte lesen * ) |
| (ERSTBYTE) := A | |
| X := #$0002 | |
| SHOWDATA aufrufen | ( *zweites Byte lesen * ) |
| (ZWEITBYTE) :=A | |
| X := #$0000 | |
| SHOWDATA aufrufen | ( *drittes Byte lesen * ) |
| (DRITTBYTE ) := A | |
| U:= (ZWEITBYTE) und(DRITTBYTE) | |
| B := "AA" | ( * Kennung * ) |
| KENNUNG aufrufen | |
| X := #$0002 | |
| SHOWADR aufrufen | ( * Anfangsadresse lesen * ) |
| (ANFADR) := Y | |
| B := "EA" | ( * Kennung * ) |
| KENNUNG aufrufen | |
| X := #$0002 | |
| SHOWADR aufrufen | ( * Endadresse lesen * ) |
| D := Y | |
| D:= D-2 | |
| (LETZTADR) := D | ( * LETZTADR := Endadresse -2 *) |
| A := (ERSTBYTE) | |
| SUCHE | |
| KENNUNG aufrufen | ( * "F0" oder "00"ausgeben * ) |
| D := Y | |
| X := #$0002 | |
| SHOWD7SG aufrufen | ( * Fund- oder Endadresse ausgeben * ) |
| Endlosschleife |
Zur Kontrolle sollte man sich noch eine Tabelle (s. Tabelle 1.4-1) anlegen, in der die Verwendung der CPU-Register gelistet ist. Man kann mit Hilfe der Tabelle unbeabsichtigte Mehrfachbenutzungen erkennen.
Der nächste Schritt ist nun die Übertragung der einzelnen Operationen in Assemblercode. Hierbei dürfte es im allgemeinen keine Schwierigkeiten geben, da die Abbildung fast überall "isomorph" ist, wenn die Vefeinerung ausführlich genug war. Statt numerischer Sprungadressen werden
Marken (Labels) verwendet und die Variablennamen für Speicherplätze werden zunächst beibehalten. Das einzige Problem stellt in unserem Beispiel die Anweisung "X := X + 1" dar. Der INC-Befehl ist nur auf die Register A und B und den Speicher anwendbar. Das X-Register muß daher z.B. mit der Anweisung "LDB ,X+" inkrementiert werden.
| Reg. | Kennung | Eingabe | Suchroutine | Ausgabe |
|---|---|---|---|---|
| A | --- | Eingabebyte | 1. Byte | --- |
| B | Kennung | verändert | verändert | Kennung |
| X | Anzeigestelle | Anzeigestelle | aktuelle Adresse | Anzeigestelle |
| Y | Eingabeadresse | Endadresse | Adresse | |
| S | Stackzeiger, für Betriebssystem und UP "KENNUNG" nötig | |||
| U | --- | 2. und 3. Byte | 2. und 3. Byte | --- |
| DP | für Betriebssystemroutinen unverändert gelassen | |||
Abschließend werden die mnemonischen Assembleranweisungen in Maschinencode übersetzt und gleichzeitig die dadurch belegten Speicheradressen hingeschrieben. Für die "Übersetzung"
eines Assemblerbefehls in den Maschinencode müssen Sie zunächst in den Tabellen des Anhangs B die Zeile suchen, die durch den Mnemocode des Assemblerbefehls bezeichnet ist. Danach entnehmen Sie den gesuchten OpCode der Spalte, die durch die gewünschte Adressierungsart gekennzeichnet ist. Hier finden Sie auch die Gesamtzahl der Bytes, die der Befehl umfaßt. Es empfiehlt sich, Programmblöcke, die nur über Programmverzweigungen oder Unterprogrammaufrufe erreichbar sind, nicht direkt an das übrige Programm anschließen zu lassen, sondern einige Bytes Freiraum zu schaffen. Auf diese Weise müssen Sie nicht das gesamte Programm verschieben und eventuell viele Sprungadressen ändern, wenn Sie nachträglich einige Programmbytes einfügen
2) .
Für Variablennamen, die Speicherzellen bezeichnen, werden jetzt die Speicheradressen eingesetzt. Wenn alle Labeladressen bekannt sind, können Sie die Sprungziele der absoluten Sprünge einsetzen und die Sprungweiten der relativen Sprünge berechnen. Beachten Sie, daß die Sprungweite "0" keinem Sprung entspricht, d.h. es wird die Anweisung nach dem Sprungbefehl abgearbeitet.
| ADR. | OPCODE | LABEL | MNEMON. | BEMERKUNGEN |
|---|---|---|---|---|
| 0400 | C6 DA | LDB #$DA | ||
| 0402 | BD 05 00 | JSR KENNUNG | "DA" IM OP.FELD | |
| 0405 | 8E 00 04 | LDX #0004 | ||
| 0408 | BD F1 50 | JSR SHOWDATA | EINGABE 1.BYTE | |
| 040B | B7 06 00 | STA ERSTBYTE | ||
| 040E | 8E 00 02 | LDX #0002 | ||
| 0411 | BD F1 50 | JSR SHOWDATA | EINGABE 2.BYTE | |
| 0414 | B7 06 01 | STA ZWEITBYTE | ||
| 0417 | 8E 00 00 | LDX #0000 | ||
| 041A | BD F1 50 | JSR SHOWDATA | EINGABE 3.BYTE | |
| 041D | B7 06 02 | STA DRITTBYTE | ||
| 0420 | FE 06 01 | LDU ZWEITBYTE | U := 2.+3.Byte | |
| 0423 | C6 AA | LDB #$AA | ||
| 0425 | BD 05 00 | JSR KENNNUNG | "AA" IM OP.FELD | |
| 0428 | 8E 00 02 | LDX #0002 | ||
| 042B | BD F1 56 | JSR SHOWADR | EINGABE ANF.ADR. | |
| 042E | 10 BF 06 03 | STY ANFADR | ||
| 0432 | C6 EA | LDB #$EA | ||
| 0434 | BD 05 00 | JSR KENNNUNG | "EA" IM OP.FELD | |
| 0437 | 8E 00 02 | LDX #0002 | ||
| 043A | BD F1 56 | JSR SHOWADR | EINGABE ENDADR | |
| 043D | 1F 20 | TFR Y,D | D := ENDADR. | |
| 043F | 83 00 02 | SUBD #$0002 | LETZTADR := | |
| 0442 | FD 06 05 | STD LETZTADR | ENDADR - 2 | |
| 0445 | BE 06 03 | LDX ANFADR | ||
| 0448 | B6 06 00 | LDA ERSTBYTE | ||
| 044B | A1 84 | VERGLCH: | CMPA ,X | A = (X) ? |
| 044D | 26 11 | BNE COMPADR | ||
| 044F | 11 A3 01 | CMPU 1,X | U = (X+1) ? | |
| 0452 | 26 0C | BNE COMPADR | ||
| 0454 | 1F 12 | TRF X,Y | Y := AKT.ADR. | |
| 0456 | C6 F0 | LDB #$F0 | KENNUNG := "F0" | |
| 0458 | 20 16 | BRA ERGEBNIS | ||
| 0460 | 5F | COMPADR: | CLRB | KENNUNG := "00" |
| 0461 | BC 06 05 | CMPX LETZTADR | AKT.ADR.= | |
| 0464 | 27 0A | BEQ ERGEBNIS | LETZTADR ? | |
| 0466 | E6 80 | LDB ,X+ | X := X + 1 | |
| 0468 | 20 E7 | BRA VERGLCH | ||
| 0470 | BD 05 00 | ERGEBNIS: | JSR KENNUNG | "F0" ODER "00" |
| 0473 | 1F 20 | TFR Y,D | AKT.ADR BZW. | |
| 0475 | 8E 00 02 | LDX #$0002 | ENDADRESSE | |
| 0478 | BD F1 23 | JSR SHOWD7SG | ANZEIGEN | |
| 047B | 20 FE | ENDE: | BRA ENDE | |
| 0500 | BD F1 10 | KENNUNG: | JSR CLRDISP | |
| 0503 | 8E 00 06 | LDX #$0006 | AUF STELLE 6 U. 7 | |
| 0506 | BD F1 20 | JSR SHOWB7SG | B ANZEIGEN | |
| 0509 | 39 | RTS | ||
Variablennamen: ERSTBYTE = $0600 ZWEITBYTE = $0601 DRITTBYTE = $0602 ANFADR = $0603 LETZTADR = $0605
Der oben beschriebene Lösungsweg erscheint vielleicht etwas aufwendig. Wenn er auch in dieser oder ähnlicher Form immer beschritten wird, so werden häufig nicht alle Zwischenschritte zu Papier gebracht. Ihre Übungsaufgaben brauchen auch nicht alle Zwischenergebnisse zu enthalten, sondern könnten sich in diesem Fall auf die Flußdiagrammme der Bilder 1.4-1 und 1.4-2 und das kommentierte Assembler- und Maschinenprogramm beschränken. Wir empfehlen Ihnen aber (vor allem, wenn Sie keine Erfahrung mit Maschinenprogrammierung besitzen ), alle hier beschriebenen Schritte explizit abzuarbeiten. Sie strukturieren Ihre Programme dadurch automatisch und die Problemlösung und Fehlersuche gestaltet sich für Sie leichter.
| Praktische Übung 1.4-1: |
|---|
| Geben Sie das obenstehende Programm in den Praktikumsrechner ein und führen Sie es mit verschiedenen zu suchenden Bytefolgen aus. Machen Sie sich anhand dieses Programms mit den Testmöglichkeiten (debugging) des Monitorprogramms quot;Einzelschrittausführung" (Taste T) und "Setzen eines Breakpoints" (Tasten G, +) vertraut. Nutzen Sie diese Möglichkeiten insbesondere zur Überprüfung der relativen Verzweigungsbefehle. |
| 1.3 Der Mikroprozessor 6809 | A: relative Adressierungsarten, Postbyte |