| 2.5 Übungen zur Assemblerprogrammierung |  | Inhalt der Kurseinheit 3 |

P2/1

## Lösungsvorschläge zu den Praktischen Übungen

Zu P2.3-1:

Für pass 1 des Assemblers ergibt sich als Flußdiagramm:

P2/2

Für pass 2 des Assemblers ergibt sich als Flußdiagramm:

P2/3

Zu P2.4-1:

1. Wird das Programm mit der Option "-L" assembliert, ergibt sich das folgende Listing. Ganz links steht die Zeilennummer des Listings. Rechts daneben steht die Speicheradresse mit dem OP-Code. Weiter rechts folgt das Quellcodeprogramm.

       0001                       **************************************************
       0002                       * P24-1.ASM
       0003                       *
       0004                       * Programm zur Addition von BCD-Zahlen ohne Verwen
       0005                       * des DAA-Befehls
       0006                       *
       0007 0400                  ORG   $0400   ;Beginn des Programmbereichs
       0008
       0009 0400 bd f1 10         JSR   CLRDISP ;Anzeige loeschen
       0010 0403 10 8e 06 00      LDY   #$0600  ;Datenzeiger laden
       0011 0407 6f a4            CLR   0,Y     ;Datenbereich mit Null initialisieren
       0012 0409 6f 21            CLR   1,Y     ;
       0013 040b 8e 00 00 START:  LDX   #$0000  ;X loeschen
       0014 040e bd f1 43         JSR   HALTKEY ;Zeichen von Tastatur lesen
       0015 0411 c1 86            CMPB  #$86    ;Vergleich, auf Ende der Eingabe "S"
       0016 0413 27 30            BEQ   ENDE    ;
       0017 0415 c1 09            CMPB  #$09    ;Test der Eingabe auf gueltige Ziffer
       0018 0417 22 f2            BHI   START   ;bei ungueltiger Ziffer zurueck
       0019 0419 eb a4            ADDB  0,Y     ;Addition der Eingabe mit LSB der Sum
       0020 041b 1f 98            TFR   B,A     ;
       0021 041d 84 f0            ANDA  #$F0    ;Test auf Ueberlauf durch vorherige A
       0022 041f 26 06            BNE   BCDADD  ;wenn ja, Korrektur
       0023 0421 1f 98            TFR   B,A     ;
       0024 0423 81 09            CMPA  #$09    ;Test auf gueltige BCD-Ziffer
       0025 0425 23 02            BLS   CARRY2  ;wenn nein, Korrektur
       0026 0427 cb 06    BCDADD: ADDB  #$06    ;Korrektur, als Ergebnis gueltige BCD
       0027 0429 1f 98    CARRY2: TFR   B,A     ;
       0028 042b 84 f0            ANDA  #$F0    ;
       0029 042d 27 06            BEQ   SHOW    ;Test auf Ueberlauf durch vorherige A
       0030 042f 86 01            LDA   #$01    ;
       0031 0431 ab 21            ADDA  1,Y     ;naechsthoeherwertige Ziffer um Eins
       0032 0433 a7 21            STA   1,Y     ;naechsthoeherwertige Ziffer abspeich
       0033 0435 c4 0f     SHOW:  ANDB  #$0F    ;eventl. Uebertrag der korrigierten Z
       0034 0437 e7 a4            STB   0,Y     ;korrigierte Ziffer abspeichern
       0035 0439 bd f1 1c         JSR   SHOWT7SG;korrigierte Ziffer in Anzeige
       0036 043c e6 21            LDB   1,Y     ;naechsthoeherwertige Ziffer laden
       0037 043e 30 01            LEAX  1,X     ;Anzeigestelle nach links verschieben
       0038 0440 bd f1 1c         JSR   SHOWT7SG;naechsthoeherwertige Ziffer in Anz
       0039 0443 20 c6            BRA   START   ;zurueck zur naechsten Eingabe
       0040 0445 3f       ENDE:   SWI1
       0041
       0042 f110          CLRDISP  EQU  $F110   ;Loeschen der Anzeige, In:-,
       0043 f11c          SHOWT7SG EQU  $F11C   ;unteres Nibble von B in Anzeige
       0044 f143          HALTKEY  EQU  $F143   ;Lesen der Tastatur mit Warten,

       Number of errors 0

2. Das Programm addiert die eingegebene BCD-Ziffer mit der niederstwertigen Ziffer der Summe. Die BCD-Addition wird ohne den DAA-Befehl durchgeführt. Überschreitet die Summe den Wert "99", so wird hexadezimal in der zweiten Stelle weitergezählt. D.h. eine Überprüfung auf eine gültige BCD-Ziffer in der zweiten Stelle findet nicht statt. Beispielsweise ergibt 99 + 1 = A0.

   P2/4

   Das folgende Flußdiagramm zeigt die Problematik:

Zu P2.4-2:

1. In dem Programm sind drei syntaktische Fehler vorhanden:
   Für Zeile 29 gibt der Assembler die Fehlermeldung "29: Branch out of Range". Hier fehlt das Sprungziel "SHOW" im Verzweigungsbefehl.
   Für Zeile 34 meldet er den Fehler "34: Unrecognized Mnemonic". Es fehlt beim Store-Befehl das Quellregister "B".
   Für Zeile 37 meldet der Assembler: "37: Illegal Register for Indexed". In diesem Fall ist bei den Operanden des "LEAX"-Befehls das erste Komma zuviel.

   P2/5

2. Der logische Fehler des Programms besteht aus einer falschen Wahl eines Verzweigungsbefehls. Statt einen vorzeichenlosen Verzweigungsbefehl zu benutzen, wurde in Zeile 18 der "BGT"-Befehl benutzt. Hierdurch werden die Tasten des Funktionsblockes akzeptiert, so daß die Addition zu falschen Ergebnissen führt.

Zu P2.4-3:

Der "DS9" erzeugt aus der Datei "P24-1.HEX" das folgende Listing:

    ;                    ***   6809 - Disassembler Ver. 1.3   ***

    0400: BD F1 10         JSR    $F110
    0403: 10 8E 06 00      LDY    #$0600
    0407: 6F A4            CLR    ,Y
    0409: 6F 21            CLR    $01,Y
    040B: 8E 00 00         LDX    #$0000
    040E: BD F1 43         JSR    $F143
    0411: C1 86            CMPB   #$86
    0413: 27 30            BEQ    $30   ;$0445
    0415: C1 09            CMPB   #$09
    0417: 22 F2            BHI    $F2   ;$040B
    0419: EB A4            ADDB   ,Y
    041B: 1F 98            TFR    B,A
    041D: 84 F0            ANDA   #$F0
    041F: 26 06            BNE    $06   ;$0427
    0421: 1F 98            TFR    B,A
    0423: 81 09            CMPA   #$09
    0425: 23 02            BLS    $02   ;$0429
    0427: CB 06            ADDB   #$06
    0429: 1F 98            TFR    B,A
    042B: 84 F0            ANDA   #$F0
    042D: 27 06            BEQ    $06   ;$0435
    042F: 86 01            LDA    #$01
    0431: AB 21            ADDA   $01,Y
    0433: A7 21            STA    $01,Y
    0435: C4 0F            ANDB   #$0F
    0437: E7 A4            STB    ,Y
    0439: BD F1 1C         JSR    $F11C
    043C: E6 21            LDB    $01,Y
    043E: 30 01            LEAX   $01,X
    0440: BD F1 1C         JSR    $F11C
    0443: 20 C6            BRA    $C6   ;$040B
    0445: 3F               SWI(1)

Symbolische Adressen für Sprungziele und Unterprogramme können bei einer Disassemblierung nicht berücksichtigt werden. Ein "Nach-Disassemblieren" von Hand ist notwendig. Der generierte Quellcode stimmt ansonsten mit dem "Original"-Code überein.
Beachten Sie bitte, daß der "DS9" bei Verzweigungen nicht nur den Offset zum Sprungziel angibt, sondern auch die absolute Adresse des Sprungzieles.

P2/6

Zu P2.5-1:

Für das Unterprogramm von Tabelle 2.4-6 ergibt sich das folgende Flußdiagramm:

Im Falle eines Übertrages auf die nächsthöhere Ziffer wird das Register A auf einen Wert ungleich Null gesetzt und dem Hauptprogramm übergeben. Die berechnete Ziffer wird abgespeichert. Die Übergabe der zugehörigen Speicheradresse erfolgt durch das Register Y.

Zu P2.5.2:

Das Flußdiagramm für das Hauptprogramm ist auf der folgenden Seite abgebildet. Erzeugt die BCD-Addition einen Übertrag, so wird das Unterprogramm nochmals aufgerufen, um den Übertrag zur nächsthöheren Ziffer der Summe zu addieren. Im worst case muß über die gesamte Zahlenbreite eine Übertragsaddition erfolgen. Wird ein Überlauf erkannt, so wird das Overflow-Symbol zur Anzeige gebracht. Wird danach die "S"-Taste gedrückt, so wird das Programm beendet, ansonsten bei Eingabe einer gültigen Ziffer mit einer neuen Addition begonnen.

P2/7

Eine mögliche Implementierung für das Hauptprogramm folgt jetzt.

P2/8

    ;****************************************************************************
    ; P1a.ASM
    ;
    ; Programm zur Addition von BCD-Zahlen mit maximal 7 Ziffern ohne Verwendung
    ; des 6809-Befehls DAA. Der Befehl wird durch das Unterprogramm UP_ADD er-
    ; setzt.
    ; Erweiterung der Wortbreite ohne Probleme moeglich
    ; (Praktikumsrechner hat allerdings max. 8 Stellen).
    ; Bei Ueberlauf Aktivierung der Anzeige.
    ; Abbruch der Eingabe mit der Taste "S" des Praktikumsrechners.
    ;
            ORG    $0400      ;Beginn des Programmbereiches

    NEW:    JSR    CLRDISP    ;Anzeige loeschen
            CLRA              ;A loeschen
            CLRB              ;B loeschen
            STD    >$0600     ;Datenbereich $0600 - $0607
            STD    >$0602     ;mit Null initialisieren
            STD    >$0604     ;
            STD    >$0606     ;
    START:  LDX    #$0000     ;X loeschen
            LDY    #$0600     ;Datenzeiger laden
            LDU    #ZWIDTH    ;Zahlenbreite laden
            JSR    HALTKEY    ;Zeichen von Tastatur lesen
            CMPB   #$86       ;Vergleich, auf Ende der Eingabe (Taste "S")
            BEQ    END        ;
            CMPB   #$09       ;Test der Eingabe auf gueltige Ziffer (0..9)
            BHI    START      ;bei ungueltiger Ziffer zurueck zur Eingabe
    CARRY:  JSR    UP_ADD     ;Aufruf UP zur Addition von zwei BCD-Ziffern
            JSR    SHOWT7SG   ;korrigierte Ziffer in Anzeige
            LEAX   1,X        ;X:=X+1
            LEAY   1,Y        ;Y:=Y+1
            LEAU   -1,U       ;U:=U-1
            LDB    #$01       ;Reg. B fuer Addition des Uebertragsbits auf 1
            CMPA   #$00       ;Vergleich auf Ueberlauf
            BEQ    START      ;wenn nein, zurueck zur naechsten Eingabe
            CMPU   #$0000     ;Vergleich auf max. Zahlenbreite
            BEQ    OVERFL     ;Overflow, Uebertrag ignorieren; zur OV-Behandlung
            BRA    CARRY      ;Uebertrag addieren und anzeigen
    OVERFL: LDA    #$5C       ;
            JSR    SHOWA      ;Overflow-Symbol in Anzeigestelle S7
            JSR    HALTKEY    ;Zeichen einlesen
            CMPB   #$86       ;Programmende?
            BNE    NEW        ;nein, neuer Lauf
    END:    SWI               ;Programmende

    ZWIDTH  EQU    $0007      ;Maximale Ziffernanzahl einer zu speichernden BCD-Zahl

    ;----------------------------------------------------------------------------
    ; Hier beginnen die Definitionen der Monitorroutinen und der Adressen der
    ; Hardware-Register.
    ;                       Monitorroutinen
    ;
    CLRDISP    EQU  $F110     ;Loeschen der Anzeige, In:-, Out:-
    SHOWT7SG   EQU  $F11C     ;unteres Nibble von B in Siebensegmentcode wandeln
                              ;und anzeigen, Position in X
    SHOWA      EQU  $F113     ;Direkte Ansteuerung der Segmente der Anzeige-
                              ;stelle durch das Byte in A, Position in X
    HALTKEY    EQU  $F143     ;Lesen der Tastatur mit Warten, In:-, Out:B

Sie finden das Assemblerprogramm in dem Unterverzeichnis "Software" der CD-ROM in der Datei "P1a.ASM".

P2/9

Zu P2.5-3:

Die folgenden drei Unterprogramme repräsentieren die geforderten Zähler. In "UP1" werden die Register über den Stack gerettet, während in "UP2" nicht benötigte Register verwendet werden. Das Programm ist auch als Datei "P25-3.ASM" im Unterverzeichnis "KE2" abgelegt.

    ;=====================================================================
    ;Unterprogramm  UP1: Zaehler, für die Anzeigestellen S5,S4
    ;                Register B und X werden ueber den Stack gerettet.
    ;
    UP1:    LDX     #$0004     ;Anzeigeposition laden (S5,S4)
            LDB     #$FF       ;Zaehler initialisieren
    NEXT1:  INCB               ;Zaehler inkrementieren
            JSR     SHOWB7SG   ;Zaehlerstand anzeigen
            PSHS    B          ;Zaehler und Anzeigeposition ueber
            PSHS    X          ;den Stack retten
            JSR     UP2        ;Aufruf des zweiten UP
            PULS    X          ;Zaehler und Anzeigeposition einlesen
            PULS    B          ;
            CMPB    #$FF       ;Abbruchbedingung erfuellt?
            BNE     NEXT1      ;wenn nein, zurueck zu NEXT1
            RTS                ;Ende Unterprogramm

    ;=====================================================================
    ;Unterprogramm  UP2: Zaehler, für die Anzeigestellen S3,S2
    ;                Register B und X werden ueber Register gerettet.
    ;
    UP2:    LDX     #$0002     ;Anzeigeposition laden (S3,S2)
            LDB     #$FF       ;Zaehler initialisieren
    NEXT2:  INCB               ;Zaehler inkrementieren
            JSR     SHOWB7SG   ;Zaehlerstand anzeigen
            TFR     B,A        ;Zaehler und Anzeigeposition ueber
            TFR     X,Y        ;andere Prozessorregister retten
            JSR     UP3        ;Aufruf des dritten (letzten) UP
            TFR     A,B        ;Zaehler und Anzeigeposition wieder
            TFR     Y,X        ;zurueckschreiben
            CMPB    #$FF       ;Abbruchbedingung erfuellt?
            BNE     NEXT2      ;wenn nein, zurueck zu NEXT2
            RTS                ;Ende Unterprogramm

    ;=====================================================================
    ;Unterprogramm  UP3: Zaehler, für die Anzeigestellen S1,S0
    ;
    UP3:    LDX     #$0000     ;Anzeigeposition laden (S1,S0)
            LDB     #$FF       ;Zaehler initialisieren
    NEXT3:  INCB               ;Zaehler inkrementieren
            JSR     SHOWB7SG   ;Zaehlerstand anzeigen
            CMPB    #$FF       ;Abbruchbedingung erfuellt?
            BNE     NEXT3      ;wenn nein, zurueck zu NEXT3
            RTS                ;Ende Unterprogramm

Im Unterprogramm "UP2" werden je zwei Stackoperationen benutzt, um die Registerinhalte auf den Stack zu schreiben bzw. einzulesen. Es ist auch möglich, die Stackoperationen mit jeweils einem Befehl auszuführen. Hierzu ist entweder der numerische Wert "immediate" anzugeben; oder die einzelnen Register durch Kommata getrennt aufzuzählen.

P2/10

Zu P2.5-4:

Eine mögliche Implementierung des Hauptprogramms und der Interruptroutine zeigt das folgende Listing:

    ;*********************************************************************
    ; P4.ASM
    ;
    ; Ziffern-Zaehlprogramm mittels einer Software-Interruptroutine.
    ; Software-Interrupt wird als Monitor- (Betriebssystem-) Gate benutzt,
    ; um Funktionen des Monitors zu benutzen. Eingegebener Wert wird in S0
    ; dargestellt, Anzahl der eingebenen Werte fuer "0" in S7, fuer "1" in
    ; S6. Uebergabeparameter: X-Reg.: call-by-value, B-Reg.: call-by-refr.
    ; Parameter werden in der Interruptroutine Stack-relativ geladen.
    ;
             ORG   $0400    ;Beginn des Programmbereiches

             LDX   #MRGATE  ;Interruptroutine in der Vektortabelle
             STX   $004C    ;an Adresse $004C, $004D eintragen
    NEW:     LDX   #$0008   ;Uebergabeparameter nach A (Anzeige loeschen)
             SWI3           ;Software-Interrupt ausfuehren
             CLRB           ;Zaehler in den Speicherstellen
             LDX   #$0006   ;$06 und $07 in der Zero-Page
             STB   ,X       ;mit Null initialisieren
             SWI3           ;und jeweiliger Wert (0)
             LEAX  1,X      ;in den Anzeigestellen
             STB   ,X       ;S6 und S7 anzeigen
             SWI3           ;
    INPUT:   LDX   #$0009   ;Uebergabeparameter fuer Tastatur nach X und
             SWI3           ;zur Interruptroutine verzweigen
             CMPB  #$01     ;Wurde Taste "1" betaetigt?
             BNE   INPUT0   ;Wenn nein, vielleicht "0"
             LDX   #$0000   ;Wert in Anzeigestelle
             SWI3           ;S0 anzeigen
             LEAX  6,X      ;Zaehler fuer den
             LDB   ,X       ;Wert "1" laden,
             INCB           ;inkrementieren und wieder
             STB   ,X       ;abspeichern
             SWI3           ;In S6 anzeigen
             BRA   INPUT    ;zurueck zur naechsten Eingabe
    INPUT0:  CMPB  #$00     ;Vergleich auf Taste "0"
             BNE   INPUT+   ;wenn nein, vielleicht "+"-Taste
             LDX   #$0000   ;Wert in Anzeigestelle
             SWI3           ;S0 anzeigen
             LEAX  7,X      ;Zaehler fuer "0"
             LDB   ,X       ;laden,
             INCB           ;inkrementieren und
             STB   ,X       ;abspeichern
             SWI3           ;In S7 anzeigen
             BRA   INPUT    ;zurueck zur naechsten Eingabe
    INPUT+:  CMPB  #$80     ;Vergleich auf "+"-Taste
             BEQ   NEW      ;Wenn ja, Zaehler neu initialisieren
             BRA   INPUT    ;ungueltige Taste, zurueck zur Eingabe

P2/11

    ; Interruptroutine
    MRGATE:  LDX   4,S      ;X-Register vom Stack holen (Stack-relativ)
             CMPX  #$0008   ;Routine fuer Anzeige gefordert ?
             BHS   DISP     ;Wenn nein, vielleicht Anzeige loeschen
             JSR   SHOWT7SG ;Anzeige des unteren Nibbles aus B
             BRA   SUBEND   ;zum RTI
    DISP:    CMPX  #$0008   ;Routine zum Loeschen der Anzeige gefordert?
             BNE   TAST     ;Wenn nein, vielleicht Routine zur Tastatureing.
             JSR   CLRDISP  ;Anzeige loeschen
             BRA   SUBEND   ;zum RTI
    TAST:    CMPX  #$0009   ;Routine zur Tastatureingabe gefordert?
             BNE   SUBEND   ;Wenn nein, falscher Uebergabeparameter; zurueck
             JSR   HALTKEY  ;auf Eingabe von Tastatur warten
             STB   2,S      ;B-Register auf den Stack legen (Stack relativ)
    SUBEND:  RTI            ;Ruecksprung zum Hauptprogramm

    CLRDISP   EQU  $F110    ;Loeschen der Anzeige, In:-, Out:-
    SHOWT7SG  EQU  $F11C    ;unteres Nibble von B in Anzeige, Position in X
    HALTKEY   EQU  $F143    ;Lesen der Tastatur mit Warten, In:-, Out:B

Es hat sich als gute Programmierpraxis erwiesen, in Unterprogrammroutinen, und insbesondere in Interruptroutinen, nur eine Aussprungstelle zu programmieren. In der obigen Interruptroutine wäre es möglich nach den einzelnen Abfragen nicht mit einem BRA-Befehl zum Ende der Routine zu verzweigen und erst dann einen RTI-Befehl durchzuführen, sondern statt eines BRA-Befehls sofort einen RTI-Befehl zu programmieren. In Bezug auf ein gutes Software-Engineering und einfache Wartungs- und Pflegeoptionen hat sich herausgestellt, daß sich eine Beschränkung auf eine einzige Aussprungstelle positiv auf die Programmierfehlerrate auswirkt.

Sie finden das Assemblerprogramm im Unterverzeichnis "Software" der CD-ROM in der Datei "P4.ASM".

| 2.5 Übungen zur Assemblerprogrammierung |  | Inhalt der Kurseinheit 3 |