2.4 Entwicklungswerkzeuge zum Praktikumsrechner Lösungsvorschläge zu den Praktischen Übungen

2.5/1

2.5 Übungen zur Assemblerprogrammierung

In diesem Abschnitt sollen die in Abschnitt 2.3.5 beschriebenen Unterprogrammtechniken an praktischen Beispielen vertieft und erweitert werden.

1. "Taschenrechner"

Das Ihnen bekannte Assemblerprogramm "P1.ASM" ist nur für eine Zahlenbreite von zwei Ziffern einsetzbar. Wünschenswert wäre ein Programm, das abhängig von einer Konstanten eine veränderbare Ziffernbreite zuläßt. Hierfür muß der Kern des Programms "P1.ASM", die BCD-Addition, aus dem Hauptprogramm herausgenommen werden und in einer eigenen Unterprogrammroutine abgelegt werden. Tabelle 2.5-1 zeigt eine mögliche Implementierung dieses Unterprogramms.

Im Unterschied zum ersten Programm, soll nun bei einem Überlauf ein Symbol (kleines Rechteck) in der Anzeige erscheinen. Wird danach die Taste "S" gedrückt, so soll das Programm beendet werden. Im Falle einer gültigen BCD-Ziffer soll das Programm von vorne beginnen.

Tabelle 2.5-1: Unterprogrammroutine UP_ADD zur BCD Addition
;========================
; Unterprogramm UP_ADD: Addition von zwei BCD-Ziffern
; In:   Y: Speicherstelle der ersten BCD-Ziffer (liegt im unteren Nib)
;       B: zweite BCD-Ziffer (liegt im unteren Nibble)
; Out:  A: gleich Null: kein Uebertrag, sonst Uebertrag fuer naechsth.
;       B: korrigierte Ziffer, vorbereitet zur Anzeige
;
         ORG   $0500    ;Beginn Unterprogramm
UP_ADD:  ADDB  0,Y      ;Addition der Ziffer
         TFR   B,A      ;
         ANDA  #$F0     ;Test auf Ueberlauf durch vorherige Addition
         BNE   BCDADD   ;wenn ja, Korrektur
         TFR   B,A      ;
         CMPA  #$09     ;Test auf gueltige BCD-Ziffer
         BLS   CARRY2   ;wenn ja, Korrektur ueberspringen
BCDADD:  ADDB  #$06     ;Korrektur, als Ergebnis gueltige BCD-Ziffer
CARRY2:  TFR   B,A      ;
         ANDA  #$F0     ;Vorbereitung zum Test auf Ueberlauf (Hauptp.)
         ANDB  #$0F     ;eventl. Uebertrag der korrig. Ziffer loeschen
         STB   0,Y      ;korrigierte Ziffer abspeichern
         RTS            ;Ruecksprung in das Hauptprogramm

Praktische Übung P2.5-1:
Analysieren Sie das in Tabelle 2.5-1 gezeigte Unterprogramm UP_ADD und entwerfen Sie ein Flußdiagramm. Wie reagiert die Routine auf einen eventuellen Übertrag auf die nächsthöhere Ziffer?

2.5/2

Praktische Übung P2.5-2:
Entwerfen Sie ein Flußdiagramm für das zugehörige Hauptprogramm zu dem Unterprogramm UP_ADD von Tabelle 2.5-1. Im Hauptprogramm soll die verarbeitete Ziffernbreite mittels einer Konstantendefinition "einstellbar" sein. Im Falle eines Überlaufes soll ein Symbol (kleines Rechteck) in der Anzeige erscheinen. Wird danach die Taste "S" gedrückt, so soll das Programm beendet werden. Im Falle der Eingabe einer gültigen BCD-Ziffer soll das Programm von vorne beginnen.
Implementieren Sie Ihr Flußdiagramm und testen Sie die Programme auf Ihrem Praktikumsrechner aus.

Werden für das Programm weitere arithmetische Operationen implementiert, so erhält man einen kleinen Taschenrechner für Dezimalzahlen, der mit variabler Ziffernbreite arbeitenkann.

2. "Vierstufen-Zähler"

Das folgende Beispiel verdeutlicht die verschiedenen Möglichkeiten zur Zwischenspeicherung von Registerinhalten in Unterprogrammen.

Es ist ein Programm zu implementieren, das einen vierstufigen Zähler repräsentiert. Die einzelnen Zählerstufen sollen über Unterprogramme realisiert werden. Der Einfachheit halber soll jede Zählstufe von $00 bis $FF zählen. Die Zähler sollen hierarchisch angeordnet sein, so daß jeder Zähler seinen darunterliegenden nächsten Zähler in jedem Zählschritt einmal aufruft. Dem höchstwertigen Zähler sind die Anzeigestellen S7,S6 zugeordnet, dem niederwertigsten Zähler die Anzeigestellen S1,S0.

2.5/3

Die folgende Tabelle 2.5-2 zeigt das Hauptprogramm des Zählers. Die Register X und B werden während der Abarbeitung des Unterprogramms über Speicherbereiche zwischengespeichert.

Tabelle 2.5-2: Hauptprogramm des Zählers
; P3.ASM
;
; Zaehlprogramm ueber drei Unterprogramme zur Darstellung der ver-
; schiedenen Moeglichkeiten zur Zwischenspeicherung sowie der ver-
; schachtelten Progr. von Unterprogrammen.
;   Zaehler des Hauptprogramms erscheint in den Anzeigestellen S7,S6
;   Register B und X werden ueber Speicherbereiche gerettet
;
          ORG   $0400     ;Beginn des Programmbereiches
          JSR   CLRDISP   ;Anzeige loeschen
          LDX   #$0006    ;Vorbereitung der Anzeige (S7,S6)
          LDB   #$FF      ;Zaehler initialisieren
NEXT0:    INCB            ;Zaehler inkrementieren
          JSR   SHOWB7SG  ;Zaehlerstand anzeigen
          STB   MEM1      ;Zaehler und Anzeigeposition ueber 
          STX   MEM2      ;Speicherbereich retten
          JSR   UP1       ;Aufruf des ersten UP
          LDB   MEM1      ;Zaehler und Anzeigeposition einlesen
          LDX   MEM2      ;
          CMPB  #$FF      ;Abbruchbedingung erfuellt?
          BNE   NEXT0     ;wenn nein, zurueck zu NEXT0
          SWI             ;Programmende
MEM1      EQU   $0500     ;Speicherbereiche zum Zwischenspeichern
MEM2      EQU   $0502     ;von Daten
CLRDISP   EQU   $F110     ;Anzeige des Praktikumsrechners loeschen
SHOWB7SG  EQU   $F120     ;Anzeige der Hexzahl, die in B steht; Pos. in X


Praktische Übung P2.5-3:
Implementieren Sie für obiges Hauptprogramm die drei geforderten Unterprogramme. Im ersten Unterprogramm sollen die Register X und B über den Stack gerettet werden. In dem zweiten Unterprogramm sollen sie über Prozessorregister zwischengespeichert werden.
Testen Sie das Programm auf Ihrem Praktikumsrechner aus.

3. "Interrupt-Routinen"

Interrupt-Routinen sind eine spezielle Art von Unterprogrammen, die ausschließlich durch Hardware- und Software-Interrupts aktiviert werden. An dieser Stelle soll die grundsätzliche Struktur von Interruptroutinen besprochen werden.

Interruptbehandlung durch den Monitor:
Wird ein Interrupt vom 6809-Prozessor erkannt, so rettet er abhängig vom Interrupttyp einige (mindestens PC und CC) 1) Register oder alle Prozessorregister auf den Systemstack und ruft die Interruptbehandlungsroutine des Monitors auf. Diese ermittelt die Interruptquelle und setzt den Programmzähler auf die Einsprungadresse der jeweiligen Behandlungsroutine der Interruptquelle. Die gültige Einsprungadresse lädt die Monitorroutine aus der sogenannten Interruptvektortabelle, in der für alle Interruptquellen eines Systems die entsprechenden Einsprungadressen hinterlegt sind. Beispielsweise ist im Falle des Praktikumsrechners die Interrupt-Vektoradresse für den SWI3 (Software-Interrupt3) die 16bit-Adresse $004C, $004D.

Struktur einer Interrupt-Routine:
Eine Interruptroutine ist ein in sich abgeschlossenes Programm, daß mindestens einen RTI-Befehl (return from interrupt) besitzen muß. Das Hauptprogramm integriert die Interruptroutine in das Gesamtprogramm, in dem es die Einsprungadresse der Interruptroutine an der entsprechenden Adresse in der Interruptvektortabelle hinterlegt. Tritt im nachfolgenden Programmfluß ein Interrupt auf, so wird durch die Monitorroutinen automatisch zu der zugehörigen Interruptbehandlungsroutine verzweigt. Am Ende des Kontrollflußes der Interruptroutine wird durch den RTI-Befehl die Programmflußkontrolle wieder an das verdrängte Programm zurückgegeben und Stackinhalte zurückgeschrieben.

2.5/4

Wie Sie bereits aus Kurseinheit 1 wissen, unterstützt der 6809-Prozessor drei Hardware-Interruptleitungen (NMI, FIRQ, IRQ) sowie drei Software-Interrupts (SWI1, SWI2, SWI3). Da in den Kurseinheiten 4 und 5 die Interruptmechanismen für die verschieden Peripheriekomponenten des Praktikumsrechners beschrieben werden, beschränken wir uns im folgenden Beispiel auf den Software-Interrupt SWI3.

Zu beachten ist, daß der 6809-Prozessor bei allen drei Software-Interrupts alle Register auf den Stack rettet. Werden an die Interruptroutine Übergabeparameter mittels Register übertragen, so liegen diese beim Einsprung in die Interruptroutine auf dem Stack. Eine oft genutzte Möglichkeit diese Werte in den Prozessor zu laden ist die Benutzung der Stack-relativen Adressierung. Hierbei werden die zu lesenden Werte relativ zum aktuellen Stackpointer adressiert und geladen. Rückgabeparameter an das aufrufende Programm können in der gleichen Weise (Stack-relativ !) auf den Stack geschrieben werden. Bei Beendigung der Interruptroutine werden diese durch den RTI-Befehl automatisch in die zugehörigen Prozessor-Register übertragen. Die Reihenfolge in der die einzelnen Registerwerte auf den Stack gelegt werden, ist in Kurseinheit 1 bei der Beschreibung des RTI-Befehls angegeben.

2.5/5

Praktische Übung P2.5-4:
Schreiben Sie ein Programm, daß die Betriebssystemschnittstelle eines PCs auf dem Praktikumsrechner beispielhaft nachbildet. Hierzu werden alle Monitorroutinen nicht mehr direkt angesprochen, sondern durch ein sogenanntes Gate 2) (Interruptroutine mit SWI3) aktiviert. Dem Gate wird als Übergabeparameter die Nummer der zu aktivierenden Monitorroutine nach folgender Tabelle in Register X übergeben:

Monitorroutine codierte Nummer
SHOWT7SG, Anzeigestelle S0 0
SHOWT7SG, Anzeigestelle S1 1
SHOWT7SG, Anzeigestelle S2 2
SHOWT7SG, Anzeigestelle S3 3
SHOWT7SG, Anzeigestelle S4 4
SHOWT7SG, Anzeigestelle S5 5
SHOWT7SG, Anzeigestelle S6 6
SHOWT7SG, Anzeigestelle S7 7
CLRDISP8
HALTKEY9

Im Register B soll abhängig von der gewünschten Monitorroutine der zu verarbeitende Wert übergeben werden, oder der zu erwartende Wert zurückgegeben werden (im Falle von HALTKEY). Verwenden Sie zur Parameterübergabe in der Interruptroutine die Stack relative Adressierung.

Das Hauptprogramm soll einen Ziffernzähler für die Ziffern "0" und "1" realisieren. Dazu sollen die eingelesenen Ziffern in der Anzeigestelle S0 dargestellt werden und die Anzahl der bereits eingegebenen Ziffern in den Anzeigestellen S7 (Ziffer 0) und S6 (Ziffer 1) ausgegeben werden. Bei Betätigen der "+"-Taste soll das Programm die beiden Zähler zurücksetzen und erneut die Auftrittshäufigkeit der beiden Ziffern "0" und "1" zählen und darstellen. Andere Tasten sollen keine Auswirkungen haben.

Hinweis:
> Die Aktivierung von Monitorroutinen durch das Hauptprogramm soll nur durch das obengenannte Gate mit dem Software-Interrupt 3 stattfinden.
> Die Interrupt-Vektoradresse für den SWI3 (Software-Interrupt3) ist die 16bit-Adresse $004C, $004D.

TRACE- und BREAK-Funktion:
Für das Austesten des Hauptprogrammes und der Interruptroutine stehen Ihnen ohne Einschränkungen die TRACE- und die BREAK-Funktion des Praktikumsrechners zur Verfügung. Diese werden durch keine zusätzlichen Interruptquellen, wie z.B. Timerinterrupts (siehe Kurseinheit 5) behindert, so daß Sie den Programmkontrollfluß genau verfolgen können.

Praktische Übung P2.5-5:
Allgemein könnte im obigen Beispiel die Parameterübergabe zwischen Hauptprogamm und Interruptroutine auch über Adressen des Speichers oder über den User-Stack des 6809 Prozessors abgewickelt werden. Allerdings sind in diesen Fällen zusätzliche Befehle zum Laden bzw. Abspeichern nötig. Auch würde dadurch die Betriebssystem-Schnittstelle im PC nicht korrekt modelliert werden, da dort tatsächlich alle Parameter über die Prozessor-Register übergeben werden.
Da das Programm bezüglich der Parameterübergabe breiten Raum zum Üben läßt, implementieren Sie die angesprochenen weiteren Möglichkeiten der Parameterübergabe und testen Sie Ihr Programm auf dem Praktikumsrechner aus!


Praktische Übung P2.5-6:
In Kapitel 3 werden die Software-Interrupts bei der Behandlung der Monitorroutinen genauer beschrieben, Hierbei werden Sie erfahren, daß die Software-Interrupts SWI2 und SWI3 jeweils zwei Interruptvektoradressen besitzen. Der Programmzähler wird abhängig von der Quelle des Interrupts - entweder RAM- oder ROM-Bereich (Monitor) - auf verschiedene Adressen gesetzt. Die Interruptbehandlungsroutine des Monitors überprüft dazu das X-Register, indem die Adresse des nächsten Befehls des Hauptprogramms steht. Benutzen Sie die TRACE-Funktion um diese kurze Sequenz der Monitorroutine zu überprüfen.


2.4 Entwicklungswerkzeuge zum Praktikumsrechner Lösungsvorschläge zu den Praktischen Übungen